From a5f57a728555f13984af80554b095f1a93155870 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Tue, 26 Jan 2016 15:11:51 -0700 Subject: [PATCH 1/7] Replaced *args with args. *args caused dictionary packets to get stripped of values and as a result, keys were only left, which is useless. --- socketIO_client/__init__.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index 44b4444..2a25218 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -79,7 +79,7 @@ def on_event(self, event, *args): """ callback, args = find_callback(args) if callback: - callback(*args) + callback(args) def on_error(self, reason, advice): 'Called after server sends an error; you can override this method' @@ -117,7 +117,7 @@ def _find_event_callback(self, event): return getattr( self, 'on_' + event.replace(' ', '_'), - lambda *args: self.on_event(event, *args)) + lambda *args: self.on_event(event, args)) class LoggingNamespace(BaseNamespace): @@ -148,7 +148,7 @@ def on_event(self, event, *args): arguments.append('callback(*args)') self._log(logging.INFO, '%s [event] %s(%s)', self.path, event, ', '.join(arguments)) - super(LoggingNamespace, self).on_event(event, *args) + super(LoggingNamespace, self).on_event(event, args) def on_error(self, reason, advice): self._log(logging.INFO, '%s [error] %s', self.path, advice) @@ -422,26 +422,27 @@ def _on_heartbeat(self, packet, find_event_callback): def _on_message(self, packet, find_event_callback): code, packet_id, path, data = packet - args = [data] + args = data + self._send_args('message', args, path, packet_id, find_event_callback) + + def _send_args(self, event, args, path, packet_id, find_event_callback): + ev_args = [args] if packet_id: - args.append(self._prepare_to_send_ack(path, packet_id)) - find_event_callback('message')(*args) + ack = self._prepare_to_send_ack(path, packet_id) + ev_args.append(ack) + find_event_callback(event)(*ev_args) def _on_json(self, packet, find_event_callback): code, packet_id, path, data = packet - args = [json.loads(data)] - if packet_id: - args.append(self._prepare_to_send_ack(path, packet_id)) - find_event_callback('message')(*args) + args = json.loads(data) + self._send_args('message', args, path, packet_id, find_event_callback) def _on_event(self, packet, find_event_callback): code, packet_id, path, data = packet value_by_name = json.loads(data) event = value_by_name['name'] args = value_by_name.get('args', []) - if packet_id: - args.append(self._prepare_to_send_ack(path, packet_id)) - find_event_callback(event)(*args) + self._send_args(event, args, path, packet_id, find_event_callback) def _on_ack(self, packet, find_event_callback): code, packet_id, path, data = packet @@ -452,7 +453,7 @@ def _on_ack(self, packet, find_event_callback): except KeyError: return args = json.loads(data_parts[1]) if len(data_parts) > 1 else [] - ack_callback(*args) + ack_callback(args) def _on_error(self, packet, find_event_callback): code, packet_id, path, data = packet From 8337e7bae6724bc7563ee21c0f332a843949f320 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Tue, 26 Jan 2016 16:35:11 -0700 Subject: [PATCH 2/7] Possibly fixed unhandled namespace. --- socketIO_client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index 2a25218..fc1bcee 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -274,7 +274,7 @@ def _process_events(self, timeout=None): def _process_packet(self, packet): code, packet_id, path, data = packet - namespace = self.get_namespace(path) + namespace = self.get_namespace(path or '') delegate = self._get_delegate(code) delegate(packet, namespace._find_event_callback) From 7dca8f9ba4e220045848c30d3c56af5bbb752378 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Wed, 27 Jan 2016 12:26:54 -0700 Subject: [PATCH 3/7] Heartbeat was incorrectly set and resulted in disconnects on certain server implementations. --- socketIO_client/__init__.py | 1 + socketIO_client/transports.py | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index fc1bcee..5a06868 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -407,6 +407,7 @@ def _get_delegate(self, code): '6': self._on_ack, '7': self._on_error, '8': self._on_noop, + '': self._on_noop }[code] except KeyError: raise PacketError('unexpected code (%s)' % code) diff --git a/socketIO_client/transports.py b/socketIO_client/transports.py index 21e046e..215c838 100644 --- a/socketIO_client/transports.py +++ b/socketIO_client/transports.py @@ -85,6 +85,8 @@ def noop(self, path=''): def send_packet(self, code, path='', data='', callback=None): packet_id = self.set_ack_callback(callback) if callback else '' packet_parts = str(code), packet_id, path, encode_unicode(data) + if not data: + packet_parts = packet_parts[:-1] packet_text = ':'.join(packet_parts) self.send(packet_text) self._log(logging.DEBUG, '[packet sent] %s', packet_text) @@ -143,6 +145,8 @@ def __init__(self, socketIO_session, is_secure, base_url, **kw): http_session = _prepare_http_session(kw) req = http_session.prepare_request(requests.Request('GET', url)) headers = ['%s: %s' % item for item in req.headers.items()] + headers.append('Connection: keep-alive') + try: self._connection = websocket.create_connection(url, header=headers) except socket.timeout as e: @@ -318,10 +322,10 @@ def _get_response(request, *args, **kw): response = request(*args, **kw) except requests.exceptions.Timeout as e: raise TimeoutError(e) - except requests.exceptions.ConnectionError as e: - raise ConnectionError(e) except requests.exceptions.SSLError as e: raise ConnectionError('could not negotiate SSL (%s)' % e) + except requests.exceptions.ConnectionError as e: + raise ConnectionError(e) status = response.status_code if 200 != status: raise ConnectionError('unexpected status code (%s)' % status) From 1748e836bc291f344176d3b8f6049fa2edb12152 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Wed, 27 Jan 2016 14:39:30 -0700 Subject: [PATCH 4/7] Ignore code errors. --- socketIO_client/__init__.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index 5a06868..aafb335 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -315,7 +315,7 @@ def _transport(self): try: if self.connected: return self.__transport - except AttributeError: + except AttributeError as e: pass socketIO_session = self._get_socketIO_session() supported_transports = self._get_supported_transports(socketIO_session) @@ -396,21 +396,18 @@ def get_namespace(self, path=''): raise PacketError('unhandled namespace path (%s)' % path) def _get_delegate(self, code): - try: - return { - '0': self._on_disconnect, - '1': self._on_connect, - '2': self._on_heartbeat, - '3': self._on_message, - '4': self._on_json, - '5': self._on_event, - '6': self._on_ack, - '7': self._on_error, - '8': self._on_noop, - '': self._on_noop - }[code] - except KeyError: - raise PacketError('unexpected code (%s)' % code) + return { + '0': self._on_disconnect, + '1': self._on_connect, + '2': self._on_heartbeat, + '3': self._on_message, + '4': self._on_json, + '5': self._on_event, + '6': self._on_ack, + '7': self._on_error, + '8': self._on_noop, + '': self._on_noop + }.get(code, self._on_noop) def _on_disconnect(self, packet, find_event_callback): find_event_callback('disconnect')() From c80a2db6653a11a9026f8a00ca7302e373177df7 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Wed, 27 Jan 2016 15:32:52 -0700 Subject: [PATCH 5/7] Fixed multiple packets being sent at once causing a json error. --- socketIO_client/__init__.py | 31 +++++++++++++----------- socketIO_client/transports.py | 45 ++++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index aafb335..1946323 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -275,7 +275,7 @@ def _process_events(self, timeout=None): def _process_packet(self, packet): code, packet_id, path, data = packet namespace = self.get_namespace(path or '') - delegate = self._get_delegate(code) + delegate = self._get_delegate(code, packet) delegate(packet, namespace._find_event_callback) def _stop_waiting(self, for_callbacks): @@ -395,19 +395,22 @@ def get_namespace(self, path=''): except KeyError: raise PacketError('unhandled namespace path (%s)' % path) - def _get_delegate(self, code): - return { - '0': self._on_disconnect, - '1': self._on_connect, - '2': self._on_heartbeat, - '3': self._on_message, - '4': self._on_json, - '5': self._on_event, - '6': self._on_ack, - '7': self._on_error, - '8': self._on_noop, - '': self._on_noop - }.get(code, self._on_noop) + def _get_delegate(self, code, packet): + try: + return { + '0': self._on_disconnect, + '1': self._on_connect, + '2': self._on_heartbeat, + '3': self._on_message, + '4': self._on_json, + '5': self._on_event, + '6': self._on_ack, + '7': self._on_error, + '8': self._on_noop, + '': self._on_noop + }[code] + except KeyError: + raise PacketError('unexpected code ({}): {}'.format([code], packet)) def _on_disconnect(self, packet, find_event_callback): find_event_callback('disconnect')() diff --git a/socketIO_client/transports.py b/socketIO_client/transports.py index 215c838..e479cd1 100644 --- a/socketIO_client/transports.py +++ b/socketIO_client/transports.py @@ -8,6 +8,7 @@ import sys import time import websocket +import re from .exceptions import ConnectionError, TimeoutError from .symmetries import _get_text @@ -97,22 +98,34 @@ def recv_packet(self, timeout=None): yield self._packets.pop(0) except IndexError: pass - for packet_text in self.recv(timeout=timeout): - self._log(logging.DEBUG, '[packet received] %s', packet_text) - try: - packet_parts = packet_text.split(':', 3) - except AttributeError: - self._log(logging.WARNING, '[packet error] %s', packet_text) - continue - code, packet_id, path, data = None, None, None, None - packet_count = len(packet_parts) - if 4 == packet_count: - code, packet_id, path, data = packet_parts - elif 3 == packet_count: - code, packet_id, path = packet_parts - elif 1 == packet_count: - code = packet_parts[0] - yield code, packet_id, path, data + for packet_texts in self.recv(timeout=timeout): + #remove packet separator + packet_texts = re.sub('^\xef\xbf\xbd\w+\xef\xbf\xbd', '', + packet_texts) + + packets = packet_texts.split('\xef\xbf\xbd')[::2] + + for packet_text in packets: + self._log(logging.DEBUG, '[packet received] %s', packet_text) + sep_count = packet_text.count('\xef\xbf\xbd')/2 + + try: + packet_parts = packet_text.split(':', 3) + except AttributeError: + self._log(logging.WARNING, '[packet error] %s', packet_text) + continue + code, packet_id, path, data = None, None, None, None + packet_count = len(packet_parts) + if 4 == packet_count: + code, packet_id, path, data = packet_parts + elif 3 == packet_count: + code, packet_id, path = packet_parts + elif 1 == packet_count: + code = packet_parts[0] + if code and len(code) > 1: + code = code[-1] + + yield code, packet_id, path, data def _enqueue_packet(self, packet): self._packets.append(packet) From 2c1e2ed3737ad8967294a470c9237e8388e9ded7 Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Mon, 1 Feb 2016 16:29:57 -0700 Subject: [PATCH 6/7] Raise error on connection lost. --- socketIO_client/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index 1946323..2fe82fe 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -237,7 +237,7 @@ def emit(self, event, *args, **kw): callback, args = find_callback(args, kw) self._transport.emit(path, event, args, callback) - def wait(self, seconds=None, for_callbacks=False): + def wait(self, seconds=None, for_callbacks=False, raise_error=False): """Wait in a loop and process events as defined in the namespaces. - Omit seconds, i.e. call wait() without arguments, to wait forever. @@ -254,6 +254,8 @@ def wait(self, seconds=None, for_callbacks=False): pass next(self._heartbeat_pacemaker) except ConnectionError as e: + if raise_error: + raise try: warning = Exception('[connection error] %s' % e) warning_screen.throw(warning) From 94b5ce7ed383e41f72e6cc174e27c86b7739971b Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Mon, 1 Feb 2016 16:36:00 -0700 Subject: [PATCH 7/7] Added function on wait error. --- socketIO_client/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/socketIO_client/__init__.py b/socketIO_client/__init__.py index 2fe82fe..208e139 100644 --- a/socketIO_client/__init__.py +++ b/socketIO_client/__init__.py @@ -237,7 +237,7 @@ def emit(self, event, *args, **kw): callback, args = find_callback(args, kw) self._transport.emit(path, event, args, callback) - def wait(self, seconds=None, for_callbacks=False, raise_error=False): + def wait(self, seconds=None, for_callbacks=False, on_error=None, error_args=None): """Wait in a loop and process events as defined in the namespaces. - Omit seconds, i.e. call wait() without arguments, to wait forever. @@ -254,8 +254,8 @@ def wait(self, seconds=None, for_callbacks=False, raise_error=False): pass next(self._heartbeat_pacemaker) except ConnectionError as e: - if raise_error: - raise + if on_error is not None and callable(on_error): + on_error(e, error_args) try: warning = Exception('[connection error] %s' % e) warning_screen.throw(warning)