python-engineio 4.7.1__py3-none-any.whl → 4.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- engineio/__init__.py +8 -18
- engineio/{asyncio_client.py → async_client.py} +8 -9
- engineio/async_drivers/asgi.py +6 -1
- engineio/async_drivers/gevent_uwsgi.py +1 -0
- engineio/async_drivers/sanic.py +2 -0
- engineio/{asyncio_server.py → async_server.py} +4 -4
- engineio/{asyncio_socket.py → async_socket.py} +10 -10
- engineio/base_client.py +146 -0
- engineio/base_server.py +338 -0
- engineio/base_socket.py +15 -0
- engineio/client.py +7 -143
- engineio/server.py +3 -333
- engineio/socket.py +9 -22
- {python_engineio-4.7.1.dist-info → python_engineio-4.8.1.dist-info}/METADATA +5 -6
- python_engineio-4.8.1.dist-info/RECORD +31 -0
- {python_engineio-4.7.1.dist-info → python_engineio-4.8.1.dist-info}/WHEEL +1 -1
- python_engineio-4.7.1.dist-info/RECORD +0 -28
- {python_engineio-4.7.1.dist-info → python_engineio-4.8.1.dist-info}/LICENSE +0 -0
- {python_engineio-4.7.1.dist-info → python_engineio-4.8.1.dist-info}/top_level.txt +0 -0
engineio/__init__.py
CHANGED
|
@@ -1,23 +1,13 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
|
|
3
1
|
from .client import Client
|
|
4
2
|
from .middleware import WSGIApp, Middleware
|
|
5
3
|
from .server import Server
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
except ImportError:
|
|
13
|
-
get_tornado_handler = None
|
|
14
|
-
else: # pragma: no cover
|
|
15
|
-
AsyncServer = None
|
|
16
|
-
AsyncClient = None
|
|
4
|
+
from .async_server import AsyncServer
|
|
5
|
+
from .async_client import AsyncClient
|
|
6
|
+
from .async_drivers.asgi import ASGIApp
|
|
7
|
+
try:
|
|
8
|
+
from .async_drivers.tornado import get_tornado_handler
|
|
9
|
+
except ImportError: # pragma: no cover
|
|
17
10
|
get_tornado_handler = None
|
|
18
|
-
ASGIApp = None
|
|
19
11
|
|
|
20
|
-
__all__ = ['Server', 'WSGIApp', 'Middleware', 'Client'
|
|
21
|
-
|
|
22
|
-
__all__ += ['AsyncServer', 'ASGIApp', 'get_tornado_handler',
|
|
23
|
-
'AsyncClient']
|
|
12
|
+
__all__ = ['Server', 'WSGIApp', 'Middleware', 'Client',
|
|
13
|
+
'AsyncServer', 'ASGIApp', 'get_tornado_handler', 'AsyncClient']
|
|
@@ -8,7 +8,7 @@ try:
|
|
|
8
8
|
except ImportError: # pragma: no cover
|
|
9
9
|
aiohttp = None
|
|
10
10
|
|
|
11
|
-
from . import
|
|
11
|
+
from . import base_client
|
|
12
12
|
from . import exceptions
|
|
13
13
|
from . import packet
|
|
14
14
|
from . import payload
|
|
@@ -22,7 +22,7 @@ def async_signal_handler():
|
|
|
22
22
|
Disconnect all active async clients.
|
|
23
23
|
"""
|
|
24
24
|
async def _handler(): # pragma: no cover
|
|
25
|
-
for c in
|
|
25
|
+
for c in base_client.connected_clients[:]:
|
|
26
26
|
if c.is_asyncio_based():
|
|
27
27
|
await c.disconnect()
|
|
28
28
|
|
|
@@ -37,7 +37,7 @@ def async_signal_handler():
|
|
|
37
37
|
asyncio.ensure_future(_handler())
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
class AsyncClient(
|
|
40
|
+
class AsyncClient(base_client.BaseClient):
|
|
41
41
|
"""An Engine.IO client for asyncio.
|
|
42
42
|
|
|
43
43
|
This class implements a fully compliant Engine.IO web client with support
|
|
@@ -71,7 +71,6 @@ class AsyncClient(client.Client):
|
|
|
71
71
|
arguments passed to
|
|
72
72
|
``aiohttp.ws_connect()``.
|
|
73
73
|
"""
|
|
74
|
-
|
|
75
74
|
def is_asyncio_based(self):
|
|
76
75
|
return True
|
|
77
76
|
|
|
@@ -164,7 +163,7 @@ class AsyncClient(client.Client):
|
|
|
164
163
|
await self.read_loop_task
|
|
165
164
|
self.state = 'disconnected'
|
|
166
165
|
try:
|
|
167
|
-
|
|
166
|
+
base_client.connected_clients.remove(self)
|
|
168
167
|
except ValueError: # pragma: no cover
|
|
169
168
|
pass
|
|
170
169
|
await self._reset()
|
|
@@ -262,7 +261,7 @@ class AsyncClient(client.Client):
|
|
|
262
261
|
self.base_url += '&sid=' + self.sid
|
|
263
262
|
|
|
264
263
|
self.state = 'connected'
|
|
265
|
-
|
|
264
|
+
base_client.connected_clients.append(self)
|
|
266
265
|
await self._trigger_event('connect', run_async=False)
|
|
267
266
|
|
|
268
267
|
for pkt in p.packets[1:]:
|
|
@@ -384,7 +383,7 @@ class AsyncClient(client.Client):
|
|
|
384
383
|
self.current_transport = 'websocket'
|
|
385
384
|
|
|
386
385
|
self.state = 'connected'
|
|
387
|
-
|
|
386
|
+
base_client.connected_clients.append(self)
|
|
388
387
|
await self._trigger_event('connect', run_async=False)
|
|
389
388
|
|
|
390
389
|
self.ws = ws
|
|
@@ -515,7 +514,7 @@ class AsyncClient(client.Client):
|
|
|
515
514
|
if self.state == 'connected':
|
|
516
515
|
await self._trigger_event('disconnect', run_async=False)
|
|
517
516
|
try:
|
|
518
|
-
|
|
517
|
+
base_client.connected_clients.remove(self)
|
|
519
518
|
except ValueError: # pragma: no cover
|
|
520
519
|
pass
|
|
521
520
|
await self._reset()
|
|
@@ -566,7 +565,7 @@ class AsyncClient(client.Client):
|
|
|
566
565
|
if self.state == 'connected':
|
|
567
566
|
await self._trigger_event('disconnect', run_async=False)
|
|
568
567
|
try:
|
|
569
|
-
|
|
568
|
+
base_client.connected_clients.remove(self)
|
|
570
569
|
except ValueError: # pragma: no cover
|
|
571
570
|
pass
|
|
572
571
|
await self._reset()
|
engineio/async_drivers/asgi.py
CHANGED
|
@@ -233,9 +233,14 @@ class WebSocket(object): # pragma: no cover
|
|
|
233
233
|
self.asgi_send = environ['asgi.send']
|
|
234
234
|
await self.asgi_send({'type': 'websocket.accept'})
|
|
235
235
|
await self.handler(self)
|
|
236
|
+
return '' # send nothing as response
|
|
236
237
|
|
|
237
238
|
async def close(self):
|
|
238
|
-
|
|
239
|
+
try:
|
|
240
|
+
await self.asgi_send({'type': 'websocket.close'})
|
|
241
|
+
except Exception:
|
|
242
|
+
# if the socket is already close we don't care
|
|
243
|
+
pass
|
|
239
244
|
|
|
240
245
|
async def send(self, message):
|
|
241
246
|
msg_bytes = None
|
engineio/async_drivers/sanic.py
CHANGED
|
@@ -114,6 +114,7 @@ class WebSocket(object): # pragma: no cover
|
|
|
114
114
|
"""
|
|
115
115
|
def __init__(self, handler, server):
|
|
116
116
|
self.handler = handler
|
|
117
|
+
self.server = server
|
|
117
118
|
self._sock = None
|
|
118
119
|
|
|
119
120
|
async def __call__(self, environ):
|
|
@@ -123,6 +124,7 @@ class WebSocket(object): # pragma: no cover
|
|
|
123
124
|
|
|
124
125
|
self.environ = environ
|
|
125
126
|
await self.handler(self)
|
|
127
|
+
return self.server._ok()
|
|
126
128
|
|
|
127
129
|
async def close(self):
|
|
128
130
|
await self._sock.close()
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import urllib
|
|
3
3
|
|
|
4
|
+
from . import base_server
|
|
4
5
|
from . import exceptions
|
|
5
6
|
from . import packet
|
|
6
|
-
from . import
|
|
7
|
-
from . import asyncio_socket
|
|
7
|
+
from . import async_socket
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class AsyncServer(
|
|
10
|
+
class AsyncServer(base_server.BaseServer):
|
|
11
11
|
"""An Engine.IO server for asyncio.
|
|
12
12
|
|
|
13
13
|
This class implements a fully compliant Engine.IO web server with support
|
|
@@ -418,7 +418,7 @@ class AsyncServer(server.Server):
|
|
|
418
418
|
self._service_task)
|
|
419
419
|
|
|
420
420
|
sid = self.generate_id()
|
|
421
|
-
s =
|
|
421
|
+
s = async_socket.AsyncSocket(self, sid)
|
|
422
422
|
self.sockets[sid] = s
|
|
423
423
|
|
|
424
424
|
pkt = packet.Packet(
|
|
@@ -2,13 +2,13 @@ import asyncio
|
|
|
2
2
|
import sys
|
|
3
3
|
import time
|
|
4
4
|
|
|
5
|
+
from . import base_socket
|
|
5
6
|
from . import exceptions
|
|
6
7
|
from . import packet
|
|
7
8
|
from . import payload
|
|
8
|
-
from . import socket
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class AsyncSocket(
|
|
11
|
+
class AsyncSocket(base_socket.BaseSocket):
|
|
12
12
|
async def poll(self):
|
|
13
13
|
"""Wait for packets to send to the client."""
|
|
14
14
|
try:
|
|
@@ -122,14 +122,14 @@ class AsyncSocket(socket.Socket):
|
|
|
122
122
|
await self.queue.join()
|
|
123
123
|
|
|
124
124
|
def schedule_ping(self):
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
125
|
+
self.server.start_background_task(self._send_ping)
|
|
126
|
+
|
|
127
|
+
async def _send_ping(self):
|
|
128
|
+
self.last_ping = None
|
|
129
|
+
await asyncio.sleep(self.server.ping_interval)
|
|
130
|
+
if not self.closing and not self.closed:
|
|
131
|
+
self.last_ping = time.time()
|
|
132
|
+
await self.send(packet.Packet(packet.PING))
|
|
133
133
|
|
|
134
134
|
async def _upgrade_websocket(self, environ):
|
|
135
135
|
"""Upgrade the connection from polling to websocket."""
|
engineio/base_client.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import signal
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
import urllib
|
|
6
|
+
from . import packet
|
|
7
|
+
|
|
8
|
+
default_logger = logging.getLogger('engineio.client')
|
|
9
|
+
connected_clients = []
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def signal_handler(sig, frame):
|
|
13
|
+
"""SIGINT handler.
|
|
14
|
+
|
|
15
|
+
Disconnect all active clients and then invoke the original signal handler.
|
|
16
|
+
"""
|
|
17
|
+
for client in connected_clients[:]:
|
|
18
|
+
if not client.is_asyncio_based():
|
|
19
|
+
client.disconnect()
|
|
20
|
+
if callable(original_signal_handler):
|
|
21
|
+
return original_signal_handler(sig, frame)
|
|
22
|
+
else: # pragma: no cover
|
|
23
|
+
# Handle case where no original SIGINT handler was present.
|
|
24
|
+
return signal.default_int_handler(sig, frame)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
original_signal_handler = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class BaseClient:
|
|
31
|
+
event_names = ['connect', 'disconnect', 'message']
|
|
32
|
+
|
|
33
|
+
def __init__(self, logger=False, json=None, request_timeout=5,
|
|
34
|
+
http_session=None, ssl_verify=True, handle_sigint=True,
|
|
35
|
+
websocket_extra_options=None):
|
|
36
|
+
global original_signal_handler
|
|
37
|
+
if handle_sigint and original_signal_handler is None and \
|
|
38
|
+
threading.current_thread() == threading.main_thread():
|
|
39
|
+
original_signal_handler = signal.signal(signal.SIGINT,
|
|
40
|
+
signal_handler)
|
|
41
|
+
self.handlers = {}
|
|
42
|
+
self.base_url = None
|
|
43
|
+
self.transports = None
|
|
44
|
+
self.current_transport = None
|
|
45
|
+
self.sid = None
|
|
46
|
+
self.upgrades = None
|
|
47
|
+
self.ping_interval = None
|
|
48
|
+
self.ping_timeout = None
|
|
49
|
+
self.http = http_session
|
|
50
|
+
self.external_http = http_session is not None
|
|
51
|
+
self.handle_sigint = handle_sigint
|
|
52
|
+
self.ws = None
|
|
53
|
+
self.read_loop_task = None
|
|
54
|
+
self.write_loop_task = None
|
|
55
|
+
self.queue = None
|
|
56
|
+
self.state = 'disconnected'
|
|
57
|
+
self.ssl_verify = ssl_verify
|
|
58
|
+
self.websocket_extra_options = websocket_extra_options or {}
|
|
59
|
+
|
|
60
|
+
if json is not None:
|
|
61
|
+
packet.Packet.json = json
|
|
62
|
+
if not isinstance(logger, bool):
|
|
63
|
+
self.logger = logger
|
|
64
|
+
else:
|
|
65
|
+
self.logger = default_logger
|
|
66
|
+
if self.logger.level == logging.NOTSET:
|
|
67
|
+
if logger:
|
|
68
|
+
self.logger.setLevel(logging.INFO)
|
|
69
|
+
else:
|
|
70
|
+
self.logger.setLevel(logging.ERROR)
|
|
71
|
+
self.logger.addHandler(logging.StreamHandler())
|
|
72
|
+
|
|
73
|
+
self.request_timeout = request_timeout
|
|
74
|
+
|
|
75
|
+
def is_asyncio_based(self):
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
def on(self, event, handler=None):
|
|
79
|
+
"""Register an event handler.
|
|
80
|
+
|
|
81
|
+
:param event: The event name. Can be ``'connect'``, ``'message'`` or
|
|
82
|
+
``'disconnect'``.
|
|
83
|
+
:param handler: The function that should be invoked to handle the
|
|
84
|
+
event. When this parameter is not given, the method
|
|
85
|
+
acts as a decorator for the handler function.
|
|
86
|
+
|
|
87
|
+
Example usage::
|
|
88
|
+
|
|
89
|
+
# as a decorator:
|
|
90
|
+
@eio.on('connect')
|
|
91
|
+
def connect_handler():
|
|
92
|
+
print('Connection request')
|
|
93
|
+
|
|
94
|
+
# as a method:
|
|
95
|
+
def message_handler(msg):
|
|
96
|
+
print('Received message: ', msg)
|
|
97
|
+
eio.send('response')
|
|
98
|
+
eio.on('message', message_handler)
|
|
99
|
+
"""
|
|
100
|
+
if event not in self.event_names:
|
|
101
|
+
raise ValueError('Invalid event')
|
|
102
|
+
|
|
103
|
+
def set_handler(handler):
|
|
104
|
+
self.handlers[event] = handler
|
|
105
|
+
return handler
|
|
106
|
+
|
|
107
|
+
if handler is None:
|
|
108
|
+
return set_handler
|
|
109
|
+
set_handler(handler)
|
|
110
|
+
|
|
111
|
+
def transport(self):
|
|
112
|
+
"""Return the name of the transport currently in use.
|
|
113
|
+
|
|
114
|
+
The possible values returned by this function are ``'polling'`` and
|
|
115
|
+
``'websocket'``.
|
|
116
|
+
"""
|
|
117
|
+
return self.current_transport
|
|
118
|
+
|
|
119
|
+
def _reset(self):
|
|
120
|
+
self.state = 'disconnected'
|
|
121
|
+
self.sid = None
|
|
122
|
+
|
|
123
|
+
def _get_engineio_url(self, url, engineio_path, transport):
|
|
124
|
+
"""Generate the Engine.IO connection URL."""
|
|
125
|
+
engineio_path = engineio_path.strip('/')
|
|
126
|
+
parsed_url = urllib.parse.urlparse(url)
|
|
127
|
+
|
|
128
|
+
if transport == 'polling':
|
|
129
|
+
scheme = 'http'
|
|
130
|
+
elif transport == 'websocket':
|
|
131
|
+
scheme = 'ws'
|
|
132
|
+
else: # pragma: no cover
|
|
133
|
+
raise ValueError('invalid transport')
|
|
134
|
+
if parsed_url.scheme in ['https', 'wss']:
|
|
135
|
+
scheme += 's'
|
|
136
|
+
|
|
137
|
+
return ('{scheme}://{netloc}/{path}/?{query}'
|
|
138
|
+
'{sep}transport={transport}&EIO=4').format(
|
|
139
|
+
scheme=scheme, netloc=parsed_url.netloc,
|
|
140
|
+
path=engineio_path, query=parsed_url.query,
|
|
141
|
+
sep='&' if parsed_url.query else '',
|
|
142
|
+
transport=transport)
|
|
143
|
+
|
|
144
|
+
def _get_url_timestamp(self):
|
|
145
|
+
"""Generate the Engine.IO query string timestamp."""
|
|
146
|
+
return '&t=' + str(time.time())
|