@scrypted/server 0.123.21 → 0.123.22
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.
@@ -7,6 +7,7 @@ exports.ClusterForkResult = exports.PeerLiveness = void 0;
|
|
7
7
|
exports.startClusterClient = startClusterClient;
|
8
8
|
exports.createClusterServer = createClusterServer;
|
9
9
|
const events_1 = require("events");
|
10
|
+
const net_1 = __importDefault(require("net"));
|
10
11
|
const source_map_support_1 = require("source-map-support");
|
11
12
|
const tls_1 = __importDefault(require("tls"));
|
12
13
|
const cluster_hash_1 = require("./cluster/cluster-hash");
|
@@ -86,9 +87,20 @@ function startClusterClient(mainFilename) {
|
|
86
87
|
// for local network, and having the immediate socket connection seems
|
87
88
|
// to hang the app since no window is created yet.
|
88
89
|
await (0, sleep_1.sleep)(1000);
|
89
|
-
const
|
90
|
+
const rawSocket = net_1.default.connect({
|
90
91
|
host,
|
91
92
|
port,
|
93
|
+
// require ipv4 to normalize cluster address.
|
94
|
+
family: 4,
|
95
|
+
});
|
96
|
+
try {
|
97
|
+
await (0, events_1.once)(rawSocket, 'connect');
|
98
|
+
}
|
99
|
+
catch (e) {
|
100
|
+
continue;
|
101
|
+
}
|
102
|
+
const socket = tls_1.default.connect({
|
103
|
+
socket: rawSocket,
|
92
104
|
rejectUnauthorized: false,
|
93
105
|
});
|
94
106
|
try {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"scrypted-cluster-main.js","sourceRoot":"","sources":["../src/scrypted-cluster-main.ts"],"names":[],"mappings":";;;;;;
|
1
|
+
{"version":3,"file":"scrypted-cluster-main.js","sourceRoot":"","sources":["../src/scrypted-cluster-main.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
@@ -0,0 +1,227 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import asyncio
|
4
|
+
import base64
|
5
|
+
import hashlib
|
6
|
+
import os
|
7
|
+
from asyncio.events import AbstractEventLoop
|
8
|
+
from collections.abc import Mapping
|
9
|
+
from typing import Any
|
10
|
+
|
11
|
+
import rpc
|
12
|
+
import rpc_reader
|
13
|
+
from typing import TypedDict
|
14
|
+
|
15
|
+
class ClusterObject(TypedDict):
|
16
|
+
id: str
|
17
|
+
address: str
|
18
|
+
port: int
|
19
|
+
proxyId: str
|
20
|
+
sourceKey: str
|
21
|
+
sha256: str
|
22
|
+
|
23
|
+
def isClusterAddress(address: str):
|
24
|
+
return not address or address == os.environ.get("SCRYPTED_CLUSTER_ADDRESS", None)
|
25
|
+
|
26
|
+
def getClusterPeerKey(address: str, port: int):
|
27
|
+
return f"{address}:{port}"
|
28
|
+
class ClusterSetup():
|
29
|
+
def __init__(self, loop: AbstractEventLoop, peer: rpc.RpcPeer):
|
30
|
+
self.loop = loop
|
31
|
+
self.peer = peer
|
32
|
+
self.clusterId: str = None
|
33
|
+
self.clusterSecret: str = None
|
34
|
+
self.clusterAddress: str = None
|
35
|
+
self.clusterPort: int = None
|
36
|
+
self.SCRYPTED_CLUSTER_ADDRESS: str = None
|
37
|
+
self.clusterPeers: Mapping[str, asyncio.Future[rpc.RpcPeer]] = {}
|
38
|
+
|
39
|
+
async def resolveObject(self, id: str, sourceKey: str):
|
40
|
+
sourcePeer: rpc.RpcPeer = (
|
41
|
+
self.peer
|
42
|
+
if not sourceKey
|
43
|
+
else await rpc.maybe_await(self.clusterPeers.get(sourceKey, None))
|
44
|
+
)
|
45
|
+
if not sourcePeer:
|
46
|
+
return
|
47
|
+
return sourcePeer.localProxyMap.get(id, None)
|
48
|
+
|
49
|
+
async def connectClusterObject(self, o: ClusterObject):
|
50
|
+
sha256 = self.computeClusterObjectHash(o)
|
51
|
+
if sha256 != o["sha256"]:
|
52
|
+
raise Exception("secret incorrect")
|
53
|
+
return await self.resolveObject(o.get('proxyId', None), o.get('sourceKey', None))
|
54
|
+
|
55
|
+
def onProxySerialization(self, peer: rpc.RpcPeer, value: Any, sourceKey: str = None):
|
56
|
+
properties: dict = rpc.RpcPeer.prepareProxyProperties(value) or {}
|
57
|
+
clusterEntry = properties.get("__cluster", None)
|
58
|
+
proxyId: str
|
59
|
+
existing = peer.localProxied.get(value, None)
|
60
|
+
if existing:
|
61
|
+
proxyId = existing["id"]
|
62
|
+
else:
|
63
|
+
proxyId = (
|
64
|
+
clusterEntry and clusterEntry.get("proxyId", None)
|
65
|
+
) or rpc.RpcPeer.generateId()
|
66
|
+
|
67
|
+
if clusterEntry:
|
68
|
+
if (
|
69
|
+
isClusterAddress(clusterEntry.get("address", None))
|
70
|
+
and self.clusterPort == clusterEntry["port"]
|
71
|
+
and sourceKey != clusterEntry.get("sourceKey", None)
|
72
|
+
):
|
73
|
+
clusterEntry = None
|
74
|
+
|
75
|
+
if not clusterEntry:
|
76
|
+
clusterEntry: ClusterObject = {
|
77
|
+
"id": self.clusterId,
|
78
|
+
"proxyId": proxyId,
|
79
|
+
"address": self.SCRYPTED_CLUSTER_ADDRESS,
|
80
|
+
"port": self.clusterPort,
|
81
|
+
"sourceKey": sourceKey,
|
82
|
+
}
|
83
|
+
clusterEntry["sha256"] = self.computeClusterObjectHash(clusterEntry)
|
84
|
+
properties["__cluster"] = clusterEntry
|
85
|
+
|
86
|
+
return proxyId, properties
|
87
|
+
|
88
|
+
async def initializeCluster(self, options: dict):
|
89
|
+
if self.clusterPort:
|
90
|
+
return
|
91
|
+
self.clusterId = options["clusterId"]
|
92
|
+
self.clusterSecret = options["clusterSecret"]
|
93
|
+
self.SCRYPTED_CLUSTER_ADDRESS = os.environ.get("SCRYPTED_CLUSTER_ADDRESS", None)
|
94
|
+
|
95
|
+
async def handleClusterClient(
|
96
|
+
reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
97
|
+
):
|
98
|
+
clusterPeerAddress, clusterPeerPort = writer.get_extra_info("peername")
|
99
|
+
clusterPeerKey = getClusterPeerKey(clusterPeerAddress, clusterPeerPort)
|
100
|
+
rpcTransport = rpc_reader.RpcStreamTransport(reader, writer)
|
101
|
+
peer: rpc.RpcPeer
|
102
|
+
peer, peerReadLoop = await rpc_reader.prepare_peer_readloop(
|
103
|
+
self.loop, rpcTransport
|
104
|
+
)
|
105
|
+
# set all params from self.peer
|
106
|
+
for key, value in self.peer.params.items():
|
107
|
+
peer.params[key] = value
|
108
|
+
peer.onProxySerialization = lambda value: self.onProxySerialization(
|
109
|
+
peer, value, clusterPeerKey
|
110
|
+
)
|
111
|
+
future: asyncio.Future[rpc.RpcPeer] = asyncio.Future()
|
112
|
+
future.set_result(peer)
|
113
|
+
self.clusterPeers[clusterPeerKey] = future
|
114
|
+
peer.params["connectRPCObject"] = lambda o: self.connectClusterObject(o)
|
115
|
+
try:
|
116
|
+
await peerReadLoop()
|
117
|
+
except:
|
118
|
+
pass
|
119
|
+
finally:
|
120
|
+
self.clusterPeers.pop(clusterPeerKey)
|
121
|
+
peer.kill("cluster client killed")
|
122
|
+
writer.close()
|
123
|
+
|
124
|
+
listenAddress = "0.0.0.0" if self.SCRYPTED_CLUSTER_ADDRESS else "127.0.0.1"
|
125
|
+
clusterRpcServer = await asyncio.start_server(
|
126
|
+
handleClusterClient, listenAddress, 0
|
127
|
+
)
|
128
|
+
self.clusterPort = clusterRpcServer.sockets[0].getsockname()[1]
|
129
|
+
self.peer.onProxySerialization = lambda value: self.onProxySerialization(self.peer, value, None)
|
130
|
+
del self.peer.params["initializeCluster"]
|
131
|
+
|
132
|
+
def computeClusterObjectHash(self, o: ClusterObject) -> str:
|
133
|
+
m = hashlib.sha256()
|
134
|
+
m.update(
|
135
|
+
bytes(
|
136
|
+
f"{o['id']}{o.get('address') or ''}{o['port']}{o.get('sourceKey', None) or ''}{o['proxyId']}{self.clusterSecret}",
|
137
|
+
"utf8",
|
138
|
+
)
|
139
|
+
)
|
140
|
+
return base64.b64encode(m.digest()).decode("utf-8")
|
141
|
+
|
142
|
+
def ensureClusterPeer(self, address: str, port: int):
|
143
|
+
if isClusterAddress(address):
|
144
|
+
address = "127.0.0.1"
|
145
|
+
clusterPeerKey = getClusterPeerKey(address, port)
|
146
|
+
clusterPeerPromise = self.clusterPeers.get(clusterPeerKey)
|
147
|
+
if clusterPeerPromise:
|
148
|
+
return clusterPeerPromise
|
149
|
+
|
150
|
+
async def connectClusterPeer():
|
151
|
+
try:
|
152
|
+
reader, writer = await asyncio.open_connection(address, port)
|
153
|
+
sourceAddress, sourcePort = writer.get_extra_info("sockname")
|
154
|
+
if (
|
155
|
+
sourceAddress != self.SCRYPTED_CLUSTER_ADDRESS
|
156
|
+
and sourceAddress != "127.0.0.1"
|
157
|
+
):
|
158
|
+
print("source address mismatch", sourceAddress)
|
159
|
+
rpcTransport = rpc_reader.RpcStreamTransport(reader, writer)
|
160
|
+
clusterPeer, peerReadLoop = await rpc_reader.prepare_peer_readloop(
|
161
|
+
self.loop, rpcTransport
|
162
|
+
)
|
163
|
+
# set all params from self.peer
|
164
|
+
for key, value in self.peer.params.items():
|
165
|
+
clusterPeer.params[key] = value
|
166
|
+
clusterPeer.onProxySerialization = (
|
167
|
+
lambda value: self.clusterSetup.onProxySerialization(
|
168
|
+
clusterPeer, value, clusterPeerKey
|
169
|
+
)
|
170
|
+
)
|
171
|
+
except:
|
172
|
+
self.clusterPeers.pop(clusterPeerKey)
|
173
|
+
raise
|
174
|
+
|
175
|
+
async def run_loop():
|
176
|
+
try:
|
177
|
+
await peerReadLoop()
|
178
|
+
except:
|
179
|
+
pass
|
180
|
+
finally:
|
181
|
+
self.clusterPeers.pop(clusterPeerKey)
|
182
|
+
|
183
|
+
asyncio.run_coroutine_threadsafe(run_loop(), self.loop)
|
184
|
+
return clusterPeer
|
185
|
+
|
186
|
+
clusterPeerPromise = self.loop.create_task(connectClusterPeer())
|
187
|
+
|
188
|
+
self.clusterPeers[clusterPeerKey] = clusterPeerPromise
|
189
|
+
return clusterPeerPromise
|
190
|
+
|
191
|
+
async def connectRPCObject(self, value):
|
192
|
+
__cluster = getattr(value, "__cluster")
|
193
|
+
if type(__cluster) is not dict:
|
194
|
+
return value
|
195
|
+
|
196
|
+
clusterObject: ClusterObject = __cluster
|
197
|
+
|
198
|
+
if clusterObject.get("id", None) != self.clusterId:
|
199
|
+
return value
|
200
|
+
|
201
|
+
address = clusterObject.get("address", None)
|
202
|
+
port = clusterObject["port"]
|
203
|
+
proxyId = clusterObject["proxyId"]
|
204
|
+
if port == self.clusterPort:
|
205
|
+
return await self.connectRPCObject(clusterObject)
|
206
|
+
|
207
|
+
clusterPeerPromise = self.ensureClusterPeer(address, port)
|
208
|
+
|
209
|
+
try:
|
210
|
+
clusterPeer = await clusterPeerPromise
|
211
|
+
weakref = clusterPeer.remoteWeakProxies.get(proxyId, None)
|
212
|
+
existing = weakref() if weakref else None
|
213
|
+
if existing:
|
214
|
+
return existing
|
215
|
+
|
216
|
+
peerConnectRPCObject = clusterPeer.tags.get("connectRPCObject")
|
217
|
+
if not peerConnectRPCObject:
|
218
|
+
peerConnectRPCObject = await clusterPeer.getParam(
|
219
|
+
"connectRPCObject"
|
220
|
+
)
|
221
|
+
clusterPeer.tags["connectRPCObject"] = peerConnectRPCObject
|
222
|
+
newValue = await peerConnectRPCObject(clusterObject)
|
223
|
+
if not newValue:
|
224
|
+
raise Exception("rpc object not found?")
|
225
|
+
return newValue
|
226
|
+
except Exception as e:
|
227
|
+
return value
|
package/python/plugin_remote.py
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import asyncio
|
4
|
-
import base64
|
5
4
|
import gc
|
6
|
-
import hashlib
|
7
5
|
import inspect
|
8
6
|
import multiprocessing
|
9
7
|
import multiprocessing.connection
|
@@ -19,39 +17,27 @@ from asyncio.futures import Future
|
|
19
17
|
from asyncio.streams import StreamReader, StreamWriter
|
20
18
|
from collections.abc import Mapping
|
21
19
|
from io import StringIO
|
22
|
-
from typing import Any, Optional, Set, Tuple, TypedDict
|
20
|
+
from typing import Any, Callable, Coroutine, Optional, Set, Tuple, TypedDict
|
23
21
|
|
24
22
|
import plugin_volume as pv
|
25
23
|
import rpc
|
26
24
|
import rpc_reader
|
27
25
|
import scrypted_python.scrypted_sdk.types
|
26
|
+
from cluster_setup import ClusterSetup
|
28
27
|
from plugin_pip import install_with_pip, need_requirements, remove_pip_dirs
|
29
28
|
from scrypted_python.scrypted_sdk import PluginFork, ScryptedStatic
|
30
|
-
from scrypted_python.scrypted_sdk.types import (
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
ScryptedInterfaceProperty,
|
37
|
-
Storage,
|
38
|
-
)
|
29
|
+
from scrypted_python.scrypted_sdk.types import (Device, DeviceManifest,
|
30
|
+
EventDetails,
|
31
|
+
ScryptedInterface,
|
32
|
+
ScryptedInterfaceMethods,
|
33
|
+
ScryptedInterfaceProperty,
|
34
|
+
Storage)
|
39
35
|
|
40
36
|
SCRYPTED_REQUIREMENTS = """
|
41
37
|
ptpython
|
42
38
|
wheel
|
43
39
|
""".strip()
|
44
40
|
|
45
|
-
|
46
|
-
class ClusterObject(TypedDict):
|
47
|
-
id: str
|
48
|
-
address: str
|
49
|
-
port: int
|
50
|
-
proxyId: str
|
51
|
-
sourceKey: str
|
52
|
-
sha256: str
|
53
|
-
|
54
|
-
|
55
41
|
class SystemDeviceState(TypedDict):
|
56
42
|
lastEventTime: int
|
57
43
|
stateTime: int
|
@@ -553,214 +539,6 @@ class DeviceManager(scrypted_python.scrypted_sdk.types.DeviceManager):
|
|
553
539
|
def getDeviceStorage(self, nativeId: str = None) -> Storage:
|
554
540
|
return self.nativeIds.get(nativeId, None)
|
555
541
|
|
556
|
-
|
557
|
-
def isClusterAddress(address: str):
|
558
|
-
return not address or address == os.environ.get("SCRYPTED_CLUSTER_ADDRESS", None)
|
559
|
-
|
560
|
-
def getClusterPeerKey(address: str, port: int):
|
561
|
-
return f"{address}:{port}"
|
562
|
-
|
563
|
-
class ClusterSetup():
|
564
|
-
def __init__(self, loop: AbstractEventLoop, peer: rpc.RpcPeer):
|
565
|
-
self.loop = loop
|
566
|
-
self.peer = peer
|
567
|
-
self.clusterId: str = None
|
568
|
-
self.clusterSecret: str = None
|
569
|
-
self.clusterAddress: str = None
|
570
|
-
self.clusterPort: int = None
|
571
|
-
self.SCRYPTED_CLUSTER_ADDRESS: str = None
|
572
|
-
self.clusterPeers: Mapping[str, asyncio.Future[rpc.RpcPeer]] = {}
|
573
|
-
|
574
|
-
async def resolveObject(self, id: str, sourceKey: str):
|
575
|
-
sourcePeer: rpc.RpcPeer = (
|
576
|
-
self.peer
|
577
|
-
if not sourceKey
|
578
|
-
else await rpc.maybe_await(self.clusterPeers.get(sourceKey, None))
|
579
|
-
)
|
580
|
-
if not sourcePeer:
|
581
|
-
return
|
582
|
-
return sourcePeer.localProxyMap.get(id, None)
|
583
|
-
|
584
|
-
async def connectClusterObject(self, o: ClusterObject):
|
585
|
-
sha256 = self.computeClusterObjectHash(o)
|
586
|
-
if sha256 != o["sha256"]:
|
587
|
-
raise Exception("secret incorrect")
|
588
|
-
return await self.resolveObject(o.get('proxyId', None), o.get('sourceKey', None))
|
589
|
-
|
590
|
-
def onProxySerialization(self, peer: rpc.RpcPeer, value: Any, sourceKey: str = None):
|
591
|
-
properties: dict = rpc.RpcPeer.prepareProxyProperties(value) or {}
|
592
|
-
clusterEntry = properties.get("__cluster", None)
|
593
|
-
proxyId: str
|
594
|
-
existing = peer.localProxied.get(value, None)
|
595
|
-
if existing:
|
596
|
-
proxyId = existing["id"]
|
597
|
-
else:
|
598
|
-
proxyId = (
|
599
|
-
clusterEntry and clusterEntry.get("proxyId", None)
|
600
|
-
) or rpc.RpcPeer.generateId()
|
601
|
-
|
602
|
-
if clusterEntry:
|
603
|
-
if (
|
604
|
-
isClusterAddress(clusterEntry.get("address", None))
|
605
|
-
and self.clusterPort == clusterEntry["port"]
|
606
|
-
and sourceKey != clusterEntry.get("sourceKey", None)
|
607
|
-
):
|
608
|
-
clusterEntry = None
|
609
|
-
|
610
|
-
if not clusterEntry:
|
611
|
-
clusterEntry: ClusterObject = {
|
612
|
-
"id": self.clusterId,
|
613
|
-
"proxyId": proxyId,
|
614
|
-
"address": self.SCRYPTED_CLUSTER_ADDRESS,
|
615
|
-
"port": self.clusterPort,
|
616
|
-
"sourceKey": sourceKey,
|
617
|
-
}
|
618
|
-
clusterEntry["sha256"] = self.computeClusterObjectHash(clusterEntry)
|
619
|
-
properties["__cluster"] = clusterEntry
|
620
|
-
|
621
|
-
return proxyId, properties
|
622
|
-
|
623
|
-
async def initializeCluster(self, options: dict):
|
624
|
-
if self.clusterPort:
|
625
|
-
return
|
626
|
-
self.clusterId = options["clusterId"]
|
627
|
-
self.clusterSecret = options["clusterSecret"]
|
628
|
-
self.SCRYPTED_CLUSTER_ADDRESS = os.environ.get("SCRYPTED_CLUSTER_ADDRESS", None)
|
629
|
-
|
630
|
-
async def handleClusterClient(
|
631
|
-
reader: asyncio.StreamReader, writer: asyncio.StreamWriter
|
632
|
-
):
|
633
|
-
clusterPeerAddress, clusterPeerPort = writer.get_extra_info("peername")
|
634
|
-
clusterPeerKey = getClusterPeerKey(clusterPeerAddress, clusterPeerPort)
|
635
|
-
rpcTransport = rpc_reader.RpcStreamTransport(reader, writer)
|
636
|
-
peer: rpc.RpcPeer
|
637
|
-
peer, peerReadLoop = await rpc_reader.prepare_peer_readloop(
|
638
|
-
self.loop, rpcTransport
|
639
|
-
)
|
640
|
-
# set all params from self.peer
|
641
|
-
for key, value in self.peer.params.items():
|
642
|
-
peer.params[key] = value
|
643
|
-
peer.onProxySerialization = lambda value: self.onProxySerialization(
|
644
|
-
peer, value, clusterPeerKey
|
645
|
-
)
|
646
|
-
future: asyncio.Future[rpc.RpcPeer] = asyncio.Future()
|
647
|
-
future.set_result(peer)
|
648
|
-
self.clusterPeers[clusterPeerKey] = future
|
649
|
-
peer.params["connectRPCObject"] = lambda o: self.connectClusterObject(o)
|
650
|
-
try:
|
651
|
-
await peerReadLoop()
|
652
|
-
except:
|
653
|
-
pass
|
654
|
-
finally:
|
655
|
-
self.clusterPeers.pop(clusterPeerKey)
|
656
|
-
peer.kill("cluster client killed")
|
657
|
-
writer.close()
|
658
|
-
|
659
|
-
listenAddress = "0.0.0.0" if self.SCRYPTED_CLUSTER_ADDRESS else "127.0.0.1"
|
660
|
-
clusterRpcServer = await asyncio.start_server(
|
661
|
-
handleClusterClient, listenAddress, 0
|
662
|
-
)
|
663
|
-
self.clusterPort = clusterRpcServer.sockets[0].getsockname()[1]
|
664
|
-
self.peer.onProxySerialization = lambda value: self.onProxySerialization(self.peer, value, None)
|
665
|
-
del self.peer.params["initializeCluster"]
|
666
|
-
|
667
|
-
def computeClusterObjectHash(self, o: ClusterObject) -> str:
|
668
|
-
m = hashlib.sha256()
|
669
|
-
m.update(
|
670
|
-
bytes(
|
671
|
-
f"{o['id']}{o.get('address') or ''}{o['port']}{o.get('sourceKey', None) or ''}{o['proxyId']}{self.clusterSecret}",
|
672
|
-
"utf8",
|
673
|
-
)
|
674
|
-
)
|
675
|
-
return base64.b64encode(m.digest()).decode("utf-8")
|
676
|
-
|
677
|
-
def ensureClusterPeer(self, address: str, port: int):
|
678
|
-
if isClusterAddress(address):
|
679
|
-
address = "127.0.0.1"
|
680
|
-
clusterPeerKey = getClusterPeerKey(address, port)
|
681
|
-
clusterPeerPromise = self.clusterPeers.get(clusterPeerKey)
|
682
|
-
if clusterPeerPromise:
|
683
|
-
return clusterPeerPromise
|
684
|
-
|
685
|
-
async def connectClusterPeer():
|
686
|
-
try:
|
687
|
-
reader, writer = await asyncio.open_connection(address, port)
|
688
|
-
sourceAddress, sourcePort = writer.get_extra_info("sockname")
|
689
|
-
if (
|
690
|
-
sourceAddress != self.SCRYPTED_CLUSTER_ADDRESS
|
691
|
-
and sourceAddress != "127.0.0.1"
|
692
|
-
):
|
693
|
-
print("source address mismatch", sourceAddress)
|
694
|
-
rpcTransport = rpc_reader.RpcStreamTransport(reader, writer)
|
695
|
-
clusterPeer, peerReadLoop = await rpc_reader.prepare_peer_readloop(
|
696
|
-
self.loop, rpcTransport
|
697
|
-
)
|
698
|
-
# set all params from self.peer
|
699
|
-
for key, value in self.peer.params.items():
|
700
|
-
clusterPeer.params[key] = value
|
701
|
-
clusterPeer.onProxySerialization = (
|
702
|
-
lambda value: self.clusterSetup.onProxySerialization(
|
703
|
-
clusterPeer, value, clusterPeerKey
|
704
|
-
)
|
705
|
-
)
|
706
|
-
except:
|
707
|
-
self.clusterPeers.pop(clusterPeerKey)
|
708
|
-
raise
|
709
|
-
|
710
|
-
async def run_loop():
|
711
|
-
try:
|
712
|
-
await peerReadLoop()
|
713
|
-
except:
|
714
|
-
pass
|
715
|
-
finally:
|
716
|
-
self.clusterPeers.pop(clusterPeerKey)
|
717
|
-
|
718
|
-
asyncio.run_coroutine_threadsafe(run_loop(), self.loop)
|
719
|
-
return clusterPeer
|
720
|
-
|
721
|
-
clusterPeerPromise = self.loop.create_task(connectClusterPeer())
|
722
|
-
|
723
|
-
self.clusterPeers[clusterPeerKey] = clusterPeerPromise
|
724
|
-
return clusterPeerPromise
|
725
|
-
|
726
|
-
async def connectRPCObject(self, value):
|
727
|
-
__cluster = getattr(value, "__cluster")
|
728
|
-
if type(__cluster) is not dict:
|
729
|
-
return value
|
730
|
-
|
731
|
-
clusterObject: ClusterObject = __cluster
|
732
|
-
|
733
|
-
if clusterObject.get("id", None) != self.clusterId:
|
734
|
-
return value
|
735
|
-
|
736
|
-
address = clusterObject.get("address", None)
|
737
|
-
port = clusterObject["port"]
|
738
|
-
proxyId = clusterObject["proxyId"]
|
739
|
-
if port == self.clusterPort:
|
740
|
-
return await self.connectRPCObject(clusterObject)
|
741
|
-
|
742
|
-
clusterPeerPromise = self.ensureClusterPeer(address, port)
|
743
|
-
|
744
|
-
try:
|
745
|
-
clusterPeer = await clusterPeerPromise
|
746
|
-
weakref = clusterPeer.remoteWeakProxies.get(proxyId, None)
|
747
|
-
existing = weakref() if weakref else None
|
748
|
-
if existing:
|
749
|
-
return existing
|
750
|
-
|
751
|
-
peerConnectRPCObject = clusterPeer.tags.get("connectRPCObject")
|
752
|
-
if not peerConnectRPCObject:
|
753
|
-
peerConnectRPCObject = await clusterPeer.getParam(
|
754
|
-
"connectRPCObject"
|
755
|
-
)
|
756
|
-
clusterPeer.tags["connectRPCObject"] = peerConnectRPCObject
|
757
|
-
newValue = await peerConnectRPCObject(clusterObject)
|
758
|
-
if not newValue:
|
759
|
-
raise Exception("rpc object not found?")
|
760
|
-
return newValue
|
761
|
-
except Exception as e:
|
762
|
-
return value
|
763
|
-
|
764
542
|
class PluginRemote:
|
765
543
|
def __init__(
|
766
544
|
self, clusterSetup: ClusterSetup, api, pluginId: str, hostInfo, loop: AbstractEventLoop
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import type { ForkOptions } from '@scrypted/types';
|
2
2
|
import { once } from 'events';
|
3
|
+
import net from 'net';
|
3
4
|
import { install as installSourceMapSupport } from 'source-map-support';
|
4
5
|
import type { Readable } from 'stream';
|
5
6
|
import tls from 'tls';
|
@@ -109,9 +110,22 @@ export function startClusterClient(mainFilename: string) {
|
|
109
110
|
// to hang the app since no window is created yet.
|
110
111
|
await sleep(1000);
|
111
112
|
|
112
|
-
const
|
113
|
+
const rawSocket = net.connect({
|
113
114
|
host,
|
114
115
|
port,
|
116
|
+
// require ipv4 to normalize cluster address.
|
117
|
+
family: 4,
|
118
|
+
});
|
119
|
+
|
120
|
+
try {
|
121
|
+
await once(rawSocket, 'connect');
|
122
|
+
}
|
123
|
+
catch( e) {
|
124
|
+
continue;
|
125
|
+
}
|
126
|
+
|
127
|
+
const socket = tls.connect({
|
128
|
+
socket: rawSocket,
|
115
129
|
rejectUnauthorized: false,
|
116
130
|
});
|
117
131
|
|