mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-08-10 18:59:33 +00:00
Completely overhaul connections and transports
Reduce abstraction leaks. Now the transport can hold any state, rather than just the tag. It's also responsible to initialize on the first connection, and they can be cleanly reset. asyncio connections are no longer used, in favour of raw sockets, which should avoid some annoyances. For the time being, more obscure transport modes have been removed, as well as proxy support, until further cleaning is done.
This commit is contained in:
4
telethon/_network/transports/__init__.py
Normal file
4
telethon/_network/transports/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .transport import Transport
|
||||
from .abridged import Abridged
|
||||
from .full import Full
|
||||
from .intermediate import Intermediate
|
43
telethon/_network/transports/abridged.py
Normal file
43
telethon/_network/transports/abridged.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from .transport import Transport
|
||||
import struct
|
||||
|
||||
|
||||
class Abridged(Transport):
|
||||
def __init__(self):
|
||||
self._init = False
|
||||
|
||||
def recreate_fresh(self):
|
||||
return type(self)()
|
||||
|
||||
def pack(self, input: bytes) -> bytes:
|
||||
if self._init:
|
||||
header = b''
|
||||
else:
|
||||
header = b'\xef'
|
||||
self._init = True
|
||||
|
||||
length = len(data) >> 2
|
||||
if length < 127:
|
||||
length = struct.pack('B', length)
|
||||
else:
|
||||
length = b'\x7f' + int.to_bytes(length, 3, 'little')
|
||||
|
||||
return header + length + data
|
||||
|
||||
def unpack(self, input: bytes) -> (int, bytes):
|
||||
if len(input) < 4:
|
||||
raise EOFError()
|
||||
|
||||
length = input[0]
|
||||
if length < 127:
|
||||
offset = 1
|
||||
else:
|
||||
offset = 4
|
||||
length = struct.unpack('<i', input[1:4] + b'\0')[0]
|
||||
|
||||
length = (length << 2) + offset
|
||||
|
||||
if len(input) < length:
|
||||
raise EOFError()
|
||||
|
||||
return length, input[offset:length]
|
41
telethon/_network/transports/full.py
Normal file
41
telethon/_network/transports/full.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from .transport import Transport
|
||||
import struct
|
||||
from zlib import crc32
|
||||
|
||||
|
||||
class Full(Transport):
|
||||
def __init__(self):
|
||||
self._send_counter = 0
|
||||
self._recv_counter = 0
|
||||
|
||||
def recreate_fresh(self):
|
||||
return type(self)()
|
||||
|
||||
def pack(self, input: bytes) -> bytes:
|
||||
# https://core.telegram.org/mtproto#tcp-transport
|
||||
length = len(input) + 12
|
||||
data = struct.pack('<ii', length, self._send_counter) + input
|
||||
crc = struct.pack('<I', crc32(data))
|
||||
self._send_counter += 1
|
||||
return data + crc
|
||||
|
||||
def unpack(self, input: bytes) -> (int, bytes):
|
||||
if len(input) < 12:
|
||||
raise EOFError()
|
||||
|
||||
length, seq = struct.unpack('<ii', input[:8])
|
||||
if len(input) < length:
|
||||
raise EOFError()
|
||||
|
||||
if seq != self._recv_counter:
|
||||
raise ValueError(f'expected sequence value {self._recv_counter!r}, got {seq!r}')
|
||||
|
||||
body = input[8:length - 4]
|
||||
checksum = struct.unpack('<I', input[length - 4:length])[0]
|
||||
|
||||
valid_checksum = crc32(input[:length - 4])
|
||||
if checksum != valid_checksum:
|
||||
raise InvalidChecksumError(checksum, valid_checksum)
|
||||
|
||||
self._recv_counter += 1
|
||||
return length, body
|
29
telethon/_network/transports/intermediate.py
Normal file
29
telethon/_network/transports/intermediate.py
Normal file
@@ -0,0 +1,29 @@
|
||||
from .transport import Transport
|
||||
import struct
|
||||
|
||||
|
||||
class Intermediate(Transport):
|
||||
def __init__(self):
|
||||
self._init = False
|
||||
|
||||
def recreate_fresh(self):
|
||||
return type(self)()
|
||||
|
||||
def pack(self, input: bytes) -> bytes:
|
||||
if self._init:
|
||||
header = b''
|
||||
else:
|
||||
header = b'\xee\xee\xee\xee'
|
||||
self._init = True
|
||||
|
||||
return header + struct.pack('<i', len(data)) + data
|
||||
|
||||
def unpack(self, input: bytes) -> (int, bytes):
|
||||
if len(input) < 4:
|
||||
raise EOFError()
|
||||
|
||||
length = struct.unpack('<i', input[:4])[0] + 4
|
||||
if len(input) < length:
|
||||
raise EOFError()
|
||||
|
||||
return length, input[4:length]
|
17
telethon/_network/transports/transport.py
Normal file
17
telethon/_network/transports/transport.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import abc
|
||||
|
||||
|
||||
class Transport(abc.ABC):
|
||||
# Should return a newly-created instance of itself
|
||||
@abc.abstractmethod
|
||||
def recreate_fresh(self):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def pack(self, input: bytes) -> bytes:
|
||||
pass
|
||||
|
||||
# Should raise EOFError if it does not have enough bytes
|
||||
@abc.abstractmethod
|
||||
def unpack(self, input: bytes) -> (int, bytes):
|
||||
pass
|
Reference in New Issue
Block a user