socket-function 0.5.0 → 0.6.0
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.
- package/SocketFunction.ts +41 -13
- package/SocketFunctionTypes.ts +2 -1
- package/package.json +5 -3
- package/require/CSSShim.ts +18 -0
- package/require/RequireController.ts +243 -0
- package/require/buffer.js +2369 -0
- package/require/compileFlags.ts +44 -0
- package/require/require.html +14 -0
- package/require/require.js +464 -0
- package/spec.txt +12 -13
- package/{CallInstance.ts → src/CallFactory.ts} +48 -22
- package/{index.ts → src/allowclient.flag} +0 -0
- package/{args.ts → src/args.ts} +0 -0
- package/{caching.ts → src/caching.ts} +2 -2
- package/src/callHTTPHandler.ts +160 -0
- package/{callManager.ts → src/callManager.ts} +8 -10
- package/{misc.ts → src/misc.ts} +9 -4
- package/{nodeAuthentication.ts → src/nodeAuthentication.ts} +60 -11
- package/{nodeCache.ts → src/nodeCache.ts} +3 -3
- package/{nodeProxy.ts → src/nodeProxy.ts} +4 -4
- package/src/socketServer.ts +140 -0
- package/{storagePath.ts → src/storagePath.ts} +2 -2
- package/{types.ts → src/types.ts} +0 -0
- package/test/allowclient.flag +0 -0
- package/test/client.css +3 -0
- package/test/client.ts +42 -0
- package/test/server.ts +34 -0
- package/test/shared.ts +65 -0
- package/socketServer.ts +0 -74
- package/test.ts +0 -87
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { CallerContext, CallType, NetworkLocation } from "
|
|
1
|
+
import { CallerContext, CallType, NetworkLocation } from "../SocketFunctionTypes";
|
|
2
2
|
import type * as ws from "ws";
|
|
3
3
|
import type * as net from "net";
|
|
4
4
|
import { performLocalCall } from "./callManager";
|
|
5
|
-
import { convertErrorStackToError } from "./misc";
|
|
5
|
+
import { convertErrorStackToError, isNode } from "./misc";
|
|
6
6
|
import { createWebsocket, getNodeId, getTLSSocket } from "./nodeAuthentication";
|
|
7
7
|
import debugbreak from "debugbreak";
|
|
8
|
+
import http from "http";
|
|
8
9
|
|
|
9
10
|
const retryInterval = 2000;
|
|
10
11
|
|
|
@@ -46,7 +47,7 @@ export async function callFactoryFromLocation(
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
export async function callFactoryFromWS(
|
|
49
|
-
webSocket: ws.WebSocket
|
|
50
|
+
webSocket: ws.WebSocket & { nodeId?: string },
|
|
50
51
|
): Promise<CallFactory> {
|
|
51
52
|
let socket = getTLSSocket(webSocket);
|
|
52
53
|
let remoteAddress = socket.remoteAddress;
|
|
@@ -69,8 +70,19 @@ export async function callFactoryFromWS(
|
|
|
69
70
|
return await createCallFactory(webSocket, location);
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
export interface SenderInterface {
|
|
74
|
+
nodeId?: string;
|
|
75
|
+
|
|
76
|
+
send(data: string): void;
|
|
77
|
+
|
|
78
|
+
on(event: "open", listener: () => void): this;
|
|
79
|
+
on(event: "close", listener: (code: number, reason: Buffer) => void): this;
|
|
80
|
+
on(event: "error", listener: (err: Error) => void): this;
|
|
81
|
+
on(event: "message", listener: (data: ws.RawData, isBinary: boolean) => void): this;
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
async function createCallFactory(
|
|
73
|
-
webSocketBase:
|
|
85
|
+
webSocketBase: SenderInterface | undefined,
|
|
74
86
|
location: NetworkLocation,
|
|
75
87
|
): Promise<CallFactory> {
|
|
76
88
|
|
|
@@ -99,14 +111,18 @@ async function createCallFactory(
|
|
|
99
111
|
|
|
100
112
|
const pendingNodeId = "PENDING";
|
|
101
113
|
let callerContext: CallerContext = { location, nodeId: pendingNodeId };
|
|
102
|
-
let webSocket!:
|
|
114
|
+
let webSocket!: SenderInterface;
|
|
103
115
|
if (!webSocketBase) {
|
|
104
116
|
await tryToReconnect();
|
|
105
117
|
} else {
|
|
106
118
|
webSocket = webSocketBase;
|
|
107
119
|
setupWebsocket(webSocketBase);
|
|
108
120
|
}
|
|
109
|
-
|
|
121
|
+
if (isNode()) {
|
|
122
|
+
callerContext.nodeId = getNodeId(webSocket);
|
|
123
|
+
} else {
|
|
124
|
+
callerContext.nodeId = location.address + ":" + location.listeningPorts[0];
|
|
125
|
+
}
|
|
110
126
|
|
|
111
127
|
niceConnectionName = `${niceConnectionName} (${callerContext.nodeId})`;
|
|
112
128
|
|
|
@@ -121,7 +137,7 @@ async function createCallFactory(
|
|
|
121
137
|
if (reconnectTimeout) {
|
|
122
138
|
await Promise.race([
|
|
123
139
|
reconnectingPromise,
|
|
124
|
-
new Promise<
|
|
140
|
+
new Promise<SenderInterface>(resolve =>
|
|
125
141
|
setTimeout(() => {
|
|
126
142
|
retriesEnabled = false;
|
|
127
143
|
resolve(webSocket);
|
|
@@ -173,13 +189,13 @@ async function createCallFactory(
|
|
|
173
189
|
setupWebsocket(newWebSocket);
|
|
174
190
|
|
|
175
191
|
let connectError = await new Promise<string | undefined>(resolve => {
|
|
176
|
-
newWebSocket.
|
|
192
|
+
newWebSocket.on("open", () => {
|
|
177
193
|
resolve(undefined);
|
|
178
194
|
});
|
|
179
|
-
newWebSocket.
|
|
195
|
+
newWebSocket.on("close", () => {
|
|
180
196
|
resolve("Connection closed for non-error reason?");
|
|
181
197
|
});
|
|
182
|
-
newWebSocket.
|
|
198
|
+
newWebSocket.on("error", e => {
|
|
183
199
|
resolve(String(e.stack));
|
|
184
200
|
});
|
|
185
201
|
});
|
|
@@ -187,14 +203,17 @@ async function createCallFactory(
|
|
|
187
203
|
if (!connectError) {
|
|
188
204
|
console.log(`Reconnected to ${location.address}:${port}`);
|
|
189
205
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
callerContext.nodeId
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
206
|
+
// NOTE: Clientside doesn't have access to peer certificates, so it can't know the nodeId of the server
|
|
207
|
+
// that way. However, it can
|
|
208
|
+
if (isNode()) {
|
|
209
|
+
let newNodeId = getNodeId(newWebSocket);
|
|
210
|
+
let prevNodeId = callerContext.nodeId;
|
|
211
|
+
if (prevNodeId === pendingNodeId) {
|
|
212
|
+
callerContext.nodeId = newNodeId;
|
|
213
|
+
} else {
|
|
214
|
+
if (newNodeId !== prevNodeId) {
|
|
215
|
+
throw new Error(`Connection lost to at ${niceConnectionName} ("${prevNodeId}"), but then re-established, however it is now "${newNodeId}"!`);
|
|
216
|
+
}
|
|
198
217
|
}
|
|
199
218
|
}
|
|
200
219
|
|
|
@@ -225,23 +244,30 @@ async function createCallFactory(
|
|
|
225
244
|
})();
|
|
226
245
|
}
|
|
227
246
|
|
|
228
|
-
function setupWebsocket(webSocket:
|
|
247
|
+
function setupWebsocket(webSocket: SenderInterface) {
|
|
229
248
|
webSocket.on("error", e => {
|
|
230
249
|
console.log(`Websocket error for ${niceConnectionName}`, e);
|
|
231
250
|
});
|
|
232
251
|
|
|
233
252
|
webSocket.on("close", async () => {
|
|
234
253
|
console.log(`Websocket closed ${niceConnectionName}`);
|
|
235
|
-
|
|
254
|
+
if (retriesEnabled) {
|
|
255
|
+
await tryToReconnect();
|
|
256
|
+
}
|
|
236
257
|
});
|
|
237
258
|
|
|
238
259
|
webSocket.on("message", onMessage);
|
|
239
260
|
}
|
|
240
261
|
|
|
241
262
|
|
|
242
|
-
async function onMessage(message: ws.RawData) {
|
|
263
|
+
async function onMessage(message: ws.RawData | MessageEvent | string) {
|
|
243
264
|
try {
|
|
244
|
-
if (
|
|
265
|
+
if (!isNode()) {
|
|
266
|
+
if (typeof message === "object" && "data" in message) {
|
|
267
|
+
message = message.data;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (message instanceof Buffer || typeof message === "string") {
|
|
245
271
|
let call = JSON.parse(message.toString()) as InternalCallType | InternalReturnType;
|
|
246
272
|
if (call.isReturn) {
|
|
247
273
|
let callbackObj = pendingCalls.get(call.seqNum);
|
|
File without changes
|
package/{args.ts → src/args.ts}
RENAMED
|
File without changes
|
|
@@ -2,10 +2,10 @@ import { arrayEqual } from "./misc";
|
|
|
2
2
|
import { AnyFunction, Args, canHaveChildren } from "./types";
|
|
3
3
|
|
|
4
4
|
export function lazy<T>(factory: () => T): () => T {
|
|
5
|
-
let value: { value: T }|undefined = undefined;
|
|
5
|
+
let value: { value: T } | undefined = undefined;
|
|
6
6
|
|
|
7
7
|
return () => {
|
|
8
|
-
if(!value) {
|
|
8
|
+
if (!value) {
|
|
9
9
|
value = { value: factory() };
|
|
10
10
|
}
|
|
11
11
|
return value.value;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import https from "https";
|
|
2
|
+
import http from "http";
|
|
3
|
+
import net from "net";
|
|
4
|
+
import tls from "tls";
|
|
5
|
+
import { CallerContext, CallType } from "../SocketFunctionTypes";
|
|
6
|
+
import { performLocalCall } from "./callManager";
|
|
7
|
+
import { getNodeIdRaw } from "./nodeAuthentication";
|
|
8
|
+
import debugbreak from "debugbreak";
|
|
9
|
+
import * as cookie from "cookie";
|
|
10
|
+
|
|
11
|
+
const nodeIdCookie = "node-id3";
|
|
12
|
+
|
|
13
|
+
let defaultHTTPCall: CallType | undefined;
|
|
14
|
+
|
|
15
|
+
export function setDefaultHTTPCall(call: CallType) {
|
|
16
|
+
defaultHTTPCall = call;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getNodeIdFromRequest(request: http.IncomingMessage): string | undefined {
|
|
20
|
+
let cookies = cookie.parse(request.headers.cookie ?? "");
|
|
21
|
+
return cookies[nodeIdCookie];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function httpCallHandler(request: http.IncomingMessage, response: http.ServerResponse) {
|
|
25
|
+
try {
|
|
26
|
+
|
|
27
|
+
let urlBase = request.url;
|
|
28
|
+
if (!urlBase) {
|
|
29
|
+
throw new Error("Missing URL");
|
|
30
|
+
}
|
|
31
|
+
if (urlBase === "/favicon.ico") {
|
|
32
|
+
response.end();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let protocol = "https";
|
|
37
|
+
let url = protocol + "://" + request.headers.host + request.url;
|
|
38
|
+
|
|
39
|
+
console.log(`HTTP request (${request.method}) ${url}`);
|
|
40
|
+
let urlObj = new URL(url);
|
|
41
|
+
|
|
42
|
+
let payload = await new Promise<Buffer>((resolve, reject) => {
|
|
43
|
+
let data: Buffer[] = [];
|
|
44
|
+
request
|
|
45
|
+
.on("data", chunk => data.push(chunk))
|
|
46
|
+
.on("end", () => resolve(Buffer.concat(data)))
|
|
47
|
+
.on("error", (err) => reject(err))
|
|
48
|
+
;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
let socket = request.connection as tls.TLSSocket;
|
|
52
|
+
|
|
53
|
+
let address = socket.remoteAddress;
|
|
54
|
+
let port = socket.remotePort;
|
|
55
|
+
if (!address) {
|
|
56
|
+
throw new Error("Missing remote address");
|
|
57
|
+
}
|
|
58
|
+
if (!port) {
|
|
59
|
+
throw new Error("Missing remote port");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let nodeId = getNodeIdRaw(socket);
|
|
63
|
+
if (!nodeId) {
|
|
64
|
+
let headerNodeId = cookie.parse(request.headers.cookie || "")[nodeIdCookie];
|
|
65
|
+
if (typeof headerNodeId === "string") {
|
|
66
|
+
nodeId = headerNodeId;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!nodeId) {
|
|
70
|
+
nodeId = "HTTP_nodeId_" + Date.now() + "_" + Math.random();
|
|
71
|
+
response.setHeader("Set-Cookie", cookie.serialize(nodeIdCookie, nodeId, {
|
|
72
|
+
httpOnly: true,
|
|
73
|
+
path: "/",
|
|
74
|
+
secure: true,
|
|
75
|
+
domain: urlObj.hostname,
|
|
76
|
+
sameSite: "none"
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
response.setHeader(nodeIdCookie, nodeId);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let caller: CallerContext = {
|
|
83
|
+
nodeId,
|
|
84
|
+
location: {
|
|
85
|
+
address,
|
|
86
|
+
localPort: port,
|
|
87
|
+
listeningPorts: [],
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
let classGuid = urlObj.searchParams.get("classGuid");
|
|
92
|
+
let functionName = urlObj.searchParams.get("functionName");
|
|
93
|
+
let args: string | unknown[] | null = urlObj.searchParams.get("args");
|
|
94
|
+
|
|
95
|
+
if (!classGuid) {
|
|
96
|
+
if (defaultHTTPCall) {
|
|
97
|
+
classGuid = defaultHTTPCall.classGuid;
|
|
98
|
+
functionName = defaultHTTPCall.functionName;
|
|
99
|
+
args = defaultHTTPCall.args;
|
|
100
|
+
} else {
|
|
101
|
+
throw new Error("Missing classGuid in URL query");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (!functionName) {
|
|
105
|
+
throw new Error("Missing functionName in URL query");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!args) {
|
|
109
|
+
args = [];
|
|
110
|
+
} else {
|
|
111
|
+
if (typeof args === "string") {
|
|
112
|
+
args = JSON.parse(args) as unknown[];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (payload.length > 0) {
|
|
117
|
+
args = JSON.parse(payload.toString())["args"] as unknown[];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let call: CallType = {
|
|
121
|
+
classGuid,
|
|
122
|
+
functionName,
|
|
123
|
+
args,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
let result = await performLocalCall({
|
|
127
|
+
caller,
|
|
128
|
+
call
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (typeof result === "object" && result && result instanceof Buffer) {
|
|
132
|
+
let headers = (result as HTTPResultType)[resultHeaders];
|
|
133
|
+
if (headers) {
|
|
134
|
+
for (let headerName in headers) {
|
|
135
|
+
response.setHeader(headerName, headers[headerName]);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
response.write(result);
|
|
139
|
+
} else {
|
|
140
|
+
response.write(JSON.stringify(result));
|
|
141
|
+
}
|
|
142
|
+
response.end();
|
|
143
|
+
} catch (e: any) {
|
|
144
|
+
console.error(`Request error`, e.stack);
|
|
145
|
+
response.writeHead(500, String(e.message));
|
|
146
|
+
response.end();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
const resultHeaders = Symbol("resultHeaders");
|
|
152
|
+
type HTTPResultType = Buffer & { [resultHeaders]?: { [header: string]: string } };
|
|
153
|
+
|
|
154
|
+
export function setHTTPResultHeaders(
|
|
155
|
+
result: HTTPResultType,
|
|
156
|
+
headers: { [header: string]: string },
|
|
157
|
+
): HTTPResultType {
|
|
158
|
+
result[resultHeaders] = headers;
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { CallContextType, CallerContext, CallType, ClientHookContext, HookContext, NetworkLocation, SocketExposedInterface, SocketExposedInterfaceClass, SocketExposedShape, SocketFunctionClientHook, SocketFunctionHook, SocketRegistered } from "
|
|
2
|
-
import { _setSocketContext } from "
|
|
1
|
+
import { CallContextType, CallerContext, CallType, ClientHookContext, HookContext, NetworkLocation, SocketExposedInterface, SocketExposedInterfaceClass, SocketExposedShape, SocketFunctionClientHook, SocketFunctionHook, SocketRegistered } from "../SocketFunctionTypes";
|
|
2
|
+
import { _setSocketContext } from "../SocketFunction";
|
|
3
3
|
|
|
4
4
|
let classes: {
|
|
5
5
|
[classGuid: string]: {
|
|
6
|
-
classType: SocketExposedInterfaceClass;
|
|
7
6
|
controller: SocketExposedInterface;
|
|
8
7
|
shape: SocketExposedShape;
|
|
9
8
|
}
|
|
10
9
|
} = {};
|
|
11
|
-
let exposedClasses = new Set<
|
|
10
|
+
let exposedClasses = new Set<string>();
|
|
12
11
|
|
|
13
12
|
let globalHooks: SocketFunctionHook[] = [];
|
|
14
13
|
let globalClientHooks: SocketFunctionClientHook[] = [];
|
|
@@ -26,7 +25,7 @@ export async function performLocalCall(
|
|
|
26
25
|
throw new Error(`Class ${call.classGuid} not found`);
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
if (!exposedClasses.has(
|
|
28
|
+
if (!exposedClasses.has(call.classGuid)) {
|
|
30
29
|
throw new Error(`Class ${call.classGuid} not exposed`);
|
|
31
30
|
}
|
|
32
31
|
|
|
@@ -55,20 +54,19 @@ export async function performLocalCall(
|
|
|
55
54
|
return await result;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
export function registerClass(classGuid: string,
|
|
57
|
+
export function registerClass(classGuid: string, controller: SocketExposedInterface, shape: SocketExposedShape) {
|
|
59
58
|
if (classes[classGuid]) {
|
|
60
59
|
throw new Error(`Class ${classGuid} already registered`);
|
|
61
60
|
}
|
|
62
61
|
|
|
63
62
|
classes[classGuid] = {
|
|
64
|
-
|
|
65
|
-
controller: new exposedClass() as SocketExposedInterface,
|
|
63
|
+
controller,
|
|
66
64
|
shape,
|
|
67
65
|
};
|
|
68
66
|
}
|
|
69
67
|
|
|
70
|
-
export function exposeClass(exposedClass:
|
|
71
|
-
exposedClasses.add(exposedClass);
|
|
68
|
+
export function exposeClass(exposedClass: SocketRegistered) {
|
|
69
|
+
exposedClasses.add(exposedClass._classGuid);
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
export function registerGlobalHook(hook: SocketFunctionHook) {
|
package/{misc.ts → src/misc.ts}
RENAMED
|
@@ -19,9 +19,14 @@ export function arrayEqual(a: unknown[], b: unknown[]) {
|
|
|
19
19
|
}
|
|
20
20
|
return true;
|
|
21
21
|
}
|
|
22
|
+
export function isNode() {
|
|
23
|
+
return typeof document === "undefined";
|
|
24
|
+
}
|
|
22
25
|
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
27
|
+
if (isNode()) {
|
|
28
|
+
// TODO: Find a better place for this...
|
|
29
|
+
process.on("unhandledRejection", async (reason: any, promise) => {
|
|
30
|
+
console.error(`Uncaught promise rejection: ${String(reason.stack || reason)}`);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import ws from "ws";
|
|
2
2
|
import tls from "tls";
|
|
3
|
+
import net from "net";
|
|
3
4
|
import { getAppFolder } from "./storagePath";
|
|
4
5
|
import fs from "fs";
|
|
5
6
|
import child_process from "child_process";
|
|
@@ -7,12 +8,17 @@ import { cacheWeak, lazy } from "./caching";
|
|
|
7
8
|
import https from "https";
|
|
8
9
|
import debugbreak from "debugbreak";
|
|
9
10
|
import crypto from "crypto";
|
|
10
|
-
import { sha256Hash } from "./misc";
|
|
11
|
+
import { isNode, sha256Hash } from "./misc";
|
|
11
12
|
import { getArgs } from "./args";
|
|
13
|
+
import { SenderInterface } from "./CallFactory";
|
|
12
14
|
|
|
13
15
|
export const getCertKeyPair = lazy((): { key: Buffer; cert: Buffer } => {
|
|
14
16
|
// TODO: Also get this working clientside...
|
|
15
17
|
// - Probably using node-forge, maybe using this as an example: https://github.com/jfromaniello/selfsigned/blob/master/index.js
|
|
18
|
+
// - ALSO, get our nodeId set in our cookies, so HTTP requests can work as well
|
|
19
|
+
// - We will need to call some kind of endpoint to do this?
|
|
20
|
+
// - Then download the certs and try to get the user to install them, so chrome can use
|
|
21
|
+
// them? Otherwise there is no point of having certs clientside.
|
|
16
22
|
|
|
17
23
|
// https://nodejs.org/en/knowledge/HTTP/servers/how-to-create-a-HTTPS-server/
|
|
18
24
|
|
|
@@ -36,27 +42,70 @@ export function getTLSSocket(webSocket: ws.WebSocket) {
|
|
|
36
42
|
return (webSocket as any)._socket as tls.TLSSocket;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
export
|
|
45
|
+
export async function getOwnNodeId() {
|
|
46
|
+
if (!isNode()) {
|
|
47
|
+
throw new Error(`Clientside nodeIds are not exposed to the client`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// This is BASICALLY just sha256Hash(getCertKeyPari().cert), however... I'm not 100% the format
|
|
51
|
+
// is the same, we would have to verify it. It isn't that important, other nodes know our nodeId,
|
|
52
|
+
// and clients don't really have a reason to use this anyway (they can't verify it, they can only
|
|
53
|
+
// really verify with a location).
|
|
54
|
+
throw new Error(`TODO: Implement getOwnNodeId`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const getNodeId = cacheWeak(function (webSocket: SenderInterface | ws.WebSocket & { nodeId?: string }): string {
|
|
58
|
+
if (!(webSocket instanceof ws.WebSocket)) {
|
|
59
|
+
if (!webSocket.nodeId) {
|
|
60
|
+
throw new Error("Sender isn't a WebSocket, and doesn't have a nodeId");
|
|
61
|
+
}
|
|
62
|
+
return webSocket.nodeId;
|
|
63
|
+
}
|
|
40
64
|
let socket = getTLSSocket(webSocket);
|
|
65
|
+
let nodeId = getNodeIdRaw(socket);
|
|
66
|
+
if (!nodeId) {
|
|
67
|
+
if (webSocket.nodeId) {
|
|
68
|
+
return webSocket.nodeId;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Peer certificate must use an RSA key or EC key (which should have a .pubkey property)`);
|
|
71
|
+
}
|
|
72
|
+
return nodeId;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export function getNodeIdRaw(socket: tls.TLSSocket) {
|
|
41
76
|
let peerCert = socket.getPeerCertificate();
|
|
42
77
|
if (!peerCert) {
|
|
43
78
|
throw new Error("WebSocket connections must provided a peer certificate");
|
|
44
79
|
}
|
|
45
80
|
let pubkey = (peerCert as any).pubkey as Buffer | undefined;
|
|
46
81
|
if (!pubkey) {
|
|
47
|
-
|
|
82
|
+
return undefined;
|
|
48
83
|
}
|
|
49
84
|
return sha256Hash(pubkey);
|
|
50
|
-
}
|
|
85
|
+
}
|
|
51
86
|
|
|
52
|
-
export function createWebsocket(address: string, port: number):
|
|
53
|
-
let { key, cert } = getCertKeyPair();
|
|
87
|
+
export function createWebsocket(address: string, port: number): SenderInterface {
|
|
54
88
|
console.log(`Connecting to ${address}:${port}`);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
89
|
+
if (!isNode()) {
|
|
90
|
+
// NOTE: We assume an HTTP request has already been made, which will setup a nodeId cookie
|
|
91
|
+
// (And as this point we can't even use peer certificates if we wanted to, as this must be done
|
|
92
|
+
// directly in the browser)
|
|
93
|
+
let webSocket = new WebSocket(`wss://${address}:${port}`);
|
|
94
|
+
return Object.assign(webSocket, {
|
|
95
|
+
on(event: string, callback: any) {
|
|
96
|
+
// TODO: Use better type safety here
|
|
97
|
+
(webSocket as any)["on" + event] = callback;
|
|
98
|
+
return this as any;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
} else {
|
|
102
|
+
let { key, cert } = getCertKeyPair();
|
|
103
|
+
return new ws.WebSocket(`wss://${address}:${port}`, {
|
|
104
|
+
cert,
|
|
105
|
+
key,
|
|
106
|
+
rejectUnauthorized: false,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
60
109
|
}
|
|
61
110
|
|
|
62
111
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { callFactoryFromLocation, CallFactory } from "./
|
|
2
|
-
import { NetworkLocation } from "
|
|
1
|
+
import { callFactoryFromLocation, CallFactory } from "./CallFactory";
|
|
2
|
+
import { NetworkLocation } from "../SocketFunctionTypes";
|
|
3
3
|
import { MaybePromise } from "./types";
|
|
4
4
|
|
|
5
5
|
// TODO: Add CallInstanceFactory.isClosed, so nodeCache can clean up old entries.
|
|
@@ -74,6 +74,6 @@ export function getCreateCallFactoryLocation(location: NetworkLocation): MaybePr
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
// TODO: Give a special error if the nodeId has been seen, but is only one-way (from HTTP requests).
|
|
77
|
-
export function getCallFactoryNodeId(nodeId: string): CallFactory|undefined {
|
|
77
|
+
export function getCallFactoryNodeId(nodeId: string): CallFactory | undefined {
|
|
78
78
|
return nodeCache.get(nodeId)?.callFactory;
|
|
79
79
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { lazy } from "./caching";
|
|
2
|
-
import { SocketExposedInterface } from "
|
|
2
|
+
import { SocketExposedInterface } from "../SocketFunctionTypes";
|
|
3
3
|
|
|
4
4
|
//todonext
|
|
5
5
|
// We need some sort of cache, but... maybe not here?
|
|
@@ -12,13 +12,13 @@ type CallProxyType = {
|
|
|
12
12
|
let proxyCache = new Map<string, CallProxyType>();
|
|
13
13
|
export function getCallProxy(id: string, callback: (controllerName: string, functionName: string, args: unknown[]) => Promise<unknown>): CallProxyType {
|
|
14
14
|
let value = proxyCache.get(id);
|
|
15
|
-
if(!value) {
|
|
15
|
+
if (!value) {
|
|
16
16
|
let controllerCache = new Map<string, CallProxyType[""]>();
|
|
17
17
|
value = new Proxy(Object.create(null), {
|
|
18
18
|
get(target, controllerName) {
|
|
19
|
-
if(typeof controllerName !== "string") return undefined;
|
|
19
|
+
if (typeof controllerName !== "string") return undefined;
|
|
20
20
|
let controller = controllerCache.get(controllerName);
|
|
21
|
-
if(!controller) {
|
|
21
|
+
if (!controller) {
|
|
22
22
|
controller = new Proxy(Object.create(null), {
|
|
23
23
|
get(target, functionName) {
|
|
24
24
|
if (typeof functionName !== "string") return undefined;
|