aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py3/twisted/spread/banana.py
diff options
context:
space:
mode:
authorshmel1k <shmel1k@ydb.tech>2023-11-26 18:16:14 +0300
committershmel1k <shmel1k@ydb.tech>2023-11-26 18:43:30 +0300
commitb8cf9e88f4c5c64d9406af533d8948deb050d695 (patch)
tree218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py3/twisted/spread/banana.py
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
downloadydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py3/twisted/spread/banana.py')
-rw-r--r--contrib/python/Twisted/py3/twisted/spread/banana.py403
1 files changed, 403 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py3/twisted/spread/banana.py b/contrib/python/Twisted/py3/twisted/spread/banana.py
new file mode 100644
index 0000000000..ee54c2e2a9
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/spread/banana.py
@@ -0,0 +1,403 @@
+# -*- 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
+"""
+
+
+import copy
+import 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
+from twisted.python.reflect import fullyQualifiedName
+
+
+class BananaError(Exception):
+ pass
+
+
+def int2b128(integer, stream):
+ if integer == 0:
+ stream(b"\0")
+ return
+ assert integer > 0, "can only encode positive integers"
+ while integer:
+ stream(bytes((integer & 0x7F,)))
+ integer = integer >> 7
+
+
+def b1282int(st):
+ """
+ Convert an integer represented as a base 128 string into an L{int}.
+
+ @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}
+ """
+ e = 1
+ i = 0
+ for char in iterbytes(st):
+ n = ord(char)
+ i += n * e
+ e <<= 7
+ return i
+
+
+# delimiter characters.
+LIST = b"\x80"
+INT = b"\x81"
+STRING = b"\x82"
+NEG = b"\x83"
+FLOAT = b"\x84"
+# "optional" -- these might be refused by a low-level implementation.
+LONGINT = b"\x85"
+LONGNEG = b"\x86"
+# really optional; this is part of the 'pb' vocabulary
+VOCAB = b"\x87"
+
+HIGH_BIT_SET = b"\x80"
+
+
+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: {} {}".format(
+ 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(f"Invalid item for pb protocol {item!r}")
+ elif typebyte == FLOAT:
+ if len(rest) >= 8:
+ buffer = rest[8:]
+ gotItem(struct.unpack("!d", rest[:8])[0])
+ else:
+ return
+ else:
+ raise NotImplementedError(f"Invalid Type Byte {typebyte!r}")
+ 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):
+ if obj < self._smallestLongInt or obj > self._largestLongInt:
+ raise BananaError("int 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 {} objects: {!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]