Ausgabe der neuen DB Einträge
This commit is contained in:
parent
bad48e1627
commit
cfbbb9ee3d
2399 changed files with 843193 additions and 43 deletions
398
venv/lib/python3.9/site-packages/twisted/spread/banana.py
Normal file
398
venv/lib/python3.9/site-packages/twisted/spread/banana.py
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
# -*- test-case-name: twisted.spread.test.test_banana -*-
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Banana -- s-exp based protocol.
|
||||
|
||||
Future Plans: This module is almost entirely stable. The same caveat applies
|
||||
to it as applies to L{twisted.spread.jelly}, however. Read its future plans
|
||||
for more details.
|
||||
|
||||
@author: Glyph Lefkowitz
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division
|
||||
|
||||
import copy, struct
|
||||
from io import BytesIO
|
||||
|
||||
from twisted.internet import protocol
|
||||
from twisted.persisted import styles
|
||||
from twisted.python import log
|
||||
from twisted.python.compat import iterbytes, long, _bytesChr as chr
|
||||
from twisted.python.reflect import fullyQualifiedName
|
||||
|
||||
class BananaError(Exception):
|
||||
pass
|
||||
|
||||
def int2b128(integer, stream):
|
||||
if integer == 0:
|
||||
stream(chr(0))
|
||||
return
|
||||
assert integer > 0, "can only encode positive integers"
|
||||
while integer:
|
||||
stream(chr(integer & 0x7f))
|
||||
integer = integer >> 7
|
||||
|
||||
|
||||
def b1282int(st):
|
||||
"""
|
||||
Convert an integer represented as a base 128 string into an L{int} or
|
||||
L{long}.
|
||||
|
||||
@param st: The integer encoded in a byte string.
|
||||
@type st: L{bytes}
|
||||
|
||||
@return: The integer value extracted from the byte string.
|
||||
@rtype: L{int} or L{long}
|
||||
"""
|
||||
e = 1
|
||||
i = 0
|
||||
for char in iterbytes(st):
|
||||
n = ord(char)
|
||||
i += (n * e)
|
||||
e <<= 7
|
||||
return i
|
||||
|
||||
|
||||
# delimiter characters.
|
||||
LIST = chr(0x80)
|
||||
INT = chr(0x81)
|
||||
STRING = chr(0x82)
|
||||
NEG = chr(0x83)
|
||||
FLOAT = chr(0x84)
|
||||
# "optional" -- these might be refused by a low-level implementation.
|
||||
LONGINT = chr(0x85)
|
||||
LONGNEG = chr(0x86)
|
||||
# really optional; this is part of the 'pb' vocabulary
|
||||
VOCAB = chr(0x87)
|
||||
|
||||
HIGH_BIT_SET = chr(0x80)
|
||||
|
||||
def setPrefixLimit(limit):
|
||||
"""
|
||||
Set the limit on the prefix length for all Banana connections
|
||||
established after this call.
|
||||
|
||||
The prefix length limit determines how many bytes of prefix a banana
|
||||
decoder will allow before rejecting a potential object as too large.
|
||||
|
||||
@type limit: L{int}
|
||||
@param limit: The number of bytes of prefix for banana to allow when
|
||||
decoding.
|
||||
"""
|
||||
global _PREFIX_LIMIT
|
||||
_PREFIX_LIMIT = limit
|
||||
|
||||
_PREFIX_LIMIT = None
|
||||
setPrefixLimit(64)
|
||||
|
||||
SIZE_LIMIT = 640 * 1024 # 640k is all you'll ever need :-)
|
||||
|
||||
class Banana(protocol.Protocol, styles.Ephemeral):
|
||||
"""
|
||||
L{Banana} implements the I{Banana} s-expression protocol, client and
|
||||
server.
|
||||
|
||||
@ivar knownDialects: These are the profiles supported by this Banana
|
||||
implementation.
|
||||
@type knownDialects: L{list} of L{bytes}
|
||||
"""
|
||||
|
||||
# The specification calls these profiles but this implementation calls them
|
||||
# dialects instead.
|
||||
knownDialects = [b"pb", b"none"]
|
||||
|
||||
prefixLimit = None
|
||||
sizeLimit = SIZE_LIMIT
|
||||
|
||||
def setPrefixLimit(self, limit):
|
||||
"""
|
||||
Set the prefix limit for decoding done by this protocol instance.
|
||||
|
||||
@see: L{setPrefixLimit}
|
||||
"""
|
||||
self.prefixLimit = limit
|
||||
self._smallestLongInt = -2 ** (limit * 7) + 1
|
||||
self._smallestInt = -2 ** 31
|
||||
self._largestInt = 2 ** 31 - 1
|
||||
self._largestLongInt = 2 ** (limit * 7) - 1
|
||||
|
||||
|
||||
def connectionReady(self):
|
||||
"""Surrogate for connectionMade
|
||||
Called after protocol negotiation.
|
||||
"""
|
||||
|
||||
|
||||
def _selectDialect(self, dialect):
|
||||
self.currentDialect = dialect
|
||||
self.connectionReady()
|
||||
|
||||
|
||||
def callExpressionReceived(self, obj):
|
||||
if self.currentDialect:
|
||||
self.expressionReceived(obj)
|
||||
else:
|
||||
# this is the first message we've received
|
||||
if self.isClient:
|
||||
# if I'm a client I have to respond
|
||||
for serverVer in obj:
|
||||
if serverVer in self.knownDialects:
|
||||
self.sendEncoded(serverVer)
|
||||
self._selectDialect(serverVer)
|
||||
break
|
||||
else:
|
||||
# I can't speak any of those dialects.
|
||||
log.msg("The client doesn't speak any of the protocols "
|
||||
"offered by the server: disconnecting.")
|
||||
self.transport.loseConnection()
|
||||
else:
|
||||
if obj in self.knownDialects:
|
||||
self._selectDialect(obj)
|
||||
else:
|
||||
# the client just selected a protocol that I did not suggest.
|
||||
log.msg("The client selected a protocol the server didn't "
|
||||
"suggest and doesn't know: disconnecting.")
|
||||
self.transport.loseConnection()
|
||||
|
||||
|
||||
def connectionMade(self):
|
||||
self.setPrefixLimit(_PREFIX_LIMIT)
|
||||
self.currentDialect = None
|
||||
if not self.isClient:
|
||||
self.sendEncoded(self.knownDialects)
|
||||
|
||||
|
||||
def gotItem(self, item):
|
||||
l = self.listStack
|
||||
if l:
|
||||
l[-1][1].append(item)
|
||||
else:
|
||||
self.callExpressionReceived(item)
|
||||
|
||||
buffer = b''
|
||||
|
||||
def dataReceived(self, chunk):
|
||||
buffer = self.buffer + chunk
|
||||
listStack = self.listStack
|
||||
gotItem = self.gotItem
|
||||
while buffer:
|
||||
assert self.buffer != buffer, "This ain't right: %s %s" % (repr(self.buffer), repr(buffer))
|
||||
self.buffer = buffer
|
||||
pos = 0
|
||||
for ch in iterbytes(buffer):
|
||||
if ch >= HIGH_BIT_SET:
|
||||
break
|
||||
pos = pos + 1
|
||||
else:
|
||||
if pos > self.prefixLimit:
|
||||
raise BananaError("Security precaution: more than %d bytes of prefix" % (self.prefixLimit,))
|
||||
return
|
||||
num = buffer[:pos]
|
||||
typebyte = buffer[pos:pos+1]
|
||||
rest = buffer[pos+1:]
|
||||
if len(num) > self.prefixLimit:
|
||||
raise BananaError("Security precaution: longer than %d bytes worth of prefix" % (self.prefixLimit,))
|
||||
if typebyte == LIST:
|
||||
num = b1282int(num)
|
||||
if num > SIZE_LIMIT:
|
||||
raise BananaError("Security precaution: List too long.")
|
||||
listStack.append((num, []))
|
||||
buffer = rest
|
||||
elif typebyte == STRING:
|
||||
num = b1282int(num)
|
||||
if num > SIZE_LIMIT:
|
||||
raise BananaError("Security precaution: String too long.")
|
||||
if len(rest) >= num:
|
||||
buffer = rest[num:]
|
||||
gotItem(rest[:num])
|
||||
else:
|
||||
return
|
||||
elif typebyte == INT:
|
||||
buffer = rest
|
||||
num = b1282int(num)
|
||||
gotItem(num)
|
||||
elif typebyte == LONGINT:
|
||||
buffer = rest
|
||||
num = b1282int(num)
|
||||
gotItem(num)
|
||||
elif typebyte == LONGNEG:
|
||||
buffer = rest
|
||||
num = b1282int(num)
|
||||
gotItem(-num)
|
||||
elif typebyte == NEG:
|
||||
buffer = rest
|
||||
num = -b1282int(num)
|
||||
gotItem(num)
|
||||
elif typebyte == VOCAB:
|
||||
buffer = rest
|
||||
num = b1282int(num)
|
||||
item = self.incomingVocabulary[num]
|
||||
if self.currentDialect == b'pb':
|
||||
# the sender issues VOCAB only for dialect pb
|
||||
gotItem(item)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"Invalid item for pb protocol {0!r}".format(item))
|
||||
elif typebyte == FLOAT:
|
||||
if len(rest) >= 8:
|
||||
buffer = rest[8:]
|
||||
gotItem(struct.unpack("!d", rest[:8])[0])
|
||||
else:
|
||||
return
|
||||
else:
|
||||
raise NotImplementedError(("Invalid Type Byte %r" % (typebyte,)))
|
||||
while listStack and (len(listStack[-1][1]) == listStack[-1][0]):
|
||||
item = listStack.pop()[1]
|
||||
gotItem(item)
|
||||
self.buffer = b''
|
||||
|
||||
|
||||
def expressionReceived(self, lst):
|
||||
"""Called when an expression (list, string, or int) is received.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
outgoingVocabulary = {
|
||||
# Jelly Data Types
|
||||
b'None' : 1,
|
||||
b'class' : 2,
|
||||
b'dereference' : 3,
|
||||
b'reference' : 4,
|
||||
b'dictionary' : 5,
|
||||
b'function' : 6,
|
||||
b'instance' : 7,
|
||||
b'list' : 8,
|
||||
b'module' : 9,
|
||||
b'persistent' : 10,
|
||||
b'tuple' : 11,
|
||||
b'unpersistable' : 12,
|
||||
|
||||
# PB Data Types
|
||||
b'copy' : 13,
|
||||
b'cache' : 14,
|
||||
b'cached' : 15,
|
||||
b'remote' : 16,
|
||||
b'local' : 17,
|
||||
b'lcache' : 18,
|
||||
|
||||
# PB Protocol Messages
|
||||
b'version' : 19,
|
||||
b'login' : 20,
|
||||
b'password' : 21,
|
||||
b'challenge' : 22,
|
||||
b'logged_in' : 23,
|
||||
b'not_logged_in' : 24,
|
||||
b'cachemessage' : 25,
|
||||
b'message' : 26,
|
||||
b'answer' : 27,
|
||||
b'error' : 28,
|
||||
b'decref' : 29,
|
||||
b'decache' : 30,
|
||||
b'uncache' : 31,
|
||||
}
|
||||
|
||||
incomingVocabulary = {}
|
||||
for k, v in outgoingVocabulary.items():
|
||||
incomingVocabulary[v] = k
|
||||
|
||||
|
||||
def __init__(self, isClient=1):
|
||||
self.listStack = []
|
||||
self.outgoingSymbols = copy.copy(self.outgoingVocabulary)
|
||||
self.outgoingSymbolCount = 0
|
||||
self.isClient = isClient
|
||||
|
||||
|
||||
def sendEncoded(self, obj):
|
||||
"""
|
||||
Send the encoded representation of the given object:
|
||||
|
||||
@param obj: An object to encode and send.
|
||||
|
||||
@raise BananaError: If the given object is not an instance of one of
|
||||
the types supported by Banana.
|
||||
|
||||
@return: L{None}
|
||||
"""
|
||||
encodeStream = BytesIO()
|
||||
self._encode(obj, encodeStream.write)
|
||||
value = encodeStream.getvalue()
|
||||
self.transport.write(value)
|
||||
|
||||
|
||||
def _encode(self, obj, write):
|
||||
if isinstance(obj, (list, tuple)):
|
||||
if len(obj) > SIZE_LIMIT:
|
||||
raise BananaError(
|
||||
"list/tuple is too long to send (%d)" % (len(obj),))
|
||||
int2b128(len(obj), write)
|
||||
write(LIST)
|
||||
for elem in obj:
|
||||
self._encode(elem, write)
|
||||
elif isinstance(obj, (int, long)):
|
||||
if obj < self._smallestLongInt or obj > self._largestLongInt:
|
||||
raise BananaError(
|
||||
"int/long is too large to send (%d)" % (obj,))
|
||||
if obj < self._smallestInt:
|
||||
int2b128(-obj, write)
|
||||
write(LONGNEG)
|
||||
elif obj < 0:
|
||||
int2b128(-obj, write)
|
||||
write(NEG)
|
||||
elif obj <= self._largestInt:
|
||||
int2b128(obj, write)
|
||||
write(INT)
|
||||
else:
|
||||
int2b128(obj, write)
|
||||
write(LONGINT)
|
||||
elif isinstance(obj, float):
|
||||
write(FLOAT)
|
||||
write(struct.pack("!d", obj))
|
||||
elif isinstance(obj, bytes):
|
||||
# TODO: an API for extending banana...
|
||||
if self.currentDialect == b"pb" and obj in self.outgoingSymbols:
|
||||
symbolID = self.outgoingSymbols[obj]
|
||||
int2b128(symbolID, write)
|
||||
write(VOCAB)
|
||||
else:
|
||||
if len(obj) > SIZE_LIMIT:
|
||||
raise BananaError(
|
||||
"byte string is too long to send (%d)" % (len(obj),))
|
||||
int2b128(len(obj), write)
|
||||
write(STRING)
|
||||
write(obj)
|
||||
else:
|
||||
raise BananaError("Banana cannot send {0} objects: {1!r}".format(
|
||||
fullyQualifiedName(type(obj)), obj))
|
||||
|
||||
|
||||
# For use from the interactive interpreter
|
||||
_i = Banana()
|
||||
_i.connectionMade()
|
||||
_i._selectDialect(b"none")
|
||||
|
||||
|
||||
def encode(lst):
|
||||
"""Encode a list s-expression."""
|
||||
encodeStream = BytesIO()
|
||||
_i.transport = encodeStream
|
||||
_i.sendEncoded(lst)
|
||||
return encodeStream.getvalue()
|
||||
|
||||
|
||||
def decode(st):
|
||||
"""
|
||||
Decode a banana-encoded string.
|
||||
"""
|
||||
l = []
|
||||
_i.expressionReceived = l.append
|
||||
try:
|
||||
_i.dataReceived(st)
|
||||
finally:
|
||||
_i.buffer = b''
|
||||
del _i.expressionReceived
|
||||
return l[0]
|
||||
Loading…
Add table
Add a link
Reference in a new issue