~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/spread/banana.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_banana -*-
 
2
# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
"""
 
6
Banana -- s-exp based protocol.
 
7
 
 
8
Future Plans: This module is almost entirely stable.  The same caveat applies
 
9
to it as applies to L{twisted.spread.jelly}, however.  Read its future plans
 
10
for more details.
 
11
 
 
12
@author: Glyph Lefkowitz
 
13
"""
 
14
 
 
15
import copy, cStringIO, struct
 
16
 
 
17
from twisted.internet import protocol
 
18
from twisted.persisted import styles
 
19
from twisted.python import log
 
20
 
 
21
class BananaError(Exception):
 
22
    pass
 
23
 
 
24
def int2b128(integer, stream):
 
25
    if integer == 0:
 
26
        stream(chr(0))
 
27
        return
 
28
    assert integer > 0, "can only encode positive integers"
 
29
    while integer:
 
30
        stream(chr(integer & 0x7f))
 
31
        integer = integer >> 7
 
32
 
 
33
 
 
34
def b1282int(st):
 
35
    """
 
36
    Convert an integer represented as a base 128 string into an C{int} or
 
37
    C{long}.
 
38
 
 
39
    @param st: The integer encoded in a string.
 
40
    @type st: C{str}
 
41
 
 
42
    @return: The integer value extracted from the string.
 
43
    @rtype: C{int} or C{long}
 
44
    """
 
45
    e = 1
 
46
    i = 0
 
47
    for char in st:
 
48
        n = ord(char)
 
49
        i += (n * e)
 
50
        e <<= 7
 
51
    return i
 
52
 
 
53
 
 
54
# delimiter characters.
 
55
LIST     = chr(0x80)
 
56
INT      = chr(0x81)
 
57
STRING   = chr(0x82)
 
58
NEG      = chr(0x83)
 
59
FLOAT    = chr(0x84)
 
60
# "optional" -- these might be refused by a low-level implementation.
 
61
LONGINT  = chr(0x85)
 
62
LONGNEG  = chr(0x86)
 
63
# really optional; this is is part of the 'pb' vocabulary
 
64
VOCAB    = chr(0x87)
 
65
 
 
66
HIGH_BIT_SET = chr(0x80)
 
67
 
 
68
def setPrefixLimit(limit):
 
69
    """
 
70
    Set the limit on the prefix length for all Banana connections
 
71
    established after this call.
 
72
 
 
73
    The prefix length limit determines how many bytes of prefix a banana
 
74
    decoder will allow before rejecting a potential object as too large.
 
75
 
 
76
    @type limit: C{int}
 
77
    @param limit: The number of bytes of prefix for banana to allow when
 
78
    decoding.
 
79
    """
 
80
    global _PREFIX_LIMIT
 
81
    _PREFIX_LIMIT = limit
 
82
setPrefixLimit(64)
 
83
 
 
84
SIZE_LIMIT = 640 * 1024   # 640k is all you'll ever need :-)
 
85
 
 
86
class Banana(protocol.Protocol, styles.Ephemeral):
 
87
    knownDialects = ["pb", "none"]
 
88
 
 
89
    prefixLimit = None
 
90
    sizeLimit = SIZE_LIMIT
 
91
 
 
92
    def setPrefixLimit(self, limit):
 
93
        """
 
94
        Set the prefix limit for decoding done by this protocol instance.
 
95
 
 
96
        @see: L{setPrefixLimit}
 
97
        """
 
98
        self.prefixLimit = limit
 
99
        self._smallestLongInt = -2 ** (limit * 7) + 1
 
100
        self._smallestInt = -2 ** 31
 
101
        self._largestInt = 2 ** 31 - 1
 
102
        self._largestLongInt = 2 ** (limit * 7) - 1
 
103
 
 
104
 
 
105
    def connectionReady(self):
 
106
        """Surrogate for connectionMade
 
107
        Called after protocol negotiation.
 
108
        """
 
109
 
 
110
    def _selectDialect(self, dialect):
 
111
        self.currentDialect = dialect
 
112
        self.connectionReady()
 
113
 
 
114
    def callExpressionReceived(self, obj):
 
115
        if self.currentDialect:
 
116
            self.expressionReceived(obj)
 
117
        else:
 
118
            # this is the first message we've received
 
119
            if self.isClient:
 
120
                # if I'm a client I have to respond
 
121
                for serverVer in obj:
 
122
                    if serverVer in self.knownDialects:
 
123
                        self.sendEncoded(serverVer)
 
124
                        self._selectDialect(serverVer)
 
125
                        break
 
126
                else:
 
