81 lines
3.3 KiB
Python
81 lines
3.3 KiB
Python
import six
|
|
|
|
from . import packet
|
|
|
|
from six.moves import urllib
|
|
|
|
|
|
class Payload(object):
|
|
"""Engine.IO payload."""
|
|
def __init__(self, packets=None, encoded_payload=None):
|
|
self.packets = packets or []
|
|
if encoded_payload is not None:
|
|
self.decode(encoded_payload)
|
|
|
|
def encode(self, b64=False, jsonp_index=None):
|
|
"""Encode the payload for transmission."""
|
|
encoded_payload = b''
|
|
for pkt in self.packets:
|
|
encoded_packet = pkt.encode(b64=b64)
|
|
packet_len = len(encoded_packet)
|
|
if b64:
|
|
encoded_payload += str(packet_len).encode('utf-8') + b':' + \
|
|
encoded_packet
|
|
else:
|
|
binary_len = b''
|
|
while packet_len != 0:
|
|
binary_len = six.int2byte(packet_len % 10) + binary_len
|
|
packet_len = int(packet_len / 10)
|
|
if not pkt.binary:
|
|
encoded_payload += b'\0'
|
|
else:
|
|
encoded_payload += b'\1'
|
|
encoded_payload += binary_len + b'\xff' + encoded_packet
|
|
if jsonp_index is not None:
|
|
encoded_payload = b'___eio[' + \
|
|
str(jsonp_index).encode() + \
|
|
b']("' + \
|
|
encoded_payload.replace(b'"', b'\\"') + \
|
|
b'");'
|
|
return encoded_payload
|
|
|
|
def decode(self, encoded_payload):
|
|
"""Decode a transmitted payload."""
|
|
self.packets = []
|
|
while encoded_payload:
|
|
# JSONP POST payload starts with 'd='
|
|
if encoded_payload.startswith(b'd='):
|
|
encoded_payload = urllib.parse.parse_qs(
|
|
encoded_payload)[b'd'][0]
|
|
|
|
if six.byte2int(encoded_payload[0:1]) <= 1:
|
|
packet_len = 0
|
|
i = 1
|
|
while six.byte2int(encoded_payload[i:i + 1]) != 255:
|
|
packet_len = packet_len * 10 + six.byte2int(
|
|
encoded_payload[i:i + 1])
|
|
i += 1
|
|
self.packets.append(packet.Packet(
|
|
encoded_packet=encoded_payload[i + 1:i + 1 + packet_len]))
|
|
else:
|
|
i = encoded_payload.find(b':')
|
|
if i == -1:
|
|
raise ValueError('invalid payload')
|
|
|
|
# extracting the packet out of the payload is extremely
|
|
# inefficient, because the payload needs to be treated as
|
|
# binary, but the non-binary packets have to be parsed as
|
|
# unicode. Luckily this complication only applies to long
|
|
# polling, as the websocket transport sends packets
|
|
# individually wrapped.
|
|
packet_len = int(encoded_payload[0:i])
|
|
pkt = encoded_payload.decode('utf-8', errors='ignore')[
|
|
i + 1: i + 1 + packet_len].encode('utf-8')
|
|
self.packets.append(packet.Packet(encoded_packet=pkt))
|
|
|
|
# the engine.io protocol sends the packet length in
|
|
# utf-8 characters, but we need it in bytes to be able to
|
|
# jump to the next packet in the payload
|
|
packet_len = len(pkt)
|
|
encoded_payload = encoded_payload[i + 1 + packet_len:]
|