pax_global_header00006660000000000000000000000064130231437530014513gustar00rootroot0000000000000052 comment=9fd6cf7c6525abaad9d3753c12d037cf92c55a92 socketIO-client-0.7.2/000077500000000000000000000000001302314375300145155ustar00rootroot00000000000000socketIO-client-0.7.2/.gitignore000066400000000000000000000001531302314375300165040ustar00rootroot00000000000000# Python *.egg* *.py[co] .cache .coverage __pycache__ build dist sdist # Transient *.log # Vim *.sw[op] *~ socketIO-client-0.7.2/.travis.yml000066400000000000000000000004701302314375300166270ustar00rootroot00000000000000language: python python: - 2.6 - 2.7 - 3.4 - 3.5 before_install: - sudo apt-get install nodejs; node --version install: - pip install -U coverage requests six websocket-client - npm install -G socket.io before_script: - DEBUG=* node socketIO_client/tests/serve.js & - sleep 1 script: nosetests socketIO-client-0.7.2/CHANGES.rst000066400000000000000000000026701302314375300163240ustar00rootroot000000000000000.7 --- - Fixed thread cleanup - Fixed disconnect detection if defined directly thanks to Andreas Strikos - Fixed support for unicode payloads 0.6 --- - Upgraded to socket.io protocol 1.x thanks to Sean Arietta and Joe Palmer - Fixed support for Python 3 - Fixed SSL support - Added locks to fix concurrency issues with polling transport - Added SocketIO.off() and SocketIO.once() 0.5 --- - Added support for Python 3 - Added support for jsonp-polling thanks to Bernard Pratz - Added support for xhr-polling thanks to Francis Bull - Added support for query params and cookies - Fixed sending acknowledgments in custom namespaces thanks to Travis Odom - Rewrote library to use coroutines instead of threads to save memory 0.4 --- - Added support for custom headers and proxies thanks to Rui and Sajal - Added support for server-side callbacks thanks to Zac Lee - Merged Channel functionality into BaseNamespace thanks to Alexandre Bourget 0.3 --- - Added support for secure connections - Added SocketIO.wait() - Improved exception handling in _RhythmicThread and _ListenerThread 0.2 --- - Added support for callbacks and channels thanks to Paul Kienzle - Incorporated suggestions from Josh VanderLinden and Ian Fitzpatrick 0.1 --- - Wrapped `code from StackOverflow `_ - Added exception handling to destructor in case of connection failure socketIO-client-0.7.2/LICENSE000066400000000000000000000020641302314375300155240ustar00rootroot00000000000000Copyright (c) 2013 Roy Hyunjin Han and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. socketIO-client-0.7.2/MANIFEST.in000066400000000000000000000001331302314375300162500ustar00rootroot00000000000000recursive-include socketIO_client * include *.html *.js *.rst LICENSE global-exclude *.pyc socketIO-client-0.7.2/README.rst000066400000000000000000000212151302314375300162050ustar00rootroot00000000000000.. 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. Please note that this version implements `socket.io protocol 1.x `_, which is not backwards compatible. If you want to communicate using `socket.io protocol 0.9 `_ (which is compatible with `gevent-socketio `_), please use `socketIO-client 0.5.7.2 `_. Installation ------------ Install the package in an isolated environment. :: VIRTUAL_ENV=$HOME/.virtualenv # Prepare isolated environment virtualenv $VIRTUAL_ENV # Activate isolated environment source $VIRTUAL_ENV/bin/activate # Install package pip install -U socketIO-client Usage ----- Activate isolated environment. :: VIRTUAL_ENV=$HOME/.virtualenv source $VIRTUAL_ENV/bin/activate Launch your socket.io server. :: cd $(python -c "import os, socketIO_client;\ print(os.path.dirname(socketIO_client.__file__))") DEBUG=* node tests/serve.js # Start socket.io server in terminal one DEBUG=* node tests/proxy.js # Start proxy server in terminal two nosetests # Run tests in terminal three For debugging information, run these commands first. :: import logging logging.getLogger('socketIO-client').setLevel(logging.DEBUG) logging.basicConfig() Emit. :: from socketIO_client import SocketIO, LoggingNamespace with SocketIO('localhost', 8000, LoggingNamespace) as socketIO: socketIO.emit('aaa') socketIO.wait(seconds=1) Emit with callback. :: from socketIO_client import SocketIO, LoggingNamespace def on_bbb_response(*args): print('on_bbb_response', args) 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, LoggingNamespace def on_connect(): print('connect') def on_disconnect(): print('disconnect') def on_reconnect(): print('reconnect') def on_aaa_response(*args): print('on_aaa_response', args) socketIO = SocketIO('localhost', 8000, LoggingNamespace) socketIO.on('connect', on_connect) socketIO.on('disconnect', on_disconnect) socketIO.on('reconnect', on_reconnect) # Listen socketIO.on('aaa_response', on_aaa_response) socketIO.emit('aaa') socketIO.emit('aaa') socketIO.wait(seconds=1) # Stop listening socketIO.off('aaa_response') socketIO.emit('aaa') socketIO.wait(seconds=1) # Listen only once socketIO.once('aaa_response', on_aaa_response) socketIO.emit('aaa') # Activate aaa_response socketIO.emit('aaa') # Ignore socketIO.wait(seconds=1) Define events in a namespace. :: from socketIO_client import SocketIO, BaseNamespace class Namespace(BaseNamespace): def on_aaa_response(self, *args): print('on_aaa_response', args) self.emit('bbb') socketIO = SocketIO('localhost', 8000, Namespace) socketIO.emit('aaa') socketIO.wait(seconds=1) Define standard events. :: from socketIO_client import SocketIO, BaseNamespace class Namespace(BaseNamespace): def on_connect(self): print('[Connected]') def on_reconnect(self): print('[Reconnected]') def on_disconnect(self): print('[Disconnected]') socketIO = SocketIO('localhost', 8000, Namespace) socketIO.wait(seconds=1) Define different namespaces on a single socket. :: from socketIO_client import SocketIO, BaseNamespace class ChatNamespace(BaseNamespace): def on_aaa_response(self, *args): print('on_aaa_response', args) class NewsNamespace(BaseNamespace): def on_aaa_response(self, *args): print('on_aaa_response', args) socketIO = SocketIO('localhost', 8000) chat_namespace = socketIO.define(ChatNamespace, '/chat') news_namespace = socketIO.define(NewsNamespace, '/news') chat_namespace.emit('aaa') news_namespace.emit('aaa') socketIO.wait(seconds=1) Connect via SSL (https://github.com/invisibleroads/socketIO-client/issues/54). :: from socketIO_client import SocketIO # Skip server certificate verification SocketIO('https://localhost', verify=False) # Verify the server certificate SocketIO('https://localhost', verify='server.crt') # Verify the server certificate and encrypt using client certificate socketIO = SocketIO('https://localhost', verify='server.crt', cert=( 'client.crt', 'client.key')) Specify params, headers, cookies, proxies thanks to the `requests `_ library. :: from socketIO_client import SocketIO from base64 import b64encode SocketIO( localhost', 8000, params={'q': 'qqq'}, headers={'Authorization': 'Basic ' + b64encode('username:password')}, cookies={'a': 'aaa'}, proxies={'https': 'https://proxy.example.com:8080'}) Wait forever. :: from socketIO_client import SocketIO socketIO = SocketIO('localhost', 8000) socketIO.wait() License ------- This software is available under the MIT License. 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 `_. - `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 `_, `Patrick Huber `_, `Brad Campbell `_, `Daniel `_, `Sean Arietta `_, `Sacha Stafyniak `_ submitted code to expand support of the socket.io protocol. - `Bernard Pratz `_, `Francis Bull `_ wrote prototypes to support xhr-polling and jsonp-polling. - `Joe Palmer `_ sponsored development. - `Eric Chen `_, `Denis Zinevich `_, `Thiago Hersan `_, `Nayef Copty `_, `Jörgen Karlsson `_, `Branden Ghena `_, `Tim Landscheidt `_, `Matt Porritt `_, `Matt Dainty `_, `Thomaz de Oliveira dos Reis `_, `Felix König `_, `George Wilson `_, `Andreas Strikos `_, `Alessio Sergi `_ `Claudio Yacarini `_, `Khairi Hafsham `_, `Robbie Clarken `_ suggested ways to make the connection more robust. - `Merlijn van Deen `_, `Frederic Sureau `_, `Marcus Cobden `_, `Drew Hutchison `_, `wuurrd `_, `Adam Kecer `_, `Alex Monk `_, `Vishal P R `_, `John Vandenberg `_, `Thomas Grainger `_, `Daniel Quinn `_, `Adric Worley `_, `Adam Roses Wight `_, `Jan Včelák `_ proposed changes that make the library more friendly and practical for you! socketIO-client-0.7.2/debian/000077500000000000000000000000001302314375300157375ustar00rootroot00000000000000socketIO-client-0.7.2/debian/changelog000066400000000000000000000002541302314375300176120ustar00rootroot00000000000000python-socketio-client (0.7.0) UNRELEASED; urgency=medium * First version with Debian packaging. -- Adam Roses Wight Tue, 04 Aug 2015 21:30:11 -0700 socketIO-client-0.7.2/debian/compat000066400000000000000000000000021302314375300171350ustar00rootroot000000000000009 socketIO-client-0.7.2/debian/control000066400000000000000000000021421302314375300173410ustar00rootroot00000000000000Source: python-socketio-client Maintainer: Roy Hyunjin Han Section: python Priority: optional Build-Depends: debhelper (>= 9), dh-python, python-all, python3-all, python-coverage, python3-coverage, python-nose, python3-nose, python-requests, python3-requests, python-setuptools, python3-setuptools, python-six, python3-six, python-websocket, python3-websocket Standards-Version: 3.9.6 Homepage: https://github.com/invisibleroads/socketIO-client Package: python-socketio-client Architecture: all Depends: ${python:Depends}, ${misc:Depends}, python-requests, python-six Description: A socket.io client library for Python 2 http://pypi.python.org/pypi/socketIO-client . This version implements socket.io protocol 1.x . Package only includes python2 bindings. Package: python3-socketio-client Architecture: all Depends: ${python3:Depends}, ${misc:Depends}, python3-requests, python3-six Description: A socket.io client library for Python 3 http://pypi.python.org/pypi/socketIO-client . This version implements socket.io protocol 1.x . Package only includes python3 bindings. socketIO-client-0.7.2/debian/copyright000066400000000000000000000023301302314375300176700ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: socketIO-client Files: * Copyright: 2016 Roy Hyunjin Han License: MIT License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. socketIO-client-0.7.2/debian/rules000077500000000000000000000005661302314375300170260ustar00rootroot00000000000000#!/usr/bin/make -f export PYBUILD_NAME=socketio-client %: dh $@ --with python2,python3 --buildsystem=pybuild override_dh_auto_test: # Skip tests. # FIXME: Need packages for these nodejs libraries. # npm install socket.io # DEBUG=* nodejs socketIO_client/tests/serve.js & # sleep 1 # PYBUILD_SYSTEM=custom \ # PYBUILD_TEST_ARGS="nosetests -v -v" \ # dh_auto_test socketIO-client-0.7.2/missions/000077500000000000000000000000001302314375300163615ustar00rootroot00000000000000socketIO-client-0.7.2/missions/Done/000077500000000000000000000000001302314375300172465ustar00rootroot00000000000000socketIO-client-0.7.2/missions/Done/PullRequests-20160331.md000066400000000000000000000134401302314375300231370ustar00rootroot00000000000000# Vision Ensure that Python scripts can communicate with a Socket.IO server. # Mission Address pull requests. # Owner Roy Hyunjin Han # Context It is important to demonstrate discipline in maintaining an open source project. # Timeframe 20160331-1245 - 20160417-1245: 17 days estimated 20160331-1245 - 20160715-1700: 107 days actual # Objectives 1. + Make sure that the library works as promised. 2. + Address pull requests. # Log 20160331-1300 - 20160331-1500 Make sure that the library works as promised. I think that if I allocate a few hours each day, then I should be able to get this done on schedule. + Clone repository + Download socket.io + Work through chat application example + Start a new branch We'll just check whether all unit tests pass. We should also change our tests to use pytest. + Check that library works with both versions of socket.io + Test that socket.io-0.9.14 works with socketIO-client-0.5.6 cd ~/Projects/socketIO-client-0.5.6 python setup.py develop pip install -I -U nose npm install socket.io@0.9.14 node serve-tests ~/.virtualenvs/crosscompute/bin/nosetests + Test that socket.io-1.4.5 works with socketIO-client-0.6.5 cd ~/Projects/socketIO-client-0.6.5 python setup.py develop pip install -I -U nose npm install socket.io yargs node socketIO_client/tests/serve.js ~/.virtualenvs/crosscompute/bin/nosetests Well, I'm pleasantly surprised. Both tests still pass! _ Write an experiment to detect version of socket.io on server + Option 1 is to try to connect with both and avoid the one that fails. But that is naive. + Connect socketIO-client-0.5.6 to socket.io-1.4.5 and see what happens (it just hangs) + Connect socketIO-client-0.6.5 to socket.io-0.9.14 and see what happens (warning: connection refused) _ Option 2 is to have more precision by testing certain connection points. Let's try "Connect socketIO-client-0.6.5 to socket.io-0.9.14" again. WARNING:root:localhost:9000/socket.io [waiting for connection] HTTPConnectionPool(host='localhost', port=9000): Max retries exceeded with url: /socket.io/?EIO=3&transport=polling&t=1459452848797-0 (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused',)) After thinking about it some more, I think supporting both protocols in the same library will be needlessly complicated. We will keep the existing structure of having different versions for different protocols. 20160331-2010 - 20160331-2030 + Add documentation for server SSL certification verification + Add documentation for client SSL certification encryption 20160331-2030 - 20160331-2130 + Work through one pull request 20160403-1100 - 20160403-1200: 1 hour estimated 20160403-1100 - 20160403-1400: 3 hours actual + Work through one pull request 20160625-1815 - 20160625-1845: 30 minutes I am noticing some thread-related errors. For example, here is an error that appears Exception in thread Thread-1 (most likely raised during interpreter shutdown): Traceback (most recent call last): File "/usr/lib64/python2.7/threading.py", line 804, in __bootstrap_inner File "/home/rhh/.virtualenvs/crosscompute/lib/python2.7/site-packages/socketIO_client/heartbeats.py", line 34, in run File "/usr/lib64/python2.7/threading.py", line 617, in wait File "/usr/lib64/python2.7/threading.py", line 367, in wait : list.remove(x): x not in list Unhandled exception in thread started by sys.excepthook is missing lost sys.stderr I am looking at line 367 of threading.py but it looks like the exception should have been ignored. try: self.__waiters.remove(waiter) except ValueError: pass I think the ValueError is coming from somewhere else. - https://github.com/segmentio/analytics-python/issues/69 - http://stackoverflow.com/questions/9532264/how-to-use-multiple-threads For now, let's just wait for the heartbeat thread to join. self._heartbeat_thread.join() If the exception appears again, then we'll use atexit.register(self._heartbeat_thread.join()). 20160714-2053 The issue is that packet_text has utf-8 but we need a bytestring and six.b only encodes using latin-1. Option 1: Encode packet_text into a bytestring using utf-8, but then we won't be working with unicode. Option 2: Leave packet_text alone and handle it differently based on whether it is unicode or a bytestring. 20160715-1315 After thinking about it a bit, I think the second approach is more robust, because we'll just leave the packet data as it is mostly and just handle it verbatim. The first option can be vulnerable to cases where the packet type might be wrong? 20160715-1700 If I ever do revisit binary support, I should try to generate a log of the binary protocol using Chrome Developer Tools. There were some notes about XHR2 vs base64. - https://github.com/socketio/engine.io-protocol - https://github.com/socketio/socket.io-protocol The feus4177 repository has a working solution for binary support, but I didn't completely understand how it worked and I felt uncomfortable about merging something without full understanding. I also wanted to address unicode support and the decision was to keep everything as an encoded bytestring and only decode for non-binary events. I tried the approach of leaving the packet alone and just handling unicode or bytestring on a case by case basis, but it proved too messy. Perhaps when my life becomes less complex, I'll revisit both binary and unicode support. But for now, I have too many things in my head and my capacity for extra complexity is becoming rather limited. socketIO-client-0.7.2/missions/PullRequests-20161209.md000066400000000000000000000047031302314375300222610ustar00rootroot00000000000000# Vision Ensure that Python scripts can communicate with a Socket.IO server. # Mission Address pull requests. # Owner Roy Hyunjin Han # Context Some of our systems now rely on this package. # Timeframe 20161209-2100 - 20161223-2100: 2 weeks estimated # Objectives 1. Review pull requests. 2. Review issues. 3. Release version. # Log 20161209-2130 - 20161209-2230: 1 hour + Make decision on websocket-client issue for #139 + Merge #139 into 0.7.2 20161209-2330 - 20161210-0000: 30 minutes + Merge #125 into 0.5.7.2 20161210-0045 - 20161210-0115: 30 minutes + Merge #136 into 0.7.2 20161210-1100 - 20161210-1200: 1 hour + Merge #135 into 0.7.2 20161210-2200 - 20161210-2230: 30 minutes I tried experimenting for a while with unicode events, where there are unicode characters in the event name, but it seems that Python 2 does not support unicode attributes. # Tasks + Release 0.5.7.2 + Release 0.7.2 Release 0.8.0 Add binary support https://github.com/invisibleroads/socketIO-client/pull/85 https://github.com/invisibleroads/socketIO-client/issues/70 https://github.com/invisibleroads/socketIO-client/issues/71 https://github.com/invisibleroads/socketIO-client/issues/91 Replace *args with args Investigate whether it is really true that callbacks can't take dictionaries https://github.com/invisibleroads/socketIO-client/pull/112 Check python3 support for socketIO-client 0.5.6 https://github.com/invisibleroads/socketIO-client/issues/83 Check why connected=True after termination https://github.com/invisibleroads/socketIO-client/issues/80 https://github.com/invisibleroads/socketIO-client/issues/98 Consider catching heartbeat thread exception https://github.com/invisibleroads/socketIO-client/issues/100 Check disconnection issues https://github.com/invisibleroads/socketIO-client/issues/107 https://github.com/invisibleroads/socketIO-client/issues/111 Check why transports are not being set https://github.com/invisibleroads/socketIO-client/issues/102 Look at 404 not found error https://github.com/invisibleroads/socketIO-client/issues/101 Check whether it works on Windows 8 https://github.com/invisibleroads/socketIO-client/issues/97 Consider allowing milliseconds https://github.com/invisibleroads/socketIO-client/issues/106 Consider using attrs instead of namedtuple socketIO-client-0.7.2/missions/README.md000066400000000000000000000007031302314375300176400ustar00rootroot00000000000000# Vision Describe your long-term goal for the project. # Mission Describe a short-term goal that you can complete within two weeks. # Owner State your name. # Context Explain why the mission is important. # Timeframe Give dates for when you will start and finish the mission. # Objectives Specify at most three goals that must be achieved for the mission to be successful. # Log Record progress toward objectives. # Tasks List remaining tasks. socketIO-client-0.7.2/setup.cfg000066400000000000000000000001531302314375300163350ustar00rootroot00000000000000[nosetests] detailed-errors = TRUE with-coverage = TRUE cover-package = socketIO_client cover-erase = TRUE socketIO-client-0.7.2/setup.py000066400000000000000000000020671302314375300162340ustar00rootroot00000000000000import io from os.path import abspath, dirname, join from setuptools import find_packages, setup 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.7.2', description='A socket.io client library', 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='rhh@crosscompute.com', url='https://github.com/invisibleroads/socketIO-client', install_requires=[ 'requests>=2.7.0', 'six', 'websocket-client', ], tests_require=[ 'nose', 'coverage', ], packages=find_packages(), include_package_data=True, zip_safe=False) socketIO-client-0.7.2/socketIO_client/000077500000000000000000000000001302314375300175735ustar00rootroot00000000000000socketIO-client-0.7.2/socketIO_client/__init__.py000066400000000000000000000454761302314375300217240ustar00rootroot00000000000000import atexit from .exceptions import ConnectionError, TimeoutError, PacketError from .heartbeats import HeartbeatThread from .logs import LoggingMixin from .namespaces import ( EngineIONamespace, SocketIONamespace, LoggingSocketIONamespace, find_callback, make_logging_prefix) 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.7.0' 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._opened = False self._wants_to_close = False atexit.register(self._close) 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( '[engine.io 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('[engine.io transport selected] %s', self.transport_name) 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('[engine.io heartbeat reset]') def _connect_namespaces(self): pass 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 __enter__(self): return self def __exit__(self, *exception_pack): self._close() def __del__(self): self._close() # Define def define(self, Namespace): self._namespace = namespace = Namespace(self) return namespace def on(self, event, callback): try: namespace = self.get_namespace() except PacketError: namespace = self.define(EngineIONamespace) return namespace.on(event, callback) def once(self, event, callback): try: namespace = self.get_namespace() except PacketError: namespace = self.define(EngineIONamespace) return namespace.once(event, callback) def off(self, event): try: namespace = self.get_namespace() except PacketError: namespace = self.define(EngineIONamespace) return namespace.off(event) def get_namespace(self): try: return self._namespace except AttributeError: raise PacketError('undefined engine.io namespace') # Act def send(self, engineIO_packet_data): self._message(engineIO_packet_data) def _open(self): engineIO_packet_type = 0 self._transport_instance.send_packet(engineIO_packet_type) def _close(self): self._wants_to_close = True try: self._heartbeat_thread.halt() self._heartbeat_thread.join() except AttributeError: pass if not hasattr(self, '_opened') or 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 _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 KeyboardInterrupt: self._close() raise 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._find_packet_callback('disconnect')() except PacketError: pass self._heartbeat_thread.relax() self._transport.set_timeout() def _should_stop_waiting(self): return self._wants_to_close 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 _process_packet(self, packet): engineIO_packet_type, engineIO_packet_data = packet # Launch callbacks namespace = self.get_namespace() try: 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: raise PacketError( 'unexpected engine.io packet type (%s)' % engineIO_packet_type) delegate(engineIO_packet_data, namespace) if engineIO_packet_type == 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')() 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 desired transports=['websocket', 'xhr-polling']. - Pass query params, headers, cookies, proxies as keyword arguments. SocketIO( 'localhost', 8000, params={'q': 'qqq'}, headers={'Authorization': 'Basic ' + b64encode('username:password')}, cookies={'a': 'aaa'}, proxies={'https': 'https://proxy.example.com:8080'}) """ def __init__( 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._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) # 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.connect(path) self.wait(for_namespace=namespace) return namespace def on(self, event, callback, path=''): try: namespace = self.get_namespace(path) except PacketError: namespace = self.define(SocketIONamespace, path) return namespace.on(event, callback) def get_namespace(self, path=''): try: return self._namespace_by_path[path] except KeyError: raise PacketError('undefined socket.io namespace (%s)' % path) # Act def connect(self, path='', with_transport_instance=False): if path or not self.connected: socketIO_packet_type = 0 socketIO_packet_data = format_socketIO_packet_data(path) self._message( str(socketIO_packet_type) + socketIO_packet_data, with_transport_instance) self._wants_to_close = False def disconnect(self, path=''): if path and self._opened: 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 elif not path: self._close() try: namespace = self._namespace_by_path[path] namespace._find_packet_callback('disconnect')() if path: del self._namespace_by_path[path] except KeyError: pass 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 send(self, data='', callback=None, **kw): path = kw.get('path', '') args = [data] if callback: args.append(callback) self.emit('message', *args, path=path) 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 _should_stop_waiting(self, for_namespace=False, for_callbacks=False): if for_namespace: namespace = for_namespace if getattr(namespace, '_invalid', False): raise ConnectionError( 'invalid socket.io namespace (%s)' % namespace.path) if not getattr(namespace, '_connected', False): self._debug( '%s[socket.io waiting for connection]', make_logging_prefix(namespace.path)) return False return True if for_callbacks and not self._has_ack_callback: return True return super(SocketIO, self)._should_stop_waiting() 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: 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 socket.io packet type (%s)' % socketIO_packet_type) delegate(parse_socketIO_packet_data(socketIO_packet_data), namespace) return socketIO_packet_data def _on_connect(self, data_parsed, namespace): namespace._connected = True namespace._find_packet_callback('connect')() self._debug( '%s[socket.io connected]', make_logging_prefix(namespace.path)) def _on_disconnect(self, data_parsed, namespace): namespace._connected = False namespace._find_packet_callback('disconnect')() def _on_event(self, data_parsed, namespace): args = data_parsed.args try: 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_ack(self, data_parsed, namespace): try: ack_callback = self._get_ack_callback(data_parsed.ack_id) except KeyError: return ack_callback(*data_parsed.args) def _on_error(self, data_parsed, namespace): namespace._find_packet_callback('error')(*data_parsed.args) def _on_binary_event(self, data_parsed, namespace): self._warn('[not implemented] binary event') def _on_binary_ack(self, data_parsed, namespace): self._warn('[not implemented] binary ack') def _prepare_to_send_ack(self, path, ack_id): 'Return function that acknowledges the server' 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 _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 socketIO-client-0.7.2/socketIO_client/exceptions.py000066400000000000000000000002651302314375300223310ustar00rootroot00000000000000class SocketIOError(Exception): pass class ConnectionError(SocketIOError): pass class TimeoutError(SocketIOError): pass class PacketError(SocketIOError): pass socketIO-client-0.7.2/socketIO_client/heartbeats.py000066400000000000000000000027141302314375300222730ustar00rootroot00000000000000import 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() socketIO-client-0.7.2/socketIO_client/logs.py000066400000000000000000000023741302314375300211170ustar00rootroot00000000000000import logging import time from .symmetries import NullHandler LOG = logging.getLogger('socketIO-client') LOG.addHandler(NullHandler()) class LoggingMixin(object): def _log(self, level, msg, *attrs): LOG.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 socketIO-client-0.7.2/socketIO_client/namespaces.py000066400000000000000000000167001302314375300222700ustar00rootroot00000000000000from .logs import LoggingMixin class EngineIONamespace(LoggingMixin): 'Define engine.io client behavior' def __init__(self, io): self._io = io self._callback_by_event = {} self._once_events = set() 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 once(self, event, callback): 'Define a callback to handle the first event emitted by the server' self._once_events.add(event) self.on(event, callback) def off(self, event): 'Remove an event handler' try: self._once_events.remove(event) except KeyError: pass self._callback_by_event.pop(event, None) 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: callback = self._callback_by_event[event] except KeyError: pass else: if event in self._once_events: self.off(event) return callback # 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.""" if data.lower() == 'invalid namespace': self._invalid = True 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: callback = self._callback_by_event[event] except KeyError: pass else: if event in self._once_events: self.off(event) return callback # 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_prefix(self.path)) super(LoggingSocketIONamespace, self).on_connect() def on_reconnect(self): self._debug( '%s[socket.io reconnect]', make_logging_prefix(self.path)) super(LoggingSocketIONamespace, self).on_reconnect() def on_disconnect(self): self._debug( '%s[socket.io disconnect]', make_logging_prefix(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_prefix(self.path), event, ', '.join(arguments)) super(LoggingSocketIONamespace, self).on_event(event, *args) def on_error(self, data): self._warn( '%s[socket.io error] %s', make_logging_prefix(self.path), data) super(LoggingSocketIONamespace, self).on_error(data) 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_prefix(path): return path + ' ' if path else '' socketIO-client-0.7.2/socketIO_client/parsers.py000066400000000000000000000106511302314375300216270ustar00rootroot00000000000000import json import six from collections import namedtuple from six.moves.urllib.parse import urlparse as parse_url from .symmetries import decode_string, encode_string, get_byte, get_character 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:%s%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 = [] if isinstance(args, six.string_types): args = [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 socketIO-client-0.7.2/socketIO_client/symmetries.py000066400000000000000000000011621302314375300223460ustar00rootroot00000000000000try: from logging import NullHandler except ImportError: # Python 2.6 from logging import Handler class NullHandler(Handler): def emit(self, record): pass from six import indexbytes try: from ssl import SSLError except ImportError: class SSLError(Exception): pass try: memoryview = memoryview except NameError: memoryview = buffer def get_byte(x, index): return indexbytes(x, index) def get_character(x, index): return chr(get_byte(x, index)) def decode_string(x): return x.decode('utf-8') def encode_string(x): return x.encode('utf-8') socketIO-client-0.7.2/socketIO_client/tests/000077500000000000000000000000001302314375300207355ustar00rootroot00000000000000socketIO-client-0.7.2/socketIO_client/tests/__init__.py000066400000000000000000000313431302314375300230520ustar00rootroot00000000000000# coding: utf-8 import logging import time from unittest import TestCase from .. import SocketIO, LoggingNamespace, find_callback from ..exceptions import ConnectionError HOST = 'localhost' PORT = 9000 DATA = 'xxx' PAYLOAD = {'xxx': 'yyy'} UNICODE_PAYLOAD = {u'인삼': u'뿌리'} BINARY_DATA = bytearray(b'\xff\xff\xff') BINARY_PAYLOAD = { 'data': BINARY_DATA, 'array': [bytearray(b'\xee'), bytearray(b'\xdd')] } logging.basicConfig(level=logging.DEBUG) class BaseMixin(object): def setUp(self): super(BaseMixin, self).setUp() self.response_count = 0 self.wait_time_in_seconds = 1 def tearDown(self): super(BaseMixin, self).tearDown() self.socketIO.disconnect() def test_disconnect(self): 'Disconnect' self.socketIO.on('disconnect', self.on_event) self.assertTrue(self.socketIO.connected) self.assertEqual(self.response_count, 0) self.socketIO.disconnect() self.assertFalse(self.socketIO.connected) self.assertEqual(self.response_count, 1) def test_disconnect_with_namespace(self): 'Disconnect with namespace' namespace = self.socketIO.define(Namespace) self.assertTrue(self.socketIO.connected) self.assertFalse('disconnect' in namespace.args_by_event) self.socketIO.disconnect() self.assertFalse(self.socketIO.connected) self.assertTrue('disconnect' in namespace.args_by_event) def test_reconnect(self): 'Reconnect' self.socketIO.on('reconnect', self.on_event) self.assertEqual(self.response_count, 0) self.socketIO.connect() self.assertEqual(self.response_count, 0) self.socketIO.disconnect() self.socketIO.connect() self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(self.response_count, 1) def test_reconnect_with_namespace(self): 'Reconnect with namespace' namespace = self.socketIO.define(Namespace) self.assertFalse('reconnect' in namespace.args_by_event) self.socketIO.connect() self.assertFalse('reconnect' in namespace.args_by_event) self.socketIO.disconnect() self.socketIO.connect() self.socketIO.wait(self.wait_time_in_seconds) self.assertTrue('reconnect' in namespace.args_by_event) 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_unicode_payload(self): 'Emit with unicode payload' namespace = self.socketIO.define(Namespace) self.socketIO.emit('emit_with_payload', UNICODE_PAYLOAD) self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(namespace.args_by_event, { 'emit_with_payload_response': (UNICODE_PAYLOAD,), }) """ def test_emit_with_binary_payload(self): 'Emit with binary payload' namespace = self.socketIO.define(Namespace) self.socketIO.emit('emit_with_payload', BINARY_PAYLOAD) self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(namespace.args_by_event, { 'emit_with_payload_response': (BINARY_PAYLOAD,), }) """ def test_emit_with_callback(self): 'Emit with callback' self.assertEqual(self.response_count, 0) self.socketIO.emit('emit_with_callback', self.on_response) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.assertEqual(self.response_count, 1) def test_emit_with_callback_with_payload(self): 'Emit with callback with payload' self.assertEqual(self.response_count, 0) self.socketIO.emit( 'emit_with_callback_with_payload', self.on_response) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.assertEqual(self.response_count, 1) def test_emit_with_callback_with_multiple_payloads(self): 'Emit with callback with multiple payloads' self.assertEqual(self.response_count, 0) self.socketIO.emit( 'emit_with_callback_with_multiple_payloads', self.on_response) self.socketIO.wait_for_callbacks(seconds=self.wait_time_in_seconds) self.assertEqual(self.response_count, 1) """ def test_emit_with_callback_with_binary_payload(self): 'Emit with callback with binary payload' self.socketIO.emit( 'emit_with_callback_with_binary_payload', self.on_binary_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.assertEqual(self.response_count, 0) self.socketIO.emit('emit_with_event', PAYLOAD) self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(self.response_count, 1) self.socketIO.emit('emit_with_event', PAYLOAD) self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(self.response_count, 2) self.socketIO.off('emit_with_event_response') self.socketIO.emit('emit_with_event', PAYLOAD) self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(self.response_count, 2) def test_once(self): 'Listen for an event only once' self.socketIO.once('emit_with_event_response', self.on_response) self.assertEqual(self.response_count, 0) self.socketIO.emit('emit_with_event', PAYLOAD) self.socketIO.emit('emit_with_event', PAYLOAD) self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(self.response_count, 1) 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_send_with_binary_data(self): 'Send with binary data' namespace = self.socketIO.define(Namespace) self.socketIO.send(BINARY_DATA) self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(namespace.response, BINARY_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_binary_ack(self): 'Respond to a server callback request with binary data' namespace = self.socketIO.define(Namespace) self.socketIO.emit( 'trigger_server_expects_callback', BINARY_PAYLOAD) self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(namespace.args_by_event, { 'server_expects_callback': (BINARY_PAYLOAD,), 'server_received_callback': (BINARY_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_invalid(self): 'Connect to a namespace that is not defined on the server' self.assertRaises( ConnectionError, self.socketIO.define, Namespace, '/invalid') def test_namespace_emit(self): 'Emit to 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_emit_with_binary_payload(self): 'Emit to namespaces with binary payload' 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', BINARY_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': (BINARY_PAYLOAD,), }) """ def test_namespace_ack(self): 'Respond to server callback request in 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 test_namespace_ack_with_binary_payload(self): 'Respond to server callback request in namespace with binary payload' chat_namespace = self.socketIO.define(Namespace, '/chat') chat_namespace.emit( 'trigger_server_expects_callback', BINARY_PAYLOAD) self.socketIO.wait(self.wait_time_in_seconds) self.assertEqual(chat_namespace.args_by_event, { 'server_expects_callback': (BINARY_PAYLOAD,), 'server_received_callback': (BINARY_PAYLOAD,), }) """ def on_event(self): self.response_count += 1 def on_response(self, *args): for arg in args: if isinstance(arg, dict): self.assertEqual(arg, PAYLOAD) else: self.assertEqual(arg, DATA) self.response_count += 1 def on_binary_response(self, *args): for arg in args: if isinstance(arg, dict): self.assertEqual(arg, BINARY_PAYLOAD) else: self.assertEqual(arg, BINARY_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.args_by_event = {} self.response = None def on_disconnect(self): self.args_by_event['disconnect'] = () def on_reconnect(self): self.args_by_event['reconnect'] = () 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 socketIO-client-0.7.2/socketIO_client/tests/index.html000066400000000000000000000016621302314375300227370ustar00rootroot00000000000000 socketIO-client-0.7.2/socketIO_client/tests/package.json000066400000000000000000000001301302314375300232150ustar00rootroot00000000000000{ "devDependencies": { "http-proxy": ">=1.14.0", "socket.io": ">=1.4.8" } } socketIO-client-0.7.2/socketIO_client/tests/proxy.js000066400000000000000000000020271302314375300224550ustar00rootroot00000000000000var 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); socketIO-client-0.7.2/socketIO_client/tests/serve.js000066400000000000000000000065711302314375300224300ustar00rootroot00000000000000// DEBUG=* node serve.js if (process.argv[2] == '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'}; var UNICODE_PAYLOAD = {'인삼': '★ 뿌리 ★'}; // Travis currently does not support Buffer.from function getBuffer(array) { var buffer = new Buffer(array.length); for (var i = 0; i < array.length; i++) { buffer[i] = array[i]; } return buffer; } var BINARY_DATA = getBuffer([255, 255, 255]); var BINARY_PAYLOAD = { 'data': BINARY_DATA, 'array': [getBuffer([238]), getBuffer([221])] } 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_callback_with_unicode_payload', function(fn) { fn(UNICODE_PAYLOAD); }); socket.on('emit_with_callback_with_binary_payload', function(fn) { fn(BINARY_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); }); } socketIO-client-0.7.2/socketIO_client/tests/ssl.crt000066400000000000000000000023251302314375300222520ustar00rootroot00000000000000-----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----- socketIO-client-0.7.2/socketIO_client/tests/ssl.key000066400000000000000000000032541302314375300222540ustar00rootroot00000000000000-----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----- socketIO-client-0.7.2/socketIO_client/transports.py000066400000000000000000000174241302314375300223740ustar00rootroot00000000000000import requests import six import ssl import threading import time from six.moves.urllib.parse import urlencode as format_query from six.moves.urllib.parse import urlparse as parse_url from socket import error as SocketError try: from websocket import ( WebSocketConnectionClosedException, WebSocketTimeoutException, create_connection) except ImportError: 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""") from .exceptions import ConnectionError, TimeoutError from .parsers import ( encode_engineIO_content, decode_engineIO_content, format_packet_text, parse_packet_text) from .symmetries import SSLError, memoryview ENGINEIO_PROTOCOL = 3 TRANSPORTS = 'xhr-polling', 'websocket' 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 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: 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), ]) get_response( self.http_session.post, self._http_url, params=params, data=memoryview(data), **self._kw_post) 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, six.string_types): 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: self._connection = create_connection(ws_url, **kw) except Exception as e: raise ConnectionError(e) def recv_packet(self): try: packet_text = self._connection.recv() except WebSocketTimeoutException as e: raise TimeoutError('recv timed out (%s)' % e) except SSLError as e: raise ConnectionError('recv disconnected by SSL (%s)' % e) except WebSocketConnectionClosedException as e: raise ConnectionError('recv disconnected (%s)' % e) except SocketError as e: raise ConnectionError('recv disconnected (%s)' % e) if not isinstance(packet_text, six.binary_type): packet_text = packet_text.encode('utf-8') engineIO_packet_type, engineIO_packet_data = parse_packet_text( packet_text) yield engineIO_packet_type, engineIO_packet_data def send_packet(self, engineIO_packet_type, engineIO_packet_data=''): packet = format_packet_text(engineIO_packet_type, engineIO_packet_data) try: self._connection.send(packet) except WebSocketTimeoutException as e: raise TimeoutError('send timed out (%s)' % e) except (SocketError, WebSocketConnectionClosedException) as e: raise ConnectionError('send disconnected (%s)' % e) def set_timeout(self, seconds=None): self._connection.settimeout(seconds or self._timeout) def get_response(request, *args, **kw): try: 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_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): 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', True) http_session.cert = _get_cert(kw) http_session.cookies.update(kw.get('cookies', {})) return http_session def _get_cert(kw): # Reduce (None, None) to None cert = kw.get('cert') if hasattr(cert, '__iter__') and cert[0] is None: cert = None return cert