wslink 2.1.3__tar.gz → 2.2.1__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {wslink-2.1.3 → wslink-2.2.1}/PKG-INFO +1 -1
- {wslink-2.1.3 → wslink-2.2.1}/setup.cfg +1 -1
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/__init__.py +8 -1
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/protocol.py +62 -46
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/publish.py +3 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/websocket.py +53 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink.egg-info/PKG-INFO +1 -1
- {wslink-2.1.3 → wslink-2.2.1}/MANIFEST.in +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/README.rst +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/setup.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/LICENSE +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/__init__.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/aiohttp/__init__.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/aiohttp/launcher.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/aiohttp/relay.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/generic/__init__.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/generic/core.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/jupyter/__init__.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/jupyter/core.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/tornado/__init__.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/backends/tornado/core.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/chunking.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/launcher.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/relay.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/server.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/ssl_context.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink/uri.py +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink.egg-info/SOURCES.txt +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink.egg-info/dependency_links.txt +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink.egg-info/requires.txt +0 -0
- {wslink-2.1.3 → wslink-2.2.1}/src/wslink.egg-info/top_level.txt +0 -0
@@ -54,7 +54,7 @@ def schedule_callback(delay, callback, *args, **kwargs):
|
|
54
54
|
return loop.call_later(delay, functools.partial(callback, *args, **kwargs))
|
55
55
|
|
56
56
|
|
57
|
-
def schedule_coroutine(delay, coro_func, *args, **kwargs):
|
57
|
+
def schedule_coroutine(delay, coro_func, *args, done_callback=None, **kwargs):
|
58
58
|
"""
|
59
59
|
Creates a coroutine out of the provided coroutine function coro_func and
|
60
60
|
the provided args and kwargs, then schedules the coroutine to be called
|
@@ -73,4 +73,11 @@ def schedule_coroutine(delay, coro_func, *args, **kwargs):
|
|
73
73
|
# See method above for comment on "get_event_loop()" vs "get_running_loop()".
|
74
74
|
loop = asyncio.get_event_loop()
|
75
75
|
coro_partial = functools.partial(coro_func, *args, **kwargs)
|
76
|
+
if done_callback is not None:
|
77
|
+
return loop.call_later(
|
78
|
+
delay,
|
79
|
+
lambda: asyncio.ensure_future(coro_partial()).add_done_callback(
|
80
|
+
done_callback
|
81
|
+
),
|
82
|
+
)
|
76
83
|
return loop.call_later(delay, lambda: asyncio.ensure_future(coro_partial()))
|
@@ -152,6 +152,7 @@ class WslinkHandler(object):
|
|
152
152
|
self.attachment_atomic = asyncio.Lock()
|
153
153
|
self.pub_manager = PublishManager()
|
154
154
|
self.unchunkers = {}
|
155
|
+
self.network_monitor = protocol.network_monitor
|
155
156
|
|
156
157
|
# Build the rpc method dictionary, assuming we were given a serverprotocol
|
157
158
|
if self.getServerProtocol():
|
@@ -257,7 +258,8 @@ class WslinkHandler(object):
|
|
257
258
|
|
258
259
|
full_message = self.unchunkers[client_id].process_chunk(msg.data)
|
259
260
|
if full_message is not None:
|
260
|
-
|
261
|
+
with self.network_monitor:
|
262
|
+
await self.onCompleteMessage(full_message, client_id)
|
261
263
|
|
262
264
|
async def onCompleteMessage(self, rpc, client_id):
|
263
265
|
logger.debug("wslink incoming msg %s", self.payloadWithSecretStripped(rpc))
|
@@ -282,23 +284,25 @@ class WslinkHandler(object):
|
|
282
284
|
|
283
285
|
# Prevent any further processing if token is not valid
|
284
286
|
if not self.isClientAuthenticated(client_id):
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
287
|
+
with self.network_monitor:
|
288
|
+
await self.sendWrappedError(
|
289
|
+
rpcid,
|
290
|
+
AUTHENTICATION_ERROR,
|
291
|
+
"Unauthorized: Skip message processing",
|
292
|
+
client_id=client_id,
|
293
|
+
)
|
291
294
|
return
|
292
295
|
|
293
296
|
# No matching method found
|
294
297
|
if not methodName in self.functionMap:
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
298
|
+
with self.network_monitor:
|
299
|
+
await self.sendWrappedError(
|
300
|
+
rpcid,
|
301
|
+
METHOD_NOT_FOUND,
|
302
|
+
"Unregistered method called",
|
303
|
+
methodName,
|
304
|
+
client_id=client_id,
|
305
|
+
)
|
302
306
|
return
|
303
307
|
|
304
308
|
obj, func = self.functionMap[methodName]
|
@@ -308,31 +312,34 @@ class WslinkHandler(object):
|
|
308
312
|
self.web_app.last_active_client_id = client_id
|
309
313
|
results = func(*args, **kwargs)
|
310
314
|
if inspect.isawaitable(results):
|
311
|
-
|
315
|
+
with self.network_monitor:
|
316
|
+
results = await results
|
312
317
|
|
313
318
|
if self.connections[client_id].closed:
|
314
319
|
# Connection was closed during RPC call.
|
315
320
|
return
|
316
321
|
|
317
|
-
|
318
|
-
|
319
|
-
|
322
|
+
with self.network_monitor:
|
323
|
+
await self.sendWrappedMessage(
|
324
|
+
rpcid, results, method=methodName, client_id=client_id
|
325
|
+
)
|
320
326
|
except Exception as e_inst:
|
321
327
|
captured_trace = traceback.format_exc()
|
322
328
|
logger.error("Exception raised")
|
323
329
|
logger.error(repr(e_inst))
|
324
330
|
logger.error(captured_trace)
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
331
|
+
with self.network_monitor:
|
332
|
+
await self.sendWrappedError(
|
333
|
+
rpcid,
|
334
|
+
EXCEPTION_ERROR,
|
335
|
+
"Exception raised",
|
336
|
+
{
|
337
|
+
"method": methodName,
|
338
|
+
"exception": repr(e_inst),
|
339
|
+
"trace": captured_trace,
|
340
|
+
},
|
341
|
+
client_id=client_id,
|
342
|
+
)
|
336
343
|
|
337
344
|
def payloadWithSecretStripped(self, payload):
|
338
345
|
payload = copy.deepcopy(payload)
|
@@ -397,13 +404,14 @@ class WslinkHandler(object):
|
|
397
404
|
except Exception:
|
398
405
|
# the content which is not serializable might be arbitrarily large, don't include.
|
399
406
|
# repr(content) would do that...
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
+
with self.network_monitor:
|
408
|
+
await self.sendWrappedError(
|
409
|
+
rpcid,
|
410
|
+
RESULT_SERIALIZE_ERROR,
|
411
|
+
"Method result cannot be serialized",
|
412
|
+
method,
|
413
|
+
client_id=client_id,
|
414
|
+
)
|
407
415
|
return
|
408
416
|
|
409
417
|
websockets = self.getAuthenticatedWebsockets(client_id, skip_last_active_client)
|
@@ -411,11 +419,15 @@ class WslinkHandler(object):
|
|
411
419
|
# aiohttp can not handle pending ws.send_bytes()
|
412
420
|
# tried with semaphore but got exception with >1
|
413
421
|
# https://github.com/aio-libs/aiohttp/issues/2934
|
414
|
-
|
415
|
-
|
416
|
-
for
|
417
|
-
|
418
|
-
|
422
|
+
with self.network_monitor:
|
423
|
+
async with self.attachment_atomic:
|
424
|
+
for chunk in generate_chunks(packed_wrapper, MAX_MSG_SIZE):
|
425
|
+
for ws in websockets:
|
426
|
+
if ws is not None:
|
427
|
+
await ws.send_bytes(chunk)
|
428
|
+
|
429
|
+
# Network operation completed
|
430
|
+
self.network_monitor.network_call_completed()
|
419
431
|
|
420
432
|
async def sendWrappedError(self, rpcid, code, message, data=None, client_id=None):
|
421
433
|
wrapper = {
|
@@ -443,11 +455,15 @@ class WslinkHandler(object):
|
|
443
455
|
# aiohttp can not handle pending ws.send_bytes()
|
444
456
|
# tried with semaphore but got exception with >1
|
445
457
|
# https://github.com/aio-libs/aiohttp/issues/2934
|
446
|
-
|
447
|
-
|
448
|
-
for
|
449
|
-
|
450
|
-
|
458
|
+
with self.network_monitor:
|
459
|
+
async with self.attachment_atomic:
|
460
|
+
for chunk in generate_chunks(packed_wrapper, MAX_MSG_SIZE):
|
461
|
+
for ws in websockets:
|
462
|
+
if ws is not None:
|
463
|
+
await ws.send_bytes(chunk)
|
464
|
+
|
465
|
+
# Network operation completed
|
466
|
+
self.network_monitor.network_call_completed()
|
451
467
|
|
452
468
|
def publish(self, topic, data, client_id=None, skip_last_active_client=False):
|
453
469
|
client_list = [client_id] if client_id else [c_id for c_id in self.connections]
|
@@ -27,6 +27,7 @@ class PublishManager(object):
|
|
27
27
|
for protocol in self.protocols:
|
28
28
|
# The client is unknown - we send to any client who is subscribed to the topic
|
29
29
|
rpcid = "publish:{0}:{1}".format(topic, self.publishCount)
|
30
|
+
protocol.network_monitor.on_enter()
|
30
31
|
schedule_coroutine(
|
31
32
|
0,
|
32
33
|
protocol.sendWrappedMessage,
|
@@ -34,6 +35,8 @@ class PublishManager(object):
|
|
34
35
|
data,
|
35
36
|
client_id=client_id,
|
36
37
|
skip_last_active_client=skip_last_active_client,
|
38
|
+
# for schedule_coroutine call
|
39
|
+
done_callback=protocol.network_monitor.on_exit,
|
37
40
|
)
|
38
41
|
|
39
42
|
|
@@ -5,6 +5,7 @@ ServerProtocol to hook all the needed LinkProtocols together.
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
import logging
|
8
|
+
import asyncio
|
8
9
|
|
9
10
|
from . import register as exportRpc
|
10
11
|
from . import schedule_callback
|
@@ -65,6 +66,57 @@ class LinkProtocol(object):
|
|
65
66
|
# =============================================================================
|
66
67
|
|
67
68
|
|
69
|
+
class NetworkMonitor:
|
70
|
+
"""
|
71
|
+
Provide context manager for increase/decrease pending request
|
72
|
+
either synchronously or asynchronously.
|
73
|
+
|
74
|
+
The Asynchronous version also await completion.
|
75
|
+
"""
|
76
|
+
|
77
|
+
def __init__(self):
|
78
|
+
|
79
|
+
self.pending = 0
|
80
|
+
self.event = asyncio.Event()
|
81
|
+
|
82
|
+
def network_call_completed(self):
|
83
|
+
"""Trigger completion event"""
|
84
|
+
self.event.set()
|
85
|
+
|
86
|
+
def on_enter(self, *args, **kwargs):
|
87
|
+
"""Increase pending request"""
|
88
|
+
self.pending += 1
|
89
|
+
|
90
|
+
def on_exit(self, *args, **kwargs):
|
91
|
+
"""Decrease pending request and trigger completion event if we reach 0 pending request"""
|
92
|
+
self.pending -= 1
|
93
|
+
if self.pending == 0 and not self.event.is_set():
|
94
|
+
self.event.set()
|
95
|
+
|
96
|
+
# Sync ctx manager
|
97
|
+
def __enter__(self):
|
98
|
+
self.on_enter()
|
99
|
+
return self
|
100
|
+
|
101
|
+
def __exit__(self, exc_type, exc_value, exc_traceback):
|
102
|
+
self.on_exit()
|
103
|
+
|
104
|
+
# Async ctx manager
|
105
|
+
async def __aenter__(self):
|
106
|
+
self.on_enter()
|
107
|
+
return self
|
108
|
+
|
109
|
+
async def __aexit__(self, exc_t, exc_v, exc_tb):
|
110
|
+
self.on_exit()
|
111
|
+
await self.completion()
|
112
|
+
|
113
|
+
async def completion(self):
|
114
|
+
"""Await completion of any pending network request"""
|
115
|
+
while self.pending:
|
116
|
+
self.event.clear()
|
117
|
+
await self.event.wait()
|
118
|
+
|
119
|
+
|
68
120
|
class ServerProtocol(object):
|
69
121
|
"""
|
70
122
|
Defines the core server protocol for wslink. Gathers a list of LinkProtocol
|
@@ -72,6 +124,7 @@ class ServerProtocol(object):
|
|
72
124
|
"""
|
73
125
|
|
74
126
|
def __init__(self):
|
127
|
+
self.network_monitor = NetworkMonitor()
|
75
128
|
self.linkProtocols = []
|
76
129
|
self.secret = None
|
77
130
|
self.initialize()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|