socket-function 0.9.0 → 0.9.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.
Files changed (45) hide show
  1. package/.eslintrc.js +50 -50
  2. package/SocketFunction.ts +280 -280
  3. package/SocketFunctionTypes.ts +90 -90
  4. package/hot/HotReloadController.ts +105 -105
  5. package/mobx/UrlParam.ts +39 -39
  6. package/mobx/observer.tsx +49 -49
  7. package/mobx/promiseToObservable.tsx +41 -41
  8. package/package.json +30 -28
  9. package/require/CSSShim.ts +19 -19
  10. package/require/RequireController.ts +252 -252
  11. package/require/buffer.js +2368 -2368
  12. package/require/compileFlags.ts +44 -44
  13. package/require/require.html +13 -13
  14. package/require/require.js +462 -456
  15. package/spec.txt +115 -115
  16. package/src/CallFactory.ts +389 -389
  17. package/src/JSONLACKS/JSONLACKS.generated.js +17 -17
  18. package/src/JSONLACKS/JSONLACKS.pegjs +247 -247
  19. package/src/JSONLACKS/JSONLACKS.ts +429 -375
  20. package/src/args.ts +21 -21
  21. package/src/batching.ts +170 -129
  22. package/src/caching.ts +318 -314
  23. package/src/callHTTPHandler.ts +203 -203
  24. package/src/callManager.ts +134 -134
  25. package/src/certStore.ts +29 -29
  26. package/src/fixLargeNetworkCalls.ts +8 -8
  27. package/src/formatting/colors.ts +78 -78
  28. package/src/formatting/format.ts +160 -156
  29. package/src/formatting/logColors.ts +17 -17
  30. package/src/misc.ts +302 -171
  31. package/src/nodeCache.ts +92 -92
  32. package/src/nodeProxy.ts +54 -54
  33. package/src/profiling/getOwnTime.ts +142 -142
  34. package/src/profiling/measure.ts +273 -244
  35. package/src/profiling/stats.ts +212 -212
  36. package/src/profiling/tcpLagProxy.ts +63 -63
  37. package/src/storagePath.ts +10 -10
  38. package/src/tlsParsing.ts +96 -96
  39. package/src/types.ts +8 -8
  40. package/src/webSocketServer.ts +250 -250
  41. package/test/client.css +2 -2
  42. package/test/client.ts +46 -46
  43. package/test/server.ts +43 -43
  44. package/test/shared.ts +52 -52
  45. package/tsconfig.json +26 -26
