python 3 migration

This commit is contained in:
manuel83
2019-08-19 22:51:08 +02:00
parent 63f9f5468b
commit 996bc54768
1363 changed files with 351010 additions and 62 deletions
@@ -0,0 +1,57 @@
import sys
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
if sys.version_info >= (3, 5):
from engineio.async_drivers import aiohttp as async_aiohttp
@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+')
class AiohttpTests(unittest.TestCase):
def test_create_route(self):
app = mock.MagicMock()
mock_server = mock.MagicMock()
async_aiohttp.create_route(app, mock_server, '/foo')
app.router.add_get.assert_any_call('/foo', mock_server.handle_request)
app.router.add_post.assert_any_call('/foo', mock_server.handle_request)
def test_translate_request(self):
request = mock.MagicMock()
request._message.method = 'PUT'
request._message.path = '/foo/bar?baz=1'
request._message.version = (1, 1)
request._message.headers = {'a': 'b', 'c-c': 'd', 'c_c': 'e',
'content-type': 'application/json',
'content-length': 123}
request._payload = b'hello world'
environ = async_aiohttp.translate_request(request)
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': 'baz=1',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': 123,
'HTTP_A': 'b',
# 'HTTP_C_C': 'd,e',
'RAW_URI': '/foo/bar?baz=1',
'SERVER_PROTOCOL': 'HTTP/1.1',
'wsgi.input': b'hello world',
'aiohttp.request': request,
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
self.assertTrue(
environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
# @mock.patch('async_aiohttp.aiohttp.web.Response')
def test_make_response(self):
rv = async_aiohttp.make_response('202 ACCEPTED', {'foo': 'bar'},
b'payload', {})
self.assertEqual(rv.status, 202)
self.assertEqual(rv.headers['foo'], 'bar')
self.assertEqual(rv.body, b'payload')
@@ -0,0 +1,279 @@
import os
import sys
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
if sys.version_info >= (3, 5):
import asyncio
from engineio.async_drivers import asgi as async_asgi
def AsyncMock(*args, **kwargs):
"""Return a mock asynchronous function."""
m = mock.MagicMock(*args, **kwargs)
async def mock_coro(*args, **kwargs):
return m(*args, **kwargs)
mock_coro.mock = m
return mock_coro
def _run(coro):
"""Run the given coroutine."""
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(
'eio', 'other_app', static_files='static_files',
engineio_path='/foo')
self.assertEqual(app.engineio_server, 'eio')
self.assertEqual(app.other_asgi_app, 'other_app')
self.assertEqual(app.static_files, 'static_files')
self.assertEqual(app.engineio_path, 'foo'),
def test_engineio_routing(self):
mock_server = mock.MagicMock()
mock_server.handle_request = AsyncMock()
app = async_asgi.ASGIApp(mock_server)
scope = {'type': 'http', 'path': '/engine.io/'}
_run(app(scope, 'receive', 'send'))
mock_server.handle_request.mock.assert_called_once_with(
scope, 'receive', 'send')
def test_other_app_routing(self):
other_app = AsyncMock()
app = async_asgi.ASGIApp('eio', other_app)
scope = {'type': 'http', 'path': '/foo'}
_run(app(scope, 'receive', 'send'))
other_app.mock.assert_called_once_with(scope, 'receive', 'send')
def test_static_file_routing(self):
root_dir = os.path.dirname(__file__)
app = async_asgi.ASGIApp('eio', static_files={
'/': root_dir + '/index.html',
'/foo': {'content_type': 'text/plain',
'filename': root_dir + '/index.html'},
'/static': root_dir,
'/static/test/': root_dir + '/',
})
def check_path(path, status_code, content_type, body):
scope = {'type': 'http', 'path': path}
receive = AsyncMock(return_value={'type': 'http.request'})
send = AsyncMock()
_run(app(scope, receive, send))
send.mock.assert_any_call({
'type': 'http.response.start',
'status': status_code,
'headers': [(b'Content-Type', content_type.encode('utf-8'))]})
send.mock.assert_any_call({
'type': 'http.response.body',
'body': body.encode('utf-8')})
check_path('/', 200, 'text/html', '<html></html>\n')
check_path('/foo', 200, 'text/plain', '<html></html>\n')
check_path('/static/index.html', 200, 'text/html', '<html></html>\n')
check_path('/static/foo.bar', 404, 'text/plain', 'Not Found')
check_path('/static/test/index.html', 200, 'text/html',
'<html></html>\n')
check_path('/bar/foo', 404, 'text/plain', 'Not Found')
check_path('', 404, 'text/plain', 'Not Found')
app.static_files[''] = 'index.html'
check_path('/static/test/', 200, 'text/html',
'<html></html>\n')
app.static_files[''] = {'filename': 'index.html'}
check_path('/static/test/', 200, 'text/html',
'<html></html>\n')
app.static_files[''] = {'filename': 'index.html',
'content_type': 'image/gif'}
check_path('/static/test/', 200, 'image/gif',
'<html></html>\n')
app.static_files[''] = {'filename': 'test.gif'}
check_path('/static/test/', 404, 'text/plain', 'Not Found')
app.static_files = {}
check_path('/static/test/index.html', 404, 'text/plain', 'Not Found')
def test_lifespan_startup(self):
app = async_asgi.ASGIApp('eio')
scope = {'type': 'lifespan'}
receive = AsyncMock(return_value={'type': 'lifespan.startup'})
send = AsyncMock()
_run(app(scope, receive, send))
send.mock.assert_called_once_with(
{'type': 'lifespan.startup.complete'})
def test_lifespan_shutdown(self):
app = async_asgi.ASGIApp('eio')
scope = {'type': 'lifespan'}
receive = AsyncMock(return_value={'type': 'lifespan.shutdown'})
send = AsyncMock()
_run(app(scope, receive, send))
send.mock.assert_called_once_with(
{'type': 'lifespan.shutdown.complete'})
def test_lifespan_invalid(self):
app = async_asgi.ASGIApp('eio')
scope = {'type': 'lifespan'}
receive = AsyncMock(return_value={'type': 'lifespan.foo'})
send = AsyncMock()
_run(app(scope, receive, send))
send.mock.assert_not_called()
def test_not_found(self):
app = async_asgi.ASGIApp('eio')
scope = {'type': 'http', 'path': '/foo'}
receive = AsyncMock(return_value={'type': 'http.request'})
send = AsyncMock()
_run(app(scope, receive, send))
send.mock.assert_any_call(
{'type': 'http.response.start', 'status': 404,
'headers': [(b'Content-Type', b'text/plain')]})
send.mock.assert_any_call({'type': 'http.response.body',
'body': b'Not Found'})
def test_translate_request(self):
receive = AsyncMock(return_value={'type': 'http.request',
'body': b'hello world'})
send = AsyncMock()
environ = _run(async_asgi.translate_request({
'type': 'http',
'method': 'PUT',
'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
(b'content-type', b'application/json'),
(b'content-length', b'123')],
'path': '/foo/bar',
'query_string': b'baz=1'}, receive, send))
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': 'baz=1',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': '123',
'HTTP_A': 'b',
# 'HTTP_C_C': 'd,e',
'RAW_URI': '/foo/bar?baz=1',
'SERVER_PROTOCOL': 'HTTP/1.1',
'asgi.receive': receive,
'asgi.send': send
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
self.assertTrue(
environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
body = _run(environ['wsgi.input'].read())
self.assertEqual(body, b'hello world')
def test_translate_request_no_query_string(self):
receive = AsyncMock(return_value={'type': 'http.request',
'body': b'hello world'})
send = AsyncMock()
environ = _run(async_asgi.translate_request({
'type': 'http',
'method': 'PUT',
'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
(b'content-type', b'application/json'),
(b'content-length', b'123')],
'path': '/foo/bar'}, receive, send))
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': '',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': '123',
'HTTP_A': 'b',
# 'HTTP_C_C': 'd,e',
'RAW_URI': '/foo/bar',
'SERVER_PROTOCOL': 'HTTP/1.1',
'asgi.receive': receive,
'asgi.send': send
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
self.assertTrue(
environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
body = _run(environ['wsgi.input'].read())
self.assertEqual(body, b'hello world')
def test_translate_request_with_large_body(self):
receive = AsyncMock(side_effect=[
{'type': 'http.request', 'body': b'hello ', 'more_body': True},
{'type': 'http.request', 'body': b'world', 'more_body': True},
{'type': 'foo.bar'}, # should stop parsing here
{'type': 'http.request', 'body': b'!!!'},
])
send = AsyncMock()
environ = _run(async_asgi.translate_request({
'type': 'http',
'method': 'PUT',
'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
(b'content-type', b'application/json'),
(b'content-length', b'123')],
'path': '/foo/bar',
'query_string': b'baz=1'}, receive, send))
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': 'baz=1',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': '123',
'HTTP_A': 'b',
# 'HTTP_C_C': 'd,e',
'RAW_URI': '/foo/bar?baz=1',
'SERVER_PROTOCOL': 'HTTP/1.1',
'asgi.receive': receive,
'asgi.send': send
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
self.assertTrue(
environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
body = _run(environ['wsgi.input'].read())
self.assertEqual(body, b'hello world')
def test_translate_websocket_request(self):
receive = AsyncMock(return_value={'type': 'websocket.connect'})
send = AsyncMock()
_run(async_asgi.translate_request({
'type': 'websocket',
'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
(b'content-type', b'application/json'),
(b'content-length', b'123')],
'path': '/foo/bar',
'query_string': b'baz=1'}, receive, send))
send.mock.assert_called_once_with({'type': 'websocket.accept'})
def test_translate_unknown_request(self):
receive = AsyncMock(return_value={'type': 'http.foo'})
send = AsyncMock()
environ = _run(async_asgi.translate_request({
'type': 'http',
'path': '/foo/bar',
'query_string': b'baz=1'}, receive, send))
self.assertEqual(environ, {})
# @mock.patch('async_aiohttp.aiohttp.web.Response')
def test_make_response(self):
environ = {
'asgi.send': AsyncMock()
}
_run(async_asgi.make_response('202 ACCEPTED', [('foo', 'bar')],
b'payload', environ))
environ['asgi.send'].mock.assert_any_call(
{'type': 'http.response.start', 'status': 202,
'headers': [(b'foo', b'bar')]})
environ['asgi.send'].mock.assert_any_call(
{'type': 'http.response.body', 'body': b'payload'})
@@ -0,0 +1,76 @@
try:
import asyncio
except ImportError:
pass
import sys
import unittest
import six
try:
import tornado.web
except ImportError:
pass
if six.PY3:
from unittest import mock
else:
import mock
if sys.version_info >= (3, 5):
from engineio.async_drivers import tornado as async_tornado
def _run(coro):
"""Run the given coroutine."""
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()
handler = async_tornado.get_tornado_handler(mock_server)
self.assertTrue(issubclass(handler,
tornado.websocket.WebSocketHandler))
def test_translate_request(self):
mock_handler = mock.MagicMock()
mock_handler.request.method = 'PUT'
mock_handler.request.path = '/foo/bar'
mock_handler.request.query = 'baz=1'
mock_handler.request.version = '1.1'
mock_handler.request.headers = {
'a': 'b',
'c': 'd',
'content-type': 'application/json',
'content-length': 123
}
mock_handler.request.body = b'hello world'
environ = async_tornado.translate_request(mock_handler)
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': 'baz=1',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': 123,
'HTTP_A': 'b',
'HTTP_C': 'd',
'RAW_URI': '/foo/bar?baz=1',
'SERVER_PROTOCOL': 'HTTP/1.1',
# 'wsgi.input': b'hello world',
'tornado.handler': mock_handler,
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
payload = _run(environ['wsgi.input'].read(1))
payload += _run(environ['wsgi.input'].read())
self.assertEqual(payload, b'hello world')
def test_make_response(self):
mock_handler = mock.MagicMock()
mock_environ = {'tornado.handler': mock_handler}
async_tornado.make_response('202 ACCEPTED', [('foo', 'bar')],
b'payload', mock_environ)
mock_handler.set_status.assert_called_once_with(202)
mock_handler.set_header.assert_called_once_with('foo', 'bar')
mock_handler.write.assert_called_once_with(b'payload')
mock_handler.finish.assert_called_once_with()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,919 @@
import asyncio
import gzip
import json
import logging
import sys
import unittest
import zlib
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio import asyncio_server
from engineio.async_drivers import aiohttp as async_aiohttp
from engineio import exceptions
from engineio import packet
from engineio import payload
def AsyncMock(*args, **kwargs):
"""Return a mock asynchronous function."""
m = mock.MagicMock(*args, **kwargs)
async def mock_coro(*args, **kwargs):
return m(*args, **kwargs)
mock_coro.mock = m
return mock_coro
def _run(coro):
"""Run the given coroutine."""
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': ''}):
a = mock.MagicMock()
a._async = {
'asyncio': True,
'create_route': mock.MagicMock(),
'translate_request': mock.MagicMock(),
'make_response': mock.MagicMock(),
'websocket': 'w',
}
a._async['translate_request'].return_value = environ
a._async['make_response'].return_value = 'response'
return a
def _get_mock_socket(self):
mock_socket = mock.MagicMock()
mock_socket.connected = False
mock_socket.closed = False
mock_socket.closing = False
mock_socket.upgraded = False
mock_socket.send = AsyncMock()
mock_socket.handle_get_request = AsyncMock()
mock_socket.handle_post_request = AsyncMock()
mock_socket.check_ping_timeout = AsyncMock()
mock_socket.close = AsyncMock()
mock_socket.session = {}
return mock_socket
@classmethod
def setUpClass(cls):
asyncio_server.AsyncServer._default_monitor_clients = False
@classmethod
def tearDownClass(cls):
asyncio_server.AsyncServer._default_monitor_clients = True
def setUp(self):
logging.getLogger('engineio').setLevel(logging.NOTSET)
def tearDown(self):
# restore JSON encoder, in case a test changed it
packet.Packet.json = json
def test_is_asyncio_based(self):
s = asyncio_server.AsyncServer()
self.assertEqual(s.is_asyncio_based(), True)
def test_async_modes(self):
s = asyncio_server.AsyncServer()
self.assertEqual(s.async_modes(), ['aiohttp', 'sanic', 'tornado',
'asgi'])
def test_async_mode_aiohttp(self):
s = asyncio_server.AsyncServer(async_mode='aiohttp')
self.assertEqual(s.async_mode, 'aiohttp')
self.assertEqual(s._async['asyncio'], True)
self.assertEqual(s._async['create_route'], async_aiohttp.create_route)
self.assertEqual(s._async['translate_request'],
async_aiohttp.translate_request)
self.assertEqual(s._async['make_response'],
async_aiohttp.make_response)
self.assertEqual(s._async['websocket'].__name__, 'WebSocket')
@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()
self.assertEqual(s.async_mode, 'aiohttp')
def test_async_modes_wsgi(self):
self.assertRaises(ValueError, asyncio_server.AsyncServer,
async_mode='eventlet')
self.assertRaises(ValueError, asyncio_server.AsyncServer,
async_mode='gevent')
self.assertRaises(ValueError, asyncio_server.AsyncServer,
async_mode='gevent_uwsgi')
self.assertRaises(ValueError, asyncio_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.attach('app', engineio_path='abc')
a._async['create_route'].assert_called_with('app', s, '/abc/')
s.attach('app', engineio_path='/def/')
a._async['create_route'].assert_called_with('app', s, '/def/')
s.attach('app', engineio_path='/ghi')
a._async['create_route'].assert_called_with('app', s, '/ghi/')
s.attach('app', engineio_path='jkl/')
a._async['create_route'].assert_called_with('app', s, '/jkl/')
def test_session(self):
s = asyncio_server.AsyncServer()
s.sockets['foo'] = self._get_mock_socket()
async def _func():
async with s.session('foo') as session:
await s.sleep(0)
session['username'] = 'bar'
self.assertEqual(await s.get_session('foo'), {'username': 'bar'})
_run(_func())
def test_disconnect(self):
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
_run(s.disconnect('foo'))
self.assertEqual(mock_socket.close.mock.call_count, 1)
mock_socket.close.mock.assert_called_once_with()
self.assertNotIn('foo', s.sockets)
def test_disconnect_all(self):
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_foo = self._get_mock_socket()
s.sockets['bar'] = mock_bar = self._get_mock_socket()
_run(s.disconnect())
self.assertEqual(mock_foo.close.mock.call_count, 1)
self.assertEqual(mock_bar.close.mock.call_count, 1)
mock_foo.close.mock.assert_called_once_with()
mock_bar.close.mock.assert_called_once_with()
self.assertNotIn('foo', s.sockets)
self.assertNotIn('bar', s.sockets)
@mock.patch('importlib.import_module')
def test_jsonp_not_supported(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'j=abc'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
response = _run(s.handle_request('request'))
self.assertEqual(response, 'response')
a._async['translate_request'].assert_called_once_with('request')
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('importlib.import_module')
def test_jsonp_index(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'j=233'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
response = _run(s.handle_request('request'))
self.assertEqual(response, 'response')
a._async['translate_request'].assert_called_once_with('request')
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
print('***', a._async['make_response'].call_args[0][2])
self.assertTrue(a._async['make_response'].call_args[0][2].startswith(
b'___eio[233]("'))
self.assertTrue(a._async['make_response'].call_args[0][2].endswith(
b'");'))
@mock.patch('importlib.import_module')
def test_connect(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 1)
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
a._async['make_response'].call_args[0][1])
packets = payload.Payload(
encoded_payload=a._async['make_response'].call_args[0][2]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.OPEN)
self.assertIn('upgrades', packets[0].data)
self.assertEqual(packets[0].data['upgrades'], ['websocket'])
self.assertIn('sid', packets[0].data)
@mock.patch('importlib.import_module')
def test_connect_async_request_response_handlers(self, import_module):
a = self.get_async_mock()
a._async['translate_request'] = AsyncMock(
return_value=a._async['translate_request'].return_value)
a._async['make_response'] = AsyncMock(
return_value=a._async['make_response'].return_value)
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 1)
self.assertEqual(a._async['make_response'].mock.call_count, 1)
self.assertEqual(a._async['make_response'].mock.call_args[0][0],
'200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
a._async['make_response'].mock.call_args[0][1])
packets = payload.Payload(encoded_payload=a._async[
'make_response'].mock.call_args[0][2]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.OPEN)
self.assertIn('upgrades', packets[0].data)
self.assertEqual(packets[0].data['upgrades'], ['websocket'])
self.assertIn('sid', packets[0].data)
@mock.patch('importlib.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)
_run(s.handle_request('request'))
packets = payload.Payload(
encoded_payload=a._async['make_response'].call_args[0][2]).packets
self.assertEqual(packets[0].data['upgrades'], [])
@mock.patch('importlib.import_module')
def test_connect_b64_with_1(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'b64=1'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'text/plain; charset=UTF-8'),
a._async['make_response'].call_args[0][1])
_run(s.send('1', b'\x00\x01\x02', binary=True))
a._async['translate_request'].return_value = {
'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=1'}
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][2],
b'6:b4AAEC')
@mock.patch('importlib.import_module')
def test_connect_b64_with_true(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'b64=true'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'text/plain; charset=UTF-8'),
a._async['make_response'].call_args[0][1])
_run(s.send('1', b'\x00\x01\x02', binary=True))
a._async['translate_request'].return_value = {
'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=true'}
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][2],
b'6:b4AAEC')
@mock.patch('importlib.import_module')
def test_connect_b64_with_0(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'b64=0'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
a._async['make_response'].call_args[0][1])
_run(s.send('1', b'\x00\x01\x02', binary=True))
a._async['translate_request'].return_value = {
'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=0'}
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][2],
b'\x01\x04\xff\x04\x00\x01\x02')
@mock.patch('importlib.import_module')
def test_connect_b64_with_false(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'b64=false'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
a._async['make_response'].call_args[0][1])
_run(s.send('1', b'\x00\x01\x02', binary=True))
a._async['translate_request'].return_value = {
'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=false'}
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][2],
b'\x01\x04\xff\x04\x00\x01\x02')
@mock.patch('importlib.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)
_run(s.handle_request('request'))
packets = payload.Payload(
encoded_payload=a._async['make_response'].call_args[0][2]).packets
self.assertEqual(packets[0].data['pingTimeout'], 123000)
self.assertEqual(packets[0].data['pingInterval'], 456000)
@mock.patch('engineio.asyncio_socket.AsyncSocket')
@mock.patch('importlib.import_module')
def test_connect_bad_poll(self, import_module, AsyncSocket):
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()
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('engineio.asyncio_socket.AsyncSocket')
@mock.patch('importlib.import_module')
def test_connect_transport_websocket(self, import_module, AsyncSocket):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=websocket'})
import_module.side_effect = [a]
AsyncSocket.return_value = self._get_mock_socket()
s = asyncio_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
_run(s.handle_request('request'))
self.assertEqual(
s.sockets['123'].send.mock.call_args[0][0].packet_type,
packet.OPEN)
@mock.patch('engineio.asyncio_socket.AsyncSocket')
@mock.patch('importlib.import_module')
def test_connect_transport_websocket_closed(self, import_module,
AsyncSocket):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=websocket'})
import_module.side_effect = [a]
AsyncSocket.return_value = self._get_mock_socket()
s = asyncio_server.AsyncServer()
s._generate_id = mock.MagicMock(return_value='123')
# this mock handler just closes the socket, as it would happen on a
# real websocket exchange
async def mock_handle(environ):
s.sockets['123'].closed = True
AsyncSocket().handle_get_request = mock_handle
_run(s.handle_request('request'))
self.assertNotIn('123', s.sockets) # socket should close on its own
@mock.patch('importlib.import_module')
def test_connect_transport_invalid(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('importlib.import_module')
def test_connect_cors_headers(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', '*'), headers)
self.assertIn(('Access-Control-Allow-Credentials', 'true'), headers)
@mock.patch('importlib.import_module')
def test_connect_cors_allowed_origin(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'b'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(cors_allowed_origins=['a', 'b'])
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', 'b'), headers)
@mock.patch('importlib.import_module')
def test_connect_cors_not_allowed_origin(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'c'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(cors_allowed_origins=['a', 'b'])
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Origin', 'c'), headers)
self.assertNotIn(('Access-Control-Allow-Origin', '*'), headers)
@mock.patch('importlib.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)
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Credentials', 'true'), headers)
@mock.patch('importlib.import_module')
def test_connect_cors_options(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'OPTIONS',
'QUERY_STRING': ''})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(cors_credentials=False)
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Access-Control-Allow-Methods',
'OPTIONS, GET, POST'), headers)
@mock.patch('importlib.import_module')
def test_connect_event(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s._generate_id = mock.MagicMock(return_value='123')
def mock_connect(sid, environ):
return True
s.on('connect', handler=mock_connect)
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 1)
@mock.patch('importlib.import_module')
def test_connect_event_rejects(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s._generate_id = mock.MagicMock(return_value='123')
def mock_connect(sid, environ):
return False
s.on('connect')(mock_connect)
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 0)
self.assertEqual(a._async['make_response'].call_args[0][0],
'401 UNAUTHORIZED')
@mock.patch('importlib.import_module')
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()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 0)
self.assertEqual(a._async['make_response'].call_args[0][0],
'405 METHOD NOT FOUND')
@mock.patch('importlib.import_module')
def test_get_request_with_bad_sid(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 0)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('importlib.import_module')
def test_post_request_with_bad_sid(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'POST',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 0)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('importlib.import_module')
def test_send(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
_run(s.send('foo', 'hello'))
self.assertEqual(mock_socket.send.mock.call_count, 1)
self.assertEqual(mock_socket.send.mock.call_args[0][0].packet_type,
packet.MESSAGE)
self.assertEqual(mock_socket.send.mock.call_args[0][0].data, 'hello')
@mock.patch('importlib.import_module')
def test_send_unknown_socket(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
# just ensure no exceptions are raised
_run(s.send('foo', 'hello'))
@mock.patch('importlib.import_module')
def test_get_request(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
packets = payload.Payload(
encoded_payload=a._async['make_response'].call_args[0][2]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.MESSAGE)
@mock.patch('importlib.import_module')
def test_get_request_custom_response(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_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'))
self.assertEqual(r, 'resp')
@mock.patch('importlib.import_module')
def test_get_request_closes_socket(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
async def mock_get_request(*args, **kwargs):
mock_socket.closed = True
return 'resp'
mock_socket.handle_get_request = mock_get_request
r = _run(s.handle_request('request'))
self.assertEqual(r, 'resp')
self.assertNotIn('foo', s.sockets)
@mock.patch('importlib.import_module')
def test_get_request_error(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
async def mock_get_request(*args, **kwargs):
raise exceptions.QueueEmpty()
mock_socket.handle_get_request = mock_get_request
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
self.assertEqual(len(s.sockets), 0)
@mock.patch('importlib.import_module')
def test_post_request(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'POST',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = self._get_mock_socket()
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
@mock.patch('importlib.import_module')
def test_post_request_error(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'POST',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
async def mock_post_request(*args, **kwargs):
raise exceptions.ContentTooLongError()
mock_socket.handle_post_request = mock_post_request
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@staticmethod
def _gzip_decompress(b):
bytesio = six.BytesIO(b)
with gzip.GzipFile(fileobj=bytesio, mode='r') as gz:
return gz.read()
@mock.patch('importlib.import_module')
def test_gzip_compression(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip,deflate'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Content-Encoding', 'gzip'), headers)
self._gzip_decompress(a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_deflate_compression(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'deflate;q=1,gzip'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Content-Encoding', 'deflate'), headers)
zlib.decompress(a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_gzip_compression_threshold(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
for header, value in headers:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress,
a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_compression_disabled(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(http_compression=False,
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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
for header, value in headers:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress,
a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_compression_unknown(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'rar'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
for header, value in headers:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress,
a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_compression_no_encoding(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': ''})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
for header, value in headers:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress,
a._async['make_response'].call_args[0][2])
@mock.patch('importlib.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._generate_id = mock.MagicMock(return_value='123')
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Set-Cookie', 'sid=123'), headers)
@mock.patch('importlib.import_module')
def test_no_cookie(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_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]
for header, value in headers:
self.assertNotEqual(header, 'Set-Cookie')
def test_logger(self):
s = asyncio_server.AsyncServer(logger=False)
self.assertEqual(s.logger.getEffectiveLevel(), logging.ERROR)
s.logger.setLevel(logging.NOTSET)
s = asyncio_server.AsyncServer(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.INFO)
s.logger.setLevel(logging.WARNING)
s = asyncio_server.AsyncServer(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.WARNING)
s.logger.setLevel(logging.NOTSET)
my_logger = logging.Logger('foo')
s = asyncio_server.AsyncServer(logger=my_logger)
self.assertEqual(s.logger, my_logger)
def test_custom_json(self):
# Warning: this test cannot run in parallel with other tests, as it
# changes the JSON encoding/decoding functions
class CustomJSON(object):
@staticmethod
def dumps(*args, **kwargs):
return '*** encoded ***'
@staticmethod
def loads(*args, **kwargs):
return '+++ decoded +++'
asyncio_server.AsyncServer(json=CustomJSON)
pkt = packet.Packet(packet.MESSAGE, data={'foo': 'bar'})
self.assertEqual(pkt.encode(), b'4*** encoded ***')
pkt2 = packet.Packet(encoded_packet=pkt.encode())
self.assertEqual(pkt2.data, '+++ decoded +++')
# restore the default JSON module
packet.Packet.json = json
def test_background_tasks(self):
r = []
async def foo(arg):
r.append(arg)
s = asyncio_server.AsyncServer()
s.start_background_task(foo, 'bar')
pending = asyncio.Task.all_tasks()
asyncio.get_event_loop().run_until_complete(asyncio.wait(pending))
self.assertEqual(r, ['bar'])
def test_sleep(self):
s = asyncio_server.AsyncServer()
_run(s.sleep(0))
def test_trigger_event_function(self):
result = []
def foo_handler(arg):
result.append('ok')
result.append(arg)
s = asyncio_server.AsyncServer()
s.on('message', handler=foo_handler)
_run(s._trigger_event('message', 'bar'))
self.assertEqual(result, ['ok', 'bar'])
def test_trigger_event_coroutine(self):
result = []
async def foo_handler(arg):
result.append('ok')
result.append(arg)
s = asyncio_server.AsyncServer()
s.on('message', handler=foo_handler)
_run(s._trigger_event('message', 'bar'))
self.assertEqual(result, ['ok', 'bar'])
def test_trigger_event_function_error(self):
def connect_handler(arg):
return 1 / 0
def foo_handler(arg):
return 1 / 0
s = asyncio_server.AsyncServer()
s.on('connect', handler=connect_handler)
s.on('message', handler=foo_handler)
self.assertFalse(_run(s._trigger_event('connect', '123')))
self.assertIsNone(_run(s._trigger_event('message', 'bar')))
def test_trigger_event_coroutine_error(self):
async def connect_handler(arg):
return 1 / 0
async def foo_handler(arg):
return 1 / 0
s = asyncio_server.AsyncServer()
s.on('connect', handler=connect_handler)
s.on('message', handler=foo_handler)
self.assertFalse(_run(s._trigger_event('connect', '123')))
self.assertIsNone(_run(s._trigger_event('message', 'bar')))
def test_trigger_event_function_async(self):
result = []
def foo_handler(arg):
result.append('ok')
result.append(arg)
s = asyncio_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)
self.assertEqual(result, ['ok', 'bar'])
def test_trigger_event_coroutine_async(self):
result = []
async def foo_handler(arg):
result.append('ok')
result.append(arg)
s = asyncio_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)
self.assertEqual(result, ['ok', 'bar'])
def test_trigger_event_function_async_error(self):
result = []
def foo_handler(arg):
result.append(arg)
return 1 / 0
s = asyncio_server.AsyncServer()
s.on('message', handler=foo_handler)
fut = _run(s._trigger_event('message', 'bar', run_async=True))
self.assertRaises(
ZeroDivisionError, asyncio.get_event_loop().run_until_complete,
fut)
self.assertEqual(result, ['bar'])
def test_trigger_event_coroutine_async_error(self):
result = []
async def foo_handler(arg):
result.append(arg)
return 1 / 0
s = asyncio_server.AsyncServer()
s.on('message', handler=foo_handler)
fut = _run(s._trigger_event('message', 'bar', run_async=True))
self.assertRaises(
ZeroDivisionError, asyncio.get_event_loop().run_until_complete,
fut)
self.assertEqual(result, ['bar'])
def test_create_queue(self):
s = asyncio_server.AsyncServer()
q = s.create_queue()
empty = s.get_queue_empty_exception()
self.assertRaises(empty, q.get_nowait)
def test_create_event(self):
s = asyncio_server.AsyncServer()
e = s.create_event()
self.assertFalse(e.is_set())
e.set()
self.assertTrue(e.is_set())
@mock.patch('importlib.import_module')
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._service_task = AsyncMock()
_run(s.handle_request('request'))
s._service_task.mock.assert_called_once_with()
@@ -0,0 +1,441 @@
import asyncio
import sys
import time
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio import asyncio_socket
from engineio import exceptions
from engineio import packet
from engineio import payload
def AsyncMock(*args, **kwargs):
"""Return a mock asynchronous function."""
m = mock.MagicMock(*args, **kwargs)
async def mock_coro(*args, **kwargs):
return m(*args, **kwargs)
mock_coro.mock = m
return mock_coro
def _run(coro):
"""Run the given coroutine."""
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()
mock_input.read = AsyncMock()
mock_input.read.mock.return_value = payload
return mock_input
def _get_mock_server(self):
mock_server = mock.Mock()
mock_server.ping_timeout = 0.2
mock_server.ping_interval = 0.2
mock_server.async_handlers = False
mock_server._async = {'asyncio': True,
'create_route': mock.MagicMock(),
'translate_request': mock.MagicMock(),
'make_response': mock.MagicMock(),
'websocket': 'w'}
mock_server._async['translate_request'].return_value = 'request'
mock_server._async['make_response'].return_value = 'response'
mock_server._trigger_event = AsyncMock()
def create_queue(*args, **kwargs):
queue = asyncio.Queue(*args, **kwargs)
queue.Empty = asyncio.QueueEmpty
return queue
mock_server.create_queue = create_queue
return mock_server
def test_create(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
self.assertEqual(s.server, mock_server)
self.assertEqual(s.sid, 'sid')
self.assertFalse(s.upgraded)
self.assertFalse(s.closed)
self.assertTrue(hasattr(s.queue, 'get'))
self.assertTrue(hasattr(s.queue, 'put'))
self.assertTrue(hasattr(s.queue, 'task_done'))
self.assertTrue(hasattr(s.queue, 'join'))
def test_empty_poll(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
self.assertRaises(exceptions.QueueEmpty, _run, s.poll())
def test_poll(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
_run(s.send(pkt1))
_run(s.send(pkt2))
self.assertEqual(_run(s.poll()), [pkt1, pkt2])
def test_poll_none(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
_run(s.queue.put(None))
self.assertEqual(_run(s.poll()), [])
def test_ping_pong(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
_run(s.receive(packet.Packet(packet.PING, data='abc')))
r = _run(s.poll())
self.assertEqual(len(r), 1)
self.assertTrue(r[0].encode(), b'3abc')
def test_message_sync_handler(self):
mock_server = self._get_mock_server()
s = asyncio_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)
def test_message_async_handler(self):
mock_server = self._get_mock_server()
s = asyncio_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(
'message', 'sid', 'foo', run_async=True)
def test_invalid_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
self.assertRaises(exceptions.UnknownPacketError, _run,
s.receive(packet.Packet(packet.OPEN)))
def test_timeout(self):
mock_server = self._get_mock_server()
mock_server.ping_interval = -6
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.last_ping = time.time() - 1
s.close = AsyncMock()
_run(s.send('packet'))
s.close.mock.assert_called_once_with(wait=False, abort=False)
def test_polling_read(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
_run(s.send(pkt1))
_run(s.send(pkt2))
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
packets = _run(s.handle_get_request(environ))
self.assertEqual(packets, [pkt1, pkt2])
def test_polling_read_error(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
self.assertRaises(exceptions.QueueEmpty, _run,
s.handle_get_request(environ))
def test_polling_write(self):
mock_server = self._get_mock_server()
mock_server.max_http_buffer_size = 1000
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
p = payload.Payload(packets=[pkt1, pkt2]).encode()
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
s.receive = AsyncMock()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo',
'CONTENT_LENGTH': len(p),
'wsgi.input': self._get_read_mock_coro(p)}
_run(s.handle_post_request(environ))
self.assertEqual(s.receive.mock.call_count, 2)
def test_polling_write_too_large(self):
mock_server = self._get_mock_server()
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
p = payload.Payload(packets=[pkt1, pkt2]).encode()
mock_server.max_http_buffer_size = len(p) - 1
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
s.receive = AsyncMock()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo',
'CONTENT_LENGTH': len(p),
'wsgi.input': self._get_read_mock_coro(p)}
self.assertRaises(exceptions.ContentTooLongError, _run,
s.handle_post_request(environ))
def test_upgrade_handshake(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
s._upgrade_websocket = AsyncMock()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_CONNECTION': 'Foo,Upgrade,Bar',
'HTTP_UPGRADE': 'websocket'}
_run(s.handle_get_request(environ))
s._upgrade_websocket.mock.assert_called_once_with(environ)
def test_upgrade(self):
mock_server = self._get_mock_server()
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.connected = True
environ = "foo"
_run(s._upgrade_websocket(environ))
mock_server._async['websocket'].assert_called_once_with(
s._websocket_handler)
mock_ws.mock.assert_called_once_with(environ)
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.connected = True
s.upgraded = True
environ = "foo"
self.assertRaises(IOError, _run, s._upgrade_websocket(environ))
def test_upgrade_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
_run(s.receive(packet.Packet(packet.UPGRADE)))
r = _run(s.poll())
self.assertEqual(len(r), 1)
self.assertEqual(r[0].encode(), packet.Packet(packet.NOOP).encode())
def test_upgrade_no_probe(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
ws = mock.MagicMock()
ws.wait = AsyncMock()
ws.wait.mock.return_value = packet.Packet(packet.NOOP).encode(
always_bytes=False)
_run(s._websocket_handler(ws))
self.assertFalse(s.upgraded)
def test_upgrade_no_upgrade_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
s.queue.join = AsyncMock(return_value=None)
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
probe = six.text_type('probe')
ws.wait.mock.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.NOOP).encode(always_bytes=False)]
_run(s._websocket_handler(ws))
ws.send.mock.assert_called_once_with(packet.Packet(
packet.PONG, data=probe).encode(always_bytes=False))
self.assertEqual(_run(s.queue.get()).packet_type, packet.NOOP)
self.assertFalse(s.upgraded)
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.connected = True
environ = "foo"
_run(s._upgrade_websocket(environ))
mock_server._bad_request.assert_called_once_with()
def test_close_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
s.close = AsyncMock()
_run(s.receive(packet.Packet(packet.CLOSE)))
s.close.mock.assert_called_once_with(wait=False, abort=True)
def test_websocket_read_write(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = False
s.queue.join = AsyncMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = AsyncMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], None])
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
_run(s._websocket_handler(ws))
self.assertTrue(s.connected)
self.assertTrue(s.upgraded)
self.assertEqual(mock_server._trigger_event.mock.call_count, 2)
mock_server._trigger_event.mock.assert_has_calls([
mock.call('message', 'sid', 'foo', run_async=False),
mock.call('disconnect', 'sid')])
ws.send.mock.assert_called_with('4bar')
def test_websocket_upgrade_read_write(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
s.queue.join = AsyncMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
probe = six.text_type('probe')
s.poll = AsyncMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE).encode(always_bytes=False),
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
_run(s._websocket_handler(ws))
self.assertTrue(s.upgraded)
self.assertEqual(mock_server._trigger_event.mock.call_count, 2)
mock_server._trigger_event.mock.assert_has_calls([
mock.call('message', 'sid', 'foo', run_async=False),
mock.call('disconnect', 'sid')])
ws.send.mock.assert_called_with('4bar')
def test_websocket_upgrade_with_payload(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
s.queue.join = AsyncMock(return_value=None)
probe = six.text_type('probe')
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE, data=b'2').encode(
always_bytes=False)]
_run(s._websocket_handler(ws))
self.assertTrue(s.upgraded)
def test_websocket_upgrade_with_backlog(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
s.queue.join = AsyncMock(return_value=None)
probe = six.text_type('probe')
foo = six.text_type('foo')
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE, data=b'2').encode(
always_bytes=False)]
s.upgrading = True
_run(s.send(packet.Packet(packet.MESSAGE, data=foo)))
_run(s._websocket_handler(ws))
self.assertTrue(s.upgraded)
self.assertFalse(s.upgrading)
self.assertEqual(s.packet_backlog, [])
ws.send.mock.assert_called_with('4foo')
def test_websocket_read_write_wait_fail(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = False
s.queue.join = AsyncMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = AsyncMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)],
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
RuntimeError]
ws.send.mock.side_effect = [None, RuntimeError]
_run(s._websocket_handler(ws))
self.assertEqual(s.closed, True)
def test_websocket_ignore_invalid_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = False
s.queue.join = AsyncMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = AsyncMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.OPEN).encode(always_bytes=False),
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
_run(s._websocket_handler(ws))
self.assertTrue(s.connected)
self.assertEqual(mock_server._trigger_event.mock.call_count, 2)
mock_server._trigger_event.mock.assert_has_calls([
mock.call('message', 'sid', foo, run_async=False),
mock.call('disconnect', 'sid')])
ws.send.mock.assert_called_with('4bar')
def test_send_after_close(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
_run(s.close(wait=False))
self.assertRaises(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')
_run(s.close(wait=False))
self.assertTrue(s.closed)
self.assertEqual(mock_server._trigger_event.mock.call_count, 1)
mock_server._trigger_event.mock.assert_called_once_with('disconnect',
'sid')
_run(s.close())
self.assertEqual(mock_server._trigger_event.mock.call_count, 1)
def test_close_and_wait(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.queue = mock.MagicMock()
s.queue.put = AsyncMock()
s.queue.join = AsyncMock()
_run(s.close(wait=True))
s.queue.join.mock.assert_called_once_with()
def test_close_without_wait(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.queue = mock.MagicMock()
s.queue.put = AsyncMock()
s.queue.join = AsyncMock()
_run(s.close(wait=False))
self.assertEqual(s.queue.join.mock.call_count, 0)
@@ -0,0 +1,30 @@
import logging
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio.async_drivers import eventlet as async_eventlet
class TestAsyncEventlet(unittest.TestCase):
def setUp(self):
logging.getLogger('engineio').setLevel(logging.NOTSET)
def test_bad_environ(self):
wsgi = async_eventlet.WebSocketWSGI(None)
environ = {'foo': 'bar'}
start_response = 'bar'
self.assertRaises(RuntimeError, wsgi, environ, start_response)
@mock.patch('engineio.async_drivers.eventlet._WebSocketWSGI.__call__',
return_value='data')
def test_wsgi_call(self, _WebSocketWSGI):
_WebSocketWSGI.__call__ = lambda e, s: 'data'
environ = {'eventlet.input': mock.MagicMock()}
start_response = 'bar'
wsgi = async_eventlet.WebSocketWSGI(None)
self.assertEqual(wsgi(environ, start_response), 'data')
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,162 @@
import os
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
import engineio
class TestWSGIApp(unittest.TestCase):
def test_wsgi_routing(self):
mock_wsgi_app = mock.MagicMock()
mock_eio_app = 'foo'
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app)
environ = {'PATH_INFO': '/foo'}
start_response = "foo"
m(environ, start_response)
mock_wsgi_app.assert_called_once_with(environ, start_response)
def test_eio_routing(self):
mock_wsgi_app = 'foo'
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app)
environ = {'PATH_INFO': '/engine.io/'}
start_response = "foo"
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_static_files(self):
root_dir = os.path.dirname(__file__)
m = engineio.WSGIApp('foo', None, static_files={
'/': root_dir + '/index.html',
'/foo': {'content_type': 'text/plain',
'filename': root_dir + '/index.html'},
'/static': root_dir,
'/static/test/': root_dir + '/',
})
def check_path(path, status_code, content_type, body):
environ = {'PATH_INFO': path}
start_response = mock.MagicMock()
r = m(environ, start_response)
self.assertEqual(r, [body.encode('utf-8')])
start_response.assert_called_once_with(
status_code, [('Content-Type', content_type)])
check_path('/', '200 OK', 'text/html', '<html></html>\n')
check_path('/foo', '200 OK', 'text/plain', '<html></html>\n')
check_path('/static/index.html', '200 OK', 'text/html',
'<html></html>\n')
check_path('/static/foo.bar', '404 Not Found', 'text/plain',
'Not Found')
check_path('/static/test/index.html', '200 OK', 'text/html',
'<html></html>\n')
check_path('/static/test/', '200 OK', 'text/html', '<html></html>\n')
check_path('/bar/foo', '404 Not Found', 'text/plain', 'Not Found')
check_path('', '404 Not Found', 'text/plain', 'Not Found')
m.static_files[''] = 'index.html'
check_path('/static/test/', '200 OK', 'text/html',
'<html></html>\n')
m.static_files[''] = {'filename': 'index.html'}
check_path('/static/test/', '200 OK', 'text/html',
'<html></html>\n')
m.static_files[''] = {'filename': 'index.html',
'content_type': 'image/gif'}
check_path('/static/test/', '200 OK', 'image/gif',
'<html></html>\n')
m.static_files[''] = {'filename': 'test.gif'}
check_path('/static/test/', '404 Not Found', 'text/plain',
'Not Found')
m.static_files = {}
check_path('/static/test/index.html', '404 Not Found', 'text/plain',
'Not Found')
def test_404(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app)
environ = {'PATH_INFO': '/foo/bar'}
start_response = mock.MagicMock()
r = m(environ, start_response)
self.assertEqual(r, [b'Not Found'])
start_response.assert_called_once_with(
"404 Not Found", [('Content-Type', 'text/plain')])
def test_custom_eio_path(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app, engineio_path='foo')
environ = {'PATH_INFO': '/engine.io/'}
start_response = mock.MagicMock()
r = m(environ, start_response)
self.assertEqual(r, [b'Not Found'])
start_response.assert_called_once_with(
"404 Not Found", [('Content-Type', 'text/plain')])
environ = {'PATH_INFO': '/foo/'}
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_custom_eio_path_slashes(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app,
engineio_path='/foo/')
environ = {'PATH_INFO': '/foo/'}
start_response = mock.MagicMock()
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_custom_eio_path_leading_slash(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app, engineio_path='/foo')
environ = {'PATH_INFO': '/foo/'}
start_response = mock.MagicMock()
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_custom_eio_path_trailing_slash(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app, engineio_path='foo/')
environ = {'PATH_INFO': '/foo/'}
start_response = mock.MagicMock()
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_gunicorn_socket(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app)
environ = {'gunicorn.socket': 123, 'PATH_INFO': '/foo/bar'}
start_response = mock.MagicMock()
m(environ, start_response)
self.assertIn('eventlet.input', environ)
self.assertEqual(environ['eventlet.input'].get_socket(), 123)
def test_legacy_middleware_class(self):
m = engineio.Middleware('eio', 'wsgi', 'eio_path')
self.assertEqual(m.engineio_app, 'eio')
self.assertEqual(m.wsgi_app, 'wsgi')
self.assertEqual(m.static_files, {})
self.assertEqual(m.engineio_path, 'eio_path')
@@ -0,0 +1,103 @@
import unittest
import six
from engineio import packet
class TestPacket(unittest.TestCase):
def test_encode_default_packet(self):
pkt = packet.Packet()
self.assertEqual(pkt.packet_type, packet.NOOP)
self.assertIsNone(pkt.data)
self.assertFalse(pkt.binary)
self.assertEqual(pkt.encode(), b'6')
def test_decode_default_packet(self):
pkt = packet.Packet(encoded_packet=b'6')
self.assertTrue(pkt.encode(), b'6')
def test_encode_text_packet(self):
data = six.text_type('text')
pkt = packet.Packet(packet.MESSAGE, data=data)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, data)
self.assertFalse(pkt.binary)
self.assertEqual(pkt.encode(), b'4text')
def test_decode_text_packet(self):
pkt = packet.Packet(encoded_packet=b'4text')
self.assertEqual(pkt.encode(), b'4text')
def test_encode_binary_packet(self):
pkt = packet.Packet(packet.MESSAGE, data=b'\x01\x02\x03',
binary=True)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, b'\x01\x02\x03')
self.assertTrue(pkt.binary)
self.assertEqual(pkt.encode(), b'\x04\x01\x02\x03')
def test_encode_binary_bytearray_packet(self):
pkt = packet.Packet(packet.MESSAGE, data=bytearray(b'\x01\x02\x03'),
binary=True)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, b'\x01\x02\x03')
self.assertTrue(pkt.binary)
self.assertEqual(pkt.encode(), b'\x04\x01\x02\x03')
def test_encode_binary_b64_packet(self):
pkt = packet.Packet(packet.MESSAGE, data=b'\x01\x02\x03\x04',
binary=True)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, b'\x01\x02\x03\x04')
self.assertTrue(pkt.binary)
self.assertEqual(pkt.encode(b64=True), b'b4AQIDBA==')
def test_encode_binary_packet_py3(self):
pkt = packet.Packet(packet.MESSAGE, data=b'\x01\x02\x03')
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, b'\x01\x02\x03')
self.assertTrue(pkt.binary)
self.assertEqual(pkt.encode(), b'\x04\x01\x02\x03')
def test_decode_binary_packet(self):
pkt = packet.Packet(encoded_packet=b'\x04\x01\x02\x03')
self.assertTrue(pkt.encode(), b'\x04\x01\x02\x03')
def test_decode_binary_bytearray_packet(self):
pkt = packet.Packet(encoded_packet=bytearray(b'\x04\x01\x02\x03'))
self.assertTrue(pkt.encode(), b'\x04\x01\x02\x03')
def test_decode_binary_b64_packet(self):
pkt = packet.Packet(encoded_packet=b'b4AAEC')
self.assertTrue(pkt.encode(), b'\x04\x01\x02\x03')
def test_encode_json_packet(self):
pkt = packet.Packet(packet.MESSAGE, data={'a': 123, 'b': '456'})
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, {'a': 123, 'b': '456'})
self.assertFalse(pkt.binary)
self.assertIn(pkt.encode(), [b'4{"a":123,"b":"456"}',
b'4{"b":"456","a":123}'])
def test_decode_json_packet(self):
pkt = packet.Packet(encoded_packet=b'4{"a":123,"b":"456"}')
self.assertIn(pkt.encode(), [b'4{"a":123,"b":"456"}',
b'4{"b":"456","a":123}'])
def test_encode_number_packet(self):
pkt = packet.Packet(packet.MESSAGE, data=123)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, 123)
self.assertFalse(pkt.binary)
self.assertEqual(pkt.encode(), b'4123')
def test_decode_number_packet(self):
pkt = packet.Packet(encoded_packet=b'4123')
self.assertEqual(pkt.packet_type, packet.MESSAGE)
# integer payloads are parsed as strings, see
# https://github.com/miguelgrinberg/python-engineio/issues/75
# for background on this decision
self.assertEqual(pkt.data, '123')
self.assertFalse(pkt.binary)
self.assertEqual(pkt.encode(), b'4123')
@@ -0,0 +1,70 @@
import unittest
import six
from engineio import packet
from engineio import payload
class TestPayload(unittest.TestCase):
def test_encode_empty_payload(self):
p = payload.Payload()
self.assertEqual(p.packets, [])
self.assertEqual(p.encode(), b'')
def test_decode_empty_payload(self):
p = payload.Payload(encoded_payload=b'')
self.assertEqual(p.encode(), b'')
def test_encode_payload_xhr2(self):
pkt = packet.Packet(packet.MESSAGE, data=six.text_type('abc'))
p = payload.Payload([pkt])
self.assertEqual(p.packets, [pkt])
self.assertEqual(p.encode(), b'\x00\x04\xff4abc')
def test_decode_payload_xhr2(self):
p = payload.Payload(encoded_payload=b'\x00\x04\xff4abc')
self.assertEqual(p.encode(), b'\x00\x04\xff4abc')
def test_encode_payload_xhr_text(self):
pkt = packet.Packet(packet.MESSAGE, data=six.text_type('abc'))
p = payload.Payload([pkt])
self.assertEqual(p.packets, [pkt])
self.assertEqual(p.encode(b64=True), b'4:4abc')
def test_decode_payload_xhr_text(self):
p = payload.Payload(encoded_payload=b'4:4abc')
self.assertEqual(p.encode(), b'\x00\x04\xff4abc')
def test_encode_payload_xhr_binary(self):
pkt = packet.Packet(packet.MESSAGE, data=b'\x00\x01\x02', binary=True)
p = payload.Payload([pkt])
self.assertEqual(p.packets, [pkt])
self.assertEqual(p.encode(b64=True), b'6:b4AAEC')
def test_decode_payload_xhr_binary(self):
p = payload.Payload(encoded_payload=b'6:b4AAEC')
self.assertEqual(p.encode(), b'\x01\x04\xff\x04\x00\x01\x02')
def test_encode_jsonp_payload(self):
pkt = packet.Packet(packet.MESSAGE, data=six.text_type('abc'))
p = payload.Payload([pkt])
self.assertEqual(p.packets, [pkt])
self.assertEqual(p.encode(jsonp_index=233),
b'___eio[233]("\x00\x04\xff4abc");')
self.assertEqual(p.encode(jsonp_index=233, b64=True),
b'___eio[233]("4:4abc");')
def test_decode_jsonp_payload(self):
p = payload.Payload(encoded_payload=b'd=4:4abc')
self.assertEqual(p.encode(), b'\x00\x04\xff4abc')
def test_decode_invalid_payload(self):
self.assertRaises(ValueError, payload.Payload,
encoded_payload=b'bad payload')
def test_decode_multi_payload(self):
p = payload.Payload(encoded_payload=b'4:4abc\x00\x04\xff4def')
self.assertEqual(len(p.packets), 2)
self.assertEqual(p.packets[0].data, 'abc')
self.assertEqual(p.packets[1].data, 'def')
@@ -0,0 +1,952 @@
import gzip
import importlib
import json
import logging
import sys
import time
import unittest
import zlib
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio import exceptions
from engineio import packet
from engineio import payload
from engineio import server
original_import_module = importlib.import_module
def _mock_import(module, *args, **kwargs):
if module.startswith('engineio.'):
return original_import_module(module, *args, **kwargs)
return module
class TestServer(unittest.TestCase):
_mock_async = mock.MagicMock()
_mock_async._async = {
'thread': 't',
'queue': 'q',
'queue_empty': RuntimeError,
'websocket': 'w',
}
def _get_mock_socket(self):
mock_socket = mock.MagicMock()
mock_socket.closed = False
mock_socket.closing = False
mock_socket.upgraded = False
mock_socket.session = {}
return mock_socket
@classmethod
def setUpClass(cls):
server.Server._default_monitor_clients = False
@classmethod
def tearDownClass(cls):
server.Server._default_monitor_clients = True
def setUp(self):
logging.getLogger('engineio').setLevel(logging.NOTSET)
def tearDown(self):
# restore JSON encoder, in case a test changed it
packet.Packet.json = json
def test_is_asyncio_based(self):
s = server.Server()
self.assertEqual(s.is_asyncio_based(), False)
def test_async_modes(self):
s = server.Server()
self.assertEqual(s.async_modes(), ['eventlet', 'gevent_uwsgi',
'gevent', 'threading'])
def test_create(self):
kwargs = {
'ping_timeout': 1,
'ping_interval': 2,
'max_http_buffer_size': 3,
'allow_upgrades': False,
'http_compression': False,
'compression_threshold': 4,
'cookie': 'foo',
'cors_allowed_origins': ['foo', 'bar', 'baz'],
'cors_credentials': False,
'async_handlers': False}
s = server.Server(**kwargs)
for arg in six.iterkeys(kwargs):
self.assertEqual(getattr(s, arg), kwargs[arg])
def test_create_ignores_kwargs(self):
server.Server(foo='bar') # this should not raise
def test_async_mode_threading(self):
s = server.Server(async_mode='threading')
self.assertEqual(s.async_mode, 'threading')
import threading
try:
import queue
except ImportError:
import Queue as queue
self.assertEqual(s._async['thread'], threading.Thread)
self.assertEqual(s._async['queue'], queue.Queue)
self.assertEqual(s._async['websocket'], None)
def test_async_mode_eventlet(self):
s = server.Server(async_mode='eventlet')
self.assertEqual(s.async_mode, 'eventlet')
from eventlet.green import threading
from eventlet import queue
from engineio.async_drivers import eventlet as async_eventlet
self.assertEqual(s._async['thread'], threading.Thread)
self.assertEqual(s._async['queue'], queue.Queue)
self.assertEqual(s._async['websocket'], async_eventlet.WebSocketWSGI)
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent_uwsgi(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['gevent'].queue = mock.MagicMock()
sys.modules['gevent.queue'] = sys.modules['gevent'].queue
sys.modules['gevent.queue'].JoinableQueue = 'foo'
sys.modules['gevent.queue'].Empty = RuntimeError
sys.modules['gevent.event'] = mock.MagicMock()
sys.modules['gevent.event'].Event = 'bar'
sys.modules['uwsgi'] = mock.MagicMock()
s = server.Server(async_mode='gevent_uwsgi')
self.assertEqual(s.async_mode, 'gevent_uwsgi')
from engineio.async_drivers import gevent_uwsgi as async_gevent_uwsgi
self.assertEqual(s._async['thread'], async_gevent_uwsgi.Thread)
self.assertEqual(s._async['queue'], 'foo')
self.assertEqual(s._async['queue_empty'], RuntimeError)
self.assertEqual(s._async['event'], 'bar')
self.assertEqual(s._async['websocket'],
async_gevent_uwsgi.uWSGIWebSocket)
del sys.modules['gevent']
del sys.modules['gevent.queue']
del sys.modules['gevent.event']
del sys.modules['uwsgi']
del sys.modules['engineio.async_drivers.gevent_uwsgi']
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent_uwsgi_without_uwsgi(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['gevent'].queue = mock.MagicMock()
sys.modules['gevent.queue'] = sys.modules['gevent'].queue
sys.modules['gevent.queue'].JoinableQueue = 'foo'
sys.modules['gevent.queue'].Empty = RuntimeError
sys.modules['gevent.event'] = mock.MagicMock()
sys.modules['gevent.event'].Event = 'bar'
sys.modules['uwsgi'] = None
self.assertRaises(ValueError, server.Server,
async_mode='gevent_uwsgi')
del sys.modules['gevent']
del sys.modules['gevent.queue']
del sys.modules['gevent.event']
del sys.modules['uwsgi']
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent_uwsgi_without_websocket(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['gevent'].queue = mock.MagicMock()
sys.modules['gevent.queue'] = sys.modules['gevent'].queue
sys.modules['gevent.queue'].JoinableQueue = 'foo'
sys.modules['gevent.queue'].Empty = RuntimeError
sys.modules['gevent.event'] = mock.MagicMock()
sys.modules['gevent.event'].Event = 'bar'
sys.modules['uwsgi'] = mock.MagicMock()
del sys.modules['uwsgi'].websocket_handshake
s = server.Server(async_mode='gevent_uwsgi')
self.assertEqual(s.async_mode, 'gevent_uwsgi')
from engineio.async_drivers import gevent_uwsgi as async_gevent_uwsgi
self.assertEqual(s._async['thread'], async_gevent_uwsgi.Thread)
self.assertEqual(s._async['queue'], 'foo')
self.assertEqual(s._async['queue_empty'], RuntimeError)
self.assertEqual(s._async['event'], 'bar')
self.assertEqual(s._async['websocket'], None)
del sys.modules['gevent']
del sys.modules['gevent.queue']
del sys.modules['gevent.event']
del sys.modules['uwsgi']
del sys.modules['engineio.async_drivers.gevent_uwsgi']
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['gevent'].queue = mock.MagicMock()
sys.modules['gevent.queue'] = sys.modules['gevent'].queue
sys.modules['gevent.queue'].JoinableQueue = 'foo'
sys.modules['gevent.queue'].Empty = RuntimeError
sys.modules['gevent.event'] = mock.MagicMock()
sys.modules['gevent.event'].Event = 'bar'
sys.modules['geventwebsocket'] = 'geventwebsocket'
s = server.Server(async_mode='gevent')
self.assertEqual(s.async_mode, 'gevent')
from engineio.async_drivers import gevent as async_gevent
self.assertEqual(s._async['thread'], async_gevent.Thread)
self.assertEqual(s._async['queue'], 'foo')
self.assertEqual(s._async['queue_empty'], RuntimeError)
self.assertEqual(s._async['event'], 'bar')
self.assertEqual(s._async['websocket'], async_gevent.WebSocketWSGI)
del sys.modules['gevent']
del sys.modules['gevent.queue']
del sys.modules['gevent.event']
del sys.modules['geventwebsocket']
del sys.modules['engineio.async_drivers.gevent']
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent_without_websocket(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['gevent'].queue = mock.MagicMock()
sys.modules['gevent.queue'] = sys.modules['gevent'].queue
sys.modules['gevent.queue'].JoinableQueue = 'foo'
sys.modules['gevent.queue'].Empty = RuntimeError
sys.modules['gevent.event'] = mock.MagicMock()
sys.modules['gevent.event'].Event = 'bar'
sys.modules['geventwebsocket'] = None
s = server.Server(async_mode='gevent')
self.assertEqual(s.async_mode, 'gevent')
from engineio.async_drivers import gevent as async_gevent
self.assertEqual(s._async['thread'], async_gevent.Thread)
self.assertEqual(s._async['queue'], 'foo')
self.assertEqual(s._async['queue_empty'], RuntimeError)
self.assertEqual(s._async['event'], 'bar')
self.assertEqual(s._async['websocket'], None)
del sys.modules['gevent']
del sys.modules['gevent.queue']
del sys.modules['gevent.event']
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()
self.assertRaises(ValueError, server.Server, async_mode='aiohttp')
@mock.patch('importlib.import_module', side_effect=[ImportError])
def test_async_mode_invalid(self, import_module):
self.assertRaises(ValueError, server.Server, async_mode='foo')
@mock.patch('importlib.import_module', side_effect=[_mock_async])
def test_async_mode_auto_eventlet(self, import_module):
s = server.Server()
self.assertEqual(s.async_mode, 'eventlet')
@mock.patch('importlib.import_module', side_effect=[ImportError,
_mock_async])
def test_async_mode_auto_gevent_uwsgi(self, import_module):
s = server.Server()
self.assertEqual(s.async_mode, 'gevent_uwsgi')
@mock.patch('importlib.import_module', side_effect=[ImportError,
ImportError,
_mock_async])
def test_async_mode_auto_gevent(self, import_module):
s = server.Server()
self.assertEqual(s.async_mode, 'gevent')
@mock.patch('importlib.import_module', side_effect=[ImportError,
ImportError,
ImportError,
_mock_async])
def test_async_mode_auto_threading(self, import_module):
s = server.Server()
self.assertEqual(s.async_mode, 'threading')
def test_generate_id(self):
s = server.Server()
self.assertNotEqual(s._generate_id(), s._generate_id())
def test_on_event(self):
s = server.Server()
@s.on('connect')
def foo():
pass
s.on('disconnect', foo)
self.assertEqual(s.handlers['connect'], foo)
self.assertEqual(s.handlers['disconnect'], foo)
def test_on_event_invalid(self):
s = server.Server()
self.assertRaises(ValueError, s.on, 'invalid')
def test_trigger_event(self):
s = server.Server()
f = {}
@s.on('connect')
def foo(sid, environ):
return sid + environ
@s.on('message')
def bar(sid, data):
f['bar'] = sid + data
return 'bar'
r = s._trigger_event('connect', 1, 2, run_async=False)
self.assertEqual(r, 3)
r = s._trigger_event('message', 3, 4, run_async=True)
r.join()
self.assertEqual(f['bar'], 7)
r = s._trigger_event('message', 5, 6)
self.assertEqual(r, 'bar')
def test_trigger_event_error(self):
s = server.Server()
@s.on('connect')
def foo(sid, environ):
return 1 / 0
@s.on('message')
def bar(sid, data):
return 1 / 0
r = s._trigger_event('connect', 1, 2, run_async=False)
self.assertEqual(r, False)
r = s._trigger_event('message', 3, 4, run_async=False)
self.assertEqual(r, None)
def test_session(self):
s = server.Server()
mock_socket = self._get_mock_socket()
s.sockets['foo'] = mock_socket
with s.session('foo') as session:
self.assertEqual(session, {})
session['username'] = 'bar'
self.assertEqual(s.get_session('foo'), {'username': 'bar'})
def test_close_one_socket(self):
s = server.Server()
mock_socket = self._get_mock_socket()
s.sockets['foo'] = mock_socket
s.disconnect('foo')
self.assertEqual(mock_socket.close.call_count, 1)
self.assertNotIn('foo', s.sockets)
def test_close_all_sockets(self):
s = server.Server()
mock_sockets = {}
for sid in ['foo', 'bar', 'baz']:
mock_sockets[sid] = self._get_mock_socket()
s.sockets[sid] = mock_sockets[sid]
s.disconnect()
for socket in six.itervalues(mock_sockets):
self.assertEqual(socket.close.call_count, 1)
self.assertEqual(s.sockets, {})
def test_upgrades(self):
s = server.Server()
s.sockets['foo'] = self._get_mock_socket()
self.assertEqual(s._upgrades('foo', 'polling'), ['websocket'])
self.assertEqual(s._upgrades('foo', 'websocket'), [])
s.sockets['foo'].upgraded = True
self.assertEqual(s._upgrades('foo', 'polling'), [])
self.assertEqual(s._upgrades('foo', 'websocket'), [])
s.allow_upgrades = False
s.sockets['foo'].upgraded = True
self.assertEqual(s._upgrades('foo', 'polling'), [])
self.assertEqual(s._upgrades('foo', 'websocket'), [])
def test_transport(self):
s = server.Server()
s.sockets['foo'] = self._get_mock_socket()
s.sockets['foo'].upgraded = False
s.sockets['bar'] = self._get_mock_socket()
s.sockets['bar'].upgraded = True
self.assertEqual(s.transport('foo'), 'polling')
self.assertEqual(s.transport('bar'), 'websocket')
def test_bad_session(self):
s = server.Server()
s.sockets['foo'] = 'client'
self.assertRaises(KeyError, s._get_socket, 'bar')
def test_closed_socket(self):
s = server.Server()
s.sockets['foo'] = self._get_mock_socket()
s.sockets['foo'].closed = True
self.assertRaises(KeyError, s._get_socket, 'foo')
def test_jsonp_with_bad_index(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'j=abc'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
def test_jsonp_index(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'j=233'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'200 OK')
self.assertTrue(r[0].startswith(b'___eio[233]("'))
self.assertTrue(r[0].endswith(b'");'))
def test_connect(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
self.assertEqual(len(s.sockets), 1)
self.assertEqual(start_response.call_count, 1)
self.assertEqual(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
start_response.call_args[0][1])
self.assertEqual(len(r), 1)
packets = payload.Payload(encoded_payload=r[0]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.OPEN)
self.assertIn('upgrades', packets[0].data)
self.assertEqual(packets[0].data['upgrades'], ['websocket'])
self.assertIn('sid', packets[0].data)
def test_connect_no_upgrades(self):
s = server.Server(allow_upgrades=False)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
packets = payload.Payload(encoded_payload=r[0]).packets
self.assertEqual(packets[0].data['upgrades'], [])
def test_connect_b64_with_1(self):
s = server.Server(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'b64=1'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertTrue(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'text/plain; charset=UTF-8'),
start_response.call_args[0][1])
s.send('1', b'\x00\x01\x02', binary=True)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=1'}
r = s.handle_request(environ, start_response)
self.assertEqual(r[0], b'6:b4AAEC')
def test_connect_b64_with_true(self):
s = server.Server(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'b64=true'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertTrue(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'text/plain; charset=UTF-8'),
start_response.call_args[0][1])
s.send('1', b'\x00\x01\x02', binary=True)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=true'}
r = s.handle_request(environ, start_response)
self.assertEqual(r[0], b'6:b4AAEC')
def test_connect_b64_with_0(self):
s = server.Server(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'b64=0'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertTrue(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
start_response.call_args[0][1])
s.send('1', b'\x00\x01\x02', binary=True)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=0'}
r = s.handle_request(environ, start_response)
self.assertEqual(r[0], b'\x01\x04\xff\x04\x00\x01\x02')
def test_connect_b64_with_false(self):
s = server.Server(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'b64=false'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertTrue(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
start_response.call_args[0][1])
s.send('1', b'\x00\x01\x02', binary=True)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=false'}
r = s.handle_request(environ, start_response)
self.assertEqual(r[0], b'\x01\x04\xff\x04\x00\x01\x02')
def test_connect_custom_ping_times(self):
s = server.Server(ping_timeout=123, ping_interval=456)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
packets = payload.Payload(encoded_payload=r[0]).packets
self.assertEqual(packets[0].data['pingTimeout'], 123000)
self.assertEqual(packets[0].data['pingInterval'], 456000)
@mock.patch('engineio.socket.Socket.poll',
side_effect=exceptions.QueueEmpty)
def test_connect_bad_poll(self, poll):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
@mock.patch('engineio.socket.Socket',
return_value=mock.MagicMock(connected=False, closed=False))
def test_connect_transport_websocket(self, Socket):
s = server.Server()
s._generate_id = mock.MagicMock(return_value='123')
environ = {'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=websocket'}
start_response = mock.MagicMock()
# force socket to stay open, so that we can check it later
Socket().closed = False
s.handle_request(environ, start_response)
self.assertEqual(s.sockets['123'].send.call_args[0][0].packet_type,
packet.OPEN)
@mock.patch('engineio.socket.Socket',
return_value=mock.MagicMock(connected=False, closed=False))
def test_connect_transport_websocket_closed(self, Socket):
s = server.Server()
s._generate_id = mock.MagicMock(return_value='123')
environ = {'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=websocket'}
start_response = mock.MagicMock()
def mock_handle(environ, start_response):
s.sockets['123'].closed = True
Socket().handle_get_request = mock_handle
s.handle_request(environ, start_response)
self.assertNotIn('123', s.sockets)
def test_connect_transport_invalid(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'transport=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
def test_connect_cors_headers(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', '*'), headers)
self.assertIn(('Access-Control-Allow-Credentials', 'true'), headers)
def test_connect_cors_allowed_origin(self):
s = server.Server(cors_allowed_origins=['a', 'b'])
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'b'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', 'b'), headers)
def test_connect_cors_not_allowed_origin(self):
s = server.Server(cors_allowed_origins=['a', 'b'])
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'c'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Origin', 'c'), headers)
self.assertNotIn(('Access-Control-Allow-Origin', '*'), headers)
def test_connect_cors_headers_all_origins(self):
s = server.Server(cors_allowed_origins='*')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', '*'), headers)
self.assertIn(('Access-Control-Allow-Credentials', 'true'), headers)
def test_connect_cors_headers_one_origin(self):
s = server.Server(cors_allowed_origins='a')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'a'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', 'a'), headers)
self.assertIn(('Access-Control-Allow-Credentials', 'true'), headers)
def test_connect_cors_headers_one_origin_not_allowed(self):
s = server.Server(cors_allowed_origins='a')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'b'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Origin', 'b'), headers)
self.assertNotIn(('Access-Control-Allow-Origin', '*'), headers)
def test_connect_cors_no_credentials(self):
s = server.Server(cors_credentials=False)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Credentials', 'true'), headers)
def test_cors_options(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'OPTIONS', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Methods', 'OPTIONS, GET, POST'),
headers)
def test_cors_request_headers(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET',
'HTTP_ACCESS_CONTROL_REQUEST_HEADERS': 'Foo, Bar'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Headers', 'Foo, Bar'), headers)
def test_connect_event(self):
s = server.Server()
s._generate_id = mock.MagicMock(return_value='123')
mock_event = mock.MagicMock()
s.on('connect')(mock_event)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
mock_event.assert_called_once_with('123', environ)
self.assertEqual(len(s.sockets), 1)
def test_connect_event_rejects(self):
s = server.Server()
s._generate_id = mock.MagicMock(return_value='123')
mock_event = mock.MagicMock(return_value=False)
s.on('connect')(mock_event)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(len(s.sockets), 0)
self.assertEqual(start_response.call_args[0][0], '401 UNAUTHORIZED')
def test_method_not_found(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'PUT', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'405 METHOD NOT FOUND')
def test_get_request_with_bad_sid(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
def test_post_request_with_bad_sid(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
def test_send(self):
s = server.Server()
mock_socket = self._get_mock_socket()
s.sockets['foo'] = mock_socket
s.send('foo', 'hello')
self.assertEqual(mock_socket.send.call_count, 1)
self.assertEqual(mock_socket.send.call_args[0][0].packet_type,
packet.MESSAGE)
self.assertEqual(mock_socket.send.call_args[0][0].data, 'hello')
def test_send_unknown_socket(self):
s = server.Server()
# just ensure no exceptions are raised
s.send('foo', 'hello')
def test_get_request(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'200 OK')
self.assertEqual(len(r), 1)
packets = payload.Payload(encoded_payload=r[0]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.MESSAGE)
def test_get_request_custom_response(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(side_effect=['resp'])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
self.assertEqual(s.handle_request(environ, start_response), 'resp')
def test_get_request_closes_socket(self):
s = server.Server()
mock_socket = self._get_mock_socket()
def mock_get_request(*args, **kwargs):
mock_socket.closed = True
return 'resp'
mock_socket.handle_get_request = mock_get_request
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
self.assertEqual(s.handle_request(environ, start_response), 'resp')
self.assertNotIn('foo', s.sockets)
def test_get_request_error(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(
side_effect=[exceptions.QueueEmpty])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
self.assertEqual(len(s.sockets), 0)
def test_post_request(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_post_request = mock.MagicMock()
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'200 OK')
def test_post_request_error(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_post_request = mock.MagicMock(
side_effect=[exceptions.EngineIOError])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
self.assertNotIn('foo', s.sockets)
@staticmethod
def _gzip_decompress(b):
bytesio = six.BytesIO(b)
with gzip.GzipFile(fileobj=bytesio, mode='r') as gz:
return gz.read()
def test_gzip_compression(self):
s = server.Server(compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip,deflate'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
self.assertIn(('Content-Encoding', 'gzip'),
start_response.call_args[0][1])
self._gzip_decompress(r[0])
def test_deflate_compression(self):
s = server.Server(compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'deflate;q=1,gzip'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
self.assertIn(('Content-Encoding', 'deflate'),
start_response.call_args[0][1])
zlib.decompress(r[0])
def test_gzip_compression_threshold(self):
s = server.Server(compression_threshold=1000)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress, r[0])
def test_compression_disabled(self):
s = server.Server(http_compression=False, compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress, r[0])
def test_compression_unknown(self):
s = server.Server(compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'rar'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress, r[0])
def test_compression_no_encoding(self):
s = server.Server(compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': ''}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress, r[0])
def test_cookie(self):
s = server.Server(cookie='sid')
s._generate_id = mock.MagicMock(return_value='123')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertIn(('Set-Cookie', 'sid=123'),
start_response.call_args[0][1])
def test_no_cookie(self):
s = server.Server(cookie=None)
s._generate_id = mock.MagicMock(return_value='123')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Set-Cookie')
def test_logger(self):
s = server.Server(logger=False)
self.assertEqual(s.logger.getEffectiveLevel(), logging.ERROR)
s.logger.setLevel(logging.NOTSET)
s = server.Server(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.INFO)
s.logger.setLevel(logging.WARNING)
s = server.Server(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.WARNING)
s.logger.setLevel(logging.NOTSET)
my_logger = logging.Logger('foo')
s = server.Server(logger=my_logger)
self.assertEqual(s.logger, my_logger)
def test_custom_json(self):
# Warning: this test cannot run in parallel with other tests, as it
# changes the JSON encoding/decoding functions
class CustomJSON(object):
@staticmethod
def dumps(*args, **kwargs):
return '*** encoded ***'
@staticmethod
def loads(*args, **kwargs):
return '+++ decoded +++'
server.Server(json=CustomJSON)
pkt = packet.Packet(packet.MESSAGE, data={'foo': 'bar'})
self.assertEqual(pkt.encode(), b'4*** encoded ***')
pkt2 = packet.Packet(encoded_packet=pkt.encode())
self.assertEqual(pkt2.data, '+++ decoded +++')
# restore the default JSON module
packet.Packet.json = json
def test_background_tasks(self):
flag = {}
def bg_task():
flag['task'] = True
s = server.Server()
task = s.start_background_task(bg_task)
task.join()
self.assertIn('task', flag)
self.assertTrue(flag['task'])
def test_sleep(self):
s = server.Server()
t = time.time()
s.sleep(0.1)
self.assertTrue(time.time() - t > 0.1)
def test_create_queue(self):
s = server.Server()
q = s.create_queue()
empty = s.get_queue_empty_exception()
self.assertRaises(empty, q.get, timeout=0.01)
def test_create_event(self):
s = server.Server()
e = s.create_event()
self.assertFalse(e.is_set())
e.set()
self.assertTrue(e.is_set())
def test_service_task_started(self):
s = server.Server(monitor_clients=True)
s._service_task = mock.MagicMock()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
s._service_task.assert_called_once_with()
@@ -0,0 +1,424 @@
import time
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio import exceptions
from engineio import packet
from engineio import payload
from engineio import socket
class TestSocket(unittest.TestCase):
def setUp(self):
self.bg_tasks = []
def _get_mock_server(self):
mock_server = mock.Mock()
mock_server.ping_timeout = 0.2
mock_server.ping_interval = 0.2
mock_server.async_handlers = True
try:
import queue
except ImportError:
import Queue as queue
import threading
mock_server._async = {'threading': threading.Thread,
'queue': queue.Queue,
'websocket': None}
def bg_task(target, *args, **kwargs):
th = threading.Thread(target=target, args=args, kwargs=kwargs)
self.bg_tasks.append(th)
th.start()
return th
def create_queue(*args, **kwargs):
return queue.Queue(*args, **kwargs)
mock_server.start_background_task = bg_task
mock_server.create_queue = create_queue
mock_server.get_queue_empty_exception.return_value = queue.Empty
return mock_server
def _join_bg_tasks(self):
for task in self.bg_tasks:
task.join()
def test_create(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
self.assertEqual(s.server, mock_server)
self.assertEqual(s.sid, 'sid')
self.assertFalse(s.upgraded)
self.assertFalse(s.closed)
self.assertTrue(hasattr(s.queue, 'get'))
self.assertTrue(hasattr(s.queue, 'put'))
self.assertTrue(hasattr(s.queue, 'task_done'))
self.assertTrue(hasattr(s.queue, 'join'))
def test_empty_poll(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
self.assertRaises(exceptions.QueueEmpty, s.poll)
def test_poll(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
s.send(pkt1)
s.send(pkt2)
self.assertEqual(s.poll(), [pkt1, pkt2])
def test_ping_pong(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.receive(packet.Packet(packet.PING, data='abc'))
r = s.poll()
self.assertEqual(len(r), 1)
self.assertTrue(r[0].encode(), b'3abc')
def test_message_async_handler(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.receive(packet.Packet(packet.MESSAGE, data='foo'))
mock_server._trigger_event.assert_called_once_with('message', 'sid',
'foo',
run_async=True)
def test_message_sync_handler(self):
mock_server = self._get_mock_server()
mock_server.async_handlers = False
s = socket.Socket(mock_server, 'sid')
s.receive(packet.Packet(packet.MESSAGE, data='foo'))
mock_server._trigger_event.assert_called_once_with('message', 'sid',
'foo',
run_async=False)
def test_invalid_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
self.assertRaises(exceptions.UnknownPacketError, s.receive,
packet.Packet(packet.OPEN))
def test_timeout(self):
mock_server = self._get_mock_server()
mock_server.ping_interval = -6
s = socket.Socket(mock_server, 'sid')
s.last_ping = time.time() - 1
s.close = mock.MagicMock()
s.send('packet')
s.close.assert_called_once_with(wait=False, abort=False)
def test_polling_read(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'foo')
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
s.send(pkt1)
s.send(pkt2)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
packets = s.handle_get_request(environ, start_response)
self.assertEqual(packets, [pkt1, pkt2])
def test_polling_read_error(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'foo')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
self.assertRaises(exceptions.QueueEmpty, s.handle_get_request, environ,
start_response)
def test_polling_write(self):
mock_server = self._get_mock_server()
mock_server.max_http_buffer_size = 1000
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
p = payload.Payload(packets=[pkt1, pkt2]).encode()
s = socket.Socket(mock_server, 'foo')
s.receive = mock.MagicMock()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo',
'CONTENT_LENGTH': len(p), 'wsgi.input': six.BytesIO(p)}
s.handle_post_request(environ)
self.assertEqual(s.receive.call_count, 2)
def test_polling_write_too_large(self):
mock_server = self._get_mock_server()
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
p = payload.Payload(packets=[pkt1, pkt2]).encode()
mock_server.max_http_buffer_size = len(p) - 1
s = socket.Socket(mock_server, 'foo')
s.receive = mock.MagicMock()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo',
'CONTENT_LENGTH': len(p), 'wsgi.input': six.BytesIO(p)}
self.assertRaises(exceptions.ContentTooLongError,
s.handle_post_request, environ)
def test_upgrade_handshake(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'foo')
s._upgrade_websocket = mock.MagicMock()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_CONNECTION': 'Foo,Upgrade,Bar',
'HTTP_UPGRADE': 'websocket'}
start_response = mock.MagicMock()
s.handle_get_request(environ, start_response)
s._upgrade_websocket.assert_called_once_with(environ, start_response)
def test_upgrade(self):
mock_server = self._get_mock_server()
mock_server._async['websocket'] = mock.MagicMock()
mock_ws = mock.MagicMock()
mock_server._async['websocket'].return_value = mock_ws
s = socket.Socket(mock_server, 'sid')
s.connected = True
environ = "foo"
start_response = "bar"
s._upgrade_websocket(environ, start_response)
mock_server._async['websocket'].assert_called_once_with(
s._websocket_handler)
mock_ws.assert_called_once_with(environ, start_response)
def test_upgrade_twice(self):
mock_server = self._get_mock_server()
mock_server._async['websocket'] = mock.MagicMock()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.upgraded = True
environ = "foo"
start_response = "bar"
self.assertRaises(IOError, s._upgrade_websocket,
environ, start_response)
def test_upgrade_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.receive(packet.Packet(packet.UPGRADE))
r = s.poll()
self.assertEqual(len(r), 1)
self.assertEqual(r[0].encode(), packet.Packet(packet.NOOP).encode())
def test_upgrade_no_probe(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
ws = mock.MagicMock()
ws.wait.return_value = packet.Packet(packet.NOOP).encode(
always_bytes=False)
s._websocket_handler(ws)
self.assertFalse(s.upgraded)
def test_upgrade_no_upgrade_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.queue.join = mock.MagicMock(return_value=None)
ws = mock.MagicMock()
probe = six.text_type('probe')
ws.wait.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.NOOP).encode(always_bytes=False)]
s._websocket_handler(ws)
ws.send.assert_called_once_with(packet.Packet(
packet.PONG, data=probe).encode(always_bytes=False))
self.assertEqual(s.queue.get().packet_type, packet.NOOP)
self.assertFalse(s.upgraded)
def test_close_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.close = mock.MagicMock()
s.receive(packet.Packet(packet.CLOSE))
s.close.assert_called_once_with(wait=False, abort=True)
def test_invalid_packet_type(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
pkt = packet.Packet(packet_type=99)
self.assertRaises(exceptions.UnknownPacketError, s.receive, pkt)
def test_upgrade_not_supported(self):
mock_server = self._get_mock_server()
mock_server._async['websocket'] = None
s = socket.Socket(mock_server, 'sid')
s.connected = True
environ = "foo"
start_response = "bar"
s._upgrade_websocket(environ, start_response)
mock_server._bad_request.assert_called_once_with()
def test_websocket_read_write(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = False
s.queue.join = mock.MagicMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = mock.MagicMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertTrue(s.connected)
self.assertTrue(s.upgraded)
self.assertEqual(mock_server._trigger_event.call_count, 2)
mock_server._trigger_event.assert_has_calls([
mock.call('message', 'sid', 'foo', run_async=True),
mock.call('disconnect', 'sid', run_async=False)])
ws.send.assert_called_with('4bar')
def test_websocket_upgrade_read_write(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.queue.join = mock.MagicMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
probe = six.text_type('probe')
s.poll = mock.MagicMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE).encode(always_bytes=False),
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertTrue(s.upgraded)
self.assertEqual(mock_server._trigger_event.call_count, 2)
mock_server._trigger_event.assert_has_calls([
mock.call('message', 'sid', 'foo', run_async=True),
mock.call('disconnect', 'sid', run_async=False)])
ws.send.assert_called_with('4bar')
def test_websocket_upgrade_with_payload(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.queue.join = mock.MagicMock(return_value=None)
probe = six.text_type('probe')
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE, data=b'2').encode(
always_bytes=False)]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertTrue(s.upgraded)
def test_websocket_upgrade_with_backlog(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.queue.join = mock.MagicMock(return_value=None)
probe = six.text_type('probe')
foo = six.text_type('foo')
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE, data=b'2').encode(
always_bytes=False)]
s.upgrading = True
s.send(packet.Packet(packet.MESSAGE, data=foo))
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertTrue(s.upgraded)
self.assertFalse(s.upgrading)
self.assertEqual(s.packet_backlog, [])
ws.send.assert_called_with('4foo')
def test_websocket_read_write_wait_fail(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = False
s.queue.join = mock.MagicMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = mock.MagicMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)],
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
RuntimeError]
ws.send.side_effect = [None, RuntimeError]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertEqual(s.closed, True)
def test_websocket_ignore_invalid_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = False
s.queue.join = mock.MagicMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = mock.MagicMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.OPEN).encode(always_bytes=False),
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertTrue(s.connected)
self.assertEqual(mock_server._trigger_event.call_count, 2)
mock_server._trigger_event.assert_has_calls([
mock.call('message', 'sid', foo, run_async=True),
mock.call('disconnect', 'sid', run_async=False)])
ws.send.assert_called_with('4bar')
def test_send_after_close(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.close(wait=False)
self.assertRaises(exceptions.SocketIsClosedError, s.send,
packet.Packet(packet.NOOP))
def test_close_after_close(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.close(wait=False)
self.assertTrue(s.closed)
self.assertEqual(mock_server._trigger_event.call_count, 1)
mock_server._trigger_event.assert_called_once_with('disconnect', 'sid',
run_async=False)
s.close()
self.assertEqual(mock_server._trigger_event.call_count, 1)
def test_close_and_wait(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.queue = mock.MagicMock()
s.close(wait=True)
s.queue.join.assert_called_once_with()
def test_close_without_wait(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.queue = mock.MagicMock()
s.close(wait=False)
self.assertEqual(s.queue.join.call_count, 0)
@@ -0,0 +1,57 @@
import sys
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
if sys.version_info >= (3, 5):
from engineio.async_drivers import aiohttp as async_aiohttp
@unittest.skipIf(sys.version_info < (3, 5), 'only for Python 3.5+')
class AiohttpTests(unittest.TestCase):
def test_create_route(self):
app = mock.MagicMock()
mock_server = mock.MagicMock()
async_aiohttp.create_route(app, mock_server, '/foo')
app.router.add_get.assert_any_call('/foo', mock_server.handle_request)
app.router.add_post.assert_any_call('/foo', mock_server.handle_request)
def test_translate_request(self):
request = mock.MagicMock()
request._message.method = 'PUT'
request._message.path = '/foo/bar?baz=1'
request._message.version = (1, 1)
request._message.headers = {'a': 'b', 'c-c': 'd', 'c_c': 'e',
'content-type': 'application/json',
'content-length': 123}
request._payload = b'hello world'
environ = async_aiohttp.translate_request(request)
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': 'baz=1',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': 123,
'HTTP_A': 'b',
# 'HTTP_C_C': 'd,e',
'RAW_URI': '/foo/bar?baz=1',
'SERVER_PROTOCOL': 'HTTP/1.1',
'wsgi.input': b'hello world',
'aiohttp.request': request,
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
self.assertTrue(
environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
# @mock.patch('async_aiohttp.aiohttp.web.Response')
def test_make_response(self):
rv = async_aiohttp.make_response('202 ACCEPTED', {'foo': 'bar'},
b'payload', {})
self.assertEqual(rv.status, 202)
self.assertEqual(rv.headers['foo'], 'bar')
self.assertEqual(rv.body, b'payload')
@@ -0,0 +1,244 @@
import os
import sys
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
if sys.version_info >= (3, 5):
import asyncio
from asyncio import coroutine
from engineio.async_drivers import asgi as async_asgi
def AsyncMock(*args, **kwargs):
"""Return a mock asynchronous function."""
m = mock.MagicMock(*args, **kwargs)
@coroutine
def mock_coro(*args, **kwargs):
return m(*args, **kwargs)
mock_coro.mock = m
return mock_coro
def _run(coro):
"""Run the given coroutine."""
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(
'eio', 'other_app', static_files='static_files',
engineio_path='/foo')
self.assertEqual(app.engineio_server, 'eio')
self.assertEqual(app.other_asgi_app, 'other_app')
self.assertEqual(app.static_files, 'static_files')
self.assertEqual(app.engineio_path, 'foo'),
def test_engineio_routing(self):
mock_server = mock.MagicMock()
mock_server.handle_request = AsyncMock()
app = async_asgi.ASGIApp(mock_server)
scope = {'type': 'http', 'path': '/engine.io/'}
handler = app(scope)
_run(handler('receive', 'send'))
mock_server.handle_request.mock.assert_called_once_with(
scope, 'receive', 'send')
def test_other_app_routing(self):
other_app = mock.MagicMock()
app = async_asgi.ASGIApp('eio', other_app)
scope = {'type': 'http', 'path': '/foo'}
app(scope)
other_app.assert_called_once_with(scope)
def test_static_file_routing(self):
root_dir = os.path.dirname(__file__)
app = async_asgi.ASGIApp('eio', static_files={
'/foo': {'content_type': 'text/html',
'filename': root_dir + '/index.html'}
})
handler = app({'type': 'http', 'path': '/foo'})
receive = AsyncMock(return_value={'type': 'http.request'})
send = AsyncMock()
_run(handler(receive, send))
send.mock.assert_called_with({'type': 'http.response.body',
'body': b'<html></html>\n'})
def test_lifespan_startup(self):
app = async_asgi.ASGIApp('eio')
handler = app({'type': 'lifespan'})
receive = AsyncMock(return_value={'type': 'lifespan.startup'})
send = AsyncMock()
_run(handler(receive, send))
send.mock.assert_called_once_with(
{'type': 'lifespan.startup.complete'})
def test_lifespan_shutdown(self):
app = async_asgi.ASGIApp('eio')
handler = app({'type': 'lifespan'})
receive = AsyncMock(return_value={'type': 'lifespan.shutdown'})
send = AsyncMock()
_run(handler(receive, send))
send.mock.assert_called_once_with(
{'type': 'lifespan.shutdown.complete'})
def test_lifespan_invalid(self):
app = async_asgi.ASGIApp('eio')
handler = app({'type': 'lifespan'})
receive = AsyncMock(return_value={'type': 'lifespan.foo'})
send = AsyncMock()
_run(handler(receive, send))
send.mock.assert_not_called()
def test_not_found(self):
app = async_asgi.ASGIApp('eio')
handler = app({'type': 'http', 'path': '/foo'})
receive = AsyncMock(return_value={'type': 'http.request'})
send = AsyncMock()
_run(handler(receive, send))
send.mock.assert_any_call(
{'type': 'http.response.start', 'status': 404,
'headers': [(b'Content-Type', b'text/plain')]})
send.mock.assert_any_call({'type': 'http.response.body',
'body': b'not found'})
def test_translate_request(self):
receive = AsyncMock(return_value={'type': 'http.request',
'body': b'hello world'})
send = AsyncMock()
environ = _run(async_asgi.translate_request({
'type': 'http',
'method': 'PUT',
'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
(b'content-type', b'application/json'),
(b'content-length', b'123')],
'path': '/foo/bar',
'query_string': b'baz=1'}, receive, send))
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': 'baz=1',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': '123',
'HTTP_A': 'b',
# 'HTTP_C_C': 'd,e',
'RAW_URI': '/foo/bar?baz=1',
'SERVER_PROTOCOL': 'HTTP/1.1',
'asgi.receive': receive,
'asgi.send': send
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
self.assertTrue(
environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
body = _run(environ['wsgi.input'].read())
self.assertEqual(body, b'hello world')
def test_translate_request_no_query_string(self):
receive = AsyncMock(return_value={'type': 'http.request',
'body': b'hello world'})
send = AsyncMock()
environ = _run(async_asgi.translate_request({
'type': 'http',
'method': 'PUT',
'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
(b'content-type', b'application/json'),
(b'content-length', b'123')],
'path': '/foo/bar'}, receive, send))
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': '',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': '123',
'HTTP_A': 'b',
# 'HTTP_C_C': 'd,e',
'RAW_URI': '/foo/bar',
'SERVER_PROTOCOL': 'HTTP/1.1',
'asgi.receive': receive,
'asgi.send': send
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
self.assertTrue(
environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
body = _run(environ['wsgi.input'].read())
self.assertEqual(body, b'hello world')
def test_translate_request_with_large_body(self):
receive = AsyncMock(side_effect=[
{'type': 'http.request', 'body': b'hello ', 'more_body': True},
{'type': 'http.request', 'body': b'world', 'more_body': True},
{'type': 'foo.bar'}, # should stop parsing here
{'type': 'http.request', 'body': b'!!!'},
])
send = AsyncMock()
environ = _run(async_asgi.translate_request({
'type': 'http',
'method': 'PUT',
'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
(b'content-type', b'application/json'),
(b'content-length', b'123')],
'path': '/foo/bar',
'query_string': b'baz=1'}, receive, send))
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': 'baz=1',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': '123',
'HTTP_A': 'b',
# 'HTTP_C_C': 'd,e',
'RAW_URI': '/foo/bar?baz=1',
'SERVER_PROTOCOL': 'HTTP/1.1',
'asgi.receive': receive,
'asgi.send': send
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
self.assertTrue(
environ['HTTP_C_C'] == 'd,e' or environ['HTTP_C_C'] == 'e,d')
body = _run(environ['wsgi.input'].read())
self.assertEqual(body, b'hello world')
def test_translate_websocket_request(self):
receive = AsyncMock(return_value={'type': 'websocket.connect'})
send = AsyncMock()
_run(async_asgi.translate_request({
'type': 'websocket',
'headers': [(b'a', b'b'), (b'c-c', b'd'), (b'c_c', b'e'),
(b'content-type', b'application/json'),
(b'content-length', b'123')],
'path': '/foo/bar',
'query_string': b'baz=1'}, receive, send))
send.mock.assert_called_once_with({'type': 'websocket.accept'})
def test_translate_unknown_request(self):
receive = AsyncMock(return_value={'type': 'http.foo'})
send = AsyncMock()
environ = _run(async_asgi.translate_request({
'type': 'http',
'path': '/foo/bar',
'query_string': b'baz=1'}, receive, send))
self.assertEqual(environ, {})
# @mock.patch('async_aiohttp.aiohttp.web.Response')
def test_make_response(self):
environ = {
'asgi.send': AsyncMock()
}
_run(async_asgi.make_response('202 ACCEPTED', [('foo', 'bar')],
b'payload', environ))
environ['asgi.send'].mock.assert_any_call(
{'type': 'http.response.start', 'status': 202,
'headers': [(b'foo', b'bar')]})
environ['asgi.send'].mock.assert_any_call(
{'type': 'http.response.body', 'body': b'payload'})
@@ -0,0 +1,30 @@
import logging
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio.async_drivers import eventlet as async_eventlet
class TestAsyncEventlet(unittest.TestCase):
def setUp(self):
logging.getLogger('engineio').setLevel(logging.NOTSET)
def test_bad_environ(self):
wsgi = async_eventlet.WebSocketWSGI(None)
environ = {'foo': 'bar'}
start_response = 'bar'
self.assertRaises(RuntimeError, wsgi, environ, start_response)
@mock.patch('engineio.async_drivers.eventlet._WebSocketWSGI.__call__',
return_value='data')
def test_wsgi_call(self, _WebSocketWSGI):
_WebSocketWSGI.__call__ = lambda e, s: 'data'
environ = {'eventlet.input': mock.MagicMock()}
start_response = 'bar'
wsgi = async_eventlet.WebSocketWSGI(None)
self.assertEqual(wsgi(environ, start_response), 'data')
@@ -0,0 +1,76 @@
try:
import asyncio
except ImportError:
pass
import sys
import unittest
import six
try:
import tornado.web
except ImportError:
pass
if six.PY3:
from unittest import mock
else:
import mock
if sys.version_info >= (3, 5):
from engineio.async_drivers import tornado as async_tornado
def _run(coro):
"""Run the given coroutine."""
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()
handler = async_tornado.get_tornado_handler(mock_server)
self.assertTrue(issubclass(handler,
tornado.websocket.WebSocketHandler))
def test_translate_request(self):
mock_handler = mock.MagicMock()
mock_handler.request.method = 'PUT'
mock_handler.request.path = '/foo/bar'
mock_handler.request.query = 'baz=1'
mock_handler.request.version = '1.1'
mock_handler.request.headers = {
'a': 'b',
'c': 'd',
'content-type': 'application/json',
'content-length': 123
}
mock_handler.request.body = b'hello world'
environ = async_tornado.translate_request(mock_handler)
expected_environ = {
'REQUEST_METHOD': 'PUT',
'PATH_INFO': '/foo/bar',
'QUERY_STRING': 'baz=1',
'CONTENT_TYPE': 'application/json',
'CONTENT_LENGTH': 123,
'HTTP_A': 'b',
'HTTP_C': 'd',
'RAW_URI': '/foo/bar?baz=1',
'SERVER_PROTOCOL': 'HTTP/1.1',
# 'wsgi.input': b'hello world',
'tornado.handler': mock_handler,
}
for k, v in expected_environ.items():
self.assertEqual(v, environ[k])
payload = _run(environ['wsgi.input'].read(1))
payload += _run(environ['wsgi.input'].read())
self.assertEqual(payload, b'hello world')
def test_make_response(self):
mock_handler = mock.MagicMock()
mock_environ = {'tornado.handler': mock_handler}
async_tornado.make_response('202 ACCEPTED', [('foo', 'bar')],
b'payload', mock_environ)
mock_handler.set_status.assert_called_once_with(202)
mock_handler.set_header.assert_called_once_with('foo', 'bar')
mock_handler.write.assert_called_once_with(b'payload')
mock_handler.finish.assert_called_once_with()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,895 @@
import gzip
import json
import logging
import sys
import unittest
import zlib
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio import exceptions
from engineio import packet
from engineio import payload
if sys.version_info >= (3, 5):
import asyncio
from asyncio import coroutine
from engineio import asyncio_server
from engineio.async_drivers import aiohttp as async_aiohttp
else:
# mock coroutine so that Python 2 doesn't complain
def coroutine(f):
return f
def AsyncMock(*args, **kwargs):
"""Return a mock asynchronous function."""
m = mock.MagicMock(*args, **kwargs)
@coroutine
def mock_coro(*args, **kwargs):
return m(*args, **kwargs)
mock_coro.mock = m
return mock_coro
def _run(coro):
"""Run the given coroutine."""
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': ''}):
a = mock.MagicMock()
a._async = {
'asyncio': True,
'create_route': mock.MagicMock(),
'translate_request': mock.MagicMock(),
'make_response': mock.MagicMock(),
'websocket': 'w',
'websocket_class': 'wc'
}
a._async['translate_request'].return_value = environ
a._async['make_response'].return_value = 'response'
return a
def _get_mock_socket(self):
mock_socket = mock.MagicMock()
mock_socket.connected = False
mock_socket.closed = False
mock_socket.closing = False
mock_socket.upgraded = False
mock_socket.send = AsyncMock()
mock_socket.handle_get_request = AsyncMock()
mock_socket.handle_post_request = AsyncMock()
mock_socket.check_ping_timeout = AsyncMock()
mock_socket.close = AsyncMock()
return mock_socket
@classmethod
def setUpClass(cls):
asyncio_server.AsyncServer._default_monitor_clients = False
@classmethod
def tearDownClass(cls):
asyncio_server.AsyncServer._default_monitor_clients = True
def setUp(self):
logging.getLogger('engineio').setLevel(logging.NOTSET)
def tearDown(self):
# restore JSON encoder, in case a test changed it
packet.Packet.json = json
def test_is_asyncio_based(self):
s = asyncio_server.AsyncServer()
self.assertEqual(s.is_asyncio_based(), True)
def test_async_modes(self):
s = asyncio_server.AsyncServer()
self.assertEqual(s.async_modes(), ['aiohttp', 'sanic', 'tornado',
'asgi'])
def test_async_mode_aiohttp(self):
s = asyncio_server.AsyncServer(async_mode='aiohttp')
self.assertEqual(s.async_mode, 'aiohttp')
self.assertEqual(s._async['asyncio'], True)
self.assertEqual(s._async['create_route'], async_aiohttp.create_route)
self.assertEqual(s._async['translate_request'],
async_aiohttp.translate_request)
self.assertEqual(s._async['make_response'],
async_aiohttp.make_response)
self.assertEqual(s._async['websocket'], async_aiohttp)
self.assertEqual(s._async['websocket_class'], 'WebSocket')
@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()
self.assertEqual(s.async_mode, 'aiohttp')
def test_async_modes_wsgi(self):
self.assertRaises(ValueError, asyncio_server.AsyncServer,
async_mode='eventlet')
self.assertRaises(ValueError, asyncio_server.AsyncServer,
async_mode='gevent')
self.assertRaises(ValueError, asyncio_server.AsyncServer,
async_mode='gevent_uwsgi')
self.assertRaises(ValueError, asyncio_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.attach('app', engineio_path='abc')
a._async['create_route'].assert_called_with('app', s, '/abc/')
s.attach('app', engineio_path='/def/')
a._async['create_route'].assert_called_with('app', s, '/def/')
s.attach('app', engineio_path='/ghi')
a._async['create_route'].assert_called_with('app', s, '/ghi/')
s.attach('app', engineio_path='jkl/')
a._async['create_route'].assert_called_with('app', s, '/jkl/')
def test_disconnect(self):
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
_run(s.disconnect('foo'))
self.assertEqual(mock_socket.close.mock.call_count, 1)
mock_socket.close.mock.assert_called_once_with()
self.assertNotIn('foo', s.sockets)
def test_disconnect_all(self):
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_foo = self._get_mock_socket()
s.sockets['bar'] = mock_bar = self._get_mock_socket()
_run(s.disconnect())
self.assertEqual(mock_foo.close.mock.call_count, 1)
self.assertEqual(mock_bar.close.mock.call_count, 1)
mock_foo.close.mock.assert_called_once_with()
mock_bar.close.mock.assert_called_once_with()
self.assertNotIn('foo', s.sockets)
self.assertNotIn('bar', s.sockets)
@mock.patch('importlib.import_module')
def test_jsonp_not_supported(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'j=abc'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
response = _run(s.handle_request('request'))
self.assertEqual(response, 'response')
a._async['translate_request'].assert_called_once_with('request')
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('importlib.import_module')
def test_connect(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 1)
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
a._async['make_response'].call_args[0][1])
packets = payload.Payload(
encoded_payload=a._async['make_response'].call_args[0][2]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.OPEN)
self.assertIn('upgrades', packets[0].data)
self.assertEqual(packets[0].data['upgrades'], ['websocket'])
self.assertIn('sid', packets[0].data)
@mock.patch('importlib.import_module')
def test_connect_async_request_response_handlers(self, import_module):
a = self.get_async_mock()
a._async['translate_request'] = AsyncMock(
return_value=a._async['translate_request'].return_value)
a._async['make_response'] = AsyncMock(
return_value=a._async['make_response'].return_value)
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 1)
self.assertEqual(a._async['make_response'].mock.call_count, 1)
self.assertEqual(a._async['make_response'].mock.call_args[0][0],
'200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
a._async['make_response'].mock.call_args[0][1])
packets = payload.Payload(encoded_payload=a._async[
'make_response'].mock.call_args[0][2]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.OPEN)
self.assertIn('upgrades', packets[0].data)
self.assertEqual(packets[0].data['upgrades'], ['websocket'])
self.assertIn('sid', packets[0].data)
@mock.patch('importlib.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)
_run(s.handle_request('request'))
packets = payload.Payload(
encoded_payload=a._async['make_response'].call_args[0][2]).packets
self.assertEqual(packets[0].data['upgrades'], [])
@mock.patch('importlib.import_module')
def test_connect_b64_with_1(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'b64=1'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'text/plain; charset=UTF-8'),
a._async['make_response'].call_args[0][1])
_run(s.send('1', b'\x00\x01\x02', binary=True))
a._async['translate_request'].return_value = {
'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=1'}
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][2],
b'6:b4AAEC')
@mock.patch('importlib.import_module')
def test_connect_b64_with_true(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'b64=true'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'text/plain; charset=UTF-8'),
a._async['make_response'].call_args[0][1])
_run(s.send('1', b'\x00\x01\x02', binary=True))
a._async['translate_request'].return_value = {
'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=true'}
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][2],
b'6:b4AAEC')
@mock.patch('importlib.import_module')
def test_connect_b64_with_0(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'b64=0'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
a._async['make_response'].call_args[0][1])
_run(s.send('1', b'\x00\x01\x02', binary=True))
a._async['translate_request'].return_value = {
'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=0'}
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][2],
b'\x01\x04\xff\x04\x00\x01\x02')
@mock.patch('importlib.import_module')
def test_connect_b64_with_false(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'b64=false'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
a._async['make_response'].call_args[0][1])
_run(s.send('1', b'\x00\x01\x02', binary=True))
a._async['translate_request'].return_value = {
'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=false'}
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][2],
b'\x01\x04\xff\x04\x00\x01\x02')
@mock.patch('importlib.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)
_run(s.handle_request('request'))
packets = payload.Payload(
encoded_payload=a._async['make_response'].call_args[0][2]).packets
self.assertEqual(packets[0].data['pingTimeout'], 123000)
self.assertEqual(packets[0].data['pingInterval'], 456000)
@mock.patch('engineio.asyncio_socket.AsyncSocket')
@mock.patch('importlib.import_module')
def test_connect_bad_poll(self, import_module, AsyncSocket):
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()
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('engineio.asyncio_socket.AsyncSocket')
@mock.patch('importlib.import_module')
def test_connect_transport_websocket(self, import_module, AsyncSocket):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=websocket'})
import_module.side_effect = [a]
AsyncSocket.return_value = self._get_mock_socket()
s = asyncio_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
_run(s.handle_request('request'))
self.assertEqual(
s.sockets['123'].send.mock.call_args[0][0].packet_type,
packet.OPEN)
@mock.patch('engineio.asyncio_socket.AsyncSocket')
@mock.patch('importlib.import_module')
def test_connect_transport_websocket_closed(self, import_module,
AsyncSocket):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=websocket'})
import_module.side_effect = [a]
AsyncSocket.return_value = self._get_mock_socket()
s = asyncio_server.AsyncServer()
s._generate_id = mock.MagicMock(return_value='123')
# this mock handler just closes the socket, as it would happen on a
# real websocket exchange
@coroutine
def mock_handle(environ):
s.sockets['123'].closed = True
AsyncSocket().handle_get_request = mock_handle
_run(s.handle_request('request'))
self.assertNotIn('123', s.sockets) # socket should close on its own
@mock.patch('importlib.import_module')
def test_connect_transport_invalid(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_count, 1)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('importlib.import_module')
def test_connect_cors_headers(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', '*'), headers)
self.assertIn(('Access-Control-Allow-Credentials', 'true'), headers)
@mock.patch('importlib.import_module')
def test_connect_cors_allowed_origin(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'b'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(cors_allowed_origins=['a', 'b'])
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', 'b'), headers)
@mock.patch('importlib.import_module')
def test_connect_cors_not_allowed_origin(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'c'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(cors_allowed_origins=['a', 'b'])
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Origin', 'c'), headers)
self.assertNotIn(('Access-Control-Allow-Origin', '*'), headers)
@mock.patch('importlib.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)
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Credentials', 'true'), headers)
@mock.patch('importlib.import_module')
def test_connect_cors_options(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'OPTIONS',
'QUERY_STRING': ''})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(cors_credentials=False)
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Access-Control-Allow-Methods',
'OPTIONS, GET, POST'), headers)
@mock.patch('importlib.import_module')
def test_connect_event(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s._generate_id = mock.MagicMock(return_value='123')
def mock_connect(sid, environ):
return True
s.on('connect', handler=mock_connect)
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 1)
@mock.patch('importlib.import_module')
def test_connect_event_rejects(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s._generate_id = mock.MagicMock(return_value='123')
def mock_connect(sid, environ):
return False
s.on('connect')(mock_connect)
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 0)
self.assertEqual(a._async['make_response'].call_args[0][0],
'401 UNAUTHORIZED')
@mock.patch('importlib.import_module')
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()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 0)
self.assertEqual(a._async['make_response'].call_args[0][0],
'405 METHOD NOT FOUND')
@mock.patch('importlib.import_module')
def test_get_request_with_bad_sid(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 0)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('importlib.import_module')
def test_post_request_with_bad_sid(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'POST',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
_run(s.handle_request('request'))
self.assertEqual(len(s.sockets), 0)
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@mock.patch('importlib.import_module')
def test_send(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
_run(s.send('foo', 'hello'))
self.assertEqual(mock_socket.send.mock.call_count, 1)
self.assertEqual(mock_socket.send.mock.call_args[0][0].packet_type,
packet.MESSAGE)
self.assertEqual(mock_socket.send.mock.call_args[0][0].data, 'hello')
@mock.patch('importlib.import_module')
def test_send_unknown_socket(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
# just ensure no exceptions are raised
_run(s.send('foo', 'hello'))
@mock.patch('importlib.import_module')
def test_get_request(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
packets = payload.Payload(
encoded_payload=a._async['make_response'].call_args[0][2]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.MESSAGE)
@mock.patch('importlib.import_module')
def test_get_request_custom_response(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_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'))
self.assertEqual(r, 'resp')
@mock.patch('importlib.import_module')
def test_get_request_closes_socket(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
@coroutine
def mock_get_request(*args, **kwargs):
mock_socket.closed = True
return 'resp'
mock_socket.handle_get_request.mock.return_value = mock_get_request()
r = _run(s.handle_request('request'))
self.assertEqual(r, 'resp')
self.assertNotIn('foo', s.sockets)
@mock.patch('importlib.import_module')
def test_get_request_error(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
@coroutine
def mock_get_request(*args, **kwargs):
raise exceptions.QueueEmpty()
mock_socket.handle_get_request.mock.return_value = mock_get_request()
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
self.assertEqual(len(s.sockets), 0)
@mock.patch('importlib.import_module')
def test_post_request(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'POST',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = self._get_mock_socket()
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][0], '200 OK')
@mock.patch('importlib.import_module')
def test_post_request_error(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'POST',
'QUERY_STRING': 'sid=foo'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer()
s.sockets['foo'] = mock_socket = self._get_mock_socket()
@coroutine
def mock_post_request(*args, **kwargs):
raise exceptions.ContentTooLongError()
mock_socket.handle_post_request.mock.return_value = mock_post_request()
_run(s.handle_request('request'))
self.assertEqual(a._async['make_response'].call_args[0][0],
'400 BAD REQUEST')
@staticmethod
def _gzip_decompress(b):
bytesio = six.BytesIO(b)
with gzip.GzipFile(fileobj=bytesio, mode='r') as gz:
return gz.read()
@mock.patch('importlib.import_module')
def test_gzip_compression(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip,deflate'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Content-Encoding', 'gzip'), headers)
self._gzip_decompress(a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_deflate_compression(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'deflate;q=1,gzip'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Content-Encoding', 'deflate'), headers)
zlib.decompress(a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_gzip_compression_threshold(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
for header, value in headers:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress,
a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_compression_disabled(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip'})
import_module.side_effect = [a]
s = asyncio_server.AsyncServer(http_compression=False,
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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
for header, value in headers:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress,
a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_compression_unknown(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'rar'})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
for header, value in headers:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress,
a._async['make_response'].call_args[0][2])
@mock.patch('importlib.import_module')
def test_compression_no_encoding(self, import_module):
a = self.get_async_mock({'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': ''})
import_module.side_effect = [a]
s = asyncio_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')]
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
for header, value in headers:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress,
a._async['make_response'].call_args[0][2])
@mock.patch('importlib.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._generate_id = mock.MagicMock(return_value='123')
_run(s.handle_request('request'))
headers = a._async['make_response'].call_args[0][1]
self.assertIn(('Set-Cookie', 'sid=123'), headers)
@mock.patch('importlib.import_module')
def test_no_cookie(self, import_module):
a = self.get_async_mock()
import_module.side_effect = [a]
s = asyncio_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]
for header, value in headers:
self.assertNotEqual(header, 'Set-Cookie')
def test_logger(self):
s = asyncio_server.AsyncServer(logger=False)
self.assertEqual(s.logger.getEffectiveLevel(), logging.ERROR)
s.logger.setLevel(logging.NOTSET)
s = asyncio_server.AsyncServer(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.INFO)
s.logger.setLevel(logging.WARNING)
s = asyncio_server.AsyncServer(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.WARNING)
s.logger.setLevel(logging.NOTSET)
my_logger = logging.Logger('foo')
s = asyncio_server.AsyncServer(logger=my_logger)
self.assertEqual(s.logger, my_logger)
def test_custom_json(self):
# Warning: this test cannot run in parallel with other tests, as it
# changes the JSON encoding/decoding functions
class CustomJSON(object):
@staticmethod
def dumps(*args, **kwargs):
return '*** encoded ***'
@staticmethod
def loads(*args, **kwargs):
return '+++ decoded +++'
asyncio_server.AsyncServer(json=CustomJSON)
pkt = packet.Packet(packet.MESSAGE, data={'foo': 'bar'})
self.assertEqual(pkt.encode(), b'4*** encoded ***')
pkt2 = packet.Packet(encoded_packet=pkt.encode())
self.assertEqual(pkt2.data, '+++ decoded +++')
# restore the default JSON module
packet.Packet.json = json
def test_background_tasks(self):
r = []
@coroutine
def foo(arg):
r.append(arg)
s = asyncio_server.AsyncServer()
s.start_background_task(foo, 'bar')
pending = asyncio.Task.all_tasks()
asyncio.get_event_loop().run_until_complete(asyncio.wait(pending))
self.assertEqual(r, ['bar'])
def test_sleep(self):
s = asyncio_server.AsyncServer()
_run(s.sleep(0))
def test_trigger_event_function(self):
result = []
def foo_handler(arg):
result.append('ok')
result.append(arg)
s = asyncio_server.AsyncServer()
s.on('message', handler=foo_handler)
_run(s._trigger_event('message', 'bar'))
self.assertEqual(result, ['ok', 'bar'])
def test_trigger_event_coroutine(self):
result = []
@coroutine
def foo_handler(arg):
result.append('ok')
result.append(arg)
s = asyncio_server.AsyncServer()
s.on('message', handler=foo_handler)
_run(s._trigger_event('message', 'bar'))
self.assertEqual(result, ['ok', 'bar'])
def test_trigger_event_function_error(self):
def connect_handler(arg):
return 1 / 0
def foo_handler(arg):
return 1 / 0
s = asyncio_server.AsyncServer()
s.on('connect', handler=connect_handler)
s.on('message', handler=foo_handler)
self.assertFalse(_run(s._trigger_event('connect', '123')))
self.assertIsNone(_run(s._trigger_event('message', 'bar')))
def test_trigger_event_coroutine_error(self):
@coroutine
def connect_handler(arg):
return 1 / 0
@coroutine
def foo_handler(arg):
return 1 / 0
s = asyncio_server.AsyncServer()
s.on('connect', handler=connect_handler)
s.on('message', handler=foo_handler)
self.assertFalse(_run(s._trigger_event('connect', '123')))
self.assertIsNone(_run(s._trigger_event('message', 'bar')))
def test_trigger_event_function_async(self):
result = []
def foo_handler(arg):
result.append('ok')
result.append(arg)
s = asyncio_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)
self.assertEqual(result, ['ok', 'bar'])
def test_trigger_event_coroutine_async(self):
result = []
@coroutine
def foo_handler(arg):
result.append('ok')
result.append(arg)
s = asyncio_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)
self.assertEqual(result, ['ok', 'bar'])
def test_trigger_event_function_async_error(self):
result = []
def foo_handler(arg):
result.append(arg)
return 1 / 0
s = asyncio_server.AsyncServer()
s.on('message', handler=foo_handler)
fut = _run(s._trigger_event('message', 'bar', run_async=True))
self.assertRaises(
ZeroDivisionError, asyncio.get_event_loop().run_until_complete,
fut)
self.assertEqual(result, ['bar'])
def test_trigger_event_coroutine_async_error(self):
result = []
@coroutine
def foo_handler(arg):
result.append(arg)
return 1 / 0
s = asyncio_server.AsyncServer()
s.on('message', handler=foo_handler)
fut = _run(s._trigger_event('message', 'bar', run_async=True))
self.assertRaises(
ZeroDivisionError, asyncio.get_event_loop().run_until_complete,
fut)
self.assertEqual(result, ['bar'])
@mock.patch('importlib.import_module')
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._service_task = AsyncMock()
_run(s.handle_request('request'))
s._service_task.mock.assert_called_once_with()
@@ -0,0 +1,421 @@
import sys
import time
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio import exceptions
from engineio import packet
from engineio import payload
if sys.version_info >= (3, 5):
import asyncio
from asyncio import coroutine
from engineio import asyncio_socket
else:
# mock coroutine so that Python 2 doesn't complain
def coroutine(f):
return f
def AsyncMock(*args, **kwargs):
"""Return a mock asynchronous function."""
m = mock.MagicMock(*args, **kwargs)
@coroutine
def mock_coro(*args, **kwargs):
return m(*args, **kwargs)
mock_coro.mock = m
return mock_coro
def _run(coro):
"""Run the given coroutine."""
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()
mock_input.read = AsyncMock()
mock_input.read.mock.return_value = payload
return mock_input
def _get_mock_server(self):
mock_server = mock.Mock()
mock_server.ping_timeout = 0.2
mock_server.ping_interval = 0.2
mock_server.async_handlers = False
mock_server._async = {'asyncio': True,
'create_route': mock.MagicMock(),
'translate_request': mock.MagicMock(),
'make_response': mock.MagicMock(),
'websocket': 'w',
'websocket_class': 'wc'}
mock_server._async['translate_request'].return_value = 'request'
mock_server._async['make_response'].return_value = 'response'
mock_server._trigger_event = AsyncMock()
return mock_server
def test_create(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
self.assertEqual(s.server, mock_server)
self.assertEqual(s.sid, 'sid')
self.assertFalse(s.upgraded)
self.assertFalse(s.closed)
self.assertTrue(hasattr(s.queue, 'get'))
self.assertTrue(hasattr(s.queue, 'put'))
self.assertTrue(hasattr(s.queue, 'task_done'))
self.assertTrue(hasattr(s.queue, 'join'))
def test_empty_poll(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
self.assertRaises(exceptions.QueueEmpty, _run, s.poll())
def test_poll(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
_run(s.send(pkt1))
_run(s.send(pkt2))
self.assertEqual(_run(s.poll()), [pkt1, pkt2])
def test_poll_none(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
_run(s.queue.put(None))
self.assertEqual(_run(s.poll()), [])
def test_ping_pong(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
_run(s.receive(packet.Packet(packet.PING, data='abc')))
r = _run(s.poll())
self.assertEqual(len(r), 1)
self.assertTrue(r[0].encode(), b'3abc')
def test_message_sync_handler(self):
mock_server = self._get_mock_server()
s = asyncio_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)
def test_message_async_handler(self):
mock_server = self._get_mock_server()
s = asyncio_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(
'message', 'sid', 'foo', run_async=True)
def test_invalid_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
self.assertRaises(exceptions.UnknownPacketError, _run,
s.receive(packet.Packet(packet.OPEN)))
def test_timeout(self):
mock_server = self._get_mock_server()
mock_server.ping_interval = -0.1
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.last_ping = time.time() - 1
s.close = AsyncMock()
_run(s.send('packet'))
s.close.mock.assert_called_once_with(wait=False, abort=False)
def test_polling_read(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
_run(s.send(pkt1))
_run(s.send(pkt2))
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
packets = _run(s.handle_get_request(environ))
self.assertEqual(packets, [pkt1, pkt2])
def test_polling_read_error(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
self.assertRaises(exceptions.QueueEmpty, _run,
s.handle_get_request(environ))
def test_polling_write(self):
mock_server = self._get_mock_server()
mock_server.max_http_buffer_size = 1000
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
p = payload.Payload(packets=[pkt1, pkt2]).encode()
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
s.receive = AsyncMock()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo',
'CONTENT_LENGTH': len(p),
'wsgi.input': self._get_read_mock_coro(p)}
_run(s.handle_post_request(environ))
self.assertEqual(s.receive.mock.call_count, 2)
def test_polling_write_too_large(self):
mock_server = self._get_mock_server()
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
p = payload.Payload(packets=[pkt1, pkt2]).encode()
mock_server.max_http_buffer_size = len(p) - 1
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
s.receive = AsyncMock()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo',
'CONTENT_LENGTH': len(p),
'wsgi.input': self._get_read_mock_coro(p)}
self.assertRaises(exceptions.ContentTooLongError, _run,
s.handle_post_request(environ))
def test_upgrade_handshake(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'foo')
s._upgrade_websocket = AsyncMock()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_CONNECTION': 'Foo,Upgrade,Bar',
'HTTP_UPGRADE': 'websocket'}
_run(s.handle_get_request(environ))
s._upgrade_websocket.mock.assert_called_once_with(environ)
def test_upgrade(self):
mock_server = self._get_mock_server()
mock_server._async['websocket'] = mock.MagicMock()
mock_server._async['websocket_class'] = 'WebSocket'
mock_ws = AsyncMock()
mock_server._async['websocket'].WebSocket.return_value = mock_ws
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
environ = "foo"
_run(s._upgrade_websocket(environ))
mock_server._async['websocket'].WebSocket.assert_called_once_with(
s._websocket_handler)
mock_ws.mock.assert_called_once_with(environ)
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.connected = True
s.upgraded = True
environ = "foo"
self.assertRaises(IOError, _run, s._upgrade_websocket(environ))
def test_upgrade_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
_run(s.receive(packet.Packet(packet.UPGRADE)))
r = _run(s.poll())
self.assertEqual(len(r), 1)
self.assertEqual(r[0].encode(), packet.Packet(packet.NOOP).encode())
def test_upgrade_no_probe(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
ws = mock.MagicMock()
ws.wait = AsyncMock()
ws.wait.mock.return_value = packet.Packet(packet.NOOP).encode(
always_bytes=False)
_run(s._websocket_handler(ws))
self.assertFalse(s.upgraded)
def test_upgrade_no_upgrade_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
s.queue.join = AsyncMock(return_value=None)
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
probe = six.text_type('probe')
ws.wait.mock.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.NOOP).encode(always_bytes=False)]
_run(s._websocket_handler(ws))
ws.send.mock.assert_called_once_with(packet.Packet(
packet.PONG, data=probe).encode(always_bytes=False))
self.assertEqual(_run(s.queue.get()).packet_type, packet.NOOP)
self.assertFalse(s.upgraded)
def test_upgrade_not_supported(self):
mock_server = self._get_mock_server()
mock_server._async['websocket'] = None
mock_server._async['websocket_class'] = None
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
environ = "foo"
_run(s._upgrade_websocket(environ))
mock_server._bad_request.assert_called_once_with()
def test_close_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
s.close = AsyncMock()
_run(s.receive(packet.Packet(packet.CLOSE)))
s.close.mock.assert_called_once_with(wait=False, abort=True)
def test_websocket_read_write(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = False
s.queue.join = AsyncMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = AsyncMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], None])
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
_run(s._websocket_handler(ws))
self.assertTrue(s.connected)
self.assertTrue(s.upgraded)
self.assertEqual(mock_server._trigger_event.mock.call_count, 2)
mock_server._trigger_event.mock.assert_has_calls([
mock.call('message', 'sid', 'foo', run_async=False),
mock.call('disconnect', 'sid')])
ws.send.mock.assert_called_with('4bar')
def test_websocket_upgrade_read_write(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
s.queue.join = AsyncMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
probe = six.text_type('probe')
s.poll = AsyncMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE).encode(always_bytes=False),
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
_run(s._websocket_handler(ws))
self.assertTrue(s.upgraded)
self.assertEqual(mock_server._trigger_event.mock.call_count, 2)
mock_server._trigger_event.mock.assert_has_calls([
mock.call('message', 'sid', 'foo', run_async=False),
mock.call('disconnect', 'sid')])
ws.send.mock.assert_called_with('4bar')
def test_websocket_upgrade_with_payload(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = True
s.queue.join = AsyncMock(return_value=None)
probe = six.text_type('probe')
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE, data=b'2').encode(
always_bytes=False)]
_run(s._websocket_handler(ws))
self.assertTrue(s.upgraded)
def test_websocket_read_write_wait_fail(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = False
s.queue.join = AsyncMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = AsyncMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)],
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
RuntimeError]
ws.send.mock.side_effect = [None, RuntimeError]
_run(s._websocket_handler(ws))
self.assertEqual(s.closed, True)
def test_websocket_ignore_invalid_packet(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.connected = False
s.queue.join = AsyncMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = AsyncMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.send = AsyncMock()
ws.wait = AsyncMock()
ws.wait.mock.side_effect = [
packet.Packet(packet.OPEN).encode(always_bytes=False),
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
_run(s._websocket_handler(ws))
self.assertTrue(s.connected)
self.assertEqual(mock_server._trigger_event.mock.call_count, 2)
mock_server._trigger_event.mock.assert_has_calls([
mock.call('message', 'sid', foo, run_async=False),
mock.call('disconnect', 'sid')])
ws.send.mock.assert_called_with('4bar')
def test_send_after_close(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
_run(s.close(wait=False))
self.assertRaises(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')
_run(s.close(wait=False))
self.assertTrue(s.closed)
self.assertEqual(mock_server._trigger_event.mock.call_count, 1)
mock_server._trigger_event.mock.assert_called_once_with('disconnect',
'sid')
_run(s.close())
self.assertEqual(mock_server._trigger_event.mock.call_count, 1)
def test_close_and_wait(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.queue = mock.MagicMock()
s.queue.put = AsyncMock()
s.queue.join = AsyncMock()
_run(s.close(wait=True))
s.queue.join.mock.assert_called_once_with()
def test_close_without_wait(self):
mock_server = self._get_mock_server()
s = asyncio_socket.AsyncSocket(mock_server, 'sid')
s.queue = mock.MagicMock()
s.queue.put = AsyncMock()
s.queue.join = AsyncMock()
_run(s.close(wait=False))
self.assertEqual(s.queue.join.mock.call_count, 0)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,126 @@
import os
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
import engineio
class TestWSGIApp(unittest.TestCase):
def test_wsgi_routing(self):
mock_wsgi_app = mock.MagicMock()
mock_eio_app = 'foo'
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app)
environ = {'PATH_INFO': '/foo'}
start_response = "foo"
m(environ, start_response)
mock_wsgi_app.assert_called_once_with(environ, start_response)
def test_eio_routing(self):
mock_wsgi_app = 'foo'
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app)
environ = {'PATH_INFO': '/engine.io/'}
start_response = "foo"
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_static_files(self):
root_dir = os.path.dirname(__file__)
m = engineio.WSGIApp('foo', None, static_files={
'/': {'content_type': 'text/html',
'filename': root_dir + '/index.html'},
'/foo': {'content_type': 'text/html',
'filename': root_dir + '/index.html'},
})
environ = {'PATH_INFO': '/'}
start_response = mock.MagicMock()
r = m(environ, start_response)
self.assertEqual(r, [b'<html></html>\n'])
start_response.assert_called_once_with(
"200 OK", [('Content-Type', 'text/html')])
def test_404(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app)
environ = {'PATH_INFO': '/foo/bar'}
start_response = mock.MagicMock()
r = m(environ, start_response)
self.assertEqual(r, ['Not Found'])
start_response.assert_called_once_with(
"404 Not Found", [('Content-type', 'text/plain')])
def test_custom_eio_path(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app, engineio_path='foo')
environ = {'PATH_INFO': '/engine.io/'}
start_response = mock.MagicMock()
r = m(environ, start_response)
self.assertEqual(r, ['Not Found'])
start_response.assert_called_once_with(
"404 Not Found", [('Content-type', 'text/plain')])
environ = {'PATH_INFO': '/foo/'}
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_custom_eio_path_slashes(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app,
engineio_path='/foo/')
environ = {'PATH_INFO': '/foo/'}
start_response = mock.MagicMock()
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_custom_eio_path_leading_slash(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app, engineio_path='/foo')
environ = {'PATH_INFO': '/foo/'}
start_response = mock.MagicMock()
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_custom_eio_path_trailing_slash(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
mock_eio_app.handle_request = mock.MagicMock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app, engineio_path='foo/')
environ = {'PATH_INFO': '/foo/'}
start_response = mock.MagicMock()
m(environ, start_response)
mock_eio_app.handle_request.assert_called_once_with(environ,
start_response)
def test_gunicorn_socket(self):
mock_wsgi_app = None
mock_eio_app = mock.Mock()
m = engineio.WSGIApp(mock_eio_app, mock_wsgi_app)
environ = {'gunicorn.socket': 123, 'PATH_INFO': '/foo/bar'}
start_response = mock.MagicMock()
m(environ, start_response)
self.assertIn('eventlet.input', environ)
self.assertEqual(environ['eventlet.input'].get_socket(), 123)
def test_legacy_middleware_class(self):
m = engineio.Middleware('eio', 'wsgi', 'eio_path')
self.assertEqual(m.engineio_app, 'eio')
self.assertEqual(m.wsgi_app, 'wsgi')
self.assertEqual(m.static_files, {})
self.assertEqual(m.engineio_path, 'eio_path')
@@ -0,0 +1,103 @@
import unittest
import six
from engineio import packet
class TestPacket(unittest.TestCase):
def test_encode_default_packet(self):
pkt = packet.Packet()
self.assertEqual(pkt.packet_type, packet.NOOP)
self.assertIsNone(pkt.data)
self.assertFalse(pkt.binary)
self.assertEqual(pkt.encode(), b'6')
def test_decode_default_packet(self):
pkt = packet.Packet(encoded_packet=b'6')
self.assertTrue(pkt.encode(), b'6')
def test_encode_text_packet(self):
data = six.text_type('text')
pkt = packet.Packet(packet.MESSAGE, data=data)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, data)
self.assertFalse(pkt.binary)
self.assertEqual(pkt.encode(), b'4text')
def test_decode_text_packet(self):
pkt = packet.Packet(encoded_packet=b'4text')
self.assertEqual(pkt.encode(), b'4text')
def test_encode_binary_packet(self):
pkt = packet.Packet(packet.MESSAGE, data=b'\x01\x02\x03',
binary=True)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, b'\x01\x02\x03')
self.assertTrue(pkt.binary)
self.assertEqual(pkt.encode(), b'\x04\x01\x02\x03')
def test_encode_binary_bytearray_packet(self):
pkt = packet.Packet(packet.MESSAGE, data=bytearray(b'\x01\x02\x03'),
binary=True)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, b'\x01\x02\x03')
self.assertTrue(pkt.binary)
self.assertEqual(pkt.encode(), b'\x04\x01\x02\x03')
def test_encode_binary_b64_packet(self):
pkt = packet.Packet(packet.MESSAGE, data=b'\x01\x02\x03\x04',
binary=True)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, b'\x01\x02\x03\x04')
self.assertTrue(pkt.binary)
self.assertEqual(pkt.encode(b64=True), b'b4AQIDBA==')
def test_encode_binary_packet_py3(self):
pkt = packet.Packet(packet.MESSAGE, data=b'\x01\x02\x03')
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, b'\x01\x02\x03')
self.assertTrue(pkt.binary)
self.assertEqual(pkt.encode(), b'\x04\x01\x02\x03')
def test_decode_binary_packet(self):
pkt = packet.Packet(encoded_packet=b'\x04\x01\x02\x03')
self.assertTrue(pkt.encode(), b'\x04\x01\x02\x03')
def test_decode_binary_bytearray_packet(self):
pkt = packet.Packet(encoded_packet=bytearray(b'\x04\x01\x02\x03'))
self.assertTrue(pkt.encode(), b'\x04\x01\x02\x03')
def test_decode_binary_b64_packet(self):
pkt = packet.Packet(encoded_packet=b'b4AAEC')
self.assertTrue(pkt.encode(), b'\x04\x01\x02\x03')
def test_encode_json_packet(self):
pkt = packet.Packet(packet.MESSAGE, data={'a': 123, 'b': '456'})
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, {'a': 123, 'b': '456'})
self.assertFalse(pkt.binary)
self.assertIn(pkt.encode(), [b'4{"a":123,"b":"456"}',
b'4{"b":"456","a":123}'])
def test_decode_json_packet(self):
pkt = packet.Packet(encoded_packet=b'4{"a":123,"b":"456"}')
self.assertIn(pkt.encode(), [b'4{"a":123,"b":"456"}',
b'4{"b":"456","a":123}'])
def test_encode_number_packet(self):
pkt = packet.Packet(packet.MESSAGE, data=123)
self.assertEqual(pkt.packet_type, packet.MESSAGE)
self.assertEqual(pkt.data, 123)
self.assertFalse(pkt.binary)
self.assertEqual(pkt.encode(), b'4123')
def test_decode_number_packet(self):
pkt = packet.Packet(encoded_packet=b'4123')
self.assertEqual(pkt.packet_type, packet.MESSAGE)
# integer payloads are parsed as strings, see
# https://github.com/miguelgrinberg/python-engineio/issues/75
# for background on this decision
self.assertEqual(pkt.data, '123')
self.assertFalse(pkt.binary)
self.assertEqual(pkt.encode(), b'4123')
@@ -0,0 +1,57 @@
import unittest
import six
from engineio import packet
from engineio import payload
class TestPayload(unittest.TestCase):
def test_encode_empty_payload(self):
p = payload.Payload()
self.assertEqual(p.packets, [])
self.assertEqual(p.encode(), b'')
def test_decode_empty_payload(self):
p = payload.Payload(encoded_payload=b'')
self.assertEqual(p.encode(), b'')
def test_encode_payload_xhr2(self):
pkt = packet.Packet(packet.MESSAGE, data=six.text_type('abc'))
p = payload.Payload([pkt])
self.assertEqual(p.packets, [pkt])
self.assertEqual(p.encode(), b'\x00\x04\xff4abc')
def test_decode_payload_xhr2(self):
p = payload.Payload(encoded_payload=b'\x00\x04\xff4abc')
self.assertEqual(p.encode(), b'\x00\x04\xff4abc')
def test_encode_payload_xhr_text(self):
pkt = packet.Packet(packet.MESSAGE, data=six.text_type('abc'))
p = payload.Payload([pkt])
self.assertEqual(p.packets, [pkt])
self.assertEqual(p.encode(b64=True), b'4:4abc')
def test_decode_payload_xhr_text(self):
p = payload.Payload(encoded_payload=b'4:4abc')
self.assertEqual(p.encode(), b'\x00\x04\xff4abc')
def test_encode_payload_xhr_binary(self):
pkt = packet.Packet(packet.MESSAGE, data=b'\x00\x01\x02', binary=True)
p = payload.Payload([pkt])
self.assertEqual(p.packets, [pkt])
self.assertEqual(p.encode(b64=True), b'6:b4AAEC')
def test_decode_payload_xhr_binary(self):
p = payload.Payload(encoded_payload=b'6:b4AAEC')
self.assertEqual(p.encode(), b'\x01\x04\xff\x04\x00\x01\x02')
def test_decode_invalid_payload(self):
self.assertRaises(ValueError, payload.Payload,
encoded_payload=b'bad payload')
def test_decode_multi_payload(self):
p = payload.Payload(encoded_payload=b'4:4abc\x00\x04\xff4def')
self.assertEqual(len(p.packets), 2)
self.assertEqual(p.packets[0].data, 'abc')
self.assertEqual(p.packets[1].data, 'def')
@@ -0,0 +1,890 @@
import gzip
import importlib
import json
import logging
import sys
import time
import unittest
import zlib
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio import exceptions
from engineio import packet
from engineio import payload
from engineio import server
original_import_module = importlib.import_module
def _mock_import(module, *args, **kwargs):
if module.startswith('engineio.'):
return original_import_module(module, *args, **kwargs)
return module
class TestServer(unittest.TestCase):
_mock_async = mock.MagicMock()
_mock_async._async = {
'threading': 't',
'thread_class': 'tc',
'queue': 'q',
'queue_class': 'qc',
'websocket': 'w',
'websocket_class': 'wc'
}
def _get_mock_socket(self):
mock_socket = mock.MagicMock()
mock_socket.closed = False
mock_socket.closing = False
mock_socket.upgraded = False
return mock_socket
@classmethod
def setUpClass(cls):
server.Server._default_monitor_clients = False
@classmethod
def tearDownClass(cls):
server.Server._default_monitor_clients = True
def setUp(self):
logging.getLogger('engineio').setLevel(logging.NOTSET)
def tearDown(self):
# restore JSON encoder, in case a test changed it
packet.Packet.json = json
def test_is_asyncio_based(self):
s = server.Server()
self.assertEqual(s.is_asyncio_based(), False)
def test_async_modes(self):
s = server.Server()
self.assertEqual(s.async_modes(), ['eventlet', 'gevent_uwsgi',
'gevent', 'threading'])
def test_create(self):
kwargs = {
'ping_timeout': 1,
'ping_interval': 2,
'max_http_buffer_size': 3,
'allow_upgrades': False,
'http_compression': False,
'compression_threshold': 4,
'cookie': 'foo',
'cors_allowed_origins': ['foo', 'bar', 'baz'],
'cors_credentials': False,
'async_handlers': False}
s = server.Server(**kwargs)
for arg in six.iterkeys(kwargs):
self.assertEqual(getattr(s, arg), kwargs[arg])
def test_create_ignores_kwargs(self):
server.Server(foo='bar') # this should not raise
def test_async_mode_threading(self):
s = server.Server(async_mode='threading')
self.assertEqual(s.async_mode, 'threading')
import threading
try:
import queue
except ImportError:
import Queue as queue
self.assertEqual(s._async['threading'], threading)
self.assertEqual(s._async['thread_class'], 'Thread')
self.assertEqual(s._async['queue'], queue)
self.assertEqual(s._async['queue_class'], 'Queue')
self.assertEqual(s._async['websocket'], None)
self.assertEqual(s._async['websocket_class'], None)
def test_async_mode_eventlet(self):
s = server.Server(async_mode='eventlet')
self.assertEqual(s.async_mode, 'eventlet')
from eventlet.green import threading
from eventlet import queue
from engineio.async_drivers import eventlet as async_eventlet
self.assertEqual(s._async['threading'], threading)
self.assertEqual(s._async['thread_class'], 'Thread')
self.assertEqual(s._async['queue'], queue)
self.assertEqual(s._async['queue_class'], 'Queue')
self.assertEqual(s._async['websocket'], async_eventlet)
self.assertEqual(s._async['websocket_class'], 'WebSocketWSGI')
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent_uwsgi(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['uwsgi'] = mock.MagicMock()
s = server.Server(async_mode='gevent_uwsgi')
self.assertEqual(s.async_mode, 'gevent_uwsgi')
from engineio.async_drivers import gevent_uwsgi as async_gevent_uwsgi
self.assertEqual(s._async['threading'], async_gevent_uwsgi)
self.assertEqual(s._async['thread_class'], 'Thread')
self.assertEqual(s._async['queue'], 'gevent.queue')
self.assertEqual(s._async['queue_class'], 'JoinableQueue')
self.assertEqual(s._async['websocket'], async_gevent_uwsgi)
self.assertEqual(s._async['websocket_class'], 'uWSGIWebSocket')
del sys.modules['gevent']
del sys.modules['uwsgi']
del sys.modules['engineio.async_drivers.gevent_uwsgi']
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent_uwsgi_without_uwsgi(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['uwsgi'] = None
self.assertRaises(ValueError, server.Server,
async_mode='gevent_uwsgi')
del sys.modules['gevent']
del sys.modules['uwsgi']
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent_uwsgi_without_websocket(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['uwsgi'] = mock.MagicMock()
del sys.modules['uwsgi'].websocket_handshake
s = server.Server(async_mode='gevent_uwsgi')
self.assertEqual(s.async_mode, 'gevent_uwsgi')
from engineio.async_drivers import gevent_uwsgi as async_gevent_uwsgi
self.assertEqual(s._async['threading'], async_gevent_uwsgi)
self.assertEqual(s._async['thread_class'], 'Thread')
self.assertEqual(s._async['queue'], 'gevent.queue')
self.assertEqual(s._async['queue_class'], 'JoinableQueue')
self.assertEqual(s._async['websocket'], None)
self.assertEqual(s._async['websocket_class'], None)
del sys.modules['gevent']
del sys.modules['uwsgi']
del sys.modules['engineio.async_drivers.gevent_uwsgi']
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['geventwebsocket'] = 'geventwebsocket'
s = server.Server(async_mode='gevent')
self.assertEqual(s.async_mode, 'gevent')
from engineio.async_drivers import gevent as async_gevent
self.assertEqual(s._async['threading'], async_gevent)
self.assertEqual(s._async['thread_class'], 'Thread')
self.assertEqual(s._async['queue'], 'gevent.queue')
self.assertEqual(s._async['queue_class'], 'JoinableQueue')
self.assertEqual(s._async['websocket'], async_gevent)
self.assertEqual(s._async['websocket_class'], 'WebSocketWSGI')
del sys.modules['gevent']
del sys.modules['geventwebsocket']
del sys.modules['engineio.async_drivers.gevent']
@mock.patch('importlib.import_module', side_effect=_mock_import)
def test_async_mode_gevent_without_websocket(self, import_module):
sys.modules['gevent'] = mock.MagicMock()
sys.modules['geventwebsocket'] = None
s = server.Server(async_mode='gevent')
self.assertEqual(s.async_mode, 'gevent')
from engineio.async_drivers import gevent as async_gevent
self.assertEqual(s._async['threading'], async_gevent)
self.assertEqual(s._async['thread_class'], 'Thread')
self.assertEqual(s._async['queue'], 'gevent.queue')
self.assertEqual(s._async['queue_class'], 'JoinableQueue')
self.assertEqual(s._async['websocket'], None)
self.assertEqual(s._async['websocket_class'], None)
del sys.modules['gevent']
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()
self.assertRaises(ValueError, server.Server, async_mode='aiohttp')
@mock.patch('importlib.import_module', side_effect=[ImportError])
def test_async_mode_invalid(self, import_module):
self.assertRaises(ValueError, server.Server, async_mode='foo')
@mock.patch('importlib.import_module', side_effect=[_mock_async])
def test_async_mode_auto_eventlet(self, import_module):
s = server.Server()
self.assertEqual(s.async_mode, 'eventlet')
@mock.patch('importlib.import_module', side_effect=[ImportError,
_mock_async])
def test_async_mode_auto_gevent_uwsgi(self, import_module):
s = server.Server()
self.assertEqual(s.async_mode, 'gevent_uwsgi')
@mock.patch('importlib.import_module', side_effect=[ImportError,
ImportError,
_mock_async])
def test_async_mode_auto_gevent(self, import_module):
s = server.Server()
self.assertEqual(s.async_mode, 'gevent')
@mock.patch('importlib.import_module', side_effect=[ImportError,
ImportError,
ImportError,
_mock_async])
def test_async_mode_auto_threading(self, import_module):
s = server.Server()
self.assertEqual(s.async_mode, 'threading')
def test_generate_id(self):
s = server.Server()
self.assertNotEqual(s._generate_id(), s._generate_id())
def test_on_event(self):
s = server.Server()
@s.on('connect')
def foo():
pass
s.on('disconnect', foo)
self.assertEqual(s.handlers['connect'], foo)
self.assertEqual(s.handlers['disconnect'], foo)
def test_on_event_invalid(self):
s = server.Server()
self.assertRaises(ValueError, s.on, 'invalid')
def test_trigger_event(self):
s = server.Server()
f = {}
@s.on('connect')
def foo(sid, environ):
return sid + environ
@s.on('message')
def bar(sid, data):
f['bar'] = sid + data
return 'bar'
r = s._trigger_event('connect', 1, 2, run_async=False)
self.assertEqual(r, 3)
r = s._trigger_event('message', 3, 4, run_async=True)
r.join()
self.assertEqual(f['bar'], 7)
r = s._trigger_event('message', 5, 6)
self.assertEqual(r, 'bar')
def test_trigger_event_error(self):
s = server.Server()
@s.on('connect')
def foo(sid, environ):
return 1 / 0
@s.on('message')
def bar(sid, data):
return 1 / 0
r = s._trigger_event('connect', 1, 2, run_async=False)
self.assertEqual(r, False)
r = s._trigger_event('message', 3, 4, run_async=False)
self.assertEqual(r, None)
def test_close_one_socket(self):
s = server.Server()
mock_socket = self._get_mock_socket()
s.sockets['foo'] = mock_socket
s.disconnect('foo')
self.assertEqual(mock_socket.close.call_count, 1)
self.assertNotIn('foo', s.sockets)
def test_close_all_sockets(self):
s = server.Server()
mock_sockets = {}
for sid in ['foo', 'bar', 'baz']:
mock_sockets[sid] = self._get_mock_socket()
s.sockets[sid] = mock_sockets[sid]
s.disconnect()
for socket in six.itervalues(mock_sockets):
self.assertEqual(socket.close.call_count, 1)
self.assertEqual(s.sockets, {})
def test_upgrades(self):
s = server.Server()
s.sockets['foo'] = self._get_mock_socket()
self.assertEqual(s._upgrades('foo', 'polling'), ['websocket'])
self.assertEqual(s._upgrades('foo', 'websocket'), [])
s.sockets['foo'].upgraded = True
self.assertEqual(s._upgrades('foo', 'polling'), [])
self.assertEqual(s._upgrades('foo', 'websocket'), [])
s.allow_upgrades = False
s.sockets['foo'].upgraded = True
self.assertEqual(s._upgrades('foo', 'polling'), [])
self.assertEqual(s._upgrades('foo', 'websocket'), [])
def test_transport(self):
s = server.Server()
s.sockets['foo'] = self._get_mock_socket()
s.sockets['foo'].upgraded = False
s.sockets['bar'] = self._get_mock_socket()
s.sockets['bar'].upgraded = True
self.assertEqual(s.transport('foo'), 'polling')
self.assertEqual(s.transport('bar'), 'websocket')
def test_bad_session(self):
s = server.Server()
s.sockets['foo'] = 'client'
self.assertRaises(KeyError, s._get_socket, 'bar')
def test_closed_socket(self):
s = server.Server()
s.sockets['foo'] = self._get_mock_socket()
s.sockets['foo'].closed = True
self.assertRaises(KeyError, s._get_socket, 'foo')
def test_jsonp_not_supported(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'j=abc'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
def test_connect(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
self.assertEqual(len(s.sockets), 1)
self.assertEqual(start_response.call_count, 1)
self.assertEqual(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
start_response.call_args[0][1])
self.assertEqual(len(r), 1)
packets = payload.Payload(encoded_payload=r[0]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.OPEN)
self.assertIn('upgrades', packets[0].data)
self.assertEqual(packets[0].data['upgrades'], ['websocket'])
self.assertIn('sid', packets[0].data)
def test_connect_no_upgrades(self):
s = server.Server(allow_upgrades=False)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
packets = payload.Payload(encoded_payload=r[0]).packets
self.assertEqual(packets[0].data['upgrades'], [])
def test_connect_b64_with_1(self):
s = server.Server(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'b64=1'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertTrue(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'text/plain; charset=UTF-8'),
start_response.call_args[0][1])
s.send('1', b'\x00\x01\x02', binary=True)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=1'}
r = s.handle_request(environ, start_response)
self.assertEqual(r[0], b'6:b4AAEC')
def test_connect_b64_with_true(self):
s = server.Server(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'b64=true'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertTrue(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'text/plain; charset=UTF-8'),
start_response.call_args[0][1])
s.send('1', b'\x00\x01\x02', binary=True)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=true'}
r = s.handle_request(environ, start_response)
self.assertEqual(r[0], b'6:b4AAEC')
def test_connect_b64_with_0(self):
s = server.Server(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'b64=0'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertTrue(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
start_response.call_args[0][1])
s.send('1', b'\x00\x01\x02', binary=True)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=0'}
r = s.handle_request(environ, start_response)
self.assertEqual(r[0], b'\x01\x04\xff\x04\x00\x01\x02')
def test_connect_b64_with_false(self):
s = server.Server(allow_upgrades=False)
s._generate_id = mock.MagicMock(return_value='1')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'b64=false'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertTrue(start_response.call_args[0][0], '200 OK')
self.assertIn(('Content-Type', 'application/octet-stream'),
start_response.call_args[0][1])
s.send('1', b'\x00\x01\x02', binary=True)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=1&b64=false'}
r = s.handle_request(environ, start_response)
self.assertEqual(r[0], b'\x01\x04\xff\x04\x00\x01\x02')
def test_connect_custom_ping_times(self):
s = server.Server(ping_timeout=123, ping_interval=456)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
packets = payload.Payload(encoded_payload=r[0]).packets
self.assertEqual(packets[0].data['pingTimeout'], 123000)
self.assertEqual(packets[0].data['pingInterval'], 456000)
@mock.patch('engineio.socket.Socket.poll',
side_effect=exceptions.QueueEmpty)
def test_connect_bad_poll(self, poll):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
@mock.patch('engineio.socket.Socket',
return_value=mock.MagicMock(connected=False, closed=False))
def test_connect_transport_websocket(self, Socket):
s = server.Server()
s._generate_id = mock.MagicMock(return_value='123')
environ = {'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=websocket'}
start_response = mock.MagicMock()
# force socket to stay open, so that we can check it later
Socket().closed = False
s.handle_request(environ, start_response)
self.assertEqual(s.sockets['123'].send.call_args[0][0].packet_type,
packet.OPEN)
@mock.patch('engineio.socket.Socket',
return_value=mock.MagicMock(connected=False, closed=False))
def test_connect_transport_websocket_closed(self, Socket):
s = server.Server()
s._generate_id = mock.MagicMock(return_value='123')
environ = {'REQUEST_METHOD': 'GET',
'QUERY_STRING': 'transport=websocket'}
start_response = mock.MagicMock()
def mock_handle(environ, start_response):
s.sockets['123'].closed = True
Socket().handle_get_request = mock_handle
s.handle_request(environ, start_response)
self.assertNotIn('123', s.sockets)
def test_connect_transport_invalid(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'transport=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
def test_connect_cors_headers(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', '*'), headers)
self.assertIn(('Access-Control-Allow-Credentials', 'true'), headers)
def test_connect_cors_allowed_origin(self):
s = server.Server(cors_allowed_origins=['a', 'b'])
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'b'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', 'b'), headers)
def test_connect_cors_not_allowed_origin(self):
s = server.Server(cors_allowed_origins=['a', 'b'])
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'c'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Origin', 'c'), headers)
self.assertNotIn(('Access-Control-Allow-Origin', '*'), headers)
def test_connect_cors_headers_all_origins(self):
s = server.Server(cors_allowed_origins='*')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', '*'), headers)
self.assertIn(('Access-Control-Allow-Credentials', 'true'), headers)
def test_connect_cors_headers_one_origin(self):
s = server.Server(cors_allowed_origins='a')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'a'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Origin', 'a'), headers)
self.assertIn(('Access-Control-Allow-Credentials', 'true'), headers)
def test_connect_cors_headers_one_origin_not_allowed(self):
s = server.Server(cors_allowed_origins='a')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': '',
'HTTP_ORIGIN': 'b'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Origin', 'b'), headers)
self.assertNotIn(('Access-Control-Allow-Origin', '*'), headers)
def test_connect_cors_no_credentials(self):
s = server.Server(cors_credentials=False)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertNotIn(('Access-Control-Allow-Credentials', 'true'), headers)
def test_cors_options(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'OPTIONS', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Methods', 'OPTIONS, GET, POST'),
headers)
def test_cors_request_headers(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET',
'HTTP_ACCESS_CONTROL_REQUEST_HEADERS': 'Foo, Bar'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
headers = start_response.call_args[0][1]
self.assertIn(('Access-Control-Allow-Headers', 'Foo, Bar'), headers)
def test_connect_event(self):
s = server.Server()
s._generate_id = mock.MagicMock(return_value='123')
mock_event = mock.MagicMock()
s.on('connect')(mock_event)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
mock_event.assert_called_once_with('123', environ)
self.assertEqual(len(s.sockets), 1)
def test_connect_event_rejects(self):
s = server.Server()
s._generate_id = mock.MagicMock(return_value='123')
mock_event = mock.MagicMock(return_value=False)
s.on('connect')(mock_event)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(len(s.sockets), 0)
self.assertEqual(start_response.call_args[0][0], '401 UNAUTHORIZED')
def test_method_not_found(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'PUT', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'405 METHOD NOT FOUND')
def test_get_request_with_bad_sid(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
def test_post_request_with_bad_sid(self):
s = server.Server()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
def test_send(self):
s = server.Server()
mock_socket = self._get_mock_socket()
s.sockets['foo'] = mock_socket
s.send('foo', 'hello')
self.assertEqual(mock_socket.send.call_count, 1)
self.assertEqual(mock_socket.send.call_args[0][0].packet_type,
packet.MESSAGE)
self.assertEqual(mock_socket.send.call_args[0][0].data, 'hello')
def test_send_unknown_socket(self):
s = server.Server()
# just ensure no exceptions are raised
s.send('foo', 'hello')
def test_get_request(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'200 OK')
self.assertEqual(len(r), 1)
packets = payload.Payload(encoded_payload=r[0]).packets
self.assertEqual(len(packets), 1)
self.assertEqual(packets[0].packet_type, packet.MESSAGE)
def test_get_request_custom_response(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(side_effect=['resp'])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
self.assertEqual(s.handle_request(environ, start_response), 'resp')
def test_get_request_closes_socket(self):
s = server.Server()
mock_socket = self._get_mock_socket()
def mock_get_request(*args, **kwargs):
mock_socket.closed = True
return 'resp'
mock_socket.handle_get_request = mock_get_request
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
self.assertEqual(s.handle_request(environ, start_response), 'resp')
self.assertNotIn('foo', s.sockets)
def test_get_request_error(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(
side_effect=[exceptions.QueueEmpty])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
self.assertEqual(len(s.sockets), 0)
def test_post_request(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_post_request = mock.MagicMock()
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'200 OK')
def test_post_request_error(self):
s = server.Server()
mock_socket = self._get_mock_socket()
mock_socket.handle_post_request = mock.MagicMock(
side_effect=[exceptions.EngineIOError])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertEqual(start_response.call_args[0][0],
'400 BAD REQUEST')
self.assertNotIn('foo', s.sockets)
@staticmethod
def _gzip_decompress(b):
bytesio = six.BytesIO(b)
with gzip.GzipFile(fileobj=bytesio, mode='r') as gz:
return gz.read()
def test_gzip_compression(self):
s = server.Server(compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip,deflate'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
self.assertIn(('Content-Encoding', 'gzip'),
start_response.call_args[0][1])
self._gzip_decompress(r[0])
def test_deflate_compression(self):
s = server.Server(compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'deflate;q=1,gzip'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
self.assertIn(('Content-Encoding', 'deflate'),
start_response.call_args[0][1])
zlib.decompress(r[0])
def test_gzip_compression_threshold(self):
s = server.Server(compression_threshold=1000)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress, r[0])
def test_compression_disabled(self):
s = server.Server(http_compression=False, compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'gzip'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress, r[0])
def test_compression_unknown(self):
s = server.Server(compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': 'rar'}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress, r[0])
def test_compression_no_encoding(self):
s = server.Server(compression_threshold=0)
mock_socket = self._get_mock_socket()
mock_socket.handle_get_request = mock.MagicMock(return_value=[
packet.Packet(packet.MESSAGE, data='hello')])
s.sockets['foo'] = mock_socket
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_ACCEPT_ENCODING': ''}
start_response = mock.MagicMock()
r = s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Content-Encoding')
self.assertRaises(IOError, self._gzip_decompress, r[0])
def test_cookie(self):
s = server.Server(cookie='sid')
s._generate_id = mock.MagicMock(return_value='123')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
self.assertIn(('Set-Cookie', 'sid=123'),
start_response.call_args[0][1])
def test_no_cookie(self):
s = server.Server(cookie=None)
s._generate_id = mock.MagicMock(return_value='123')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
for header, value in start_response.call_args[0][1]:
self.assertNotEqual(header, 'Set-Cookie')
def test_logger(self):
s = server.Server(logger=False)
self.assertEqual(s.logger.getEffectiveLevel(), logging.ERROR)
s.logger.setLevel(logging.NOTSET)
s = server.Server(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.INFO)
s.logger.setLevel(logging.WARNING)
s = server.Server(logger=True)
self.assertEqual(s.logger.getEffectiveLevel(), logging.WARNING)
s.logger.setLevel(logging.NOTSET)
my_logger = logging.Logger('foo')
s = server.Server(logger=my_logger)
self.assertEqual(s.logger, my_logger)
def test_custom_json(self):
# Warning: this test cannot run in parallel with other tests, as it
# changes the JSON encoding/decoding functions
class CustomJSON(object):
@staticmethod
def dumps(*args, **kwargs):
return '*** encoded ***'
@staticmethod
def loads(*args, **kwargs):
return '+++ decoded +++'
server.Server(json=CustomJSON)
pkt = packet.Packet(packet.MESSAGE, data={'foo': 'bar'})
self.assertEqual(pkt.encode(), b'4*** encoded ***')
pkt2 = packet.Packet(encoded_packet=pkt.encode())
self.assertEqual(pkt2.data, '+++ decoded +++')
# restore the default JSON module
packet.Packet.json = json
def test_background_tasks(self):
flag = {}
def bg_task():
flag['task'] = True
s = server.Server()
task = s.start_background_task(bg_task)
task.join()
self.assertIn('task', flag)
self.assertTrue(flag['task'])
def test_sleep(self):
s = server.Server()
t = time.time()
s.sleep(0.1)
self.assertTrue(time.time() - t > 0.1)
def test_service_task_started(self):
s = server.Server(monitor_clients=True)
s._service_task = mock.MagicMock()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': ''}
start_response = mock.MagicMock()
s.handle_request(environ, start_response)
s._service_task.assert_called_once_with()
@@ -0,0 +1,401 @@
import time
import unittest
import six
if six.PY3:
from unittest import mock
else:
import mock
from engineio import exceptions
from engineio import packet
from engineio import payload
from engineio import socket
class TestSocket(unittest.TestCase):
def setUp(self):
self.bg_tasks = []
def _get_mock_server(self):
mock_server = mock.Mock()
mock_server.ping_timeout = 0.2
mock_server.ping_interval = 0.2
mock_server.async_handlers = True
try:
import queue
except ImportError:
import Queue as queue
import threading
mock_server._async = {'threading': threading,
'thread_class': 'Thread',
'queue': queue,
'queue_class': 'Queue',
'websocket': None}
def bg_task(target, *args, **kwargs):
th = threading.Thread(target=target, args=args, kwargs=kwargs)
self.bg_tasks.append(th)
th.start()
return th
mock_server.start_background_task = bg_task
return mock_server
def _join_bg_tasks(self):
for task in self.bg_tasks:
task.join()
def test_create(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
self.assertEqual(s.server, mock_server)
self.assertEqual(s.sid, 'sid')
self.assertFalse(s.upgraded)
self.assertFalse(s.closed)
self.assertTrue(hasattr(s.queue, 'get'))
self.assertTrue(hasattr(s.queue, 'put'))
self.assertTrue(hasattr(s.queue, 'task_done'))
self.assertTrue(hasattr(s.queue, 'join'))
def test_empty_poll(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
self.assertRaises(exceptions.QueueEmpty, s.poll)
def test_poll(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
s.send(pkt1)
s.send(pkt2)
self.assertEqual(s.poll(), [pkt1, pkt2])
def test_ping_pong(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.receive(packet.Packet(packet.PING, data='abc'))
r = s.poll()
self.assertEqual(len(r), 1)
self.assertTrue(r[0].encode(), b'3abc')
def test_message_async_handler(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.receive(packet.Packet(packet.MESSAGE, data='foo'))
mock_server._trigger_event.assert_called_once_with('message', 'sid',
'foo',
run_async=True)
def test_message_sync_handler(self):
mock_server = self._get_mock_server()
mock_server.async_handlers = False
s = socket.Socket(mock_server, 'sid')
s.receive(packet.Packet(packet.MESSAGE, data='foo'))
mock_server._trigger_event.assert_called_once_with('message', 'sid',
'foo',
run_async=False)
def test_invalid_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
self.assertRaises(exceptions.UnknownPacketError, s.receive,
packet.Packet(packet.OPEN))
def test_timeout(self):
mock_server = self._get_mock_server()
mock_server.ping_interval = -0.1
s = socket.Socket(mock_server, 'sid')
s.last_ping = time.time() - 1
s.close = mock.MagicMock()
s.send('packet')
s.close.assert_called_once_with(wait=False, abort=False)
def test_polling_read(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'foo')
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
s.send(pkt1)
s.send(pkt2)
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
packets = s.handle_get_request(environ, start_response)
self.assertEqual(packets, [pkt1, pkt2])
def test_polling_read_error(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'foo')
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo'}
start_response = mock.MagicMock()
self.assertRaises(exceptions.QueueEmpty, s.handle_get_request, environ,
start_response)
def test_polling_write(self):
mock_server = self._get_mock_server()
mock_server.max_http_buffer_size = 1000
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
p = payload.Payload(packets=[pkt1, pkt2]).encode()
s = socket.Socket(mock_server, 'foo')
s.receive = mock.MagicMock()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo',
'CONTENT_LENGTH': len(p), 'wsgi.input': six.BytesIO(p)}
s.handle_post_request(environ)
self.assertEqual(s.receive.call_count, 2)
def test_polling_write_too_large(self):
mock_server = self._get_mock_server()
pkt1 = packet.Packet(packet.MESSAGE, data='hello')
pkt2 = packet.Packet(packet.MESSAGE, data='bye')
p = payload.Payload(packets=[pkt1, pkt2]).encode()
mock_server.max_http_buffer_size = len(p) - 1
s = socket.Socket(mock_server, 'foo')
s.receive = mock.MagicMock()
environ = {'REQUEST_METHOD': 'POST', 'QUERY_STRING': 'sid=foo',
'CONTENT_LENGTH': len(p), 'wsgi.input': six.BytesIO(p)}
self.assertRaises(exceptions.ContentTooLongError,
s.handle_post_request, environ)
def test_upgrade_handshake(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'foo')
s._upgrade_websocket = mock.MagicMock()
environ = {'REQUEST_METHOD': 'GET', 'QUERY_STRING': 'sid=foo',
'HTTP_CONNECTION': 'Foo,Upgrade,Bar',
'HTTP_UPGRADE': 'websocket'}
start_response = mock.MagicMock()
s.handle_get_request(environ, start_response)
s._upgrade_websocket.assert_called_once_with(environ, start_response)
def test_upgrade(self):
mock_server = self._get_mock_server()
mock_server._async['websocket'] = mock.MagicMock()
mock_server._async['websocket_class'] = 'WebSocket'
mock_ws = mock.MagicMock()
mock_server._async['websocket'].WebSocket.return_value = mock_ws
s = socket.Socket(mock_server, 'sid')
s.connected = True
environ = "foo"
start_response = "bar"
s._upgrade_websocket(environ, start_response)
mock_server._async['websocket'].WebSocket.assert_called_once_with(
s._websocket_handler)
mock_ws.assert_called_once_with(environ, start_response)
def test_upgrade_twice(self):
mock_server = self._get_mock_server()
mock_server._async['websocket'] = mock.MagicMock()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.upgraded = True
environ = "foo"
start_response = "bar"
self.assertRaises(IOError, s._upgrade_websocket,
environ, start_response)
def test_upgrade_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.receive(packet.Packet(packet.UPGRADE))
r = s.poll()
self.assertEqual(len(r), 1)
self.assertEqual(r[0].encode(), packet.Packet(packet.NOOP).encode())
def test_upgrade_no_probe(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
ws = mock.MagicMock()
ws.wait.return_value = packet.Packet(packet.NOOP).encode(
always_bytes=False)
s._websocket_handler(ws)
self.assertFalse(s.upgraded)
def test_upgrade_no_upgrade_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.queue.join = mock.MagicMock(return_value=None)
ws = mock.MagicMock()
probe = six.text_type('probe')
ws.wait.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.NOOP).encode(always_bytes=False)]
s._websocket_handler(ws)
ws.send.assert_called_once_with(packet.Packet(
packet.PONG, data=probe).encode(always_bytes=False))
self.assertEqual(s.queue.get().packet_type, packet.NOOP)
self.assertFalse(s.upgraded)
def test_close_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.close = mock.MagicMock()
s.receive(packet.Packet(packet.CLOSE))
s.close.assert_called_once_with(wait=False, abort=True)
def test_invalid_packet_type(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
pkt = packet.Packet(packet_type=99)
self.assertRaises(exceptions.UnknownPacketError, s.receive, pkt)
def test_upgrade_not_supported(self):
mock_server = self._get_mock_server()
mock_server._async['websocket'] = None
mock_server._async['websocket_class'] = None
s = socket.Socket(mock_server, 'sid')
s.connected = True
environ = "foo"
start_response = "bar"
s._upgrade_websocket(environ, start_response)
mock_server._bad_request.assert_called_once_with()
def test_websocket_read_write(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = False
s.queue.join = mock.MagicMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = mock.MagicMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertTrue(s.connected)
self.assertTrue(s.upgraded)
self.assertEqual(mock_server._trigger_event.call_count, 2)
mock_server._trigger_event.assert_has_calls([
mock.call('message', 'sid', 'foo', run_async=True),
mock.call('disconnect', 'sid', run_async=False)])
ws.send.assert_called_with('4bar')
def test_websocket_upgrade_read_write(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.queue.join = mock.MagicMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
probe = six.text_type('probe')
s.poll = mock.MagicMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE).encode(always_bytes=False),
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertTrue(s.upgraded)
self.assertEqual(mock_server._trigger_event.call_count, 2)
mock_server._trigger_event.assert_has_calls([
mock.call('message', 'sid', 'foo', run_async=True),
mock.call('disconnect', 'sid', run_async=False)])
ws.send.assert_called_with('4bar')
def test_websocket_upgrade_with_payload(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = True
s.queue.join = mock.MagicMock(return_value=None)
probe = six.text_type('probe')
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.PING, data=probe).encode(
always_bytes=False),
packet.Packet(packet.UPGRADE, data=b'2').encode(
always_bytes=False)]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertTrue(s.upgraded)
def test_websocket_read_write_wait_fail(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = False
s.queue.join = mock.MagicMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = mock.MagicMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)],
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
RuntimeError]
ws.send.side_effect = [None, RuntimeError]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertEqual(s.closed, True)
def test_websocket_ignore_invalid_packet(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.connected = False
s.queue.join = mock.MagicMock(return_value=None)
foo = six.text_type('foo')
bar = six.text_type('bar')
s.poll = mock.MagicMock(side_effect=[
[packet.Packet(packet.MESSAGE, data=bar)], exceptions.QueueEmpty])
ws = mock.MagicMock()
ws.wait.side_effect = [
packet.Packet(packet.OPEN).encode(always_bytes=False),
packet.Packet(packet.MESSAGE, data=foo).encode(
always_bytes=False),
None]
s._websocket_handler(ws)
self._join_bg_tasks()
self.assertTrue(s.connected)
self.assertEqual(mock_server._trigger_event.call_count, 2)
mock_server._trigger_event.assert_has_calls([
mock.call('message', 'sid', foo, run_async=True),
mock.call('disconnect', 'sid', run_async=False)])
ws.send.assert_called_with('4bar')
def test_send_after_close(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.close(wait=False)
self.assertRaises(exceptions.SocketIsClosedError, s.send,
packet.Packet(packet.NOOP))
def test_close_after_close(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.close(wait=False)
self.assertTrue(s.closed)
self.assertEqual(mock_server._trigger_event.call_count, 1)
mock_server._trigger_event.assert_called_once_with('disconnect', 'sid',
run_async=False)
s.close()
self.assertEqual(mock_server._trigger_event.call_count, 1)
def test_close_and_wait(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.queue = mock.MagicMock()
s.close(wait=True)
s.queue.join.assert_called_once_with()
def test_close_without_wait(self):
mock_server = self._get_mock_server()
s = socket.Socket(mock_server, 'sid')
s.queue = mock.MagicMock()
s.close(wait=False)
self.assertEqual(s.queue.join.call_count, 0)