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
18
# Generic List ENcoding
20
from twisted.protocols import protocol
19
"""Banana -- s-exp based protocol.
21
Stability: semi-stable
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
27
@author: U{Glyph Lefkowitz<mailto:glyph@twistedmatrix.com>}
30
__version__ = "$Revision: 1.37 $"[11:-2]
32
from twisted.internet import protocol
21
33
from twisted.persisted import styles
34
from twisted.python import log
22
36
import types, copy, cStringIO, struct
38
class BananaError(Exception):
24
41
def int2b128(integer, stream):
31
48
integer = integer >> 7
35
oneHundredAndTwentyEight = 128l
37
oneHundredAndTwentyEight = 128
51
oneHundredAndTwentyEight = 128l
42
56
i = i + (num * (oneHundredAndTwentyEight ** place))
49
63
# delimiter characters.
57
# This will make it easier for people writing low-level implementations.
69
# "optional" -- these might be refused by a low-level implementation.
72
# really optional; this is is part of the 'pb' vocabulary
61
75
HIGH_BIT_SET = chr(0x80)
63
class Banana(protocol.Protocol, styles.Ephemeral):
77
SIZE_LIMIT = 640 * 1024 # 640k is all you'll ever need :-)
79
class Pynana(protocol.Protocol, styles.Ephemeral):
80
knownDialects = ["pb", "none"]
82
def connectionReady(self):
83
"""Surrogate for connectionMade
84
Called after protocol negotiation.
87
def _selectDialect(self, dialect):
88
self.currentDialect = dialect
89
self.connectionReady()
91
def callExpressionReceived(self, obj):
92
if self.currentDialect:
93
self.expressionReceived(obj)
95
# this is the first message we've received
97
# if I'm a client I have to respond
99
if serverVer in self.knownDialects:
100
self.sendEncoded(serverVer)
101
self._selectDialect(serverVer)
104
# I can't speak any of those dialects.
105
log.msg('error losing')
106
self.transport.loseConnection()
108
if obj in self.knownDialects:
109
self._selectDialect(obj)
111
# the client just selected a protocol that I did not suggest.
112
log.msg('freaky losing')
113
self.transport.loseConnection()
64
116
def connectionMade(self):
117
self.currentDialect = None
118
if not self.isClient:
119
self.sendEncoded(self.knownDialects)
67
121
def gotItem(self, item):
68
122
l = self.listStack
70
124
l[-1][1].append(item)
72
self.expressionReceived(item)
126
self.callExpressionReceived(item)
90
raise Exception("Security precaution: more than 64 bytes of prefix")
144
raise BananaError("Security precaution: more than 64 bytes of prefix")
92
146
num = buffer[:pos]
93
147
typebyte = buffer[pos]
94
148
rest = buffer[pos+1:]
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)
154
raise BananaError("Security precaution: List too long.")
99
155
listStack.append((num, []))
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.")
160
raise BananaError("Security precaution: String too long.")
105
161
if len(rest) >= num:
106
162
buffer = rest[num:]
107
163
gotItem(rest[:num])
153
206
outgoingVocabulary = {
154
207
# Jelly Data Types
166
'unpersistable' : -12,
219
'unpersistable' : 12,
176
229
# PB Protocol Messages
182
'not_logged_in' : -24,
183
'cachemessage' : -25,
235
'not_logged_in' : 24,
192
245
incomingVocabulary = {}
193
246
for k, v in outgoingVocabulary.items():
194
247
incomingVocabulary[v] = k
249
def __init__(self, isClient=1):
197
251
self.outgoingSymbols = copy.copy(self.outgoingVocabulary)
198
252
self.outgoingSymbolCount = 0
253
self.isClient = isClient
200
255
def sendEncoded(self, obj):
201
256
io = cStringIO.StringIO()
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:
265
"list/tuple is too long to send (%d)" % len(obj)
208
266
int2b128(len(obj), write)
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]
233
int2b128(-symbolID, write)
236
int2b128(symbolID, write)
291
int2b128(symbolID, write)
294
if len(obj) > SIZE_LIMIT:
296
"string is too long to send (%d)" % len(obj)
239
297
int2b128(len(obj), write)
243
raise RuntimeError, "could not send object: %s" % repr(obj)
245
class Canana(Banana):
301
raise BananaError, "could not send object: %s" % repr(obj)
305
class Canana(Pynana):
246
307
def connectionMade(self):
247
308
self.state = cBanana.newState()
309
self.cbuf = cBanana.newBuf()
310
Pynana.connectionMade(self)
312
def sendEncoded(self, obj):
314
cBanana.encode(obj, self.cbuf)
316
self.transport.write(rv)
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:]
325
cBanana.pyb1282int = b1282int
326
cBanana.pyint2b128 = int2b128
256
# cBanana is currently out of sync with python Banana
260
# #print 'using python banana'
263
# #print 'using C banana'
266
333
# For use from the interactive interpreter
268
335
_i.connectionMade()
336
_i._selectDialect("none")
340
"""Encode a list s-expression."""
271
341
io = cStringIO.StringIO()
272
_i._encode(lst, io.write)
273
344
return io.getvalue()
348
"""Decode a banana-encoded string."""
277
350
_i.expressionReceived = l.append
278
351
_i.dataReceived(st)