@@ -1,204 +1,204 @@
1
- import http from "http";
2
- import tls from "tls";
3
- import { CallerContext, CallType, FullCallType } from "../SocketFunctionTypes";
4
- import { isDataImmutable, performLocalCall } from "./callManager";
5
- import { SocketFunction } from "../SocketFunction";
6
- import { gzip } from "zlib";
7
- import { formatNumberSuffixed, sha256Hash } from "./misc";
8
- import { getClientNodeId, getNodeId } from "./nodeCache";
9
-
10
- let defaultHTTPCall: CallType | undefined;
11
-
12
- export function setDefaultHTTPCall(call: CallType) {
13
- defaultHTTPCall = call;
14
- }
15
-
16
- export function getServerLocationFromRequest(request: http.IncomingMessage) {
17
- let host = request.headers.host;
18
- if (!host) {
19
- throw new Error(`Missing host in request headers`);
20
- }
21
- let port = 443;
22
- if (host.includes(":")) {
23
- port = +host.split(":")[1];
24
- host = host.split(":")[0];
25
- }
26
- return {
27
- address: host,
28
- // This is OUR location, so whatever they connected to us... we must be listening on!
29
- // (and the localPort doesn't matter in this case)
30
- port,
31
- };
32
- }
33
-
34
- export function getNodeIdsFromRequest(request: http.IncomingMessage) {
35
- // TODO: Support passing signed proof of userCertificate via headers in the HTTP request.
36
- // THAT WAY HTTP can have consistent nodeIds, instead of making them randomly every time!
37
- // (This isn't needed or possible for websockets, but they stay open, so calling functions
38
- // after they open to set the nodeId is possible, and preferred).
39
- let remoteAddress = request.socket.remoteAddress;
40
- if (!remoteAddress) {
41
- throw new Error(`Missing remoteAddress`);
42
- }
43
- const nodeId = getClientNodeId(remoteAddress);
44
-
45
- const serverLocation = getServerLocationFromRequest(request);
46
- // IMPORTANT! Not the actual local id, but is the id the client called
47
- const localNodeId = getNodeId(serverLocation.address, serverLocation.port);
48
- return { nodeId, localNodeId };
49
- }
50
-
51
- export async function httpCallHandler(request: http.IncomingMessage, response: http.ServerResponse) {
52
- try {
53
-
54
- let urlBase = request.url;
55
- if (!urlBase) {
56
- throw new Error("Missing URL");
57
- }
58
- if (urlBase === "/favicon.ico") {
59
- response.end();
60
- return;
61
- }
62
-
63
- let protocol = "https";
64
- let url = protocol + "://" + request.headers.host + request.url;
65
-
66
- console.log(`HTTP request (${request.method}) ${url}`);
67
- let urlObj = new URL(url);
68
-
69
- let payload = await new Promise<Buffer>((resolve, reject) => {
70
- let data: Buffer[] = [];
71
- request
72
- .on("data", chunk => data.push(chunk))
73
- .on("end", () => resolve(Buffer.concat(data)))
74
- .on("error", (err) => reject(err))
75
- ;
76
- });
77
-
78
- const { nodeId, localNodeId } = getNodeIdsFromRequest(request);
79
-
80
- let caller: CallerContext = {
81
- nodeId,
82
- localNodeId,
83
- };
84
-
85
- let classGuid = urlObj.searchParams.get("classGuid");
86
- let functionName = urlObj.searchParams.get("functionName");
87
- let args: string | unknown[] | null = urlObj.searchParams.get("args");
88
-
89
- if (!classGuid) {
90
- if (defaultHTTPCall) {
91
- classGuid = defaultHTTPCall.classGuid;
92
- functionName = defaultHTTPCall.functionName;
93
- args = defaultHTTPCall.args;
94
- } else {
95
- throw new Error("Missing classGuid in URL query");
96
- }
97
- }
98
- if (!functionName) {
99
- throw new Error("Missing functionName in URL query");
100
- }
101
-
102
- if (!args) {
103
- args = [];
104
- } else {
105
- if (typeof args === "string") {
106
- args = JSON.parse(args) as unknown[];
107
- }
108
- }
109
-
110
- if (payload.length > 0) {
111
- args = JSON.parse(payload.toString())["args"] as unknown[];
112
- }
113
-
114
- let call: FullCallType = {
115
- nodeId,
116
- classGuid,
117
- functionName,
118
- args,
119
- };
120
-
121
- if (isDataImmutable(call)) {
122
- /** ETag cache, BUT, hashes only the input. Only valid for fully immutable resources
123
- * (ex data storage, as any endpoints that run code could have that code change).
124
- * - Shouldn't be needed, but I am seeing chrome fail to cache a lot of requests,
125
- * which could cost us multiple dollars in server costs from atlas.
126
- */
127
- response.setHeader("cache-control", "public, max-age=15206400, immutable");
128
- let hash = sha256Hash(Buffer.from(JSON.stringify(call)));
129
- response.setHeader("ETag", hash);
130
- if (request.headers["if-none-match"] === hash) {
131
- response.writeHead(304);
132
- console.log(`CACHED Immutable HTTP response (${request.method}) ${url}`);
133
- return;
134
- }
135
- }
136
-
137
- let result = await performLocalCall({
138
- caller,
139
- call
140
- });
141
-
142
- let resultBuffer: Buffer;
143
- if (typeof result === "object" && result && result instanceof Buffer) {
144
- resultBuffer = result;
145
- } else {
146
- resultBuffer = Buffer.from(JSON.stringify(result));
147
- }
148
-
149
- let headers = (resultBuffer as HTTPResultType)[resultHeaders];
150
- if (SocketFunction.compression?.type === "gzip" && !headers?.["Content-Encoding"]) {
151
- if (request.headers["accept-encoding"]?.includes("gzip")) {
152
- resultBuffer = await new Promise<Buffer>((resolve, reject) =>
153
- gzip(resultBuffer, (err, result) => err ? reject(err) : resolve(result))
154
- );
155
- response.setHeader("Content-Encoding", "gzip");
156
- }
157
- }
158
-
159
-
160
- // NOTE: Our ETag caching is only to reduce data sent on the wire, we evaluate the calls
161
- // every time (so it is strictly a wire cache for HTTP, not a computation cache)
162
- if (SocketFunction.httpETagCache) {
163
- response.setHeader("cache-control", "private, max-age=0, must-revalidate");
164
- let hash = sha256Hash(resultBuffer);
165
- response.setHeader("ETag", hash);
166
- if (request.headers["if-none-match"] === hash) {
167
- response.writeHead(304);
168
- console.log(`CACHED HTTP response ${formatNumberSuffixed(resultBuffer.length)}B (${request.method}) ${url}`);
169
- return;
170
- }
171
- }
172
-
173
- if (headers) {
174
- for (let headerName in headers) {
175
- response.setHeader(headerName, headers[headerName]);
176
- }
177
- let status = headers["status"];
178
- if (status) {
179
- response.writeHead(+status);
180
- return;
181
- }
182
- }
183
- response.write(resultBuffer);
184
- console.log(`HTTP response ${formatNumberSuffixed(resultBuffer.length)}B (${request.method}) ${url}`);
185
-
186
- } catch (e: any) {
187
- console.log(`HTTP error (${request.method}) ${e.stack}`);
188
- response.writeHead(500, String(e.message));
189
- } finally {
190
- response.end();
191
- }
192
- }
193
-
194
-
195
- const resultHeaders = Symbol("resultHeaders");
196
- type HTTPResultType = Buffer & { [resultHeaders]?: { [header: string]: string } };
197
-
198
- export function setHTTPResultHeaders(
199
- result: HTTPResultType,
200
- headers: { [header: string]: string },
201
- ): HTTPResultType {
202
- result[resultHeaders] = headers;
203
- return result;
1
+ import http from "http";
2
+ import tls from "tls";
3
+ import { CallerContext, CallType, FullCallType } from "../SocketFunctionTypes";
4
+ import { isDataImmutable, performLocalCall } from "./callManager";
5
+ import { SocketFunction } from "../SocketFunction";
6
+ import { gzip } from "zlib";
7
+ import { formatNumberSuffixed, sha256Hash } from "./misc";
8
+ import { getClientNodeId, getNodeId } from "./nodeCache";
9
+
10
+ let defaultHTTPCall: CallType | undefined;
11
+
12
+ export function setDefaultHTTPCall(call: CallType) {
13
+ defaultHTTPCall = call;
14
+ }
15
+
16
+ export function getServerLocationFromRequest(request: http.IncomingMessage) {
17
+ let host = request.headers.host;
18
+ if (!host) {
19
+ throw new Error(`Missing host in request headers`);
20
+ }
21
+ let port = 443;
22
+ if (host.includes(":")) {
23
+ port = +host.split(":")[1];
24
+ host = host.split(":")[0];
25
+ }
26
+ return {
27
+ address: host,
28
+ // This is OUR location, so whatever they connected to us... we must be listening on!
29
+ // (and the localPort doesn't matter in this case)
30
+ port,
31
+ };
32
+ }
33
+
34
+ export function getNodeIdsFromRequest(request: http.IncomingMessage) {
35
+ // TODO: Support passing signed proof of userCertificate via headers in the HTTP request.
36
+ // THAT WAY HTTP can have consistent nodeIds, instead of making them randomly every time!
37
+ // (This isn't needed or possible for websockets, but they stay open, so calling functions
38
+ // after they open to set the nodeId is possible, and preferred).
39
+ let remoteAddress = request.socket.remoteAddress;
40
+ if (!remoteAddress) {
41
+ throw new Error(`Missing remoteAddress`);
42
+ }
43
+ const nodeId = getClientNodeId(remoteAddress);
44
+
45
+ const serverLocation = getServerLocationFromRequest(request);
46
+ // IMPORTANT! Not the actual local id, but is the id the client called
47
+ const localNodeId = getNodeId(serverLocation.address, serverLocation.port);
48
+ return { nodeId, localNodeId };
49
+ }
50
+
51
+ export async function httpCallHandler(request: http.IncomingMessage, response: http.ServerResponse) {
52
+ try {
53
+
54
+ let urlBase = request.url;
55
+ if (!urlBase) {
56
+ throw new Error("Missing URL");
57
+ }
58
+ if (urlBase === "/favicon.ico") {
59
+ response.end();
60
+ return;
61
+ }
62
+
63
+ let protocol = "https";
64
+ let url = protocol + "://" + request.headers.host + request.url;
65
+
66
+ console.log(`HTTP request (${request.method}) ${url}`);
67
+ let urlObj = new URL(url);
68
+
69
+ let payload = await new Promise<Buffer>((resolve, reject) => {
70
+ let data: Buffer[] = [];
71
+ request
72
+ .on("data", chunk => data.push(chunk))
73
+ .on("end", () => resolve(Buffer.concat(data)))
74
+ .on("error", (err) => reject(err))
75
+ ;
76
+ });
77
+
78
+ const { nodeId, localNodeId } = getNodeIdsFromRequest(request);
79
+
80
+ let caller: CallerContext = {
81
+ nodeId,
82
+ localNodeId,
83
+ };
84
+
85
+ let classGuid = urlObj.searchParams.get("classGuid");
86
+ let functionName = urlObj.searchParams.get("functionName");
87
+ let args: string | unknown[] | null = urlObj.searchParams.get("args");
88
+
89
+ if (!classGuid) {
90
+ if (defaultHTTPCall) {
91
+ classGuid = defaultHTTPCall.classGuid;
92
+ functionName = defaultHTTPCall.functionName;
93
+ args = defaultHTTPCall.args;
94
+ } else {
95
+ throw new Error("Missing classGuid in URL query");
96
+ }
97
+ }
98
+ if (!functionName) {
99
+ throw new Error("Missing functionName in URL query");
100
+ }
101
+
102
+ if (!args) {
103
+ args = [];
104
+ } else {
105
+ if (typeof args === "string") {
106
+ args = JSON.parse(args) as unknown[];
107
+ }
108
+ }
109
+
110
+ if (payload.length > 0) {
111
+ args = JSON.parse(payload.toString())["args"] as unknown[];
112
+ }
113
+
114
+ let call: FullCallType = {
115
+ nodeId,
116
+ classGuid,
117
+ functionName,
118
+ args,
119
+ };
120
+
121
+ if (isDataImmutable(call)) {
122
+ /** ETag cache, BUT, hashes only the input. Only valid for fully immutable resources
123
+ * (ex data storage, as any endpoints that run code could have that code change).
124
+ * - Shouldn't be needed, but I am seeing chrome fail to cache a lot of requests,
125
+ * which could cost us multiple dollars in server costs from atlas.
126
+ */
127
+ response.setHeader("cache-control", "public, max-age=15206400, immutable");
128
+ let hash = sha256Hash(Buffer.from(JSON.stringify(call)));
129
+ response.setHeader("ETag", hash);
130
+ if (request.headers["if-none-match"] === hash) {
131
+ response.writeHead(304);
132
+ console.log(`CACHED Immutable HTTP response (${request.method}) ${url}`);
133
+ return;
134
+ }
135
+ }
136
+
137
+ let result = await performLocalCall({
138
+ caller,
139
+ call
140
+ });
141
+
142
+ let resultBuffer: Buffer;
143
+ if (typeof result === "object" && result && result instanceof Buffer) {
144
+ resultBuffer = result;
145
+ } else {
146
+ resultBuffer = Buffer.from(JSON.stringify(result));
147
+ }
148
+
149
+ let headers = (resultBuffer as HTTPResultType)[resultHeaders];
150
+ if (SocketFunction.compression?.type === "gzip" && !headers?.["Content-Encoding"]) {
151
+ if (request.headers["accept-encoding"]?.includes("gzip")) {
152
+ resultBuffer = await new Promise<Buffer>((resolve, reject) =>
153
+ gzip(resultBuffer, (err, result) => err ? reject(err) : resolve(result))
154
+ );
155
+ response.setHeader("Content-Encoding", "gzip");
156
+ }
157
+ }
158
+
159
+
160
+ // NOTE: Our ETag caching is only to reduce data sent on the wire, we evaluate the calls
161
+ // every time (so it is strictly a wire cache for HTTP, not a computation cache)
162
+ if (SocketFunction.httpETagCache) {
163
+ response.setHeader("cache-control", "private, max-age=0, must-revalidate");
164
+ let hash = sha256Hash(resultBuffer);
165
+ response.setHeader("ETag", hash);
166
+ if (request.headers["if-none-match"] === hash) {
167
+ response.writeHead(304);
168
+ console.log(`CACHED HTTP response ${formatNumberSuffixed(resultBuffer.length)}B (${request.method}) ${url}`);
169
+ return;
170
+ }
171
+ }
172
+
173
+ if (headers) {
174
+ for (let headerName in headers) {
175
+ response.setHeader(headerName, headers[headerName]);
176
+ }
177
+ let status = headers["status"];
178
+ if (status) {
179
+ response.writeHead(+status);
180
+ return;
181
+ }
182
+ }
183
+ response.write(resultBuffer);
184
+ console.log(`HTTP response ${formatNumberSuffixed(resultBuffer.length)}B (${request.method}) ${url}`);
185
+
186
+ } catch (e: any) {
187
+ console.log(`HTTP error (${request.method}) ${e.stack}`);
188
+ response.writeHead(500, String(e.message));
189
+ } finally {
190
+ response.end();
191
+ }
192
+ }
193
+
194
+
195
+ const resultHeaders = Symbol("resultHeaders");
196
+ type HTTPResultType = Buffer & { [resultHeaders]?: { [header: string]: string } };
197
+
198
+ export function setHTTPResultHeaders(
199
+ result: HTTPResultType,
200
+ headers: { [header: string]: string },
201
+ ): HTTPResultType {
202
+ result[resultHeaders] = headers;
203
+ return result;
204
204
  }