127
                    # I can't speak any of those dialects.
 
128
                    log.msg("The client doesn't speak any of the protocols "
 
129
                            "offered by the server: disconnecting.")
 
130
                    self.transport.loseConnection()
 
131
            else:
 
132
                if obj in self.knownDialects:
 
133
                    self._selectDialect(obj)
 
134
                else:
 
135
                    # the client just selected a protocol that I did not suggest.
 
136
                    log.msg("The client selected a protocol the server didn't "
 
137
                            "suggest and doesn't know: disconnecting.")
 
138
                    self.transport.loseConnection()
 
139
 
 
140
 
 
141
    def connectionMade(self):
 
142
        self.setPrefixLimit(_PREFIX_LIMIT)
 
143
        self.currentDialect = None
 
144
        if not self.isClient:
 
145
            self.sendEncoded(self.knownDialects)
 
146
 
 
147
 
 
148
    def gotItem(self, item):
 
149
        l = self.listStack
 
150
        if l:
 
151
            l[-1][1].append(item)
 
152
        else:
 
153
            self.callExpressionReceived(item)
 
154
 
 
155
    buffer = ''
 
156
 
 
157
    def dataReceived(self, chunk):
 
158
        buffer = self.buffer + chunk
 
159
        listStack = self.listStack
 
160
        gotItem = self.gotItem
 
161
        while buffer:
 
162
            assert self.buffer != buffer, "This ain't right: %s %s" % (repr(self.buffer), repr(buffer))
 
163
            self.buffer = buffer
 
164
            pos = 0
 
165
            for ch in buffer:
 
166
                if ch >= HIGH_BIT_SET:
 
167
                    break
 
168
                pos = pos + 1
 
169
            else:
 
170
                if pos > self.prefixLimit:
 
171
                    raise BananaError("Security precaution: more than %d bytes of prefix" % (self.prefixLimit,))
 
172
                return
 
173
            num = buffer[:pos]
 
174
            typebyte = buffer[pos]
 
175
            rest = buffer[pos+1:]
 
176
            if len(num) > self.prefixLimit:
 
177
                raise BananaError("Security precaution: longer than %d bytes worth of prefix" % (self.prefixLimit,))
 
178
            if typebyte == LIST:
 
179
                num = b1282int(num)
 
180
                if num > SIZE_LIMIT:
 
181
                    raise BananaError("Security precaution: List too long.")
 
182
                listStack.append((num, []))
 
183
                buffer = rest
 
184
            elif typebyte == STRING:
 
185
                num = b1282int(num)
 
186
                if num > SIZE_LIMIT:
 
187
                    raise BananaError("Security precaution: String too long.")
 
188
                if len(rest) >= num:
 
189
                    buffer = rest[num:]
 
190
                    gotItem(rest[:num])
 
191
                else:
 
192
                    return
 
193
            elif typebyte == INT:
 
194
                buffer = rest
 
195
                num = b1282int(num)
 
196
                gotItem(num)
 
197
            elif typebyte == LONGINT:
 
198
                buffer = rest
 
199
                num = b1282int(num)
 
200
                gotItem(num)
 
201
            elif typebyte == LONGNEG:
 
202
                buffer = rest
 
203
                num = b1282int(num)
 
204
                gotItem(-num)
 
205
            elif typebyte == NEG:
 
206
                buffer = rest
 
207
                num = -b1282int(num)
 
208
                gotItem(num)
 
209
            elif typebyte == VOCAB:
 
210
                buffer = rest
 
211
                num = b1282int(num)
 
212
                gotItem(self.incomingVocabulary[num])
 
213
            elif typebyte == FLOAT:
 
214
                if len(rest) >= 8:
 
215
                    buffer = rest[8:]
 
216
                    gotItem(struct.unpack("!d", rest[:8])[0])
 
217
                else:
 
218
                    return
 
219
            else:
 
220
                raise NotImplementedError(("Invalid Type Byte %r" % (typebyte,)))
 
221
            while listStack and (len(listStack[-1][1]) == listStack[-1][0]):
 
222
                item = listStack.pop()[1]
 
223
                gotItem(item)
 
224
        self.buffer = ''
 
225
 
 
226
 
 
227
    def expressionReceived(self, lst):
 
228
        """Called when an expression (list, string, or int) is received.
 
229
        """
 
230
        raise NotImplementedError()
 
