diff --git a/.gitattributes b/.gitattributes index eb965318..013f4fff 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,6 +4,6 @@ example/aiohttp/static/engine.io.js linguist-vendored tests/common/index.html binary tests/common/files/index.html binary tests/common/files/file.txt binary -tests/asyncio/index.html binary -tests/asyncio/files/index.html binary -tests/asyncio/files/file.txt binary +tests/async/index.html binary +tests/async/files/index.html binary +tests/async/files/file.txt binary diff --git a/docs/api.rst b/docs/api.rst index aa4a3bea..338097dd 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -6,45 +6,27 @@ API Reference .. module:: engineio -``Client`` class ----------------- - .. autoclass:: Client :members: - -``AsyncClient`` class ---------------------- + :inherited-members: .. autoclass:: AsyncClient :members: :inherited-members: -``Server`` class ----------------- - .. autoclass:: Server :members: - -``AsyncServer`` class ---------------------- + :inherited-members: .. autoclass:: AsyncServer :members: :inherited-members: -``WSGIApp`` class ------------------ - .. autoclass:: WSGIApp :members: -``ASGIApp`` class ------------------ - .. autoclass:: ASGIApp - -``Middleware`` class (deprecated) ---------------------------------- + :members: .. autoclass:: Middleware :members: diff --git a/src/engineio/__init__.py b/src/engineio/__init__.py index 7eb6719b..4919efd8 100644 --- a/src/engineio/__init__.py +++ b/src/engineio/__init__.py @@ -1,23 +1,13 @@ -import sys - from .client import Client from .middleware import WSGIApp, Middleware from .server import Server -if sys.version_info >= (3, 5): # pragma: no cover - from .asyncio_server import AsyncServer - from .asyncio_client import AsyncClient - from .async_drivers.asgi import ASGIApp - try: - from .async_drivers.tornado import get_tornado_handler - except ImportError: - get_tornado_handler = None -else: # pragma: no cover - AsyncServer = None - AsyncClient = None +from .async_server import AsyncServer +from .async_client import AsyncClient +from .async_drivers.asgi import ASGIApp +try: + from .async_drivers.tornado import get_tornado_handler +except ImportError: # pragma: no cover get_tornado_handler = None - ASGIApp = None -__all__ = ['Server', 'WSGIApp', 'Middleware', 'Client'] -if AsyncServer is not None: # pragma: no cover - __all__ += ['AsyncServer', 'ASGIApp', 'get_tornado_handler', - 'AsyncClient'] +__all__ = ['Server', 'WSGIApp', 'Middleware', 'Client', + 'AsyncServer', 'ASGIApp', 'get_tornado_handler', 'AsyncClient'] diff --git a/src/engineio/asyncio_client.py b/src/engineio/async_client.py similarity index 98% rename from src/engineio/asyncio_client.py rename to src/engineio/async_client.py index cf7ae1a1..5e5980f8 100644 --- a/src/engineio/asyncio_client.py +++ b/src/engineio/async_client.py @@ -8,7 +8,7 @@ except ImportError: # pragma: no cover aiohttp = None -from . import client +from . import base_client from . import exceptions from . import packet from . import payload @@ -22,7 +22,7 @@ def async_signal_handler(): Disconnect all active async clients. """ async def _handler(): # pragma: no cover - for c in client.connected_clients[:]: + for c in base_client.connected_clients[:]: if c.is_asyncio_based(): await c.disconnect() @@ -37,7 +37,7 @@ async def _handler(): # pragma: no cover asyncio.ensure_future(_handler()) -class AsyncClient(client.Client): +class AsyncClient(base_client.BaseClient): """An Engine.IO client for asyncio. This class implements a fully compliant Engine.IO web client with support @@ -164,7 +164,7 @@ async def disconnect(self, abort=False): await self.read_loop_task self.state = 'disconnected' try: - client.connected_clients.remove(self) + base_client.connected_clients.remove(self) except ValueError: # pragma: no cover pass await self._reset() @@ -262,7 +262,7 @@ async def _connect_polling(self, url, headers, engineio_path): self.base_url += '&sid=' + self.sid self.state = 'connected' - client.connected_clients.append(self) + base_client.connected_clients.append(self) await self._trigger_event('connect', run_async=False) for pkt in p.packets[1:]: @@ -384,7 +384,7 @@ async def _connect_websocket(self, url, headers, engineio_path): self.current_transport = 'websocket' self.state = 'connected' - client.connected_clients.append(self) + base_client.connected_clients.append(self) await self._trigger_event('connect', run_async=False) self.ws = ws @@ -515,7 +515,7 @@ async def _read_loop_polling(self): if self.state == 'connected': await self._trigger_event('disconnect', run_async=False) try: - client.connected_clients.remove(self) + base_client.connected_clients.remove(self) except ValueError: # pragma: no cover pass await self._reset() @@ -566,7 +566,7 @@ async def _read_loop_websocket(self): if self.state == 'connected': await self._trigger_event('disconnect', run_async=False) try: - client.connected_clients.remove(self) + base_client.connected_clients.remove(self) except ValueError: # pragma: no cover pass await self._reset() diff --git a/src/engineio/asyncio_server.py b/src/engineio/async_server.py similarity index 99% rename from src/engineio/asyncio_server.py rename to src/engineio/async_server.py index 96252cd5..2fe2e800 100644 --- a/src/engineio/asyncio_server.py +++ b/src/engineio/async_server.py @@ -1,13 +1,13 @@ import asyncio import urllib +from . import base_server from . import exceptions from . import packet -from . import server -from . import asyncio_socket +from . import async_socket -class AsyncServer(server.Server): +class AsyncServer(base_server.BaseServer): """An Engine.IO server for asyncio. This class implements a fully compliant Engine.IO web server with support @@ -418,7 +418,7 @@ async def _handle_connect(self, environ, transport, jsonp_index=None): self._service_task) sid = self.generate_id() - s = asyncio_socket.AsyncSocket(self, sid) + s = async_socket.AsyncSocket(self, sid) self.sockets[sid] = s pkt = packet.Packet( diff --git a/src/engineio/asyncio_socket.py b/src/engineio/async_socket.py similarity index 99% rename from src/engineio/asyncio_socket.py rename to src/engineio/async_socket.py index 1fbd0ef3..b6e6354b 100644 --- a/src/engineio/asyncio_socket.py +++ b/src/engineio/async_socket.py @@ -2,13 +2,13 @@ import sys import time +from . import base_socket from . import exceptions from . import packet from . import payload -from . import socket -class AsyncSocket(socket.Socket): +class AsyncSocket(base_socket.BaseSocket): async def poll(self): """Wait for packets to send to the client.""" try: diff --git a/src/engineio/base_client.py b/src/engineio/base_client.py new file mode 100644 index 00000000..5b2eac17 --- /dev/null +++ b/src/engineio/base_client.py @@ -0,0 +1,121 @@ +import logging +import time +import urllib +from . import packet + +default_logger = logging.getLogger('engineio.client') +connected_clients = [] + + +class BaseClient: + event_names = ['connect', 'disconnect', 'message'] + + def __init__(self, logger=False, json=None, request_timeout=5, + http_session=None, ssl_verify=True, handle_sigint=True, + websocket_extra_options=None): + self.handlers = {} + self.base_url = None + self.transports = None + self.current_transport = None + self.sid = None + self.upgrades = None + self.ping_interval = None + self.ping_timeout = None + self.http = http_session + self.external_http = http_session is not None + self.handle_sigint = handle_sigint + self.ws = None + self.read_loop_task = None + self.write_loop_task = None + self.queue = None + self.state = 'disconnected' + self.ssl_verify = ssl_verify + self.websocket_extra_options = websocket_extra_options or {} + + if json is not None: + packet.Packet.json = json + if not isinstance(logger, bool): + self.logger = logger + else: + self.logger = default_logger + if self.logger.level == logging.NOTSET: + if logger: + self.logger.setLevel(logging.INFO) + else: + self.logger.setLevel(logging.ERROR) + self.logger.addHandler(logging.StreamHandler()) + + self.request_timeout = request_timeout + + def is_asyncio_based(self): + return False + + def on(self, event, handler=None): + """Register an event handler. + + :param event: The event name. Can be ``'connect'``, ``'message'`` or + ``'disconnect'``. + :param handler: The function that should be invoked to handle the + event. When this parameter is not given, the method + acts as a decorator for the handler function. + + Example usage:: + + # as a decorator: + @eio.on('connect') + def connect_handler(): + print('Connection request') + + # as a method: + def message_handler(msg): + print('Received message: ', msg) + eio.send('response') + eio.on('message', message_handler) + """ + if event not in self.event_names: + raise ValueError('Invalid event') + + def set_handler(handler): + self.handlers[event] = handler + return handler + + if handler is None: + return set_handler + set_handler(handler) + + def transport(self): + """Return the name of the transport currently in use. + + The possible values returned by this function are ``'polling'`` and + ``'websocket'``. + """ + return self.current_transport + + def _reset(self): + self.state = 'disconnected' + self.sid = None + + def _get_engineio_url(self, url, engineio_path, transport): + """Generate the Engine.IO connection URL.""" + engineio_path = engineio_path.strip('/') + parsed_url = urllib.parse.urlparse(url) + + if transport == 'polling': + scheme = 'http' + elif transport == 'websocket': + scheme = 'ws' + else: # pragma: no cover + raise ValueError('invalid transport') + if parsed_url.scheme in ['https', 'wss']: + scheme += 's' + + return ('{scheme}://{netloc}/{path}/?{query}' + '{sep}transport={transport}&EIO=4').format( + scheme=scheme, netloc=parsed_url.netloc, + path=engineio_path, query=parsed_url.query, + sep='&' if parsed_url.query else '', + transport=transport) + + def _get_url_timestamp(self): + """Generate the Engine.IO query string timestamp.""" + return '&t=' + str(time.time()) diff --git a/src/engineio/base_server.py b/src/engineio/base_server.py new file mode 100644 index 00000000..c32cb141 --- /dev/null +++ b/src/engineio/base_server.py @@ -0,0 +1,338 @@ +import base64 +import gzip +import importlib +import io +import logging +import secrets +import zlib + +from . import packet +from . import payload + +default_logger = logging.getLogger('engineio.server') + + +class BaseServer: + compression_methods = ['gzip', 'deflate'] + event_names = ['connect', 'disconnect', 'message'] + valid_transports = ['polling', 'websocket'] + _default_monitor_clients = True + sequence_number = 0 + + def __init__(self, async_mode=None, ping_interval=25, ping_timeout=20, + max_http_buffer_size=1000000, allow_upgrades=True, + http_compression=True, compression_threshold=1024, + cookie=None, cors_allowed_origins=None, + cors_credentials=True, logger=False, json=None, + async_handlers=True, monitor_clients=None, transports=None, + **kwargs): + self.ping_timeout = ping_timeout + if isinstance(ping_interval, tuple): + self.ping_interval = ping_interval[0] + self.ping_interval_grace_period = ping_interval[1] + else: + self.ping_interval = ping_interval + self.ping_interval_grace_period = 0 + self.max_http_buffer_size = max_http_buffer_size + self.allow_upgrades = allow_upgrades + self.http_compression = http_compression + self.compression_threshold = compression_threshold + self.cookie = cookie + self.cors_allowed_origins = cors_allowed_origins + self.cors_credentials = cors_credentials + self.async_handlers = async_handlers + self.sockets = {} + self.handlers = {} + self.log_message_keys = set() + self.start_service_task = monitor_clients \ + if monitor_clients is not None else self._default_monitor_clients + self.service_task_handle = None + self.service_task_event = None + if json is not None: + packet.Packet.json = json + if not isinstance(logger, bool): + self.logger = logger + else: + self.logger = default_logger + if self.logger.level == logging.NOTSET: + if logger: + self.logger.setLevel(logging.INFO) + else: + self.logger.setLevel(logging.ERROR) + self.logger.addHandler(logging.StreamHandler()) + modes = self.async_modes() + if async_mode is not None: + modes = [async_mode] if async_mode in modes else [] + self._async = None + self.async_mode = None + for mode in modes: + try: + self._async = importlib.import_module( + 'engineio.async_drivers.' + mode)._async + asyncio_based = self._async['asyncio'] \ + if 'asyncio' in self._async else False + if asyncio_based != self.is_asyncio_based(): + continue # pragma: no cover + self.async_mode = mode + break + except ImportError: + pass + if self.async_mode is None: + raise ValueError('Invalid async_mode specified') + if self.is_asyncio_based() and \ + ('asyncio' not in self._async or not + self._async['asyncio']): # pragma: no cover + raise ValueError('The selected async_mode is not asyncio ' + 'compatible') + if not self.is_asyncio_based() and 'asyncio' in self._async and \ + self._async['asyncio']: # pragma: no cover + raise ValueError('The selected async_mode requires asyncio and ' + 'must use the AsyncServer class') + if transports is not None: + if isinstance(transports, str): + transports = [transports] + transports = [transport for transport in transports + if transport in self.valid_transports] + if not transports: + raise ValueError('No valid transports provided') + self.transports = transports or self.valid_transports + self.logger.info('Server initialized for %s.', self.async_mode) + + def is_asyncio_based(self): + return False + + def async_modes(self): + return ['eventlet', 'gevent_uwsgi', 'gevent', 'threading'] + + def on(self, event, handler=None): + """Register an event handler. + + :param event: The event name. Can be ``'connect'``, ``'message'`` or + ``'disconnect'``. + :param handler: The function that should be invoked to handle the + event. When this parameter is not given, the method + acts as a decorator for the handler function. + + Example usage:: + + # as a decorator: + @eio.on('connect') + def connect_handler(sid, environ): + print('Connection request') + if environ['REMOTE_ADDR'] in blacklisted: + return False # reject + + # as a method: + def message_handler(sid, msg): + print('Received message: ', msg) + eio.send(sid, 'response') + eio.on('message', message_handler) + + The handler function receives the ``sid`` (session ID) for the + client as first argument. The ``'connect'`` event handler receives the + WSGI environment as a second argument, and can return ``False`` to + reject the connection. The ``'message'`` handler receives the message + payload as a second argument. The ``'disconnect'`` handler does not + take a second argument. + """ + if event not in self.event_names: + raise ValueError('Invalid event') + + def set_handler(handler): + self.handlers[event] = handler + return handler + + if handler is None: + return set_handler + set_handler(handler) + + def transport(self, sid): + """Return the name of the transport used by the client. + + The two possible values returned by this function are ``'polling'`` + and ``'websocket'``. + + :param sid: The session of the client. + """ + return 'websocket' if self._get_socket(sid).upgraded else 'polling' + + def create_queue(self, *args, **kwargs): + """Create a queue object using the appropriate async model. + + This is a utility function that applications can use to create a queue + without having to worry about using the correct call for the selected + async mode. + """ + return self._async['queue'](*args, **kwargs) + + def get_queue_empty_exception(self): + """Return the queue empty exception for the appropriate async model. + + This is a utility function that applications can use to work with a + queue without having to worry about using the correct call for the + selected async mode. + """ + return self._async['queue_empty'] + + def create_event(self, *args, **kwargs): + """Create an event object using the appropriate async model. + + This is a utility function that applications can use to create an + event without having to worry about using the correct call for the + selected async mode. + """ + return self._async['event'](*args, **kwargs) + + def generate_id(self): + """Generate a unique session id.""" + id = base64.b64encode( + secrets.token_bytes(12) + self.sequence_number.to_bytes(3, 'big')) + self.sequence_number = (self.sequence_number + 1) & 0xffffff + return id.decode('utf-8').replace('/', '_').replace('+', '-') + + def _generate_sid_cookie(self, sid, attributes): + """Generate the sid cookie.""" + cookie = attributes.get('name', 'io') + '=' + sid + for attribute, value in attributes.items(): + if attribute == 'name': + continue + if callable(value): + value = value() + if value is True: + cookie += '; ' + attribute + else: + cookie += '; ' + attribute + '=' + value + return cookie + + def _upgrades(self, sid, transport): + """Return the list of possible upgrades for a client connection.""" + if not self.allow_upgrades or self._get_socket(sid).upgraded or \ + transport == 'websocket': + return [] + if self._async['websocket'] is None: # pragma: no cover + self._log_error_once( + 'The WebSocket transport is not available, you must install a ' + 'WebSocket server that is compatible with your async mode to ' + 'enable it. See the documentation for details.', + 'no-websocket') + return [] + return ['websocket'] + + def _get_socket(self, sid): + """Return the socket object for a given session.""" + try: + s = self.sockets[sid] + except KeyError: + raise KeyError('Session not found') + if s.closed: + del self.sockets[sid] + raise KeyError('Session is disconnected') + return s + + def _ok(self, packets=None, headers=None, jsonp_index=None): + """Generate a successful HTTP response.""" + if packets is not None: + if headers is None: + headers = [] + headers += [('Content-Type', 'text/plain; charset=UTF-8')] + return {'status': '200 OK', + 'headers': headers, + 'response': payload.Payload(packets=packets).encode( + jsonp_index=jsonp_index).encode('utf-8')} + else: + return {'status': '200 OK', + 'headers': [('Content-Type', 'text/plain')], + 'response': b'OK'} + + def _bad_request(self, message=None): + """Generate a bad request HTTP error response.""" + if message is None: + message = 'Bad Request' + message = packet.Packet.json.dumps(message) + return {'status': '400 BAD REQUEST', + 'headers': [('Content-Type', 'text/plain')], + 'response': message.encode('utf-8')} + + def _method_not_found(self): + """Generate a method not found HTTP error response.""" + return {'status': '405 METHOD NOT FOUND', + 'headers': [('Content-Type', 'text/plain')], + 'response': b'Method Not Found'} + + def _unauthorized(self, message=None): + """Generate a unauthorized HTTP error response.""" + if message is None: + message = 'Unauthorized' + message = packet.Packet.json.dumps(message) + return {'status': '401 UNAUTHORIZED', + 'headers': [('Content-Type', 'application/json')], + 'response': message.encode('utf-8')} + + def _cors_allowed_origins(self, environ): + default_origins = [] + if 'wsgi.url_scheme' in environ and 'HTTP_HOST' in environ: + default_origins.append('{scheme}://{host}'.format( + scheme=environ['wsgi.url_scheme'], host=environ['HTTP_HOST'])) + if 'HTTP_X_FORWARDED_PROTO' in environ or \ + 'HTTP_X_FORWARDED_HOST' in environ: + scheme = environ.get( + 'HTTP_X_FORWARDED_PROTO', + environ['wsgi.url_scheme']).split(',')[0].strip() + default_origins.append('{scheme}://{host}'.format( + scheme=scheme, host=environ.get( + 'HTTP_X_FORWARDED_HOST', environ['HTTP_HOST']).split( + ',')[0].strip())) + if self.cors_allowed_origins is None: + allowed_origins = default_origins + elif self.cors_allowed_origins == '*': + allowed_origins = None + elif isinstance(self.cors_allowed_origins, str): + allowed_origins = [self.cors_allowed_origins] + elif callable(self.cors_allowed_origins): + origin = environ.get('HTTP_ORIGIN') + allowed_origins = [origin] \ + if self.cors_allowed_origins(origin) else [] + else: + allowed_origins = self.cors_allowed_origins + return allowed_origins + + def _cors_headers(self, environ): + """Return the cross-origin-resource-sharing headers.""" + if self.cors_allowed_origins == []: + # special case, CORS handling is completely disabled + return [] + headers = [] + allowed_origins = self._cors_allowed_origins(environ) + if 'HTTP_ORIGIN' in environ and \ + (allowed_origins is None or environ['HTTP_ORIGIN'] in + allowed_origins): + headers = [('Access-Control-Allow-Origin', environ['HTTP_ORIGIN'])] + if environ['REQUEST_METHOD'] == 'OPTIONS': + headers += [('Access-Control-Allow-Methods', 'OPTIONS, GET, POST')] + if 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS' in environ: + headers += [('Access-Control-Allow-Headers', + environ['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])] + if self.cors_credentials: + headers += [('Access-Control-Allow-Credentials', 'true')] + return headers + + def _gzip(self, response): + """Apply gzip compression to a response.""" + bytesio = io.BytesIO() + with gzip.GzipFile(fileobj=bytesio, mode='w') as gz: + gz.write(response) + return bytesio.getvalue() + + def _deflate(self, response): + """Apply deflate compression to a response.""" + return zlib.compress(response) + + def _log_error_once(self, message, message_key): + """Log message with logging.ERROR level the first time, then log + with given level.""" + if message_key not in self.log_message_keys: + self.logger.error(message + ' (further occurrences of this error ' + 'will be logged with level INFO)') + self.log_message_keys.add(message_key) + else: + self.logger.info(message) diff --git a/src/engineio/base_socket.py b/src/engineio/base_socket.py new file mode 100644 index 00000000..6d42bfec --- /dev/null +++ b/src/engineio/base_socket.py @@ -0,0 +1,15 @@ + +class BaseSocket: + upgrade_protocols = ['websocket'] + + def __init__(self, server, sid): + self.server = server + self.sid = sid + self.queue = self.server.create_queue() + self.last_ping = None + self.connected = False + self.upgrading = False + self.upgraded = False + self.closing = False + self.closed = False + self.session = {} diff --git a/src/engineio/client.py b/src/engineio/client.py index cfc1c24f..c9f52926 100644 --- a/src/engineio/client.py +++ b/src/engineio/client.py @@ -16,6 +16,7 @@ import websocket except ImportError: # pragma: no cover websocket = None +from . import base_client from . import exceptions from . import packet from . import payload @@ -42,7 +43,7 @@ def signal_handler(sig, frame): original_signal_handler = None -class Client(object): +class Client(base_client.BaseClient): """An Engine.IO client. This class implements a fully compliant Engine.IO web client with support @@ -85,75 +86,11 @@ def __init__(self, logger=False, json=None, request_timeout=5, threading.current_thread() == threading.main_thread(): original_signal_handler = signal.signal(signal.SIGINT, signal_handler) - self.handlers = {} - self.base_url = None - self.transports = None - self.current_transport = None - self.sid = None - self.upgrades = None - self.ping_interval = None - self.ping_timeout = None - self.http = http_session - self.external_http = http_session is not None - self.handle_sigint = handle_sigint - self.ws = None - self.read_loop_task = None - self.write_loop_task = None - self.queue = None - self.state = 'disconnected' - self.ssl_verify = ssl_verify - self.websocket_extra_options = websocket_extra_options or {} - - if json is not None: - packet.Packet.json = json - if not isinstance(logger, bool): - self.logger = logger - else: - self.logger = default_logger - if self.logger.level == logging.NOTSET: - if logger: - self.logger.setLevel(logging.INFO) - else: - self.logger.setLevel(logging.ERROR) - self.logger.addHandler(logging.StreamHandler()) - - self.request_timeout = request_timeout - - def is_asyncio_based(self): - return False - - def on(self, event, handler=None): - """Register an event handler. - - :param event: The event name. Can be ``'connect'``, ``'message'`` or - ``'disconnect'``. - :param handler: The function that should be invoked to handle the - event. When this parameter is not given, the method - acts as a decorator for the handler function. - - Example usage:: - - # as a decorator: - @eio.on('connect') - def connect_handler(): - print('Connection request') - - # as a method: - def message_handler(msg): - print('Received message: ', msg) - eio.send('response') - eio.on('message', message_handler) - """ - if event not in self.event_names: - raise ValueError('Invalid event') - - def set_handler(handler): - self.handlers[event] = handler - return handler - - if handler is None: - return set_handler - set_handler(handler) + super().__init__(logger=logger, json=json, + request_timeout=request_timeout, + http_session=http_session, ssl_verify=ssl_verify, + handle_sigint=handle_sigint, + websocket_extra_options=websocket_extra_options) def connect(self, url, headers=None, transports=None, engineio_path='engine.io'): @@ -231,14 +168,6 @@ def disconnect(self, abort=False): pass self._reset() - def transport(self): - """Return the name of the transport currently in use. - - The possible values returned by this function are ``'polling'`` and - ``'websocket'``. - """ - return self.current_transport - def start_background_task(self, target, *args, **kwargs): """Start a background task. @@ -272,10 +201,6 @@ def create_event(self, *args, **kwargs): """Create an event object.""" return threading.Event(*args, **kwargs) - def _reset(self): - self.state = 'disconnected' - self.sid = None - def _connect_polling(self, url, headers, engineio_path): """Establish a long-polling connection to the Engine.IO server.""" if requests is None: # pragma: no cover @@ -556,31 +481,6 @@ def _trigger_event(self, event, *args, **kwargs): except: self.logger.exception(event + ' handler error') - def _get_engineio_url(self, url, engineio_path, transport): - """Generate the Engine.IO connection URL.""" - engineio_path = engineio_path.strip('/') - parsed_url = urllib.parse.urlparse(url) - - if transport == 'polling': - scheme = 'http' - elif transport == 'websocket': - scheme = 'ws' - else: # pragma: no cover - raise ValueError('invalid transport') - if parsed_url.scheme in ['https', 'wss']: - scheme += 's' - - return ('{scheme}://{netloc}/{path}/?{query}' - '{sep}transport={transport}&EIO=4').format( - scheme=scheme, netloc=parsed_url.netloc, - path=engineio_path, query=parsed_url.query, - sep='&' if parsed_url.query else '', - transport=transport) - - def _get_url_timestamp(self): - """Generate the Engine.IO query string timestamp.""" - return '&t=' + str(time.time()) - def _read_loop_polling(self): """Read packets by polling the Engine.IO server.""" while self.state == 'connected': diff --git a/src/engineio/server.py b/src/engineio/server.py index f7353707..914b08b5 100644 --- a/src/engineio/server.py +++ b/src/engineio/server.py @@ -1,21 +1,15 @@ -import base64 -import gzip -import importlib -import io import logging -import secrets import urllib -import zlib +from . import base_server from . import exceptions from . import packet -from . import payload from . import socket default_logger = logging.getLogger('engineio.server') -class Server(object): +class Server(base_server.BaseServer): """An Engine.IO server. This class implements a fully compliant Engine.IO web server with support @@ -83,139 +77,6 @@ class Server(object): :param kwargs: Reserved for future extensions, any additional parameters given as keyword arguments will be silently ignored. """ - compression_methods = ['gzip', 'deflate'] - event_names = ['connect', 'disconnect', 'message'] - valid_transports = ['polling', 'websocket'] - _default_monitor_clients = True - sequence_number = 0 - - def __init__(self, async_mode=None, ping_interval=25, ping_timeout=20, - max_http_buffer_size=1000000, allow_upgrades=True, - http_compression=True, compression_threshold=1024, - cookie=None, cors_allowed_origins=None, - cors_credentials=True, logger=False, json=None, - async_handlers=True, monitor_clients=None, transports=None, - **kwargs): - self.ping_timeout = ping_timeout - if isinstance(ping_interval, tuple): - self.ping_interval = ping_interval[0] - self.ping_interval_grace_period = ping_interval[1] - else: - self.ping_interval = ping_interval - self.ping_interval_grace_period = 0 - self.max_http_buffer_size = max_http_buffer_size - self.allow_upgrades = allow_upgrades - self.http_compression = http_compression - self.compression_threshold = compression_threshold - self.cookie = cookie - self.cors_allowed_origins = cors_allowed_origins - self.cors_credentials = cors_credentials - self.async_handlers = async_handlers - self.sockets = {} - self.handlers = {} - self.log_message_keys = set() - self.start_service_task = monitor_clients \ - if monitor_clients is not None else self._default_monitor_clients - self.service_task_handle = None - self.service_task_event = None - if json is not None: - packet.Packet.json = json - if not isinstance(logger, bool): - self.logger = logger - else: - self.logger = default_logger - if self.logger.level == logging.NOTSET: - if logger: - self.logger.setLevel(logging.INFO) - else: - self.logger.setLevel(logging.ERROR) - self.logger.addHandler(logging.StreamHandler()) - modes = self.async_modes() - if async_mode is not None: - modes = [async_mode] if async_mode in modes else [] - self._async = None - self.async_mode = None - for mode in modes: - try: - self._async = importlib.import_module( - 'engineio.async_drivers.' + mode)._async - asyncio_based = self._async['asyncio'] \ - if 'asyncio' in self._async else False - if asyncio_based != self.is_asyncio_based(): - continue # pragma: no cover - self.async_mode = mode - break - except ImportError: - pass - if self.async_mode is None: - raise ValueError('Invalid async_mode specified') - if self.is_asyncio_based() and \ - ('asyncio' not in self._async or not - self._async['asyncio']): # pragma: no cover - raise ValueError('The selected async_mode is not asyncio ' - 'compatible') - if not self.is_asyncio_based() and 'asyncio' in self._async and \ - self._async['asyncio']: # pragma: no cover - raise ValueError('The selected async_mode requires asyncio and ' - 'must use the AsyncServer class') - if transports is not None: - if isinstance(transports, str): - transports = [transports] - transports = [transport for transport in transports - if transport in self.valid_transports] - if not transports: - raise ValueError('No valid transports provided') - self.transports = transports or self.valid_transports - self.logger.info('Server initialized for %s.', self.async_mode) - - def is_asyncio_based(self): - return False - - def async_modes(self): - return ['eventlet', 'gevent_uwsgi', 'gevent', 'threading'] - - def on(self, event, handler=None): - """Register an event handler. - - :param event: The event name. Can be ``'connect'``, ``'message'`` or - ``'disconnect'``. - :param handler: The function that should be invoked to handle the - event. When this parameter is not given, the method - acts as a decorator for the handler function. - - Example usage:: - - # as a decorator: - @eio.on('connect') - def connect_handler(sid, environ): - print('Connection request') - if environ['REMOTE_ADDR'] in blacklisted: - return False # reject - - # as a method: - def message_handler(sid, msg): - print('Received message: ', msg) - eio.send(sid, 'response') - eio.on('message', message_handler) - - The handler function receives the ``sid`` (session ID) for the - client as first argument. The ``'connect'`` event handler receives the - WSGI environment as a second argument, and can return ``False`` to - reject the connection. The ``'message'`` handler receives the message - payload as a second argument. The ``'disconnect'`` handler does not - take a second argument. - """ - if event not in self.event_names: - raise ValueError('Invalid event') - - def set_handler(handler): - self.handlers[event] = handler - return handler - - if handler is None: - return set_handler - set_handler(handler) - def send(self, sid, data): """Send a message to a client. @@ -320,16 +181,6 @@ def disconnect(self, sid=None): client.close() self.sockets = {} - def transport(self, sid): - """Return the name of the transport used by the client. - - The two possible values returned by this function are ``'polling'`` - and ``'websocket'``. - - :param sid: The session of the client. - """ - return 'websocket' if self._get_socket(sid).upgraded else 'polling' - def handle_request(self, environ, start_response): """Handle an HTTP request from the client. @@ -511,54 +362,6 @@ def sleep(self, seconds=0): """ return self._async['sleep'](seconds) - def create_queue(self, *args, **kwargs): - """Create a queue object using the appropriate async model. - - This is a utility function that applications can use to create a queue - without having to worry about using the correct call for the selected - async mode. - """ - return self._async['queue'](*args, **kwargs) - - def get_queue_empty_exception(self): - """Return the queue empty exception for the appropriate async model. - - This is a utility function that applications can use to work with a - queue without having to worry about using the correct call for the - selected async mode. - """ - return self._async['queue_empty'] - - def create_event(self, *args, **kwargs): - """Create an event object using the appropriate async model. - - This is a utility function that applications can use to create an - event without having to worry about using the correct call for the - selected async mode. - """ - return self._async['event'](*args, **kwargs) - - def generate_id(self): - """Generate a unique session id.""" - id = base64.b64encode( - secrets.token_bytes(12) + self.sequence_number.to_bytes(3, 'big')) - self.sequence_number = (self.sequence_number + 1) & 0xffffff - return id.decode('utf-8').replace('/', '_').replace('+', '-') - - def _generate_sid_cookie(self, sid, attributes): - """Generate the sid cookie.""" - cookie = attributes.get('name', 'io') + '=' + sid - for attribute, value in attributes.items(): - if attribute == 'name': - continue - if callable(value): - value = value() - if value is True: - cookie += '; ' + attribute - else: - cookie += '; ' + attribute + '=' + value - return cookie - def _handle_connect(self, environ, start_response, transport, jsonp_index=None): """Handle a client connection request.""" @@ -619,20 +422,6 @@ def _handle_connect(self, environ, start_response, transport, except exceptions.QueueEmpty: return self._bad_request() - def _upgrades(self, sid, transport): - """Return the list of possible upgrades for a client connection.""" - if not self.allow_upgrades or self._get_socket(sid).upgraded or \ - transport == 'websocket': - return [] - if self._async['websocket'] is None: # pragma: no cover - self._log_error_once( - 'The WebSocket transport is not available, you must install a ' - 'WebSocket server that is compatible with your async mode to ' - 'enable it. See the documentation for details.', - 'no-websocket') - return [] - return ['websocket'] - def _trigger_event(self, event, *args, **kwargs): """Invoke an event handler.""" run_async = kwargs.pop('run_async', False) @@ -652,125 +441,6 @@ def run_handler(): else: return run_handler() - def _get_socket(self, sid): - """Return the socket object for a given session.""" - try: - s = self.sockets[sid] - except KeyError: - raise KeyError('Session not found') - if s.closed: - del self.sockets[sid] - raise KeyError('Session is disconnected') - return s - - def _ok(self, packets=None, headers=None, jsonp_index=None): - """Generate a successful HTTP response.""" - if packets is not None: - if headers is None: - headers = [] - headers += [('Content-Type', 'text/plain; charset=UTF-8')] - return {'status': '200 OK', - 'headers': headers, - 'response': payload.Payload(packets=packets).encode( - jsonp_index=jsonp_index).encode('utf-8')} - else: - return {'status': '200 OK', - 'headers': [('Content-Type', 'text/plain')], - 'response': b'OK'} - - def _bad_request(self, message=None): - """Generate a bad request HTTP error response.""" - if message is None: - message = 'Bad Request' - message = packet.Packet.json.dumps(message) - return {'status': '400 BAD REQUEST', - 'headers': [('Content-Type', 'text/plain')], - 'response': message.encode('utf-8')} - - def _method_not_found(self): - """Generate a method not found HTTP error response.""" - return {'status': '405 METHOD NOT FOUND', - 'headers': [('Content-Type', 'text/plain')], - 'response': b'Method Not Found'} - - def _unauthorized(self, message=None): - """Generate a unauthorized HTTP error response.""" - if message is None: - message = 'Unauthorized' - message = packet.Packet.json.dumps(message) - return {'status': '401 UNAUTHORIZED', - 'headers': [('Content-Type', 'application/json')], - 'response': message.encode('utf-8')} - - def _cors_allowed_origins(self, environ): - default_origins = [] - if 'wsgi.url_scheme' in environ and 'HTTP_HOST' in environ: - default_origins.append('{scheme}://{host}'.format( - scheme=environ['wsgi.url_scheme'], host=environ['HTTP_HOST'])) - if 'HTTP_X_FORWARDED_PROTO' in environ or \ - 'HTTP_X_FORWARDED_HOST' in environ: - scheme = environ.get( - 'HTTP_X_FORWARDED_PROTO', - environ['wsgi.url_scheme']).split(',')[0].strip() - default_origins.append('{scheme}://{host}'.format( - scheme=scheme, host=environ.get( - 'HTTP_X_FORWARDED_HOST', environ['HTTP_HOST']).split( - ',')[0].strip())) - if self.cors_allowed_origins is None: - allowed_origins = default_origins - elif self.cors_allowed_origins == '*': - allowed_origins = None - elif isinstance(self.cors_allowed_origins, str): - allowed_origins = [self.cors_allowed_origins] - elif callable(self.cors_allowed_origins): - origin = environ.get('HTTP_ORIGIN') - allowed_origins = [origin] \ - if self.cors_allowed_origins(origin) else [] - else: - allowed_origins = self.cors_allowed_origins - return allowed_origins - - def _cors_headers(self, environ): - """Return the cross-origin-resource-sharing headers.""" - if self.cors_allowed_origins == []: - # special case, CORS handling is completely disabled - return [] - headers = [] - allowed_origins = self._cors_allowed_origins(environ) - if 'HTTP_ORIGIN' in environ and \ - (allowed_origins is None or environ['HTTP_ORIGIN'] in - allowed_origins): - headers = [('Access-Control-Allow-Origin', environ['HTTP_ORIGIN'])] - if environ['REQUEST_METHOD'] == 'OPTIONS': - headers += [('Access-Control-Allow-Methods', 'OPTIONS, GET, POST')] - if 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS' in environ: - headers += [('Access-Control-Allow-Headers', - environ['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])] - if self.cors_credentials: - headers += [('Access-Control-Allow-Credentials', 'true')] - return headers - - def _gzip(self, response): - """Apply gzip compression to a response.""" - bytesio = io.BytesIO() - with gzip.GzipFile(fileobj=bytesio, mode='w') as gz: - gz.write(response) - return bytesio.getvalue() - - def _deflate(self, response): - """Apply deflate compression to a response.""" - return zlib.compress(response) - - def _log_error_once(self, message, message_key): - """Log message with logging.ERROR level the first time, then log - with given level.""" - if message_key not in self.log_message_keys: - self.logger.error(message + ' (further occurrences of this error ' - 'will be logged with level INFO)') - self.log_message_keys.add(message_key) - else: - self.logger.info(message) - def _service_task(self): # pragma: no cover """Monitor connected clients and clean up those that time out.""" self.service_task_event = self.create_event() diff --git a/src/engineio/socket.py b/src/engineio/socket.py index dfa414b0..a8c14c20 100644 --- a/src/engineio/socket.py +++ b/src/engineio/socket.py @@ -1,27 +1,14 @@ import sys import time +from . import base_socket from . import exceptions from . import packet from . import payload -class Socket(object): +class Socket(base_socket.BaseSocket): """An Engine.IO socket.""" - upgrade_protocols = ['websocket'] - - def __init__(self, server, sid): - self.server = server - self.sid = sid - self.queue = self.server.create_queue() - self.last_ping = None - self.connected = False - self.upgrading = False - self.upgraded = False - self.closing = False - self.closed = False - self.session = {} - def poll(self): """Wait for packets to send to the client.""" queue_empty = self.server.get_queue_empty_exception() diff --git a/tests/asyncio/__init__.py b/tests/async/__init__.py similarity index 100% rename from tests/asyncio/__init__.py rename to tests/async/__init__.py diff --git a/tests/asyncio/files/file.txt b/tests/async/files/file.txt similarity index 100% rename from tests/asyncio/files/file.txt rename to tests/async/files/file.txt diff --git a/tests/asyncio/files/index.html b/tests/async/files/index.html similarity index 100% rename from tests/asyncio/files/index.html rename to tests/async/files/index.html diff --git a/tests/asyncio/index.html b/tests/async/index.html similarity index 100% rename from tests/asyncio/index.html rename to tests/async/index.html diff --git a/tests/asyncio/test_async_aiohttp.py b/tests/async/test_aiohttp.py similarity index 100% rename from tests/asyncio/test_async_aiohttp.py rename to tests/async/test_aiohttp.py diff --git a/tests/asyncio/test_async_asgi.py b/tests/async/test_asgi.py similarity index 99% rename from tests/asyncio/test_async_asgi.py rename to tests/async/test_asgi.py index 6611be0c..d51c38bd 100644 --- a/tests/asyncio/test_async_asgi.py +++ b/tests/async/test_asgi.py @@ -1,6 +1,5 @@ import asyncio import os -import sys import unittest from unittest import mock @@ -23,7 +22,6 @@ def _run(coro): return asyncio.get_event_loop().run_until_complete(coro) -@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+') class AsgiTests(unittest.TestCase): def test_create_app(self): app = async_asgi.ASGIApp( diff --git a/tests/asyncio/test_asyncio_client.py b/tests/async/test_client.py similarity index 91% rename from tests/asyncio/test_asyncio_client.py rename to tests/async/test_client.py index c3371ce2..665a3a4c 100644 --- a/tests/asyncio/test_asyncio_client.py +++ b/tests/async/test_client.py @@ -10,8 +10,8 @@ aiohttp = None import pytest -from engineio import asyncio_client -from engineio import client +from engineio import async_client +from engineio import base_client from engineio import exceptions from engineio import packet from engineio import payload @@ -36,42 +36,42 @@ def _run(coro): @unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+') class TestAsyncClient(unittest.TestCase): def test_is_asyncio_based(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() assert c.is_asyncio_based() def test_already_connected(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'connected' with pytest.raises(ValueError): _run(c.connect('http://foo')) def test_invalid_transports(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() with pytest.raises(ValueError): _run(c.connect('http://foo', transports=['foo', 'bar'])) def test_some_invalid_transports(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._connect_websocket = AsyncMock() _run(c.connect('http://foo', transports=['foo', 'websocket', 'bar'])) assert c.transports == ['websocket'] def test_connect_polling(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._connect_polling = AsyncMock(return_value='foo') assert _run(c.connect('http://foo')) == 'foo' c._connect_polling.mock.assert_called_once_with( 'http://foo', {}, 'engine.io' ) - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._connect_polling = AsyncMock(return_value='foo') assert _run(c.connect('http://foo', transports=['polling'])) == 'foo' c._connect_polling.mock.assert_called_once_with( 'http://foo', {}, 'engine.io' ) - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._connect_polling = AsyncMock(return_value='foo') assert ( _run(c.connect('http://foo', transports=['polling', 'websocket'])) @@ -82,14 +82,14 @@ def test_connect_polling(self): ) def test_connect_websocket(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._connect_websocket = AsyncMock(return_value='foo') assert _run(c.connect('http://foo', transports=['websocket'])) == 'foo' c._connect_websocket.mock.assert_called_once_with( 'http://foo', {}, 'engine.io' ) - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._connect_websocket = AsyncMock(return_value='foo') assert _run(c.connect('http://foo', transports='websocket')) == 'foo' c._connect_websocket.mock.assert_called_once_with( @@ -97,7 +97,7 @@ def test_connect_websocket(self): ) def test_connect_query_string(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._connect_polling = AsyncMock(return_value='foo') assert _run(c.connect('http://foo?bar=baz')) == 'foo' c._connect_polling.mock.assert_called_once_with( @@ -105,7 +105,7 @@ def test_connect_query_string(self): ) def test_connect_custom_headers(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._connect_polling = AsyncMock(return_value='foo') assert _run(c.connect('http://foo', headers={'Foo': 'Bar'})) == 'foo' c._connect_polling.mock.assert_called_once_with( @@ -113,7 +113,7 @@ def test_connect_custom_headers(self): ) def test_wait(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() done = [] async def fake_read_look_task(): @@ -124,12 +124,12 @@ async def fake_read_look_task(): assert done == [True] def test_wait_no_task(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.read_loop_task = None _run(c.wait()) def test_send(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() saved_packets = [] async def fake_send_packet(pkt): @@ -150,7 +150,7 @@ async def fake_send_packet(pkt): assert saved_packets[2].binary def test_disconnect_not_connected(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'foo' c.sid = 'bar' _run(c.disconnect()) @@ -158,8 +158,8 @@ def test_disconnect_not_connected(self): assert c.sid is None def test_disconnect_polling(self): - c = asyncio_client.AsyncClient() - client.connected_clients.append(c) + c = async_client.AsyncClient() + base_client.connected_clients.append(c) c.state = 'connected' c.current_transport = 'polling' c.queue = mock.MagicMock() @@ -171,14 +171,14 @@ def test_disconnect_polling(self): c._trigger_event = AsyncMock() _run(c.disconnect()) c.ws.close.mock.assert_not_called() - assert c not in client.connected_clients + assert c not in base_client.connected_clients c._trigger_event.mock.assert_called_once_with( 'disconnect', run_async=False ) def test_disconnect_websocket(self): - c = asyncio_client.AsyncClient() - client.connected_clients.append(c) + c = async_client.AsyncClient() + base_client.connected_clients.append(c) c.state = 'connected' c.current_transport = 'websocket' c.queue = mock.MagicMock() @@ -190,14 +190,14 @@ def test_disconnect_websocket(self): c._trigger_event = AsyncMock() _run(c.disconnect()) c.ws.close.mock.assert_called_once_with() - assert c not in client.connected_clients + assert c not in base_client.connected_clients c._trigger_event.mock.assert_called_once_with( 'disconnect', run_async=False ) def test_disconnect_polling_abort(self): - c = asyncio_client.AsyncClient() - client.connected_clients.append(c) + c = async_client.AsyncClient() + base_client.connected_clients.append(c) c.state = 'connected' c.current_transport = 'polling' c.queue = mock.MagicMock() @@ -209,11 +209,11 @@ def test_disconnect_polling_abort(self): _run(c.disconnect(abort=True)) c.queue.join.mock.assert_not_called() c.ws.close.mock.assert_not_called() - assert c not in client.connected_clients + assert c not in base_client.connected_clients def test_disconnect_websocket_abort(self): - c = asyncio_client.AsyncClient() - client.connected_clients.append(c) + c = async_client.AsyncClient() + base_client.connected_clients.append(c) c.state = 'connected' c.current_transport = 'websocket' c.queue = mock.MagicMock() @@ -225,7 +225,7 @@ def test_disconnect_websocket_abort(self): _run(c.disconnect(abort=True)) c.queue.join.mock.assert_not_called() c.ws.mock.assert_not_called() - assert c not in client.connected_clients + assert c not in base_client.connected_clients def test_background_tasks(self): r = [] @@ -233,7 +233,7 @@ def test_background_tasks(self): async def foo(arg): r.append(arg) - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.start_background_task(foo, 'bar') pending = asyncio.all_tasks(loop=asyncio.get_event_loop()) \ if hasattr(asyncio, 'all_tasks') else asyncio.Task.all_tasks() @@ -241,17 +241,17 @@ async def foo(arg): assert r == ['bar'] def test_sleep(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() _run(c.sleep(0)) def test_create_queue(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() q = c.create_queue() with pytest.raises(q.Empty): q.get_nowait() def test_create_event(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() e = c.create_event() assert not e.is_set() e.set() @@ -259,7 +259,7 @@ def test_create_event(self): @mock.patch('engineio.client.time.time', return_value=123.456) def test_polling_connection_failed(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_request = AsyncMock(return_value=None) with pytest.raises(exceptions.ConnectionError): _run(c.connect('http://foo', headers={'Foo': 'Bar'})) @@ -271,7 +271,7 @@ def test_polling_connection_failed(self, _time): ) def test_polling_connection_404(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_request = AsyncMock() c._send_request.mock.return_value.status = 404 c._send_request.mock.return_value.json = AsyncMock( @@ -287,7 +287,7 @@ def test_polling_connection_404(self): assert exc.args[1] == {'foo': 'bar'} def test_polling_connection_404_no_json(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_request = AsyncMock() c._send_request.mock.return_value.status = 404 c._send_request.mock.return_value.json = AsyncMock( @@ -303,7 +303,7 @@ def test_polling_connection_404_no_json(self): assert exc.args[1] is None def test_polling_connection_invalid_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_request = AsyncMock() c._send_request.mock.return_value.status = 200 c._send_request.mock.return_value.read = AsyncMock(return_value=b'foo') @@ -311,7 +311,7 @@ def test_polling_connection_invalid_packet(self): _run(c.connect('http://foo')) def test_polling_connection_no_open_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_request = AsyncMock() c._send_request.mock.return_value.status = 200 c._send_request.mock.return_value.read = AsyncMock( @@ -333,7 +333,7 @@ def test_polling_connection_no_open_packet(self): _run(c.connect('http://foo')) def test_polling_connection_successful(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_request = AsyncMock() c._send_request.mock.return_value.status = 200 c._send_request.mock.return_value.read = AsyncMock( @@ -362,7 +362,7 @@ def test_polling_connection_successful(self): c._read_loop_websocket.mock.assert_not_called() c._write_loop.mock.assert_called_once_with() on_connect.mock.assert_called_once_with() - assert c in client.connected_clients + assert c in base_client.connected_clients assert ( c.base_url == 'http://foo/engine.io/?transport=polling&EIO=4&sid=123' @@ -374,7 +374,7 @@ def test_polling_connection_successful(self): assert c.transport() == 'polling' def test_polling_https_noverify_connection_successful(self): - c = asyncio_client.AsyncClient(ssl_verify=False) + c = async_client.AsyncClient(ssl_verify=False) c._send_request = AsyncMock() c._send_request.mock.return_value.status = 200 c._send_request.mock.return_value.read = AsyncMock( @@ -403,7 +403,7 @@ def test_polling_https_noverify_connection_successful(self): c._read_loop_websocket.mock.assert_not_called() c._write_loop.mock.assert_called_once_with() on_connect.mock.assert_called_once_with() - assert c in client.connected_clients + assert c in base_client.connected_clients assert ( c.base_url == 'https://foo/engine.io/?transport=polling&EIO=4&sid=123' @@ -415,7 +415,7 @@ def test_polling_https_noverify_connection_successful(self): assert c.transport() == 'polling' def test_polling_connection_with_more_packets(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_request = AsyncMock() c._send_request.mock.return_value.status = 200 c._send_request.mock.return_value.read = AsyncMock( @@ -448,7 +448,7 @@ def test_polling_connection_with_more_packets(self): ) def test_polling_connection_upgraded(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_request = AsyncMock() c._send_request.mock.return_value.status = 200 c._send_request.mock.return_value.read = AsyncMock( @@ -475,7 +475,7 @@ def test_polling_connection_upgraded(self): 'http://foo', {}, 'engine.io' ) on_connect.assert_called_once_with() - assert c in client.connected_clients + assert c in base_client.connected_clients assert ( c.base_url == 'http://foo/engine.io/?transport=polling&EIO=4&sid=123' @@ -486,7 +486,7 @@ def test_polling_connection_upgraded(self): assert c.upgrades == ['websocket'] def test_polling_connection_not_upgraded(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_request = AsyncMock() c._send_request.mock.return_value.status = 200 c._send_request.mock.return_value.read = AsyncMock( @@ -519,11 +519,11 @@ def test_polling_connection_not_upgraded(self): c._read_loop_websocket.mock.assert_not_called() c._write_loop.mock.assert_called_once_with() on_connect.assert_called_once_with() - assert c in client.connected_clients + assert c in base_client.connected_clients @mock.patch('engineio.client.time.time', return_value=123.456) def test_websocket_connection_failed(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock( side_effect=[aiohttp.client_exceptions.ServerConnectionError()] @@ -544,7 +544,7 @@ def test_websocket_connection_failed(self, _time): @mock.patch('engineio.client.time.time', return_value=123.456) def test_websocket_connection_extra(self, _time): - c = asyncio_client.AsyncClient(websocket_extra_options={ + c = async_client.AsyncClient(websocket_extra_options={ 'headers': {'Baz': 'Qux'}, 'timeout': 10 }) @@ -568,7 +568,7 @@ def test_websocket_connection_extra(self, _time): @mock.patch('engineio.client.time.time', return_value=123.456) def test_websocket_upgrade_failed(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock( side_effect=[aiohttp.client_exceptions.ServerConnectionError()] @@ -582,7 +582,7 @@ def test_websocket_upgrade_failed(self, _time): ) def test_websocket_connection_no_open_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock() ws = c.http.ws_connect.mock.return_value @@ -595,7 +595,7 @@ def test_websocket_connection_no_open_packet(self): @mock.patch('engineio.client.time.time', return_value=123.456) def test_websocket_connection_successful(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock() ws = c.http.ws_connect.mock.return_value @@ -620,7 +620,7 @@ def test_websocket_connection_successful(self, _time): c._read_loop_websocket.mock.assert_called_once_with() c._write_loop.mock.assert_called_once_with() on_connect.assert_called_once_with() - assert c in client.connected_clients + assert c in base_client.connected_clients assert c.base_url == 'ws://foo/engine.io/?transport=websocket&EIO=4' assert c.sid == '123' assert c.ping_interval == 1 @@ -636,7 +636,7 @@ def test_websocket_connection_successful(self, _time): @mock.patch('engineio.client.time.time', return_value=123.456) def test_websocket_https_noverify_connection_successful(self, _time): - c = asyncio_client.AsyncClient(ssl_verify=False) + c = async_client.AsyncClient(ssl_verify=False) c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock() ws = c.http.ws_connect.mock.return_value @@ -661,7 +661,7 @@ def test_websocket_https_noverify_connection_successful(self, _time): c._read_loop_websocket.mock.assert_called_once_with() c._write_loop.mock.assert_called_once_with() on_connect.assert_called_once_with() - assert c in client.connected_clients + assert c in base_client.connected_clients assert c.base_url == 'wss://foo/engine.io/?transport=websocket&EIO=4' assert c.sid == '123' assert c.ping_interval == 1 @@ -676,7 +676,7 @@ def test_websocket_https_noverify_connection_successful(self, _time): @mock.patch('engineio.client.time.time', return_value=123.456) def test_websocket_connection_with_cookies(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock() ws = c.http.ws_connect.mock.return_value @@ -709,7 +709,7 @@ def test_websocket_connection_with_cookies(self, _time): @mock.patch('engineio.client.time.time', return_value=123.456) def test_websocket_connection_with_cookie_header(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock() ws = c.http.ws_connect.mock.return_value @@ -747,7 +747,7 @@ def test_websocket_connection_with_cookie_header(self, _time): @mock.patch('engineio.client.time.time', return_value=123.456) def test_websocket_connection_with_cookies_and_headers(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock() ws = c.http.ws_connect.mock.return_value @@ -788,7 +788,7 @@ def test_websocket_connection_with_cookies_and_headers(self, _time): ) def test_websocket_upgrade_no_pong(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock() ws = c.http.ws_connect.mock.return_value @@ -820,7 +820,7 @@ def test_websocket_upgrade_no_pong(self): ws.send_str.mock.assert_called_once_with('2probe') def test_websocket_upgrade_successful(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.http = mock.MagicMock(closed=False) c.http.ws_connect = AsyncMock() ws = c.http.ws_connect.mock.return_value @@ -843,7 +843,7 @@ def test_websocket_upgrade_successful(self): c._read_loop_websocket.mock.assert_called_once_with() c._write_loop.mock.assert_called_once_with() on_connect.assert_not_called() # was called by polling - assert c not in client.connected_clients # was added by polling + assert c not in base_client.connected_clients # was added by polling assert c.base_url == 'http://foo' # not changed assert c.sid == '123' # not changed assert c.transport() == 'websocket' @@ -852,23 +852,23 @@ def test_websocket_upgrade_successful(self): assert ws.send_str.mock.call_args_list[1] == (('5',),) # upgrade def test_receive_unknown_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() _run(c._receive_packet(packet.Packet(encoded_packet='9'))) # should be ignored def test_receive_noop_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() _run(c._receive_packet(packet.Packet(packet.NOOP))) # should be ignored def test_receive_ping_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._send_packet = AsyncMock() _run(c._receive_packet(packet.Packet(packet.PING))) assert c._send_packet.mock.call_args_list[0][0][0].encode() == '3' def test_receive_message_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c._trigger_event = AsyncMock() _run(c._receive_packet(packet.Packet(packet.MESSAGE, {'foo': 'bar'}))) c._trigger_event.mock.assert_called_once_with( @@ -876,20 +876,20 @@ def test_receive_message_packet(self): ) def test_receive_close_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.disconnect = AsyncMock() _run(c._receive_packet(packet.Packet(packet.CLOSE))) c.disconnect.mock.assert_called_once_with(abort=True) def test_send_packet_disconnected(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.queue = c.create_queue() c.state = 'disconnected' _run(c._send_packet(packet.Packet(packet.NOOP))) assert c.queue.empty() def test_send_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.queue = c.create_queue() c.state = 'connected' _run(c._send_packet(packet.Packet(packet.NOOP))) @@ -904,7 +904,7 @@ def foo_handler(arg): result.append('ok') result.append(arg) - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.on('message', handler=foo_handler) _run(c._trigger_event('message', 'bar')) assert result == ['ok', 'bar'] @@ -916,7 +916,7 @@ async def foo_handler(arg): result.append('ok') result.append(arg) - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.on('message', handler=foo_handler) _run(c._trigger_event('message', 'bar')) assert result == ['ok', 'bar'] @@ -928,7 +928,7 @@ def connect_handler(arg): def foo_handler(arg): return 1 / 0 - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.on('connect', handler=connect_handler) c.on('message', handler=foo_handler) assert not _run(c._trigger_event('connect', '123')) @@ -941,7 +941,7 @@ async def connect_handler(arg): async def foo_handler(arg): return 1 / 0 - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.on('connect', handler=connect_handler) c.on('message', handler=foo_handler) assert not _run(c._trigger_event('connect', '123')) @@ -954,7 +954,7 @@ def foo_handler(arg): result.append('ok') result.append(arg) - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.on('message', handler=foo_handler) fut = _run(c._trigger_event('message', 'bar', run_async=True)) asyncio.get_event_loop().run_until_complete(fut) @@ -967,7 +967,7 @@ async def foo_handler(arg): result.append('ok') result.append(arg) - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.on('message', handler=foo_handler) fut = _run(c._trigger_event('message', 'bar', run_async=True)) asyncio.get_event_loop().run_until_complete(fut) @@ -980,7 +980,7 @@ def foo_handler(arg): result.append(arg) return 1 / 0 - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.on('message', handler=foo_handler) fut = _run(c._trigger_event('message', 'bar', run_async=True)) with pytest.raises(ZeroDivisionError): @@ -994,7 +994,7 @@ async def foo_handler(arg): result.append(arg) return 1 / 0 - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.on('message', handler=foo_handler) fut = _run(c._trigger_event('message', 'bar', run_async=True)) with pytest.raises(ZeroDivisionError): @@ -1002,13 +1002,13 @@ async def foo_handler(arg): assert result == ['bar'] def test_trigger_unknown_event(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() _run(c._trigger_event('connect', run_async=False)) _run(c._trigger_event('message', 123, run_async=True)) # should do nothing def test_read_loop_polling_disconnected(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'disconnected' c._trigger_event = AsyncMock() c.write_loop_task = AsyncMock()() @@ -1018,7 +1018,7 @@ def test_read_loop_polling_disconnected(self): @mock.patch('engineio.client.time.time', return_value=123.456) def test_read_loop_polling_no_response(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.ping_interval = 25 c.ping_timeout = 5 c.state = 'connected' @@ -1040,7 +1040,7 @@ def test_read_loop_polling_no_response(self, _time): @mock.patch('engineio.client.time.time', return_value=123.456) def test_read_loop_polling_bad_status(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.ping_interval = 25 c.ping_timeout = 5 c.state = 'connected' @@ -1059,7 +1059,7 @@ def test_read_loop_polling_bad_status(self, _time): @mock.patch('engineio.client.time.time', return_value=123.456) def test_read_loop_polling_bad_packet(self, _time): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.ping_interval = 25 c.ping_timeout = 60 c.state = 'connected' @@ -1078,7 +1078,7 @@ def test_read_loop_polling_bad_packet(self, _time): ) def test_read_loop_polling(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.ping_interval = 25 c.ping_timeout = 5 c.state = 'connected' @@ -1111,14 +1111,14 @@ def test_read_loop_polling(self): assert c._receive_packet.mock.call_args_list[1][0][0].encode() == '6' def test_read_loop_websocket_disconnected(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'disconnected' c.write_loop_task = AsyncMock()() _run(c._read_loop_websocket()) # should not block def test_read_loop_websocket_timeout(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.ping_interval = 1 c.ping_timeout = 2 c.base_url = 'ws://foo' @@ -1133,7 +1133,7 @@ def test_read_loop_websocket_timeout(self): c.queue.put.mock.assert_called_once_with(None) def test_read_loop_websocket_no_response(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.ping_interval = 1 c.ping_timeout = 2 c.base_url = 'ws://foo' @@ -1150,7 +1150,7 @@ def test_read_loop_websocket_no_response(self): c.queue.put.mock.assert_called_once_with(None) def test_read_loop_websocket_unexpected_error(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.ping_interval = 1 c.ping_timeout = 2 c.base_url = 'ws://foo' @@ -1165,7 +1165,7 @@ def test_read_loop_websocket_unexpected_error(self): c.queue.put.mock.assert_called_once_with(None) def test_read_loop_websocket(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.ping_interval = 1 c.ping_timeout = 2 c.base_url = 'ws://foo' @@ -1187,13 +1187,13 @@ def test_read_loop_websocket(self): c.queue.put.mock.assert_called_once_with(None) def test_write_loop_disconnected(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'disconnected' _run(c._write_loop()) # should not block def test_write_loop_no_packets(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'connected' c.ping_interval = 1 c.ping_timeout = 2 @@ -1204,7 +1204,7 @@ def test_write_loop_no_packets(self): c.queue.get.mock.assert_called_once_with() def test_write_loop_empty_queue(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'connected' c.ping_interval = 1 c.ping_timeout = 2 @@ -1215,7 +1215,7 @@ def test_write_loop_empty_queue(self): c.queue.get.mock.assert_called_once_with() def test_write_loop_polling_one_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.base_url = 'http://foo' c.state = 'connected' c.ping_interval = 1 @@ -1246,7 +1246,7 @@ def test_write_loop_polling_one_packet(self): ) def test_write_loop_polling_three_packets(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.base_url = 'http://foo' c.state = 'connected' c.ping_interval = 1 @@ -1287,7 +1287,7 @@ def test_write_loop_polling_three_packets(self): ) def test_write_loop_polling_two_packets_done(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.base_url = 'http://foo' c.state = 'connected' c.ping_interval = 1 @@ -1324,7 +1324,7 @@ def test_write_loop_polling_two_packets_done(self): assert c.state == 'connected' def test_write_loop_polling_bad_connection(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.base_url = 'http://foo' c.state = 'connected' c.ping_interval = 1 @@ -1352,7 +1352,7 @@ def test_write_loop_polling_bad_connection(self): assert c.state == 'connected' def test_write_loop_polling_bad_status(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.base_url = 'http://foo' c.state = 'connected' c.ping_interval = 1 @@ -1381,7 +1381,7 @@ def test_write_loop_polling_bad_status(self): assert c.state == 'disconnected' def test_write_loop_websocket_one_packet(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'connected' c.ping_interval = 1 c.ping_timeout = 2 @@ -1403,7 +1403,7 @@ def test_write_loop_websocket_one_packet(self): c.ws.send_str.mock.assert_called_once_with('4{"foo":"bar"}') def test_write_loop_websocket_three_packets(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'connected' c.ping_interval = 1 c.ping_timeout = 2 @@ -1433,7 +1433,7 @@ def test_write_loop_websocket_three_packets(self): assert c.ws.send_str.mock.call_args_list[2][0][0] == '6' def test_write_loop_websocket_one_packet_binary(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'connected' c.ping_interval = 1 c.ping_timeout = 2 @@ -1452,7 +1452,7 @@ def test_write_loop_websocket_one_packet_binary(self): c.ws.send_bytes.mock.assert_called_once_with(b'foo') def test_write_loop_websocket_bad_connection(self): - c = asyncio_client.AsyncClient() + c = async_client.AsyncClient() c.state = 'connected' c.ping_interval = 1 c.ping_timeout = 2 @@ -1476,12 +1476,12 @@ def test_write_loop_websocket_bad_connection(self): @mock.patch('engineio.client.original_signal_handler') def test_signal_handler(self, original_handler): clients = [mock.MagicMock(), mock.MagicMock()] - client.connected_clients = clients[:] - client.connected_clients[0].is_asyncio_based.return_value = False - client.connected_clients[1].is_asyncio_based.return_value = True + base_client.connected_clients = clients[:] + base_client.connected_clients[0].is_asyncio_based.return_value = False + base_client.connected_clients[1].is_asyncio_based.return_value = True async def test(): - asyncio_client.async_signal_handler() + async_client.async_signal_handler() asyncio.get_event_loop().run_until_complete(test()) clients[0].disconnect.assert_not_called() diff --git a/tests/asyncio/test_async_sanic.py b/tests/async/test_sanic.py similarity index 100% rename from tests/asyncio/test_async_sanic.py rename to tests/async/test_sanic.py diff --git a/tests/asyncio/test_asyncio_server.py b/tests/async/test_server.py similarity index 89% rename from tests/asyncio/test_asyncio_server.py rename to tests/async/test_server.py index 9360a745..d967577e 100644 --- a/tests/asyncio/test_asyncio_server.py +++ b/tests/async/test_server.py @@ -2,14 +2,13 @@ import gzip import io import logging -import sys import unittest from unittest import mock import zlib import pytest -from engineio import asyncio_server +from engineio import async_server from engineio.async_drivers import aiohttp as async_aiohttp from engineio import exceptions from engineio import json @@ -33,7 +32,6 @@ def _run(coro): return asyncio.get_event_loop().run_until_complete(coro) -@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+') class TestAsyncServer(unittest.TestCase): @staticmethod def get_async_mock(environ={'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}): @@ -70,11 +68,11 @@ def _get_mock_socket(self): @classmethod def setUpClass(cls): - asyncio_server.AsyncServer._default_monitor_clients = False + async_server.AsyncServer._default_monitor_clients = False @classmethod def tearDownClass(cls): - asyncio_server.AsyncServer._default_monitor_clients = True + async_server.AsyncServer._default_monitor_clients = True def setUp(self): logging.getLogger('engineio').setLevel(logging.NOTSET) @@ -84,15 +82,15 @@ def tearDown(self): packet.Packet.json = json def test_is_asyncio_based(self): - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() assert s.is_asyncio_based() def test_async_modes(self): - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() assert s.async_modes() == ['aiohttp', 'sanic', 'tornado', 'asgi'] def test_async_mode_aiohttp(self): - s = asyncio_server.AsyncServer(async_mode='aiohttp') + s = async_server.AsyncServer(async_mode='aiohttp') assert s.async_mode == 'aiohttp' assert s._async['asyncio'] assert s._async['create_route'] == async_aiohttp.create_route @@ -103,24 +101,24 @@ def test_async_mode_aiohttp(self): @mock.patch('importlib.import_module') def test_async_mode_auto_aiohttp(self, import_module): import_module.side_effect = [self.get_async_mock()] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() assert s.async_mode == 'aiohttp' def test_async_modes_wsgi(self): with pytest.raises(ValueError): - asyncio_server.AsyncServer(async_mode='eventlet') + async_server.AsyncServer(async_mode='eventlet') with pytest.raises(ValueError): - asyncio_server.AsyncServer(async_mode='gevent') + async_server.AsyncServer(async_mode='gevent') with pytest.raises(ValueError): - asyncio_server.AsyncServer(async_mode='gevent_uwsgi') + async_server.AsyncServer(async_mode='gevent_uwsgi') with pytest.raises(ValueError): - asyncio_server.AsyncServer(async_mode='threading') + async_server.AsyncServer(async_mode='threading') @mock.patch('importlib.import_module') def test_attach(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.attach('app', engineio_path='abc') a._async['create_route'].assert_called_with('app', s, '/abc/') s.attach('app', engineio_path='/def/') @@ -131,7 +129,7 @@ def test_attach(self, import_module): a._async['create_route'].assert_called_with('app', s, '/jkl/') def test_session(self): - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = self._get_mock_socket() async def _func(): @@ -143,7 +141,7 @@ async def _func(): _run(_func()) def test_disconnect(self): - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = mock_socket = self._get_mock_socket() _run(s.disconnect('foo')) assert mock_socket.close.mock.call_count == 1 @@ -151,7 +149,7 @@ def test_disconnect(self): assert 'foo' not in s.sockets def test_disconnect_all(self): - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = mock_foo = self._get_mock_socket() s.sockets['bar'] = mock_bar = self._get_mock_socket() _run(s.disconnect()) @@ -168,7 +166,7 @@ def test_jsonp_not_supported(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'j=abc'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() response = _run(s.handle_request('request')) assert response == 'response' a._async['translate_request'].assert_called_once_with('request') @@ -181,7 +179,7 @@ def test_jsonp_index(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'j=233'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() response = _run(s.handle_request('request')) assert response == 'response' a._async['translate_request'].assert_called_once_with('request') @@ -198,7 +196,7 @@ def test_jsonp_index(self, import_module): def test_connect(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert len(s.sockets) == 1 assert a._async['make_response'].call_count == 1 @@ -225,7 +223,7 @@ def test_connect_async_request_response_handlers(self, import_module): return_value=a._async['make_response'].return_value ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert len(s.sockets) == 1 assert a._async['make_response'].mock.call_count == 1 @@ -246,7 +244,7 @@ def test_connect_async_request_response_handlers(self, import_module): def test_connect_no_upgrades(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer(allow_upgrades=False) + s = async_server.AsyncServer(allow_upgrades=False) _run(s.handle_request('request')) packets = payload.Payload( encoded_payload=a._async['make_response'].call_args[0][2].decode( @@ -259,7 +257,7 @@ def test_connect_bad_eio_version(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'EIO=1'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert a._async['make_response'].call_count == 1 assert a._async['make_response'].call_args[0][0] == '400 BAD REQUEST' @@ -270,7 +268,7 @@ def test_connect_bad_eio_version(self, import_module): def test_connect_custom_ping_times(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer(ping_timeout=123, ping_interval=456) + s = async_server.AsyncServer(ping_timeout=123, ping_interval=456) _run(s.handle_request('request')) packets = payload.Payload( encoded_payload=a._async['make_response'].call_args[0][2].decode( @@ -279,19 +277,19 @@ def test_connect_custom_ping_times(self, import_module): assert packets[0].data['pingInterval'] == 456000 @mock.patch('importlib.import_module') - @mock.patch('engineio.asyncio_server.asyncio_socket.AsyncSocket') + @mock.patch('engineio.async_server.async_socket.AsyncSocket') def test_connect_bad_poll(self, AsyncSocket, import_module): a = self.get_async_mock() import_module.side_effect = [a] AsyncSocket.return_value = self._get_mock_socket() AsyncSocket.return_value.poll.side_effect = [exceptions.QueueEmpty] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert a._async['make_response'].call_count == 1 assert a._async['make_response'].call_args[0][0] == '400 BAD REQUEST' @mock.patch('importlib.import_module') - @mock.patch('engineio.asyncio_server.asyncio_socket.AsyncSocket') + @mock.patch('engineio.async_server.async_socket.AsyncSocket') def test_connect_transport_websocket(self, AsyncSocket, import_module): a = self.get_async_mock( { @@ -302,7 +300,7 @@ def test_connect_transport_websocket(self, AsyncSocket, import_module): ) import_module.side_effect = [a] AsyncSocket.return_value = self._get_mock_socket() - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.generate_id = mock.MagicMock(return_value='123') # force socket to stay open, so that we can check it later AsyncSocket().closed = False @@ -313,7 +311,7 @@ def test_connect_transport_websocket(self, AsyncSocket, import_module): ) @mock.patch('importlib.import_module') - @mock.patch('engineio.asyncio_server.asyncio_socket.AsyncSocket') + @mock.patch('engineio.async_server.async_socket.AsyncSocket') def test_http_upgrade_case_insensitive(self, AsyncSocket, import_module): a = self.get_async_mock( { @@ -324,7 +322,7 @@ def test_http_upgrade_case_insensitive(self, AsyncSocket, import_module): ) import_module.side_effect = [a] AsyncSocket.return_value = self._get_mock_socket() - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.generate_id = mock.MagicMock(return_value='123') # force socket to stay open, so that we can check it later AsyncSocket().closed = False @@ -335,7 +333,7 @@ def test_http_upgrade_case_insensitive(self, AsyncSocket, import_module): ) @mock.patch('importlib.import_module') - @mock.patch('engineio.asyncio_server.asyncio_socket.AsyncSocket') + @mock.patch('engineio.async_server.async_socket.AsyncSocket') def test_connect_transport_websocket_closed( self, AsyncSocket, import_module): a = self.get_async_mock( @@ -347,7 +345,7 @@ def test_connect_transport_websocket_closed( ) import_module.side_effect = [a] AsyncSocket.return_value = self._get_mock_socket() - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.generate_id = mock.MagicMock(return_value='123') # this mock handler just closes the socket, as it would happen on a @@ -365,7 +363,7 @@ def test_connect_transport_invalid(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'transport=foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert a._async['make_response'].call_count == 1 assert a._async['make_response'].call_args[0][0] == '400 BAD REQUEST' @@ -376,7 +374,7 @@ def test_connect_transport_websocket_without_upgrade(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'transport=websocket'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert a._async['make_response'].call_count == 1 assert a._async['make_response'].call_args[0][0] == '400 BAD REQUEST' @@ -385,7 +383,7 @@ def test_connect_transport_websocket_without_upgrade(self, import_module): def test_connect_cors_headers(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -397,7 +395,7 @@ def test_connect_cors_allowed_origin(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'HTTP_ORIGIN': 'b'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins=['a', 'b']) + s = async_server.AsyncServer(cors_allowed_origins=['a', 'b']) _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -416,7 +414,7 @@ def cors(origin): a = self.get_async_mock(environ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins=cors) + s = async_server.AsyncServer(cors_allowed_origins=cors) _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -432,7 +430,7 @@ def test_connect_cors_not_allowed_origin(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'HTTP_ORIGIN': 'c'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins=['a', 'b']) + s = async_server.AsyncServer(cors_allowed_origins=['a', 'b']) _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '400 BAD REQUEST' headers = a._async['make_response'].call_args[0][1] @@ -450,7 +448,7 @@ def test_connect_cors_not_allowed_origin_async_response( return_value=a._async['make_response'].return_value ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins=['a', 'b']) + s = async_server.AsyncServer(cors_allowed_origins=['a', 'b']) _run(s.handle_request('request')) assert ( a._async['make_response'].mock.call_args[0][0] == '400 BAD REQUEST' @@ -465,7 +463,7 @@ def test_connect_cors_all_origins(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'HTTP_ORIGIN': 'foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins='*') + s = async_server.AsyncServer(cors_allowed_origins='*') _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -478,7 +476,7 @@ def test_connect_cors_one_origin(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'HTTP_ORIGIN': 'a'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins='a') + s = async_server.AsyncServer(cors_allowed_origins='a') _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -491,7 +489,7 @@ def test_connect_cors_one_origin_not_allowed(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '', 'HTTP_ORIGIN': 'b'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins='a') + s = async_server.AsyncServer(cors_allowed_origins='a') _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '400 BAD REQUEST' headers = a._async['make_response'].call_args[0][1] @@ -510,7 +508,7 @@ def test_connect_cors_headers_default_origin(self, import_module): } ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -520,7 +518,7 @@ def test_connect_cors_headers_default_origin(self, import_module): def test_connect_cors_no_credentials(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_credentials=False) + s = async_server.AsyncServer(cors_credentials=False) _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -532,7 +530,7 @@ def test_connect_cors_options(self, import_module): {'REQUEST_METHOD': 'OPTIONS', 'QUERY_STRING': ''} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_credentials=False) + s = async_server.AsyncServer(cors_credentials=False) _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -551,7 +549,7 @@ def test_connect_cors_disabled(self, import_module): } ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins=[]) + s = async_server.AsyncServer(cors_allowed_origins=[]) _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -562,7 +560,7 @@ def test_connect_cors_disabled(self, import_module): def test_connect_cors_default_no_origin(self, import_module): a = self.get_async_mock({'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins=[]) + s = async_server.AsyncServer(cors_allowed_origins=[]) _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -573,7 +571,7 @@ def test_connect_cors_default_no_origin(self, import_module): def test_connect_cors_all_no_origin(self, import_module): a = self.get_async_mock({'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins='*') + s = async_server.AsyncServer(cors_allowed_origins='*') _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -584,7 +582,7 @@ def test_connect_cors_all_no_origin(self, import_module): def test_connect_cors_disabled_no_origin(self, import_module): a = self.get_async_mock({'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cors_allowed_origins=[]) + s = async_server.AsyncServer(cors_allowed_origins=[]) _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' headers = a._async['make_response'].call_args[0][1] @@ -595,7 +593,7 @@ def test_connect_cors_disabled_no_origin(self, import_module): def test_connect_event(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.generate_id = mock.MagicMock(return_value='123') def mock_connect(sid, environ): @@ -609,7 +607,7 @@ def mock_connect(sid, environ): def test_connect_event_rejects(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.generate_id = mock.MagicMock(return_value='123') def mock_connect(sid, environ): @@ -625,7 +623,7 @@ def mock_connect(sid, environ): def test_connect_event_rejects_with_message(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.generate_id = mock.MagicMock(return_value='123') def mock_connect(sid, environ): @@ -643,7 +641,7 @@ def mock_connect(sid, environ): def test_method_not_found(self, import_module): a = self.get_async_mock({'REQUEST_METHOD': 'PUT', 'QUERY_STRING': ''}) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert len(s.sockets) == 0 assert ( @@ -656,7 +654,7 @@ def test_get_request_with_bad_sid(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert len(s.sockets) == 0 assert a._async['make_response'].call_args[0][0] == '400 BAD REQUEST' @@ -667,7 +665,7 @@ def test_post_request_with_bad_sid(self, import_module): {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.handle_request('request')) assert len(s.sockets) == 0 assert a._async['make_response'].call_args[0][0] == '400 BAD REQUEST' @@ -676,7 +674,7 @@ def test_post_request_with_bad_sid(self, import_module): def test_send(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = mock_socket = self._get_mock_socket() _run(s.send('foo', 'hello')) assert mock_socket.send.mock.call_count == 1 @@ -689,7 +687,7 @@ def test_send(self, import_module): def test_send_unknown_socket(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() # just ensure no exceptions are raised _run(s.send('foo', 'hello')) @@ -699,7 +697,7 @@ def test_get_request(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = mock_socket = self._get_mock_socket() mock_socket.handle_get_request.mock.return_value = [ packet.Packet(packet.MESSAGE, data='hello') @@ -719,7 +717,7 @@ def test_get_request_custom_response(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = mock_socket = self._get_mock_socket() mock_socket.handle_get_request.mock.return_value = 'resp' r = _run(s.handle_request('request')) @@ -731,7 +729,7 @@ def test_get_request_closes_socket(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = mock_socket = self._get_mock_socket() async def mock_get_request(*args, **kwargs): @@ -749,7 +747,7 @@ def test_get_request_error(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = mock_socket = self._get_mock_socket() async def mock_get_request(*args, **kwargs): @@ -766,7 +764,7 @@ def test_post_request(self, import_module): {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = self._get_mock_socket() _run(s.handle_request('request')) assert a._async['make_response'].call_args[0][0] == '200 OK' @@ -777,7 +775,7 @@ def test_post_request_error(self, import_module): {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.sockets['foo'] = mock_socket = self._get_mock_socket() async def mock_post_request(*args, **kwargs): @@ -803,7 +801,7 @@ def test_gzip_compression(self, import_module): } ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(compression_threshold=0) + s = async_server.AsyncServer(compression_threshold=0) s.sockets['foo'] = mock_socket = self._get_mock_socket() mock_socket.handle_get_request.mock.return_value = [ packet.Packet(packet.MESSAGE, data='hello') @@ -823,7 +821,7 @@ def test_deflate_compression(self, import_module): } ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(compression_threshold=0) + s = async_server.AsyncServer(compression_threshold=0) s.sockets['foo'] = mock_socket = self._get_mock_socket() mock_socket.handle_get_request.mock.return_value = [ packet.Packet(packet.MESSAGE, data='hello') @@ -843,7 +841,7 @@ def test_gzip_compression_threshold(self, import_module): } ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(compression_threshold=1000) + s = async_server.AsyncServer(compression_threshold=1000) s.sockets['foo'] = mock_socket = self._get_mock_socket() mock_socket.handle_get_request.mock.return_value = [ packet.Packet(packet.MESSAGE, data='hello') @@ -865,7 +863,7 @@ def test_compression_disabled(self, import_module): } ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer( + s = async_server.AsyncServer( http_compression=False, compression_threshold=0 ) s.sockets['foo'] = mock_socket = self._get_mock_socket() @@ -889,7 +887,7 @@ def test_compression_unknown(self, import_module): } ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(compression_threshold=0) + s = async_server.AsyncServer(compression_threshold=0) s.sockets['foo'] = mock_socket = self._get_mock_socket() mock_socket.handle_get_request.mock.return_value = [ packet.Packet(packet.MESSAGE, data='hello') @@ -911,7 +909,7 @@ def test_compression_no_encoding(self, import_module): } ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(compression_threshold=0) + s = async_server.AsyncServer(compression_threshold=0) s.sockets['foo'] = mock_socket = self._get_mock_socket() mock_socket.handle_get_request.mock.return_value = [ packet.Packet(packet.MESSAGE, data='hello') @@ -927,7 +925,7 @@ def test_compression_no_encoding(self, import_module): def test_cookie(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cookie='sid') + s = async_server.AsyncServer(cookie='sid') s.generate_id = mock.MagicMock(return_value='123') _run(s.handle_request('request')) headers = a._async['make_response'].call_args[0][1] @@ -940,7 +938,7 @@ def get_path(): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cookie={ + s = async_server.AsyncServer(cookie={ 'name': 'test', 'path': get_path, 'SameSite': 'None', @@ -957,7 +955,7 @@ def get_path(): def test_no_cookie(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer(cookie=None) + s = async_server.AsyncServer(cookie=None) s.generate_id = mock.MagicMock(return_value='123') _run(s.handle_request('request')) headers = a._async['make_response'].call_args[0][1] @@ -965,17 +963,17 @@ def test_no_cookie(self, import_module): assert header != 'Set-Cookie' def test_logger(self): - s = asyncio_server.AsyncServer(logger=False) + s = async_server.AsyncServer(logger=False) assert s.logger.getEffectiveLevel() == logging.ERROR s.logger.setLevel(logging.NOTSET) - s = asyncio_server.AsyncServer(logger=True) + s = async_server.AsyncServer(logger=True) assert s.logger.getEffectiveLevel() == logging.INFO s.logger.setLevel(logging.WARNING) - s = asyncio_server.AsyncServer(logger=True) + s = async_server.AsyncServer(logger=True) assert s.logger.getEffectiveLevel() == logging.WARNING s.logger.setLevel(logging.NOTSET) my_logger = logging.Logger('foo') - s = asyncio_server.AsyncServer(logger=my_logger) + s = async_server.AsyncServer(logger=my_logger) assert s.logger == my_logger def test_custom_json(self): @@ -991,7 +989,7 @@ def dumps(*args, **kwargs): def loads(*args, **kwargs): return '+++ decoded +++' - asyncio_server.AsyncServer(json=CustomJSON) + async_server.AsyncServer(json=CustomJSON) pkt = packet.Packet(packet.MESSAGE, data={'foo': 'bar'}) assert pkt.encode() == '4*** encoded ***' pkt2 = packet.Packet(encoded_packet=pkt.encode()) @@ -1006,7 +1004,7 @@ def test_background_tasks(self): async def foo(arg): r.append(arg) - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.start_background_task(foo, 'bar') pending = asyncio.all_tasks(loop=asyncio.get_event_loop()) \ if hasattr(asyncio, 'all_tasks') else asyncio.Task.all_tasks() @@ -1014,7 +1012,7 @@ async def foo(arg): assert r == ['bar'] def test_sleep(self): - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() _run(s.sleep(0)) def test_trigger_event_function(self): @@ -1024,7 +1022,7 @@ def foo_handler(arg): result.append('ok') result.append(arg) - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.on('message', handler=foo_handler) _run(s._trigger_event('message', 'bar')) assert result == ['ok', 'bar'] @@ -1036,7 +1034,7 @@ async def foo_handler(arg): result.append('ok') result.append(arg) - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.on('message', handler=foo_handler) _run(s._trigger_event('message', 'bar')) assert result == ['ok', 'bar'] @@ -1048,7 +1046,7 @@ def connect_handler(arg): def foo_handler(arg): return 1 / 0 - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.on('connect', handler=connect_handler) s.on('message', handler=foo_handler) assert not _run(s._trigger_event('connect', '123')) @@ -1061,7 +1059,7 @@ async def connect_handler(arg): async def foo_handler(arg): return 1 / 0 - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.on('connect', handler=connect_handler) s.on('message', handler=foo_handler) assert not _run(s._trigger_event('connect', '123')) @@ -1074,7 +1072,7 @@ def foo_handler(arg): result.append('ok') result.append(arg) - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.on('message', handler=foo_handler) fut = _run(s._trigger_event('message', 'bar', run_async=True)) asyncio.get_event_loop().run_until_complete(fut) @@ -1087,7 +1085,7 @@ async def foo_handler(arg): result.append('ok') result.append(arg) - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.on('message', handler=foo_handler) fut = _run(s._trigger_event('message', 'bar', run_async=True)) asyncio.get_event_loop().run_until_complete(fut) @@ -1100,7 +1098,7 @@ def foo_handler(arg): result.append(arg) return 1 / 0 - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.on('message', handler=foo_handler) fut = _run(s._trigger_event('message', 'bar', run_async=True)) asyncio.get_event_loop().run_until_complete(fut) @@ -1113,21 +1111,21 @@ async def foo_handler(arg): result.append(arg) return 1 / 0 - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() s.on('message', handler=foo_handler) fut = _run(s._trigger_event('message', 'bar', run_async=True)) asyncio.get_event_loop().run_until_complete(fut) assert result == ['bar'] def test_create_queue(self): - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() q = s.create_queue() empty = s.get_queue_empty_exception() with pytest.raises(empty): q.get_nowait() def test_create_event(self): - s = asyncio_server.AsyncServer() + s = async_server.AsyncServer() e = s.create_event() assert not e.is_set() e.set() @@ -1137,7 +1135,7 @@ def test_create_event(self): def test_service_task_started(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer(monitor_clients=True) + s = async_server.AsyncServer(monitor_clients=True) s._service_task = AsyncMock() _run(s.handle_request('request')) s._service_task.mock.assert_called_once_with() @@ -1146,7 +1144,7 @@ def test_service_task_started(self, import_module): def test_shutdown(self, import_module): a = self.get_async_mock() import_module.side_effect = [a] - s = asyncio_server.AsyncServer(monitor_clients=True) + s = async_server.AsyncServer(monitor_clients=True) _run(s.handle_request('request')) assert s.service_task_handle is not None _run(s.shutdown()) @@ -1158,7 +1156,7 @@ def test_transports_disallowed(self, import_module): {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'transport=polling'} ) import_module.side_effect = [a] - s = asyncio_server.AsyncServer(transports='websocket') + s = async_server.AsyncServer(transports='websocket') response = _run(s.handle_request('request')) assert response == 'response' a._async['translate_request'].assert_called_once_with('request') diff --git a/tests/asyncio/test_asyncio_socket.py b/tests/async/test_socket.py similarity index 89% rename from tests/asyncio/test_asyncio_socket.py rename to tests/async/test_socket.py index 78c003e7..b1ea8b41 100644 --- a/tests/asyncio/test_asyncio_socket.py +++ b/tests/async/test_socket.py @@ -1,12 +1,11 @@ import asyncio -import sys import time import unittest from unittest import mock import pytest -from engineio import asyncio_socket +from engineio import async_socket from engineio import exceptions from engineio import packet from engineio import payload @@ -28,7 +27,6 @@ def _run(coro): return asyncio.get_event_loop().run_until_complete(coro) -@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+') class TestSocket(unittest.TestCase): def _get_read_mock_coro(self, payload): mock_input = mock.MagicMock() @@ -68,7 +66,7 @@ def create_queue(*args, **kwargs): def test_create(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') assert s.server == mock_server assert s.sid == 'sid' assert not s.upgraded @@ -80,13 +78,13 @@ def test_create(self): def test_empty_poll(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') with pytest.raises(exceptions.QueueEmpty): _run(s.poll()) def test_poll(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') pkt1 = packet.Packet(packet.MESSAGE, data='hello') pkt2 = packet.Packet(packet.MESSAGE, data='bye') _run(s.send(pkt1)) @@ -95,13 +93,13 @@ def test_poll(self): def test_poll_none(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') _run(s.queue.put(None)) assert _run(s.poll()) == [] def test_poll_none_after_packet(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') pkt = packet.Packet(packet.MESSAGE, data='hello') _run(s.send(pkt)) _run(s.queue.put(None)) @@ -111,7 +109,7 @@ def test_poll_none_after_packet(self): def test_schedule_ping(self): mock_server = self._get_mock_server() mock_server.ping_interval = 0.01 - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.send = AsyncMock() async def schedule_ping(): @@ -125,7 +123,7 @@ async def schedule_ping(): def test_schedule_ping_closed_socket(self): mock_server = self._get_mock_server() mock_server.ping_interval = 0.01 - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.send = AsyncMock() s.closed = True @@ -139,14 +137,14 @@ async def schedule_ping(): def test_pong(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.schedule_ping = mock.MagicMock() _run(s.receive(packet.Packet(packet.PONG, data='abc'))) s.schedule_ping.assert_called_once_with() def test_message_sync_handler(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') _run(s.receive(packet.Packet(packet.MESSAGE, data='foo'))) mock_server._trigger_event.mock.assert_called_once_with( 'message', 'sid', 'foo', run_async=False @@ -154,7 +152,7 @@ def test_message_sync_handler(self): def test_message_async_handler(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') mock_server.async_handlers = True _run(s.receive(packet.Packet(packet.MESSAGE, data='foo'))) mock_server._trigger_event.mock.assert_called_once_with( @@ -163,7 +161,7 @@ def test_message_async_handler(self): def test_invalid_packet(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') with pytest.raises(exceptions.UnknownPacketError): _run(s.receive(packet.Packet(packet.OPEN))) @@ -171,7 +169,7 @@ def test_timeout(self): mock_server = self._get_mock_server() mock_server.ping_interval = 6 mock_server.ping_interval_grace_period = 2 - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.last_ping = time.time() - 9 s.close = AsyncMock() _run(s.send('packet')) @@ -179,7 +177,7 @@ def test_timeout(self): def test_polling_read(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'foo') + s = async_socket.AsyncSocket(mock_server, 'foo') pkt1 = packet.Packet(packet.MESSAGE, data='hello') pkt2 = packet.Packet(packet.MESSAGE, data='bye') _run(s.send(pkt1)) @@ -190,7 +188,7 @@ def test_polling_read(self): def test_polling_read_error(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'foo') + s = async_socket.AsyncSocket(mock_server, 'foo') environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'} with pytest.raises(exceptions.QueueEmpty): _run(s.handle_get_request(environ)) @@ -201,7 +199,7 @@ def test_polling_write(self): pkt1 = packet.Packet(packet.MESSAGE, data='hello') pkt2 = packet.Packet(packet.MESSAGE, data='bye') p = payload.Payload(packets=[pkt1, pkt2]).encode().encode('utf-8') - s = asyncio_socket.AsyncSocket(mock_server, 'foo') + s = async_socket.AsyncSocket(mock_server, 'foo') s.receive = AsyncMock() environ = { 'REQUEST_METHOD': 'POST', @@ -218,7 +216,7 @@ def test_polling_write_too_large(self): pkt2 = packet.Packet(packet.MESSAGE, data='bye') p = payload.Payload(packets=[pkt1, pkt2]).encode().encode('utf-8') mock_server.max_http_buffer_size = len(p) - 1 - s = asyncio_socket.AsyncSocket(mock_server, 'foo') + s = async_socket.AsyncSocket(mock_server, 'foo') s.receive = AsyncMock() environ = { 'REQUEST_METHOD': 'POST', @@ -231,7 +229,7 @@ def test_polling_write_too_large(self): def test_upgrade_handshake(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'foo') + s = async_socket.AsyncSocket(mock_server, 'foo') s._upgrade_websocket = AsyncMock() environ = { 'REQUEST_METHOD': 'GET', @@ -247,7 +245,7 @@ def test_upgrade(self): mock_server._async['websocket'] = mock.MagicMock() mock_ws = AsyncMock() mock_server._async['websocket'].return_value = mock_ws - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True environ = "foo" _run(s._upgrade_websocket(environ)) @@ -259,7 +257,7 @@ def test_upgrade(self): def test_upgrade_twice(self): mock_server = self._get_mock_server() mock_server._async['websocket'] = mock.MagicMock() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True s.upgraded = True environ = "foo" @@ -268,7 +266,7 @@ def test_upgrade_twice(self): def test_upgrade_packet(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True _run(s.receive(packet.Packet(packet.UPGRADE))) r = _run(s.poll()) @@ -277,7 +275,7 @@ def test_upgrade_packet(self): def test_upgrade_no_probe(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True ws = mock.MagicMock() ws.wait = AsyncMock() @@ -287,7 +285,7 @@ def test_upgrade_no_probe(self): def test_upgrade_no_upgrade_packet(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True s.queue.join = AsyncMock(return_value=None) ws = mock.MagicMock() @@ -308,7 +306,7 @@ def test_upgrade_no_upgrade_packet(self): def test_upgrade_not_supported(self): mock_server = self._get_mock_server() mock_server._async['websocket'] = None - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True environ = "foo" _run(s._upgrade_websocket(environ)) @@ -316,7 +314,7 @@ def test_upgrade_not_supported(self): def test_close_packet(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True s.close = AsyncMock() _run(s.receive(packet.Packet(packet.CLOSE))) @@ -324,7 +322,7 @@ def test_close_packet(self): def test_websocket_read_write(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = False s.queue.join = AsyncMock(return_value=None) foo = 'foo' @@ -355,7 +353,7 @@ def test_websocket_read_write(self): def test_websocket_upgrade_read_write(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True s.queue.join = AsyncMock(return_value=None) foo = 'foo' @@ -391,7 +389,7 @@ def test_websocket_upgrade_read_write(self): def test_websocket_upgrade_with_payload(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True s.queue.join = AsyncMock(return_value=None) probe = 'probe' @@ -408,7 +406,7 @@ def test_websocket_upgrade_with_payload(self): def test_websocket_upgrade_with_backlog(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True s.queue.join = AsyncMock(return_value=None) probe = 'probe' @@ -440,7 +438,7 @@ def test_websocket_upgrade_with_backlog(self): def test_websocket_read_write_wait_fail(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = False s.queue.join = AsyncMock(return_value=None) foo = 'foo' @@ -466,7 +464,7 @@ def test_websocket_read_write_wait_fail(self): def test_websocket_upgrade_with_large_packet(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = True s.queue.join = AsyncMock(return_value=None) probe = 'probe' @@ -483,7 +481,7 @@ def test_websocket_upgrade_with_large_packet(self): def test_websocket_ignore_invalid_packet(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.connected = False s.queue.join = AsyncMock(return_value=None) foo = 'foo' @@ -517,14 +515,14 @@ def test_websocket_ignore_invalid_packet(self): def test_send_after_close(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') _run(s.close(wait=False)) with pytest.raises(exceptions.SocketIsClosedError): _run(s.send(packet.Packet(packet.NOOP))) def test_close_after_close(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') _run(s.close(wait=False)) assert s.closed assert mock_server._trigger_event.mock.call_count == 1 @@ -536,7 +534,7 @@ def test_close_after_close(self): def test_close_and_wait(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.queue = mock.MagicMock() s.queue.put = AsyncMock() s.queue.join = AsyncMock() @@ -545,7 +543,7 @@ def test_close_and_wait(self): def test_close_without_wait(self): mock_server = self._get_mock_server() - s = asyncio_socket.AsyncSocket(mock_server, 'sid') + s = async_socket.AsyncSocket(mock_server, 'sid') s.queue = mock.MagicMock() s.queue.put = AsyncMock() s.queue.join = AsyncMock() diff --git a/tests/asyncio/test_async_tornado.py b/tests/async/test_tornado.py similarity index 96% rename from tests/asyncio/test_async_tornado.py rename to tests/async/test_tornado.py index e6a8a3c1..f2ea3c00 100644 --- a/tests/asyncio/test_async_tornado.py +++ b/tests/async/test_tornado.py @@ -1,5 +1,4 @@ import asyncio -import sys import unittest from unittest import mock @@ -16,7 +15,6 @@ def _run(coro): return asyncio.get_event_loop().run_until_complete(coro) -@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+') class TornadoTests(unittest.TestCase): def test_get_tornado_handler(self): mock_server = mock.MagicMock() diff --git a/tests/common/test_server.py b/tests/common/test_server.py index 5ec0377f..adddea1b 100644 --- a/tests/common/test_server.py +++ b/tests/common/test_server.py @@ -217,7 +217,6 @@ def test_async_mode_gevent(self, import_module): del sys.modules['geventwebsocket'] del sys.modules['engineio.async_drivers.gevent'] - @unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+') @mock.patch('importlib.import_module', side_effect=_mock_import) def test_async_mode_aiohttp(self, import_module): sys.modules['aiohttp'] = mock.MagicMock()