socket-function 0.7.7 → 0.7.10
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 +5 -7
- package/SocketFunctionTypes.ts +4 -5
- package/package.json +2 -2
- package/require/RequireController.ts +16 -13
- package/src/CallFactory.ts +24 -12
- package/src/callHTTPHandler.ts +28 -5
- package/src/nodeAuthentication.ts +1 -1
- package/src/nodeCache.ts +1 -1
- package/src/{socketServer.ts → webSocketServer.ts} +2 -2
- package/test/client.ts +2 -1
- package/test/server.ts +2 -2
package/SocketFunction.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SocketExposedInterface, CallContextType, SocketFunctionHook, SocketFunctionClientHook, SocketExposedShape, SocketRegistered, NetworkLocation, CallerContext, SocketExposedInterfaceClass, CallType } from "./SocketFunctionTypes";
|
|
2
2
|
import { exposeClass, registerClass, registerGlobalClientHook, registerGlobalHook, runClientHooks } from "./src/callManager";
|
|
3
|
-
import { SocketServerConfig, startSocketServer } from "./src/
|
|
3
|
+
import { SocketServerConfig, startSocketServer } from "./src/webSocketServer";
|
|
4
4
|
import { getCallFactoryNodeId, getCreateCallFactoryLocation, getNetworkLocationHash } from "./src/nodeCache";
|
|
5
5
|
import { getCallProxy } from "./src/nodeProxy";
|
|
6
6
|
import { Args } from "./src/types";
|
|
@@ -113,22 +113,20 @@ export class SocketFunction {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
public static async connect(location: NetworkLocation | { address: string; port: number }): Promise<string> {
|
|
116
|
-
if (!("
|
|
116
|
+
if (!("listeningPorts" in location)) {
|
|
117
117
|
location = {
|
|
118
118
|
address: location.address,
|
|
119
|
-
listeningPorts: [location.port]
|
|
120
|
-
localPort: 0,
|
|
119
|
+
listeningPorts: [location.port]
|
|
121
120
|
};
|
|
122
121
|
}
|
|
123
122
|
return await getCreateCallFactoryLocation(location);
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
public static connectSync(location: NetworkLocation | { address: string; port: number }): string {
|
|
127
|
-
if (!("
|
|
126
|
+
if (!("listeningPorts" in location)) {
|
|
128
127
|
location = {
|
|
129
128
|
address: location.address,
|
|
130
|
-
listeningPorts: [location.port]
|
|
131
|
-
localPort: 0,
|
|
129
|
+
listeningPorts: [location.port]
|
|
132
130
|
};
|
|
133
131
|
}
|
|
134
132
|
let tempNodeId = "syncTempNodeId_" + getNetworkLocationHash(location);
|
package/SocketFunctionTypes.ts
CHANGED
|
@@ -65,17 +65,16 @@ export type CallerContext = {
|
|
|
65
65
|
// them call functions directly using nodeId. Instead pass location, and have them use connect.
|
|
66
66
|
// - nodeId SHOULD be used to identify users though, as it cannot be impersonated
|
|
67
67
|
nodeId: string;
|
|
68
|
+
fromPort: number;
|
|
68
69
|
location: NetworkLocation;
|
|
70
|
+
// The location of the server. It helps if it is told, due to the fact that one server
|
|
71
|
+
// can serve multiple domains.
|
|
72
|
+
serverLocation: NetworkLocation;
|
|
69
73
|
};
|
|
70
74
|
|
|
71
75
|
// IMPORTANT! Nodes at the same network location may vary, so you cannot store NetworkLocation
|
|
72
76
|
// in a list of allowed users, otherwise they can be impersonated!
|
|
73
77
|
export interface NetworkLocation {
|
|
74
78
|
address: string;
|
|
75
|
-
/** localPort is the port the connection was made from, which is often uninteresting.
|
|
76
|
-
* It is useful internally, to help reduce collisions when a process restarts and
|
|
77
|
-
* reconnects (as it may have the same source address, but the localPort will likely change).
|
|
78
|
-
*/
|
|
79
|
-
localPort: number;
|
|
80
79
|
listeningPorts: number[];
|
|
81
80
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "socket-function",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.10",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"dependencies": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"ws": "^8.8.0"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"test": "yarn typenode ./
|
|
16
|
+
"test": "yarn typenode ./test/server.ts",
|
|
17
17
|
"type": "yarn tsc --noEmit"
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -66,7 +66,7 @@ const resolvedHTMLFile = isNodeTrue() && (
|
|
|
66
66
|
);
|
|
67
67
|
|
|
68
68
|
class RequireControllerBase {
|
|
69
|
-
|
|
69
|
+
public rootResolvePath = "";
|
|
70
70
|
|
|
71
71
|
public async requireHTML(bootRequirePath?: string) {
|
|
72
72
|
let result = resolvedHTMLFile;
|
|
@@ -229,15 +229,18 @@ class RequireControllerBase {
|
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
}
|
|
232
|
+
let baseController = new RequireControllerBase();
|
|
233
|
+
export function setRequireBootRequire(path: string) {
|
|
234
|
+
baseController.rootResolvePath = path;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export const RequireController = SocketFunction.register(
|
|
238
|
+
"RequireController-e2f811f3-14b8-4759-b0d6-73f14516cf1d",
|
|
239
|
+
baseController,
|
|
240
|
+
{
|
|
241
|
+
getModules: {},
|
|
242
|
+
requireHTML: {},
|
|
243
|
+
bufferJS: {},
|
|
244
|
+
requireJS: {},
|
|
245
|
+
}
|
|
246
|
+
);
|
package/src/CallFactory.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CallerContext, CallType, NetworkLocation } from "../SocketFunctionTypes";
|
|
2
|
-
import
|
|
2
|
+
import * as ws from "ws";
|
|
3
3
|
import type * as net from "net";
|
|
4
4
|
import { performLocalCall } from "./callManager";
|
|
5
5
|
import { convertErrorStackToError, isNode } from "./misc";
|
|
@@ -34,20 +34,24 @@ export interface CallFactory {
|
|
|
34
34
|
export async function callFactoryFromLocation(
|
|
35
35
|
location: NetworkLocation
|
|
36
36
|
): Promise<CallFactory> {
|
|
37
|
-
if (location.localPort !== 0) {
|
|
38
|
-
throw new Error(`Expected localPort to be 0, but it was ${location.localPort}`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
37
|
let listeningPort = location.listeningPorts[0];
|
|
42
38
|
if (typeof listeningPort !== "number") {
|
|
43
39
|
throw new Error(`Expected listeningPorts to be provided, but it was empty`);
|
|
44
40
|
}
|
|
45
41
|
|
|
46
|
-
|
|
42
|
+
// Because we are the client, we don't get to know our NetworkLocation (but we shouldn't
|
|
43
|
+
// need to anyway).
|
|
44
|
+
let serverLocation: NetworkLocation = {
|
|
45
|
+
address: "localhost",
|
|
46
|
+
listeningPorts: [],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return await createCallFactory(undefined, location, serverLocation);
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
export async function callFactoryFromWS(
|
|
50
53
|
webSocket: ws.WebSocket & { nodeId?: string },
|
|
54
|
+
serverLocation: NetworkLocation,
|
|
51
55
|
): Promise<CallFactory> {
|
|
52
56
|
let socket = getTLSSocket(webSocket);
|
|
53
57
|
let remoteAddress = socket.remoteAddress;
|
|
@@ -63,11 +67,10 @@ export async function callFactoryFromWS(
|
|
|
63
67
|
// their process is dead, and is going to stay dead.
|
|
64
68
|
let location: NetworkLocation = {
|
|
65
69
|
address: remoteAddress,
|
|
66
|
-
localPort: remotePort,
|
|
67
70
|
listeningPorts: [],
|
|
68
71
|
};
|
|
69
72
|
|
|
70
|
-
return await createCallFactory(webSocket, location);
|
|
73
|
+
return await createCallFactory(webSocket, location, serverLocation);
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
export interface SenderInterface {
|
|
@@ -84,11 +87,20 @@ export interface SenderInterface {
|
|
|
84
87
|
async function createCallFactory(
|
|
85
88
|
webSocketBase: SenderInterface | undefined,
|
|
86
89
|
location: NetworkLocation,
|
|
90
|
+
serverLocation: NetworkLocation,
|
|
87
91
|
): Promise<CallFactory> {
|
|
88
92
|
|
|
89
93
|
let closedForever = false;
|
|
90
94
|
|
|
91
|
-
let
|
|
95
|
+
let fromPort = 0;
|
|
96
|
+
if (webSocketBase && webSocketBase instanceof ws.WebSocket) {
|
|
97
|
+
let socket = getTLSSocket(webSocketBase);
|
|
98
|
+
fromPort = socket.remotePort ?? fromPort;
|
|
99
|
+
}
|
|
100
|
+
let niceConnectionName = `${location.address}:${location.listeningPorts.join("|")}`;
|
|
101
|
+
if (fromPort && location.listeningPorts.length === 0) {
|
|
102
|
+
niceConnectionName += `(${fromPort})`;
|
|
103
|
+
}
|
|
92
104
|
|
|
93
105
|
let retriesEnabled = location.listeningPorts.length === 0;
|
|
94
106
|
|
|
@@ -110,7 +122,7 @@ async function createCallFactory(
|
|
|
110
122
|
let nextSeqNum = Math.random();
|
|
111
123
|
|
|
112
124
|
const pendingNodeId = "PENDING";
|
|
113
|
-
let callerContext: CallerContext = { location, nodeId: pendingNodeId };
|
|
125
|
+
let callerContext: CallerContext = { location, nodeId: pendingNodeId, serverLocation, fromPort };
|
|
114
126
|
let webSocket!: SenderInterface;
|
|
115
127
|
if (!webSocketBase) {
|
|
116
128
|
await tryToReconnect();
|
|
@@ -176,7 +188,7 @@ async function createCallFactory(
|
|
|
176
188
|
call.callback({
|
|
177
189
|
isReturn: true,
|
|
178
190
|
result: undefined,
|
|
179
|
-
error: `Connection lost to ${
|
|
191
|
+
error: `Connection lost to ${niceConnectionName}`,
|
|
180
192
|
seqNum: call.call.seqNum,
|
|
181
193
|
});
|
|
182
194
|
}
|
|
@@ -317,7 +329,7 @@ async function createCallFactory(
|
|
|
317
329
|
location,
|
|
318
330
|
async performCall(call: CallType) {
|
|
319
331
|
if (closedForever) {
|
|
320
|
-
throw new Error(`Connection lost to ${
|
|
332
|
+
throw new Error(`Connection lost to ${niceConnectionName}`);
|
|
321
333
|
}
|
|
322
334
|
|
|
323
335
|
let seqNum = nextSeqNum++;
|
package/src/callHTTPHandler.ts
CHANGED
|
@@ -2,13 +2,13 @@ import https from "https";
|
|
|
2
2
|
import http from "http";
|
|
3
3
|
import net from "net";
|
|
4
4
|
import tls from "tls";
|
|
5
|
-
import { CallerContext, CallType } from "../SocketFunctionTypes";
|
|
5
|
+
import { CallerContext, CallType, NetworkLocation } from "../SocketFunctionTypes";
|
|
6
6
|
import { performLocalCall } from "./callManager";
|
|
7
7
|
import { getNodeIdRaw } from "./nodeAuthentication";
|
|
8
8
|
import debugbreak from "debugbreak";
|
|
9
9
|
import * as cookie from "cookie";
|
|
10
10
|
|
|
11
|
-
const nodeIdCookie = "node-
|
|
11
|
+
const nodeIdCookie = "node-id4";
|
|
12
12
|
|
|
13
13
|
let defaultHTTPCall: CallType | undefined;
|
|
14
14
|
|
|
@@ -24,6 +24,23 @@ export function getNodeIdFromRequest(request: http.IncomingMessage): string | un
|
|
|
24
24
|
if (!value.startsWith(cookieNodeIdPrefix)) return undefined;
|
|
25
25
|
return value;
|
|
26
26
|
}
|
|
27
|
+
export function getServerLocationFromRequest(request: http.IncomingMessage): NetworkLocation {
|
|
28
|
+
let host = request.headers.host;
|
|
29
|
+
if (!host) {
|
|
30
|
+
throw new Error(`Missing host in request headers`);
|
|
31
|
+
}
|
|
32
|
+
let port = 443;
|
|
33
|
+
if (host.includes(":")) {
|
|
34
|
+
port = +host.split(":")[1];
|
|
35
|
+
host = host.split(":")[0];
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
address: host,
|
|
39
|
+
// This is OUR location, so whatever they connected to us... we must be listening on!
|
|
40
|
+
// (and the localPort doesn't matter in this case)
|
|
41
|
+
listeningPorts: [port],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
27
44
|
|
|
28
45
|
export async function httpCallHandler(request: http.IncomingMessage, response: http.ServerResponse) {
|
|
29
46
|
try {
|
|
@@ -85,11 +102,12 @@ export async function httpCallHandler(request: http.IncomingMessage, response: h
|
|
|
85
102
|
|
|
86
103
|
let caller: CallerContext = {
|
|
87
104
|
nodeId,
|
|
105
|
+
fromPort: port,
|
|
88
106
|
location: {
|
|
89
107
|
address,
|
|
90
|
-
localPort: port,
|
|
91
108
|
listeningPorts: [],
|
|
92
|
-
}
|
|
109
|
+
},
|
|
110
|
+
serverLocation: getServerLocationFromRequest(request),
|
|
93
111
|
};
|
|
94
112
|
|
|
95
113
|
let classGuid = urlObj.searchParams.get("classGuid");
|
|
@@ -138,15 +156,20 @@ export async function httpCallHandler(request: http.IncomingMessage, response: h
|
|
|
138
156
|
for (let headerName in headers) {
|
|
139
157
|
response.setHeader(headerName, headers[headerName]);
|
|
140
158
|
}
|
|
159
|
+
let status = headers["status"];
|
|
160
|
+
if (status) {
|
|
161
|
+
response.writeHead(+status);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
141
164
|
}
|
|
142
165
|
response.write(result);
|
|
143
166
|
} else {
|
|
144
167
|
response.write(JSON.stringify(result));
|
|
145
168
|
}
|
|
146
|
-
response.end();
|
|
147
169
|
} catch (e: any) {
|
|
148
170
|
console.error(`Request error`, e.stack);
|
|
149
171
|
response.writeHead(500, String(e.message));
|
|
172
|
+
} finally {
|
|
150
173
|
response.end();
|
|
151
174
|
}
|
|
152
175
|
}
|
|
@@ -67,7 +67,7 @@ export const getNodeId = cacheWeak(function (webSocket: SenderInterface | ws.Web
|
|
|
67
67
|
if (webSocket.nodeId) {
|
|
68
68
|
return webSocket.nodeId;
|
|
69
69
|
}
|
|
70
|
-
throw new Error(`
|
|
70
|
+
throw new Error(`Missing nodeId. If it is from the browser, this likely means your websocket and HTTP request are using different domains (so the cookies are lost). If it is from NodeJs peer certificate must use an RSA key or EC key (which should have a .pubkey property)`);
|
|
71
71
|
}
|
|
72
72
|
return nodeId;
|
|
73
73
|
});
|
package/src/nodeCache.ts
CHANGED
|
@@ -19,7 +19,7 @@ const nodeCache = new Map<string, {
|
|
|
19
19
|
const locationLookup = new Map<string, MaybePromise<string>>();
|
|
20
20
|
|
|
21
21
|
export function getNetworkLocationHash(location: NetworkLocation): string {
|
|
22
|
-
return location.address + ":" + location.
|
|
22
|
+
return location.address + ":" + location.listeningPorts.join("|");
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// NOTE: For client connections, at which point we have the nodeId, location and callFactory.
|
|
@@ -10,7 +10,7 @@ import { registerNodeClient } from "./nodeCache";
|
|
|
10
10
|
import { getCertKeyPair, getNodeId, getNodeIdRaw } from "./nodeAuthentication";
|
|
11
11
|
import debugbreak from "debugbreak";
|
|
12
12
|
import { cache } from "./caching";
|
|
13
|
-
import { getNodeIdFromRequest, httpCallHandler } from "./callHTTPHandler";
|
|
13
|
+
import { getNodeIdFromRequest, getServerLocationFromRequest, httpCallHandler } from "./callHTTPHandler";
|
|
14
14
|
|
|
15
15
|
// TODO: Support conditional peer certificate requests, as it the certificate prompt
|
|
16
16
|
// seems suspicious in the browser (the user can just click cancel though).
|
|
@@ -59,7 +59,7 @@ export async function startSocketServer(
|
|
|
59
59
|
let requestNodeId = getNodeIdFromRequest(request);
|
|
60
60
|
Object.assign(ws, { nodeId: requestNodeId });
|
|
61
61
|
|
|
62
|
-
let clientCallFactory = await callFactoryFromWS(ws);
|
|
62
|
+
let clientCallFactory = await callFactoryFromWS(ws, getServerLocationFromRequest(request));
|
|
63
63
|
registerNodeClient(clientCallFactory);
|
|
64
64
|
});
|
|
65
65
|
});
|
package/test/client.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { setFlag } from "../require/compileFlags";
|
|
|
7
7
|
import { SocketFunction } from "../SocketFunction";
|
|
8
8
|
import { Test } from "./shared";
|
|
9
9
|
|
|
10
|
+
import "../require/CSSShim";
|
|
10
11
|
import "./client.css";
|
|
11
12
|
import { isNode } from "../src/misc";
|
|
12
13
|
|
|
@@ -28,7 +29,7 @@ async function main() {
|
|
|
28
29
|
|
|
29
30
|
const port = 2542;
|
|
30
31
|
|
|
31
|
-
let serverId = await SocketFunction.connect({ port, address: "
|
|
32
|
+
let serverId = await SocketFunction.connect({ port, address: "localhost" });
|
|
32
33
|
let test = await Test.nodes[serverId].add(1, 2);
|
|
33
34
|
console.log(`${test}=${1 + 2}`);
|
|
34
35
|
|
package/test/server.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// debugbreak(1);
|
|
3
3
|
// debugger;
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { RequireController } from "../require/RequireController";
|
|
6
6
|
import { SocketFunction } from "../SocketFunction";
|
|
7
7
|
import { getArgs } from "../src/args";
|
|
8
8
|
import { Test } from "./shared";
|
|
@@ -23,7 +23,7 @@ void main();
|
|
|
23
23
|
async function main() {
|
|
24
24
|
SocketFunction.expose(Test);
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
RequireController._classGuid;
|
|
27
27
|
SocketFunction.expose(RequireController);
|
|
28
28
|
|
|
29
29
|
SocketFunction.setDefaultHTTPCall(RequireController, "requireHTML", "./test/client");
|