1import json 2from collections import namedtuple 3 4from .symmetries import ( 5 decode_string, encode_string, get_byte, get_character, parse_url) 6 7 8EngineIOSession = namedtuple('EngineIOSession', [ 9 'id', 'ping_interval', 'ping_timeout', 'transport_upgrades']) 10SocketIOData = namedtuple('SocketIOData', ['path', 'ack_id', 'args']) 11 12 13def parse_host(host, port, resource): 14 if not host.startswith('http'): 15 host = 'http://' + host 16 url_pack = parse_url(host) 17 is_secure = url_pack.scheme == 'https' 18 port = port or url_pack.port or (443 if is_secure else 80) 19 url = '%s:%s%s/%s' % (url_pack.hostname, port, url_pack.path, resource) 20 return is_secure, url 21 22 23def parse_engineIO_session(engineIO_packet_data): 24 d = json.loads(decode_string(engineIO_packet_data)) 25 return EngineIOSession( 26 id=d['sid'], 27 ping_interval=d['pingInterval'] / float(1000), 28 ping_timeout=d['pingTimeout'] / float(1000), 29 transport_upgrades=d['upgrades']) 30 31 32def encode_engineIO_content(engineIO_packets): 33 content = bytearray() 34 for packet_type, packet_data in engineIO_packets: 35 packet_text = format_packet_text(packet_type, packet_data) 36 content.extend(_make_packet_prefix(packet_text) + packet_text) 37 return content 38 39 40def decode_engineIO_content(content): 41 content_index = 0 42 content_length = len(content) 43 while content_index < content_length: 44 try: 45 content_index, packet_length = _read_packet_length( 46 content, content_index) 47 except IndexError: 48 break 49 content_index, packet_text = _read_packet_text( 50 content, content_index, packet_length) 51 engineIO_packet_type, engineIO_packet_data = parse_packet_text( 52 packet_text) 53 yield engineIO_packet_type, engineIO_packet_data 54 55 56def format_socketIO_packet_data(path=None, ack_id=None, args=None): 57 socketIO_packet_data = json.dumps(args, ensure_ascii=False) if args else '' 58 if ack_id is not None: 59 socketIO_packet_data = str(ack_id) + socketIO_packet_data 60 if path: 61 socketIO_packet_data = path + ',' + socketIO_packet_data 62 return socketIO_packet_data 63 64 65def parse_socketIO_packet_data(socketIO_packet_data): 66 data = decode_string(socketIO_packet_data) 67 if data.startswith('/'): 68 try: 69 path, data = data.split(',', 1) 70 except ValueError: 71 path = data 72 data = '' 73 else: 74 path = '' 75 try: 76 ack_id_string, data = data.split('[', 1) 77 data = '[' + data 78 ack_id = int(ack_id_string) 79 except (ValueError, IndexError): 80 ack_id = None 81 try: 82 args = json.loads(data) 83 except ValueError: 84 args = [] 85 return SocketIOData(path=path, ack_id=ack_id, args=args) 86 87 88def format_packet_text(packet_type, packet_data): 89 return encode_string(str(packet_type) + packet_data) 90 91 92def parse_packet_text(packet_text): 93 packet_type = int(get_character(packet_text, 0)) 94 packet_data = packet_text[1:] 95 return packet_type, packet_data 96 97 98def get_namespace_path(socketIO_packet_data): 99 if not socketIO_packet_data.startswith(b'/'): 100 return '' 101 # Loop incrementally in case there is binary data 102 parts = [] 103 for i in range(len(socketIO_packet_data)): 104 character = get_character(socketIO_packet_data, i) 105 if ',' == character: 106 break 107 parts.append(character) 108 return ''.join(parts) 109 110 111def _make_packet_prefix(packet): 112 length_string = str(len(packet)) 113 header_digits = bytearray([0]) 114 for i in range(len(length_string)): 115 header_digits.append(ord(length_string[i]) - 48) 116 header_digits.append(255) 117 return header_digits 118 119 120def _read_packet_length(content, content_index): 121 while get_byte(content, content_index) != 0: 122 content_index += 1 123 content_index += 1 124 packet_length_string = '' 125 byte = get_byte(content, content_index) 126 while byte != 255: 127 packet_length_string += str(byte) 128 content_index += 1 129 byte = get_byte(content, content_index) 130 return content_index, int(packet_length_string) 131 132 133def _read_packet_text(content, content_index, packet_length): 134 while get_byte(content, content_index) == 255: 135 content_index += 1 136 packet_text = content[content_index:content_index + packet_length] 137 return content_index + packet_length, packet_text 138