Control: tags 808010 + patch Control: tags 808010 + pending Dear maintainer,
Since python-socketio-client has no reverse dependencies in Debian, I've prepared an NMU for python-socketio-client (versioned as 0.6.5-0.1) and uploaded it to DELAYED/7. Please feel free to tell me if I should delay it longer or cancel it altogether. Full source debdiff attached. Regards, Apollon
diff -Nru python-socketio-client-0.5.3/CHANGES.rst python-socketio-client-0.6.5/CHANGES.rst --- python-socketio-client-0.5.3/CHANGES.rst 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/CHANGES.rst 2015-06-01 23:16:32.000000000 +0300 @@ -1,3 +1,33 @@ +0.6.5 +----- +- Updated wait loop to be more responsive under websocket transport + +0.6.4 +----- +- Fixed support for Python 3 +- Fixed thread cleanup + +0.6.3 +----- +- Upgraded to socket.io protocol 1.x for websocket transport +- Added locks to fix concurrency issues with polling transport +- Fixed SSL support + +0.6.1 +----- +- Upgraded to socket.io protocol 1.x thanks to Sean Arietta and Joe Palmer + +0.5.6 +----- +- Backported to support requests 0.8.2 + +0.5.5 +----- +- Fixed reconnection in the event of server restart +- Fixed calling on_reconnect() so that it is actually called +- Set default Namespace=None +- Added support for Python 3.4 + 0.5.3 ----- - Updated wait loop to exit if the client wants to disconnect @@ -42,7 +72,5 @@ 0.1 --- -- Wrapped code from StackOverflow_ +- Wrapped `code from StackOverflow <http://stackoverflow.com/questions/6692908/formatting-messages-to-send-to-socket-io-node-js-server-from-python-client>`_ - Added exception handling to destructor in case of connection failure - -.. _StackOverflow: http://stackoverflow.com/questions/6692908/formatting-messages-to-send-to-socket-io-node-js-server-from-python-client diff -Nru python-socketio-client-0.5.3/debian/changelog python-socketio-client-0.6.5/debian/changelog --- python-socketio-client-0.5.3/debian/changelog 2014-04-26 12:19:25.000000000 +0300 +++ python-socketio-client-0.6.5/debian/changelog 2016-01-12 14:17:23.000000000 +0200 @@ -1,3 +1,17 @@ +python-socketio-client (0.6.5-0.1) unstable; urgency=medium + + * Non-maintainer Upload. + * New upstream version (closes: #808010). + + Adds support for the socket.io 1.x protocol and drops support for 0.9. + * Adjust d/docs and remove files deleted upstream. + * Switch to pybuild and enable Python3 support. + + New binary package, python3-socketio-client + * Disable tests during build, as the test suite requires a nodejs instance + and operational networking. + * Bump standards to 3.9.6 (no changes needed). + + -- Apollon Oikonomopoulos <apoi...@debian.org> Mon, 11 Jan 2016 18:03:17 +0200 + python-socketio-client (0.5.3-1) unstable; urgency=low * Initial release (Closes: #744690) diff -Nru python-socketio-client-0.5.3/debian/control python-socketio-client-0.6.5/debian/control --- python-socketio-client-0.5.3/debian/control 2014-04-26 12:13:48.000000000 +0300 +++ python-socketio-client-0.6.5/debian/control 2016-01-12 14:13:24.000000000 +0200 @@ -4,8 +4,10 @@ Maintainer: Leo Iannacone <l...@ubuntu.com> Build-Depends: debhelper (>= 9) , python-all (>= 2.6.6-3~) + , python3-all , python-setuptools -Standards-Version: 3.9.5 + , python3-setuptools +Standards-Version: 3.9.6 Homepage: https://github.com/invisibleroads/socketIO-client Vcs-Git: git://anonscm.debian.org/collab-maint/python-socketio-client.git Vcs-Browser: http://anonscm.debian.org/gitweb/?p=collab-maint/python-socketio-client.git @@ -17,9 +19,23 @@ , python-requests , python-six , python-websocket -Description: socket.io-client library for python +Description: socket.io-client library for Python This package contains a socket.io client library for Python. . You can use it to write test code against your socket.io server. + +Package: python3-socketio-client +Architecture: all +Depends: ${python:Depends} + , ${misc:Depends} + , python3-requests + , python3-six + , python3-websocket +Description: socket.io-client library for Python3 + This package contains a socket.io client library + for Python3. + . + You can use it to write test code against your + socket.io server. diff -Nru python-socketio-client-0.5.3/debian/docs python-socketio-client-0.6.5/debian/docs --- python-socketio-client-0.5.3/debian/docs 2014-04-26 11:29:54.000000000 +0300 +++ python-socketio-client-0.6.5/debian/docs 1970-01-01 02:00:00.000000000 +0200 @@ -1,4 +0,0 @@ -README.rst -TODO.goals -TODO.log -serve_tests.js diff -Nru python-socketio-client-0.5.3/debian/python3-socketio-client.docs python-socketio-client-0.6.5/debian/python3-socketio-client.docs --- python-socketio-client-0.5.3/debian/python3-socketio-client.docs 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/debian/python3-socketio-client.docs 2016-01-12 14:17:22.000000000 +0200 @@ -0,0 +1,2 @@ +README.rst +TODO.goals diff -Nru python-socketio-client-0.5.3/debian/python-socketio-client.docs python-socketio-client-0.6.5/debian/python-socketio-client.docs --- python-socketio-client-0.5.3/debian/python-socketio-client.docs 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/debian/python-socketio-client.docs 2016-01-12 14:17:22.000000000 +0200 @@ -0,0 +1,2 @@ +README.rst +TODO.goals diff -Nru python-socketio-client-0.5.3/debian/rules python-socketio-client-0.6.5/debian/rules --- python-socketio-client-0.5.3/debian/rules 2014-04-26 12:17:29.000000000 +0300 +++ python-socketio-client-0.6.5/debian/rules 2016-01-12 14:13:24.000000000 +0200 @@ -4,8 +4,11 @@ # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 +export PYBUILD_NAME=socketio-client +export PYBUILD_DISABLE=test + %: - dh $@ --with python2 + dh $@ --with python2,python3 --buildsystem=pybuild override_dh_auto_clean: dh_auto_clean diff -Nru python-socketio-client-0.5.3/MANIFEST.in python-socketio-client-0.6.5/MANIFEST.in --- python-socketio-client-0.5.3/MANIFEST.in 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/MANIFEST.in 2015-06-01 23:16:32.000000000 +0300 @@ -1,3 +1,3 @@ recursive-include socketIO_client * -include *.rst +include *.html *.js *.rst global-exclude *.pyc diff -Nru python-socketio-client-0.5.3/README.rst python-socketio-client-0.6.5/README.rst --- python-socketio-client-0.5.3/README.rst 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/README.rst 2015-06-01 23:16:32.000000000 +0300 @@ -1,11 +1,17 @@ +.. image:: https://travis-ci.org/invisibleroads/socketIO-client.svg?branch=master + :target: https://travis-ci.org/invisibleroads/socketIO-client + + socketIO-client =============== -Here is a socket.io_ client library for Python. You can use it to write test code for your socket.io_ server. +Here is a `socket.io <http://socket.io>`_ client library for Python. You can use it to write test code for your socket.io server. + +Please note that this version implements `socket.io protocol 1.x <https://github.com/automattic/socket.io-protocol>`_, which is not backwards compatible. If you want to communicate using `socket.io protocol 0.9 <https://github.com/learnboost/socket.io-spec>`_ (which is compatible with `gevent-socketio <https://github.com/abourget/gevent-socketio>`_), please use `socketIO-client 0.5.6 <https://pypi.python.org/pypi/socketIO-client/0.5.6>`_. Installation ------------ -:: +Install the package in an isolated environment. :: VIRTUAL_ENV=$HOME/.virtualenv @@ -26,38 +32,49 @@ VIRTUAL_ENV=$HOME/.virtualenv source $VIRTUAL_ENV/bin/activate +Launch your socket.io server. :: + + # Get package folder + PACKAGE_FOLDER=`python -c "import os, socketIO_client;\ + print(os.path.dirname(socketIO_client.__file__))"` + # Start socket.io server + DEBUG=* node $PACKAGE_FOLDER/tests/serve.js + # Start proxy server in a separate terminal on the same machine + DEBUG=* node $PACKAGE_FOLDER/tests/proxy.js + For debugging information, run these commands first. :: import logging + logging.getLogger('requests').setLevel(logging.WARNING) logging.basicConfig(level=logging.DEBUG) Emit. :: - from socketIO_client import SocketIO + from socketIO_client import SocketIO, LoggingNamespace - with SocketIO('localhost', 8000) as socketIO: + with SocketIO('localhost', 8000, LoggingNamespace) as socketIO: socketIO.emit('aaa') socketIO.wait(seconds=1) Emit with callback. :: - from socketIO_client import SocketIO + from socketIO_client import SocketIO, LoggingNamespace def on_bbb_response(*args): - print 'on_bbb_response', args + print('on_bbb_response', args) - with SocketIO('localhost', 8000) as socketIO: + with SocketIO('localhost', 8000, LoggingNamespace) as socketIO: socketIO.emit('bbb', {'xxx': 'yyy'}, on_bbb_response) socketIO.wait_for_callbacks(seconds=1) Define events. :: - from socketIO_client import SocketIO + from socketIO_client import SocketIO, LoggingNamespace def on_aaa_response(*args): - print 'on_aaa_response', args + print('on_aaa_response', args) - socketIO = SocketIO('localhost', 8000) + socketIO = SocketIO('localhost', 8000, LoggingNamespace) socketIO.on('aaa_response', on_aaa_response) socketIO.emit('aaa') socketIO.wait(seconds=1) @@ -69,7 +86,7 @@ class Namespace(BaseNamespace): def on_aaa_response(self, *args): - print 'on_aaa_response', args + print('on_aaa_response', args) self.emit('bbb') socketIO = SocketIO('localhost', 8000, Namespace) @@ -83,7 +100,7 @@ class Namespace(BaseNamespace): def on_connect(self): - print '[Connected]' + print('[Connected]') socketIO = SocketIO('localhost', 8000, Namespace) socketIO.wait(seconds=1) @@ -95,12 +112,12 @@ class ChatNamespace(BaseNamespace): def on_aaa_response(self, *args): - print 'on_aaa_response', args + print('on_aaa_response', args) class NewsNamespace(BaseNamespace): def on_aaa_response(self, *args): - print 'on_aaa_response', args + print('on_aaa_response', args) socketIO = SocketIO('localhost', 8000) chat_namespace = socketIO.define(ChatNamespace, '/chat') @@ -114,14 +131,15 @@ from socketIO_client import SocketIO - SocketIO('https://localhost') + SocketIO('https://localhost', verify=False) -Specify params, headers, cookies, proxies thanks to the `requests`_ library. :: +Specify params, headers, cookies, proxies thanks to the `requests <http://python-requests.org>`_ library. :: from socketIO_client import SocketIO from base64 import b64encode - SocketIO('localhost', 8000, + SocketIO( + localhost', 8000, params={'q': 'qqq'}, headers={'Authorization': 'Basic ' + b64encode('username:password')}, cookies={'a': 'aaa'}, @@ -131,7 +149,7 @@ from socketIO_client import SocketIO - socketIO = SocketIO('localhost') + socketIO = SocketIO('localhost', 8000) socketIO.wait() @@ -142,41 +160,11 @@ Credits ------- -- `Guillermo Rauch`_ wrote the `socket.io specification`_. -- `Hiroki Ohtani`_ wrote websocket-client_. -- rod_ wrote a `prototype for a Python client to a socket.io server`_ on StackOverflow. -- `Alexandre Bourget`_ wrote gevent-socketio_, which is a socket.io server written in Python. -- `Paul Kienzle`_, `Zac Lee`_, `Josh VanderLinden`_, `Ian Fitzpatrick`_, `Lucas Klein`_, `Rui Chicoria`_, `Travis Odom`_ submitted code to expand support of the socket.io protocol. -- `Bernard Pratz`_ and `Francis Bull`_ wrote prototypes to support xhr-polling and jsonp-polling. -- `Eric Chen`_, `Denis Zinevich`_, `Thiago Hersan`_, `Nayef Copty`_ suggested ways to make the connection more robust. - - -.. _socket.io: http://socket.io -.. _requests: http://python-requests.org - -.. _Guillermo Rauch: https://github.com/guille -.. _socket.io specification: https://github.com/LearnBoost/socket.io-spec - -.. _Hiroki Ohtani: https://github.com/liris -.. _websocket-client: https://github.com/liris/websocket-client - -.. _rod: http://stackoverflow.com/users/370115/rod -.. _prototype for a Python client to a socket.io server: http://stackoverflow.com/questions/6692908/formatting-messages-to-send-to-socket-io-node-js-server-from-python-client - -.. _Alexandre Bourget: https://github.com/abourget -.. _gevent-socketio: https://github.com/abourget/gevent-socketio - -.. _Bernard Pratz: https://github.com/guyzmo -.. _Francis Bull: https://github.com/franbull -.. _Paul Kienzle: https://github.com/pkienzle -.. _Zac Lee: https://github.com/zratic -.. _Josh VanderLinden: https://github.com/codekoala -.. _Ian Fitzpatrick: https://github.com/GraphEffect -.. _Lucas Klein: https://github.com/lukashed -.. _Rui Chicoria: https://github.com/rchicoria -.. _Travis Odom: https://github.com/burstaholic - -.. _Eric Chen: https://github.com/taiyangc -.. _Denis Zinevich: https://github.com/dzinevich -.. _Thiago Hersan: https://github.com/thiagohersan -.. _Nayef Copty: https://github.com/nayefc +- `Guillermo Rauch <https://github.com/rauchg>`_ wrote the `socket.io specification <https://github.com/automattic/socket.io-protocol>`_. +- `Hiroki Ohtani <https://github.com/liris>`_ wrote `websocket-client <https://github.com/liris/websocket-client>`_. +- `rod <http://stackoverflow.com/users/370115/rod>`_ wrote a `prototype for a Python client to a socket.io server <http://stackoverflow.com/questions/6692908/formatting-messages-to-send-to-socket-io-node-js-server-from-python-client>`_. +- `Alexandre Bourget <https://github.com/abourget>`_ wrote `gevent-socketio <https://github.com/abourget/gevent-socketio>`_, which is a socket.io server written in Python. +- `Paul Kienzle <https://github.com/pkienzle>`_, `Zac Lee <https://github.com/zratic>`_, `Josh VanderLinden <https://github.com/codekoala>`_, `Ian Fitzpatrick <https://github.com/ifitzpatrick>`_, `Lucas Klein <https://github.com/lukasklein>`_, `Rui Chicoria <https://github.com/rchicoria>`_, `Travis Odom <https://github.com/burstaholic>`_, `Patrick Huber <https://github.com/stackmagic>`_, `Brad Campbell <https://github.com/bradjc>`_, `Daniel <https://github.com/dabidan>`_, `Sean Arietta <https://github.com/sarietta>`_ submitted code to expand support of the socket.io protocol. +- `Bernard Pratz <https://github.com/guyzmo>`_, `Francis Bull <https://github.com/franbull>`_ wrote prototypes to support xhr-polling and jsonp-polling. +- `Eric Chen <https://github.com/taiyangc>`_, `Denis Zinevich <https://github.com/dzinevich>`_, `Thiago Hersan <https://github.com/thiagohersan>`_, `Nayef Copty <https://github.com/nayefc>`_, `Jörgen Karlsson <https://github.com/jorgen-k>`_, `Branden Ghena <https://github.com/brghena>`_, `Tim Landscheidt <https://github.com/scfc>`_, `Matt Porritt <https://github.com/mattporritt>`_ suggested ways to make the connection more robust. +- `Merlijn van Deen <https://github.com/valhallasw>`_, `Frederic Sureau <https://github.com/fredericsureau>`_, `Marcus Cobden <https://github.com/leth>`_, `Drew Hutchison <https://github.com/drewhutchison>`_, `wuurrd <https://github.com/wuurrd>`_, `Adam Kecer <https://github.com/amfg>`_, `Alex Monk <https://github.com/Krenair>`_, `Vishal P R <https://github.com/vishalwy>`_, `John Vandenberg <https://github.com/jayvdb>`_, `Thomas Grainger <https://github.com/graingert>`_ proposed changes that make the library more friendly and practical for you! diff -Nru python-socketio-client-0.5.3/serve_tests.js python-socketio-client-0.6.5/serve_tests.js --- python-socketio-client-0.5.3/serve_tests.js 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/serve_tests.js 1970-01-01 02:00:00.000000000 +0200 @@ -1,79 +0,0 @@ -var io = require('socket.io').listen(8000); - -var main = io.of('').on('connection', function(socket) { - socket.on('message', function(data, fn) { - if (fn) { // Client expects a callback - if (data) { - fn(data); - } else { - fn(); - } - } else if (typeof data === 'object') { - socket.json.send(data ? data : 'message_response'); // object or null - } else { - socket.send(data ? data : 'message_response'); // string or '' - } - }); - socket.on('emit', function() { - socket.emit('emit_response'); - }); - socket.on('emit_with_payload', function(payload) { - socket.emit('emit_with_payload_response', payload); - }); - socket.on('emit_with_multiple_payloads', function(payload, payload) { - socket.emit('emit_with_multiple_payloads_response', payload, payload); - }); - socket.on('emit_with_callback', function(fn) { - fn(); - }); - socket.on('emit_with_callback_with_payload', function(fn) { - fn(PAYLOAD); - }); - socket.on('emit_with_callback_with_multiple_payloads', function(fn) { - fn(PAYLOAD, PAYLOAD); - }); - socket.on('emit_with_event', function(payload) { - socket.emit('emit_with_event_response', payload); - }); - socket.on('ack', function(payload) { - socket.emit('ack_response', payload, function(payload) { - socket.emit('ack_callback_response', payload); - }); - }); - socket.on('aaa', function() { - socket.emit('aaa_response', PAYLOAD); - }); - socket.on('bbb', function(payload, fn) { - if (fn) { - fn(payload); - } - }); - socket.on('wait_with_disconnect', function() { - socket.emit('wait_with_disconnect_response'); - }); -}); - -var chat = io.of('/chat').on('connection', function (socket) { - socket.on('emit_with_payload', function(payload) { - socket.emit('emit_with_payload_response', payload); - }); - socket.on('aaa', function() { - socket.emit('aaa_response', 'in chat'); - }); - socket.on('ack', function(payload) { - socket.emit('ack_response', payload, function(payload) { - socket.emit('ack_callback_response', payload); - }); - }); -}); - -var news = io.of('/news').on('connection', function (socket) { - socket.on('emit_with_payload', function(payload) { - socket.emit('emit_with_payload_response', payload); - }); - socket.on('aaa', function() { - socket.emit('aaa_response', 'in news'); - }); -}); - -var PAYLOAD = {'xxx': 'yyy'}; diff -Nru python-socketio-client-0.5.3/setup.cfg python-socketio-client-0.6.5/setup.cfg --- python-socketio-client-0.5.3/setup.cfg 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/setup.cfg 2015-06-01 23:16:32.000000000 +0300 @@ -1,5 +1,5 @@ [nosetests] -detailed-errors=TRUE -with-coverage=TRUE -cover-package=socketIO_client -cover-erase=TRUE +detailed-errors = TRUE +with-coverage = TRUE +cover-package = socketIO_client +cover-erase = TRUE diff -Nru python-socketio-client-0.5.3/setup.py python-socketio-client-0.6.5/setup.py --- python-socketio-client-0.5.3/setup.py 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/setup.py 2015-06-01 23:16:32.000000000 +0300 @@ -1,32 +1,42 @@ -import os -from setuptools import setup, find_packages +import io +from os.path import abspath, dirname, join +from setuptools import find_packages, setup -here = os.path.abspath(os.path.dirname(__file__)) -README = open(os.path.join(here, 'README.rst')).read() -CHANGES = open(os.path.join(here, 'CHANGES.rst')).read() +REQUIREMENTS = [ + 'requests', + 'six', + 'websocket-client', +] +HERE = dirname(abspath(__file__)) +LOAD_TEXT = lambda name: io.open(join(HERE, name), encoding='UTF-8').read() +DESCRIPTION = '\n\n'.join(LOAD_TEXT(_) for _ in [ + 'README.rst', + 'CHANGES.rst', +]) setup( - name='socketIO-client', - version='0.5.3', + name='socketIO_client', + version='0.6.5', description='A socket.io client library', - long_description=README + '\n\n' + CHANGES, + long_description=DESCRIPTION, license='MIT', classifiers=[ 'Intended Audience :: Developers', 'Programming Language :: Python', 'License :: OSI Approved :: MIT License', + 'Development Status :: 5 - Production/Stable', ], keywords='socket.io node.js', author='Roy Hyunjin Han', author_email='r...@crosscompute.com', url='https://github.com/invisibleroads/socketIO-client', - install_requires=[ - 'requests', - 'six', - 'websocket-client', + install_requires=REQUIREMENTS, + tests_require=[ + 'nose', + 'coverage', ], packages=find_packages(), include_package_data=True, - zip_safe=True) + zip_safe=False) diff -Nru python-socketio-client-0.5.3/socketIO_client/heartbeats.py python-socketio-client-0.6.5/socketIO_client/heartbeats.py --- python-socketio-client-0.5.3/socketIO_client/heartbeats.py 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/heartbeats.py 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,52 @@ +import logging +from threading import Thread, Event + +from .exceptions import ConnectionError, TimeoutError + + +class HeartbeatThread(Thread): + + daemon = True + + def __init__( + self, send_heartbeat, + relax_interval_in_seconds, + hurry_interval_in_seconds): + super(HeartbeatThread, self).__init__() + self._send_heartbeat = send_heartbeat + self._relax_interval_in_seconds = relax_interval_in_seconds + self._hurry_interval_in_seconds = hurry_interval_in_seconds + self._adrenaline = Event() + self._rest = Event() + self._halt = Event() + + def run(self): + try: + while not self._halt.is_set(): + try: + self._send_heartbeat() + except TimeoutError: + pass + if self._adrenaline.is_set(): + interval_in_seconds = self._hurry_interval_in_seconds + else: + interval_in_seconds = self._relax_interval_in_seconds + self._rest.wait(interval_in_seconds) + except ConnectionError: + logging.debug('[heartbeat connection error]') + + def relax(self): + self._adrenaline.clear() + + def hurry(self): + self._adrenaline.set() + self._rest.set() + self._rest.clear() + + @property + def hurried(self): + return self._adrenaline.is_set() + + def halt(self): + self._rest.set() + self._halt.set() diff -Nru python-socketio-client-0.5.3/socketIO_client/__init__.py python-socketio-client-0.6.5/socketIO_client/__init__.py --- python-socketio-client-0.5.3/socketIO_client/__init__.py 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/__init__.py 2015-06-01 23:16:32.000000000 +0300 @@ -1,124 +1,318 @@ -import logging -import json -import requests -import time -from collections import namedtuple -from urlparse import urlparse - from .exceptions import ConnectionError, TimeoutError, PacketError -from .transports import _get_response, _negotiate_transport, TRANSPORTS +from .heartbeats import HeartbeatThread +from .logs import LoggingMixin +from .namespaces import ( + EngineIONamespace, SocketIONamespace, LoggingSocketIONamespace, + find_callback) +from .parsers import ( + parse_host, parse_engineIO_session, + format_socketIO_packet_data, parse_socketIO_packet_data, + get_namespace_path) +from .symmetries import get_character +from .transports import ( + WebsocketTransport, XHR_PollingTransport, prepare_http_session, TRANSPORTS) + + +__all__ = 'SocketIO', 'SocketIONamespace' +__version__ = '0.6.3' +BaseNamespace = SocketIONamespace +LoggingNamespace = LoggingSocketIONamespace + + +def retry(f): + def wrap(*args, **kw): + self = args[0] + try: + return f(*args, **kw) + except (TimeoutError, ConnectionError): + self._opened = False + return f(*args, **kw) + return wrap + + +class EngineIO(LoggingMixin): + + def __init__( + self, host, port=None, Namespace=EngineIONamespace, + wait_for_connection=True, transports=TRANSPORTS, + resource='engine.io', hurry_interval_in_seconds=1, **kw): + self._is_secure, self._url = parse_host(host, port, resource) + self._wait_for_connection = wait_for_connection + self._client_transports = transports + self._hurry_interval_in_seconds = hurry_interval_in_seconds + self._http_session = prepare_http_session(kw) + + self._log_name = self._url + self._wants_to_close = False + self._opened = False + + if Namespace: + self.define(Namespace) + self._transport + + # Connect + + @property + def _transport(self): + if self._opened: + return self._transport_instance + self._engineIO_session = self._get_engineIO_session() + self._negotiate_transport() + self._connect_namespaces() + self._opened = True + self._reset_heartbeat() + return self._transport_instance + def _get_engineIO_session(self): + warning_screen = self._yield_warning_screen() + for elapsed_time in warning_screen: + transport = XHR_PollingTransport( + self._http_session, self._is_secure, self._url) + try: + engineIO_packet_type, engineIO_packet_data = next( + transport.recv_packet()) + break + except (TimeoutError, ConnectionError) as e: + if not self._wait_for_connection: + raise + warning = Exception('[waiting for connection] %s' % e) + warning_screen.throw(warning) + assert engineIO_packet_type == 0 # engineIO_packet_type == open + return parse_engineIO_session(engineIO_packet_data) + + def _negotiate_transport(self): + self._transport_instance = self._get_transport('xhr-polling') + self.transport_name = 'xhr-polling' + is_ws_client = 'websocket' in self._client_transports + is_ws_server = 'websocket' in self._engineIO_session.transport_upgrades + if is_ws_client and is_ws_server: + try: + transport = self._get_transport('websocket') + transport.send_packet(2, 'probe') + for packet_type, packet_data in transport.recv_packet(): + if packet_type == 3 and packet_data == b'probe': + transport.send_packet(5, '') + self._transport_instance = transport + self.transport_name = 'websocket' + else: + self._warn('unexpected engine.io packet') + except Exception: + pass + self._debug('[transport selected] %s', self.transport_name) -_SocketIOSession = namedtuple('_SocketIOSession', [ - 'id', - 'heartbeat_timeout', - 'server_supported_transports', -]) -_log = logging.getLogger(__name__) -PROTOCOL_VERSION = 1 -RETRY_INTERVAL_IN_SECONDS = 1 - - -class BaseNamespace(object): - 'Define client behavior' - - def __init__(self, _transport, path): - self._transport = _transport - self.path = path - self._callback_by_event = {} - self.initialize() + def _reset_heartbeat(self): + try: + self._heartbeat_thread.halt() + hurried = self._heartbeat_thread.hurried + except AttributeError: + hurried = False + ping_interval = self._engineIO_session.ping_interval + if self.transport_name.endswith('-polling'): + # Use ping/pong to unblock recv for polling transport + hurry_interval_in_seconds = self._hurry_interval_in_seconds + else: + # Use timeout to unblock recv for websocket transport + hurry_interval_in_seconds = ping_interval + self._heartbeat_thread = HeartbeatThread( + send_heartbeat=self._ping, + relax_interval_in_seconds=ping_interval, + hurry_interval_in_seconds=hurry_interval_in_seconds) + self._heartbeat_thread.start() + if hurried: + self._heartbeat_thread.hurry() + self._debug('[heartbeat reset]') - def initialize(self): - 'Initialize custom variables here; you can override this method' + def _connect_namespaces(self): pass - def message(self, data='', callback=None): - self._transport.message(self.path, data, callback) + def _get_transport(self, transport_name): + SelectedTransport = { + 'xhr-polling': XHR_PollingTransport, + 'websocket': WebsocketTransport, + }[transport_name] + return SelectedTransport( + self._http_session, self._is_secure, self._url, + self._engineIO_session) - def emit(self, event, *args, **kw): - callback, args = find_callback(args, kw) - self._transport.emit(self.path, event, args, callback) + def __enter__(self): + return self + + def __exit__(self, *exception_pack): + self._close() + + def __del__(self): + self._close() + + # Define - def disconnect(self): - self._transport.disconnect(self.path) + def define(self, Namespace): + self._namespace = namespace = Namespace(self) + return namespace def on(self, event, callback): - 'Define a callback to handle a custom event emitted by the server' - self._callback_by_event[event] = callback + try: + namespace = self.get_namespace() + except PacketError: + namespace = self.define(EngineIONamespace) + return namespace.on(event, callback) - def on_connect(self): - 'Called after server connects; you can override this method' - _log.debug('%s [connect]', self.path) - - def on_disconnect(self): - 'Called after server disconnects; you can override this method' - _log.debug('%s [disconnect]', self.path) - - def on_heartbeat(self): - 'Called after server sends a heartbeat; you can override this method' - _log.debug('%s [heartbeat]', self.path) - - def on_message(self, data): - 'Called after server sends a message; you can override this method' - _log.info('%s [message] %s', self.path, data) - - def on_event(self, event, *args): - """ - Called after server sends an event; you can override this method. - Called only if a custom event handler does not exist, - such as one defined by namespace.on('my_event', my_function). - """ - callback, args = find_callback(args) - arguments = [repr(_) for _ in args] - if callback: - arguments.append('callback(*args)') - callback(*args) - _log.info('%s [event] %s(%s)', self.path, event, ', '.join(arguments)) + def get_namespace(self): + try: + return self._namespace + except AttributeError: + raise PacketError('undefined engine.io namespace') + + # Act - def on_error(self, reason, advice): - 'Called after server sends an error; you can override this method' - _log.info('%s [error] %s', self.path, advice) + def send(self, engineIO_packet_data): + self._message(engineIO_packet_data) - def on_noop(self): - 'Called after server sends a noop; you can override this method' - _log.info('%s [noop]', self.path) + def _open(self): + engineIO_packet_type = 0 + self._transport_instance.send_packet(engineIO_packet_type) - def on_open(self, *args): - _log.info('%s [open] %s', self.path, args) + def _close(self): + self._wants_to_close = True + try: + self._heartbeat_thread.halt() + except AttributeError: + pass + if not self._opened: + return + engineIO_packet_type = 1 + try: + self._transport_instance.send_packet(engineIO_packet_type) + except (TimeoutError, ConnectionError): + pass + self._opened = False - def on_close(self, *args): - _log.info('%s [close] %s', self.path, args) + def _ping(self, engineIO_packet_data=''): + engineIO_packet_type = 2 + self._transport_instance.send_packet( + engineIO_packet_type, engineIO_packet_data) + + def _pong(self, engineIO_packet_data=''): + engineIO_packet_type = 3 + self._transport_instance.send_packet( + engineIO_packet_type, engineIO_packet_data) + + @retry + def _message(self, engineIO_packet_data, with_transport_instance=False): + engineIO_packet_type = 4 + if with_transport_instance: + transport = self._transport_instance + else: + transport = self._transport + transport.send_packet(engineIO_packet_type, engineIO_packet_data) + self._debug('[socket.io packet sent] %s', engineIO_packet_data) + + def _upgrade(self): + engineIO_packet_type = 5 + self._transport_instance.send_packet(engineIO_packet_type) + + def _noop(self): + engineIO_packet_type = 6 + self._transport_instance.send_packet(engineIO_packet_type) + + # React + + def wait(self, seconds=None, **kw): + 'Wait in a loop and react to events as defined in the namespaces' + # Use ping/pong to unblock recv for polling transport + self._heartbeat_thread.hurry() + # Use timeout to unblock recv for websocket transport + self._transport.set_timeout(seconds=1) + # Listen + warning_screen = self._yield_warning_screen(seconds) + for elapsed_time in warning_screen: + if self._should_stop_waiting(**kw): + break + try: + try: + self._process_packets() + except TimeoutError: + pass + except ConnectionError as e: + self._opened = False + try: + warning = Exception('[connection error] %s' % e) + warning_screen.throw(warning) + except StopIteration: + self._warn(warning) + try: + namespace = self.get_namespace() + namespace.on_disconnect() + except PacketError: + pass + self._heartbeat_thread.relax() + self._transport.set_timeout() - def on_retry(self, *args): - _log.info('%s [retry] %s', self.path, args) + def _should_stop_waiting(self): + return self._wants_to_close - def on_reconnect(self, *args): - _log.info('%s [reconnect] %s', self.path, args) + def _process_packets(self): + for engineIO_packet in self._transport.recv_packet(): + try: + self._process_packet(engineIO_packet) + except PacketError as e: + self._warn('[packet error] %s', e) - def _find_event_callback(self, event): - # Check callbacks defined by on() + def _process_packet(self, packet): + engineIO_packet_type, engineIO_packet_data = packet + # Launch callbacks + namespace = self.get_namespace() try: - return self._callback_by_event[event] + delegate = { + 0: self._on_open, + 1: self._on_close, + 2: self._on_ping, + 3: self._on_pong, + 4: self._on_message, + 5: self._on_upgrade, + 6: self._on_noop, + }[engineIO_packet_type] except KeyError: - pass - # Check callbacks defined explicitly or use on_event() - return getattr( - self, - 'on_' + event.replace(' ', '_'), - lambda *args: self.on_event(event, *args)) + raise PacketError( + 'unexpected engine.io packet type (%s)' % engineIO_packet_type) + delegate(engineIO_packet_data, namespace) + if engineIO_packet_type is 4: + return engineIO_packet_data + + def _on_open(self, data, namespace): + namespace._find_packet_callback('open')() + + def _on_close(self, data, namespace): + namespace._find_packet_callback('close')() + + def _on_ping(self, data, namespace): + self._pong(data) + namespace._find_packet_callback('ping')(data) + + def _on_pong(self, data, namespace): + namespace._find_packet_callback('pong')(data) + + def _on_message(self, data, namespace): + namespace._find_packet_callback('message')(data) + def _on_upgrade(self, data, namespace): + namespace._find_packet_callback('upgrade')() -class SocketIO(object): + def _on_noop(self, data, namespace): + namespace._find_packet_callback('noop')() + + +class SocketIO(EngineIO): """Create a socket.io client that connects to a socket.io server at the specified host and port. - Define the behavior of the client by specifying a custom Namespace. - Prefix host with https:// to use SSL. - Set wait_for_connection=True to block until we have a connection. - - Specify the transports you want to use. + - Specify desired transports=['websocket', 'xhr-polling']. - Pass query params, headers, cookies, proxies as keyword arguments. - SocketIO('localhost', 8000, + SocketIO( + 'localhost', 8000, params={'q': 'qqq'}, headers={'Authorization': 'Basic ' + b64encode('username:password')}, cookies={'a': 'aaa'}, @@ -126,281 +320,196 @@ """ def __init__( - self, host, port=None, Namespace=BaseNamespace, - wait_for_connection=True, transports=TRANSPORTS, **kw): - self.is_secure, self.base_url = _parse_host(host, port) - self.wait_for_connection = wait_for_connection + self, host, port=None, Namespace=SocketIONamespace, + wait_for_connection=True, transports=TRANSPORTS, + resource='socket.io', hurry_interval_in_seconds=1, **kw): self._namespace_by_path = {} - self.client_supported_transports = transports - self.kw = kw - self.define(Namespace) + self._callback_by_ack_id = {} + self._ack_id = 0 + super(SocketIO, self).__init__( + host, port, Namespace, wait_for_connection, transports, + resource, hurry_interval_in_seconds, **kw) - def __enter__(self): - return self + # Connect + + @property + def connected(self): + return self._opened + + def _connect_namespaces(self): + for path, namespace in self._namespace_by_path.items(): + namespace._transport = self._transport_instance + if path: + self.connect(path, with_transport_instance=True) def __exit__(self, *exception_pack): self.disconnect() + super(SocketIO, self).__exit__(*exception_pack) def __del__(self): self.disconnect() + super(SocketIO, self).__del__() + + # Define def define(self, Namespace, path=''): + self._namespace_by_path[path] = namespace = Namespace(self, path) if path: - self._transport.connect(path) - namespace = Namespace(self._transport, path) - self._namespace_by_path[path] = namespace + self.connect(path) + self.wait(for_connect=True) return namespace def on(self, event, callback, path=''): - return self.get_namespace(path).on(event, callback) + try: + namespace = self.get_namespace(path) + except PacketError: + namespace = self.define(SocketIONamespace, path) + return namespace.on(event, callback) - def message(self, data='', callback=None, path=''): - self._transport.message(path, data, callback) + def get_namespace(self, path=''): + try: + return self._namespace_by_path[path] + except KeyError: + raise PacketError('undefined socket.io namespace (%s)' % path) - def emit(self, event, *args, **kw): - path = kw.get('path', '') - callback, args = find_callback(args, kw) - self._transport.emit(path, event, args, callback) + # Act - def wait(self, seconds=None, for_callbacks=False): - """Wait in a loop and process events as defined in the namespaces. + def connect(self, path, with_transport_instance=False): + socketIO_packet_type = 0 + socketIO_packet_data = format_socketIO_packet_data(path) + self._message( + str(socketIO_packet_type) + socketIO_packet_data, + with_transport_instance) - - Omit seconds, i.e. call wait() without arguments, to wait forever. - """ + def disconnect(self, path=''): + if not path or not self._opened: + self._close() + elif path: + socketIO_packet_type = 1 + socketIO_packet_data = format_socketIO_packet_data(path) + try: + self._message(str(socketIO_packet_type) + socketIO_packet_data) + except (TimeoutError, ConnectionError): + pass try: - warning_screen = _yield_warning_screen(seconds) - for elapsed_time in warning_screen: - try: - try: - self._process_events() - except TimeoutError: - pass - if self._stop_waiting(for_callbacks): - break - self.heartbeat_pacemaker.send(elapsed_time) - except ConnectionError as e: - try: - warning = Exception('[connection error] %s' % e) - warning_screen.throw(warning) - except StopIteration: - _log.warn(warning) - self.disconnect() - except KeyboardInterrupt: + namespace = self._namespace_by_path.pop(path) + namespace.on_disconnect() + except KeyError: pass - def _process_events(self): - for packet in self._transport.recv_packet(): - try: - self._process_packet(packet) - except PacketError as e: - _log.warn('[packet error] %s', e) + def emit(self, event, *args, **kw): + path = kw.get('path', '') + callback, args = find_callback(args, kw) + ack_id = self._set_ack_callback(callback) if callback else None + args = [event] + list(args) + socketIO_packet_type = 2 + socketIO_packet_data = format_socketIO_packet_data(path, ack_id, args) + self._message(str(socketIO_packet_type) + socketIO_packet_data) - def _process_packet(self, packet): - code, packet_id, path, data = packet - namespace = self.get_namespace(path) - delegate = self._get_delegate(code) - delegate(packet, namespace._find_event_callback) + def send(self, data='', callback=None, **kw): + path = kw.get('path', '') + args = [data] + if callback: + args.append(callback) + self.emit('message', *args, path=path) - def _stop_waiting(self, for_callbacks): - # Use __transport to make sure that we do not reconnect inadvertently - if for_callbacks and not self.__transport.has_ack_callback: - return True - if self.__transport._wants_to_disconnect: - return True - return False + def _ack(self, path, ack_id, *args): + socketIO_packet_type = 3 + socketIO_packet_data = format_socketIO_packet_data(path, ack_id, args) + self._message(str(socketIO_packet_type) + socketIO_packet_data) + + # React def wait_for_callbacks(self, seconds=None): self.wait(seconds, for_callbacks=True) - def disconnect(self, path=''): - if self.connected: - self._transport.disconnect(path) - namespace = self._namespace_by_path[path] - namespace.on_disconnect() - if path: - del self._namespace_by_path[path] - - @property - def connected(self): - return self.__transport.connected - - @property - def _transport(self): - try: - if self.connected: - return self.__transport - except AttributeError: - pass - warning_screen = _yield_warning_screen(seconds=None) - for elapsed_time in warning_screen: - try: - self.__transport = self._get_transport() - break - except ConnectionError as e: - if not self.wait_for_connection: - raise - try: - warning = Exception('[waiting for connection] %s' % e) - warning_screen.throw(warning) - except StopIteration: - _log.warn(warning) - return self.__transport - - def _get_transport(self): - socketIO_session = _get_socketIO_session( - self.is_secure, self.base_url, **self.kw) - _log.debug('[transports available] %s', ' '.join( - socketIO_session.server_supported_transports)) - # Initialize heartbeat_pacemaker - self.heartbeat_pacemaker = self._make_heartbeat_pacemaker( - heartbeat_interval=socketIO_session.heartbeat_timeout / 2) - self.heartbeat_pacemaker.next() - # Negotiate transport - transport = _negotiate_transport( - self.client_supported_transports, socketIO_session, - self.is_secure, self.base_url, **self.kw) - # Update namespaces - for namespace in self._namespace_by_path.values(): - namespace._transport = transport - return transport - - def _make_heartbeat_pacemaker(self, heartbeat_interval): - heartbeat_time = 0 - while True: - elapsed_time = (yield) - if elapsed_time - heartbeat_time > heartbeat_interval: - heartbeat_time = elapsed_time - self._transport.send_heartbeat() + def _should_stop_waiting(self, for_connect=False, for_callbacks=False): + if for_connect: + for namespace in self._namespace_by_path.values(): + is_namespace_connected = getattr( + namespace, '_connected', False) + if not is_namespace_connected: + return False + return True + if for_callbacks and not self._has_ack_callback: + return True + return super(SocketIO, self)._should_stop_waiting() - def get_namespace(self, path=''): + def _process_packet(self, packet): + engineIO_packet_data = super(SocketIO, self)._process_packet(packet) + if engineIO_packet_data is None: + return + self._debug('[socket.io packet received] %s', engineIO_packet_data) + socketIO_packet_type = int(get_character(engineIO_packet_data, 0)) + socketIO_packet_data = engineIO_packet_data[1:] + # Launch callbacks + path = get_namespace_path(socketIO_packet_data) + namespace = self.get_namespace(path) try: - return self._namespace_by_path[path] + delegate = { + 0: self._on_connect, + 1: self._on_disconnect, + 2: self._on_event, + 3: self._on_ack, + 4: self._on_error, + 5: self._on_binary_event, + 6: self._on_binary_ack, + }[socketIO_packet_type] except KeyError: - raise PacketError('unexpected namespace path (%s)' % path) - - def _get_delegate(self, code): + raise PacketError( + 'unexpected socket.io packet type (%s)' % socketIO_packet_type) + delegate(socketIO_packet_data, namespace) + return socketIO_packet_data + + def _on_connect(self, data, namespace): + namespace._connected = True + namespace._find_packet_callback('connect')() + + def _on_disconnect(self, data, namespace): + namespace._connected = False + namespace._find_packet_callback('disconnect')() + + def _on_event(self, data, namespace): + data_parsed = parse_socketIO_packet_data(data) + args = data_parsed.args 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, - }[code] - except KeyError: - raise PacketError('unexpected code (%s)' % code) - - def _on_disconnect(self, packet, find_event_callback): - find_event_callback('disconnect')() - - def _on_connect(self, packet, find_event_callback): - find_event_callback('connect')() - - def _on_heartbeat(self, packet, find_event_callback): - find_event_callback('heartbeat')() + event = args.pop(0) + except IndexError: + raise PacketError('missing event name') + if data_parsed.ack_id is not None: + args.append(self._prepare_to_send_ack( + data_parsed.path, data_parsed.ack_id)) + namespace._find_packet_callback(event)(*args) - def _on_message(self, packet, find_event_callback): - code, packet_id, path, data = packet - args = [data] - if packet_id: - args.append(self._prepare_to_send_ack(path, packet_id)) - find_event_callback('message')(*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) - - 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) - - def _on_ack(self, packet, find_event_callback): - code, packet_id, path, data = packet - data_parts = data.split('+', 1) - packet_id = data_parts[0] + def _on_ack(self, data, namespace): + data_parsed = parse_socketIO_packet_data(data) try: - ack_callback = self._transport.get_ack_callback(packet_id) + ack_callback = self._get_ack_callback(data_parsed.ack_id) except KeyError: return - args = json.loads(data_parts[1]) if len(data_parts) > 1 else [] - ack_callback(*args) + ack_callback(*data_parsed.args) + + def _on_error(self, data, namespace): + namespace._find_packet_callback('error')(data) - def _on_error(self, packet, find_event_callback): - code, packet_id, path, data = packet - reason, advice = data.split('+', 1) - find_event_callback('error')(reason, advice) + def _on_binary_event(self, data, namespace): + self._warn('[not implemented] binary event') - def _on_noop(self, packet, find_event_callback): - find_event_callback('noop')() + def _on_binary_ack(self, data, namespace): + self._warn('[not implemented] binary ack') - def _prepare_to_send_ack(self, path, packet_id): + def _prepare_to_send_ack(self, path, ack_id): 'Return function that acknowledges the server' - return lambda *args: self._transport.ack(path, packet_id, *args) + return lambda *args: self._ack(path, ack_id, *args) + def _set_ack_callback(self, callback): + self._ack_id += 1 + self._callback_by_ack_id[self._ack_id] = callback + return self._ack_id -def find_callback(args, kw=None): - 'Return callback whether passed as a last argument or as a keyword' - if args and callable(args[-1]): - return args[-1], args[:-1] - try: - return kw['callback'], args - except (KeyError, TypeError): - return None, args - - -def _parse_host(host, port): - if not host.startswith('http'): - host = 'http://' + host - url_pack = urlparse(host) - is_secure = url_pack.scheme == 'https' - port = port or url_pack.port or (443 if is_secure else 80) - base_url = '%s:%d%s/socket.io/%s' % ( - url_pack.hostname, port, url_pack.path, PROTOCOL_VERSION) - return is_secure, base_url - - -def _yield_warning_screen(seconds=None): - last_warning = None - for elapsed_time in _yield_elapsed_time(seconds): - try: - yield elapsed_time - except Exception as warning: - warning = str(warning) - if last_warning != warning: - last_warning = warning - _log.warn(warning) - time.sleep(RETRY_INTERVAL_IN_SECONDS) - - -def _yield_elapsed_time(seconds=None): - start_time = time.time() - if seconds is None: - while True: - yield time.time() - start_time - while time.time() - start_time < seconds: - yield time.time() - start_time - - -def _get_socketIO_session(is_secure, base_url, **kw): - server_url = '%s://%s/' % ('https' if is_secure else 'http', base_url) - try: - response = _get_response(requests.get, server_url, **kw) - except TimeoutError as e: - raise ConnectionError(e) - response_parts = response.text.split(':') - return _SocketIOSession( - id=response_parts[0], - heartbeat_timeout=int(response_parts[1]), - server_supported_transports=response_parts[3].split(',')) + def _get_ack_callback(self, ack_id): + return self._callback_by_ack_id.pop(ack_id) + + @property + def _has_ack_callback(self): + return True if self._callback_by_ack_id else False diff -Nru python-socketio-client-0.5.3/socketIO_client/logs.py python-socketio-client-0.6.5/socketIO_client/logs.py --- python-socketio-client-0.5.3/socketIO_client/logs.py 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/logs.py 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,42 @@ +import logging +import time + + +class LoggingMixin(object): + + def _log(self, level, msg, *attrs): + logging.log(level, '%s %s' % (self._log_name, msg), *attrs) + + def _debug(self, msg, *attrs): + self._log(logging.DEBUG, msg, *attrs) + + def _info(self, msg, *attrs): + self._log(logging.INFO, msg, *attrs) + + def _warn(self, msg, *attrs): + self._log(logging.WARNING, msg, *attrs) + + def _yield_warning_screen(self, seconds=None): + last_warning = None + for elapsed_time in _yield_elapsed_time(seconds): + try: + yield elapsed_time + except Exception as warning: + warning = str(warning) + if last_warning != warning: + last_warning = warning + self._warn(warning) + time.sleep(1) + + +def _yield_elapsed_time(seconds=None): + start_time = time.time() + if seconds is None: + while True: + yield _get_elapsed_time(start_time) + while _get_elapsed_time(start_time) < seconds: + yield _get_elapsed_time(start_time) + + +def _get_elapsed_time(start_time): + return time.time() - start_time diff -Nru python-socketio-client-0.5.3/socketIO_client/namespaces.py python-socketio-client-0.6.5/socketIO_client/namespaces.py --- python-socketio-client-0.5.3/socketIO_client/namespaces.py 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/namespaces.py 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,224 @@ +from .logs import LoggingMixin + + +class EngineIONamespace(LoggingMixin): + 'Define engine.io client behavior' + + def __init__(self, io): + self._io = io + self._callback_by_event = {} + self._log_name = io._url + self.initialize() + + def initialize(self): + """Initialize custom variables here. + You can override this method.""" + + def on(self, event, callback): + 'Define a callback to handle an event emitted by the server' + self._callback_by_event[event] = callback + + def send(self, data): + 'Send a message' + self._io.send(data) + + def on_open(self): + """Called after engine.io connects. + You can override this method.""" + + def on_close(self): + """Called after engine.io disconnects. + You can override this method.""" + + def on_ping(self, data): + """Called after engine.io sends a ping packet. + You can override this method.""" + + def on_pong(self, data): + """Called after engine.io sends a pong packet. + You can override this method.""" + + def on_message(self, data): + """Called after engine.io sends a message packet. + You can override this method.""" + + def on_upgrade(self): + """Called after engine.io sends an upgrade packet. + You can override this method.""" + + def on_noop(self): + """Called after engine.io sends a noop packet. + You can override this method.""" + + def _find_packet_callback(self, event): + # Check callbacks defined by on() + try: + return self._callback_by_event[event] + except KeyError: + pass + # Check callbacks defined explicitly + return getattr(self, 'on_' + event) + + +class SocketIONamespace(EngineIONamespace): + 'Define socket.io client behavior' + + def __init__(self, io, path): + self.path = path + super(SocketIONamespace, self).__init__(io) + + def connect(self): + self._io.connect(self.path) + + def disconnect(self): + self._io.disconnect(self.path) + + def emit(self, event, *args, **kw): + self._io.emit(event, path=self.path, *args, **kw) + + def send(self, data='', callback=None): + self._io.send(data, callback) + + def on_connect(self): + """Called after socket.io connects. + You can override this method.""" + + def on_reconnect(self): + """Called after socket.io reconnects. + You can override this method.""" + + def on_disconnect(self): + """Called after socket.io disconnects. + You can override this method.""" + + def on_event(self, event, *args): + """ + Called if there is no matching event handler. + You can override this method. + There are three ways to define an event handler: + + - Call socketIO.on() + + socketIO = SocketIO('localhost', 8000) + socketIO.on('my_event', my_function) + + - Call namespace.on() + + namespace = socketIO.get_namespace() + namespace.on('my_event', my_function) + + - Define namespace.on_xxx + + class Namespace(SocketIONamespace): + + def on_my_event(self, *args): + my_function(*args) + + socketIO.define(Namespace)""" + + def on_error(self, data): + """Called after socket.io sends an error packet. + You can override this method.""" + + def _find_packet_callback(self, event): + # Interpret events + if event == 'connect': + if not hasattr(self, '_was_connected'): + self._was_connected = True + else: + event = 'reconnect' + # Check callbacks defined by on() + try: + return self._callback_by_event[event] + except KeyError: + pass + # Check callbacks defined explicitly or use on_event() + return getattr( + self, 'on_' + event.replace(' ', '_'), + lambda *args: self.on_event(event, *args)) + + +class LoggingEngineIONamespace(EngineIONamespace): + + def on_open(self): + self._debug('[engine.io open]') + super(LoggingEngineIONamespace, self).on_open() + + def on_close(self): + self._debug('[engine.io close]') + super(LoggingEngineIONamespace, self).on_close() + + def on_ping(self, data): + self._debug('[engine.io ping] %s', data) + super(LoggingEngineIONamespace, self).on_ping(data) + + def on_pong(self, data): + self._debug('[engine.io pong] %s', data) + super(LoggingEngineIONamespace, self).on_pong(data) + + def on_message(self, data): + self._debug('[engine.io message] %s', data) + super(LoggingEngineIONamespace, self).on_message(data) + + def on_upgrade(self): + self._debug('[engine.io upgrade]') + super(LoggingEngineIONamespace, self).on_upgrade() + + def on_noop(self): + self._debug('[engine.io noop]') + super(LoggingEngineIONamespace, self).on_noop() + + def on_event(self, event, *args): + callback, args = find_callback(args) + arguments = [repr(_) for _ in args] + if callback: + arguments.append('callback(*args)') + self._info('[engine.io event] %s(%s)', event, ', '.join(arguments)) + super(LoggingEngineIONamespace, self).on_event(event, *args) + + +class LoggingSocketIONamespace(SocketIONamespace, LoggingEngineIONamespace): + + def on_connect(self): + self._debug( + '%s[socket.io connect]', _make_logging_header(self.path)) + super(LoggingSocketIONamespace, self).on_connect() + + def on_reconnect(self): + self._debug( + '%s[socket.io reconnect]', _make_logging_header(self.path)) + super(LoggingSocketIONamespace, self).on_reconnect() + + def on_disconnect(self): + self._debug( + '%s[socket.io disconnect]', _make_logging_header(self.path)) + super(LoggingSocketIONamespace, self).on_disconnect() + + def on_event(self, event, *args): + callback, args = find_callback(args) + arguments = [repr(_) for _ in args] + if callback: + arguments.append('callback(*args)') + self._info( + '%s[socket.io event] %s(%s)', _make_logging_header(self.path), + event, ', '.join(arguments)) + super(LoggingSocketIONamespace, self).on_event(event, *args) + + def on_error(self, data): + self._debug( + '%s[socket.io error] %s', _make_logging_header(self.path), data) + super(LoggingSocketIONamespace, self).on_error() + + +def find_callback(args, kw=None): + 'Return callback whether passed as a last argument or as a keyword' + if args and callable(args[-1]): + return args[-1], args[:-1] + try: + return kw['callback'], args + except (KeyError, TypeError): + return None, args + + +def _make_logging_header(path): + return path + ' ' if path else '' diff -Nru python-socketio-client-0.5.3/socketIO_client/parsers.py python-socketio-client-0.6.5/socketIO_client/parsers.py --- python-socketio-client-0.5.3/socketIO_client/parsers.py 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/parsers.py 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,137 @@ +import json +from collections import namedtuple + +from .symmetries import ( + decode_string, encode_string, get_byte, get_character, parse_url) + + +EngineIOSession = namedtuple('EngineIOSession', [ + 'id', 'ping_interval', 'ping_timeout', 'transport_upgrades']) +SocketIOData = namedtuple('SocketIOData', ['path', 'ack_id', 'args']) + + +def parse_host(host, port, resource): + if not host.startswith('http'): + host = 'http://' + host + url_pack = parse_url(host) + is_secure = url_pack.scheme == 'https' + port = port or url_pack.port or (443 if is_secure else 80) + url = '%s:%d%s/%s' % (url_pack.hostname, port, url_pack.path, resource) + return is_secure, url + + +def parse_engineIO_session(engineIO_packet_data): + d = json.loads(decode_string(engineIO_packet_data)) + return EngineIOSession( + id=d['sid'], + ping_interval=d['pingInterval'] / float(1000), + ping_timeout=d['pingTimeout'] / float(1000), + transport_upgrades=d['upgrades']) + + +def encode_engineIO_content(engineIO_packets): + content = bytearray() + for packet_type, packet_data in engineIO_packets: + packet_text = format_packet_text(packet_type, packet_data) + content.extend(_make_packet_prefix(packet_text) + packet_text) + return content + + +def decode_engineIO_content(content): + content_index = 0 + content_length = len(content) + while content_index < content_length: + try: + content_index, packet_length = _read_packet_length( + content, content_index) + except IndexError: + break + content_index, packet_text = _read_packet_text( + content, content_index, packet_length) + engineIO_packet_type, engineIO_packet_data = parse_packet_text( + packet_text) + yield engineIO_packet_type, engineIO_packet_data + + +def format_socketIO_packet_data(path=None, ack_id=None, args=None): + socketIO_packet_data = json.dumps(args, ensure_ascii=False) if args else '' + if ack_id is not None: + socketIO_packet_data = str(ack_id) + socketIO_packet_data + if path: + socketIO_packet_data = path + ',' + socketIO_packet_data + return socketIO_packet_data + + +def parse_socketIO_packet_data(socketIO_packet_data): + data = decode_string(socketIO_packet_data) + if data.startswith('/'): + try: + path, data = data.split(',', 1) + except ValueError: + path = data + data = '' + else: + path = '' + try: + ack_id_string, data = data.split('[', 1) + data = '[' + data + ack_id = int(ack_id_string) + except (ValueError, IndexError): + ack_id = None + try: + args = json.loads(data) + except ValueError: + args = [] + return SocketIOData(path=path, ack_id=ack_id, args=args) + + +def format_packet_text(packet_type, packet_data): + return encode_string(str(packet_type) + packet_data) + + +def parse_packet_text(packet_text): + packet_type = int(get_character(packet_text, 0)) + packet_data = packet_text[1:] + return packet_type, packet_data + + +def get_namespace_path(socketIO_packet_data): + if not socketIO_packet_data.startswith(b'/'): + return '' + # Loop incrementally in case there is binary data + parts = [] + for i in range(len(socketIO_packet_data)): + character = get_character(socketIO_packet_data, i) + if ',' == character: + break + parts.append(character) + return ''.join(parts) + + +def _make_packet_prefix(packet): + length_string = str(len(packet)) + header_digits = bytearray([0]) + for i in range(len(length_string)): + header_digits.append(ord(length_string[i]) - 48) + header_digits.append(255) + return header_digits + + +def _read_packet_length(content, content_index): + while get_byte(content, content_index) != 0: + content_index += 1 + content_index += 1 + packet_length_string = '' + byte = get_byte(content, content_index) + while byte != 255: + packet_length_string += str(byte) + content_index += 1 + byte = get_byte(content, content_index) + return content_index, int(packet_length_string) + + +def _read_packet_text(content, content_index, packet_length): + while get_byte(content, content_index) == 255: + content_index += 1 + packet_text = content[content_index:content_index + packet_length] + return content_index + packet_length, packet_text diff -Nru python-socketio-client-0.5.3/socketIO_client/symmetries.py python-socketio-client-0.6.5/socketIO_client/symmetries.py --- python-socketio-client-0.5.3/socketIO_client/symmetries.py 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/symmetries.py 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,29 @@ +import six +try: + from urllib import urlencode as format_query +except ImportError: + from urllib.parse import urlencode as format_query +try: + from urlparse import urlparse as parse_url +except ImportError: + from urllib.parse import urlparse as parse_url +try: + memoryview = memoryview +except NameError: + memoryview = buffer + + +def get_character(x, index): + return chr(get_byte(x, index)) + + +def get_byte(x, index): + return six.indexbytes(x, index) + + +def encode_string(x): + return x.encode('utf-8') + + +def decode_string(x): + return x.decode('utf-8') diff -Nru python-socketio-client-0.5.3/socketIO_client/tests/index.html python-socketio-client-0.6.5/socketIO_client/tests/index.html --- python-socketio-client-0.5.3/socketIO_client/tests/index.html 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/tests/index.html 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,25 @@ +<script src="/socket.io/socket.io.js"></script> +<script> +var socket = io('//localhost'); +var chat = io('/chat'); +var news = io('/news'); + +socket.on('server_expects_callback', function(payload, fn) {fn(payload)}); +socket.emit('trigger_server_expects_callback', 'whee'); +socket.emit('emit'); +socket.emit('emit_with_payload'); +socket.emit('emit_with_multiple_payloads', 'aaa', 'bbb'); +socket.emit('emit_with_callback', function() {console.log('whee')}); +socket.emit('emit_with_callback_with_payload', function(x) {console.log('whee ' + x)}); +socket.emit('emit_with_callback_with_multiple_payloads', function(x, y) {console.log('whee ' + x + ' ' + y)}); +socket.emit('emit_with_event'); +socket.emit('aaa'); + +chat.on('server_expects_callback', function(payload, fn) {fn(payload)}); +chat.emit('trigger_server_expects_callback', 'whee'); +chat.emit('emit_with_payload'); +chat.emit('aaa'); + +news.emit('emit_with_payload'); +news.emit('aaa'); +</script> diff -Nru python-socketio-client-0.5.3/socketIO_client/tests/__init__.py python-socketio-client-0.6.5/socketIO_client/tests/__init__.py --- python-socketio-client-0.5.3/socketIO_client/tests/__init__.py 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/tests/__init__.py 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,192 @@ +import logging +import time +from unittest import TestCase + +from .. import SocketIO, LoggingNamespace, find_callback + + +HOST = 'localhost' +PORT = 9000 +DATA = 'xxx' +PAYLOAD = {'xxx': 'yyy'} +logging.basicConfig(level=logging.DEBUG) + + +class BaseMixin(object): + + def setUp(self): + super(BaseMixin, self).setUp() + self.called_on_response = False + self.wait_time_in_seconds = 1 + + def tearDown(self): + super(BaseMixin, self).tearDown() + self.socketIO.disconnect() + + def test_disconnect(self): + 'Disconnect' + namespace = self.socketIO.define(Namespace) + self.assertTrue(self.socketIO.connected) + self.assertFalse(namespace.called_on_disconnect) + self.socketIO.disconnect() + self.assertTrue(namespace.called_on_disconnect) + self.assertFalse(self.socketIO.connected) + + def test_emit(self): + 'Emit' + namespace = self.socketIO.define(Namespace) + self.socketIO.emit('emit') + self.socketIO.wait(self.wait_time_in_seconds) + self.assertEqual(namespace.args_by_event, { + 'emit_response': (), + }) + + def test_emit_with_payload(self): + 'Emit with payload' + namespace = self.socketIO.define(Namespace) + self.socketIO.emit('emit_with_payload', PAYLOAD) + self.socketIO.wait(self.wait_time_in_seconds) + self.assertEqual(namespace.args_by_event, { + 'emit_with_payload_response': (PAYLOAD,), + }) + + def test_emit_with_multiple_payloads(self): + 'Emit with multiple payloads' + namespace = self.socketIO.define(Namespace) + self.socketIO.emit('emit_with_multiple_payloads', PAYLOAD, PAYLOAD) + self.socketIO.wait(self.wait_time_in_seconds) + self.assertEqual(namespace.args_by_event, { + 'emit_with_multiple_payloads_response': (PAYLOAD, PAYLOAD), + }) + + def test_emit_with_callback(self): + 'Emit with callback' + self.socketIO.emit('emit_with_callback', self.on_response) + self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) + self.assertTrue(self.called_on_response) + + def test_emit_with_callback_with_payload(self): + 'Emit with callback with payload' + self.socketIO.emit( + 'emit_with_callback_with_payload', self.on_response) + self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) + self.assertTrue(self.called_on_response) + + def test_emit_with_callback_with_multiple_payloads(self): + 'Emit with callback with multiple payloads' + self.socketIO.emit( + 'emit_with_callback_with_multiple_payloads', self.on_response) + self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) + self.assertTrue(self.called_on_response) + + def test_emit_with_event(self): + 'Emit to trigger an event' + self.socketIO.on('emit_with_event_response', self.on_response) + self.socketIO.emit('emit_with_event', PAYLOAD) + self.socketIO.wait(self.wait_time_in_seconds) + self.assertTrue(self.called_on_response) + + def test_send(self): + 'Send' + namespace = self.socketIO.define(Namespace) + self.socketIO.send() + self.socketIO.wait(self.wait_time_in_seconds) + self.assertEqual(namespace.response, 'message_response') + + def test_send_with_data(self): + 'Send with data' + namespace = self.socketIO.define(Namespace) + self.socketIO.send(DATA) + self.socketIO.wait(self.wait_time_in_seconds) + self.assertEqual(namespace.response, DATA) + + def test_ack(self): + 'Respond to a server callback request' + namespace = self.socketIO.define(Namespace) + self.socketIO.emit('trigger_server_expects_callback', PAYLOAD) + self.socketIO.wait(self.wait_time_in_seconds) + self.assertEqual(namespace.args_by_event, { + 'server_expects_callback': (PAYLOAD,), + 'server_received_callback': (PAYLOAD,), + }) + + def test_wait_with_disconnect(self): + 'Exit loop when the client wants to disconnect' + self.socketIO.define(Namespace) + self.socketIO.disconnect() + timeout_in_seconds = 5 + start_time = time.time() + self.socketIO.wait(timeout_in_seconds) + self.assertTrue(time.time() - start_time < timeout_in_seconds) + + def test_namespace_emit(self): + 'Behave differently in different namespaces' + main_namespace = self.socketIO.define(Namespace) + chat_namespace = self.socketIO.define(Namespace, '/chat') + news_namespace = self.socketIO.define(Namespace, '/news') + news_namespace.emit('emit_with_payload', PAYLOAD) + self.socketIO.wait(self.wait_time_in_seconds) + self.assertEqual(main_namespace.args_by_event, {}) + self.assertEqual(chat_namespace.args_by_event, {}) + self.assertEqual(news_namespace.args_by_event, { + 'emit_with_payload_response': (PAYLOAD,), + }) + + def test_namespace_ack(self): + 'Respond to a server callback request within a namespace' + chat_namespace = self.socketIO.define(Namespace, '/chat') + chat_namespace.emit('trigger_server_expects_callback', PAYLOAD) + self.socketIO.wait(self.wait_time_in_seconds) + self.assertEqual(chat_namespace.args_by_event, { + 'server_expects_callback': (PAYLOAD,), + 'server_received_callback': (PAYLOAD,), + }) + + def on_response(self, *args): + for arg in args: + if isinstance(arg, dict): + self.assertEqual(arg, PAYLOAD) + else: + self.assertEqual(arg, DATA) + self.called_on_response = True + + +class Test_XHR_PollingTransport(BaseMixin, TestCase): + + def setUp(self): + super(Test_XHR_PollingTransport, self).setUp() + self.socketIO = SocketIO(HOST, PORT, LoggingNamespace, transports=[ + 'xhr-polling'], verify=False) + self.assertEqual(self.socketIO.transport_name, 'xhr-polling') + + +class Test_WebsocketTransport(BaseMixin, TestCase): + + def setUp(self): + super(Test_WebsocketTransport, self).setUp() + self.socketIO = SocketIO(HOST, PORT, LoggingNamespace, transports=[ + 'xhr-polling', 'websocket'], verify=False) + self.assertEqual(self.socketIO.transport_name, 'websocket') + + +class Namespace(LoggingNamespace): + + def initialize(self): + self.called_on_disconnect = False + self.args_by_event = {} + self.response = None + + def on_disconnect(self): + self.called_on_disconnect = True + + def on_wait_with_disconnect_response(self): + self.disconnect() + + def on_event(self, event, *args): + callback, args = find_callback(args) + if callback: + callback(*args) + self.args_by_event[event] = args + + def on_message(self, data): + self.response = data diff -Nru python-socketio-client-0.5.3/socketIO_client/tests/proxy.js python-socketio-client-0.6.5/socketIO_client/tests/proxy.js --- python-socketio-client-0.5.3/socketIO_client/tests/proxy.js 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/tests/proxy.js 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,36 @@ +var proxy = require('http-proxy').createProxyServer({ + target: {host: 'localhost', port: 9000} +}).on('error', function(err, req, res) { + console.log('[ERROR] %s', err); + res.end(); +}); +var server = require('http').createServer(function(req, res) { + console.log('[REQUEST.%s] %s', req.method, req.url); + console.log(req['headers']); + if (req.method == 'POST') { + var body = ''; + req.on('data', function (data) { + body += data; + }); + req.on('end', function () { + print_body('[REQUEST.BODY] ', body); + }); + } + var write = res.write; + res.write = function(data) { + print_body('[RESPONSE.BODY] ', data); + write.call(res, data); + } + proxy.web(req, res); +}); +function print_body(header, body) { + var text = String(body); + console.log(header + text); + if (text.charCodeAt(0) != 0) return; + for (var i = 0; i < text.length; i++) { + var character_code = text.charCodeAt(i); + console.log('body[%s] = %s = %s', i, text[i], character_code); + if (character_code == 65533) break; + } +} +server.listen(8000); diff -Nru python-socketio-client-0.5.3/socketIO_client/tests/serve.js python-socketio-client-0.6.5/socketIO_client/tests/serve.js --- python-socketio-client-0.5.3/socketIO_client/tests/serve.js 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/tests/serve.js 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,98 @@ +// DEBUG=* node serve.js + +var argv = require('yargs').argv; +if (argv.secure) { + var fs = require('fs'); + var path = require('path'); + var app = require('https').createServer({ + key: fs.readFileSync(path.resolve(__dirname, 'ssl.key')), + cert: fs.readFileSync(path.resolve(__dirname, 'ssl.crt')) + }, serve); +} else { + var app = require('http').createServer(serve); +} +app.listen(9000); + +var io = require('socket.io')(app); +var PAYLOAD = {'xxx': 'yyy'}; + +io.on('connection', function(socket) { + socket.on('message', function(data, fn) { + if (fn) { + // Client requests callback + if (data) { + fn(data); + } else { + fn(); + } + } else if (typeof data === 'object') { + // Data has type object or is null + socket.json.send(data ? data : 'message_response'); + } else { + // Data has type string or is '' + socket.send(data ? data : 'message_response'); + } + }); + socket.on('emit', function() { + socket.emit('emit_response'); + }); + socket.on('emit_with_payload', function(payload) { + socket.emit('emit_with_payload_response', payload); + }); + socket.on('emit_with_multiple_payloads', function(payload1, payload2) { + socket.emit('emit_with_multiple_payloads_response', payload1, payload2); + }); + socket.on('emit_with_callback', function(fn) { + fn(); + }); + socket.on('emit_with_callback_with_payload', function(fn) { + fn(PAYLOAD); + }); + socket.on('emit_with_callback_with_multiple_payloads', function(fn) { + fn(PAYLOAD, PAYLOAD); + }); + socket.on('emit_with_event', function(payload) { + socket.emit('emit_with_event_response', payload); + }); + socket.on('trigger_server_expects_callback', function(payload) { + socket.emit('server_expects_callback', payload, function(payload) { + socket.emit('server_received_callback', payload); + }); + }); + socket.on('aaa', function() { + socket.emit('aaa_response', PAYLOAD); + }); + socket.on('bbb', function(payload, fn) { + if (fn) fn(payload); + }); +}); + +io.of('/chat').on('connection', function(socket) { + socket.on('emit_with_payload', function(payload) { + socket.emit('emit_with_payload_response', payload); + }); + socket.on('aaa', function() { + socket.emit('aaa_response', 'in chat'); + }); + socket.on('trigger_server_expects_callback', function(payload) { + socket.emit('server_expects_callback', payload, function(payload) { + socket.emit('server_received_callback', payload); + }); + }); +}); + +io.of('/news').on('connection', function(socket) { + socket.on('emit_with_payload', function(payload) { + socket.emit('emit_with_payload_response', payload); + }); + socket.on('aaa', function() { + socket.emit('aaa_response', 'in news'); + }); +}); + +function serve(req, res) { + fs.readFile(__dirname + '/index.html', function(err, data) { + res.writeHead(200); + res.end(data); + }); +} diff -Nru python-socketio-client-0.5.3/socketIO_client/tests/ssl.crt python-socketio-client-0.6.5/socketIO_client/tests/ssl.crt --- python-socketio-client-0.5.3/socketIO_client/tests/ssl.crt 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/tests/ssl.crt 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDZTCCAk2gAwIBAgIJAK1HKQ8zF3cCMA0GCSqGSIb3DQEBBQUAMEkxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJOWTELMAkGA1UEBwwCTlkxDDAKBgNVBAoMA1hZWjES +MBAGA1UEAwwJbG9jYWxob3N0MB4XDTE1MDQxNTE5NDUwNFoXDTE2MDQxNDE5NDUw +NFowSTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMQswCQYDVQQHDAJOWTEMMAoG +A1UECgwDWFlaMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDAQUM9+xbiDeJXg+7X6HgXwla2AnGKWbZ11hZZUYbQwHyq +ABDSqRQXVWvzac6b59/trZiJ7cQEH4+c8ln1C4qbCLvr1aWkL1BDAtSbFUFhQ2Sb +R/xkSUpq35yTuR5+oHgahDg1gbgXgPhB3Y6HoBlYMSpSUKF+INu354kxfYi0t4tP +8f309KUe6eQH3gXgTBR7pPJEUpaPOsrk6UR3cHCMqyzHulyfhgvkk5FN+EtSR9ex +dIrF6WXmfynhsAa/+bxbsgeBF9MNj3zvckCzxdQStdqOvy0mu40/7i9vwguh9cRo +HDn6lx5EaE+gSGU48UNnKX5iQdqEhprNVDj31MiJAgMBAAGjUDBOMB0GA1UdDgQW +BBRkFsPxYU+e6ZSFwmzoS45qiOzAaDAfBgNVHSMEGDAWgBRkFsPxYU+e6ZSFwmzo +S45qiOzAaDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQB4JyOA5bZ3 +NbkMvOjpDw+tKzcNiXZIkGfoQ8NC1DbGVhgG7Ps4VjXgUB552YSUV7iwyd3G78OC ++cxEcr+BvxXHXL2Wlxy0c/ZgBjRI5VnGbYQjjI2Iy2qJV+x5IR2oZatv45soZSLq +NFCg2KpOgcSRgs0oDGVBYO0d9m73s/kOySj2NGqVJsaQXqXtLWKnqToaCfl4Vnl+ +zcMdUv8ajBZEPRg6oNi2QIvcNT8fS5gd/T4OXBa7pYuC79yOZ1X6bkKsZrcAdNGM +zO/jH6jKFjIBBx1Of+uZTzfAj/eoTu3foPuUQ+Z9NNE2nkE6SLyBSlxE7wD+SfjS +4/J0PNj22Uh3 +-----END CERTIFICATE----- diff -Nru python-socketio-client-0.5.3/socketIO_client/tests/ssl.key python-socketio-client-0.6.5/socketIO_client/tests/ssl.key --- python-socketio-client-0.5.3/socketIO_client/tests/ssl.key 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/tests/ssl.key 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDAQUM9+xbiDeJX +g+7X6HgXwla2AnGKWbZ11hZZUYbQwHyqABDSqRQXVWvzac6b59/trZiJ7cQEH4+c +8ln1C4qbCLvr1aWkL1BDAtSbFUFhQ2SbR/xkSUpq35yTuR5+oHgahDg1gbgXgPhB +3Y6HoBlYMSpSUKF+INu354kxfYi0t4tP8f309KUe6eQH3gXgTBR7pPJEUpaPOsrk +6UR3cHCMqyzHulyfhgvkk5FN+EtSR9exdIrF6WXmfynhsAa/+bxbsgeBF9MNj3zv +ckCzxdQStdqOvy0mu40/7i9vwguh9cRoHDn6lx5EaE+gSGU48UNnKX5iQdqEhprN +VDj31MiJAgMBAAECggEANAFzbxC83+lhkMrfkQgRdFvdmN6QWBxsfvOql/61uUJY +dqQN6O5TwPwad33npcTTjjenS6hFndfrwUjNjLvSgp2aN/FTHVavH3FkkY7uYKEa +VebjHz20I7TZZhxtY1OFKacajV7JrZH1lduY8pccQ/8Is7ub88JvrQ+0zO5oTHnh +KEPYY5r2wLxKrzGm0NavRW9MpiHxz1vUGykvaGq9vR8dVFvZlLC5szYII+BvlII+ +78XMnZbJ9ahT7dzfnzPdPPuyP3m4cdJ9c+7Advs0g2F3K/IDL3jZZCRZIaLxHIs0 +PeI17teW0OmK4RWrnf6dSf0bww05x5In8GzUYgppAQKBgQD4lJVi3UmAB319CGeP +NE4cZFuMneNsuCkNEJONb8Cfsah2maM0if8tUNoR96JorjWgkUTG4oThGSQgJQw8 +fPy6cW4EUhSvWCO+Q4MFFWpTcf0hBiz5O1d06FHVo39o8Ct9dv2bxJqfNtCUUf31 +Fz5tvA+wvByOSazUC3AowQZ6FwKBgQDF/ksJbOBd/bu3ss7eJRjE2sXmuhfxrUiu +P5RoOEqHROAifatJk/3hwT6lx2y1g+3kJpZm9V16dNTkcuybL0yJ/VBE3uWuodrj +i9+wcg8XSnRp3BPVKzebKIKDTMdypOeb1f5yhx6cCtChRm1frKQdoXpMQqptM0jq +w3B4bryWXwKBgQCWSv+nLrPpvJ2aoyI56x3u/J59fliquw3W4FbWBOMpqnh4fJu4 +gFbQRzoR8u82611xH2O9++brUhANf1jOmaMT9tDVu+rVuSyjNJ5azH/kw96PwPQg +HEjcXjpcOOYnxE4HJZJgQ5ZY/QNPKeOp88vC/RlfedyqCtF7ww6lFU+dMQKBgQC2 +M7ut4sne9R8If74rZAwVLBauq1ZZi1O1NsFF33eGX/W7B9bXER+z3vfd61W4/L2x +FWmXOflaNaWsza27aZ2P5tM1bcIEIOKkQBYL9Aq7LkNPH74Ij4rOeEsStVddwy94 +k0di8cFTbAhuQbdpMiCdO/qlrzvS3j0d/djEm3NlFQKBgQCpIrHaMcckCFsf2Y6o +zMnbi3859hve94OOJjauQLlw/nRE/+OaDsDN8iJoxnK0seek8ro1ixSBTScpuX8W +G2DBgqs9NrSQLe6FAckkGqVJdluoh5GewNneAcowkkauj2srnb6XtJDhFtTDY141 +EPbeqGB9PUY9Ny8VzHkAb1vi6g== +-----END PRIVATE KEY----- diff -Nru python-socketio-client-0.5.3/socketIO_client/tests.py python-socketio-client-0.6.5/socketIO_client/tests.py --- python-socketio-client-0.5.3/socketIO_client/tests.py 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/tests.py 1970-01-01 02:00:00.000000000 +0200 @@ -1,219 +0,0 @@ -import logging -import time -from unittest import TestCase - -from . import SocketIO, BaseNamespace, find_callback -from .transports import TIMEOUT_IN_SECONDS - - -HOST = 'localhost' -PORT = 8000 -DATA = 'xxx' -PAYLOAD = {'xxx': 'yyy'} -logging.basicConfig(level=logging.DEBUG) - - -class BaseMixin(object): - - def setUp(self): - self.called_on_response = False - - def tearDown(self): - del self.socketIO - - def on_response(self, *args): - for arg in args: - if isinstance(arg, dict): - self.assertEqual(arg, PAYLOAD) - else: - self.assertEqual(arg, DATA) - self.called_on_response = True - - def test_disconnect(self): - 'Disconnect' - self.assertTrue(self.socketIO.connected) - self.socketIO.disconnect() - self.assertFalse(self.socketIO.connected) - # Use context manager - with SocketIO(HOST, PORT, Namespace) as self.socketIO: - namespace = self.socketIO.get_namespace() - self.assertFalse(namespace.called_on_disconnect) - self.assertTrue(self.socketIO.connected) - self.assertTrue(namespace.called_on_disconnect) - self.assertFalse(self.socketIO.connected) - - def test_message(self): - 'Message' - namespace = self.socketIO.define(Namespace) - self.socketIO.message() - self.socketIO.wait(self.wait_time_in_seconds) - self.assertEqual(namespace.response, 'message_response') - - def test_message_with_data(self): - 'Message with data' - namespace = self.socketIO.define(Namespace) - self.socketIO.message(DATA) - self.socketIO.wait(self.wait_time_in_seconds) - self.assertEqual(namespace.response, DATA) - - def test_message_with_payload(self): - 'Message with payload' - namespace = self.socketIO.define(Namespace) - self.socketIO.message(PAYLOAD) - self.socketIO.wait(self.wait_time_in_seconds) - self.assertEqual(namespace.response, PAYLOAD) - - def test_message_with_callback(self): - 'Message with callback' - self.socketIO.message(callback=self.on_response) - self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) - self.assertTrue(self.called_on_response) - - def test_message_with_callback_with_data(self): - 'Message with callback with data' - self.socketIO.message(DATA, self.on_response) - self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) - self.assertTrue(self.called_on_response) - - def test_emit(self): - 'Emit' - namespace = self.socketIO.define(Namespace) - self.socketIO.emit('emit') - self.socketIO.wait(self.wait_time_in_seconds) - self.assertEqual(namespace.args_by_event, { - 'emit_response': (), - }) - - def test_emit_with_payload(self): - 'Emit with payload' - namespace = self.socketIO.define(Namespace) - self.socketIO.emit('emit_with_payload', PAYLOAD) - self.socketIO.wait(self.wait_time_in_seconds) - self.assertEqual(namespace.args_by_event, { - 'emit_with_payload_response': (PAYLOAD,), - }) - - def test_emit_with_multiple_payloads(self): - 'Emit with multiple payloads' - namespace = self.socketIO.define(Namespace) - self.socketIO.emit('emit_with_multiple_payloads', PAYLOAD, PAYLOAD) - self.socketIO.wait(self.wait_time_in_seconds) - self.assertEqual(namespace.args_by_event, { - 'emit_with_multiple_payloads_response': (PAYLOAD, PAYLOAD), - }) - - def test_emit_with_callback(self): - 'Emit with callback' - self.socketIO.emit('emit_with_callback', self.on_response) - self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) - self.assertTrue(self.called_on_response) - - def test_emit_with_callback_with_payload(self): - 'Emit with callback with payload' - self.socketIO.emit( - 'emit_with_callback_with_payload', self.on_response) - self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) - self.assertTrue(self.called_on_response) - - def test_emit_with_callback_with_multiple_payloads(self): - 'Emit with callback with multiple payloads' - self.socketIO.emit( - 'emit_with_callback_with_multiple_payloads', self.on_response) - self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) - self.assertTrue(self.called_on_response) - - def test_emit_with_event(self): - 'Emit to trigger an event' - self.socketIO.on('emit_with_event_response', self.on_response) - self.socketIO.emit('emit_with_event', PAYLOAD) - self.socketIO.wait(self.wait_time_in_seconds) - self.assertTrue(self.called_on_response) - - def test_ack(self): - 'Trigger server callback' - namespace = self.socketIO.define(Namespace) - self.socketIO.emit('ack', PAYLOAD) - self.socketIO.wait(self.wait_time_in_seconds) - self.assertEqual(namespace.args_by_event, { - 'ack_response': (PAYLOAD,), - 'ack_callback_response': (PAYLOAD,), - }) - - def test_wait_with_disconnect(self): - 'Exit loop when the client wants to disconnect' - self.socketIO.define(Namespace) - self.socketIO.emit('wait_with_disconnect') - timeout_in_seconds = 5 - start_time = time.time() - self.socketIO.wait(timeout_in_seconds) - self.assertTrue(time.time() - start_time < timeout_in_seconds) - - def test_namespace_emit(self): - 'Behave differently in different namespaces' - main_namespace = self.socketIO.define(Namespace) - chat_namespace = self.socketIO.define(Namespace, '/chat') - news_namespace = self.socketIO.define(Namespace, '/news') - news_namespace.emit('emit_with_payload', PAYLOAD) - self.socketIO.wait(self.wait_time_in_seconds) - self.assertEqual(main_namespace.args_by_event, {}) - self.assertEqual(chat_namespace.args_by_event, {}) - self.assertEqual(news_namespace.args_by_event, { - 'emit_with_payload_response': (PAYLOAD,), - }) - - def test_namespace_ack(self): - 'Trigger server callback' - chat_namespace = self.socketIO.define(Namespace, '/chat') - chat_namespace.emit('ack', PAYLOAD) - self.socketIO.wait(self.wait_time_in_seconds) - self.assertEqual(chat_namespace.args_by_event, { - 'ack_response': (PAYLOAD,), - 'ack_callback_response': (PAYLOAD,), - }) - - -class Test_WebsocketTransport(TestCase, BaseMixin): - - def setUp(self): - super(Test_WebsocketTransport, self).setUp() - self.socketIO = SocketIO(HOST, PORT, transports=['websocket']) - self.wait_time_in_seconds = 0.1 - - -class Test_XHR_PollingTransport(TestCase, BaseMixin): - - def setUp(self): - super(Test_XHR_PollingTransport, self).setUp() - self.socketIO = SocketIO(HOST, PORT, transports=['xhr-polling']) - self.wait_time_in_seconds = TIMEOUT_IN_SECONDS + 1 - - -class Test_JSONP_PollingTransport(TestCase, BaseMixin): - - def setUp(self): - super(Test_JSONP_PollingTransport, self).setUp() - self.socketIO = SocketIO(HOST, PORT, transports=['jsonp-polling']) - self.wait_time_in_seconds = TIMEOUT_IN_SECONDS + 1 - - -class Namespace(BaseNamespace): - - def initialize(self): - self.response = None - self.args_by_event = {} - self.called_on_disconnect = False - - def on_disconnect(self): - self.called_on_disconnect = True - - def on_message(self, data): - self.response = data - - def on_event(self, event, *args): - callback, args = find_callback(args) - if callback: - callback(*args) - self.args_by_event[event] = args - - def on_wait_with_disconnect_response(self): - self.disconnect() diff -Nru python-socketio-client-0.5.3/socketIO_client/transports.py python-socketio-client-0.6.5/socketIO_client/transports.py --- python-socketio-client-0.5.3/socketIO_client/transports.py 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/socketIO_client/transports.py 2015-06-01 23:16:32.000000000 +0300 @@ -1,332 +1,199 @@ -import json -import logging -import re import requests import six import socket +import ssl +import sys +import threading import time import websocket -from itertools import izip -from .exceptions import SocketIOError, ConnectionError, TimeoutError +from .exceptions import ConnectionError, TimeoutError +from .parsers import ( + encode_engineIO_content, decode_engineIO_content, + format_packet_text, parse_packet_text) +from .symmetries import format_query, memoryview, parse_url -TRANSPORTS = 'websocket', 'xhr-polling', 'jsonp-polling' -BOUNDARY = six.u('\ufffd') -TIMEOUT_IN_SECONDS = 3 -_log = logging.getLogger(__name__) +if not hasattr(websocket, 'create_connection'): + sys.exit("""\ +An incompatible websocket library is conflicting with the one we need. +You can remove the incompatible library and install the correct one +by running the following commands: +yes | pip uninstall websocket websocket-client +pip install -U websocket-client""") -class _AbstractTransport(object): - def __init__(self): - self._packet_id = 0 - self._callback_by_packet_id = {} - self._wants_to_disconnect = False - self._packets = [] +ENGINEIO_PROTOCOL = 3 +TRANSPORTS = 'xhr-polling', 'websocket' - def disconnect(self, path=''): - if not path: - self._wants_to_disconnect = True - if not self.connected: - return - if path: - self.send_packet(0, path) - else: - self.close() - def connect(self, path): - self.send_packet(1, path) +class AbstractTransport(object): + + def __init__(self, http_session, is_secure, url, engineIO_session=None): + self.http_session = http_session + self.is_secure = is_secure + self.url = url + self.engineIO_session = engineIO_session + + def recv_packet(self): + pass + + def send_packet(self, engineIO_packet_type, engineIO_packet_data=''): + pass + + def set_timeout(self, seconds=None): + pass - def send_heartbeat(self): - self.send_packet(2) - def message(self, path, data, callback): - if isinstance(data, basestring): - code = 3 +class XHR_PollingTransport(AbstractTransport): + + def __init__(self, http_session, is_secure, url, engineIO_session=None): + super(XHR_PollingTransport, self).__init__( + http_session, is_secure, url, engineIO_session) + self._params = { + 'EIO': ENGINEIO_PROTOCOL, 'transport': 'polling'} + if engineIO_session: + self._request_index = 1 + self._kw_get = dict( + timeout=engineIO_session.ping_timeout) + self._kw_post = dict( + timeout=engineIO_session.ping_timeout, + headers={'content-type': 'application/octet-stream'}) + self._params['sid'] = engineIO_session.id else: - code = 4 - data = json.dumps(data, ensure_ascii=False) - self.send_packet(code, path, data, callback) - - def emit(self, path, event, args, callback): - data = json.dumps(dict(name=event, args=args), ensure_ascii=False) - self.send_packet(5, path, data, callback) - - def ack(self, path, packet_id, *args): - packet_id = packet_id.rstrip('+') - data = '%s+%s' % ( - packet_id, - json.dumps(args, ensure_ascii=False), - ) if args else packet_id - self.send_packet(6, path, data) - - def noop(self, path=''): - self.send_packet(8, 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, data - packet_text = ':'.join(packet_parts) - self.send(packet_text) - _log.debug('[packet sent] %s', packet_text) + self._request_index = 0 + self._kw_get = {} + self._kw_post = {} + http_scheme = 'https' if is_secure else 'http' + self._http_url = '%s://%s/' % (http_scheme, url) + self._request_index_lock = threading.Lock() + self._send_packet_lock = threading.Lock() def recv_packet(self): + params = dict(self._params) + params['t'] = self._get_timestamp() + response = get_response( + self.http_session.get, + self._http_url, + params=params, + **self._kw_get) + for engineIO_packet in decode_engineIO_content(response.content): + engineIO_packet_type, engineIO_packet_data = engineIO_packet + yield engineIO_packet_type, engineIO_packet_data + + def send_packet(self, engineIO_packet_type, engineIO_packet_data=''): + with self._send_packet_lock: + params = dict(self._params) + params['t'] = self._get_timestamp() + data = encode_engineIO_content([ + (engineIO_packet_type, engineIO_packet_data), + ]) + response = get_response( + self.http_session.post, + self._http_url, + params=params, + data=memoryview(data), + **self._kw_post) + assert response.content == b'ok' + + def _get_timestamp(self): + with self._request_index_lock: + timestamp = '%s-%s' % ( + int(time.time() * 1000), self._request_index) + self._request_index += 1 + return timestamp + + +class WebsocketTransport(AbstractTransport): + + def __init__(self, http_session, is_secure, url, engineIO_session=None): + super(WebsocketTransport, self).__init__( + http_session, is_secure, url, engineIO_session) + params = dict(http_session.params, **{ + 'EIO': ENGINEIO_PROTOCOL, 'transport': 'websocket'}) + request = http_session.prepare_request(requests.Request('GET', url)) + kw = {'header': ['%s: %s' % x for x in request.headers.items()]} + if engineIO_session: + params['sid'] = engineIO_session.id + kw['timeout'] = self._timeout = engineIO_session.ping_timeout + ws_url = '%s://%s/?%s' % ( + 'wss' if is_secure else 'ws', url, format_query(params)) + http_scheme = 'https' if is_secure else 'http' + if http_scheme in http_session.proxies: # Use the correct proxy + proxy_url_pack = parse_url(http_session.proxies[http_scheme]) + kw['http_proxy_host'] = proxy_url_pack.hostname + kw['http_proxy_port'] = proxy_url_pack.port + if proxy_url_pack.username: + kw['http_proxy_auth'] = ( + proxy_url_pack.username, proxy_url_pack.password) + if http_session.verify: + if http_session.cert: # Specify certificate path on disk + if isinstance(http_session.cert, basestring): + kw['ca_certs'] = http_session.cert + else: + kw['ca_certs'] = http_session.cert[0] + else: # Do not verify the SSL certificate + kw['sslopt'] = {'cert_reqs': ssl.CERT_NONE} try: - while self._packets: - yield self._packets.pop(0) - except IndexError: - pass - for packet_text in self.recv(): - _log.debug('[packet received] %s', packet_text) - try: - packet_parts = packet_text.split(':', 3) - except AttributeError: - _log.warn('[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 - - def _enqueue_packet(self, packet): - self._packets.append(packet) - - def set_ack_callback(self, callback): - 'Set callback to be called after server sends an acknowledgment' - self._packet_id += 1 - self._callback_by_packet_id[str(self._packet_id)] = callback - return '%s+' % self._packet_id - - def get_ack_callback(self, packet_id): - 'Get callback to be called after server sends an acknowledgment' - callback = self._callback_by_packet_id[packet_id] - del self._callback_by_packet_id[packet_id] - return callback - - @property - def has_ack_callback(self): - return True if self._callback_by_packet_id else False - - -class _WebsocketTransport(_AbstractTransport): - - def __init__(self, socketIO_session, is_secure, base_url, **kw): - super(_WebsocketTransport, self).__init__() - url = '%s://%s/websocket/%s' % ( - 'wss' if is_secure else 'ws', - base_url, socketIO_session.id) - try: - self._connection = websocket.create_connection(url) - except socket.timeout as e: - raise ConnectionError(e) - except socket.error as e: + self._connection = websocket.create_connection(ws_url, **kw) + except Exception as e: raise ConnectionError(e) - self._connection.settimeout(TIMEOUT_IN_SECONDS) - @property - def connected(self): - return self._connection.connected - - def send(self, packet_text): + def recv_packet(self): try: - self._connection.send(packet_text) + packet_text = self._connection.recv() except websocket.WebSocketTimeoutException as e: - message = 'timed out while sending %s (%s)' % (packet_text, e) - _log.warn(message) - raise TimeoutError(e) + raise TimeoutError('recv timed out (%s)' % e) + except websocket.SSLError as e: + raise ConnectionError('recv disconnected by SSL (%s)' % e) + except websocket.WebSocketConnectionClosedException as e: + raise ConnectionError('recv disconnected (%s)' % e) except socket.error as e: - message = 'disconnected while sending %s (%s)' % (packet_text, e) - _log.warn(message) - raise ConnectionError(message) + raise ConnectionError('recv disconnected (%s)' % e) + engineIO_packet_type, engineIO_packet_data = parse_packet_text( + six.b(packet_text)) + yield engineIO_packet_type, engineIO_packet_data - def recv(self): + def send_packet(self, engineIO_packet_type, engineIO_packet_data=''): + packet = format_packet_text(engineIO_packet_type, engineIO_packet_data) try: - yield self._connection.recv() + self._connection.send(packet) except websocket.WebSocketTimeoutException as e: - raise TimeoutError(e) - except websocket.SSLError as e: - raise ConnectionError(e) - except websocket.WebSocketConnectionClosedException as e: - raise ConnectionError('connection closed (%s)' % e) + raise TimeoutError('send timed out (%s)' % e) except socket.error as e: - raise ConnectionError(e) - - def close(self): - self._connection.close() - - -class _XHR_PollingTransport(_AbstractTransport): + raise ConnectionError('send disconnected (%s)' % e) + except websocket.WebSocketConnectionClosedException as e: + raise ConnectionError('send disconnected (%s)' % e) - def __init__(self, socketIO_session, is_secure, base_url, **kw): - super(_XHR_PollingTransport, self).__init__() - self._url = '%s://%s/xhr-polling/%s' % ( - 'https' if is_secure else 'http', - base_url, socketIO_session.id) - self._connected = True - self._http_session = _prepare_http_session(kw) - # Create connection - for packet in self.recv_packet(): - self._enqueue_packet(packet) - - @property - def connected(self): - return self._connected - - @property - def _params(self): - return dict(t=int(time.time())) - - def send(self, packet_text): - _get_response( - self._http_session.post, - self._url, - params=self._params, - data=packet_text, - timeout=TIMEOUT_IN_SECONDS) - - def recv(self): - response = _get_response( - self._http_session.get, - self._url, - params=self._params, - timeout=TIMEOUT_IN_SECONDS) - response_text = response.text - if not response_text.startswith(BOUNDARY): - yield response_text - return - for packet_text in _yield_text_from_framed_data(response_text): - yield packet_text - - def close(self): - _get_response( - self._http_session.get, - self._url, - params=dict(self._params.items() + [('disconnect', True)])) - self._connected = False - - -class _JSONP_PollingTransport(_AbstractTransport): - - RESPONSE_PATTERN = re.compile(r'io.j\[(\d+)\]\("(.*)"\);') - - def __init__(self, socketIO_session, is_secure, base_url, **kw): - super(_JSONP_PollingTransport, self).__init__() - self._url = '%s://%s/jsonp-polling/%s' % ( - 'https' if is_secure else 'http', - base_url, socketIO_session.id) - self._connected = True - self._http_session = _prepare_http_session(kw) - self._id = 0 - # Create connection - for packet in self.recv_packet(): - self._enqueue_packet(packet) - - @property - def connected(self): - return self._connected - - @property - def _params(self): - return dict(t=int(time.time()), i=self._id) - - def send(self, packet_text): - _get_response( - self._http_session.post, - self._url, - params=self._params, - data='d=%s' % requests.utils.quote(json.dumps(packet_text)), - headers={'content-type': 'application/x-www-form-urlencoded'}, - timeout=TIMEOUT_IN_SECONDS) - - def recv(self): - 'Decode the JavaScript response so that we can parse it as JSON' - response = _get_response( - self._http_session.get, - self._url, - params=self._params, - headers={'content-type': 'text/javascript; charset=UTF-8'}, - timeout=TIMEOUT_IN_SECONDS) - response_text = response.text - try: - self._id, response_text = self.RESPONSE_PATTERN.match( - response_text).groups() - except AttributeError: - _log.warn('[packet error] %s', response_text) - return - if not response_text.startswith(BOUNDARY): - yield response_text.decode('unicode_escape') - return - for packet_text in _yield_text_from_framed_data( - response_text, parse=lambda x: x.decode('unicode_escape')): - yield packet_text - - def close(self): - _get_response( - self._http_session.get, - self._url, - params=dict(self._params.items() + [('disconnect', True)])) - self._connected = False - - -def _negotiate_transport( - client_supported_transports, session, - is_secure, base_url, **kw): - server_supported_transports = session.server_supported_transports - for supported_transport in client_supported_transports: - if supported_transport in server_supported_transports: - _log.debug('[transport selected] %s', supported_transport) - return { - 'websocket': _WebsocketTransport, - 'xhr-polling': _XHR_PollingTransport, - 'jsonp-polling': _JSONP_PollingTransport, - }[supported_transport](session, is_secure, base_url, **kw) - raise SocketIOError(' '.join([ - 'could not negotiate a transport:', - 'client supports %s but' % ', '.join(client_supported_transports), - 'server supports %s' % ', '.join(server_supported_transports), - ])) - - -def _yield_text_from_framed_data(framed_data, parse=lambda x: x): - parts = [parse(x) for x in framed_data.split(BOUNDARY)] - for text_length, text in izip(parts[1::2], parts[2::2]): - if text_length != str(len(text)): - warning = 'invalid declared length=%s for packet_text=%s' % ( - text_length, text) - _log.warn('[packet error] %s', warning) - continue - yield text + def set_timeout(self, seconds=None): + self._connection.settimeout(seconds or self._timeout) -def _get_response(request, *args, **kw): +def get_response(request, *args, **kw): try: - response = request(*args, **kw) + response = request(*args, stream=True, **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) - status = response.status_code - if 200 != status: - raise ConnectionError('unexpected status code (%s)' % status) + status_code = response.status_code + if 200 != status_code: + raise ConnectionError('unexpected status code (%s %s)' % ( + status_code, response.text)) return response -def _prepare_http_session(kw): +def prepare_http_session(kw): http_session = requests.Session() http_session.headers.update(kw.get('headers', {})) http_session.auth = kw.get('auth') http_session.proxies.update(kw.get('proxies', {})) http_session.hooks.update(kw.get('hooks', {})) http_session.params.update(kw.get('params', {})) - http_session.verify = kw.get('verify') + http_session.verify = kw.get('verify', True) http_session.cert = kw.get('cert') http_session.cookies.update(kw.get('cookies', {})) return http_session diff -Nru python-socketio-client-0.5.3/TODO.goals python-socketio-client-0.6.5/TODO.goals --- python-socketio-client-0.5.3/TODO.goals 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/TODO.goals 2015-06-01 23:16:32.000000000 +0300 @@ -1,2 +1,3 @@ -# US/Pacific 11/19/2013 -Include graingert's pull request on travis.yml +Implement rooms #65 +Implement binary event +Implement binary ack diff -Nru python-socketio-client-0.5.3/TODO.log python-socketio-client-0.6.5/TODO.log --- python-socketio-client-0.5.3/TODO.log 2013-11-20 18:17:44.000000000 +0200 +++ python-socketio-client-0.6.5/TODO.log 1970-01-01 02:00:00.000000000 +0200 @@ -1,5 +0,0 @@ -# UTC 11/19/2013 -+ Add nayefc to acknowledgments [11/19/2013] -+ Beware of scheme included in URL [11/17/2013] -+ Add test for server ack callback in namespace [11/17/2013] -+ Set port automatically if it is not automatically specified [11/17/2013] \ No newline at end of file diff -Nru python-socketio-client-0.5.3/.travis.yml python-socketio-client-0.6.5/.travis.yml --- python-socketio-client-0.5.3/.travis.yml 1970-01-01 02:00:00.000000000 +0200 +++ python-socketio-client-0.6.5/.travis.yml 2015-06-01 23:16:32.000000000 +0300 @@ -0,0 +1,19 @@ +language: python +python: + - 2.6 + - 2.7 + - 3.4 +before_install: + - sudo apt-get update + - sudo apt-get install nodejs +install: + - npm install -G socket.io + - npm install -G yargs + - pip install -U requests + - pip install -U six + - pip install -U websocket-client + - pip install -U coverage +before_script: + - DEBUG=* node socketIO_client/tests/serve.js & + - sleep 3 +script: nosetests
signature.asc
Description: PGP signature