@scrypted/server 0.6.24 → 0.7.2
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.
Potentially problematic release.
This version of @scrypted/server might be problematic. Click here for more details.
- package/dist/http-interfaces.js +4 -1
- package/dist/http-interfaces.js.map +1 -1
- package/dist/listen-zero.js +5 -2
- package/dist/listen-zero.js.map +1 -1
- package/dist/plugin/media.js +25 -20
- package/dist/plugin/media.js.map +1 -1
- package/dist/plugin/plugin-console.js +157 -4
- package/dist/plugin/plugin-console.js.map +1 -1
- package/dist/plugin/plugin-device.js +2 -0
- package/dist/plugin/plugin-device.js.map +1 -1
- package/dist/plugin/plugin-host.js +5 -0
- package/dist/plugin/plugin-host.js.map +1 -1
- package/dist/plugin/plugin-remote-stats.js +30 -0
- package/dist/plugin/plugin-remote-stats.js.map +1 -0
- package/dist/plugin/plugin-remote-worker.js +69 -149
- package/dist/plugin/plugin-remote-worker.js.map +1 -1
- package/dist/plugin/plugin-repl.js +4 -1
- package/dist/plugin/plugin-repl.js.map +1 -1
- package/dist/plugin/runtime/python-worker.js +1 -0
- package/dist/plugin/runtime/python-worker.js.map +1 -1
- package/dist/plugin/system.js +4 -0
- package/dist/plugin/system.js.map +1 -1
- package/dist/rpc.js +183 -45
- package/dist/rpc.js.map +1 -1
- package/dist/runtime.js +3 -0
- package/dist/runtime.js.map +1 -1
- package/dist/threading.js +1 -0
- package/dist/threading.js.map +1 -1
- package/package.json +3 -4
- package/python/plugin_remote.py +134 -51
- package/python/rpc-iterator-test.py +45 -0
- package/python/rpc.py +168 -50
- package/python/rpc_reader.py +57 -60
- package/src/http-interfaces.ts +5 -1
- package/src/listen-zero.ts +6 -2
- package/src/plugin/media.ts +38 -35
- package/src/plugin/plugin-api.ts +4 -1
- package/src/plugin/plugin-console.ts +154 -6
- package/src/plugin/plugin-device.ts +3 -0
- package/src/plugin/plugin-host.ts +5 -0
- package/src/plugin/plugin-remote-stats.ts +36 -0
- package/src/plugin/plugin-remote-worker.ts +77 -178
- package/src/plugin/plugin-remote.ts +1 -1
- package/src/plugin/plugin-repl.ts +4 -1
- package/src/plugin/runtime/python-worker.ts +2 -0
- package/src/plugin/system.ts +6 -0
- package/src/rpc.ts +230 -52
- package/src/runtime.ts +3 -0
- package/src/threading.ts +2 -0
- package/test/rpc-iterator-test.ts +46 -0
- package/test/rpc-python-test.ts +44 -0
package/python/rpc.py
CHANGED
@@ -4,7 +4,6 @@ import traceback
|
|
4
4
|
import inspect
|
5
5
|
from typing_extensions import TypedDict
|
6
6
|
import weakref
|
7
|
-
import sys
|
8
7
|
|
9
8
|
jsonSerializable = set()
|
10
9
|
jsonSerializable.add(float)
|
@@ -16,14 +15,16 @@ jsonSerializable.add(list)
|
|
16
15
|
|
17
16
|
|
18
17
|
async def maybe_await(value):
|
19
|
-
if (inspect.
|
18
|
+
if (inspect.isawaitable(value)):
|
20
19
|
return await value
|
21
20
|
return value
|
22
21
|
|
23
22
|
|
24
|
-
class
|
25
|
-
name
|
26
|
-
stack
|
23
|
+
class RPCResultError(Exception):
|
24
|
+
name: str
|
25
|
+
stack: str
|
26
|
+
message: str
|
27
|
+
caught: Exception
|
27
28
|
|
28
29
|
def __init__(self, caught, message):
|
29
30
|
self.caught = caught
|
@@ -58,16 +59,31 @@ class RpcProxy(object):
|
|
58
59
|
self.__dict__['__proxy_entry'] = entry
|
59
60
|
self.__dict__['__proxy_constructor'] = proxyConstructorName
|
60
61
|
self.__dict__['__proxy_peer'] = peer
|
61
|
-
self.__dict__[
|
62
|
+
self.__dict__[RpcPeer.PROPERTY_PROXY_PROPERTIES] = proxyProps
|
62
63
|
self.__dict__['__proxy_oneway_methods'] = proxyOneWayMethods
|
63
64
|
|
65
|
+
def __aiter__(self):
|
66
|
+
if self.__dict__[RpcPeer.PROPERTY_PROXY_PROPERTIES] and 'Symbol(Symbol.asyncIterator)' in self.__dict__[RpcPeer.PROPERTY_PROXY_PROPERTIES]:
|
67
|
+
return self
|
68
|
+
raise Exception('RpcProxy is not an async iterable')
|
69
|
+
|
70
|
+
async def __anext__(self):
|
71
|
+
if self.__dict__[RpcPeer.PROPERTY_PROXY_PROPERTIES] and 'Symbol(Symbol.asyncIterator)' in self.__dict__[RpcPeer.PROPERTY_PROXY_PROPERTIES]:
|
72
|
+
try:
|
73
|
+
return await RpcProxyMethod(self, self.__dict__[RpcPeer.PROPERTY_PROXY_PROPERTIES]['Symbol(Symbol.asyncIterator)']['next'])()
|
74
|
+
except RPCResultError as e:
|
75
|
+
if e.name == 'StopAsyncIteration':
|
76
|
+
raise StopAsyncIteration()
|
77
|
+
raise
|
78
|
+
raise Exception('RpcProxy is not an async iterable')
|
79
|
+
|
64
80
|
def __getattr__(self, name):
|
65
81
|
if name == '__proxy_finalizer_id':
|
66
82
|
return self.dict['__proxy_entry']['finalizerId']
|
67
83
|
if name in self.__dict__:
|
68
84
|
return self.__dict__[name]
|
69
|
-
if self.__dict__[
|
70
|
-
return self.__dict__[
|
85
|
+
if self.__dict__[RpcPeer.PROPERTY_PROXY_PROPERTIES] and name in self.__dict__[RpcPeer.PROPERTY_PROXY_PROPERTIES]:
|
86
|
+
return self.__dict__[RpcPeer.PROPERTY_PROXY_PROPERTIES][name]
|
71
87
|
return RpcProxyMethod(self, name)
|
72
88
|
|
73
89
|
def __setattr__(self, name: str, value: Any) -> None:
|
@@ -85,6 +101,10 @@ class RpcProxy(object):
|
|
85
101
|
|
86
102
|
|
87
103
|
class RpcPeer:
|
104
|
+
RPC_RESULT_ERROR_NAME = 'RPCResultError'
|
105
|
+
PROPERTY_PROXY_PROPERTIES = '__proxy_props'
|
106
|
+
PROPERTY_JSON_COPY_SERIALIZE_CHILDREN = '__json_copy_serialize_children'
|
107
|
+
|
88
108
|
def __init__(self, send: Callable[[object, Callable[[Exception], None], Dict], None]) -> None:
|
89
109
|
self.send = send
|
90
110
|
self.idCounter = 1
|
@@ -97,12 +117,14 @@ class RpcPeer:
|
|
97
117
|
self.pendingResults: Mapping[str, Future] = {}
|
98
118
|
self.remoteWeakProxies: Mapping[str, any] = {}
|
99
119
|
self.nameDeserializerMap: Mapping[str, RpcSerializer] = {}
|
120
|
+
self.onProxySerialization: Callable[[Any, str], Any] = None
|
121
|
+
self.killed = False
|
100
122
|
|
101
123
|
def __apply__(self, proxyId: str, oneWayMethods: List[str], method: str, args: list):
|
102
124
|
serializationContext: Dict = {}
|
103
125
|
serializedArgs = []
|
104
126
|
for arg in args:
|
105
|
-
serializedArgs.append(self.serialize(arg,
|
127
|
+
serializedArgs.append(self.serialize(arg, serializationContext))
|
106
128
|
|
107
129
|
rpcApply = {
|
108
130
|
'type': 'apply',
|
@@ -124,21 +146,112 @@ class RpcPeer:
|
|
124
146
|
self.send(rpcApply, reject, serializationContext)
|
125
147
|
return self.createPendingResult(send)
|
126
148
|
|
127
|
-
def kill(self):
|
149
|
+
def kill(self, message: str = None):
|
150
|
+
# not thread safe..
|
151
|
+
if self.killed:
|
152
|
+
return
|
128
153
|
self.killed = True
|
129
154
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
155
|
+
error = RPCResultError(None, message or 'peer was killed')
|
156
|
+
# this.killedDeferred.reject(error);
|
157
|
+
for str, future in self.pendingResults.items():
|
158
|
+
future.set_exception(error)
|
159
|
+
|
160
|
+
self.pendingResults = None
|
161
|
+
self.params = None
|
162
|
+
self.remoteWeakProxies = None
|
163
|
+
self.localProxyMap = None
|
164
|
+
self.localProxied = None
|
165
|
+
|
166
|
+
def createErrorResult(self, result: Any, e: Exception):
|
167
|
+
s = self.serializeError(e)
|
168
|
+
result['result'] = s
|
169
|
+
result['throw'] = True
|
170
|
+
|
171
|
+
# TODO 3/2/2023 deprecate these properties
|
172
|
+
tb = traceback.format_exc()
|
173
|
+
message = str(e)
|
174
|
+
result['stack'] = tb or '[no stack]',
|
175
|
+
result['message'] = message or '[no message]',
|
176
|
+
# END TODO
|
177
|
+
|
178
|
+
|
179
|
+
def deserializeError(e: Dict) -> RPCResultError:
|
180
|
+
error = RPCResultError(None, e.get('message'))
|
181
|
+
error.stack = e.get('stack')
|
182
|
+
error.name = e.get('name')
|
183
|
+
return error
|
184
|
+
|
185
|
+
def serializeError(self, e: Exception):
|
186
|
+
tb = traceback.format_exc()
|
187
|
+
name = type(e).__name__
|
188
|
+
message = str(e)
|
189
|
+
|
190
|
+
serialized = {
|
191
|
+
'stack': tb or '[no stack]',
|
192
|
+
'name': name or '[no name]',
|
193
|
+
'message': message or '[no message]',
|
194
|
+
}
|
195
|
+
|
196
|
+
return {
|
197
|
+
'__remote_constructor_name': RpcPeer.RPC_RESULT_ERROR_NAME,
|
198
|
+
'__serialized_value': serialized,
|
199
|
+
}
|
200
|
+
|
201
|
+
# def getProxyProperties(value):
|
202
|
+
# return getattr(value, RpcPeer.PROPERTY_PROXY_PROPERTIES, None)
|
203
|
+
|
204
|
+
# def setProxyProperties(value, properties):
|
205
|
+
# setattr(value, RpcPeer.PROPERTY_PROXY_PROPERTIES, properties)
|
206
|
+
|
207
|
+
def prepareProxyProperties(value):
|
208
|
+
if not hasattr(value, '__aiter__') or not hasattr(value, '__anext__'):
|
209
|
+
return getattr(value, RpcPeer.PROPERTY_PROXY_PROPERTIES, None)
|
210
|
+
|
211
|
+
props = getattr(value, RpcPeer.PROPERTY_PROXY_PROPERTIES, None) or {}
|
212
|
+
if not props.get('Symbol(Symbol.asyncIterator)'):
|
213
|
+
props['Symbol(Symbol.asyncIterator)'] = {
|
214
|
+
'next': '__anext__',
|
215
|
+
'throw': 'athrow',
|
216
|
+
'return': 'aclose',
|
217
|
+
}
|
218
|
+
return props
|
219
|
+
|
220
|
+
def isTransportSafe(value: any):
|
221
|
+
return not value or (type(value) in jsonSerializable)
|
222
|
+
|
223
|
+
def serialize(self, value, serializationContext: Dict):
|
224
|
+
if type(value) == dict and value.get(RpcPeer.PROPERTY_JSON_COPY_SERIALIZE_CHILDREN, None):
|
225
|
+
ret = {}
|
226
|
+
for (key, val) in value.items():
|
227
|
+
ret[key] = self.serialize(val, serializationContext)
|
228
|
+
return ret
|
134
229
|
|
135
|
-
|
136
|
-
if (not value or (not requireProxy and type(value) in jsonSerializable)):
|
230
|
+
if (RpcPeer.isTransportSafe(value)):
|
137
231
|
return value
|
138
232
|
|
139
233
|
__remote_constructor_name = 'Function' if callable(value) else value.__proxy_constructor if hasattr(
|
140
234
|
value, '__proxy_constructor') else type(value).__name__
|
141
235
|
|
236
|
+
if isinstance(value, Exception):
|
237
|
+
return self.serializeError(value)
|
238
|
+
|
239
|
+
serializerMapName = self.constructorSerializerMap.get(
|
240
|
+
type(value), None)
|
241
|
+
if serializerMapName:
|
242
|
+
__remote_constructor_name = serializerMapName
|
243
|
+
serializer = self.nameDeserializerMap.get(serializerMapName, None)
|
244
|
+
serialized = serializer.serialize(value, serializationContext)
|
245
|
+
ret = {
|
246
|
+
'__remote_proxy_id': None,
|
247
|
+
'__remote_proxy_finalizer_id': None,
|
248
|
+
'__remote_constructor_name': __remote_constructor_name,
|
249
|
+
'__remote_proxy_props': RpcPeer.prepareProxyProperties(value),
|
250
|
+
'__remote_proxy_oneway_methods': getattr(value, '__proxy_oneway_methods', None),
|
251
|
+
'__serialized_value': serialized,
|
252
|
+
}
|
253
|
+
return ret
|
254
|
+
|
142
255
|
proxiedEntry = self.localProxied.get(value, None)
|
143
256
|
if proxiedEntry:
|
144
257
|
proxiedEntry['finalizerId'] = str(self.proxyCounter)
|
@@ -147,7 +260,7 @@ class RpcPeer:
|
|
147
260
|
'__remote_proxy_id': proxiedEntry['id'],
|
148
261
|
'__remote_proxy_finalizer_id': proxiedEntry['finalizerId'],
|
149
262
|
'__remote_constructor_name': __remote_constructor_name,
|
150
|
-
'__remote_proxy_props':
|
263
|
+
'__remote_proxy_props': RpcPeer.prepareProxyProperties(value),
|
151
264
|
'__remote_proxy_oneway_methods': getattr(value, '__proxy_oneway_methods', None),
|
152
265
|
}
|
153
266
|
return ret
|
@@ -160,22 +273,6 @@ class RpcPeer:
|
|
160
273
|
}
|
161
274
|
return ret
|
162
275
|
|
163
|
-
serializerMapName = self.constructorSerializerMap.get(
|
164
|
-
type(value), None)
|
165
|
-
if serializerMapName:
|
166
|
-
__remote_constructor_name = serializerMapName
|
167
|
-
serializer = self.nameDeserializerMap.get(serializerMapName, None)
|
168
|
-
serialized = serializer.serialize(value, serializationContext)
|
169
|
-
ret = {
|
170
|
-
'__remote_proxy_id': None,
|
171
|
-
'__remote_proxy_finalizer_id': None,
|
172
|
-
'__remote_constructor_name': __remote_constructor_name,
|
173
|
-
'__remote_proxy_props': getattr(value, '__proxy_props', None),
|
174
|
-
'__remote_proxy_oneway_methods': getattr(value, '__proxy_oneway_methods', None),
|
175
|
-
'__serialized_value': serialized,
|
176
|
-
}
|
177
|
-
return ret
|
178
|
-
|
179
276
|
proxyId = str(self.proxyCounter)
|
180
277
|
self.proxyCounter = self.proxyCounter + 1
|
181
278
|
proxiedEntry = {
|
@@ -185,17 +282,24 @@ class RpcPeer:
|
|
185
282
|
self.localProxied[value] = proxiedEntry
|
186
283
|
self.localProxyMap[proxyId] = value
|
187
284
|
|
285
|
+
if self.onProxySerialization:
|
286
|
+
__remote_proxy_props = self.onProxySerialization(value, proxyId)
|
287
|
+
else:
|
288
|
+
__remote_proxy_props = RpcPeer.prepareProxyProperties(value)
|
289
|
+
|
188
290
|
ret = {
|
189
291
|
'__remote_proxy_id': proxyId,
|
190
292
|
'__remote_proxy_finalizer_id': proxyId,
|
191
293
|
'__remote_constructor_name': __remote_constructor_name,
|
192
|
-
'__remote_proxy_props':
|
294
|
+
'__remote_proxy_props': __remote_proxy_props,
|
193
295
|
'__remote_proxy_oneway_methods': getattr(value, '__proxy_oneway_methods', None),
|
194
296
|
}
|
195
297
|
|
196
298
|
return ret
|
197
299
|
|
198
300
|
def finalize(self, localProxiedEntry: LocalProxiedEntry):
|
301
|
+
if self.killed:
|
302
|
+
return
|
199
303
|
id = localProxiedEntry['id']
|
200
304
|
self.remoteWeakProxies.pop(id, None)
|
201
305
|
rpcFinalize = {
|
@@ -224,6 +328,13 @@ class RpcPeer:
|
|
224
328
|
if type(value) != dict:
|
225
329
|
return value
|
226
330
|
|
331
|
+
copySerializeChildren = value.get(RpcPeer.PROPERTY_JSON_COPY_SERIALIZE_CHILDREN, None)
|
332
|
+
if copySerializeChildren:
|
333
|
+
ret = {}
|
334
|
+
for (key, val) in value.items():
|
335
|
+
ret[key] = self.deserialize(val, deserializationContext)
|
336
|
+
return ret
|
337
|
+
|
227
338
|
__remote_proxy_id = value.get('__remote_proxy_id', None)
|
228
339
|
__remote_proxy_finalizer_id = value.get(
|
229
340
|
'__remote_proxy_finalizer_id', None)
|
@@ -235,6 +346,9 @@ class RpcPeer:
|
|
235
346
|
__remote_proxy_oneway_methods = value.get(
|
236
347
|
'__remote_proxy_oneway_methods', None)
|
237
348
|
|
349
|
+
if __remote_constructor_name == RpcPeer.RPC_RESULT_ERROR_NAME:
|
350
|
+
return RpcPeer.deserializeError(__serialized_value)
|
351
|
+
|
238
352
|
if __remote_proxy_id:
|
239
353
|
weakref = self.remoteWeakProxies.get('__remote_proxy_id', None)
|
240
354
|
proxy = weakref() if weakref else None
|
@@ -247,7 +361,7 @@ class RpcPeer:
|
|
247
361
|
if __local_proxy_id:
|
248
362
|
ret = self.localProxyMap.get(__local_proxy_id, None)
|
249
363
|
if not ret:
|
250
|
-
raise
|
364
|
+
raise RPCResultError(
|
251
365
|
None, 'invalid local proxy id %s' % __local_proxy_id)
|
252
366
|
return ret
|
253
367
|
|
@@ -258,7 +372,7 @@ class RpcPeer:
|
|
258
372
|
|
259
373
|
return value
|
260
374
|
|
261
|
-
async def handleMessage(self, message:
|
375
|
+
async def handleMessage(self, message: Dict, deserializationContext: Dict):
|
262
376
|
try:
|
263
377
|
messageType = message['type']
|
264
378
|
if messageType == 'param':
|
@@ -271,8 +385,7 @@ class RpcPeer:
|
|
271
385
|
try:
|
272
386
|
value = self.params.get(message['param'], None)
|
273
387
|
value = await maybe_await(value)
|
274
|
-
result['result'] = self.serialize(
|
275
|
-
value, message.get('requireProxy', None), serializationContext)
|
388
|
+
result['result'] = self.serialize(value, serializationContext)
|
276
389
|
except Exception as e:
|
277
390
|
tb = traceback.format_exc()
|
278
391
|
self.createErrorResult(
|
@@ -299,6 +412,9 @@ class RpcPeer:
|
|
299
412
|
for arg in (message['args'] or []):
|
300
413
|
args.append(self.deserialize(arg, deserializationContext))
|
301
414
|
|
415
|
+
# if method == 'asend' and hasattr(target, '__aiter__') and hasattr(target, '__anext__') and not len(args):
|
416
|
+
# args.append(None)
|
417
|
+
|
302
418
|
value = None
|
303
419
|
if method:
|
304
420
|
if not hasattr(target, method):
|
@@ -309,12 +425,11 @@ class RpcPeer:
|
|
309
425
|
else:
|
310
426
|
value = await maybe_await(target(*args))
|
311
427
|
|
312
|
-
result['result'] = self.serialize(value,
|
428
|
+
result['result'] = self.serialize(value, serializationContext)
|
429
|
+
except StopAsyncIteration as e:
|
430
|
+
self.createErrorResult(result, e)
|
313
431
|
except Exception as e:
|
314
|
-
|
315
|
-
# print('failure', method, e, tb)
|
316
|
-
self.createErrorResult(
|
317
|
-
result, type(e).__name__, str(e), tb)
|
432
|
+
self.createErrorResult(result, e)
|
318
433
|
|
319
434
|
if not message.get('oneway', False):
|
320
435
|
self.send(result, None, serializationContext)
|
@@ -323,18 +438,21 @@ class RpcPeer:
|
|
323
438
|
id = message['id']
|
324
439
|
future = self.pendingResults.get(id, None)
|
325
440
|
if not future:
|
326
|
-
raise
|
441
|
+
raise RPCResultError(
|
327
442
|
None, 'unknown result %s' % id)
|
328
443
|
del self.pendingResults[id]
|
329
|
-
if hasattr(message, 'message') or hasattr(message, 'stack'):
|
330
|
-
e =
|
444
|
+
if (hasattr(message, 'message') or hasattr(message, 'stack')) and not hasattr(message, 'throw'):
|
445
|
+
e = RPCResultError(
|
331
446
|
None, message.get('message', None))
|
332
447
|
e.stack = message.get('stack', None)
|
333
448
|
e.name = message.get('name', None)
|
334
449
|
future.set_exception(e)
|
335
450
|
return
|
336
|
-
|
337
|
-
|
451
|
+
deserialized = self.deserialize(message.get('result', None), deserializationContext)
|
452
|
+
if message.get('throw'):
|
453
|
+
future.set_exception(deserialized)
|
454
|
+
else:
|
455
|
+
future.set_result(deserialized)
|
338
456
|
elif messageType == 'finalize':
|
339
457
|
finalizerId = message.get('__local_proxy_finalizer_id', None)
|
340
458
|
proxyId = message['__local_proxy_id']
|
@@ -347,7 +465,7 @@ class RpcPeer:
|
|
347
465
|
self.localProxied.pop(local, None)
|
348
466
|
local = self.localProxyMap.pop(proxyId, None)
|
349
467
|
else:
|
350
|
-
raise
|
468
|
+
raise RPCResultError(
|
351
469
|
None, 'unknown rpc message type %s' % type)
|
352
470
|
except Exception as e:
|
353
471
|
print("unhandled rpc error", self.peerName, e)
|
@@ -361,7 +479,7 @@ class RpcPeer:
|
|
361
479
|
self.idCounter = self.idCounter + 1
|
362
480
|
future = Future()
|
363
481
|
self.pendingResults[id] = future
|
364
|
-
await cb(id, lambda e: future.set_exception(
|
482
|
+
await cb(id, lambda e: future.set_exception(RPCResultError(e, None)))
|
365
483
|
return await future
|
366
484
|
|
367
485
|
async def getParam(self, param):
|
package/python/rpc_reader.py
CHANGED
@@ -2,33 +2,16 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import asyncio
|
4
4
|
import base64
|
5
|
-
import gc
|
6
5
|
import json
|
7
|
-
import sys
|
8
6
|
import os
|
9
|
-
import
|
10
|
-
import shutil
|
11
|
-
import subprocess
|
7
|
+
import sys
|
12
8
|
import threading
|
13
|
-
import time
|
14
|
-
import traceback
|
15
|
-
import zipfile
|
16
9
|
from asyncio.events import AbstractEventLoop
|
17
|
-
from asyncio.futures import Future
|
18
|
-
from asyncio.streams import StreamReader, StreamWriter
|
19
|
-
from collections.abc import Mapping
|
20
|
-
from io import StringIO
|
21
10
|
from os import sys
|
22
|
-
from typing import
|
11
|
+
from typing import List
|
23
12
|
|
24
13
|
import aiofiles
|
25
|
-
import scrypted_python.scrypted_sdk.types
|
26
|
-
from scrypted_python.scrypted_sdk import ScryptedStatic, PluginFork
|
27
|
-
from scrypted_python.scrypted_sdk.types import Device, DeviceManifest, EventDetails, ScryptedInterfaceProperty, Storage
|
28
|
-
from typing_extensions import TypedDict
|
29
14
|
import rpc
|
30
|
-
import multiprocessing
|
31
|
-
import multiprocessing.connection
|
32
15
|
|
33
16
|
|
34
17
|
class BufferSerializer(rpc.RpcSerializer):
|
@@ -53,39 +36,61 @@ class SidebandBufferSerializer(rpc.RpcSerializer):
|
|
53
36
|
buffer = buffers.pop()
|
54
37
|
return buffer
|
55
38
|
|
56
|
-
async def readLoop(loop, peer: rpc.RpcPeer, reader):
|
39
|
+
async def readLoop(loop, peer: rpc.RpcPeer, reader: asyncio.StreamReader):
|
57
40
|
deserializationContext = {
|
58
41
|
'buffers': []
|
59
42
|
}
|
60
43
|
|
44
|
+
if isinstance(reader, asyncio.StreamReader):
|
45
|
+
async def read(n):
|
46
|
+
return await reader.readexactly(n)
|
47
|
+
else:
|
48
|
+
async def read(n):
|
49
|
+
return await reader.read(n)
|
50
|
+
|
51
|
+
|
61
52
|
while True:
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
sys.exit()
|
83
|
-
|
84
|
-
async def prepare_peer_readloop(loop: AbstractEventLoop, readFd: int, writeFd: int):
|
85
|
-
reader = await aiofiles.open(readFd, mode='rb')
|
53
|
+
lengthBytes = await read(4)
|
54
|
+
typeBytes = await read(1)
|
55
|
+
type = typeBytes[0]
|
56
|
+
length = int.from_bytes(lengthBytes, 'big')
|
57
|
+
data = await read(length - 1)
|
58
|
+
|
59
|
+
if type == 1:
|
60
|
+
deserializationContext['buffers'].append(data)
|
61
|
+
continue
|
62
|
+
|
63
|
+
message = json.loads(data)
|
64
|
+
asyncio.run_coroutine_threadsafe(
|
65
|
+
peer.handleMessage(message, deserializationContext), loop)
|
66
|
+
|
67
|
+
deserializationContext = {
|
68
|
+
'buffers': []
|
69
|
+
}
|
70
|
+
|
71
|
+
async def prepare_peer_readloop(loop: AbstractEventLoop, readFd: int = None, writeFd: int = None, reader: asyncio.StreamReader = None, writer: asyncio.StreamWriter = None):
|
72
|
+
reader = reader or await aiofiles.open(readFd, mode='rb')
|
86
73
|
|
87
74
|
mutex = threading.Lock()
|
88
75
|
|
76
|
+
if writer:
|
77
|
+
def write(buffers, reject):
|
78
|
+
try:
|
79
|
+
for b in buffers:
|
80
|
+
writer.write(b)
|
81
|
+
except Exception as e:
|
82
|
+
if reject:
|
83
|
+
reject(e)
|
84
|
+
return None
|
85
|
+
else:
|
86
|
+
def write(buffers, reject):
|
87
|
+
try:
|
88
|
+
for b in buffers:
|
89
|
+
os.write(writeFd, b)
|
90
|
+
except Exception as e:
|
91
|
+
if reject:
|
92
|
+
reject(e)
|
93
|
+
|
89
94
|
def send(message, reject=None, serializationContext=None):
|
90
95
|
with mutex:
|
91
96
|
if serializationContext:
|
@@ -95,34 +100,26 @@ async def prepare_peer_readloop(loop: AbstractEventLoop, readFd: int, writeFd: i
|
|
95
100
|
length = len(buffer) + 1
|
96
101
|
lb = length.to_bytes(4, 'big')
|
97
102
|
type = 1
|
98
|
-
|
99
|
-
os.write(writeFd, lb)
|
100
|
-
os.write(writeFd, bytes([type]))
|
101
|
-
os.write(writeFd, buffer)
|
102
|
-
except Exception as e:
|
103
|
-
if reject:
|
104
|
-
reject(e)
|
105
|
-
return
|
103
|
+
write([lb, bytes([type]), buffer], reject)
|
106
104
|
|
107
105
|
jsonString = json.dumps(message)
|
108
106
|
b = bytes(jsonString, 'utf8')
|
109
107
|
length = len(b) + 1
|
110
108
|
lb = length.to_bytes(4, 'big')
|
111
109
|
type = 0
|
112
|
-
|
113
|
-
os.write(writeFd, lb)
|
114
|
-
os.write(writeFd, bytes([type]))
|
115
|
-
os.write(writeFd, b)
|
116
|
-
except Exception as e:
|
117
|
-
if reject:
|
118
|
-
reject(e)
|
110
|
+
write([lb, bytes([type]), b], reject)
|
119
111
|
|
120
112
|
peer = rpc.RpcPeer(send)
|
121
113
|
peer.nameDeserializerMap['Buffer'] = SidebandBufferSerializer()
|
122
114
|
peer.constructorSerializerMap[bytes] = 'Buffer'
|
123
115
|
peer.constructorSerializerMap[bytearray] = 'Buffer'
|
116
|
+
peer.constructorSerializerMap[memoryview] = 'Buffer'
|
124
117
|
|
125
118
|
async def peerReadLoop():
|
126
|
-
|
119
|
+
try:
|
120
|
+
await readLoop(loop, peer, reader)
|
121
|
+
except:
|
122
|
+
peer.kill()
|
123
|
+
raise
|
127
124
|
|
128
125
|
return peer, peerReadLoop
|
package/src/http-interfaces.ts
CHANGED
@@ -61,7 +61,11 @@ export function createResponseInterface(res: Response, unzippedDir: string, file
|
|
61
61
|
}
|
62
62
|
}
|
63
63
|
}
|
64
|
-
|
64
|
+
|
65
|
+
// prefer etag
|
66
|
+
res.sendFile(filePath, {
|
67
|
+
cacheControl: false,
|
68
|
+
});
|
65
69
|
}
|
66
70
|
|
67
71
|
sendSocket(socket: net.Socket, options: HttpResponseOptions) {
|
package/src/listen-zero.ts
CHANGED
@@ -26,10 +26,14 @@ export async function listenZeroSingleClient(hostname?: string) {
|
|
26
26
|
|
27
27
|
clientPromise.catch(() => { });
|
28
28
|
|
29
|
+
let host = hostname;
|
30
|
+
if (!host || host === '0.0.0.0')
|
31
|
+
host = '127.0.0.1';
|
32
|
+
|
29
33
|
return {
|
30
34
|
server,
|
31
|
-
url: `tcp
|
32
|
-
host
|
35
|
+
url: `tcp://${host}:${port}`,
|
36
|
+
host,
|
33
37
|
port,
|
34
38
|
clientPromise,
|
35
39
|
}
|