@interopio/gateway-server 0.7.0-beta → 0.8.1-beta.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/changelog.md +22 -0
- package/dist/gateway-ent.cjs +17 -17
- package/dist/gateway-ent.cjs.map +2 -2
- package/dist/gateway-ent.js +17 -17
- package/dist/gateway-ent.js.map +2 -2
- package/dist/index.cjs +1389 -623
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +1389 -623
- package/dist/index.js.map +4 -4
- package/dist/metrics/publisher/rest.cjs +13 -1
- package/dist/metrics/publisher/rest.cjs.map +3 -3
- package/dist/metrics/publisher/rest.js +14 -2
- package/dist/metrics/publisher/rest.js.map +2 -2
- package/dist/web/test.js +1392 -0
- package/dist/web/test.js.map +7 -0
- package/gateway-server.d.ts +2 -4
- package/license.md +5 -0
- package/package.json +7 -3
- package/readme.md +59 -26
- package/types/web/client.d.ts +12 -0
- package/types/web/http.d.ts +38 -26
- package/types/web/server.d.ts +35 -24
- package/types/web/{ws.d.ts → socket.d.ts} +8 -6
- package/types/web/test.d.ts +14 -0
package/dist/index.js
CHANGED
|
@@ -9,11 +9,10 @@ var server_exports = {};
|
|
|
9
9
|
__export(server_exports, {
|
|
10
10
|
Factory: () => Factory
|
|
11
11
|
});
|
|
12
|
-
import * as ws from "ws";
|
|
13
12
|
import http2 from "node:http";
|
|
14
13
|
import https from "node:https";
|
|
15
14
|
import { readFileSync } from "node:fs";
|
|
16
|
-
import { AsyncLocalStorage as
|
|
15
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
|
|
17
16
|
import { IOGateway as IOGateway6 } from "@interopio/gateway";
|
|
18
17
|
|
|
19
18
|
// src/logger.ts
|
|
@@ -21,13 +20,41 @@ import { IOGateway } from "@interopio/gateway";
|
|
|
21
20
|
function getLogger(name) {
|
|
22
21
|
return IOGateway.Logging.getLogger(`gateway.server.${name}`);
|
|
23
22
|
}
|
|
23
|
+
function regexAwareReplacer(_key, value) {
|
|
24
|
+
return value instanceof RegExp ? value.toString() : value;
|
|
25
|
+
}
|
|
24
26
|
|
|
25
27
|
// src/gateway/ws/core.ts
|
|
26
28
|
import { IOGateway as IOGateway2 } from "@interopio/gateway";
|
|
29
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
27
30
|
var GatewayEncoders = IOGateway2.Encoding;
|
|
28
31
|
var log = getLogger("ws");
|
|
29
32
|
var codec = GatewayEncoders.json();
|
|
30
|
-
function
|
|
33
|
+
function principalName(authentication) {
|
|
34
|
+
let name;
|
|
35
|
+
if (authentication.authenticated) {
|
|
36
|
+
name = authentication.name;
|
|
37
|
+
if (name === void 0) {
|
|
38
|
+
if (authentication["principal"] !== void 0) {
|
|
39
|
+
const principal = authentication["principal"];
|
|
40
|
+
if (typeof principal === "object") {
|
|
41
|
+
name = principal.name;
|
|
42
|
+
}
|
|
43
|
+
if (name === void 0) {
|
|
44
|
+
if (principal === void 0) {
|
|
45
|
+
name = "";
|
|
46
|
+
} else {
|
|
47
|
+
name = String(principal);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return name;
|
|
54
|
+
}
|
|
55
|
+
function initClient(socket, authenticationPromise, remoteAddress) {
|
|
56
|
+
const key = `${remoteAddress?.address}:${remoteAddress?.port}`;
|
|
57
|
+
const host = remoteAddress?.address ?? "<unknown>";
|
|
31
58
|
const opts = {
|
|
32
59
|
key,
|
|
33
60
|
host,
|
|
@@ -35,7 +62,7 @@ function initClient(socket, authenticationPromise, key, host) {
|
|
|
35
62
|
onAuthenticate: async () => {
|
|
36
63
|
const authentication = await authenticationPromise();
|
|
37
64
|
if (authentication?.authenticated) {
|
|
38
|
-
return { type: "success", user: authentication
|
|
65
|
+
return { type: "success", user: principalName(authentication) };
|
|
39
66
|
}
|
|
40
67
|
throw new Error(`no valid client authentication ${key}`);
|
|
41
68
|
},
|
|
@@ -68,30 +95,34 @@ function initClient(socket, authenticationPromise, key, host) {
|
|
|
68
95
|
log.warn(`${key} failed to create client`, err);
|
|
69
96
|
}
|
|
70
97
|
}
|
|
71
|
-
async function create(
|
|
72
|
-
log.info(`starting gateway on ${
|
|
73
|
-
await this.start(
|
|
74
|
-
return async (socket, handshake) => {
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const client = initClient.call(this, socket, handshake.principal, key, host);
|
|
98
|
+
async function create(environment) {
|
|
99
|
+
log.info(`starting gateway on ${environment.endpoint}`);
|
|
100
|
+
await this.start(environment);
|
|
101
|
+
return async ({ socket, handshake }) => {
|
|
102
|
+
const { logPrefix, remoteAddress, principal: principalPromise } = handshake;
|
|
103
|
+
log.info(`${logPrefix}connected on gw using ${remoteAddress?.address}`);
|
|
104
|
+
const client = initClient.call(this, socket, principalPromise, remoteAddress);
|
|
79
105
|
if (!client) {
|
|
80
|
-
log.error(`${
|
|
106
|
+
log.error(`${logPrefix}gw client init failed`);
|
|
81
107
|
socket.terminate();
|
|
82
108
|
return;
|
|
83
109
|
}
|
|
84
110
|
socket.on("error", (err) => {
|
|
85
|
-
log.error(`${
|
|
111
|
+
log.error(`${logPrefix}websocket error: ${err}`, err);
|
|
86
112
|
});
|
|
113
|
+
const contextFn = environment.storage !== void 0 ? AsyncLocalStorage.snapshot() : void 0;
|
|
87
114
|
socket.on("message", (data, _isBinary) => {
|
|
88
115
|
if (Array.isArray(data)) {
|
|
89
116
|
data = Buffer.concat(data);
|
|
90
117
|
}
|
|
91
|
-
|
|
118
|
+
if (contextFn !== void 0) {
|
|
119
|
+
contextFn(() => client.send(data));
|
|
120
|
+
} else {
|
|
121
|
+
client.send(data);
|
|
122
|
+
}
|
|
92
123
|
});
|
|
93
124
|
socket.on("close", (code) => {
|
|
94
|
-
log.info(`${
|
|
125
|
+
log.info(`${logPrefix}disconnected from gw. code: ${code}`);
|
|
95
126
|
client.close();
|
|
96
127
|
});
|
|
97
128
|
};
|
|
@@ -110,61 +141,50 @@ function compose(...middleware) {
|
|
|
110
141
|
}
|
|
111
142
|
}
|
|
112
143
|
return async function(ctx, next) {
|
|
113
|
-
const dispatch = async (i) => {
|
|
144
|
+
const dispatch = async (i, dispatchedCtx) => {
|
|
114
145
|
const fn = i === fns.length ? next : fns[i];
|
|
115
146
|
if (fn === void 0) {
|
|
116
147
|
return;
|
|
117
148
|
}
|
|
118
149
|
let nextCalled = false;
|
|
119
150
|
let nextResolved = false;
|
|
120
|
-
const nextFn = async () => {
|
|
151
|
+
const nextFn = async (nextCtx) => {
|
|
121
152
|
if (nextCalled) {
|
|
122
153
|
throw new Error("next() called multiple times");
|
|
123
154
|
}
|
|
124
155
|
nextCalled = true;
|
|
125
156
|
try {
|
|
126
|
-
return await dispatch(i + 1);
|
|
157
|
+
return await dispatch(i + 1, nextCtx ?? dispatchedCtx);
|
|
127
158
|
} finally {
|
|
128
159
|
nextResolved = true;
|
|
129
160
|
}
|
|
130
161
|
};
|
|
131
|
-
const result = await fn(
|
|
162
|
+
const result = await fn(dispatchedCtx, nextFn);
|
|
132
163
|
if (nextCalled && !nextResolved) {
|
|
133
164
|
throw new Error("middleware resolved before downstream.\n You are probably missing an await or return statement in your middleware function.");
|
|
134
165
|
}
|
|
135
166
|
return result;
|
|
136
167
|
};
|
|
137
|
-
return dispatch(0);
|
|
168
|
+
return dispatch(0, ctx);
|
|
138
169
|
};
|
|
139
170
|
}
|
|
140
171
|
|
|
141
|
-
// src/
|
|
142
|
-
import http from "node:http";
|
|
172
|
+
// src/http/exchange.ts
|
|
143
173
|
import { Cookie } from "tough-cookie";
|
|
144
|
-
function
|
|
145
|
-
let
|
|
146
|
-
if (Array.isArray(proto)) {
|
|
147
|
-
proto = proto[0];
|
|
148
|
-
}
|
|
149
|
-
if (proto !== void 0) {
|
|
150
|
-
return proto.split(",", 1)[0].trim();
|
|
151
|
-
}
|
|
152
|
-
return defaultProtocol;
|
|
153
|
-
}
|
|
154
|
-
function requestToHost(request, defaultHost) {
|
|
155
|
-
let host = request.headers.get("x-forwarded-for");
|
|
174
|
+
function parseHost(headers2, defaultHost) {
|
|
175
|
+
let host = headers2.get("x-forwarded-for");
|
|
156
176
|
if (host === void 0) {
|
|
157
|
-
host =
|
|
177
|
+
host = headers2.get("x-forwarded-host");
|
|
158
178
|
if (Array.isArray(host)) {
|
|
159
179
|
host = host[0];
|
|
160
180
|
}
|
|
161
181
|
if (host) {
|
|
162
|
-
const port =
|
|
182
|
+
const port = headers2.one("x-forwarded-port");
|
|
163
183
|
if (port) {
|
|
164
184
|
host = `${host}:${port}`;
|
|
165
185
|
}
|
|
166
186
|
}
|
|
167
|
-
host ??=
|
|
187
|
+
host ??= headers2.one("host");
|
|
168
188
|
}
|
|
169
189
|
if (Array.isArray(host)) {
|
|
170
190
|
host = host[0];
|
|
@@ -174,15 +194,292 @@ function requestToHost(request, defaultHost) {
|
|
|
174
194
|
}
|
|
175
195
|
return defaultHost;
|
|
176
196
|
}
|
|
177
|
-
function
|
|
178
|
-
|
|
179
|
-
|
|
197
|
+
function parseProtocol(headers2, defaultProtocol) {
|
|
198
|
+
let proto = headers2.get("x-forwarded-proto");
|
|
199
|
+
if (Array.isArray(proto)) {
|
|
200
|
+
proto = proto[0];
|
|
201
|
+
}
|
|
202
|
+
if (proto !== void 0) {
|
|
203
|
+
return proto.split(",", 1)[0].trim();
|
|
204
|
+
}
|
|
205
|
+
return defaultProtocol;
|
|
206
|
+
}
|
|
207
|
+
var AbstractHttpMessage = class {
|
|
208
|
+
#headers;
|
|
209
|
+
constructor(headers2) {
|
|
210
|
+
this.#headers = headers2;
|
|
211
|
+
}
|
|
212
|
+
get headers() {
|
|
213
|
+
return this.#headers;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
var AbstractHttpRequest = class _AbstractHttpRequest extends AbstractHttpMessage {
|
|
217
|
+
static logIdCounter = 0;
|
|
218
|
+
#id;
|
|
219
|
+
get id() {
|
|
220
|
+
if (this.#id === void 0) {
|
|
221
|
+
this.#id = `${this.initId()}-${++_AbstractHttpRequest.logIdCounter}`;
|
|
222
|
+
}
|
|
223
|
+
return this.#id;
|
|
224
|
+
}
|
|
225
|
+
initId() {
|
|
226
|
+
return "request";
|
|
227
|
+
}
|
|
228
|
+
get cookies() {
|
|
229
|
+
return parseCookies(this.headers);
|
|
230
|
+
}
|
|
231
|
+
parseHost(defaultHost) {
|
|
232
|
+
return parseHost(this.headers, defaultHost);
|
|
233
|
+
}
|
|
234
|
+
parseProtocol(defaultProtocol) {
|
|
235
|
+
return parseProtocol(this.headers, defaultProtocol);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
var AbstractHttpResponse = class extends AbstractHttpMessage {
|
|
239
|
+
get cookies() {
|
|
240
|
+
return parseResponseCookies(this.headers);
|
|
241
|
+
}
|
|
242
|
+
setCookieValue(responseCookie) {
|
|
243
|
+
const cookie = new Cookie({
|
|
244
|
+
key: responseCookie.name,
|
|
245
|
+
value: responseCookie.value,
|
|
246
|
+
maxAge: responseCookie.maxAge,
|
|
247
|
+
domain: responseCookie.domain,
|
|
248
|
+
path: responseCookie.path,
|
|
249
|
+
secure: responseCookie.secure,
|
|
250
|
+
httpOnly: responseCookie.httpOnly,
|
|
251
|
+
sameSite: responseCookie.sameSite
|
|
252
|
+
});
|
|
253
|
+
return cookie.toString();
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
function parseHeader(value) {
|
|
257
|
+
const list = [];
|
|
258
|
+
{
|
|
259
|
+
let start2 = 0;
|
|
260
|
+
let end = 0;
|
|
261
|
+
for (let i = 0; i < value.length; i++) {
|
|
262
|
+
switch (value.charCodeAt(i)) {
|
|
263
|
+
case 32:
|
|
264
|
+
if (start2 === end) {
|
|
265
|
+
start2 = end = i + 1;
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
case 44:
|
|
269
|
+
list.push(value.slice(start2, end));
|
|
270
|
+
start2 = end = i + 1;
|
|
271
|
+
break;
|
|
272
|
+
default:
|
|
273
|
+
end = end + 1;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
list.push(value.slice(start2, end));
|
|
278
|
+
}
|
|
279
|
+
return list;
|
|
280
|
+
}
|
|
281
|
+
function toList(values) {
|
|
282
|
+
if (typeof values === "string") {
|
|
283
|
+
values = [values];
|
|
284
|
+
}
|
|
285
|
+
if (typeof values === "number") {
|
|
286
|
+
values = [String(values)];
|
|
287
|
+
}
|
|
288
|
+
const list = [];
|
|
289
|
+
if (values) {
|
|
290
|
+
for (const value of values) {
|
|
291
|
+
if (value) {
|
|
292
|
+
list.push(...parseHeader(value));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return list;
|
|
297
|
+
}
|
|
298
|
+
function parseCookies(headers2) {
|
|
299
|
+
return headers2.list("cookie").map((s) => s.split(";").map((cs) => Cookie.parse(cs))).flat(1).filter((tc) => tc !== void 0).map((tc) => {
|
|
300
|
+
const result = Object.freeze({ name: tc.key, value: tc.value });
|
|
180
301
|
return result;
|
|
181
302
|
});
|
|
182
303
|
}
|
|
304
|
+
function parseResponseCookies(headers2) {
|
|
305
|
+
return headers2.list("set-cookie").map((cookie) => {
|
|
306
|
+
const parsed = Cookie.parse(cookie);
|
|
307
|
+
if (parsed) {
|
|
308
|
+
const result = { name: parsed.key, value: parsed.value, maxAge: Number(parsed.maxAge ?? -1) };
|
|
309
|
+
if (parsed.httpOnly) result.httpOnly = true;
|
|
310
|
+
if (parsed.domain) result.domain = parsed.domain;
|
|
311
|
+
if (parsed.path) result.path = parsed.path;
|
|
312
|
+
if (parsed.secure) result.secure = true;
|
|
313
|
+
if (parsed.httpOnly) result.httpOnly = true;
|
|
314
|
+
if (parsed.sameSite) result.sameSite = parsed.sameSite;
|
|
315
|
+
return Object.freeze(result);
|
|
316
|
+
}
|
|
317
|
+
}).filter((cookie) => cookie !== void 0);
|
|
318
|
+
}
|
|
319
|
+
var AbstractHttpHeaders = class {
|
|
320
|
+
constructor() {
|
|
321
|
+
}
|
|
322
|
+
toList(name) {
|
|
323
|
+
const values = this.get(name);
|
|
324
|
+
return toList(values);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
var MapHttpHeaders = class extends Map {
|
|
328
|
+
get(name) {
|
|
329
|
+
return super.get(name.toLowerCase());
|
|
330
|
+
}
|
|
331
|
+
one(name) {
|
|
332
|
+
return this.get(name)?.[0];
|
|
333
|
+
}
|
|
334
|
+
list(name) {
|
|
335
|
+
const values = super.get(name.toLowerCase());
|
|
336
|
+
return toList(values);
|
|
337
|
+
}
|
|
338
|
+
set(name, value) {
|
|
339
|
+
if (typeof value === "number") {
|
|
340
|
+
value = String(value);
|
|
341
|
+
}
|
|
342
|
+
if (typeof value === "string") {
|
|
343
|
+
value = [value];
|
|
344
|
+
}
|
|
345
|
+
if (value) {
|
|
346
|
+
return super.set(name.toLowerCase(), value);
|
|
347
|
+
} else {
|
|
348
|
+
super.delete(name.toLowerCase());
|
|
349
|
+
return this;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
add(name, value) {
|
|
353
|
+
const prev = super.get(name.toLowerCase());
|
|
354
|
+
if (typeof value === "string") {
|
|
355
|
+
value = [value];
|
|
356
|
+
}
|
|
357
|
+
if (prev) {
|
|
358
|
+
value = prev.concat(value);
|
|
359
|
+
}
|
|
360
|
+
this.set(name, value);
|
|
361
|
+
return this;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// src/http/status.ts
|
|
366
|
+
var DefaultHttpStatusCode = class {
|
|
367
|
+
#value;
|
|
368
|
+
constructor(value) {
|
|
369
|
+
this.#value = value;
|
|
370
|
+
}
|
|
371
|
+
get value() {
|
|
372
|
+
return this.#value;
|
|
373
|
+
}
|
|
374
|
+
toString() {
|
|
375
|
+
return this.#value.toString();
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
var HttpStatus = class _HttpStatus {
|
|
379
|
+
static CONTINUE = new _HttpStatus(100, "Continue");
|
|
380
|
+
static SWITCHING_PROTOCOLS = new _HttpStatus(101, "Switching Protocols");
|
|
381
|
+
// 2xx Success
|
|
382
|
+
static OK = new _HttpStatus(200, "OK");
|
|
383
|
+
static CREATED = new _HttpStatus(201, "Created");
|
|
384
|
+
static ACCEPTED = new _HttpStatus(202, "Accepted");
|
|
385
|
+
static NON_AUTHORITATIVE_INFORMATION = new _HttpStatus(203, "Non-Authoritative Information");
|
|
386
|
+
static NO_CONTENT = new _HttpStatus(204, "No Content");
|
|
387
|
+
static RESET_CONTENT = new _HttpStatus(205, "Reset Content");
|
|
388
|
+
static PARTIAL_CONTENT = new _HttpStatus(206, "Partial Content");
|
|
389
|
+
static MULTI_STATUS = new _HttpStatus(207, "Multi-Status");
|
|
390
|
+
static IM_USED = new _HttpStatus(226, "IM Used");
|
|
391
|
+
// 3xx Redirection
|
|
392
|
+
static MULTIPLE_CHOICES = new _HttpStatus(300, "Multiple Choices");
|
|
393
|
+
static MOVED_PERMANENTLY = new _HttpStatus(301, "Moved Permanently");
|
|
394
|
+
// 4xx Client Error
|
|
395
|
+
static BAD_REQUEST = new _HttpStatus(400, "Bad Request");
|
|
396
|
+
static UNAUTHORIZED = new _HttpStatus(401, "Unauthorized");
|
|
397
|
+
static FORBIDDEN = new _HttpStatus(403, "Forbidden");
|
|
398
|
+
static NOT_FOUND = new _HttpStatus(404, "Not Found");
|
|
399
|
+
static METHOD_NOT_ALLOWED = new _HttpStatus(405, "Method Not Allowed");
|
|
400
|
+
static NOT_ACCEPTABLE = new _HttpStatus(406, "Not Acceptable");
|
|
401
|
+
static PROXY_AUTHENTICATION_REQUIRED = new _HttpStatus(407, "Proxy Authentication Required");
|
|
402
|
+
static REQUEST_TIMEOUT = new _HttpStatus(408, "Request Timeout");
|
|
403
|
+
static CONFLICT = new _HttpStatus(409, "Conflict");
|
|
404
|
+
static GONE = new _HttpStatus(410, "Gone");
|
|
405
|
+
static LENGTH_REQUIRED = new _HttpStatus(411, "Length Required");
|
|
406
|
+
static PRECONDITION_FAILED = new _HttpStatus(412, "Precondition Failed");
|
|
407
|
+
static PAYLOAD_TOO_LARGE = new _HttpStatus(413, "Payload Too Large");
|
|
408
|
+
static URI_TOO_LONG = new _HttpStatus(414, "URI Too Long");
|
|
409
|
+
static UNSUPPORTED_MEDIA_TYPE = new _HttpStatus(415, "Unsupported Media Type");
|
|
410
|
+
static EXPECTATION_FAILED = new _HttpStatus(417, "Expectation Failed");
|
|
411
|
+
static IM_A_TEAPOT = new _HttpStatus(418, "I'm a teapot");
|
|
412
|
+
static TOO_EARLY = new _HttpStatus(425, "Too Early");
|
|
413
|
+
static UPGRADE_REQUIRED = new _HttpStatus(426, "Upgrade Required");
|
|
414
|
+
static PRECONDITION_REQUIRED = new _HttpStatus(428, "Precondition Required");
|
|
415
|
+
static TOO_MANY_REQUESTS = new _HttpStatus(429, "Too Many Requests");
|
|
416
|
+
static REQUEST_HEADER_FIELDS_TOO_LARGE = new _HttpStatus(431, "Request Header Fields Too Large");
|
|
417
|
+
static UNAVAILABLE_FOR_LEGAL_REASONS = new _HttpStatus(451, "Unavailable For Legal Reasons");
|
|
418
|
+
// 5xx Server Error
|
|
419
|
+
static INTERNAL_SERVER_ERROR = new _HttpStatus(500, "Internal Server Error");
|
|
420
|
+
static NOT_IMPLEMENTED = new _HttpStatus(501, "Not Implemented");
|
|
421
|
+
static BAD_GATEWAY = new _HttpStatus(502, "Bad Gateway");
|
|
422
|
+
static SERVICE_UNAVAILABLE = new _HttpStatus(503, "Service Unavailable");
|
|
423
|
+
static GATEWAY_TIMEOUT = new _HttpStatus(504, "Gateway Timeout");
|
|
424
|
+
static HTTP_VERSION_NOT_SUPPORTED = new _HttpStatus(505, "HTTP Version Not Supported");
|
|
425
|
+
static VARIANT_ALSO_NEGOTIATES = new _HttpStatus(506, "Variant Also Negotiates");
|
|
426
|
+
static INSUFFICIENT_STORAGE = new _HttpStatus(507, "Insufficient Storage");
|
|
427
|
+
static LOOP_DETECTED = new _HttpStatus(508, "Loop Detected");
|
|
428
|
+
static NOT_EXTENDED = new _HttpStatus(510, "Not Extended");
|
|
429
|
+
static NETWORK_AUTHENTICATION_REQUIRED = new _HttpStatus(511, "Network Authentication Required");
|
|
430
|
+
static #VALUES = [];
|
|
431
|
+
static {
|
|
432
|
+
Object.keys(_HttpStatus).filter((key) => key !== "VALUES" && key !== "resolve").forEach((key) => {
|
|
433
|
+
const value = _HttpStatus[key];
|
|
434
|
+
if (value instanceof _HttpStatus) {
|
|
435
|
+
Object.defineProperty(value, "name", { enumerable: true, value: key, writable: false });
|
|
436
|
+
_HttpStatus.#VALUES.push(value);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
static resolve(code) {
|
|
441
|
+
for (const status of _HttpStatus.#VALUES) {
|
|
442
|
+
if (status.value === code) {
|
|
443
|
+
return status;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
#value;
|
|
448
|
+
#phrase;
|
|
449
|
+
constructor(value, phrase) {
|
|
450
|
+
this.#value = value;
|
|
451
|
+
this.#phrase = phrase;
|
|
452
|
+
}
|
|
453
|
+
get value() {
|
|
454
|
+
return this.#value;
|
|
455
|
+
}
|
|
456
|
+
get phrase() {
|
|
457
|
+
return this.#phrase;
|
|
458
|
+
}
|
|
459
|
+
toString() {
|
|
460
|
+
return `${this.#value} ${this["name"]}`;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
function httpStatusCode(value) {
|
|
464
|
+
if (typeof value === "number") {
|
|
465
|
+
if (value < 100 || value > 999) {
|
|
466
|
+
throw new Error(`status code ${value} should be in range 100-999`);
|
|
467
|
+
}
|
|
468
|
+
const status = HttpStatus.resolve(value);
|
|
469
|
+
if (status !== void 0) {
|
|
470
|
+
return status;
|
|
471
|
+
}
|
|
472
|
+
return new DefaultHttpStatusCode(value);
|
|
473
|
+
}
|
|
474
|
+
return value;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/server/exchange.ts
|
|
478
|
+
import http from "node:http";
|
|
183
479
|
var ExtendedHttpIncomingMessage = class extends http.IncomingMessage {
|
|
184
480
|
// circular reference to the exchange
|
|
185
481
|
exchange;
|
|
482
|
+
upgradeHead;
|
|
186
483
|
get urlBang() {
|
|
187
484
|
return this.url;
|
|
188
485
|
}
|
|
@@ -190,29 +487,118 @@ var ExtendedHttpIncomingMessage = class extends http.IncomingMessage {
|
|
|
190
487
|
return this.socket["encrypted"] === true;
|
|
191
488
|
}
|
|
192
489
|
};
|
|
193
|
-
var ExtendedHttpServerResponse = class
|
|
194
|
-
static forUpgrade(req, socket, head) {
|
|
195
|
-
const res = new _ExtendedHttpServerResponse(req);
|
|
196
|
-
res.upgradeHead = head;
|
|
197
|
-
res.assignSocket(socket);
|
|
198
|
-
return res;
|
|
199
|
-
}
|
|
200
|
-
upgradeHead;
|
|
490
|
+
var ExtendedHttpServerResponse = class extends http.ServerResponse {
|
|
201
491
|
markHeadersSent() {
|
|
202
492
|
this["_header"] = true;
|
|
203
493
|
}
|
|
494
|
+
getRawHeaderNames() {
|
|
495
|
+
return super["getRawHeaderNames"]();
|
|
496
|
+
}
|
|
204
497
|
};
|
|
205
|
-
var
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
#
|
|
498
|
+
var AbstractServerHttpRequest = class extends AbstractHttpRequest {
|
|
499
|
+
};
|
|
500
|
+
var AbstractServerHttpResponse = class extends AbstractHttpResponse {
|
|
501
|
+
#cookies = [];
|
|
502
|
+
#statusCode;
|
|
503
|
+
#state = "new";
|
|
504
|
+
#commitActions = [];
|
|
505
|
+
setStatusCode(statusCode) {
|
|
506
|
+
if (this.#state === "committed") {
|
|
507
|
+
return false;
|
|
508
|
+
} else {
|
|
509
|
+
this.#statusCode = statusCode;
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
setRawStatusCode(statusCode) {
|
|
514
|
+
return this.setStatusCode(statusCode === void 0 ? void 0 : httpStatusCode(statusCode));
|
|
515
|
+
}
|
|
516
|
+
get statusCode() {
|
|
517
|
+
return this.#statusCode;
|
|
518
|
+
}
|
|
519
|
+
addCookie(cookie) {
|
|
520
|
+
if (this.#state === "committed") {
|
|
521
|
+
throw new Error(`Cannot add cookie ${JSON.stringify(cookie)} because HTTP response has already been committed`);
|
|
522
|
+
}
|
|
523
|
+
this.#cookies.push(cookie);
|
|
524
|
+
return this;
|
|
525
|
+
}
|
|
526
|
+
beforeCommit(action) {
|
|
527
|
+
this.#commitActions.push(action);
|
|
528
|
+
}
|
|
529
|
+
get commited() {
|
|
530
|
+
const state = this.#state;
|
|
531
|
+
return state !== "new" && state !== "commit-action-failed";
|
|
532
|
+
}
|
|
533
|
+
async body(body) {
|
|
534
|
+
if (body instanceof ReadableStream) {
|
|
535
|
+
throw new Error("ReadableStream body not supported yet");
|
|
536
|
+
}
|
|
537
|
+
const buffer = await body;
|
|
538
|
+
try {
|
|
539
|
+
return await this.doCommit(async () => {
|
|
540
|
+
return await this.bodyInternal(Promise.resolve(buffer));
|
|
541
|
+
}).catch((error) => {
|
|
542
|
+
throw error;
|
|
543
|
+
});
|
|
544
|
+
} catch (error) {
|
|
545
|
+
throw error;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
async end() {
|
|
549
|
+
if (!this.commited) {
|
|
550
|
+
return this.doCommit();
|
|
551
|
+
} else {
|
|
552
|
+
return Promise.resolve(false);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
doCommit(writeAction) {
|
|
556
|
+
const state = this.#state;
|
|
557
|
+
let allActions = Promise.resolve();
|
|
558
|
+
if (state === "new") {
|
|
559
|
+
this.#state = "committing";
|
|
560
|
+
if (this.#commitActions.length > 0) {
|
|
561
|
+
allActions = this.#commitActions.reduce(
|
|
562
|
+
(acc, cur) => acc.then(() => cur()),
|
|
563
|
+
Promise.resolve()
|
|
564
|
+
).catch((error) => {
|
|
565
|
+
const state2 = this.#state;
|
|
566
|
+
if (state2 === "committing") {
|
|
567
|
+
this.#state = "commit-action-failed";
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
} else if (state === "commit-action-failed") {
|
|
572
|
+
this.#state = "committing";
|
|
573
|
+
} else {
|
|
574
|
+
return Promise.resolve(false);
|
|
575
|
+
}
|
|
576
|
+
allActions = allActions.then(() => {
|
|
577
|
+
this.applyStatusCode();
|
|
578
|
+
this.applyHeaders();
|
|
579
|
+
this.applyCookies();
|
|
580
|
+
this.#state = "committed";
|
|
581
|
+
});
|
|
582
|
+
return allActions.then(async () => {
|
|
583
|
+
return writeAction !== void 0 ? await writeAction() : true;
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
applyStatusCode() {
|
|
587
|
+
}
|
|
588
|
+
applyHeaders() {
|
|
589
|
+
}
|
|
590
|
+
applyCookies() {
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
var HttpServerRequest = class extends AbstractServerHttpRequest {
|
|
594
|
+
#url;
|
|
595
|
+
#cookies;
|
|
210
596
|
#req;
|
|
211
597
|
constructor(req) {
|
|
598
|
+
super(new IncomingMessageHeaders(req));
|
|
212
599
|
this.#req = req;
|
|
213
|
-
this.#headers = new IncomingMessageHeaders(this.#req);
|
|
214
600
|
}
|
|
215
|
-
|
|
601
|
+
getNativeRequest() {
|
|
216
602
|
return this.#req;
|
|
217
603
|
}
|
|
218
604
|
get upgrade() {
|
|
@@ -221,15 +607,12 @@ var HttpServerRequest = class {
|
|
|
221
607
|
get http2() {
|
|
222
608
|
return this.#req.httpVersionMajor >= 2;
|
|
223
609
|
}
|
|
224
|
-
get headers() {
|
|
225
|
-
return this.#headers;
|
|
226
|
-
}
|
|
227
610
|
get path() {
|
|
228
611
|
return this.URL?.pathname;
|
|
229
612
|
}
|
|
230
613
|
get URL() {
|
|
231
|
-
this
|
|
232
|
-
return this
|
|
614
|
+
this.#url ??= new URL(this.#req.urlBang, `${this.protocol}://${this.host}`);
|
|
615
|
+
return this.#url;
|
|
233
616
|
}
|
|
234
617
|
get query() {
|
|
235
618
|
return this.URL?.search;
|
|
@@ -243,7 +626,7 @@ var HttpServerRequest = class {
|
|
|
243
626
|
dh = this.#req.headers[":authority"];
|
|
244
627
|
}
|
|
245
628
|
dh ??= this.#req.socket.remoteAddress;
|
|
246
|
-
return
|
|
629
|
+
return super.parseHost(dh);
|
|
247
630
|
}
|
|
248
631
|
get protocol() {
|
|
249
632
|
let dp = void 0;
|
|
@@ -251,47 +634,65 @@ var HttpServerRequest = class {
|
|
|
251
634
|
dp = this.#req.headers[":scheme"];
|
|
252
635
|
}
|
|
253
636
|
dp ??= this.#req.socketEncrypted ? "https" : "http";
|
|
254
|
-
return
|
|
637
|
+
return super.parseProtocol(dp);
|
|
255
638
|
}
|
|
256
639
|
get socket() {
|
|
257
640
|
return this.#req.socket;
|
|
258
641
|
}
|
|
642
|
+
get remoteAddress() {
|
|
643
|
+
const family = this.#req.socket.remoteFamily;
|
|
644
|
+
const address = this.#req.socket.remoteAddress;
|
|
645
|
+
const port = this.#req.socket.remotePort;
|
|
646
|
+
if (!family || !address || !port) {
|
|
647
|
+
return void 0;
|
|
648
|
+
}
|
|
649
|
+
return { family, address, port };
|
|
650
|
+
}
|
|
259
651
|
get cookies() {
|
|
260
|
-
this
|
|
261
|
-
return this
|
|
652
|
+
this.#cookies ??= super.cookies;
|
|
653
|
+
return this.#cookies;
|
|
262
654
|
}
|
|
263
655
|
get body() {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
656
|
+
return http.IncomingMessage.toWeb(this.#req);
|
|
657
|
+
}
|
|
658
|
+
async blob() {
|
|
659
|
+
const chunks = [];
|
|
660
|
+
if (this.body !== void 0) {
|
|
661
|
+
for await (const chunk of this.body) {
|
|
662
|
+
chunks.push(chunk);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return new Blob(chunks, { type: this.headers.one("content-type") || "application/octet-stream" });
|
|
271
666
|
}
|
|
272
|
-
|
|
273
|
-
|
|
667
|
+
async text() {
|
|
668
|
+
const blob = await this.blob();
|
|
669
|
+
return await blob.text();
|
|
274
670
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
return formData;
|
|
280
|
-
});
|
|
671
|
+
async formData() {
|
|
672
|
+
const blob = await this.blob();
|
|
673
|
+
const text = await blob.text();
|
|
674
|
+
return new URLSearchParams(text);
|
|
281
675
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
676
|
+
async json() {
|
|
677
|
+
const blob = await this.blob();
|
|
678
|
+
if (blob.size === 0) {
|
|
679
|
+
return void 0;
|
|
680
|
+
}
|
|
681
|
+
const text = await blob.text();
|
|
682
|
+
return JSON.parse(text);
|
|
683
|
+
}
|
|
684
|
+
initId() {
|
|
685
|
+
const remoteIp = this.#req.socket.remoteAddress;
|
|
686
|
+
if (!remoteIp) {
|
|
687
|
+
throw new Error("Socket has no remote address");
|
|
688
|
+
}
|
|
689
|
+
return `${remoteIp}:${this.#req.socket.remotePort}`;
|
|
290
690
|
}
|
|
291
691
|
};
|
|
292
|
-
var IncomingMessageHeaders = class {
|
|
692
|
+
var IncomingMessageHeaders = class extends AbstractHttpHeaders {
|
|
293
693
|
#msg;
|
|
294
694
|
constructor(msg) {
|
|
695
|
+
super();
|
|
295
696
|
this.#msg = msg;
|
|
296
697
|
}
|
|
297
698
|
has(name) {
|
|
@@ -301,7 +702,7 @@ var IncomingMessageHeaders = class {
|
|
|
301
702
|
return this.#msg.headers[name];
|
|
302
703
|
}
|
|
303
704
|
list(name) {
|
|
304
|
-
return toList(
|
|
705
|
+
return super.toList(name);
|
|
305
706
|
}
|
|
306
707
|
one(name) {
|
|
307
708
|
const value = this.#msg.headers[name];
|
|
@@ -314,9 +715,10 @@ var IncomingMessageHeaders = class {
|
|
|
314
715
|
return Object.keys(this.#msg.headers).values();
|
|
315
716
|
}
|
|
316
717
|
};
|
|
317
|
-
var OutgoingMessageHeaders = class {
|
|
718
|
+
var OutgoingMessageHeaders = class extends AbstractHttpHeaders {
|
|
318
719
|
#msg;
|
|
319
720
|
constructor(msg) {
|
|
721
|
+
super();
|
|
320
722
|
this.#msg = msg;
|
|
321
723
|
}
|
|
322
724
|
has(name) {
|
|
@@ -350,284 +752,248 @@ var OutgoingMessageHeaders = class {
|
|
|
350
752
|
}
|
|
351
753
|
return this;
|
|
352
754
|
}
|
|
353
|
-
add(name, value) {
|
|
354
|
-
if (!this.#msg.headersSent) {
|
|
355
|
-
this.#msg.appendHeader(name, value);
|
|
356
|
-
}
|
|
357
|
-
return this;
|
|
358
|
-
}
|
|
359
|
-
list(name) {
|
|
360
|
-
const values = this.get(name);
|
|
361
|
-
return toList(values);
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
var HttpServerResponse = class {
|
|
365
|
-
#headers;
|
|
366
|
-
#res;
|
|
367
|
-
constructor(res) {
|
|
368
|
-
this.#res = res;
|
|
369
|
-
this.#headers = new OutgoingMessageHeaders(res);
|
|
370
|
-
}
|
|
371
|
-
get unsafeServerResponse() {
|
|
372
|
-
return this.#res;
|
|
373
|
-
}
|
|
374
|
-
get statusCode() {
|
|
375
|
-
return this.#res.statusCode;
|
|
376
|
-
}
|
|
377
|
-
set statusCode(value) {
|
|
378
|
-
if (this.#res.headersSent) {
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
this.#res.statusCode = value;
|
|
382
|
-
}
|
|
383
|
-
set statusMessage(value) {
|
|
384
|
-
this.#res.statusMessage = value;
|
|
385
|
-
}
|
|
386
|
-
get headers() {
|
|
387
|
-
return this.#headers;
|
|
388
|
-
}
|
|
389
|
-
get cookies() {
|
|
390
|
-
return this.headers.list("set-cookie").map((cookie) => {
|
|
391
|
-
const parsed = Cookie.parse(cookie);
|
|
392
|
-
if (parsed) {
|
|
393
|
-
const result = { name: parsed.key, value: parsed.value, maxAge: Number(parsed.maxAge ?? -1) };
|
|
394
|
-
if (parsed.httpOnly) result.httpOnly = true;
|
|
395
|
-
if (parsed.domain) result.domain = parsed.domain;
|
|
396
|
-
if (parsed.path) result.path = parsed.path;
|
|
397
|
-
if (parsed.secure) result.secure = true;
|
|
398
|
-
if (parsed.httpOnly) result.httpOnly = true;
|
|
399
|
-
if (parsed.sameSite) result.sameSite = parsed.sameSite;
|
|
400
|
-
return result;
|
|
401
|
-
}
|
|
402
|
-
}).filter((cookie) => cookie !== void 0);
|
|
403
|
-
}
|
|
404
|
-
end(chunk) {
|
|
405
|
-
if (!this.#res.headersSent) {
|
|
406
|
-
return new Promise((resolve, reject) => {
|
|
407
|
-
try {
|
|
408
|
-
if (chunk === void 0) {
|
|
409
|
-
this.#res.end(() => {
|
|
410
|
-
resolve(true);
|
|
411
|
-
});
|
|
412
|
-
} else {
|
|
413
|
-
this.#res.end(chunk, () => {
|
|
414
|
-
resolve(true);
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
} catch (e) {
|
|
418
|
-
reject(e instanceof Error ? e : new Error(`end failed: ${e}`));
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
} else {
|
|
422
|
-
return Promise.resolve(false);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
addCookie(cookie) {
|
|
426
|
-
this.headers.add("set-cookie", new Cookie({
|
|
427
|
-
key: cookie.name,
|
|
428
|
-
value: cookie.value,
|
|
429
|
-
maxAge: cookie.maxAge,
|
|
430
|
-
domain: cookie.domain,
|
|
431
|
-
path: cookie.path,
|
|
432
|
-
secure: cookie.secure,
|
|
433
|
-
httpOnly: cookie.httpOnly,
|
|
434
|
-
sameSite: cookie.sameSite
|
|
435
|
-
}).toString());
|
|
436
|
-
return this;
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
var DefaultWebExchange = class {
|
|
440
|
-
request;
|
|
441
|
-
response;
|
|
442
|
-
constructor(request, response) {
|
|
443
|
-
this.request = request;
|
|
444
|
-
this.response = response;
|
|
445
|
-
}
|
|
446
|
-
get method() {
|
|
447
|
-
return this.request.method;
|
|
448
|
-
}
|
|
449
|
-
get path() {
|
|
450
|
-
return this.request.path;
|
|
451
|
-
}
|
|
452
|
-
principal() {
|
|
453
|
-
return Promise.resolve(void 0);
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
function toList(values) {
|
|
457
|
-
if (typeof values === "string") {
|
|
458
|
-
values = [values];
|
|
459
|
-
}
|
|
460
|
-
if (typeof values === "number") {
|
|
461
|
-
values = [String(values)];
|
|
462
|
-
}
|
|
463
|
-
const list = [];
|
|
464
|
-
if (values) {
|
|
465
|
-
for (const value of values) {
|
|
466
|
-
if (value) {
|
|
467
|
-
list.push(...parseHeader(value));
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
return list;
|
|
472
|
-
}
|
|
473
|
-
function parseHeader(value) {
|
|
474
|
-
const list = [];
|
|
475
|
-
{
|
|
476
|
-
let start2 = 0;
|
|
477
|
-
let end = 0;
|
|
478
|
-
for (let i = 0; i < value.length; i++) {
|
|
479
|
-
switch (value.charCodeAt(i)) {
|
|
480
|
-
case 32:
|
|
481
|
-
if (start2 === end) {
|
|
482
|
-
start2 = end = i + 1;
|
|
483
|
-
}
|
|
484
|
-
break;
|
|
485
|
-
case 44:
|
|
486
|
-
list.push(value.slice(start2, end));
|
|
487
|
-
start2 = end = i + 1;
|
|
488
|
-
break;
|
|
489
|
-
default:
|
|
490
|
-
end = end + 1;
|
|
491
|
-
break;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
list.push(value.slice(start2, end));
|
|
495
|
-
}
|
|
496
|
-
return list;
|
|
497
|
-
}
|
|
498
|
-
var MapHttpHeaders = class extends Map {
|
|
499
|
-
get(name) {
|
|
500
|
-
return super.get(name.toLowerCase());
|
|
501
|
-
}
|
|
502
|
-
one(name) {
|
|
503
|
-
return this.get(name)?.[0];
|
|
504
|
-
}
|
|
505
|
-
list(name) {
|
|
506
|
-
const values = super.get(name.toLowerCase());
|
|
507
|
-
return toList(values);
|
|
508
|
-
}
|
|
509
|
-
set(name, value) {
|
|
510
|
-
if (typeof value === "number") {
|
|
511
|
-
value = String(value);
|
|
512
|
-
}
|
|
513
|
-
if (typeof value === "string") {
|
|
514
|
-
value = [value];
|
|
515
|
-
}
|
|
516
|
-
if (value) {
|
|
517
|
-
return super.set(name.toLowerCase(), value);
|
|
518
|
-
} else {
|
|
519
|
-
super.delete(name.toLowerCase());
|
|
520
|
-
return this;
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
add(name, value) {
|
|
524
|
-
const prev = super.get(name.toLowerCase());
|
|
525
|
-
if (typeof value === "string") {
|
|
526
|
-
value = [value];
|
|
527
|
-
}
|
|
528
|
-
if (prev) {
|
|
529
|
-
value = prev.concat(value);
|
|
755
|
+
add(name, value) {
|
|
756
|
+
if (!this.#msg.headersSent) {
|
|
757
|
+
this.#msg.appendHeader(name, value);
|
|
530
758
|
}
|
|
531
|
-
this.set(name, value);
|
|
532
759
|
return this;
|
|
533
760
|
}
|
|
761
|
+
list(name) {
|
|
762
|
+
return super.toList(name);
|
|
763
|
+
}
|
|
534
764
|
};
|
|
535
|
-
var
|
|
536
|
-
#
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
765
|
+
var HttpServerResponse = class extends AbstractServerHttpResponse {
|
|
766
|
+
#res;
|
|
767
|
+
constructor(res) {
|
|
768
|
+
super(new OutgoingMessageHeaders(res));
|
|
769
|
+
this.#res = res;
|
|
770
|
+
}
|
|
771
|
+
getNativeResponse() {
|
|
772
|
+
return this.#res;
|
|
773
|
+
}
|
|
774
|
+
get statusCode() {
|
|
775
|
+
const status = super.statusCode;
|
|
776
|
+
return status ?? { value: this.#res.statusCode };
|
|
777
|
+
}
|
|
778
|
+
applyStatusCode() {
|
|
779
|
+
const status = super.statusCode;
|
|
780
|
+
if (status !== void 0) {
|
|
781
|
+
this.#res.statusCode = status.value;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
addCookie(cookie) {
|
|
785
|
+
this.headers.add("Set-Cookie", super.setCookieValue(cookie));
|
|
786
|
+
return this;
|
|
787
|
+
}
|
|
788
|
+
async bodyInternal(body) {
|
|
789
|
+
if (!this.#res.headersSent) {
|
|
790
|
+
if (body instanceof ReadableStream) {
|
|
791
|
+
throw new Error("ReadableStream body not supported in response");
|
|
546
792
|
} else {
|
|
547
|
-
|
|
793
|
+
const chunk = await body;
|
|
794
|
+
return await new Promise((resolve, reject) => {
|
|
795
|
+
try {
|
|
796
|
+
if (chunk === void 0) {
|
|
797
|
+
this.#res.end(() => {
|
|
798
|
+
resolve(true);
|
|
799
|
+
});
|
|
800
|
+
} else {
|
|
801
|
+
if (!this.headers.has("content-length")) {
|
|
802
|
+
if (typeof chunk === "string") {
|
|
803
|
+
this.headers.set("content-length", Buffer.byteLength(chunk));
|
|
804
|
+
} else {
|
|
805
|
+
this.headers.set("content-length", chunk.byteLength);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
this.#res.end(chunk, () => {
|
|
809
|
+
resolve(true);
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
} catch (e) {
|
|
813
|
+
reject(e instanceof Error ? e : new Error(`end failed: ${e}`));
|
|
814
|
+
}
|
|
815
|
+
});
|
|
548
816
|
}
|
|
817
|
+
} else {
|
|
818
|
+
return false;
|
|
549
819
|
}
|
|
550
|
-
this.#url = url;
|
|
551
|
-
this.method = method ?? "GET";
|
|
552
|
-
this.headers.set("Host", this.#url.hostname);
|
|
553
|
-
this.path = this.#url.pathname ?? "/";
|
|
554
820
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
821
|
+
};
|
|
822
|
+
var ServerHttpRequestDecorator = class _ServerHttpRequestDecorator {
|
|
823
|
+
#delegate;
|
|
824
|
+
constructor(request) {
|
|
825
|
+
this.#delegate = request;
|
|
826
|
+
}
|
|
827
|
+
get delegate() {
|
|
828
|
+
return this.#delegate;
|
|
829
|
+
}
|
|
830
|
+
get id() {
|
|
831
|
+
return this.#delegate.id;
|
|
832
|
+
}
|
|
833
|
+
get method() {
|
|
834
|
+
return this.#delegate.method;
|
|
835
|
+
}
|
|
836
|
+
get path() {
|
|
837
|
+
return this.#delegate.path;
|
|
559
838
|
}
|
|
560
839
|
get protocol() {
|
|
561
|
-
return
|
|
840
|
+
return this.#delegate.protocol;
|
|
841
|
+
}
|
|
842
|
+
get host() {
|
|
843
|
+
return this.#delegate.host;
|
|
844
|
+
}
|
|
845
|
+
get URL() {
|
|
846
|
+
return this.#delegate.URL;
|
|
847
|
+
}
|
|
848
|
+
get headers() {
|
|
849
|
+
return this.#delegate.headers;
|
|
562
850
|
}
|
|
563
851
|
get cookies() {
|
|
564
|
-
return
|
|
852
|
+
return this.#delegate.cookies;
|
|
853
|
+
}
|
|
854
|
+
get remoteAddress() {
|
|
855
|
+
return this.#delegate.remoteAddress;
|
|
856
|
+
}
|
|
857
|
+
get upgrade() {
|
|
858
|
+
return this.#delegate.upgrade;
|
|
565
859
|
}
|
|
566
860
|
get body() {
|
|
567
|
-
|
|
568
|
-
return body ? Promise.resolve(body) : Promise.reject(new Error(`no body set`));
|
|
861
|
+
return this.#delegate.body;
|
|
569
862
|
}
|
|
570
|
-
|
|
571
|
-
return this.
|
|
863
|
+
async blob() {
|
|
864
|
+
return await this.#delegate.blob();
|
|
572
865
|
}
|
|
573
|
-
|
|
574
|
-
return
|
|
866
|
+
async text() {
|
|
867
|
+
return await this.#delegate.text();
|
|
575
868
|
}
|
|
576
|
-
|
|
577
|
-
this.#
|
|
578
|
-
if (!this.headers.has("content-type")) {
|
|
579
|
-
this.headers.set("content-type", value.type || "application/octet-stream");
|
|
580
|
-
}
|
|
869
|
+
async formData() {
|
|
870
|
+
return await this.#delegate.formData();
|
|
581
871
|
}
|
|
582
|
-
|
|
583
|
-
return
|
|
872
|
+
async json() {
|
|
873
|
+
return await this.#delegate.json();
|
|
584
874
|
}
|
|
585
|
-
|
|
586
|
-
return
|
|
875
|
+
toString() {
|
|
876
|
+
return `${_ServerHttpRequestDecorator.name} [delegate: ${this.delegate.toString()}]`;
|
|
877
|
+
}
|
|
878
|
+
static getNativeRequest(request) {
|
|
879
|
+
if (request instanceof AbstractServerHttpRequest) {
|
|
880
|
+
return request.getNativeRequest();
|
|
881
|
+
} else if (request instanceof _ServerHttpRequestDecorator) {
|
|
882
|
+
return _ServerHttpRequestDecorator.getNativeRequest(request.delegate);
|
|
883
|
+
} else {
|
|
884
|
+
throw new Error(`Cannot get native request from ${request.constructor.name}`);
|
|
885
|
+
}
|
|
587
886
|
}
|
|
588
887
|
};
|
|
589
|
-
var
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
888
|
+
var ServerHttpResponseDecorator = class _ServerHttpResponseDecorator {
|
|
889
|
+
#delegate;
|
|
890
|
+
constructor(response) {
|
|
891
|
+
this.#delegate = response;
|
|
892
|
+
}
|
|
893
|
+
get delegate() {
|
|
894
|
+
return this.#delegate;
|
|
895
|
+
}
|
|
896
|
+
setStatusCode(statusCode) {
|
|
897
|
+
return this.delegate.setStatusCode(statusCode);
|
|
898
|
+
}
|
|
899
|
+
setRawStatusCode(statusCode) {
|
|
900
|
+
return this.delegate.setRawStatusCode(statusCode);
|
|
901
|
+
}
|
|
902
|
+
get statusCode() {
|
|
903
|
+
return this.delegate.statusCode;
|
|
904
|
+
}
|
|
905
|
+
get cookies() {
|
|
906
|
+
return this.delegate.cookies;
|
|
907
|
+
}
|
|
593
908
|
addCookie(cookie) {
|
|
594
|
-
this.
|
|
909
|
+
this.delegate.addCookie(cookie);
|
|
595
910
|
return this;
|
|
596
911
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
default:
|
|
617
|
-
throw new Error(`Unsupported chunk type: ${typeof chunk}`);
|
|
912
|
+
async end() {
|
|
913
|
+
return await this.delegate.end();
|
|
914
|
+
}
|
|
915
|
+
async body(body) {
|
|
916
|
+
return await this.#delegate.body(body);
|
|
917
|
+
}
|
|
918
|
+
get headers() {
|
|
919
|
+
return this.#delegate.headers;
|
|
920
|
+
}
|
|
921
|
+
toString() {
|
|
922
|
+
return `${_ServerHttpResponseDecorator.name} [delegate: ${this.delegate.toString()}]`;
|
|
923
|
+
}
|
|
924
|
+
static getNativeResponse(response) {
|
|
925
|
+
if (response instanceof AbstractServerHttpResponse) {
|
|
926
|
+
return response.getNativeResponse();
|
|
927
|
+
} else if (response instanceof _ServerHttpResponseDecorator) {
|
|
928
|
+
return _ServerHttpResponseDecorator.getNativeResponse(response.delegate);
|
|
929
|
+
} else {
|
|
930
|
+
throw new Error(`Cannot get native response from ${response.constructor.name}`);
|
|
618
931
|
}
|
|
619
|
-
return true;
|
|
620
932
|
}
|
|
621
933
|
};
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
if (!remoteIp) {
|
|
627
|
-
throw new Error("Socket has no remote address");
|
|
934
|
+
var ServerWebExchangeDecorator = class _ServerWebExchangeDecorator {
|
|
935
|
+
#delegate;
|
|
936
|
+
constructor(exchange) {
|
|
937
|
+
this.#delegate = exchange;
|
|
628
938
|
}
|
|
629
|
-
|
|
630
|
-
|
|
939
|
+
get delegate() {
|
|
940
|
+
return this.#delegate;
|
|
941
|
+
}
|
|
942
|
+
get request() {
|
|
943
|
+
return this.#delegate.request;
|
|
944
|
+
}
|
|
945
|
+
get response() {
|
|
946
|
+
return this.#delegate.response;
|
|
947
|
+
}
|
|
948
|
+
attribute(name) {
|
|
949
|
+
return this.#delegate.attribute(name);
|
|
950
|
+
}
|
|
951
|
+
principal() {
|
|
952
|
+
return this.#delegate.principal();
|
|
953
|
+
}
|
|
954
|
+
get logPrefix() {
|
|
955
|
+
return this.#delegate.logPrefix;
|
|
956
|
+
}
|
|
957
|
+
toString() {
|
|
958
|
+
return `${_ServerWebExchangeDecorator.name} [delegate: ${this.delegate}]`;
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
var DefaultWebExchange = class {
|
|
962
|
+
request;
|
|
963
|
+
response;
|
|
964
|
+
#attributes = {};
|
|
965
|
+
#logId;
|
|
966
|
+
#logPrefix = "";
|
|
967
|
+
constructor(request, response) {
|
|
968
|
+
this.#attributes[LOG_ID_ATTRIBUTE] = request.id;
|
|
969
|
+
this.request = request;
|
|
970
|
+
this.response = response;
|
|
971
|
+
}
|
|
972
|
+
get method() {
|
|
973
|
+
return this.request.method;
|
|
974
|
+
}
|
|
975
|
+
get path() {
|
|
976
|
+
return this.request.path;
|
|
977
|
+
}
|
|
978
|
+
get attributes() {
|
|
979
|
+
return this.#attributes;
|
|
980
|
+
}
|
|
981
|
+
attribute(name) {
|
|
982
|
+
return this.attributes[name];
|
|
983
|
+
}
|
|
984
|
+
principal() {
|
|
985
|
+
return Promise.resolve(void 0);
|
|
986
|
+
}
|
|
987
|
+
get logPrefix() {
|
|
988
|
+
const value = this.attribute(LOG_ID_ATTRIBUTE);
|
|
989
|
+
if (this.#logId !== value) {
|
|
990
|
+
this.#logId = value;
|
|
991
|
+
this.#logPrefix = value !== void 0 ? `[${value}] ` : "";
|
|
992
|
+
}
|
|
993
|
+
return this.#logPrefix;
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
var LOG_ID_ATTRIBUTE = "io.interop.gateway.server.log_id";
|
|
631
997
|
|
|
632
998
|
// src/server/address.ts
|
|
633
999
|
import { networkInterfaces } from "node:os";
|
|
@@ -809,7 +1175,20 @@ async function stop(m) {
|
|
|
809
1175
|
return await run(m, "stop");
|
|
810
1176
|
}
|
|
811
1177
|
|
|
812
|
-
// src/
|
|
1178
|
+
// src/server/server-header.ts
|
|
1179
|
+
import info from "@interopio/gateway-server/package.json" with { type: "json" };
|
|
1180
|
+
var serverHeader = (server) => {
|
|
1181
|
+
server ??= `${info.name} - v${info.version}`;
|
|
1182
|
+
return async ({ response }, next) => {
|
|
1183
|
+
if (server !== false && !response.headers.has("server")) {
|
|
1184
|
+
response.headers.set("Server", server);
|
|
1185
|
+
}
|
|
1186
|
+
await next();
|
|
1187
|
+
};
|
|
1188
|
+
};
|
|
1189
|
+
var server_header_default = (server) => serverHeader(server);
|
|
1190
|
+
|
|
1191
|
+
// src/server/ws-client-verify.ts
|
|
813
1192
|
import { IOGateway as IOGateway3 } from "@interopio/gateway";
|
|
814
1193
|
var log3 = getLogger("gateway.ws.client-verify");
|
|
815
1194
|
function acceptsMissing(originFilters) {
|
|
@@ -881,19 +1260,6 @@ function regexifyOriginFilters(originFilters) {
|
|
|
881
1260
|
}
|
|
882
1261
|
}
|
|
883
1262
|
|
|
884
|
-
// src/server/server-header.ts
|
|
885
|
-
import info from "@interopio/gateway-server/package.json" with { type: "json" };
|
|
886
|
-
var serverHeader = (server) => {
|
|
887
|
-
server ??= `${info.name} - v${info.version}`;
|
|
888
|
-
return async ({ response }, next) => {
|
|
889
|
-
if (server !== false && !response.headers.has("server")) {
|
|
890
|
-
response.headers.set("Server", server);
|
|
891
|
-
}
|
|
892
|
-
await next();
|
|
893
|
-
};
|
|
894
|
-
};
|
|
895
|
-
var server_header_default = (server) => serverHeader(server);
|
|
896
|
-
|
|
897
1263
|
// src/server/util/matchers.ts
|
|
898
1264
|
var or = (matchers) => {
|
|
899
1265
|
return async (exchange) => {
|
|
@@ -1000,15 +1366,6 @@ upgradeMatcher.toString = () => "websocket upgrade";
|
|
|
1000
1366
|
|
|
1001
1367
|
// src/app/route.ts
|
|
1002
1368
|
import { IOGateway as IOGateway4 } from "@interopio/gateway";
|
|
1003
|
-
function findSocketRoute({ request }, { sockets: routes }) {
|
|
1004
|
-
const path = request.path ?? "/";
|
|
1005
|
-
const route = routes.get(path) ?? Array.from(routes.values()).find((route2) => {
|
|
1006
|
-
if (path === "/" && route2.default === true) {
|
|
1007
|
-
return true;
|
|
1008
|
-
}
|
|
1009
|
-
});
|
|
1010
|
-
return [route, path];
|
|
1011
|
-
}
|
|
1012
1369
|
async function configure(app, config, routes) {
|
|
1013
1370
|
const applyCors = (matcher, requestMethod, options) => {
|
|
1014
1371
|
if (options?.cors) {
|
|
@@ -1210,7 +1567,7 @@ var corsFilter = (opts) => {
|
|
|
1210
1567
|
var cors_default = corsFilter;
|
|
1211
1568
|
var logger = getLogger("cors");
|
|
1212
1569
|
function rejectRequest(response) {
|
|
1213
|
-
response.
|
|
1570
|
+
response.setStatusCode(HttpStatus.FORBIDDEN);
|
|
1214
1571
|
}
|
|
1215
1572
|
function handleInternal(exchange, config, preFlightRequest) {
|
|
1216
1573
|
const { request, response } = exchange;
|
|
@@ -1300,70 +1657,243 @@ function checkMethods(config, requestMethod) {
|
|
|
1300
1657
|
if (allowedMethods === ALL) {
|
|
1301
1658
|
return [requestMethod];
|
|
1302
1659
|
}
|
|
1303
|
-
if (IOGateway5.Filtering.valuesMatch(allowedMethods, requestMethod)) {
|
|
1304
|
-
return allowedMethods;
|
|
1660
|
+
if (IOGateway5.Filtering.valuesMatch(allowedMethods, requestMethod)) {
|
|
1661
|
+
return allowedMethods;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
function checkHeaders(config, requestHeaders) {
|
|
1666
|
+
if (requestHeaders === void 0) {
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
if (requestHeaders.length == 0) {
|
|
1670
|
+
return [];
|
|
1671
|
+
}
|
|
1672
|
+
const allowedHeaders = config.allowHeaders;
|
|
1673
|
+
if (allowedHeaders === void 0) {
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
const allowAnyHeader = allowedHeaders === ALL || allowedHeaders.includes(ALL);
|
|
1677
|
+
const result = [];
|
|
1678
|
+
for (const requestHeader of requestHeaders) {
|
|
1679
|
+
const value = requestHeader?.trim();
|
|
1680
|
+
if (value) {
|
|
1681
|
+
if (allowAnyHeader) {
|
|
1682
|
+
result.push(value);
|
|
1683
|
+
} else {
|
|
1684
|
+
for (const allowedHeader of allowedHeaders) {
|
|
1685
|
+
if (value.toLowerCase() === allowedHeader) {
|
|
1686
|
+
result.push(value);
|
|
1687
|
+
break;
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
if (result.length > 0) {
|
|
1694
|
+
return result;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
function trimTrailingSlash(origin) {
|
|
1698
|
+
return origin.endsWith("/") ? origin.slice(0, -1) : origin;
|
|
1699
|
+
}
|
|
1700
|
+
function getMethodToUse(request, isPreFlight) {
|
|
1701
|
+
return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
|
|
1702
|
+
}
|
|
1703
|
+
function getHeadersToUse(request, isPreFlight) {
|
|
1704
|
+
const headers2 = request.headers;
|
|
1705
|
+
return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
|
|
1706
|
+
}
|
|
1707
|
+
var matchingCorsConfigSource = (opts) => {
|
|
1708
|
+
return async (exchange) => {
|
|
1709
|
+
for (const [matcher, config] of opts.mappings) {
|
|
1710
|
+
if ((await matcher(exchange)).match) {
|
|
1711
|
+
logger.debug(`resolved cors config on '${exchange.request.path}' using ${matcher}: ${JSON.stringify(config)}`);
|
|
1712
|
+
return config;
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
};
|
|
1717
|
+
|
|
1718
|
+
// src/mock/server/exchange.ts
|
|
1719
|
+
import stream from "node:stream/web";
|
|
1720
|
+
var MockServerHttpRequest = class extends AbstractHttpRequest {
|
|
1721
|
+
#url;
|
|
1722
|
+
#body;
|
|
1723
|
+
upgrade = false;
|
|
1724
|
+
constructor(url, method) {
|
|
1725
|
+
super(new MapHttpHeaders());
|
|
1726
|
+
if (typeof url === "string") {
|
|
1727
|
+
if (URL.canParse(url)) {
|
|
1728
|
+
url = new URL(url);
|
|
1729
|
+
} else if (URL.canParse(url, "http://localhost")) {
|
|
1730
|
+
url = new URL(url, "http://localhost");
|
|
1731
|
+
} else {
|
|
1732
|
+
throw new TypeError("URL cannot parse url");
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
this.#url = url;
|
|
1736
|
+
this.method = method ?? "GET";
|
|
1737
|
+
this.setHeader("Host", this.#url.hostname);
|
|
1738
|
+
this.path = this.#url.pathname ?? "/";
|
|
1739
|
+
}
|
|
1740
|
+
method;
|
|
1741
|
+
path;
|
|
1742
|
+
get host() {
|
|
1743
|
+
return super.parseHost(this.#url.host);
|
|
1744
|
+
}
|
|
1745
|
+
get protocol() {
|
|
1746
|
+
return super.parseProtocol(this.#url.protocol.slice(0, -1));
|
|
1747
|
+
}
|
|
1748
|
+
get body() {
|
|
1749
|
+
if (this.#body !== void 0) {
|
|
1750
|
+
const blob = this.#body;
|
|
1751
|
+
const asyncIterator = async function* () {
|
|
1752
|
+
const bytes = await blob.bytes();
|
|
1753
|
+
yield bytes;
|
|
1754
|
+
}();
|
|
1755
|
+
return stream.ReadableStream.from(asyncIterator);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
blob() {
|
|
1759
|
+
const body = this.#body;
|
|
1760
|
+
return body ? Promise.resolve(body) : Promise.reject(new Error(`no body set`));
|
|
1761
|
+
}
|
|
1762
|
+
async text() {
|
|
1763
|
+
const blob = await this.blob();
|
|
1764
|
+
return await blob.text();
|
|
1765
|
+
}
|
|
1766
|
+
async formData() {
|
|
1767
|
+
const blob = await this.blob();
|
|
1768
|
+
const text = await blob.text();
|
|
1769
|
+
return new URLSearchParams(text);
|
|
1770
|
+
}
|
|
1771
|
+
async json() {
|
|
1772
|
+
const blob = await this.blob();
|
|
1773
|
+
if (blob.size === 0) {
|
|
1774
|
+
return void 0;
|
|
1775
|
+
}
|
|
1776
|
+
const text = await blob.text();
|
|
1777
|
+
return JSON.parse(text);
|
|
1778
|
+
}
|
|
1779
|
+
async writeBody(body) {
|
|
1780
|
+
if (body === void 0) {
|
|
1781
|
+
this.#body = new Blob([]);
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
if (body instanceof ReadableStream) {
|
|
1785
|
+
const chunks = [];
|
|
1786
|
+
const reader = body.getReader();
|
|
1787
|
+
let done = false;
|
|
1788
|
+
while (!done) {
|
|
1789
|
+
const { value, done: readDone } = await reader.read();
|
|
1790
|
+
if (readDone) {
|
|
1791
|
+
done = true;
|
|
1792
|
+
} else {
|
|
1793
|
+
chunks.push(value);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
this.#body = new Blob(chunks);
|
|
1797
|
+
} else {
|
|
1798
|
+
body = await body;
|
|
1799
|
+
if (typeof body === "string") {
|
|
1800
|
+
this.#body = new Blob([body], { type: "text/plain" });
|
|
1801
|
+
} else {
|
|
1802
|
+
this.#body = new Blob([body]);
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
if (!this.headers.has("content-type")) {
|
|
1806
|
+
this.setHeader("Content-Type", this.#body.type || "application/octet-stream");
|
|
1305
1807
|
}
|
|
1306
1808
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
if (requestHeaders === void 0) {
|
|
1310
|
-
return;
|
|
1809
|
+
get URL() {
|
|
1810
|
+
return new URL(this.path + this.#url.search + this.#url.hash, `${this.protocol}://${this.host}`);
|
|
1311
1811
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1812
|
+
addHeader(name, value) {
|
|
1813
|
+
this.headers.add(name, value);
|
|
1814
|
+
return this;
|
|
1314
1815
|
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
return;
|
|
1816
|
+
setHeader(name, value) {
|
|
1817
|
+
this.headers.set(name, value);
|
|
1818
|
+
return this;
|
|
1318
1819
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1820
|
+
};
|
|
1821
|
+
var MockServerHttpResponse = class extends AbstractServerHttpResponse {
|
|
1822
|
+
#writeHandler;
|
|
1823
|
+
#body = () => {
|
|
1824
|
+
throw new Error("No content was written to the response body nor end was called on this response.");
|
|
1825
|
+
};
|
|
1826
|
+
constructor() {
|
|
1827
|
+
super(new MapHttpHeaders());
|
|
1828
|
+
this.#writeHandler = async (body) => {
|
|
1829
|
+
const chunks = [];
|
|
1830
|
+
let bodyStream;
|
|
1831
|
+
this.#body = () => {
|
|
1832
|
+
bodyStream ??= stream.ReadableStream.from(chunks);
|
|
1833
|
+
return bodyStream;
|
|
1834
|
+
};
|
|
1835
|
+
const reader = body.getReader();
|
|
1836
|
+
let done = false;
|
|
1837
|
+
do {
|
|
1838
|
+
const { value, done: readDone } = await reader.read();
|
|
1839
|
+
if (readDone) {
|
|
1840
|
+
done = true;
|
|
1841
|
+
} else {
|
|
1842
|
+
chunks.push(value);
|
|
1332
1843
|
}
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1844
|
+
} while (!done);
|
|
1845
|
+
return true;
|
|
1846
|
+
};
|
|
1335
1847
|
}
|
|
1336
|
-
|
|
1337
|
-
return
|
|
1848
|
+
get statusCode() {
|
|
1849
|
+
return super.statusCode;
|
|
1338
1850
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
return config;
|
|
1356
|
-
}
|
|
1851
|
+
set writeHandler(handler) {
|
|
1852
|
+
this.#body = () => {
|
|
1853
|
+
throw new Error("Not available with custom write handler");
|
|
1854
|
+
};
|
|
1855
|
+
this.#writeHandler = handler;
|
|
1856
|
+
}
|
|
1857
|
+
getNativeResponse() {
|
|
1858
|
+
throw new Error("This is a mock. No running server, no native response.");
|
|
1859
|
+
}
|
|
1860
|
+
applyStatusCode() {
|
|
1861
|
+
}
|
|
1862
|
+
applyHeaders() {
|
|
1863
|
+
}
|
|
1864
|
+
applyCookies() {
|
|
1865
|
+
for (const cookie of this.cookies) {
|
|
1866
|
+
this.headers.add("Set-Cookie", super.setCookieValue(cookie));
|
|
1357
1867
|
}
|
|
1358
|
-
}
|
|
1868
|
+
}
|
|
1869
|
+
bodyInternal(body) {
|
|
1870
|
+
const it = async function* () {
|
|
1871
|
+
const resolved = await body;
|
|
1872
|
+
if (resolved === void 0) {
|
|
1873
|
+
return;
|
|
1874
|
+
}
|
|
1875
|
+
yield resolved;
|
|
1876
|
+
}();
|
|
1877
|
+
return this.#writeHandler(stream.ReadableStream.from(it));
|
|
1878
|
+
}
|
|
1879
|
+
async end() {
|
|
1880
|
+
return this.doCommit(async () => {
|
|
1881
|
+
return await new Promise((resolve, reject) => {
|
|
1882
|
+
this.#writeHandler(stream.ReadableStream.from([]));
|
|
1883
|
+
});
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
getBody() {
|
|
1887
|
+
return this.#body();
|
|
1888
|
+
}
|
|
1359
1889
|
};
|
|
1360
1890
|
|
|
1361
1891
|
// src/app/cors.ts
|
|
1362
1892
|
function mockUpgradeExchange(path) {
|
|
1363
|
-
const request = new
|
|
1364
|
-
request.
|
|
1893
|
+
const request = new MockServerHttpRequest(path, "GET");
|
|
1894
|
+
request.setHeader("Upgrade", "websocket");
|
|
1365
1895
|
request.upgrade = true;
|
|
1366
|
-
return new DefaultWebExchange(request, new
|
|
1896
|
+
return new DefaultWebExchange(request, new MockServerHttpResponse());
|
|
1367
1897
|
}
|
|
1368
1898
|
async function createCorsConfigSource(context) {
|
|
1369
1899
|
const { sockets: routes, cors } = context;
|
|
@@ -1380,8 +1910,8 @@ async function createCorsConfigSource(context) {
|
|
|
1380
1910
|
routeCorsConfig = combineCorsConfig(routeCorsConfig, {
|
|
1381
1911
|
allowOrigins: route.originFilters?.allow,
|
|
1382
1912
|
allowMethods: ["GET", "CONNECT"],
|
|
1383
|
-
allowHeaders: ["
|
|
1384
|
-
exposeHeaders: ["
|
|
1913
|
+
allowHeaders: ["Upgrade", "Connection", "Origin", "Sec-Websocket-Key", "Sec-Websocket-Version", "Sec-Websocket-Protocol", "Sec-Websocket-Extensions"],
|
|
1914
|
+
exposeHeaders: ["Sec-Websocket-Accept", "Sec-Websocket-Protocol", "Sec-Websocket-Extensions"],
|
|
1385
1915
|
allowCredentials: route.authorize?.access !== "permitted"
|
|
1386
1916
|
});
|
|
1387
1917
|
validatedConfigs.push([and([upgradeMatcher, pattern(path)]), validateCorsConfig(routeCorsConfig)]);
|
|
@@ -1624,7 +2154,7 @@ var httpBasicEntryPoint = (opts) => {
|
|
|
1624
2154
|
const headerValue = createHeaderValue(opts?.realm ?? DEFAULT_REALM);
|
|
1625
2155
|
return async (exchange, _error) => {
|
|
1626
2156
|
const { response } = exchange;
|
|
1627
|
-
response.
|
|
2157
|
+
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
|
1628
2158
|
response.headers.set("WWW-Authenticate", headerValue);
|
|
1629
2159
|
};
|
|
1630
2160
|
};
|
|
@@ -1644,12 +2174,17 @@ var httpBasicAuthenticationConverter = (opts) => {
|
|
|
1644
2174
|
if (parts.length !== 2) {
|
|
1645
2175
|
return void 0;
|
|
1646
2176
|
}
|
|
1647
|
-
return {
|
|
2177
|
+
return {
|
|
2178
|
+
type: "UsernamePassword",
|
|
2179
|
+
authenticated: false,
|
|
2180
|
+
principal: parts[0],
|
|
2181
|
+
credentials: parts[1]
|
|
2182
|
+
};
|
|
1648
2183
|
};
|
|
1649
2184
|
};
|
|
1650
2185
|
|
|
1651
2186
|
// src/server/security/security-context.ts
|
|
1652
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2187
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
1653
2188
|
var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder {
|
|
1654
2189
|
static hasSecurityContext(storage) {
|
|
1655
2190
|
return storage.getStore()?.securityContext !== void 0;
|
|
@@ -1661,7 +2196,7 @@ var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder
|
|
|
1661
2196
|
delete storage.getStore()?.securityContext;
|
|
1662
2197
|
}
|
|
1663
2198
|
static withSecurityContext(securityContext) {
|
|
1664
|
-
return (storage = new
|
|
2199
|
+
return (storage = new AsyncLocalStorage2()) => {
|
|
1665
2200
|
storage.getStore().securityContext = securityContext;
|
|
1666
2201
|
return storage;
|
|
1667
2202
|
};
|
|
@@ -1738,8 +2273,7 @@ function authenticationFilter(opts) {
|
|
|
1738
2273
|
var httpStatusEntryPoint = (opts) => {
|
|
1739
2274
|
return async (exchange, _error) => {
|
|
1740
2275
|
const response = exchange.response;
|
|
1741
|
-
response.
|
|
1742
|
-
response.statusMessage = opts.httpStatus.message;
|
|
2276
|
+
response.setStatusCode(opts.httpStatus);
|
|
1743
2277
|
};
|
|
1744
2278
|
};
|
|
1745
2279
|
|
|
@@ -1747,7 +2281,7 @@ var httpStatusEntryPoint = (opts) => {
|
|
|
1747
2281
|
var logger2 = getLogger("auth.entry-point");
|
|
1748
2282
|
var delegatingEntryPoint = (opts) => {
|
|
1749
2283
|
const defaultEntryPoint = opts.defaultEntryPoint ?? (async ({ response }, _error) => {
|
|
1750
|
-
response.
|
|
2284
|
+
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
|
1751
2285
|
await response.end();
|
|
1752
2286
|
});
|
|
1753
2287
|
return async (exchange, error) => {
|
|
@@ -1785,12 +2319,12 @@ function httpBasic(opts) {
|
|
|
1785
2319
|
const headers2 = exchange.request.headers;
|
|
1786
2320
|
const h = headers2.list("X-Requested-With");
|
|
1787
2321
|
if (h.includes("XMLHttpRequest")) {
|
|
1788
|
-
return
|
|
2322
|
+
return match();
|
|
1789
2323
|
}
|
|
1790
|
-
return
|
|
2324
|
+
return NO_MATCH;
|
|
1791
2325
|
};
|
|
1792
2326
|
const defaultEntryPoint = delegatingEntryPoint({
|
|
1793
|
-
entryPoints: [[xhrMatcher, httpStatusEntryPoint({ httpStatus:
|
|
2327
|
+
entryPoints: [[xhrMatcher, httpStatusEntryPoint({ httpStatus: HttpStatus.UNAUTHORIZED })]],
|
|
1794
2328
|
defaultEntryPoint: httpBasicEntryPoint({})
|
|
1795
2329
|
});
|
|
1796
2330
|
const entryPoint = opts.entryPoint ?? defaultEntryPoint;
|
|
@@ -1833,7 +2367,7 @@ var DEFAULT_URI = "https://tools.ietf.org/html/rfc6750#section-3.1";
|
|
|
1833
2367
|
function invalidToken(message) {
|
|
1834
2368
|
return {
|
|
1835
2369
|
errorCode: BearerTokenErrorCodes.invalid_token,
|
|
1836
|
-
httpStatus:
|
|
2370
|
+
httpStatus: HttpStatus.UNAUTHORIZED,
|
|
1837
2371
|
description: message,
|
|
1838
2372
|
uri: DEFAULT_URI
|
|
1839
2373
|
};
|
|
@@ -1841,7 +2375,7 @@ function invalidToken(message) {
|
|
|
1841
2375
|
function invalidRequest(message) {
|
|
1842
2376
|
return {
|
|
1843
2377
|
errorCode: BearerTokenErrorCodes.invalid_request,
|
|
1844
|
-
httpStatus:
|
|
2378
|
+
httpStatus: HttpStatus.BAD_REQUEST,
|
|
1845
2379
|
description: message,
|
|
1846
2380
|
uri: DEFAULT_URI
|
|
1847
2381
|
};
|
|
@@ -1917,7 +2451,10 @@ async function resolveFromBody(exchange, allow = false) {
|
|
|
1917
2451
|
if (!allow || "application/x-www-form-urlencoded" !== request.headers.one("content-type") || request.method !== "POST") {
|
|
1918
2452
|
return;
|
|
1919
2453
|
}
|
|
1920
|
-
|
|
2454
|
+
const parameters = await exchange.request.formData();
|
|
2455
|
+
if (parameters) {
|
|
2456
|
+
return resolveTokens(parameters);
|
|
2457
|
+
}
|
|
1921
2458
|
}
|
|
1922
2459
|
var token_converter_default = serverBearerTokenAuthenticationConverter;
|
|
1923
2460
|
|
|
@@ -1947,7 +2484,7 @@ function getStatus(authError) {
|
|
|
1947
2484
|
return error.httpStatus;
|
|
1948
2485
|
}
|
|
1949
2486
|
}
|
|
1950
|
-
return
|
|
2487
|
+
return HttpStatus.UNAUTHORIZED;
|
|
1951
2488
|
}
|
|
1952
2489
|
function createParameters(authError, realm) {
|
|
1953
2490
|
const parameters = /* @__PURE__ */ new Map();
|
|
@@ -1976,7 +2513,7 @@ var bearerTokenServerAuthenticationEntryPoint = (opts) => {
|
|
|
1976
2513
|
const wwwAuthenticate = computeWWWAuthenticate(parameters);
|
|
1977
2514
|
const { response } = exchange;
|
|
1978
2515
|
response.headers.set("WWW-Authenticate", wwwAuthenticate);
|
|
1979
|
-
response.
|
|
2516
|
+
response.setStatusCode(status);
|
|
1980
2517
|
await response.end();
|
|
1981
2518
|
};
|
|
1982
2519
|
};
|
|
@@ -2063,18 +2600,17 @@ async function commenceAuthentication(exchange, authentication, entryPoint) {
|
|
|
2063
2600
|
}
|
|
2064
2601
|
await entryPoint(exchange, e);
|
|
2065
2602
|
}
|
|
2066
|
-
function httpStatusAccessDeniedHandler(
|
|
2603
|
+
function httpStatusAccessDeniedHandler(httpStatus) {
|
|
2067
2604
|
return async (exchange, _error) => {
|
|
2068
|
-
exchange.response.
|
|
2069
|
-
exchange.response.
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
exchange.response.
|
|
2073
|
-
await exchange.response.end(bytes);
|
|
2605
|
+
exchange.response.setStatusCode(httpStatus);
|
|
2606
|
+
exchange.response.headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
2607
|
+
const buffer = Buffer.from("Access Denied", "utf-8");
|
|
2608
|
+
exchange.response.headers.set("Content-Length", buffer.length);
|
|
2609
|
+
await exchange.response.body(buffer);
|
|
2074
2610
|
};
|
|
2075
2611
|
}
|
|
2076
2612
|
var errorFilter = (opts) => {
|
|
2077
|
-
const accessDeniedHandler = httpStatusAccessDeniedHandler(
|
|
2613
|
+
const accessDeniedHandler = httpStatusAccessDeniedHandler(HttpStatus.FORBIDDEN);
|
|
2078
2614
|
const authenticationEntryPoint = opts.authenticationEntryPoint ?? httpBasicEntryPoint();
|
|
2079
2615
|
return async (exchange, next) => {
|
|
2080
2616
|
try {
|
|
@@ -2086,17 +2622,19 @@ var errorFilter = (opts) => {
|
|
|
2086
2622
|
await commenceAuthentication(exchange, void 0, authenticationEntryPoint);
|
|
2087
2623
|
} else {
|
|
2088
2624
|
if (!principal.authenticated) {
|
|
2089
|
-
|
|
2625
|
+
await accessDeniedHandler(exchange, error);
|
|
2090
2626
|
}
|
|
2091
2627
|
await commenceAuthentication(exchange, principal, authenticationEntryPoint);
|
|
2092
2628
|
}
|
|
2629
|
+
return;
|
|
2093
2630
|
}
|
|
2631
|
+
throw error;
|
|
2094
2632
|
}
|
|
2095
2633
|
};
|
|
2096
2634
|
};
|
|
2097
2635
|
|
|
2098
2636
|
// src/server/security/delegating-authorization-manager.ts
|
|
2099
|
-
var logger3 = getLogger("auth");
|
|
2637
|
+
var logger3 = getLogger("security.auth");
|
|
2100
2638
|
function delegatingAuthorizationManager(opts) {
|
|
2101
2639
|
const check = async (authentication, exchange) => {
|
|
2102
2640
|
let decision;
|
|
@@ -2139,6 +2677,25 @@ function authorizationFilter(opts) {
|
|
|
2139
2677
|
};
|
|
2140
2678
|
}
|
|
2141
2679
|
|
|
2680
|
+
// src/server/security/exchange-filter.ts
|
|
2681
|
+
var SecurityContextServerWebExchange = class extends ServerWebExchangeDecorator {
|
|
2682
|
+
#context;
|
|
2683
|
+
constructor(exchange, context) {
|
|
2684
|
+
super(exchange);
|
|
2685
|
+
this.#context = context;
|
|
2686
|
+
}
|
|
2687
|
+
async principal() {
|
|
2688
|
+
const context = await this.#context();
|
|
2689
|
+
return context?.authentication;
|
|
2690
|
+
}
|
|
2691
|
+
};
|
|
2692
|
+
var exchangeFilter = (opts) => {
|
|
2693
|
+
const storage = opts.storage;
|
|
2694
|
+
return async (exchange, next) => {
|
|
2695
|
+
await next(new SecurityContextServerWebExchange(exchange, async () => await AsyncStorageSecurityContextHolder.getContext(storage)));
|
|
2696
|
+
};
|
|
2697
|
+
};
|
|
2698
|
+
|
|
2142
2699
|
// src/server/security/config.ts
|
|
2143
2700
|
var filterOrder = {
|
|
2144
2701
|
first: Number.MAX_SAFE_INTEGER,
|
|
@@ -2147,6 +2704,7 @@ var filterOrder = {
|
|
|
2147
2704
|
cors: 3 * 100,
|
|
2148
2705
|
http_basic: 6 * 100,
|
|
2149
2706
|
authentication: 8 * 100,
|
|
2707
|
+
security_context_server_web_exchange: 15 * 100,
|
|
2150
2708
|
error_translation: 18 * 100,
|
|
2151
2709
|
authorization: 19 * 100,
|
|
2152
2710
|
last: Number.MAX_SAFE_INTEGER
|
|
@@ -2233,9 +2791,12 @@ var config_default = (config, context) => {
|
|
|
2233
2791
|
const authenticationConverterMatcher = async (exchange) => {
|
|
2234
2792
|
try {
|
|
2235
2793
|
const a = await authenticationConverter(exchange);
|
|
2236
|
-
|
|
2794
|
+
if (a === void 0) {
|
|
2795
|
+
return NO_MATCH;
|
|
2796
|
+
}
|
|
2797
|
+
return match();
|
|
2237
2798
|
} catch (e) {
|
|
2238
|
-
return
|
|
2799
|
+
return NO_MATCH;
|
|
2239
2800
|
}
|
|
2240
2801
|
};
|
|
2241
2802
|
const entryPoint = token_entry_point_default({});
|
|
@@ -2249,6 +2810,9 @@ var config_default = (config, context) => {
|
|
|
2249
2810
|
filter[filterOrderSymbol] = filterOrder.authentication;
|
|
2250
2811
|
middleware.push(filter);
|
|
2251
2812
|
}
|
|
2813
|
+
const exchangeF = exchangeFilter({ storage: context.storage });
|
|
2814
|
+
middleware.push(exchangeF);
|
|
2815
|
+
exchangeF[filterOrderSymbol] = filterOrder.security_context_server_web_exchange;
|
|
2252
2816
|
if (config.authorize !== void 0) {
|
|
2253
2817
|
const errorFf = errorFilter({ authenticationEntryPoint: this.authenticationEntryPoint });
|
|
2254
2818
|
errorFf[filterOrderSymbol] = filterOrder.error_translation;
|
|
@@ -2342,121 +2906,341 @@ async function httpSecurity(context) {
|
|
|
2342
2906
|
return config_default(config, { storage, corsConfigSource });
|
|
2343
2907
|
}
|
|
2344
2908
|
|
|
2345
|
-
// src/server.ts
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
logger5.warn(`${info2} dropping ws connection request from ${host} on ${path}. max connections exceeded.`);
|
|
2375
|
-
socket.destroy();
|
|
2376
|
-
return;
|
|
2377
|
-
}
|
|
2378
|
-
const origin = request.headers.one("origin");
|
|
2379
|
-
if (!acceptsOrigin(origin, route.originFilters)) {
|
|
2380
|
-
logger5.info(`${info2} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
|
|
2381
|
-
socket.destroy();
|
|
2382
|
-
return;
|
|
2383
|
-
}
|
|
2384
|
-
if (logger5.enabledFor("debug")) {
|
|
2385
|
-
logger5.debug(`${info2} accepted new ws connection request from ${host} on ${path}`);
|
|
2386
|
-
}
|
|
2387
|
-
const upgradeHead = response.unsafeServerResponse.upgradeHead;
|
|
2388
|
-
wss.handleUpgrade(request.unsafeIncomingMessage, socket, upgradeHead, (ws2) => {
|
|
2389
|
-
response.unsafeServerResponse.markHeadersSent();
|
|
2390
|
-
ws2.on("pong", () => ws2["connected"] = true);
|
|
2391
|
-
ws2.on("ping", () => {
|
|
2392
|
-
});
|
|
2393
|
-
wss.emit("connection", ws2, request.unsafeIncomingMessage);
|
|
2394
|
-
});
|
|
2395
|
-
} else {
|
|
2396
|
-
logger5.warn(`${info2} rejected upgrade request from ${host} on ${path}`);
|
|
2397
|
-
socket.destroy();
|
|
2398
|
-
}
|
|
2399
|
-
} else {
|
|
2400
|
-
if (route.default) {
|
|
2401
|
-
await next();
|
|
2402
|
-
return;
|
|
2403
|
-
}
|
|
2404
|
-
if (logger5.enabledFor("debug")) {
|
|
2405
|
-
logger5.debug(`rejecting request for ${path} with method ${request.method} from ${request.socket.remoteAddress}:${request.socket.remotePort} with headers: ${JSON.stringify(request.headers)}`);
|
|
2406
|
-
}
|
|
2407
|
-
response.statusCode = 426;
|
|
2408
|
-
response.headers.set("Upgrade", "websocket").set("Connection", "Upgrade").set("Content-Type", "text/plain");
|
|
2409
|
-
await response.end(`This service [${request.path}] requires use of the websocket protocol.`);
|
|
2410
|
-
}
|
|
2909
|
+
// src/server/handler.ts
|
|
2910
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
|
|
2911
|
+
var HttpHeadResponseDecorator = class extends ServerHttpResponseDecorator {
|
|
2912
|
+
};
|
|
2913
|
+
var HandlerAdapter = class {
|
|
2914
|
+
#logger;
|
|
2915
|
+
#enableLoggingRequestDetails = false;
|
|
2916
|
+
#delegate;
|
|
2917
|
+
#storage;
|
|
2918
|
+
constructor(logger7, delegate) {
|
|
2919
|
+
this.#logger = logger7;
|
|
2920
|
+
this.#delegate = delegate;
|
|
2921
|
+
}
|
|
2922
|
+
createExchange(request, response) {
|
|
2923
|
+
const exchange = new DefaultWebExchange(request, response);
|
|
2924
|
+
return exchange;
|
|
2925
|
+
}
|
|
2926
|
+
set storage(storage) {
|
|
2927
|
+
this.#storage = storage;
|
|
2928
|
+
}
|
|
2929
|
+
set enableLoggingRequestDetails(value) {
|
|
2930
|
+
this.#enableLoggingRequestDetails = value;
|
|
2931
|
+
}
|
|
2932
|
+
formatHeaders(headers2) {
|
|
2933
|
+
let result = "{";
|
|
2934
|
+
for (const key of headers2.keys()) {
|
|
2935
|
+
if (!this.#enableLoggingRequestDetails) {
|
|
2936
|
+
result += "masked, ";
|
|
2937
|
+
break;
|
|
2411
2938
|
} else {
|
|
2412
|
-
|
|
2939
|
+
const value = headers2.get(key);
|
|
2940
|
+
result += `"${key}": "${value}", `;
|
|
2413
2941
|
}
|
|
2414
|
-
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2942
|
+
}
|
|
2943
|
+
if (result.endsWith(", ")) {
|
|
2944
|
+
result = result.slice(0, -2);
|
|
2945
|
+
}
|
|
2946
|
+
result += "}";
|
|
2947
|
+
return result;
|
|
2948
|
+
}
|
|
2949
|
+
formatRequest(request) {
|
|
2950
|
+
const query = request.URL.search;
|
|
2951
|
+
return `HTTP ${request.method} "${request.path}${query}`;
|
|
2952
|
+
}
|
|
2953
|
+
logRequest(exchange) {
|
|
2954
|
+
if (this.#logger.enabledFor("debug")) {
|
|
2955
|
+
const trace = this.#logger.enabledFor("trace");
|
|
2956
|
+
this.#logger.debug(`${exchange.logPrefix}${this.formatRequest(exchange.request)}${trace ? `, headers: ${this.formatHeaders(exchange.request.headers)}` : ""}"`);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
logResponse(exchange) {
|
|
2960
|
+
if (this.#logger.enabledFor("debug")) {
|
|
2961
|
+
const trace = this.#logger.enabledFor("trace");
|
|
2962
|
+
const status = exchange.response.statusCode;
|
|
2963
|
+
this.#logger.debug(`${exchange.logPrefix}Completed ${status ?? "200 OK"}${trace ? `, headers: ${this.formatHeaders(exchange.response.headers)}` : ""}"`);
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
handleUnresolvedError(exchange, error) {
|
|
2967
|
+
const { request, response, logPrefix } = exchange;
|
|
2968
|
+
if (response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR)) {
|
|
2969
|
+
this.#logger.error(`${logPrefix}500 Server Error for ${this.formatRequest(request)}`, error);
|
|
2970
|
+
return;
|
|
2971
|
+
}
|
|
2972
|
+
this.#logger.error(`${logPrefix}Error [${error.message} for ${this.formatRequest(request)}, but already ended (${response.statusCode})`, error);
|
|
2973
|
+
throw error;
|
|
2974
|
+
}
|
|
2975
|
+
async web(exchange) {
|
|
2976
|
+
return await this.#delegate(exchange);
|
|
2977
|
+
}
|
|
2978
|
+
async http(request, response) {
|
|
2979
|
+
const exchange = this.createExchange(request, response);
|
|
2980
|
+
const callback = () => {
|
|
2981
|
+
this.logRequest(exchange);
|
|
2982
|
+
return this.web(exchange).then(() => {
|
|
2983
|
+
this.logResponse(exchange);
|
|
2984
|
+
}).catch((error) => {
|
|
2985
|
+
this.handleUnresolvedError(exchange, error);
|
|
2986
|
+
}).then(async () => {
|
|
2987
|
+
await exchange.response.end();
|
|
2988
|
+
});
|
|
2989
|
+
};
|
|
2990
|
+
await new Promise((resolve, reject) => {
|
|
2991
|
+
if (this.#storage !== void 0) {
|
|
2992
|
+
this.#storage.run({ exchange }, () => {
|
|
2993
|
+
callback().then(() => resolve()).catch((error) => reject(error));
|
|
2994
|
+
});
|
|
2421
2995
|
} else {
|
|
2422
|
-
|
|
2996
|
+
callback().then(() => resolve()).catch((error) => reject(error));
|
|
2423
2997
|
}
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
3000
|
+
};
|
|
3001
|
+
var WebHttpHandlerBuilder = class {
|
|
3002
|
+
#webHandler;
|
|
3003
|
+
#storage = new AsyncLocalStorage3();
|
|
3004
|
+
#handlerDecorator;
|
|
3005
|
+
storage(storage) {
|
|
3006
|
+
this.#storage = storage;
|
|
3007
|
+
return this;
|
|
3008
|
+
}
|
|
3009
|
+
httpHandlerDecorator(decorator) {
|
|
3010
|
+
if (this.#handlerDecorator === void 0) {
|
|
3011
|
+
this.#handlerDecorator = decorator;
|
|
3012
|
+
} else {
|
|
3013
|
+
const previousDecorator = this.#handlerDecorator;
|
|
3014
|
+
this.#handlerDecorator = (handler) => {
|
|
3015
|
+
handler = previousDecorator(handler);
|
|
3016
|
+
return decorator(handler);
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
return this;
|
|
3020
|
+
}
|
|
3021
|
+
constructor(webHandler) {
|
|
3022
|
+
this.#webHandler = webHandler;
|
|
3023
|
+
}
|
|
3024
|
+
build() {
|
|
3025
|
+
const logger7 = getLogger("http");
|
|
3026
|
+
const adapter = new HandlerAdapter(logger7, this.#webHandler);
|
|
3027
|
+
if (this.#storage !== void 0) adapter.storage = this.#storage;
|
|
3028
|
+
adapter.enableLoggingRequestDetails = false;
|
|
3029
|
+
const adapted = async (request, response) => adapter.http(request, response);
|
|
3030
|
+
return this.#handlerDecorator ? this.#handlerDecorator(adapted) : adapted;
|
|
3031
|
+
}
|
|
3032
|
+
};
|
|
3033
|
+
|
|
3034
|
+
// src/server/ws.ts
|
|
3035
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
3036
|
+
|
|
3037
|
+
// src/server/socket.ts
|
|
3038
|
+
function createHandshakeInfo(req, protocol) {
|
|
3039
|
+
const exchange = req?.exchange;
|
|
3040
|
+
const request = exchange?.request ?? new HttpServerRequest(req);
|
|
3041
|
+
const principalPromiseProvider = exchange?.principal;
|
|
3042
|
+
const principal = principalPromiseProvider ? principalPromiseProvider.bind(exchange) : async function principal2() {
|
|
3043
|
+
return void 0;
|
|
3044
|
+
};
|
|
3045
|
+
const url = request.URL;
|
|
3046
|
+
const headers2 = new MapHttpHeaders();
|
|
3047
|
+
for (const key of request.headers.keys()) {
|
|
3048
|
+
headers2.set(key, request.headers.list(key));
|
|
3049
|
+
}
|
|
3050
|
+
const cookies = request.cookies;
|
|
3051
|
+
const logPrefix = exchange?.logPrefix ?? `[${request.id}] `;
|
|
3052
|
+
const remoteAddress = request.remoteAddress;
|
|
3053
|
+
const handshake = {
|
|
3054
|
+
url,
|
|
3055
|
+
headers: headers2,
|
|
3056
|
+
cookies,
|
|
3057
|
+
principal,
|
|
3058
|
+
protocol,
|
|
3059
|
+
remoteAddress,
|
|
3060
|
+
logPrefix
|
|
3061
|
+
};
|
|
3062
|
+
return handshake;
|
|
3063
|
+
}
|
|
3064
|
+
function webSockets(context) {
|
|
3065
|
+
const sockets = async (exchange, next) => {
|
|
3066
|
+
const request = exchange.request;
|
|
3067
|
+
const path = request.path ?? "/";
|
|
3068
|
+
const routes = context.sockets;
|
|
3069
|
+
const route = routes.get(path) ?? Array.from(routes.values()).find((route2) => {
|
|
3070
|
+
if (path === "/" && route2.default === true) {
|
|
3071
|
+
return true;
|
|
3072
|
+
}
|
|
3073
|
+
});
|
|
3074
|
+
if (route !== void 0) {
|
|
3075
|
+
const { request: request2, response } = exchange;
|
|
3076
|
+
const upgradeMatchResult = await upgradeMatcher(exchange);
|
|
3077
|
+
if ((request2.method === "GET" || request2.method === "CONNECT") && upgradeMatchResult.match) {
|
|
3078
|
+
if (route.upgradeStrategy !== void 0) {
|
|
3079
|
+
route.upgradeStrategy(exchange);
|
|
3080
|
+
return;
|
|
3081
|
+
} else {
|
|
3082
|
+
throw new Error(`No upgrade strategy defined for route on ${path}`);
|
|
3083
|
+
}
|
|
2429
3084
|
} else {
|
|
2430
|
-
|
|
3085
|
+
if (route.default) {
|
|
3086
|
+
await next();
|
|
3087
|
+
return;
|
|
3088
|
+
}
|
|
3089
|
+
response.setStatusCode(HttpStatus.UPGRADE_REQUIRED);
|
|
3090
|
+
response.headers.set("Upgrade", "websocket").set("Connection", "Upgrade").set("Content-Type", "text/plain");
|
|
3091
|
+
const buffer = Buffer.from(`This service [${request2.path}] requires use of the websocket protocol.`, "utf-8");
|
|
3092
|
+
await response.body(buffer);
|
|
2431
3093
|
}
|
|
2432
|
-
}
|
|
2433
|
-
|
|
2434
|
-
async ({ response }, _next) => {
|
|
2435
|
-
response.statusCode = 404;
|
|
2436
|
-
await response.end(http2.STATUS_CODES[404]);
|
|
3094
|
+
} else {
|
|
3095
|
+
await next();
|
|
2437
3096
|
}
|
|
2438
|
-
|
|
2439
|
-
return
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
3097
|
+
};
|
|
3098
|
+
return [sockets];
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
// src/server/ws.ts
|
|
3102
|
+
var ExtendedWebSocket = class extends WebSocket {
|
|
3103
|
+
constructor(first, second, options) {
|
|
3104
|
+
super(null, void 0, options);
|
|
3105
|
+
}
|
|
3106
|
+
connected;
|
|
3107
|
+
};
|
|
3108
|
+
var logger5 = getLogger("ws");
|
|
3109
|
+
function upgradeStrategy(path, route, wss, onSocketError) {
|
|
3110
|
+
return (exchange) => {
|
|
3111
|
+
const { logPrefix, request } = exchange;
|
|
3112
|
+
const req = ServerHttpRequestDecorator.getNativeRequest(request);
|
|
3113
|
+
req.exchange = exchange;
|
|
3114
|
+
const { socket, upgradeHead } = req;
|
|
3115
|
+
const host = request.host;
|
|
3116
|
+
socket.removeListener("error", onSocketError);
|
|
3117
|
+
if (route.maxConnections !== void 0 && wss.clients?.size >= route.maxConnections) {
|
|
3118
|
+
logger5.warn(`${logPrefix}dropping ws connection request on ${host}${path}. max connections exceeded.`);
|
|
3119
|
+
socket.destroy();
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
const origin = request.headers.one("origin");
|
|
3123
|
+
if (!acceptsOrigin(origin, route.originFilters)) {
|
|
3124
|
+
if (logger5.enabledFor("info")) {
|
|
3125
|
+
logger5.info(`${logPrefix}dropping ws connection request on ${host}${path}. origin ${origin ?? "<missing>"}`);
|
|
2449
3126
|
}
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
3127
|
+
socket.destroy();
|
|
3128
|
+
return;
|
|
3129
|
+
}
|
|
3130
|
+
if (logger5.enabledFor("debug")) {
|
|
3131
|
+
logger5.debug(`${logPrefix}accepted new ws connection request on ${host}${path}`);
|
|
3132
|
+
}
|
|
3133
|
+
wss.handleUpgrade(req, socket, upgradeHead, (client, req2) => {
|
|
3134
|
+
wss.emit("connection", client, req2);
|
|
3135
|
+
});
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
function applyHandshakeHeaders(headers2, response) {
|
|
3139
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3140
|
+
headers2.forEach((header, index) => {
|
|
3141
|
+
if (index === 0 && header.startsWith("HTTP/1.1 101 ")) {
|
|
3142
|
+
response.setStatusCode(HttpStatus.SWITCHING_PROTOCOLS);
|
|
3143
|
+
return;
|
|
3144
|
+
}
|
|
3145
|
+
const [name, value] = header.split(": ");
|
|
3146
|
+
if (response.headers.has(name)) {
|
|
3147
|
+
headers2[index] = `${name}: ${response.headers.one(name)}`;
|
|
3148
|
+
} else {
|
|
3149
|
+
response.headers.set(name, value);
|
|
3150
|
+
}
|
|
3151
|
+
seen.add(name.toLowerCase());
|
|
3152
|
+
});
|
|
3153
|
+
const nativeResponse = ServerHttpResponseDecorator.getNativeResponse(response);
|
|
3154
|
+
for (const name of nativeResponse.getRawHeaderNames()) {
|
|
3155
|
+
const nameLowerCase = name.toLowerCase();
|
|
3156
|
+
if (!seen.has(nameLowerCase)) {
|
|
3157
|
+
const value = response.headers.get(nameLowerCase);
|
|
3158
|
+
if (value !== void 0) {
|
|
3159
|
+
headers2.push(`${name}: ${value}`);
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
nativeResponse.markHeadersSent();
|
|
3164
|
+
}
|
|
3165
|
+
async function initRoute(path, route, endpoint, storage, onSocketError) {
|
|
3166
|
+
try {
|
|
3167
|
+
logger5.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
|
|
3168
|
+
const wss = new WebSocketServer({
|
|
3169
|
+
noServer: true,
|
|
3170
|
+
WebSocket: ExtendedWebSocket,
|
|
3171
|
+
autoPong: false
|
|
3172
|
+
});
|
|
3173
|
+
const handler = await route.factory({ endpoint, storage });
|
|
3174
|
+
wss.on("error", (err) => {
|
|
3175
|
+
logger5.error(`error starting the ws server for [${path}]`, err);
|
|
3176
|
+
}).on("listening", () => {
|
|
3177
|
+
logger5.info(`ws server for [${path}] is listening`);
|
|
3178
|
+
}).on("headers", (headers2, request) => {
|
|
3179
|
+
if (request.exchange !== void 0) {
|
|
3180
|
+
const { response } = request.exchange;
|
|
3181
|
+
applyHandshakeHeaders(headers2, response);
|
|
2458
3182
|
}
|
|
3183
|
+
}).on("connection", (socket, request) => {
|
|
3184
|
+
const handshake = createHandshakeInfo(request, socket.protocol);
|
|
3185
|
+
socket.on("pong", () => socket.connected = true);
|
|
3186
|
+
socket.on("ping", () => {
|
|
3187
|
+
});
|
|
3188
|
+
handler({ socket, handshake });
|
|
2459
3189
|
});
|
|
3190
|
+
const pingInterval = route.ping;
|
|
3191
|
+
if (pingInterval) {
|
|
3192
|
+
const pingIntervalId = setInterval(() => {
|
|
3193
|
+
for (const client of wss.clients) {
|
|
3194
|
+
if (client.connected === false) {
|
|
3195
|
+
client.terminate();
|
|
3196
|
+
}
|
|
3197
|
+
client.connected = false;
|
|
3198
|
+
client.ping();
|
|
3199
|
+
}
|
|
3200
|
+
}, pingInterval);
|
|
3201
|
+
wss.on("close", () => {
|
|
3202
|
+
clearInterval(pingIntervalId);
|
|
3203
|
+
});
|
|
3204
|
+
}
|
|
3205
|
+
route.upgradeStrategy = upgradeStrategy(path, route, wss, onSocketError);
|
|
3206
|
+
route.close = async () => {
|
|
3207
|
+
await handler.close?.call(handler);
|
|
3208
|
+
logger5.info(`stopping ws server for [${path}]. clients: ${wss.clients?.size ?? 0}`);
|
|
3209
|
+
wss.clients?.forEach((client) => {
|
|
3210
|
+
client.terminate();
|
|
3211
|
+
});
|
|
3212
|
+
wss.close();
|
|
3213
|
+
};
|
|
3214
|
+
} catch (e) {
|
|
3215
|
+
logger5.warn(`failed to init route ${path}`, e);
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
// src/server.ts
|
|
3220
|
+
var logger6 = getLogger("app");
|
|
3221
|
+
function secureContextOptions(ssl) {
|
|
3222
|
+
const options = {};
|
|
3223
|
+
if (ssl.key) options.key = readFileSync(ssl.key);
|
|
3224
|
+
if (ssl.cert) options.cert = readFileSync(ssl.cert);
|
|
3225
|
+
if (ssl.ca) options.ca = readFileSync(ssl.ca);
|
|
3226
|
+
return options;
|
|
3227
|
+
}
|
|
3228
|
+
async function createListener(builder, onSocketError) {
|
|
3229
|
+
const httpHandler = builder.build();
|
|
3230
|
+
return async (req, resOrUpgradeHead) => {
|
|
3231
|
+
req.socket.addListener("error", onSocketError);
|
|
3232
|
+
let res;
|
|
3233
|
+
if (resOrUpgradeHead instanceof ExtendedHttpServerResponse) {
|
|
3234
|
+
res = resOrUpgradeHead;
|
|
3235
|
+
} else {
|
|
3236
|
+
req.upgradeHead = resOrUpgradeHead;
|
|
3237
|
+
res = new ExtendedHttpServerResponse(req);
|
|
3238
|
+
res.assignSocket(req.socket);
|
|
3239
|
+
}
|
|
3240
|
+
const request = new HttpServerRequest(req);
|
|
3241
|
+
const response = new HttpServerResponse(res);
|
|
3242
|
+
const decoratedResponse = request.method === "HEAD" ? new HttpHeadResponseDecorator(response) : response;
|
|
3243
|
+
await httpHandler(request, decoratedResponse);
|
|
2460
3244
|
};
|
|
2461
3245
|
}
|
|
2462
3246
|
function promisify(fn) {
|
|
@@ -2481,8 +3265,44 @@ function memoryMonitor(config) {
|
|
|
2481
3265
|
});
|
|
2482
3266
|
}
|
|
2483
3267
|
}
|
|
2484
|
-
function
|
|
2485
|
-
|
|
3268
|
+
async function initBuilder(context) {
|
|
3269
|
+
const storage = context.storage;
|
|
3270
|
+
const security = await httpSecurity(context);
|
|
3271
|
+
const sockets = webSockets(context);
|
|
3272
|
+
const handler = compose(
|
|
3273
|
+
server_header_default(context.serverHeader),
|
|
3274
|
+
...security,
|
|
3275
|
+
...sockets,
|
|
3276
|
+
...context.middleware,
|
|
3277
|
+
// health check
|
|
3278
|
+
async ({ request, response }, next) => {
|
|
3279
|
+
if (request.method === "GET" && request.path === "/health") {
|
|
3280
|
+
response.setStatusCode(HttpStatus.OK);
|
|
3281
|
+
const buffer = Buffer.from("UP", "utf-8");
|
|
3282
|
+
response.headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
3283
|
+
await response.body(buffer);
|
|
3284
|
+
} else {
|
|
3285
|
+
await next();
|
|
3286
|
+
}
|
|
3287
|
+
},
|
|
3288
|
+
// home page
|
|
3289
|
+
async ({ request, response }, next) => {
|
|
3290
|
+
if (request.method === "GET" && request.path === "/") {
|
|
3291
|
+
response.setStatusCode(HttpStatus.OK);
|
|
3292
|
+
const buffer = Buffer.from("io.Gateway Server", "utf-8");
|
|
3293
|
+
response.headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
3294
|
+
await response.body(buffer);
|
|
3295
|
+
} else {
|
|
3296
|
+
await next();
|
|
3297
|
+
}
|
|
3298
|
+
},
|
|
3299
|
+
// not found
|
|
3300
|
+
async ({ response }, _next) => {
|
|
3301
|
+
response.setStatusCode(HttpStatus.NOT_FOUND);
|
|
3302
|
+
await response.end();
|
|
3303
|
+
}
|
|
3304
|
+
);
|
|
3305
|
+
return new WebHttpHandlerBuilder(handler).storage(storage);
|
|
2486
3306
|
}
|
|
2487
3307
|
var Factory = async (options) => {
|
|
2488
3308
|
const ssl = options.ssl;
|
|
@@ -2494,7 +3314,7 @@ var Factory = async (options) => {
|
|
|
2494
3314
|
cors: [],
|
|
2495
3315
|
authConfig: options.auth,
|
|
2496
3316
|
authorize: [],
|
|
2497
|
-
storage: new
|
|
3317
|
+
storage: new AsyncLocalStorage4(),
|
|
2498
3318
|
sockets: /* @__PURE__ */ new Map()
|
|
2499
3319
|
};
|
|
2500
3320
|
const gw = IOGateway6.Factory({ ...options.gateway });
|
|
@@ -2509,8 +3329,9 @@ var Factory = async (options) => {
|
|
|
2509
3329
|
}
|
|
2510
3330
|
const ports = portRange(options.port ?? 0);
|
|
2511
3331
|
const host = options.host;
|
|
2512
|
-
const onSocketError = (err) =>
|
|
2513
|
-
const
|
|
3332
|
+
const onSocketError = (err) => logger6.error(`socket error: ${err}`, err);
|
|
3333
|
+
const builder = await initBuilder(context);
|
|
3334
|
+
const listener = await createListener(builder, onSocketError);
|
|
2514
3335
|
const serverP = new Promise((resolve, reject) => {
|
|
2515
3336
|
const server2 = createServer({
|
|
2516
3337
|
IncomingMessage: ExtendedHttpIncomingMessage,
|
|
@@ -2518,95 +3339,45 @@ var Factory = async (options) => {
|
|
|
2518
3339
|
}, listener);
|
|
2519
3340
|
server2.on("error", (e) => {
|
|
2520
3341
|
if (e["code"] === "EADDRINUSE") {
|
|
2521
|
-
|
|
3342
|
+
logger6.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
|
|
2522
3343
|
const { value: port } = ports.next();
|
|
2523
3344
|
if (port) {
|
|
2524
|
-
|
|
3345
|
+
logger6.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
|
|
2525
3346
|
server2.close();
|
|
2526
3347
|
server2.listen(port, host);
|
|
2527
3348
|
} else {
|
|
2528
|
-
|
|
3349
|
+
logger6.warn(`all configured port(s) ${options.port} are in use. closing...`);
|
|
2529
3350
|
server2.close();
|
|
2530
3351
|
reject(e);
|
|
2531
3352
|
}
|
|
2532
3353
|
} else {
|
|
2533
|
-
|
|
3354
|
+
logger6.error(`server error: ${e.message}`, e);
|
|
2534
3355
|
reject(e);
|
|
2535
3356
|
}
|
|
2536
3357
|
});
|
|
2537
3358
|
server2.on("listening", async () => {
|
|
2538
3359
|
const info2 = server2.address();
|
|
2539
3360
|
for (const [path, route] of context.sockets) {
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
const wss = new ws.WebSocketServer({ noServer: true });
|
|
2543
|
-
const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info2.port}${path}`;
|
|
2544
|
-
const handler = await route.factory({ endpoint });
|
|
2545
|
-
wss.on("error", (err) => {
|
|
2546
|
-
logger5.error(`error starting the ws server for [${path}]`, err);
|
|
2547
|
-
}).on("listening", () => {
|
|
2548
|
-
logger5.info(`ws server for [${path}] is listening`);
|
|
2549
|
-
}).on("connection", (socket, req) => {
|
|
2550
|
-
const { request, principal } = req.exchange;
|
|
2551
|
-
const url = request.URL;
|
|
2552
|
-
const headers2 = new MapHttpHeaders();
|
|
2553
|
-
for (const key of request.headers.keys()) {
|
|
2554
|
-
headers2.set(key, request.headers.get(key));
|
|
2555
|
-
}
|
|
2556
|
-
const cookies = request.cookies;
|
|
2557
|
-
const handshake = {
|
|
2558
|
-
url,
|
|
2559
|
-
headers: headers2,
|
|
2560
|
-
cookies,
|
|
2561
|
-
principal,
|
|
2562
|
-
protocol: socket.protocol,
|
|
2563
|
-
remoteAddress: request.socket.remoteAddress,
|
|
2564
|
-
remoteFamily: request.socket.remoteFamily,
|
|
2565
|
-
remotePort: request.socket.remotePort,
|
|
2566
|
-
logPrefix: socketKey(request.socket)
|
|
2567
|
-
};
|
|
2568
|
-
handler(socket, handshake);
|
|
2569
|
-
});
|
|
2570
|
-
const pingInterval = route.ping;
|
|
2571
|
-
if (pingInterval) {
|
|
2572
|
-
const pingIntervalId = setInterval(() => {
|
|
2573
|
-
for (const client of wss.clients) {
|
|
2574
|
-
if (client["connected"] === false) {
|
|
2575
|
-
client.terminate();
|
|
2576
|
-
}
|
|
2577
|
-
client["connected"] = false;
|
|
2578
|
-
client.ping();
|
|
2579
|
-
}
|
|
2580
|
-
}, pingInterval);
|
|
2581
|
-
wss.on("close", () => {
|
|
2582
|
-
clearInterval(pingIntervalId);
|
|
2583
|
-
});
|
|
2584
|
-
}
|
|
2585
|
-
route.wss = wss;
|
|
2586
|
-
route.close = handler.close?.bind(handler);
|
|
2587
|
-
} catch (e) {
|
|
2588
|
-
logger5.warn(`failed to init route ${path}`, e);
|
|
2589
|
-
}
|
|
3361
|
+
const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info2.port}${path}`;
|
|
3362
|
+
await initRoute(path, route, endpoint, context.storage, onSocketError);
|
|
2590
3363
|
}
|
|
2591
|
-
|
|
3364
|
+
logger6.info(`http server listening on ${info2.address}:${info2.port}`);
|
|
2592
3365
|
resolve(server2);
|
|
2593
3366
|
});
|
|
2594
|
-
server2.on("upgrade", (req,
|
|
2595
|
-
socket.on("error", onSocketError);
|
|
3367
|
+
server2.on("upgrade", (req, _socket, head) => {
|
|
2596
3368
|
try {
|
|
2597
|
-
|
|
2598
|
-
listener(req, res);
|
|
3369
|
+
listener(req, head);
|
|
2599
3370
|
} catch (err) {
|
|
2600
|
-
|
|
3371
|
+
logger6.error(`upgrade error: ${err}`, err);
|
|
2601
3372
|
}
|
|
2602
3373
|
}).on("close", async () => {
|
|
2603
|
-
|
|
3374
|
+
logger6.info(`http server closed.`);
|
|
2604
3375
|
});
|
|
2605
3376
|
try {
|
|
2606
3377
|
const { value: port } = ports.next();
|
|
2607
3378
|
server2.listen(port, host);
|
|
2608
3379
|
} catch (e) {
|
|
2609
|
-
|
|
3380
|
+
logger6.error(`error starting web socket server`, e);
|
|
2610
3381
|
reject(e instanceof Error ? e : new Error(`listen failed: ${e}`));
|
|
2611
3382
|
}
|
|
2612
3383
|
});
|
|
@@ -2616,16 +3387,11 @@ var Factory = async (options) => {
|
|
|
2616
3387
|
async close() {
|
|
2617
3388
|
for (const [path, route] of context.sockets) {
|
|
2618
3389
|
try {
|
|
2619
|
-
if (route.close) {
|
|
3390
|
+
if (route.close !== void 0) {
|
|
2620
3391
|
await route.close();
|
|
2621
3392
|
}
|
|
2622
|
-
logger5.info(`stopping ws server for [${path}]. clients: ${route.wss?.clients?.size ?? 0}`);
|
|
2623
|
-
route.wss?.clients?.forEach((client) => {
|
|
2624
|
-
client.terminate();
|
|
2625
|
-
});
|
|
2626
|
-
route.wss?.close();
|
|
2627
3393
|
} catch (e) {
|
|
2628
|
-
|
|
3394
|
+
logger6.warn(`error closing route ${path}`, e);
|
|
2629
3395
|
}
|
|
2630
3396
|
}
|
|
2631
3397
|
await promisify((cb) => {
|