231
 
 
232
 
 
233
    outgoingVocabulary = {
 
234
        # Jelly Data Types
 
235
        'None'           :  1,
 
236
        'class'          :  2,
 
237
        'dereference'    :  3,
 
238
        'reference'      :  4,
 
239
        'dictionary'     :  5,
 
240
        'function'       :  6,
 
241
        'instance'       :  7,
 
242
        'list'           :  8,
 
243
        'module'         :  9,
 
244
        'persistent'     : 10,
 
245
        'tuple'          : 11,
 
246
        'unpersistable'  : 12,
 
247
 
 
248
        # PB Data Types
 
249
        'copy'           : 13,
 
250
        'cache'          : 14,
 
251
        'cached'         : 15,
 
252
        'remote'         : 16,
 
253
        'local'          : 17,
 
254
        'lcache'         : 18,
 
255
 
 
256
        # PB Protocol Messages
 
257
        'version'        : 19,
 
258
        'login'          : 20,
 
259
        'password'       : 21,
 
260
        'challenge'      : 22,
 
261
        'logged_in'      : 23,
 
262
        'not_logged_in'  : 24,
 
263
        'cachemessage'   : 25,
 
264
        'message'        : 26,
 
265
        'answer'         : 27,
 
266
        'error'          : 28,
 
267
        'decref'         : 29,
 
268
        'decache'        : 30,
 
269
        'uncache'        : 31,
 
270
        }
 
271
 
 
272
    incomingVocabulary = {}
 
273
    for k, v in outgoingVocabulary.items():
 
274
        incomingVocabulary[v] = k
 
275
 
 
276
    def __init__(self, isClient=1):
 
277
        self.listStack = []
 
278
        self.outgoingSymbols = copy.copy(self.outgoingVocabulary)
 
279
        self.outgoingSymbolCount = 0
 
280
        self.isClient = isClient
 
281
 
 
282
    def sendEncoded(self, obj):
 
283
        io = cStringIO.StringIO()
 
284
        self._encode(obj, io.write)
 
285
        value = io.getvalue()
 
286
        self.transport.write(value)
 
287
 
 
288
    def _encode(self, obj, write):
 
289
        if isinstance(obj, (list, tuple)):
 
290
            if len(obj) > SIZE_LIMIT:
 
291
                raise BananaError(
 
292
                    "list/tuple is too long to send (%d)" % (len(obj),))
 
293
            int2b128(len(obj), write)
 
294
            write(LIST)
 
295
            for elem in obj:
 
296
                self._encode(elem, write)
 
297
        elif isinstance(obj, (int, long)):
 
298
            if obj < self._smallestLongInt or obj > self._largestLongInt:
 
299
                raise BananaError(
 
300
                    "int/long is too large to send (%d)" % (obj,))
 
301
            if obj < self._smallestInt:
 
302
                int2b128(-obj, write)
 
303
                write(LONGNEG)
 
304
            elif obj < 0:
 
305
                int2b128(-obj, write)
 
306
                write(NEG)
 
307
            elif obj <= self._largestInt:
 
308
                int2b128(obj, write)
 
309
                write(INT)
 
310
            else:
 
311
                int2b128(obj, write)
 
312
                write(LONGINT)
 
313
        elif isinstance(obj, float):
 
314
            write(FLOAT)
 
315
            write(struct.pack("!d", obj))
 
316
        elif isinstance(obj, str):
 
317
            # TODO: an API for extending banana...
 
318
            if self.currentDialect == "pb" and obj in self.outgoingSymbols:
 
319
                symbolID = self.outgoingSymbols[obj]
 
320
                int2b128(symbolID, write)
 
321
                write(VOCAB)
 
322
            else:
 
323
                if len(obj) > SIZE_LIMIT:
 
324
                    raise BananaError(
 
325
                        "string is too long to send (%d)" % (len(obj),))
 
326
                int2b128(len(obj), write)
 
327
                write(STRING)
 
328
                write(obj)
 
329
        else:
 
330
            raise BananaError("could not send object: %r" % (obj,))
 
331
 
 
332
 
 
333
# For use from the interactive interpreter
 
334
_i = Banana()
 
335
_i.connectionMade()
 
336
_i._selectDialect("none")
 
337
 
 
338
 
 
339
def encode(lst):
 
340
    """Encode a list s-expression."""
 
341
    io = cStringIO.StringIO()
 
342
    _i.transport = io
 
343
    _i.sendEncoded(lst)
 
344
    return io.getvalue()
 
345
 
 
346
 
 
347
def decode(st):
 
348
    """
 
349
    Decode a banana-encoded string.
 
350
    """
 
351
    l = []
 
352
    _i.expressionReceived = l.append
 
353
    try:
 
354
        _i.dataReceived(st)
 
355
    finally:
 
356
        _i.buffer = ''
 
357
        del _i.expressionReceived
 
358
    return l[0]