@scrypted/server 0.7.13 → 0.7.15
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/package.json +6 -3
- package/python/plugin_remote.py +18 -37
- package/python/rpc_reader.py +127 -52
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@scrypted/server",
|
3
|
-
"version": "0.7.
|
3
|
+
"version": "0.7.15",
|
4
4
|
"description": "",
|
5
5
|
"dependencies": {
|
6
6
|
"@mapbox/node-pre-gyp": "^1.0.10",
|
@@ -67,8 +67,11 @@
|
|
67
67
|
"prebuild": "rimraf dist",
|
68
68
|
"build": "tsc --outDir dist",
|
69
69
|
"postbuild": "node test/check-build-output.js",
|
70
|
-
"
|
71
|
-
"
|
70
|
+
"prebeta": "npm version patch && git add package.json && npm run build && git commit -m prebeta",
|
71
|
+
"beta": "npm publish --tag beta",
|
72
|
+
"release": "npm publish",
|
73
|
+
"prerelease": "npm version patch && git add package.json && npm run build && git commit -m prerelease",
|
74
|
+
"postrelease": "git tag v$npm_package_version && git push origin v$npm_package_version",
|
72
75
|
"docker": "scripts/github-workflow-publish-docker.sh"
|
73
76
|
},
|
74
77
|
"author": "",
|
package/python/plugin_remote.py
CHANGED
@@ -35,25 +35,6 @@ class SystemDeviceState(TypedDict):
|
|
35
35
|
stateTime: int
|
36
36
|
value: any
|
37
37
|
|
38
|
-
|
39
|
-
class StreamPipeReader:
|
40
|
-
def __init__(self, conn: multiprocessing.connection.Connection) -> None:
|
41
|
-
self.conn = conn
|
42
|
-
self.executor = concurrent.futures.ThreadPoolExecutor()
|
43
|
-
|
44
|
-
def readBlocking(self, n):
|
45
|
-
b = bytes(0)
|
46
|
-
while len(b) < n:
|
47
|
-
self.conn.poll(None)
|
48
|
-
add = os.read(self.conn.fileno(), n - len(b))
|
49
|
-
if not len(add):
|
50
|
-
raise Exception('unable to read requested bytes')
|
51
|
-
b += add
|
52
|
-
return b
|
53
|
-
|
54
|
-
async def read(self, n):
|
55
|
-
return await asyncio.get_event_loop().run_in_executor(self.executor, lambda: self.readBlocking(n))
|
56
|
-
|
57
38
|
class SystemManager(scrypted_python.scrypted_sdk.types.SystemManager):
|
58
39
|
def __init__(self, api: Any, systemState: Mapping[str, Mapping[str, SystemDeviceState]]) -> None:
|
59
40
|
super().__init__()
|
@@ -288,8 +269,9 @@ class PluginRemote:
|
|
288
269
|
clusterSecret = options['clusterSecret']
|
289
270
|
|
290
271
|
async def handleClusterClient(reader: asyncio.StreamReader, writer: asyncio.StreamWriter):
|
272
|
+
rpcTransport = rpc_reader.RpcStreamTransport(reader, writer)
|
291
273
|
peer: rpc.RpcPeer
|
292
|
-
peer, peerReadLoop = await rpc_reader.prepare_peer_readloop(self.loop,
|
274
|
+
peer, peerReadLoop = await rpc_reader.prepare_peer_readloop(self.loop, rpcTransport)
|
293
275
|
async def connectRPCObject(id: str, secret: str):
|
294
276
|
m = hashlib.sha256()
|
295
277
|
m.update(bytes('%s%s' % (clusterPort, clusterSecret), 'utf8'))
|
@@ -324,7 +306,8 @@ class PluginRemote:
|
|
324
306
|
async def connectClusterPeer():
|
325
307
|
reader, writer = await asyncio.open_connection(
|
326
308
|
'127.0.0.1', port)
|
327
|
-
|
309
|
+
rpcTransport = rpc_reader.RpcStreamTransport(reader, writer)
|
310
|
+
peer, peerReadLoop = await rpc_reader.prepare_peer_readloop(self.loop, rpcTransport)
|
328
311
|
async def run_loop():
|
329
312
|
try:
|
330
313
|
await peerReadLoop()
|
@@ -485,8 +468,8 @@ class PluginRemote:
|
|
485
468
|
schedule_exit_check()
|
486
469
|
|
487
470
|
async def getFork():
|
488
|
-
|
489
|
-
forkPeer, readLoop = await rpc_reader.prepare_peer_readloop(self.loop,
|
471
|
+
rpcTransport = rpc_reader.RpcConnectionTransport(parent_conn)
|
472
|
+
forkPeer, readLoop = await rpc_reader.prepare_peer_readloop(self.loop, rpcTransport)
|
490
473
|
forkPeer.peerName = 'thread'
|
491
474
|
|
492
475
|
async def updateStats(stats):
|
@@ -502,7 +485,7 @@ class PluginRemote:
|
|
502
485
|
finally:
|
503
486
|
allMemoryStats.pop(forkPeer)
|
504
487
|
parent_conn.close()
|
505
|
-
|
488
|
+
rpcTransport.executor.shutdown()
|
506
489
|
asyncio.run_coroutine_threadsafe(forkReadLoop(), loop=self.loop)
|
507
490
|
getRemote = await forkPeer.getParam('getRemote')
|
508
491
|
remote: PluginRemote = await getRemote(self.api, self.pluginId, self.hostInfo)
|
@@ -594,8 +577,8 @@ class PluginRemote:
|
|
594
577
|
|
595
578
|
allMemoryStats = {}
|
596
579
|
|
597
|
-
async def plugin_async_main(loop: AbstractEventLoop,
|
598
|
-
peer, readLoop = await rpc_reader.prepare_peer_readloop(loop,
|
580
|
+
async def plugin_async_main(loop: AbstractEventLoop, rpcTransport: rpc_reader.RpcTransport):
|
581
|
+
peer, readLoop = await rpc_reader.prepare_peer_readloop(loop, rpcTransport)
|
599
582
|
peer.params['print'] = print
|
600
583
|
peer.params['getRemote'] = lambda api, pluginId, hostInfo: PluginRemote(peer, api, pluginId, hostInfo, loop)
|
601
584
|
|
@@ -642,11 +625,11 @@ async def plugin_async_main(loop: AbstractEventLoop, readFd: int = None, writeFd
|
|
642
625
|
try:
|
643
626
|
await readLoop()
|
644
627
|
finally:
|
645
|
-
if
|
646
|
-
r:
|
628
|
+
if type(rpcTransport) == rpc_reader.RpcConnectionTransport:
|
629
|
+
r: rpc_reader.RpcConnectionTransport = rpcTransport
|
647
630
|
r.executor.shutdown()
|
648
631
|
|
649
|
-
def main(
|
632
|
+
def main(rpcTransport: rpc_reader.RpcTransport):
|
650
633
|
loop = asyncio.new_event_loop()
|
651
634
|
|
652
635
|
def gc_runner():
|
@@ -654,10 +637,10 @@ def main(readFd: int = None, writeFd: int = None, reader: asyncio.StreamReader =
|
|
654
637
|
loop.call_later(10, gc_runner)
|
655
638
|
gc_runner()
|
656
639
|
|
657
|
-
loop.run_until_complete(plugin_async_main(loop,
|
640
|
+
loop.run_until_complete(plugin_async_main(loop, rpcTransport))
|
658
641
|
loop.close()
|
659
642
|
|
660
|
-
def plugin_main(
|
643
|
+
def plugin_main(rpcTransport: rpc_reader.RpcTransport):
|
661
644
|
try:
|
662
645
|
import gi
|
663
646
|
gi.require_version('Gst', '1.0')
|
@@ -666,18 +649,16 @@ def plugin_main(readFd: int = None, writeFd: int = None, reader: asyncio.StreamR
|
|
666
649
|
|
667
650
|
loop = GLib.MainLoop()
|
668
651
|
|
669
|
-
worker = threading.Thread(target=main, args=(
|
652
|
+
worker = threading.Thread(target=main, args=(rpcTransport,), name="asyncio-main")
|
670
653
|
worker.start()
|
671
654
|
|
672
655
|
loop.run()
|
673
656
|
except:
|
674
|
-
main(
|
657
|
+
main(rpcTransport)
|
675
658
|
|
676
659
|
|
677
660
|
def plugin_fork(conn: multiprocessing.connection.Connection):
|
678
|
-
|
679
|
-
reader = StreamPipeReader(conn)
|
680
|
-
plugin_main(reader=reader, writeFd=fd)
|
661
|
+
plugin_main(rpc_reader.RpcConnectionTransport(conn))
|
681
662
|
|
682
663
|
if __name__ == "__main__":
|
683
|
-
plugin_main(3, 4)
|
664
|
+
plugin_main(rpc_reader.RpcFileTransport(3, 4))
|
package/python/rpc_reader.py
CHANGED
@@ -4,14 +4,14 @@ import asyncio
|
|
4
4
|
import base64
|
5
5
|
import json
|
6
6
|
import os
|
7
|
-
import sys
|
8
7
|
import threading
|
9
8
|
from asyncio.events import AbstractEventLoop
|
10
|
-
from
|
11
|
-
|
12
|
-
|
9
|
+
from typing import List, Any
|
10
|
+
import multiprocessing.connection
|
13
11
|
import aiofiles
|
14
12
|
import rpc
|
13
|
+
import concurrent.futures
|
14
|
+
import json
|
15
15
|
|
16
16
|
|
17
17
|
class BufferSerializer(rpc.RpcSerializer):
|
@@ -36,31 +36,131 @@ class SidebandBufferSerializer(rpc.RpcSerializer):
|
|
36
36
|
buffer = buffers.pop()
|
37
37
|
return buffer
|
38
38
|
|
39
|
-
async def readLoop(loop, peer: rpc.RpcPeer, reader: asyncio.StreamReader):
|
40
|
-
deserializationContext = {
|
41
|
-
'buffers': []
|
42
|
-
}
|
43
39
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
else:
|
48
|
-
async def read(n):
|
49
|
-
return await reader.read(n)
|
40
|
+
class RpcTransport:
|
41
|
+
async def prepare(self):
|
42
|
+
pass
|
50
43
|
|
44
|
+
async def read(self):
|
45
|
+
pass
|
51
46
|
|
52
|
-
|
53
|
-
|
54
|
-
|
47
|
+
def writeBuffer(self, buffer, reject):
|
48
|
+
pass
|
49
|
+
|
50
|
+
def writeJSON(self, json, reject):
|
51
|
+
pass
|
52
|
+
|
53
|
+
|
54
|
+
class RpcFileTransport(RpcTransport):
|
55
|
+
reader: asyncio.StreamReader
|
56
|
+
|
57
|
+
def __init__(self, readFd: int, writeFd: int) -> None:
|
58
|
+
super().__init__()
|
59
|
+
self.readFd = readFd
|
60
|
+
self.writeFd = writeFd
|
61
|
+
self.reader = None
|
62
|
+
|
63
|
+
async def prepare(self):
|
64
|
+
await super().prepare()
|
65
|
+
self.reader = await aiofiles.open(self.readFd, mode='rb')
|
66
|
+
|
67
|
+
async def read(self):
|
68
|
+
lengthBytes = await self.reader.read(4)
|
69
|
+
typeBytes = await self.reader.read(1)
|
55
70
|
type = typeBytes[0]
|
56
71
|
length = int.from_bytes(lengthBytes, 'big')
|
57
|
-
data = await read(length - 1)
|
72
|
+
data = await self.reader.read(length - 1)
|
73
|
+
if type == 1:
|
74
|
+
return data
|
75
|
+
message = json.loads(data)
|
76
|
+
return message
|
77
|
+
|
78
|
+
def writeMessage(self, type: int, buffer, reject):
|
79
|
+
length = len(buffer) + 1
|
80
|
+
lb = length.to_bytes(4, 'big')
|
81
|
+
try:
|
82
|
+
for b in [lb, bytes([type]), buffer]:
|
83
|
+
os.write(self.writeFd, b)
|
84
|
+
except Exception as e:
|
85
|
+
if reject:
|
86
|
+
reject(e)
|
87
|
+
|
88
|
+
def writeJSON(self, j, reject):
|
89
|
+
return self.writeMessage(0, bytes(json.dumps(j), 'utf8'), reject)
|
90
|
+
|
91
|
+
def writeBuffer(self, buffer, reject):
|
92
|
+
return self.writeMessage(1, buffer, reject)
|
58
93
|
|
94
|
+
|
95
|
+
class RpcStreamTransport(RpcTransport):
|
96
|
+
def __init__(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
|
97
|
+
super().__init__()
|
98
|
+
self.reader = reader
|
99
|
+
self.writer = writer
|
100
|
+
|
101
|
+
async def read(self):
|
102
|
+
lengthBytes = await self.reader.readexactly(4)
|
103
|
+
typeBytes = await self.reader.readexactly(1)
|
104
|
+
type = typeBytes[0]
|
105
|
+
length = int.from_bytes(lengthBytes, 'big')
|
106
|
+
data = await self.reader.readexactly(length - 1)
|
59
107
|
if type == 1:
|
60
|
-
|
108
|
+
return data
|
109
|
+
message = json.loads(data)
|
110
|
+
return message
|
111
|
+
|
112
|
+
def writeMessage(self, type: int, buffer, reject):
|
113
|
+
length = len(buffer) + 1
|
114
|
+
lb = length.to_bytes(4, 'big')
|
115
|
+
try:
|
116
|
+
for b in [lb, bytes([type]), buffer]:
|
117
|
+
self.writer.write(b)
|
118
|
+
except Exception as e:
|
119
|
+
if reject:
|
120
|
+
reject(e)
|
121
|
+
|
122
|
+
def writeJSON(self, j, reject):
|
123
|
+
return self.writeMessage(0, bytes(json.dumps(j), 'utf8'), reject)
|
124
|
+
|
125
|
+
def writeBuffer(self, buffer, reject):
|
126
|
+
return self.writeMessage(1, buffer, reject)
|
127
|
+
|
128
|
+
|
129
|
+
class RpcConnectionTransport(RpcTransport):
|
130
|
+
def __init__(self, connection: multiprocessing.connection.Connection) -> None:
|
131
|
+
super().__init__()
|
132
|
+
self.connection = connection
|
133
|
+
self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=1)
|
134
|
+
|
135
|
+
async def read(self):
|
136
|
+
return await asyncio.get_event_loop().run_in_executor(self.executor, lambda: self.connection.recv())
|
137
|
+
|
138
|
+
def writeMessage(self, json, reject):
|
139
|
+
try:
|
140
|
+
self.connection.send(json)
|
141
|
+
except Exception as e:
|
142
|
+
if reject:
|
143
|
+
reject(e)
|
144
|
+
|
145
|
+
def writeJSON(self, json, reject):
|
146
|
+
return self.writeMessage(json, reject)
|
147
|
+
|
148
|
+
def writeBuffer(self, buffer, reject):
|
149
|
+
return self.writeMessage(bytes(buffer), reject)
|
150
|
+
|
151
|
+
|
152
|
+
async def readLoop(loop, peer: rpc.RpcPeer, rpcTransport: RpcTransport):
|
153
|
+
deserializationContext = {
|
154
|
+
'buffers': []
|
155
|
+
}
|
156
|
+
|
157
|
+
while True:
|
158
|
+
message = await rpcTransport.read()
|
159
|
+
|
160
|
+
if type(message) != dict:
|
161
|
+
deserializationContext['buffers'].append(message)
|
61
162
|
continue
|
62
163
|
|
63
|
-
message = json.loads(data)
|
64
164
|
asyncio.run_coroutine_threadsafe(
|
65
165
|
peer.handleMessage(message, deserializationContext), loop)
|
66
166
|
|
@@ -68,28 +168,11 @@ async def readLoop(loop, peer: rpc.RpcPeer, reader: asyncio.StreamReader):
|
|
68
168
|
'buffers': []
|
69
169
|
}
|
70
170
|
|
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')
|
73
171
|
|
74
|
-
|
172
|
+
async def prepare_peer_readloop(loop: AbstractEventLoop, rpcTransport: RpcTransport):
|
173
|
+
await rpcTransport.prepare()
|
75
174
|
|
76
|
-
|
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)
|
175
|
+
mutex = threading.Lock()
|
93
176
|
|
94
177
|
def send(message, reject=None, serializationContext=None):
|
95
178
|
with mutex:
|
@@ -97,17 +180,9 @@ async def prepare_peer_readloop(loop: AbstractEventLoop, readFd: int = None, wri
|
|
97
180
|
buffers = serializationContext.get('buffers', None)
|
98
181
|
if buffers:
|
99
182
|
for buffer in buffers:
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
write([lb, bytes([type]), buffer], reject)
|
104
|
-
|
105
|
-
jsonString = json.dumps(message)
|
106
|
-
b = bytes(jsonString, 'utf8')
|
107
|
-
length = len(b) + 1
|
108
|
-
lb = length.to_bytes(4, 'big')
|
109
|
-
type = 0
|
110
|
-
write([lb, bytes([type]), b], reject)
|
183
|
+
rpcTransport.writeBuffer(buffer, reject)
|
184
|
+
|
185
|
+
rpcTransport.writeJSON(message, reject)
|
111
186
|
|
112
187
|
peer = rpc.RpcPeer(send)
|
113
188
|
peer.nameDeserializerMap['Buffer'] = SidebandBufferSerializer()
|
@@ -117,7 +192,7 @@ async def prepare_peer_readloop(loop: AbstractEventLoop, readFd: int = None, wri
|
|
117
192
|
|
118
193
|
async def peerReadLoop():
|
119
194
|
try:
|
120
|
-
await readLoop(loop, peer,
|
195
|
+
await readLoop(loop, peer, rpcTransport)
|
121
196
|
except:
|
122
197
|
peer.kill()
|
123
198
|
raise
|