~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/spread/banana.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2004-06-21 22:01:11 UTC
  • mto: (2.2.3 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20040621220111-vkf909euqnyrp3nr
Tags: upstream-1.3.0
ImportĀ upstreamĀ versionĀ 1.3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
 
1
# -*- test-case-name: twisted.test.test_banana -*-
 
2
#
2
3
# Twisted, the Framework of Your Internet
3
4
# Copyright (C) 2001 Matthew W. Lefkowitz
4
5
#
15
16
# License along with this library; if not, write to the Free Software
16
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18
 
18
 
# Generic List ENcoding
19
 
 
20
 
from twisted.protocols import protocol
 
19
"""Banana -- s-exp based protocol.
 
20
 
 
21
Stability: semi-stable
 
22
 
 
23
Future Plans: This module is almost entirely stable.  The same caveat applies
 
24
to it as applies to L{twisted.spread.jelly}, however.  Read its future plans
 
25
for more details.
 
26
 
 
27
@author: U{Glyph Lefkowitz<mailto:glyph@twistedmatrix.com>}
 
28
"""
 
29
 
 
30
__version__ = "$Revision: 1.37 $"[11:-2]
 
31
 
 
32
from twisted.internet import protocol
21
33
from twisted.persisted import styles
 
34
from twisted.python import log
 
35
 
22
36
import types, copy, cStringIO, struct
23
37
 
 
38
class BananaError(Exception):
 
39
    pass
 
40
 
24
41
def int2b128(integer, stream):
25
42
    if integer == 0:
26
43
        stream(chr(0))
31
48
        integer = integer >> 7
32
49
 
33
50
def b1282int(st):
34
 
    if len(st) > 5:
35
 
        oneHundredAndTwentyEight = 128l
36
 
    else:
37
 
        oneHundredAndTwentyEight = 128
 
51
    oneHundredAndTwentyEight = 128l
38
52
    i = 0
39
53
    place = 0
40
54
    for char in st:
41
55
        num = ord(char)
42
56
        i = i + (num * (oneHundredAndTwentyEight ** place))
43
57
        place = place + 1
44
 
    try:
 
58
    if i <= 2147483647:
45
59
        return int(i)
46
 
    except OverflowError:
 
60
    else:
47
61
        return i
48
62
 
49
63
# delimiter characters.
50
64
LIST     = chr(0x80)
51
65
INT      = chr(0x81)
52
66
STRING   = chr(0x82)
53
 
SYMBOL   = chr(0x83)
54
 
NEG      = chr(0x84)
55
 
VOCAB    = chr(0x85)
56
 
FLOAT    = chr(0x86)
57
 
# This will make it easier for people writing low-level implementations.
58
 
LONGINT  = chr(0x87)
59
 
LONGNEG  = chr(0x88)
 
67
NEG      = chr(0x83)
 
68
FLOAT    = chr(0x84)
 
69
# "optional" -- these might be refused by a low-level implementation.
 
70
LONGINT  = chr(0x85)
 
71
LONGNEG  = chr(0x86)
 
72
# really optional; this is is part of the 'pb' vocabulary
 
73
VOCAB    = chr(0x87)
60
74
 
61
75
HIGH_BIT_SET = chr(0x80)
62
76
 
63
 
class Banana(protocol.Protocol, styles.Ephemeral):
 
77
SIZE_LIMIT = 640 * 1024   # 640k is all you'll ever need :-)
 
78
 
 
79
class Pynana(protocol.Protocol, styles.Ephemeral):
 
80
    knownDialects = ["pb", "none"]
 
81
 
 
82
    def connectionReady(self):
 
83
        """Surrogate for connectionMade
 
84
        Called after protocol negotiation.
 
85
        """
 
86
 
 
87
    def _selectDialect(self, dialect):
 
88
        self.currentDialect = dialect
 
89
        self.connectionReady()
 
90
 
 
91
    def callExpressionReceived(self, obj):
 
92
        if self.currentDialect:
 
93
            self.expressionReceived(obj)
 
94
        else:
 
95
            # this is the first message we've received
 
96
            if self.isClient:
 
97
                # if I'm a client I have to respond
 
98
                for serverVer in obj:
 
99
                    if serverVer in self.knownDialects:
 
100
                        self.sendEncoded(serverVer)
 
101
                        self._selectDialect(serverVer)
 
102
                        break
 
103
                else:
 
104
                    # I can't speak any of those dialects.
 
105
                    log.msg('error losing')
 
106
                    self.transport.loseConnection()
 
107
            else:
 
108
                if obj in self.knownDialects:
 
109
                    self._selectDialect(obj)
 
110
                else:
 
111
                    # the client just selected a protocol that I did not suggest.
 
112
                    log.msg('freaky losing')
 
113
                    self.transport.loseConnection()
 
114
 
 
115
 
64
116
    def connectionMade(self):
65
 
        self.listStack = []
 
117
        self.currentDialect = None
 
118
        if not self.isClient:
 
119
            self.sendEncoded(self.knownDialects)
66
120
 
67
121
    def gotItem(self, item):
68
122
        l = self.listStack
69
123
        if l:
70
124
            l[-1][1].append(item)
71
125
        else:
72
 
            self.expressionReceived(item)
 
126
            self.callExpressionReceived(item)
73
127
 
74
128
    buffer = ''
75
129
 
87
141
                pos = pos + 1
88
142
            else:
89
143
                if pos > 64:
90
 
                    raise Exception("Security precaution: more than 64 bytes of prefix")
 
144
                    raise BananaError("Security precaution: more than 64 bytes of prefix")
91
145
                return
92
146
            num = buffer[:pos]
93
147
            typebyte = buffer[pos]
94
148
            rest = buffer[pos+1:]
95
149
            if len(num) > 64:
96
 
                raise Exception("Security precaution: longer than 64 bytes worth of prefix")
 
150
                raise BananaError("Security precaution: longer than 64 bytes worth of prefix")
97
151
            if typebyte == LIST:
98
152
                num = b1282int(num)
 
153
                if num > SIZE_LIMIT:
 
154
                    raise BananaError("Security precaution: List too long.")
99
155
                listStack.append((num, []))
100
156
                buffer = rest
101
157
            elif typebyte == STRING:
102
158
                num = b1282int(num)
103
 
                if num > 640 * 1024: # 640k is all you'll ever need :-)
104
 
                    raise Exception("Security precaution: Length identifier too long.")
 
159
                if num > SIZE_LIMIT:
 
160
                    raise BananaError("Security precaution: String too long.")
105
161
                if len(rest) >= num:
106
162
                    buffer = rest[num:]
107
163
                    gotItem(rest[:num])
123
179
                buffer = rest
124
180
                num = -b1282int(num)
125
181
                gotItem(num)
126
 
            elif typebyte == SYMBOL:
 
182
            elif typebyte == VOCAB:
127
183
                buffer = rest
128
184
                num = b1282int(num)
129
185
                gotItem(self.incomingVocabulary[num])
130
 
            elif typebyte == VOCAB:
131
 
                buffer = rest
132
 
                num = b1282int(num)
133
 
                gotItem(self.incomingVocabulary[-num])
134
186
            elif typebyte == FLOAT:
135
187
                if len(rest) >= 8:
136
188
                    buffer = rest[8:]
144
196
                gotItem(item)
145
197
        self.buffer = ''
146
198
 
 
199
 
147
200
    def expressionReceived(self, lst):
148
201
        """Called when an expression (list, string, or int) is received.
149
202
        """
152
205
 
153
206
    outgoingVocabulary = {
154
207
        # Jelly Data Types
155
 
        'None'           :  -1,
156
 
        'class'          :  -2,
157
 
        'dereference'    :  -3,
158
 
        'reference'      :  -4,
159
 
        'dictionary'     :  -5,
160
 
        'function'       :  -6,
161
 
        'instance'       :  -7,
162
 
        'list'           :  -8,
163
 
        'module'         :  -9,
164
 
        'persistent'     : -10,
165
 
        'tuple'          : -11,
166
 
        'unpersistable'  : -12,
 
208
        'None'           :  1,
 
209
        'class'          :  2,
 
210
        'dereference'    :  3,
 
211
        'reference'      :  4,
 
212
        'dictionary'     :  5,
 
213
        'function'       :  6,
 
214
        'instance'       :  7,
 
215
        'list'           :  8,
 
216
        'module'         :  9,
 
217
        'persistent'     : 10,
 
218
        'tuple'          : 11,
 
219
        'unpersistable'  : 12,
167
220
 
168
221
        # PB Data Types
169
 
        'copy'           : -13,
170
 
        'cache'          : -14,
171
 
        'cached'         : -15,
172
 
        'remote'         : -16,
173
 
        'local'          : -17,
174
 
        'lcache'         : -18,
 
222
        'copy'           : 13,
 
223
        'cache'          : 14,
 
224
        'cached'         : 15,
 
225
        'remote'         : 16,
 
226
        'local'          : 17,
 
227
        'lcache'         : 18,
175
228
 
176
229
        # PB Protocol Messages
177
 
        'version'        : -19,
178
 
        'login'          : -20,
179
 
        'password'       : -21,
180
 
        'challenge'      : -22,
181
 
        'logged_in'      : -23,
182
 
        'not_logged_in'  : -24,
183
 
        'cachemessage'   : -25,
184
 
        'message'        : -26,
185
 
        'answer'         : -27,
186
 
        'error'          : -28,
187
 
        'decref'         : -29,
188
 
        'decache'        : -30,
189
 
        'uncache'        : -31,
 
230
        'version'        : 19,
 
231
        'login'          : 20,
 
232
        'password'       : 21,
 
233
        'challenge'      : 22,
 
234
        'logged_in'      : 23,
 
235
        'not_logged_in'  : 24,
 
236
        'cachemessage'   : 25,
 
237
        'message'        : 26,
 
238
        'answer'         : 27,
 
239
        'error'          : 28,
 
240
        'decref'         : 29,
 
241
        'decache'        : 30,
 
242
        'uncache'        : 31,
190
243
        }
191
244
 
192
245
    incomingVocabulary = {}
193
246
    for k, v in outgoingVocabulary.items():
194
247
        incomingVocabulary[v] = k
195
248
 
196
 
    def __init__(self):
 
249
    def __init__(self, isClient=1):
 
250
        self.listStack = []
197
251
        self.outgoingSymbols = copy.copy(self.outgoingVocabulary)
198
252
        self.outgoingSymbolCount = 0
 
253
        self.isClient = isClient
199
254
 
200
255
    def sendEncoded(self, obj):
201
256
        io = cStringIO.StringIO()
205
260
 
206
261
    def _encode(self, obj, write):
207
262
        if isinstance(obj, types.ListType) or isinstance(obj, types.TupleType):
 
263
            if len(obj) > SIZE_LIMIT:
 
264
                raise BananaError, \
 
265
                      "list/tuple is too long to send (%d)" % len(obj)
208
266
            int2b128(len(obj), write)
209
267
            write(LIST)
210
268
            for elem in obj:
227
285
            write(FLOAT)
228
286
            write(struct.pack("!d", obj))
229
287
        elif isinstance(obj, types.StringType):
230
 
            if self.outgoingSymbols.has_key(obj):
 
288
            # TODO: an API for extending banana...
 
289
            if (self.currentDialect == "pb") and self.outgoingSymbols.has_key(obj):
231
290
                symbolID = self.outgoingSymbols[obj]
232
 
                if symbolID < 0:
233
 
                    int2b128(-symbolID, write)
234
 
                    write(VOCAB)
235
 
                else:
236
 
                    int2b128(symbolID, write)
237
 
                    write(SYMBOL)
 
291
                int2b128(symbolID, write)
 
292
                write(VOCAB)
238
293
            else:
 
294
                if len(obj) > SIZE_LIMIT:
 
295
                    raise BananaError, \
 
296
                          "string is too long to send (%d)" % len(obj)
239
297
                int2b128(len(obj), write)
240
298
                write(STRING)
241
299
                write(obj)
242
300
        else:
243
 
            raise RuntimeError, "could not send object: %s" % repr(obj)
244
 
 
245
 
class Canana(Banana):
 
301
            raise BananaError, "could not send object: %s" % repr(obj)
 
302
Banana = Pynana
 
303
 
 
304
 
 
305
class Canana(Pynana):
 
306
 
246
307
    def connectionMade(self):
247
308
        self.state = cBanana.newState()
 
309
        self.cbuf = cBanana.newBuf()
 
310
        Pynana.connectionMade(self)
 
311
 
 
312
    def sendEncoded(self, obj):
 
313
        self.cbuf.clear()
 
314
        cBanana.encode(obj, self.cbuf)
 
315
        rv = self.cbuf.get()
 
316
        self.transport.write(rv)
248
317
 
249
318
    def dataReceived(self, chunk):
250
319
        buffer = self.buffer + chunk
251
 
        processed = cBanana.dataReceived(self.state, buffer, self.expressionReceived)
 
320
        processed = cBanana.dataReceived(self.state, buffer, self.callExpressionReceived)
252
321
        self.buffer = buffer[processed:]
253
322
 
254
 
Pynana = Banana
 
323
try:
 
324
    import cBanana
 
325
    cBanana.pyb1282int = b1282int
 
326
    cBanana.pyint2b128 = int2b128
 
327
except ImportError:
 
328
    pass
 
329
else:
 
330
    Banana = Canana
255
331
 
256
 
# cBanana is currently out of sync with python Banana
257
 
#try:
258
 
#    import cBanana
259
 
#except ImportError:
260
 
#    #print 'using python banana'
261
 
#    pass
262
 
#else:
263
 
#    #print 'using C banana'
264
 
#    Banana = Canana
265
332
 
266
333
# For use from the interactive interpreter
267
334
_i = Banana()
268
335
_i.connectionMade()
 
336
_i._selectDialect("none")
 
337
 
269
338
 
270
339
def encode(lst):
 
340
    """Encode a list s-expression."""
271
341
    io = cStringIO.StringIO()
272
 
    _i._encode(lst, io.write)
 
342
    _i.transport = io
 
343
    _i.sendEncoded(lst)
273
344
    return io.getvalue()
274
345
 
 
346
 
275
347
def decode(st):
 
348
    """Decode a banana-encoded string."""
276
349
    l=[]
277
350
    _i.expressionReceived = l.append
278
351
    _i.dataReceived(st)