@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.cjs
CHANGED
|
@@ -40,11 +40,10 @@ var server_exports = {};
|
|
|
40
40
|
__export(server_exports, {
|
|
41
41
|
Factory: () => Factory
|
|
42
42
|
});
|
|
43
|
-
var ws = __toESM(require("ws"), 1);
|
|
44
43
|
var import_node_http2 = __toESM(require("node:http"), 1);
|
|
45
44
|
var import_node_https = __toESM(require("node:https"), 1);
|
|
46
45
|
var import_node_fs = require("node:fs");
|
|
47
|
-
var
|
|
46
|
+
var import_node_async_hooks4 = require("node:async_hooks");
|
|
48
47
|
var import_gateway6 = require("@interopio/gateway");
|
|
49
48
|
|
|
50
49
|
// src/logger.ts
|
|
@@ -52,13 +51,41 @@ var import_gateway = require("@interopio/gateway");
|
|
|
52
51
|
function getLogger(name) {
|
|
53
52
|
return import_gateway.IOGateway.Logging.getLogger(`gateway.server.${name}`);
|
|
54
53
|
}
|
|
54
|
+
function regexAwareReplacer(_key, value) {
|
|
55
|
+
return value instanceof RegExp ? value.toString() : value;
|
|
56
|
+
}
|
|
55
57
|
|
|
56
58
|
// src/gateway/ws/core.ts
|
|
57
59
|
var import_gateway2 = require("@interopio/gateway");
|
|
60
|
+
var import_node_async_hooks = require("node:async_hooks");
|
|
58
61
|
var GatewayEncoders = import_gateway2.IOGateway.Encoding;
|
|
59
62
|
var log = getLogger("ws");
|
|
60
63
|
var codec = GatewayEncoders.json();
|
|
61
|
-
function
|
|
64
|
+
function principalName(authentication) {
|
|
65
|
+
let name;
|
|
66
|
+
if (authentication.authenticated) {
|
|
67
|
+
name = authentication.name;
|
|
68
|
+
if (name === void 0) {
|
|
69
|
+
if (authentication["principal"] !== void 0) {
|
|
70
|
+
const principal = authentication["principal"];
|
|
71
|
+
if (typeof principal === "object") {
|
|
72
|
+
name = principal.name;
|
|
73
|
+
}
|
|
74
|
+
if (name === void 0) {
|
|
75
|
+
if (principal === void 0) {
|
|
76
|
+
name = "";
|
|
77
|
+
} else {
|
|
78
|
+
name = String(principal);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return name;
|
|
85
|
+
}
|
|
86
|
+
function initClient(socket, authenticationPromise, remoteAddress) {
|
|
87
|
+
const key = `${remoteAddress?.address}:${remoteAddress?.port}`;
|
|
88
|
+
const host = remoteAddress?.address ?? "<unknown>";
|
|
62
89
|
const opts = {
|
|
63
90
|
key,
|
|
64
91
|
host,
|
|
@@ -66,7 +93,7 @@ function initClient(socket, authenticationPromise, key, host) {
|
|
|
66
93
|
onAuthenticate: async () => {
|
|
67
94
|
const authentication = await authenticationPromise();
|
|
68
95
|
if (authentication?.authenticated) {
|
|
69
|
-
return { type: "success", user: authentication
|
|
96
|
+
return { type: "success", user: principalName(authentication) };
|
|
70
97
|
}
|
|
71
98
|
throw new Error(`no valid client authentication ${key}`);
|
|
72
99
|
},
|
|
@@ -99,30 +126,34 @@ function initClient(socket, authenticationPromise, key, host) {
|
|
|
99
126
|
log.warn(`${key} failed to create client`, err);
|
|
100
127
|
}
|
|
101
128
|
}
|
|
102
|
-
async function create(
|
|
103
|
-
log.info(`starting gateway on ${
|
|
104
|
-
await this.start(
|
|
105
|
-
return async (socket, handshake) => {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const client = initClient.call(this, socket, handshake.principal, key, host);
|
|
129
|
+
async function create(environment) {
|
|
130
|
+
log.info(`starting gateway on ${environment.endpoint}`);
|
|
131
|
+
await this.start(environment);
|
|
132
|
+
return async ({ socket, handshake }) => {
|
|
133
|
+
const { logPrefix, remoteAddress, principal: principalPromise } = handshake;
|
|
134
|
+
log.info(`${logPrefix}connected on gw using ${remoteAddress?.address}`);
|
|
135
|
+
const client = initClient.call(this, socket, principalPromise, remoteAddress);
|
|
110
136
|
if (!client) {
|
|
111
|
-
log.error(`${
|
|
137
|
+
log.error(`${logPrefix}gw client init failed`);
|
|
112
138
|
socket.terminate();
|
|
113
139
|
return;
|
|
114
140
|
}
|
|
115
141
|
socket.on("error", (err) => {
|
|
116
|
-
log.error(`${
|
|
142
|
+
log.error(`${logPrefix}websocket error: ${err}`, err);
|
|
117
143
|
});
|
|
144
|
+
const contextFn = environment.storage !== void 0 ? import_node_async_hooks.AsyncLocalStorage.snapshot() : void 0;
|
|
118
145
|
socket.on("message", (data, _isBinary) => {
|
|
119
146
|
if (Array.isArray(data)) {
|
|
120
147
|
data = Buffer.concat(data);
|
|
121
148
|
}
|
|
122
|
-
|
|
149
|
+
if (contextFn !== void 0) {
|
|
150
|
+
contextFn(() => client.send(data));
|
|
151
|
+
} else {
|
|
152
|
+
client.send(data);
|
|
153
|
+
}
|
|
123
154
|
});
|
|
124
155
|
socket.on("close", (code) => {
|
|
125
|
-
log.info(`${
|
|
156
|
+
log.info(`${logPrefix}disconnected from gw. code: ${code}`);
|
|
126
157
|
client.close();
|
|
127
158
|
});
|
|
128
159
|
};
|
|
@@ -141,61 +172,50 @@ function compose(...middleware) {
|
|
|
141
172
|
}
|
|
142
173
|
}
|
|
143
174
|
return async function(ctx, next) {
|
|
144
|
-
const dispatch = async (i) => {
|
|
175
|
+
const dispatch = async (i, dispatchedCtx) => {
|
|
145
176
|
const fn = i === fns.length ? next : fns[i];
|
|
146
177
|
if (fn === void 0) {
|
|
147
178
|
return;
|
|
148
179
|
}
|
|
149
180
|
let nextCalled = false;
|
|
150
181
|
let nextResolved = false;
|
|
151
|
-
const nextFn = async () => {
|
|
182
|
+
const nextFn = async (nextCtx) => {
|
|
152
183
|
if (nextCalled) {
|
|
153
184
|
throw new Error("next() called multiple times");
|
|
154
185
|
}
|
|
155
186
|
nextCalled = true;
|
|
156
187
|
try {
|
|
157
|
-
return await dispatch(i + 1);
|
|
188
|
+
return await dispatch(i + 1, nextCtx ?? dispatchedCtx);
|
|
158
189
|
} finally {
|
|
159
190
|
nextResolved = true;
|
|
160
191
|
}
|
|
161
192
|
};
|
|
162
|
-
const result = await fn(
|
|
193
|
+
const result = await fn(dispatchedCtx, nextFn);
|
|
163
194
|
if (nextCalled && !nextResolved) {
|
|
164
195
|
throw new Error("middleware resolved before downstream.\n You are probably missing an await or return statement in your middleware function.");
|
|
165
196
|
}
|
|
166
197
|
return result;
|
|
167
198
|
};
|
|
168
|
-
return dispatch(0);
|
|
199
|
+
return dispatch(0, ctx);
|
|
169
200
|
};
|
|
170
201
|
}
|
|
171
202
|
|
|
172
|
-
// src/
|
|
173
|
-
var import_node_http = __toESM(require("node:http"), 1);
|
|
203
|
+
// src/http/exchange.ts
|
|
174
204
|
var import_tough_cookie = require("tough-cookie");
|
|
175
|
-
function
|
|
176
|
-
let
|
|
177
|
-
if (Array.isArray(proto)) {
|
|
178
|
-
proto = proto[0];
|
|
179
|
-
}
|
|
180
|
-
if (proto !== void 0) {
|
|
181
|
-
return proto.split(",", 1)[0].trim();
|
|
182
|
-
}
|
|
183
|
-
return defaultProtocol;
|
|
184
|
-
}
|
|
185
|
-
function requestToHost(request, defaultHost) {
|
|
186
|
-
let host = request.headers.get("x-forwarded-for");
|
|
205
|
+
function parseHost(headers2, defaultHost) {
|
|
206
|
+
let host = headers2.get("x-forwarded-for");
|
|
187
207
|
if (host === void 0) {
|
|
188
|
-
host =
|
|
208
|
+
host = headers2.get("x-forwarded-host");
|
|
189
209
|
if (Array.isArray(host)) {
|
|
190
210
|
host = host[0];
|
|
191
211
|
}
|
|
192
212
|
if (host) {
|
|
193
|
-
const port =
|
|
213
|
+
const port = headers2.one("x-forwarded-port");
|
|
194
214
|
if (port) {
|
|
195
215
|
host = `${host}:${port}`;
|
|
196
216
|
}
|
|
197
217
|
}
|
|
198
|
-
host ??=
|
|
218
|
+
host ??= headers2.one("host");
|
|
199
219
|
}
|
|
200
220
|
if (Array.isArray(host)) {
|
|
201
221
|
host = host[0];
|
|
@@ -205,15 +225,292 @@ function requestToHost(request, defaultHost) {
|
|
|
205
225
|
}
|
|
206
226
|
return defaultHost;
|
|
207
227
|
}
|
|
208
|
-
function
|
|
209
|
-
|
|
210
|
-
|
|
228
|
+
function parseProtocol(headers2, defaultProtocol) {
|
|
229
|
+
let proto = headers2.get("x-forwarded-proto");
|
|
230
|
+
if (Array.isArray(proto)) {
|
|
231
|
+
proto = proto[0];
|
|
232
|
+
}
|
|
233
|
+
if (proto !== void 0) {
|
|
234
|
+
return proto.split(",", 1)[0].trim();
|
|
235
|
+
}
|
|
236
|
+
return defaultProtocol;
|
|
237
|
+
}
|
|
238
|
+
var AbstractHttpMessage = class {
|
|
239
|
+
#headers;
|
|
240
|
+
constructor(headers2) {
|
|
241
|
+
this.#headers = headers2;
|
|
242
|
+
}
|
|
243
|
+
get headers() {
|
|
244
|
+
return this.#headers;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
var AbstractHttpRequest = class _AbstractHttpRequest extends AbstractHttpMessage {
|
|
248
|
+
static logIdCounter = 0;
|
|
249
|
+
#id;
|
|
250
|
+
get id() {
|
|
251
|
+
if (this.#id === void 0) {
|
|
252
|
+
this.#id = `${this.initId()}-${++_AbstractHttpRequest.logIdCounter}`;
|
|
253
|
+
}
|
|
254
|
+
return this.#id;
|
|
255
|
+
}
|
|
256
|
+
initId() {
|
|
257
|
+
return "request";
|
|
258
|
+
}
|
|
259
|
+
get cookies() {
|
|
260
|
+
return parseCookies(this.headers);
|
|
261
|
+
}
|
|
262
|
+
parseHost(defaultHost) {
|
|
263
|
+
return parseHost(this.headers, defaultHost);
|
|
264
|
+
}
|
|
265
|
+
parseProtocol(defaultProtocol) {
|
|
266
|
+
return parseProtocol(this.headers, defaultProtocol);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
var AbstractHttpResponse = class extends AbstractHttpMessage {
|
|
270
|
+
get cookies() {
|
|
271
|
+
return parseResponseCookies(this.headers);
|
|
272
|
+
}
|
|
273
|
+
setCookieValue(responseCookie) {
|
|
274
|
+
const cookie = new import_tough_cookie.Cookie({
|
|
275
|
+
key: responseCookie.name,
|
|
276
|
+
value: responseCookie.value,
|
|
277
|
+
maxAge: responseCookie.maxAge,
|
|
278
|
+
domain: responseCookie.domain,
|
|
279
|
+
path: responseCookie.path,
|
|
280
|
+
secure: responseCookie.secure,
|
|
281
|
+
httpOnly: responseCookie.httpOnly,
|
|
282
|
+
sameSite: responseCookie.sameSite
|
|
283
|
+
});
|
|
284
|
+
return cookie.toString();
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
function parseHeader(value) {
|
|
288
|
+
const list = [];
|
|
289
|
+
{
|
|
290
|
+
let start2 = 0;
|
|
291
|
+
let end = 0;
|
|
292
|
+
for (let i = 0; i < value.length; i++) {
|
|
293
|
+
switch (value.charCodeAt(i)) {
|
|
294
|
+
case 32:
|
|
295
|
+
if (start2 === end) {
|
|
296
|
+
start2 = end = i + 1;
|
|
297
|
+
}
|
|
298
|
+
break;
|
|
299
|
+
case 44:
|
|
300
|
+
list.push(value.slice(start2, end));
|
|
301
|
+
start2 = end = i + 1;
|
|
302
|
+
break;
|
|
303
|
+
default:
|
|
304
|
+
end = end + 1;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
list.push(value.slice(start2, end));
|
|
309
|
+
}
|
|
310
|
+
return list;
|
|
311
|
+
}
|
|
312
|
+
function toList(values) {
|
|
313
|
+
if (typeof values === "string") {
|
|
314
|
+
values = [values];
|
|
315
|
+
}
|
|
316
|
+
if (typeof values === "number") {
|
|
317
|
+
values = [String(values)];
|
|
318
|
+
}
|
|
319
|
+
const list = [];
|
|
320
|
+
if (values) {
|
|
321
|
+
for (const value of values) {
|
|
322
|
+
if (value) {
|
|
323
|
+
list.push(...parseHeader(value));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return list;
|
|
328
|
+
}
|
|
329
|
+
function parseCookies(headers2) {
|
|
330
|
+
return headers2.list("cookie").map((s) => s.split(";").map((cs) => import_tough_cookie.Cookie.parse(cs))).flat(1).filter((tc) => tc !== void 0).map((tc) => {
|
|
331
|
+
const result = Object.freeze({ name: tc.key, value: tc.value });
|
|
211
332
|
return result;
|
|
212
333
|
});
|
|
213
334
|
}
|
|
335
|
+
function parseResponseCookies(headers2) {
|
|
336
|
+
return headers2.list("set-cookie").map((cookie) => {
|
|
337
|
+
const parsed = import_tough_cookie.Cookie.parse(cookie);
|
|
338
|
+
if (parsed) {
|
|
339
|
+
const result = { name: parsed.key, value: parsed.value, maxAge: Number(parsed.maxAge ?? -1) };
|
|
340
|
+
if (parsed.httpOnly) result.httpOnly = true;
|
|
341
|
+
if (parsed.domain) result.domain = parsed.domain;
|
|
342
|
+
if (parsed.path) result.path = parsed.path;
|
|
343
|
+
if (parsed.secure) result.secure = true;
|
|
344
|
+
if (parsed.httpOnly) result.httpOnly = true;
|
|
345
|
+
if (parsed.sameSite) result.sameSite = parsed.sameSite;
|
|
346
|
+
return Object.freeze(result);
|
|
347
|
+
}
|
|
348
|
+
}).filter((cookie) => cookie !== void 0);
|
|
349
|
+
}
|
|
350
|
+
var AbstractHttpHeaders = class {
|
|
351
|
+
constructor() {
|
|
352
|
+
}
|
|
353
|
+
toList(name) {
|
|
354
|
+
const values = this.get(name);
|
|
355
|
+
return toList(values);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
var MapHttpHeaders = class extends Map {
|
|
359
|
+
get(name) {
|
|
360
|
+
return super.get(name.toLowerCase());
|
|
361
|
+
}
|
|
362
|
+
one(name) {
|
|
363
|
+
return this.get(name)?.[0];
|
|
364
|
+
}
|
|
365
|
+
list(name) {
|
|
366
|
+
const values = super.get(name.toLowerCase());
|
|
367
|
+
return toList(values);
|
|
368
|
+
}
|
|
369
|
+
set(name, value) {
|
|
370
|
+
if (typeof value === "number") {
|
|
371
|
+
value = String(value);
|
|
372
|
+
}
|
|
373
|
+
if (typeof value === "string") {
|
|
374
|
+
value = [value];
|
|
375
|
+
}
|
|
376
|
+
if (value) {
|
|
377
|
+
return super.set(name.toLowerCase(), value);
|
|
378
|
+
} else {
|
|
379
|
+
super.delete(name.toLowerCase());
|
|
380
|
+
return this;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
add(name, value) {
|
|
384
|
+
const prev = super.get(name.toLowerCase());
|
|
385
|
+
if (typeof value === "string") {
|
|
386
|
+
value = [value];
|
|
387
|
+
}
|
|
388
|
+
if (prev) {
|
|
389
|
+
value = prev.concat(value);
|
|
390
|
+
}
|
|
391
|
+
this.set(name, value);
|
|
392
|
+
return this;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
// src/http/status.ts
|
|
397
|
+
var DefaultHttpStatusCode = class {
|
|
398
|
+
#value;
|
|
399
|
+
constructor(value) {
|
|
400
|
+
this.#value = value;
|
|
401
|
+
}
|
|
402
|
+
get value() {
|
|
403
|
+
return this.#value;
|
|
404
|
+
}
|
|
405
|
+
toString() {
|
|
406
|
+
return this.#value.toString();
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
var HttpStatus = class _HttpStatus {
|
|
410
|
+
static CONTINUE = new _HttpStatus(100, "Continue");
|
|
411
|
+
static SWITCHING_PROTOCOLS = new _HttpStatus(101, "Switching Protocols");
|
|
412
|
+
// 2xx Success
|
|
413
|
+
static OK = new _HttpStatus(200, "OK");
|
|
414
|
+
static CREATED = new _HttpStatus(201, "Created");
|
|
415
|
+
static ACCEPTED = new _HttpStatus(202, "Accepted");
|
|
416
|
+
static NON_AUTHORITATIVE_INFORMATION = new _HttpStatus(203, "Non-Authoritative Information");
|
|
417
|
+
static NO_CONTENT = new _HttpStatus(204, "No Content");
|
|
418
|
+
static RESET_CONTENT = new _HttpStatus(205, "Reset Content");
|
|
419
|
+
static PARTIAL_CONTENT = new _HttpStatus(206, "Partial Content");
|
|
420
|
+
static MULTI_STATUS = new _HttpStatus(207, "Multi-Status");
|
|
421
|
+
static IM_USED = new _HttpStatus(226, "IM Used");
|
|
422
|
+
// 3xx Redirection
|
|
423
|
+
static MULTIPLE_CHOICES = new _HttpStatus(300, "Multiple Choices");
|
|
424
|
+
static MOVED_PERMANENTLY = new _HttpStatus(301, "Moved Permanently");
|
|
425
|
+
// 4xx Client Error
|
|
426
|
+
static BAD_REQUEST = new _HttpStatus(400, "Bad Request");
|
|
427
|
+
static UNAUTHORIZED = new _HttpStatus(401, "Unauthorized");
|
|
428
|
+
static FORBIDDEN = new _HttpStatus(403, "Forbidden");
|
|
429
|
+
static NOT_FOUND = new _HttpStatus(404, "Not Found");
|
|
430
|
+
static METHOD_NOT_ALLOWED = new _HttpStatus(405, "Method Not Allowed");
|
|
431
|
+
static NOT_ACCEPTABLE = new _HttpStatus(406, "Not Acceptable");
|
|
432
|
+
static PROXY_AUTHENTICATION_REQUIRED = new _HttpStatus(407, "Proxy Authentication Required");
|
|
433
|
+
static REQUEST_TIMEOUT = new _HttpStatus(408, "Request Timeout");
|
|
434
|
+
static CONFLICT = new _HttpStatus(409, "Conflict");
|
|
435
|
+
static GONE = new _HttpStatus(410, "Gone");
|
|
436
|
+
static LENGTH_REQUIRED = new _HttpStatus(411, "Length Required");
|
|
437
|
+
static PRECONDITION_FAILED = new _HttpStatus(412, "Precondition Failed");
|
|
438
|
+
static PAYLOAD_TOO_LARGE = new _HttpStatus(413, "Payload Too Large");
|
|
439
|
+
static URI_TOO_LONG = new _HttpStatus(414, "URI Too Long");
|
|
440
|
+
static UNSUPPORTED_MEDIA_TYPE = new _HttpStatus(415, "Unsupported Media Type");
|
|
441
|
+
static EXPECTATION_FAILED = new _HttpStatus(417, "Expectation Failed");
|
|
442
|
+
static IM_A_TEAPOT = new _HttpStatus(418, "I'm a teapot");
|
|
443
|
+
static TOO_EARLY = new _HttpStatus(425, "Too Early");
|
|
444
|
+
static UPGRADE_REQUIRED = new _HttpStatus(426, "Upgrade Required");
|
|
445
|
+
static PRECONDITION_REQUIRED = new _HttpStatus(428, "Precondition Required");
|
|
446
|
+
static TOO_MANY_REQUESTS = new _HttpStatus(429, "Too Many Requests");
|
|
447
|
+
static REQUEST_HEADER_FIELDS_TOO_LARGE = new _HttpStatus(431, "Request Header Fields Too Large");
|
|
448
|
+
static UNAVAILABLE_FOR_LEGAL_REASONS = new _HttpStatus(451, "Unavailable For Legal Reasons");
|
|
449
|
+
// 5xx Server Error
|
|
450
|
+
static INTERNAL_SERVER_ERROR = new _HttpStatus(500, "Internal Server Error");
|
|
451
|
+
static NOT_IMPLEMENTED = new _HttpStatus(501, "Not Implemented");
|
|
452
|
+
static BAD_GATEWAY = new _HttpStatus(502, "Bad Gateway");
|
|
453
|
+
static SERVICE_UNAVAILABLE = new _HttpStatus(503, "Service Unavailable");
|
|
454
|
+
static GATEWAY_TIMEOUT = new _HttpStatus(504, "Gateway Timeout");
|
|
455
|
+
static HTTP_VERSION_NOT_SUPPORTED = new _HttpStatus(505, "HTTP Version Not Supported");
|
|
456
|
+
static VARIANT_ALSO_NEGOTIATES = new _HttpStatus(506, "Variant Also Negotiates");
|
|
457
|
+
static INSUFFICIENT_STORAGE = new _HttpStatus(507, "Insufficient Storage");
|
|
458
|
+
static LOOP_DETECTED = new _HttpStatus(508, "Loop Detected");
|
|
459
|
+
static NOT_EXTENDED = new _HttpStatus(510, "Not Extended");
|
|
460
|
+
static NETWORK_AUTHENTICATION_REQUIRED = new _HttpStatus(511, "Network Authentication Required");
|
|
461
|
+
static #VALUES = [];
|
|
462
|
+
static {
|
|
463
|
+
Object.keys(_HttpStatus).filter((key) => key !== "VALUES" && key !== "resolve").forEach((key) => {
|
|
464
|
+
const value = _HttpStatus[key];
|
|
465
|
+
if (value instanceof _HttpStatus) {
|
|
466
|
+
Object.defineProperty(value, "name", { enumerable: true, value: key, writable: false });
|
|
467
|
+
_HttpStatus.#VALUES.push(value);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
static resolve(code) {
|
|
472
|
+
for (const status of _HttpStatus.#VALUES) {
|
|
473
|
+
if (status.value === code) {
|
|
474
|
+
return status;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
#value;
|
|
479
|
+
#phrase;
|
|
480
|
+
constructor(value, phrase) {
|
|
481
|
+
this.#value = value;
|
|
482
|
+
this.#phrase = phrase;
|
|
483
|
+
}
|
|
484
|
+
get value() {
|
|
485
|
+
return this.#value;
|
|
486
|
+
}
|
|
487
|
+
get phrase() {
|
|
488
|
+
return this.#phrase;
|
|
489
|
+
}
|
|
490
|
+
toString() {
|
|
491
|
+
return `${this.#value} ${this["name"]}`;
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
function httpStatusCode(value) {
|
|
495
|
+
if (typeof value === "number") {
|
|
496
|
+
if (value < 100 || value > 999) {
|
|
497
|
+
throw new Error(`status code ${value} should be in range 100-999`);
|
|
498
|
+
}
|
|
499
|
+
const status = HttpStatus.resolve(value);
|
|
500
|
+
if (status !== void 0) {
|
|
501
|
+
return status;
|
|
502
|
+
}
|
|
503
|
+
return new DefaultHttpStatusCode(value);
|
|
504
|
+
}
|
|
505
|
+
return value;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// src/server/exchange.ts
|
|
509
|
+
var import_node_http = __toESM(require("node:http"), 1);
|
|
214
510
|
var ExtendedHttpIncomingMessage = class extends import_node_http.default.IncomingMessage {
|
|
215
511
|
// circular reference to the exchange
|
|
216
512
|
exchange;
|
|
513
|
+
upgradeHead;
|
|
217
514
|
get urlBang() {
|
|
218
515
|
return this.url;
|
|
219
516
|
}
|
|
@@ -221,29 +518,118 @@ var ExtendedHttpIncomingMessage = class extends import_node_http.default.Incomin
|
|
|
221
518
|
return this.socket["encrypted"] === true;
|
|
222
519
|
}
|
|
223
520
|
};
|
|
224
|
-
var ExtendedHttpServerResponse = class
|
|
225
|
-
static forUpgrade(req, socket, head) {
|
|
226
|
-
const res = new _ExtendedHttpServerResponse(req);
|
|
227
|
-
res.upgradeHead = head;
|
|
228
|
-
res.assignSocket(socket);
|
|
229
|
-
return res;
|
|
230
|
-
}
|
|
231
|
-
upgradeHead;
|
|
521
|
+
var ExtendedHttpServerResponse = class extends import_node_http.default.ServerResponse {
|
|
232
522
|
markHeadersSent() {
|
|
233
523
|
this["_header"] = true;
|
|
234
524
|
}
|
|
525
|
+
getRawHeaderNames() {
|
|
526
|
+
return super["getRawHeaderNames"]();
|
|
527
|
+
}
|
|
235
528
|
};
|
|
236
|
-
var
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
#
|
|
529
|
+
var AbstractServerHttpRequest = class extends AbstractHttpRequest {
|
|
530
|
+
};
|
|
531
|
+
var AbstractServerHttpResponse = class extends AbstractHttpResponse {
|
|
532
|
+
#cookies = [];
|
|
533
|
+
#statusCode;
|
|
534
|
+
#state = "new";
|
|
535
|
+
#commitActions = [];
|
|
536
|
+
setStatusCode(statusCode) {
|
|
537
|
+
if (this.#state === "committed") {
|
|
538
|
+
return false;
|
|
539
|
+
} else {
|
|
540
|
+
this.#statusCode = statusCode;
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
setRawStatusCode(statusCode) {
|
|
545
|
+
return this.setStatusCode(statusCode === void 0 ? void 0 : httpStatusCode(statusCode));
|
|
546
|
+
}
|
|
547
|
+
get statusCode() {
|
|
548
|
+
return this.#statusCode;
|
|
549
|
+
}
|
|
550
|
+
addCookie(cookie) {
|
|
551
|
+
if (this.#state === "committed") {
|
|
552
|
+
throw new Error(`Cannot add cookie ${JSON.stringify(cookie)} because HTTP response has already been committed`);
|
|
553
|
+
}
|
|
554
|
+
this.#cookies.push(cookie);
|
|
555
|
+
return this;
|
|
556
|
+
}
|
|
557
|
+
beforeCommit(action) {
|
|
558
|
+
this.#commitActions.push(action);
|
|
559
|
+
}
|
|
560
|
+
get commited() {
|
|
561
|
+
const state = this.#state;
|
|
562
|
+
return state !== "new" && state !== "commit-action-failed";
|
|
563
|
+
}
|
|
564
|
+
async body(body) {
|
|
565
|
+
if (body instanceof ReadableStream) {
|
|
566
|
+
throw new Error("ReadableStream body not supported yet");
|
|
567
|
+
}
|
|
568
|
+
const buffer = await body;
|
|
569
|
+
try {
|
|
570
|
+
return await this.doCommit(async () => {
|
|
571
|
+
return await this.bodyInternal(Promise.resolve(buffer));
|
|
572
|
+
}).catch((error) => {
|
|
573
|
+
throw error;
|
|
574
|
+
});
|
|
575
|
+
} catch (error) {
|
|
576
|
+
throw error;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
async end() {
|
|
580
|
+
if (!this.commited) {
|
|
581
|
+
return this.doCommit();
|
|
582
|
+
} else {
|
|
583
|
+
return Promise.resolve(false);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
doCommit(writeAction) {
|
|
587
|
+
const state = this.#state;
|
|
588
|
+
let allActions = Promise.resolve();
|
|
589
|
+
if (state === "new") {
|
|
590
|
+
this.#state = "committing";
|
|
591
|
+
if (this.#commitActions.length > 0) {
|
|
592
|
+
allActions = this.#commitActions.reduce(
|
|
593
|
+
(acc, cur) => acc.then(() => cur()),
|
|
594
|
+
Promise.resolve()
|
|
595
|
+
).catch((error) => {
|
|
596
|
+
const state2 = this.#state;
|
|
597
|
+
if (state2 === "committing") {
|
|
598
|
+
this.#state = "commit-action-failed";
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
} else if (state === "commit-action-failed") {
|
|
603
|
+
this.#state = "committing";
|
|
604
|
+
} else {
|
|
605
|
+
return Promise.resolve(false);
|
|
606
|
+
}
|
|
607
|
+
allActions = allActions.then(() => {
|
|
608
|
+
this.applyStatusCode();
|
|
609
|
+
this.applyHeaders();
|
|
610
|
+
this.applyCookies();
|
|
611
|
+
this.#state = "committed";
|
|
612
|
+
});
|
|
613
|
+
return allActions.then(async () => {
|
|
614
|
+
return writeAction !== void 0 ? await writeAction() : true;
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
applyStatusCode() {
|
|
618
|
+
}
|
|
619
|
+
applyHeaders() {
|
|
620
|
+
}
|
|
621
|
+
applyCookies() {
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
var HttpServerRequest = class extends AbstractServerHttpRequest {
|
|
625
|
+
#url;
|
|
626
|
+
#cookies;
|
|
241
627
|
#req;
|
|
242
628
|
constructor(req) {
|
|
629
|
+
super(new IncomingMessageHeaders(req));
|
|
243
630
|
this.#req = req;
|
|
244
|
-
this.#headers = new IncomingMessageHeaders(this.#req);
|
|
245
631
|
}
|
|
246
|
-
|
|
632
|
+
getNativeRequest() {
|
|
247
633
|
return this.#req;
|
|
248
634
|
}
|
|
249
635
|
get upgrade() {
|
|
@@ -252,15 +638,12 @@ var HttpServerRequest = class {
|
|
|
252
638
|
get http2() {
|
|
253
639
|
return this.#req.httpVersionMajor >= 2;
|
|
254
640
|
}
|
|
255
|
-
get headers() {
|
|
256
|
-
return this.#headers;
|
|
257
|
-
}
|
|
258
641
|
get path() {
|
|
259
642
|
return this.URL?.pathname;
|
|
260
643
|
}
|
|
261
644
|
get URL() {
|
|
262
|
-
this
|
|
263
|
-
return this
|
|
645
|
+
this.#url ??= new URL(this.#req.urlBang, `${this.protocol}://${this.host}`);
|
|
646
|
+
return this.#url;
|
|
264
647
|
}
|
|
265
648
|
get query() {
|
|
266
649
|
return this.URL?.search;
|
|
@@ -274,7 +657,7 @@ var HttpServerRequest = class {
|
|
|
274
657
|
dh = this.#req.headers[":authority"];
|
|
275
658
|
}
|
|
276
659
|
dh ??= this.#req.socket.remoteAddress;
|
|
277
|
-
return
|
|
660
|
+
return super.parseHost(dh);
|
|
278
661
|
}
|
|
279
662
|
get protocol() {
|
|
280
663
|
let dp = void 0;
|
|
@@ -282,47 +665,65 @@ var HttpServerRequest = class {
|
|
|
282
665
|
dp = this.#req.headers[":scheme"];
|
|
283
666
|
}
|
|
284
667
|
dp ??= this.#req.socketEncrypted ? "https" : "http";
|
|
285
|
-
return
|
|
668
|
+
return super.parseProtocol(dp);
|
|
286
669
|
}
|
|
287
670
|
get socket() {
|
|
288
671
|
return this.#req.socket;
|
|
289
672
|
}
|
|
673
|
+
get remoteAddress() {
|
|
674
|
+
const family = this.#req.socket.remoteFamily;
|
|
675
|
+
const address = this.#req.socket.remoteAddress;
|
|
676
|
+
const port = this.#req.socket.remotePort;
|
|
677
|
+
if (!family || !address || !port) {
|
|
678
|
+
return void 0;
|
|
679
|
+
}
|
|
680
|
+
return { family, address, port };
|
|
681
|
+
}
|
|
290
682
|
get cookies() {
|
|
291
|
-
this
|
|
292
|
-
return this
|
|
683
|
+
this.#cookies ??= super.cookies;
|
|
684
|
+
return this.#cookies;
|
|
293
685
|
}
|
|
294
686
|
get body() {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
687
|
+
return import_node_http.default.IncomingMessage.toWeb(this.#req);
|
|
688
|
+
}
|
|
689
|
+
async blob() {
|
|
690
|
+
const chunks = [];
|
|
691
|
+
if (this.body !== void 0) {
|
|
692
|
+
for await (const chunk of this.body) {
|
|
693
|
+
chunks.push(chunk);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return new Blob(chunks, { type: this.headers.one("content-type") || "application/octet-stream" });
|
|
302
697
|
}
|
|
303
|
-
|
|
304
|
-
|
|
698
|
+
async text() {
|
|
699
|
+
const blob = await this.blob();
|
|
700
|
+
return await blob.text();
|
|
305
701
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
return formData;
|
|
311
|
-
});
|
|
702
|
+
async formData() {
|
|
703
|
+
const blob = await this.blob();
|
|
704
|
+
const text = await blob.text();
|
|
705
|
+
return new URLSearchParams(text);
|
|
312
706
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
707
|
+
async json() {
|
|
708
|
+
const blob = await this.blob();
|
|
709
|
+
if (blob.size === 0) {
|
|
710
|
+
return void 0;
|
|
711
|
+
}
|
|
712
|
+
const text = await blob.text();
|
|
713
|
+
return JSON.parse(text);
|
|
714
|
+
}
|
|
715
|
+
initId() {
|
|
716
|
+
const remoteIp = this.#req.socket.remoteAddress;
|
|
717
|
+
if (!remoteIp) {
|
|
718
|
+
throw new Error("Socket has no remote address");
|
|
719
|
+
}
|
|
720
|
+
return `${remoteIp}:${this.#req.socket.remotePort}`;
|
|
321
721
|
}
|
|
322
722
|
};
|
|
323
|
-
var IncomingMessageHeaders = class {
|
|
723
|
+
var IncomingMessageHeaders = class extends AbstractHttpHeaders {
|
|
324
724
|
#msg;
|
|
325
725
|
constructor(msg) {
|
|
726
|
+
super();
|
|
326
727
|
this.#msg = msg;
|
|
327
728
|
}
|
|
328
729
|
has(name) {
|
|
@@ -332,7 +733,7 @@ var IncomingMessageHeaders = class {
|
|
|
332
733
|
return this.#msg.headers[name];
|
|
333
734
|
}
|
|
334
735
|
list(name) {
|
|
335
|
-
return toList(
|
|
736
|
+
return super.toList(name);
|
|
336
737
|
}
|
|
337
738
|
one(name) {
|
|
338
739
|
const value = this.#msg.headers[name];
|
|
@@ -345,9 +746,10 @@ var IncomingMessageHeaders = class {
|
|
|
345
746
|
return Object.keys(this.#msg.headers).values();
|
|
346
747
|
}
|
|
347
748
|
};
|
|
348
|
-
var OutgoingMessageHeaders = class {
|
|
749
|
+
var OutgoingMessageHeaders = class extends AbstractHttpHeaders {
|
|
349
750
|
#msg;
|
|
350
751
|
constructor(msg) {
|
|
752
|
+
super();
|
|
351
753
|
this.#msg = msg;
|
|
352
754
|
}
|
|
353
755
|
has(name) {
|
|
@@ -381,284 +783,248 @@ var OutgoingMessageHeaders = class {
|
|
|
381
783
|
}
|
|
382
784
|
return this;
|
|
383
785
|
}
|
|
384
|
-
add(name, value) {
|
|
385
|
-
if (!this.#msg.headersSent) {
|
|
386
|
-
this.#msg.appendHeader(name, value);
|
|
387
|
-
}
|
|
388
|
-
return this;
|
|
389
|
-
}
|
|
390
|
-
list(name) {
|
|
391
|
-
const values = this.get(name);
|
|
392
|
-
return toList(values);
|
|
393
|
-
}
|
|
394
|
-
};
|
|
395
|
-
var HttpServerResponse = class {
|
|
396
|
-
#headers;
|
|
397
|
-
#res;
|
|
398
|
-
constructor(res) {
|
|
399
|
-
this.#res = res;
|
|
400
|
-
this.#headers = new OutgoingMessageHeaders(res);
|
|
401
|
-
}
|
|
402
|
-
get unsafeServerResponse() {
|
|
403
|
-
return this.#res;
|
|
404
|
-
}
|
|
405
|
-
get statusCode() {
|
|
406
|
-
return this.#res.statusCode;
|
|
407
|
-
}
|
|
408
|
-
set statusCode(value) {
|
|
409
|
-
if (this.#res.headersSent) {
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
this.#res.statusCode = value;
|
|
413
|
-
}
|
|
414
|
-
set statusMessage(value) {
|
|
415
|
-
this.#res.statusMessage = value;
|
|
416
|
-
}
|
|
417
|
-
get headers() {
|
|
418
|
-
return this.#headers;
|
|
419
|
-
}
|
|
420
|
-
get cookies() {
|
|
421
|
-
return this.headers.list("set-cookie").map((cookie) => {
|
|
422
|
-
const parsed = import_tough_cookie.Cookie.parse(cookie);
|
|
423
|
-
if (parsed) {
|
|
424
|
-
const result = { name: parsed.key, value: parsed.value, maxAge: Number(parsed.maxAge ?? -1) };
|
|
425
|
-
if (parsed.httpOnly) result.httpOnly = true;
|
|
426
|
-
if (parsed.domain) result.domain = parsed.domain;
|
|
427
|
-
if (parsed.path) result.path = parsed.path;
|
|
428
|
-
if (parsed.secure) result.secure = true;
|
|
429
|
-
if (parsed.httpOnly) result.httpOnly = true;
|
|
430
|
-
if (parsed.sameSite) result.sameSite = parsed.sameSite;
|
|
431
|
-
return result;
|
|
432
|
-
}
|
|
433
|
-
}).filter((cookie) => cookie !== void 0);
|
|
434
|
-
}
|
|
435
|
-
end(chunk) {
|
|
436
|
-
if (!this.#res.headersSent) {
|
|
437
|
-
return new Promise((resolve, reject) => {
|
|
438
|
-
try {
|
|
439
|
-
if (chunk === void 0) {
|
|
440
|
-
this.#res.end(() => {
|
|
441
|
-
resolve(true);
|
|
442
|
-
});
|
|
443
|
-
} else {
|
|
444
|
-
this.#res.end(chunk, () => {
|
|
445
|
-
resolve(true);
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
} catch (e) {
|
|
449
|
-
reject(e instanceof Error ? e : new Error(`end failed: ${e}`));
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
} else {
|
|
453
|
-
return Promise.resolve(false);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
addCookie(cookie) {
|
|
457
|
-
this.headers.add("set-cookie", new import_tough_cookie.Cookie({
|
|
458
|
-
key: cookie.name,
|
|
459
|
-
value: cookie.value,
|
|
460
|
-
maxAge: cookie.maxAge,
|
|
461
|
-
domain: cookie.domain,
|
|
462
|
-
path: cookie.path,
|
|
463
|
-
secure: cookie.secure,
|
|
464
|
-
httpOnly: cookie.httpOnly,
|
|
465
|
-
sameSite: cookie.sameSite
|
|
466
|
-
}).toString());
|
|
467
|
-
return this;
|
|
468
|
-
}
|
|
469
|
-
};
|
|
470
|
-
var DefaultWebExchange = class {
|
|
471
|
-
request;
|
|
472
|
-
response;
|
|
473
|
-
constructor(request, response) {
|
|
474
|
-
this.request = request;
|
|
475
|
-
this.response = response;
|
|
476
|
-
}
|
|
477
|
-
get method() {
|
|
478
|
-
return this.request.method;
|
|
479
|
-
}
|
|
480
|
-
get path() {
|
|
481
|
-
return this.request.path;
|
|
482
|
-
}
|
|
483
|
-
principal() {
|
|
484
|
-
return Promise.resolve(void 0);
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
function toList(values) {
|
|
488
|
-
if (typeof values === "string") {
|
|
489
|
-
values = [values];
|
|
490
|
-
}
|
|
491
|
-
if (typeof values === "number") {
|
|
492
|
-
values = [String(values)];
|
|
493
|
-
}
|
|
494
|
-
const list = [];
|
|
495
|
-
if (values) {
|
|
496
|
-
for (const value of values) {
|
|
497
|
-
if (value) {
|
|
498
|
-
list.push(...parseHeader(value));
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
return list;
|
|
503
|
-
}
|
|
504
|
-
function parseHeader(value) {
|
|
505
|
-
const list = [];
|
|
506
|
-
{
|
|
507
|
-
let start2 = 0;
|
|
508
|
-
let end = 0;
|
|
509
|
-
for (let i = 0; i < value.length; i++) {
|
|
510
|
-
switch (value.charCodeAt(i)) {
|
|
511
|
-
case 32:
|
|
512
|
-
if (start2 === end) {
|
|
513
|
-
start2 = end = i + 1;
|
|
514
|
-
}
|
|
515
|
-
break;
|
|
516
|
-
case 44:
|
|
517
|
-
list.push(value.slice(start2, end));
|
|
518
|
-
start2 = end = i + 1;
|
|
519
|
-
break;
|
|
520
|
-
default:
|
|
521
|
-
end = end + 1;
|
|
522
|
-
break;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
list.push(value.slice(start2, end));
|
|
526
|
-
}
|
|
527
|
-
return list;
|
|
528
|
-
}
|
|
529
|
-
var MapHttpHeaders = class extends Map {
|
|
530
|
-
get(name) {
|
|
531
|
-
return super.get(name.toLowerCase());
|
|
532
|
-
}
|
|
533
|
-
one(name) {
|
|
534
|
-
return this.get(name)?.[0];
|
|
535
|
-
}
|
|
536
|
-
list(name) {
|
|
537
|
-
const values = super.get(name.toLowerCase());
|
|
538
|
-
return toList(values);
|
|
539
|
-
}
|
|
540
|
-
set(name, value) {
|
|
541
|
-
if (typeof value === "number") {
|
|
542
|
-
value = String(value);
|
|
543
|
-
}
|
|
544
|
-
if (typeof value === "string") {
|
|
545
|
-
value = [value];
|
|
546
|
-
}
|
|
547
|
-
if (value) {
|
|
548
|
-
return super.set(name.toLowerCase(), value);
|
|
549
|
-
} else {
|
|
550
|
-
super.delete(name.toLowerCase());
|
|
551
|
-
return this;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
add(name, value) {
|
|
555
|
-
const prev = super.get(name.toLowerCase());
|
|
556
|
-
if (typeof value === "string") {
|
|
557
|
-
value = [value];
|
|
558
|
-
}
|
|
559
|
-
if (prev) {
|
|
560
|
-
value = prev.concat(value);
|
|
786
|
+
add(name, value) {
|
|
787
|
+
if (!this.#msg.headersSent) {
|
|
788
|
+
this.#msg.appendHeader(name, value);
|
|
561
789
|
}
|
|
562
|
-
this.set(name, value);
|
|
563
790
|
return this;
|
|
564
791
|
}
|
|
792
|
+
list(name) {
|
|
793
|
+
return super.toList(name);
|
|
794
|
+
}
|
|
565
795
|
};
|
|
566
|
-
var
|
|
567
|
-
#
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
796
|
+
var HttpServerResponse = class extends AbstractServerHttpResponse {
|
|
797
|
+
#res;
|
|
798
|
+
constructor(res) {
|
|
799
|
+
super(new OutgoingMessageHeaders(res));
|
|
800
|
+
this.#res = res;
|
|
801
|
+
}
|
|
802
|
+
getNativeResponse() {
|
|
803
|
+
return this.#res;
|
|
804
|
+
}
|
|
805
|
+
get statusCode() {
|
|
806
|
+
const status = super.statusCode;
|
|
807
|
+
return status ?? { value: this.#res.statusCode };
|
|
808
|
+
}
|
|
809
|
+
applyStatusCode() {
|
|
810
|
+
const status = super.statusCode;
|
|
811
|
+
if (status !== void 0) {
|
|
812
|
+
this.#res.statusCode = status.value;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
addCookie(cookie) {
|
|
816
|
+
this.headers.add("Set-Cookie", super.setCookieValue(cookie));
|
|
817
|
+
return this;
|
|
818
|
+
}
|
|
819
|
+
async bodyInternal(body) {
|
|
820
|
+
if (!this.#res.headersSent) {
|
|
821
|
+
if (body instanceof ReadableStream) {
|
|
822
|
+
throw new Error("ReadableStream body not supported in response");
|
|
577
823
|
} else {
|
|
578
|
-
|
|
824
|
+
const chunk = await body;
|
|
825
|
+
return await new Promise((resolve, reject) => {
|
|
826
|
+
try {
|
|
827
|
+
if (chunk === void 0) {
|
|
828
|
+
this.#res.end(() => {
|
|
829
|
+
resolve(true);
|
|
830
|
+
});
|
|
831
|
+
} else {
|
|
832
|
+
if (!this.headers.has("content-length")) {
|
|
833
|
+
if (typeof chunk === "string") {
|
|
834
|
+
this.headers.set("content-length", Buffer.byteLength(chunk));
|
|
835
|
+
} else {
|
|
836
|
+
this.headers.set("content-length", chunk.byteLength);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
this.#res.end(chunk, () => {
|
|
840
|
+
resolve(true);
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
} catch (e) {
|
|
844
|
+
reject(e instanceof Error ? e : new Error(`end failed: ${e}`));
|
|
845
|
+
}
|
|
846
|
+
});
|
|
579
847
|
}
|
|
848
|
+
} else {
|
|
849
|
+
return false;
|
|
580
850
|
}
|
|
581
|
-
this.#url = url;
|
|
582
|
-
this.method = method ?? "GET";
|
|
583
|
-
this.headers.set("Host", this.#url.hostname);
|
|
584
|
-
this.path = this.#url.pathname ?? "/";
|
|
585
851
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
852
|
+
};
|
|
853
|
+
var ServerHttpRequestDecorator = class _ServerHttpRequestDecorator {
|
|
854
|
+
#delegate;
|
|
855
|
+
constructor(request) {
|
|
856
|
+
this.#delegate = request;
|
|
857
|
+
}
|
|
858
|
+
get delegate() {
|
|
859
|
+
return this.#delegate;
|
|
860
|
+
}
|
|
861
|
+
get id() {
|
|
862
|
+
return this.#delegate.id;
|
|
863
|
+
}
|
|
864
|
+
get method() {
|
|
865
|
+
return this.#delegate.method;
|
|
866
|
+
}
|
|
867
|
+
get path() {
|
|
868
|
+
return this.#delegate.path;
|
|
590
869
|
}
|
|
591
870
|
get protocol() {
|
|
592
|
-
return
|
|
871
|
+
return this.#delegate.protocol;
|
|
872
|
+
}
|
|
873
|
+
get host() {
|
|
874
|
+
return this.#delegate.host;
|
|
875
|
+
}
|
|
876
|
+
get URL() {
|
|
877
|
+
return this.#delegate.URL;
|
|
878
|
+
}
|
|
879
|
+
get headers() {
|
|
880
|
+
return this.#delegate.headers;
|
|
593
881
|
}
|
|
594
882
|
get cookies() {
|
|
595
|
-
return
|
|
883
|
+
return this.#delegate.cookies;
|
|
884
|
+
}
|
|
885
|
+
get remoteAddress() {
|
|
886
|
+
return this.#delegate.remoteAddress;
|
|
887
|
+
}
|
|
888
|
+
get upgrade() {
|
|
889
|
+
return this.#delegate.upgrade;
|
|
596
890
|
}
|
|
597
891
|
get body() {
|
|
598
|
-
|
|
599
|
-
return body ? Promise.resolve(body) : Promise.reject(new Error(`no body set`));
|
|
892
|
+
return this.#delegate.body;
|
|
600
893
|
}
|
|
601
|
-
|
|
602
|
-
return this.
|
|
894
|
+
async blob() {
|
|
895
|
+
return await this.#delegate.blob();
|
|
603
896
|
}
|
|
604
|
-
|
|
605
|
-
return
|
|
897
|
+
async text() {
|
|
898
|
+
return await this.#delegate.text();
|
|
606
899
|
}
|
|
607
|
-
|
|
608
|
-
this.#
|
|
609
|
-
if (!this.headers.has("content-type")) {
|
|
610
|
-
this.headers.set("content-type", value.type || "application/octet-stream");
|
|
611
|
-
}
|
|
900
|
+
async formData() {
|
|
901
|
+
return await this.#delegate.formData();
|
|
612
902
|
}
|
|
613
|
-
|
|
614
|
-
return
|
|
903
|
+
async json() {
|
|
904
|
+
return await this.#delegate.json();
|
|
615
905
|
}
|
|
616
|
-
|
|
617
|
-
return
|
|
906
|
+
toString() {
|
|
907
|
+
return `${_ServerHttpRequestDecorator.name} [delegate: ${this.delegate.toString()}]`;
|
|
908
|
+
}
|
|
909
|
+
static getNativeRequest(request) {
|
|
910
|
+
if (request instanceof AbstractServerHttpRequest) {
|
|
911
|
+
return request.getNativeRequest();
|
|
912
|
+
} else if (request instanceof _ServerHttpRequestDecorator) {
|
|
913
|
+
return _ServerHttpRequestDecorator.getNativeRequest(request.delegate);
|
|
914
|
+
} else {
|
|
915
|
+
throw new Error(`Cannot get native request from ${request.constructor.name}`);
|
|
916
|
+
}
|
|
618
917
|
}
|
|
619
918
|
};
|
|
620
|
-
var
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
919
|
+
var ServerHttpResponseDecorator = class _ServerHttpResponseDecorator {
|
|
920
|
+
#delegate;
|
|
921
|
+
constructor(response) {
|
|
922
|
+
this.#delegate = response;
|
|
923
|
+
}
|
|
924
|
+
get delegate() {
|
|
925
|
+
return this.#delegate;
|
|
926
|
+
}
|
|
927
|
+
setStatusCode(statusCode) {
|
|
928
|
+
return this.delegate.setStatusCode(statusCode);
|
|
929
|
+
}
|
|
930
|
+
setRawStatusCode(statusCode) {
|
|
931
|
+
return this.delegate.setRawStatusCode(statusCode);
|
|
932
|
+
}
|
|
933
|
+
get statusCode() {
|
|
934
|
+
return this.delegate.statusCode;
|
|
935
|
+
}
|
|
936
|
+
get cookies() {
|
|
937
|
+
return this.delegate.cookies;
|
|
938
|
+
}
|
|
624
939
|
addCookie(cookie) {
|
|
625
|
-
this.
|
|
940
|
+
this.delegate.addCookie(cookie);
|
|
626
941
|
return this;
|
|
627
942
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
default:
|
|
648
|
-
throw new Error(`Unsupported chunk type: ${typeof chunk}`);
|
|
943
|
+
async end() {
|
|
944
|
+
return await this.delegate.end();
|
|
945
|
+
}
|
|
946
|
+
async body(body) {
|
|
947
|
+
return await this.#delegate.body(body);
|
|
948
|
+
}
|
|
949
|
+
get headers() {
|
|
950
|
+
return this.#delegate.headers;
|
|
951
|
+
}
|
|
952
|
+
toString() {
|
|
953
|
+
return `${_ServerHttpResponseDecorator.name} [delegate: ${this.delegate.toString()}]`;
|
|
954
|
+
}
|
|
955
|
+
static getNativeResponse(response) {
|
|
956
|
+
if (response instanceof AbstractServerHttpResponse) {
|
|
957
|
+
return response.getNativeResponse();
|
|
958
|
+
} else if (response instanceof _ServerHttpResponseDecorator) {
|
|
959
|
+
return _ServerHttpResponseDecorator.getNativeResponse(response.delegate);
|
|
960
|
+
} else {
|
|
961
|
+
throw new Error(`Cannot get native response from ${response.constructor.name}`);
|
|
649
962
|
}
|
|
650
|
-
return true;
|
|
651
963
|
}
|
|
652
964
|
};
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if (!remoteIp) {
|
|
658
|
-
throw new Error("Socket has no remote address");
|
|
965
|
+
var ServerWebExchangeDecorator = class _ServerWebExchangeDecorator {
|
|
966
|
+
#delegate;
|
|
967
|
+
constructor(exchange) {
|
|
968
|
+
this.#delegate = exchange;
|
|
659
969
|
}
|
|
660
|
-
|
|
661
|
-
|
|
970
|
+
get delegate() {
|
|
971
|
+
return this.#delegate;
|
|
972
|
+
}
|
|
973
|
+
get request() {
|
|
974
|
+
return this.#delegate.request;
|
|
975
|
+
}
|
|
976
|
+
get response() {
|
|
977
|
+
return this.#delegate.response;
|
|
978
|
+
}
|
|
979
|
+
attribute(name) {
|
|
980
|
+
return this.#delegate.attribute(name);
|
|
981
|
+
}
|
|
982
|
+
principal() {
|
|
983
|
+
return this.#delegate.principal();
|
|
984
|
+
}
|
|
985
|
+
get logPrefix() {
|
|
986
|
+
return this.#delegate.logPrefix;
|
|
987
|
+
}
|
|
988
|
+
toString() {
|
|
989
|
+
return `${_ServerWebExchangeDecorator.name} [delegate: ${this.delegate}]`;
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
var DefaultWebExchange = class {
|
|
993
|
+
request;
|
|
994
|
+
response;
|
|
995
|
+
#attributes = {};
|
|
996
|
+
#logId;
|
|
997
|
+
#logPrefix = "";
|
|
998
|
+
constructor(request, response) {
|
|
999
|
+
this.#attributes[LOG_ID_ATTRIBUTE] = request.id;
|
|
1000
|
+
this.request = request;
|
|
1001
|
+
this.response = response;
|
|
1002
|
+
}
|
|
1003
|
+
get method() {
|
|
1004
|
+
return this.request.method;
|
|
1005
|
+
}
|
|
1006
|
+
get path() {
|
|
1007
|
+
return this.request.path;
|
|
1008
|
+
}
|
|
1009
|
+
get attributes() {
|
|
1010
|
+
return this.#attributes;
|
|
1011
|
+
}
|
|
1012
|
+
attribute(name) {
|
|
1013
|
+
return this.attributes[name];
|
|
1014
|
+
}
|
|
1015
|
+
principal() {
|
|
1016
|
+
return Promise.resolve(void 0);
|
|
1017
|
+
}
|
|
1018
|
+
get logPrefix() {
|
|
1019
|
+
const value = this.attribute(LOG_ID_ATTRIBUTE);
|
|
1020
|
+
if (this.#logId !== value) {
|
|
1021
|
+
this.#logId = value;
|
|
1022
|
+
this.#logPrefix = value !== void 0 ? `[${value}] ` : "";
|
|
1023
|
+
}
|
|
1024
|
+
return this.#logPrefix;
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
var LOG_ID_ATTRIBUTE = "io.interop.gateway.server.log_id";
|
|
662
1028
|
|
|
663
1029
|
// src/server/address.ts
|
|
664
1030
|
var import_node_os = require("node:os");
|
|
@@ -840,7 +1206,20 @@ async function stop(m) {
|
|
|
840
1206
|
return await run(m, "stop");
|
|
841
1207
|
}
|
|
842
1208
|
|
|
843
|
-
// src/
|
|
1209
|
+
// src/server/server-header.ts
|
|
1210
|
+
var import_package = __toESM(require("@interopio/gateway-server/package.json"), 1);
|
|
1211
|
+
var serverHeader = (server) => {
|
|
1212
|
+
server ??= `${import_package.default.name} - v${import_package.default.version}`;
|
|
1213
|
+
return async ({ response }, next) => {
|
|
1214
|
+
if (server !== false && !response.headers.has("server")) {
|
|
1215
|
+
response.headers.set("Server", server);
|
|
1216
|
+
}
|
|
1217
|
+
await next();
|
|
1218
|
+
};
|
|
1219
|
+
};
|
|
1220
|
+
var server_header_default = (server) => serverHeader(server);
|
|
1221
|
+
|
|
1222
|
+
// src/server/ws-client-verify.ts
|
|
844
1223
|
var import_gateway3 = require("@interopio/gateway");
|
|
845
1224
|
var log3 = getLogger("gateway.ws.client-verify");
|
|
846
1225
|
function acceptsMissing(originFilters) {
|
|
@@ -912,19 +1291,6 @@ function regexifyOriginFilters(originFilters) {
|
|
|
912
1291
|
}
|
|
913
1292
|
}
|
|
914
1293
|
|
|
915
|
-
// src/server/server-header.ts
|
|
916
|
-
var import_package = __toESM(require("@interopio/gateway-server/package.json"), 1);
|
|
917
|
-
var serverHeader = (server) => {
|
|
918
|
-
server ??= `${import_package.default.name} - v${import_package.default.version}`;
|
|
919
|
-
return async ({ response }, next) => {
|
|
920
|
-
if (server !== false && !response.headers.has("server")) {
|
|
921
|
-
response.headers.set("Server", server);
|
|
922
|
-
}
|
|
923
|
-
await next();
|
|
924
|
-
};
|
|
925
|
-
};
|
|
926
|
-
var server_header_default = (server) => serverHeader(server);
|
|
927
|
-
|
|
928
1294
|
// src/server/util/matchers.ts
|
|
929
1295
|
var or = (matchers) => {
|
|
930
1296
|
return async (exchange) => {
|
|
@@ -1031,15 +1397,6 @@ upgradeMatcher.toString = () => "websocket upgrade";
|
|
|
1031
1397
|
|
|
1032
1398
|
// src/app/route.ts
|
|
1033
1399
|
var import_gateway4 = require("@interopio/gateway");
|
|
1034
|
-
function findSocketRoute({ request }, { sockets: routes }) {
|
|
1035
|
-
const path = request.path ?? "/";
|
|
1036
|
-
const route = routes.get(path) ?? Array.from(routes.values()).find((route2) => {
|
|
1037
|
-
if (path === "/" && route2.default === true) {
|
|
1038
|
-
return true;
|
|
1039
|
-
}
|
|
1040
|
-
});
|
|
1041
|
-
return [route, path];
|
|
1042
|
-
}
|
|
1043
1400
|
async function configure(app, config, routes) {
|
|
1044
1401
|
const applyCors = (matcher, requestMethod, options) => {
|
|
1045
1402
|
if (options?.cors) {
|
|
@@ -1241,7 +1598,7 @@ var corsFilter = (opts) => {
|
|
|
1241
1598
|
var cors_default = corsFilter;
|
|
1242
1599
|
var logger = getLogger("cors");
|
|
1243
1600
|
function rejectRequest(response) {
|
|
1244
|
-
response.
|
|
1601
|
+
response.setStatusCode(HttpStatus.FORBIDDEN);
|
|
1245
1602
|
}
|
|
1246
1603
|
function handleInternal(exchange, config, preFlightRequest) {
|
|
1247
1604
|
const { request, response } = exchange;
|
|
@@ -1331,70 +1688,243 @@ function checkMethods(config, requestMethod) {
|
|
|
1331
1688
|
if (allowedMethods === ALL) {
|
|
1332
1689
|
return [requestMethod];
|
|
1333
1690
|
}
|
|
1334
|
-
if (import_gateway5.IOGateway.Filtering.valuesMatch(allowedMethods, requestMethod)) {
|
|
1335
|
-
return allowedMethods;
|
|
1691
|
+
if (import_gateway5.IOGateway.Filtering.valuesMatch(allowedMethods, requestMethod)) {
|
|
1692
|
+
return allowedMethods;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
function checkHeaders(config, requestHeaders) {
|
|
1697
|
+
if (requestHeaders === void 0) {
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
if (requestHeaders.length == 0) {
|
|
1701
|
+
return [];
|
|
1702
|
+
}
|
|
1703
|
+
const allowedHeaders = config.allowHeaders;
|
|
1704
|
+
if (allowedHeaders === void 0) {
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
const allowAnyHeader = allowedHeaders === ALL || allowedHeaders.includes(ALL);
|
|
1708
|
+
const result = [];
|
|
1709
|
+
for (const requestHeader of requestHeaders) {
|
|
1710
|
+
const value = requestHeader?.trim();
|
|
1711
|
+
if (value) {
|
|
1712
|
+
if (allowAnyHeader) {
|
|
1713
|
+
result.push(value);
|
|
1714
|
+
} else {
|
|
1715
|
+
for (const allowedHeader of allowedHeaders) {
|
|
1716
|
+
if (value.toLowerCase() === allowedHeader) {
|
|
1717
|
+
result.push(value);
|
|
1718
|
+
break;
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
if (result.length > 0) {
|
|
1725
|
+
return result;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
function trimTrailingSlash(origin) {
|
|
1729
|
+
return origin.endsWith("/") ? origin.slice(0, -1) : origin;
|
|
1730
|
+
}
|
|
1731
|
+
function getMethodToUse(request, isPreFlight) {
|
|
1732
|
+
return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
|
|
1733
|
+
}
|
|
1734
|
+
function getHeadersToUse(request, isPreFlight) {
|
|
1735
|
+
const headers2 = request.headers;
|
|
1736
|
+
return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
|
|
1737
|
+
}
|
|
1738
|
+
var matchingCorsConfigSource = (opts) => {
|
|
1739
|
+
return async (exchange) => {
|
|
1740
|
+
for (const [matcher, config] of opts.mappings) {
|
|
1741
|
+
if ((await matcher(exchange)).match) {
|
|
1742
|
+
logger.debug(`resolved cors config on '${exchange.request.path}' using ${matcher}: ${JSON.stringify(config)}`);
|
|
1743
|
+
return config;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1747
|
+
};
|
|
1748
|
+
|
|
1749
|
+
// src/mock/server/exchange.ts
|
|
1750
|
+
var import_web = __toESM(require("node:stream/web"), 1);
|
|
1751
|
+
var MockServerHttpRequest = class extends AbstractHttpRequest {
|
|
1752
|
+
#url;
|
|
1753
|
+
#body;
|
|
1754
|
+
upgrade = false;
|
|
1755
|
+
constructor(url, method) {
|
|
1756
|
+
super(new MapHttpHeaders());
|
|
1757
|
+
if (typeof url === "string") {
|
|
1758
|
+
if (URL.canParse(url)) {
|
|
1759
|
+
url = new URL(url);
|
|
1760
|
+
} else if (URL.canParse(url, "http://localhost")) {
|
|
1761
|
+
url = new URL(url, "http://localhost");
|
|
1762
|
+
} else {
|
|
1763
|
+
throw new TypeError("URL cannot parse url");
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
this.#url = url;
|
|
1767
|
+
this.method = method ?? "GET";
|
|
1768
|
+
this.setHeader("Host", this.#url.hostname);
|
|
1769
|
+
this.path = this.#url.pathname ?? "/";
|
|
1770
|
+
}
|
|
1771
|
+
method;
|
|
1772
|
+
path;
|
|
1773
|
+
get host() {
|
|
1774
|
+
return super.parseHost(this.#url.host);
|
|
1775
|
+
}
|
|
1776
|
+
get protocol() {
|
|
1777
|
+
return super.parseProtocol(this.#url.protocol.slice(0, -1));
|
|
1778
|
+
}
|
|
1779
|
+
get body() {
|
|
1780
|
+
if (this.#body !== void 0) {
|
|
1781
|
+
const blob = this.#body;
|
|
1782
|
+
const asyncIterator = async function* () {
|
|
1783
|
+
const bytes = await blob.bytes();
|
|
1784
|
+
yield bytes;
|
|
1785
|
+
}();
|
|
1786
|
+
return import_web.default.ReadableStream.from(asyncIterator);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
blob() {
|
|
1790
|
+
const body = this.#body;
|
|
1791
|
+
return body ? Promise.resolve(body) : Promise.reject(new Error(`no body set`));
|
|
1792
|
+
}
|
|
1793
|
+
async text() {
|
|
1794
|
+
const blob = await this.blob();
|
|
1795
|
+
return await blob.text();
|
|
1796
|
+
}
|
|
1797
|
+
async formData() {
|
|
1798
|
+
const blob = await this.blob();
|
|
1799
|
+
const text = await blob.text();
|
|
1800
|
+
return new URLSearchParams(text);
|
|
1801
|
+
}
|
|
1802
|
+
async json() {
|
|
1803
|
+
const blob = await this.blob();
|
|
1804
|
+
if (blob.size === 0) {
|
|
1805
|
+
return void 0;
|
|
1806
|
+
}
|
|
1807
|
+
const text = await blob.text();
|
|
1808
|
+
return JSON.parse(text);
|
|
1809
|
+
}
|
|
1810
|
+
async writeBody(body) {
|
|
1811
|
+
if (body === void 0) {
|
|
1812
|
+
this.#body = new Blob([]);
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
if (body instanceof ReadableStream) {
|
|
1816
|
+
const chunks = [];
|
|
1817
|
+
const reader = body.getReader();
|
|
1818
|
+
let done = false;
|
|
1819
|
+
while (!done) {
|
|
1820
|
+
const { value, done: readDone } = await reader.read();
|
|
1821
|
+
if (readDone) {
|
|
1822
|
+
done = true;
|
|
1823
|
+
} else {
|
|
1824
|
+
chunks.push(value);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
this.#body = new Blob(chunks);
|
|
1828
|
+
} else {
|
|
1829
|
+
body = await body;
|
|
1830
|
+
if (typeof body === "string") {
|
|
1831
|
+
this.#body = new Blob([body], { type: "text/plain" });
|
|
1832
|
+
} else {
|
|
1833
|
+
this.#body = new Blob([body]);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
if (!this.headers.has("content-type")) {
|
|
1837
|
+
this.setHeader("Content-Type", this.#body.type || "application/octet-stream");
|
|
1336
1838
|
}
|
|
1337
1839
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
if (requestHeaders === void 0) {
|
|
1341
|
-
return;
|
|
1840
|
+
get URL() {
|
|
1841
|
+
return new URL(this.path + this.#url.search + this.#url.hash, `${this.protocol}://${this.host}`);
|
|
1342
1842
|
}
|
|
1343
|
-
|
|
1344
|
-
|
|
1843
|
+
addHeader(name, value) {
|
|
1844
|
+
this.headers.add(name, value);
|
|
1845
|
+
return this;
|
|
1345
1846
|
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
return;
|
|
1847
|
+
setHeader(name, value) {
|
|
1848
|
+
this.headers.set(name, value);
|
|
1849
|
+
return this;
|
|
1349
1850
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1851
|
+
};
|
|
1852
|
+
var MockServerHttpResponse = class extends AbstractServerHttpResponse {
|
|
1853
|
+
#writeHandler;
|
|
1854
|
+
#body = () => {
|
|
1855
|
+
throw new Error("No content was written to the response body nor end was called on this response.");
|
|
1856
|
+
};
|
|
1857
|
+
constructor() {
|
|
1858
|
+
super(new MapHttpHeaders());
|
|
1859
|
+
this.#writeHandler = async (body) => {
|
|
1860
|
+
const chunks = [];
|
|
1861
|
+
let bodyStream;
|
|
1862
|
+
this.#body = () => {
|
|
1863
|
+
bodyStream ??= import_web.default.ReadableStream.from(chunks);
|
|
1864
|
+
return bodyStream;
|
|
1865
|
+
};
|
|
1866
|
+
const reader = body.getReader();
|
|
1867
|
+
let done = false;
|
|
1868
|
+
do {
|
|
1869
|
+
const { value, done: readDone } = await reader.read();
|
|
1870
|
+
if (readDone) {
|
|
1871
|
+
done = true;
|
|
1872
|
+
} else {
|
|
1873
|
+
chunks.push(value);
|
|
1363
1874
|
}
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1875
|
+
} while (!done);
|
|
1876
|
+
return true;
|
|
1877
|
+
};
|
|
1366
1878
|
}
|
|
1367
|
-
|
|
1368
|
-
return
|
|
1879
|
+
get statusCode() {
|
|
1880
|
+
return super.statusCode;
|
|
1369
1881
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
return config;
|
|
1387
|
-
}
|
|
1882
|
+
set writeHandler(handler) {
|
|
1883
|
+
this.#body = () => {
|
|
1884
|
+
throw new Error("Not available with custom write handler");
|
|
1885
|
+
};
|
|
1886
|
+
this.#writeHandler = handler;
|
|
1887
|
+
}
|
|
1888
|
+
getNativeResponse() {
|
|
1889
|
+
throw new Error("This is a mock. No running server, no native response.");
|
|
1890
|
+
}
|
|
1891
|
+
applyStatusCode() {
|
|
1892
|
+
}
|
|
1893
|
+
applyHeaders() {
|
|
1894
|
+
}
|
|
1895
|
+
applyCookies() {
|
|
1896
|
+
for (const cookie of this.cookies) {
|
|
1897
|
+
this.headers.add("Set-Cookie", super.setCookieValue(cookie));
|
|
1388
1898
|
}
|
|
1389
|
-
}
|
|
1899
|
+
}
|
|
1900
|
+
bodyInternal(body) {
|
|
1901
|
+
const it = async function* () {
|
|
1902
|
+
const resolved = await body;
|
|
1903
|
+
if (resolved === void 0) {
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
yield resolved;
|
|
1907
|
+
}();
|
|
1908
|
+
return this.#writeHandler(import_web.default.ReadableStream.from(it));
|
|
1909
|
+
}
|
|
1910
|
+
async end() {
|
|
1911
|
+
return this.doCommit(async () => {
|
|
1912
|
+
return await new Promise((resolve, reject) => {
|
|
1913
|
+
this.#writeHandler(import_web.default.ReadableStream.from([]));
|
|
1914
|
+
});
|
|
1915
|
+
});
|
|
1916
|
+
}
|
|
1917
|
+
getBody() {
|
|
1918
|
+
return this.#body();
|
|
1919
|
+
}
|
|
1390
1920
|
};
|
|
1391
1921
|
|
|
1392
1922
|
// src/app/cors.ts
|
|
1393
1923
|
function mockUpgradeExchange(path) {
|
|
1394
|
-
const request = new
|
|
1395
|
-
request.
|
|
1924
|
+
const request = new MockServerHttpRequest(path, "GET");
|
|
1925
|
+
request.setHeader("Upgrade", "websocket");
|
|
1396
1926
|
request.upgrade = true;
|
|
1397
|
-
return new DefaultWebExchange(request, new
|
|
1927
|
+
return new DefaultWebExchange(request, new MockServerHttpResponse());
|
|
1398
1928
|
}
|
|
1399
1929
|
async function createCorsConfigSource(context) {
|
|
1400
1930
|
const { sockets: routes, cors } = context;
|
|
@@ -1411,8 +1941,8 @@ async function createCorsConfigSource(context) {
|
|
|
1411
1941
|
routeCorsConfig = combineCorsConfig(routeCorsConfig, {
|
|
1412
1942
|
allowOrigins: route.originFilters?.allow,
|
|
1413
1943
|
allowMethods: ["GET", "CONNECT"],
|
|
1414
|
-
allowHeaders: ["
|
|
1415
|
-
exposeHeaders: ["
|
|
1944
|
+
allowHeaders: ["Upgrade", "Connection", "Origin", "Sec-Websocket-Key", "Sec-Websocket-Version", "Sec-Websocket-Protocol", "Sec-Websocket-Extensions"],
|
|
1945
|
+
exposeHeaders: ["Sec-Websocket-Accept", "Sec-Websocket-Protocol", "Sec-Websocket-Extensions"],
|
|
1416
1946
|
allowCredentials: route.authorize?.access !== "permitted"
|
|
1417
1947
|
});
|
|
1418
1948
|
validatedConfigs.push([and([upgradeMatcher, pattern(path)]), validateCorsConfig(routeCorsConfig)]);
|
|
@@ -1655,7 +2185,7 @@ var httpBasicEntryPoint = (opts) => {
|
|
|
1655
2185
|
const headerValue = createHeaderValue(opts?.realm ?? DEFAULT_REALM);
|
|
1656
2186
|
return async (exchange, _error) => {
|
|
1657
2187
|
const { response } = exchange;
|
|
1658
|
-
response.
|
|
2188
|
+
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
|
1659
2189
|
response.headers.set("WWW-Authenticate", headerValue);
|
|
1660
2190
|
};
|
|
1661
2191
|
};
|
|
@@ -1675,12 +2205,17 @@ var httpBasicAuthenticationConverter = (opts) => {
|
|
|
1675
2205
|
if (parts.length !== 2) {
|
|
1676
2206
|
return void 0;
|
|
1677
2207
|
}
|
|
1678
|
-
return {
|
|
2208
|
+
return {
|
|
2209
|
+
type: "UsernamePassword",
|
|
2210
|
+
authenticated: false,
|
|
2211
|
+
principal: parts[0],
|
|
2212
|
+
credentials: parts[1]
|
|
2213
|
+
};
|
|
1679
2214
|
};
|
|
1680
2215
|
};
|
|
1681
2216
|
|
|
1682
2217
|
// src/server/security/security-context.ts
|
|
1683
|
-
var
|
|
2218
|
+
var import_node_async_hooks2 = require("node:async_hooks");
|
|
1684
2219
|
var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder {
|
|
1685
2220
|
static hasSecurityContext(storage) {
|
|
1686
2221
|
return storage.getStore()?.securityContext !== void 0;
|
|
@@ -1692,7 +2227,7 @@ var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder
|
|
|
1692
2227
|
delete storage.getStore()?.securityContext;
|
|
1693
2228
|
}
|
|
1694
2229
|
static withSecurityContext(securityContext) {
|
|
1695
|
-
return (storage = new
|
|
2230
|
+
return (storage = new import_node_async_hooks2.AsyncLocalStorage()) => {
|
|
1696
2231
|
storage.getStore().securityContext = securityContext;
|
|
1697
2232
|
return storage;
|
|
1698
2233
|
};
|
|
@@ -1769,8 +2304,7 @@ function authenticationFilter(opts) {
|
|
|
1769
2304
|
var httpStatusEntryPoint = (opts) => {
|
|
1770
2305
|
return async (exchange, _error) => {
|
|
1771
2306
|
const response = exchange.response;
|
|
1772
|
-
response.
|
|
1773
|
-
response.statusMessage = opts.httpStatus.message;
|
|
2307
|
+
response.setStatusCode(opts.httpStatus);
|
|
1774
2308
|
};
|
|
1775
2309
|
};
|
|
1776
2310
|
|
|
@@ -1778,7 +2312,7 @@ var httpStatusEntryPoint = (opts) => {
|
|
|
1778
2312
|
var logger2 = getLogger("auth.entry-point");
|
|
1779
2313
|
var delegatingEntryPoint = (opts) => {
|
|
1780
2314
|
const defaultEntryPoint = opts.defaultEntryPoint ?? (async ({ response }, _error) => {
|
|
1781
|
-
response.
|
|
2315
|
+
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
|
1782
2316
|
await response.end();
|
|
1783
2317
|
});
|
|
1784
2318
|
return async (exchange, error) => {
|
|
@@ -1816,12 +2350,12 @@ function httpBasic(opts) {
|
|
|
1816
2350
|
const headers2 = exchange.request.headers;
|
|
1817
2351
|
const h = headers2.list("X-Requested-With");
|
|
1818
2352
|
if (h.includes("XMLHttpRequest")) {
|
|
1819
|
-
return
|
|
2353
|
+
return match();
|
|
1820
2354
|
}
|
|
1821
|
-
return
|
|
2355
|
+
return NO_MATCH;
|
|
1822
2356
|
};
|
|
1823
2357
|
const defaultEntryPoint = delegatingEntryPoint({
|
|
1824
|
-
entryPoints: [[xhrMatcher, httpStatusEntryPoint({ httpStatus:
|
|
2358
|
+
entryPoints: [[xhrMatcher, httpStatusEntryPoint({ httpStatus: HttpStatus.UNAUTHORIZED })]],
|
|
1825
2359
|
defaultEntryPoint: httpBasicEntryPoint({})
|
|
1826
2360
|
});
|
|
1827
2361
|
const entryPoint = opts.entryPoint ?? defaultEntryPoint;
|
|
@@ -1864,7 +2398,7 @@ var DEFAULT_URI = "https://tools.ietf.org/html/rfc6750#section-3.1";
|
|
|
1864
2398
|
function invalidToken(message) {
|
|
1865
2399
|
return {
|
|
1866
2400
|
errorCode: BearerTokenErrorCodes.invalid_token,
|
|
1867
|
-
httpStatus:
|
|
2401
|
+
httpStatus: HttpStatus.UNAUTHORIZED,
|
|
1868
2402
|
description: message,
|
|
1869
2403
|
uri: DEFAULT_URI
|
|
1870
2404
|
};
|
|
@@ -1872,7 +2406,7 @@ function invalidToken(message) {
|
|
|
1872
2406
|
function invalidRequest(message) {
|
|
1873
2407
|
return {
|
|
1874
2408
|
errorCode: BearerTokenErrorCodes.invalid_request,
|
|
1875
|
-
httpStatus:
|
|
2409
|
+
httpStatus: HttpStatus.BAD_REQUEST,
|
|
1876
2410
|
description: message,
|
|
1877
2411
|
uri: DEFAULT_URI
|
|
1878
2412
|
};
|
|
@@ -1948,7 +2482,10 @@ async function resolveFromBody(exchange, allow = false) {
|
|
|
1948
2482
|
if (!allow || "application/x-www-form-urlencoded" !== request.headers.one("content-type") || request.method !== "POST") {
|
|
1949
2483
|
return;
|
|
1950
2484
|
}
|
|
1951
|
-
|
|
2485
|
+
const parameters = await exchange.request.formData();
|
|
2486
|
+
if (parameters) {
|
|
2487
|
+
return resolveTokens(parameters);
|
|
2488
|
+
}
|
|
1952
2489
|
}
|
|
1953
2490
|
var token_converter_default = serverBearerTokenAuthenticationConverter;
|
|
1954
2491
|
|
|
@@ -1978,7 +2515,7 @@ function getStatus(authError) {
|
|
|
1978
2515
|
return error.httpStatus;
|
|
1979
2516
|
}
|
|
1980
2517
|
}
|
|
1981
|
-
return
|
|
2518
|
+
return HttpStatus.UNAUTHORIZED;
|
|
1982
2519
|
}
|
|
1983
2520
|
function createParameters(authError, realm) {
|
|
1984
2521
|
const parameters = /* @__PURE__ */ new Map();
|
|
@@ -2007,7 +2544,7 @@ var bearerTokenServerAuthenticationEntryPoint = (opts) => {
|
|
|
2007
2544
|
const wwwAuthenticate = computeWWWAuthenticate(parameters);
|
|
2008
2545
|
const { response } = exchange;
|
|
2009
2546
|
response.headers.set("WWW-Authenticate", wwwAuthenticate);
|
|
2010
|
-
response.
|
|
2547
|
+
response.setStatusCode(status);
|
|
2011
2548
|
await response.end();
|
|
2012
2549
|
};
|
|
2013
2550
|
};
|
|
@@ -2094,18 +2631,17 @@ async function commenceAuthentication(exchange, authentication, entryPoint) {
|
|
|
2094
2631
|
}
|
|
2095
2632
|
await entryPoint(exchange, e);
|
|
2096
2633
|
}
|
|
2097
|
-
function httpStatusAccessDeniedHandler(
|
|
2634
|
+
function httpStatusAccessDeniedHandler(httpStatus) {
|
|
2098
2635
|
return async (exchange, _error) => {
|
|
2099
|
-
exchange.response.
|
|
2100
|
-
exchange.response.
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
exchange.response.
|
|
2104
|
-
await exchange.response.end(bytes);
|
|
2636
|
+
exchange.response.setStatusCode(httpStatus);
|
|
2637
|
+
exchange.response.headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
2638
|
+
const buffer = Buffer.from("Access Denied", "utf-8");
|
|
2639
|
+
exchange.response.headers.set("Content-Length", buffer.length);
|
|
2640
|
+
await exchange.response.body(buffer);
|
|
2105
2641
|
};
|
|
2106
2642
|
}
|
|
2107
2643
|
var errorFilter = (opts) => {
|
|
2108
|
-
const accessDeniedHandler = httpStatusAccessDeniedHandler(
|
|
2644
|
+
const accessDeniedHandler = httpStatusAccessDeniedHandler(HttpStatus.FORBIDDEN);
|
|
2109
2645
|
const authenticationEntryPoint = opts.authenticationEntryPoint ?? httpBasicEntryPoint();
|
|
2110
2646
|
return async (exchange, next) => {
|
|
2111
2647
|
try {
|
|
@@ -2117,17 +2653,19 @@ var errorFilter = (opts) => {
|
|
|
2117
2653
|
await commenceAuthentication(exchange, void 0, authenticationEntryPoint);
|
|
2118
2654
|
} else {
|
|
2119
2655
|
if (!principal.authenticated) {
|
|
2120
|
-
|
|
2656
|
+
await accessDeniedHandler(exchange, error);
|
|
2121
2657
|
}
|
|
2122
2658
|
await commenceAuthentication(exchange, principal, authenticationEntryPoint);
|
|
2123
2659
|
}
|
|
2660
|
+
return;
|
|
2124
2661
|
}
|
|
2662
|
+
throw error;
|
|
2125
2663
|
}
|
|
2126
2664
|
};
|
|
2127
2665
|
};
|
|
2128
2666
|
|
|
2129
2667
|
// src/server/security/delegating-authorization-manager.ts
|
|
2130
|
-
var logger3 = getLogger("auth");
|
|
2668
|
+
var logger3 = getLogger("security.auth");
|
|
2131
2669
|
function delegatingAuthorizationManager(opts) {
|
|
2132
2670
|
const check = async (authentication, exchange) => {
|
|
2133
2671
|
let decision;
|
|
@@ -2170,6 +2708,25 @@ function authorizationFilter(opts) {
|
|
|
2170
2708
|
};
|
|
2171
2709
|
}
|
|
2172
2710
|
|
|
2711
|
+
// src/server/security/exchange-filter.ts
|
|
2712
|
+
var SecurityContextServerWebExchange = class extends ServerWebExchangeDecorator {
|
|
2713
|
+
#context;
|
|
2714
|
+
constructor(exchange, context) {
|
|
2715
|
+
super(exchange);
|
|
2716
|
+
this.#context = context;
|
|
2717
|
+
}
|
|
2718
|
+
async principal() {
|
|
2719
|
+
const context = await this.#context();
|
|
2720
|
+
return context?.authentication;
|
|
2721
|
+
}
|
|
2722
|
+
};
|
|
2723
|
+
var exchangeFilter = (opts) => {
|
|
2724
|
+
const storage = opts.storage;
|
|
2725
|
+
return async (exchange, next) => {
|
|
2726
|
+
await next(new SecurityContextServerWebExchange(exchange, async () => await AsyncStorageSecurityContextHolder.getContext(storage)));
|
|
2727
|
+
};
|
|
2728
|
+
};
|
|
2729
|
+
|
|
2173
2730
|
// src/server/security/config.ts
|
|
2174
2731
|
var filterOrder = {
|
|
2175
2732
|
first: Number.MAX_SAFE_INTEGER,
|
|
@@ -2178,6 +2735,7 @@ var filterOrder = {
|
|
|
2178
2735
|
cors: 3 * 100,
|
|
2179
2736
|
http_basic: 6 * 100,
|
|
2180
2737
|
authentication: 8 * 100,
|
|
2738
|
+
security_context_server_web_exchange: 15 * 100,
|
|
2181
2739
|
error_translation: 18 * 100,
|
|
2182
2740
|
authorization: 19 * 100,
|
|
2183
2741
|
last: Number.MAX_SAFE_INTEGER
|
|
@@ -2264,9 +2822,12 @@ var config_default = (config, context) => {
|
|
|
2264
2822
|
const authenticationConverterMatcher = async (exchange) => {
|
|
2265
2823
|
try {
|
|
2266
2824
|
const a = await authenticationConverter(exchange);
|
|
2267
|
-
|
|
2825
|
+
if (a === void 0) {
|
|
2826
|
+
return NO_MATCH;
|
|
2827
|
+
}
|
|
2828
|
+
return match();
|
|
2268
2829
|
} catch (e) {
|
|
2269
|
-
return
|
|
2830
|
+
return NO_MATCH;
|
|
2270
2831
|
}
|
|
2271
2832
|
};
|
|
2272
2833
|
const entryPoint = token_entry_point_default({});
|
|
@@ -2280,6 +2841,9 @@ var config_default = (config, context) => {
|
|
|
2280
2841
|
filter[filterOrderSymbol] = filterOrder.authentication;
|
|
2281
2842
|
middleware.push(filter);
|
|
2282
2843
|
}
|
|
2844
|
+
const exchangeF = exchangeFilter({ storage: context.storage });
|
|
2845
|
+
middleware.push(exchangeF);
|
|
2846
|
+
exchangeF[filterOrderSymbol] = filterOrder.security_context_server_web_exchange;
|
|
2283
2847
|
if (config.authorize !== void 0) {
|
|
2284
2848
|
const errorFf = errorFilter({ authenticationEntryPoint: this.authenticationEntryPoint });
|
|
2285
2849
|
errorFf[filterOrderSymbol] = filterOrder.error_translation;
|
|
@@ -2373,121 +2937,341 @@ async function httpSecurity(context) {
|
|
|
2373
2937
|
return config_default(config, { storage, corsConfigSource });
|
|
2374
2938
|
}
|
|
2375
2939
|
|
|
2376
|
-
// src/server.ts
|
|
2377
|
-
var
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
logger5.warn(`${info2} dropping ws connection request from ${host} on ${path}. max connections exceeded.`);
|
|
2406
|
-
socket.destroy();
|
|
2407
|
-
return;
|
|
2408
|
-
}
|
|
2409
|
-
const origin = request.headers.one("origin");
|
|
2410
|
-
if (!acceptsOrigin(origin, route.originFilters)) {
|
|
2411
|
-
logger5.info(`${info2} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
|
|
2412
|
-
socket.destroy();
|
|
2413
|
-
return;
|
|
2414
|
-
}
|
|
2415
|
-
if (logger5.enabledFor("debug")) {
|
|
2416
|
-
logger5.debug(`${info2} accepted new ws connection request from ${host} on ${path}`);
|
|
2417
|
-
}
|
|
2418
|
-
const upgradeHead = response.unsafeServerResponse.upgradeHead;
|
|
2419
|
-
wss.handleUpgrade(request.unsafeIncomingMessage, socket, upgradeHead, (ws2) => {
|
|
2420
|
-
response.unsafeServerResponse.markHeadersSent();
|
|
2421
|
-
ws2.on("pong", () => ws2["connected"] = true);
|
|
2422
|
-
ws2.on("ping", () => {
|
|
2423
|
-
});
|
|
2424
|
-
wss.emit("connection", ws2, request.unsafeIncomingMessage);
|
|
2425
|
-
});
|
|
2426
|
-
} else {
|
|
2427
|
-
logger5.warn(`${info2} rejected upgrade request from ${host} on ${path}`);
|
|
2428
|
-
socket.destroy();
|
|
2429
|
-
}
|
|
2430
|
-
} else {
|
|
2431
|
-
if (route.default) {
|
|
2432
|
-
await next();
|
|
2433
|
-
return;
|
|
2434
|
-
}
|
|
2435
|
-
if (logger5.enabledFor("debug")) {
|
|
2436
|
-
logger5.debug(`rejecting request for ${path} with method ${request.method} from ${request.socket.remoteAddress}:${request.socket.remotePort} with headers: ${JSON.stringify(request.headers)}`);
|
|
2437
|
-
}
|
|
2438
|
-
response.statusCode = 426;
|
|
2439
|
-
response.headers.set("Upgrade", "websocket").set("Connection", "Upgrade").set("Content-Type", "text/plain");
|
|
2440
|
-
await response.end(`This service [${request.path}] requires use of the websocket protocol.`);
|
|
2441
|
-
}
|
|
2940
|
+
// src/server/handler.ts
|
|
2941
|
+
var import_node_async_hooks3 = require("node:async_hooks");
|
|
2942
|
+
var HttpHeadResponseDecorator = class extends ServerHttpResponseDecorator {
|
|
2943
|
+
};
|
|
2944
|
+
var HandlerAdapter = class {
|
|
2945
|
+
#logger;
|
|
2946
|
+
#enableLoggingRequestDetails = false;
|
|
2947
|
+
#delegate;
|
|
2948
|
+
#storage;
|
|
2949
|
+
constructor(logger7, delegate) {
|
|
2950
|
+
this.#logger = logger7;
|
|
2951
|
+
this.#delegate = delegate;
|
|
2952
|
+
}
|
|
2953
|
+
createExchange(request, response) {
|
|
2954
|
+
const exchange = new DefaultWebExchange(request, response);
|
|
2955
|
+
return exchange;
|
|
2956
|
+
}
|
|
2957
|
+
set storage(storage) {
|
|
2958
|
+
this.#storage = storage;
|
|
2959
|
+
}
|
|
2960
|
+
set enableLoggingRequestDetails(value) {
|
|
2961
|
+
this.#enableLoggingRequestDetails = value;
|
|
2962
|
+
}
|
|
2963
|
+
formatHeaders(headers2) {
|
|
2964
|
+
let result = "{";
|
|
2965
|
+
for (const key of headers2.keys()) {
|
|
2966
|
+
if (!this.#enableLoggingRequestDetails) {
|
|
2967
|
+
result += "masked, ";
|
|
2968
|
+
break;
|
|
2442
2969
|
} else {
|
|
2443
|
-
|
|
2970
|
+
const value = headers2.get(key);
|
|
2971
|
+
result += `"${key}": "${value}", `;
|
|
2444
2972
|
}
|
|
2445
|
-
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2973
|
+
}
|
|
2974
|
+
if (result.endsWith(", ")) {
|
|
2975
|
+
result = result.slice(0, -2);
|
|
2976
|
+
}
|
|
2977
|
+
result += "}";
|
|
2978
|
+
return result;
|
|
2979
|
+
}
|
|
2980
|
+
formatRequest(request) {
|
|
2981
|
+
const query = request.URL.search;
|
|
2982
|
+
return `HTTP ${request.method} "${request.path}${query}`;
|
|
2983
|
+
}
|
|
2984
|
+
logRequest(exchange) {
|
|
2985
|
+
if (this.#logger.enabledFor("debug")) {
|
|
2986
|
+
const trace = this.#logger.enabledFor("trace");
|
|
2987
|
+
this.#logger.debug(`${exchange.logPrefix}${this.formatRequest(exchange.request)}${trace ? `, headers: ${this.formatHeaders(exchange.request.headers)}` : ""}"`);
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
logResponse(exchange) {
|
|
2991
|
+
if (this.#logger.enabledFor("debug")) {
|
|
2992
|
+
const trace = this.#logger.enabledFor("trace");
|
|
2993
|
+
const status = exchange.response.statusCode;
|
|
2994
|
+
this.#logger.debug(`${exchange.logPrefix}Completed ${status ?? "200 OK"}${trace ? `, headers: ${this.formatHeaders(exchange.response.headers)}` : ""}"`);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
handleUnresolvedError(exchange, error) {
|
|
2998
|
+
const { request, response, logPrefix } = exchange;
|
|
2999
|
+
if (response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR)) {
|
|
3000
|
+
this.#logger.error(`${logPrefix}500 Server Error for ${this.formatRequest(request)}`, error);
|
|
3001
|
+
return;
|
|
3002
|
+
}
|
|
3003
|
+
this.#logger.error(`${logPrefix}Error [${error.message} for ${this.formatRequest(request)}, but already ended (${response.statusCode})`, error);
|
|
3004
|
+
throw error;
|
|
3005
|
+
}
|
|
3006
|
+
async web(exchange) {
|
|
3007
|
+
return await this.#delegate(exchange);
|
|
3008
|
+
}
|
|
3009
|
+
async http(request, response) {
|
|
3010
|
+
const exchange = this.createExchange(request, response);
|
|
3011
|
+
const callback = () => {
|
|
3012
|
+
this.logRequest(exchange);
|
|
3013
|
+
return this.web(exchange).then(() => {
|
|
3014
|
+
this.logResponse(exchange);
|
|
3015
|
+
}).catch((error) => {
|
|
3016
|
+
this.handleUnresolvedError(exchange, error);
|
|
3017
|
+
}).then(async () => {
|
|
3018
|
+
await exchange.response.end();
|
|
3019
|
+
});
|
|
3020
|
+
};
|
|
3021
|
+
await new Promise((resolve, reject) => {
|
|
3022
|
+
if (this.#storage !== void 0) {
|
|
3023
|
+
this.#storage.run({ exchange }, () => {
|
|
3024
|
+
callback().then(() => resolve()).catch((error) => reject(error));
|
|
3025
|
+
});
|
|
2452
3026
|
} else {
|
|
2453
|
-
|
|
3027
|
+
callback().then(() => resolve()).catch((error) => reject(error));
|
|
2454
3028
|
}
|
|
2455
|
-
}
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
};
|
|
3032
|
+
var WebHttpHandlerBuilder = class {
|
|
3033
|
+
#webHandler;
|
|
3034
|
+
#storage = new import_node_async_hooks3.AsyncLocalStorage();
|
|
3035
|
+
#handlerDecorator;
|
|
3036
|
+
storage(storage) {
|
|
3037
|
+
this.#storage = storage;
|
|
3038
|
+
return this;
|
|
3039
|
+
}
|
|
3040
|
+
httpHandlerDecorator(decorator) {
|
|
3041
|
+
if (this.#handlerDecorator === void 0) {
|
|
3042
|
+
this.#handlerDecorator = decorator;
|
|
3043
|
+
} else {
|
|
3044
|
+
const previousDecorator = this.#handlerDecorator;
|
|
3045
|
+
this.#handlerDecorator = (handler) => {
|
|
3046
|
+
handler = previousDecorator(handler);
|
|
3047
|
+
return decorator(handler);
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
return this;
|
|
3051
|
+
}
|
|
3052
|
+
constructor(webHandler) {
|
|
3053
|
+
this.#webHandler = webHandler;
|
|
3054
|
+
}
|
|
3055
|
+
build() {
|
|
3056
|
+
const logger7 = getLogger("http");
|
|
3057
|
+
const adapter = new HandlerAdapter(logger7, this.#webHandler);
|
|
3058
|
+
if (this.#storage !== void 0) adapter.storage = this.#storage;
|
|
3059
|
+
adapter.enableLoggingRequestDetails = false;
|
|
3060
|
+
const adapted = async (request, response) => adapter.http(request, response);
|
|
3061
|
+
return this.#handlerDecorator ? this.#handlerDecorator(adapted) : adapted;
|
|
3062
|
+
}
|
|
3063
|
+
};
|
|
3064
|
+
|
|
3065
|
+
// src/server/ws.ts
|
|
3066
|
+
var import_ws = require("ws");
|
|
3067
|
+
|
|
3068
|
+
// src/server/socket.ts
|
|
3069
|
+
function createHandshakeInfo(req, protocol) {
|
|
3070
|
+
const exchange = req?.exchange;
|
|
3071
|
+
const request = exchange?.request ?? new HttpServerRequest(req);
|
|
3072
|
+
const principalPromiseProvider = exchange?.principal;
|
|
3073
|
+
const principal = principalPromiseProvider ? principalPromiseProvider.bind(exchange) : async function principal2() {
|
|
3074
|
+
return void 0;
|
|
3075
|
+
};
|
|
3076
|
+
const url = request.URL;
|
|
3077
|
+
const headers2 = new MapHttpHeaders();
|
|
3078
|
+
for (const key of request.headers.keys()) {
|
|
3079
|
+
headers2.set(key, request.headers.list(key));
|
|
3080
|
+
}
|
|
3081
|
+
const cookies = request.cookies;
|
|
3082
|
+
const logPrefix = exchange?.logPrefix ?? `[${request.id}] `;
|
|
3083
|
+
const remoteAddress = request.remoteAddress;
|
|
3084
|
+
const handshake = {
|
|
3085
|
+
url,
|
|
3086
|
+
headers: headers2,
|
|
3087
|
+
cookies,
|
|
3088
|
+
principal,
|
|
3089
|
+
protocol,
|
|
3090
|
+
remoteAddress,
|
|
3091
|
+
logPrefix
|
|
3092
|
+
};
|
|
3093
|
+
return handshake;
|
|
3094
|
+
}
|
|
3095
|
+
function webSockets(context) {
|
|
3096
|
+
const sockets = async (exchange, next) => {
|
|
3097
|
+
const request = exchange.request;
|
|
3098
|
+
const path = request.path ?? "/";
|
|
3099
|
+
const routes = context.sockets;
|
|
3100
|
+
const route = routes.get(path) ?? Array.from(routes.values()).find((route2) => {
|
|
3101
|
+
if (path === "/" && route2.default === true) {
|
|
3102
|
+
return true;
|
|
3103
|
+
}
|
|
3104
|
+
});
|
|
3105
|
+
if (route !== void 0) {
|
|
3106
|
+
const { request: request2, response } = exchange;
|
|
3107
|
+
const upgradeMatchResult = await upgradeMatcher(exchange);
|
|
3108
|
+
if ((request2.method === "GET" || request2.method === "CONNECT") && upgradeMatchResult.match) {
|
|
3109
|
+
if (route.upgradeStrategy !== void 0) {
|
|
3110
|
+
route.upgradeStrategy(exchange);
|
|
3111
|
+
return;
|
|
3112
|
+
} else {
|
|
3113
|
+
throw new Error(`No upgrade strategy defined for route on ${path}`);
|
|
3114
|
+
}
|
|
2460
3115
|
} else {
|
|
2461
|
-
|
|
3116
|
+
if (route.default) {
|
|
3117
|
+
await next();
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
3120
|
+
response.setStatusCode(HttpStatus.UPGRADE_REQUIRED);
|
|
3121
|
+
response.headers.set("Upgrade", "websocket").set("Connection", "Upgrade").set("Content-Type", "text/plain");
|
|
3122
|
+
const buffer = Buffer.from(`This service [${request2.path}] requires use of the websocket protocol.`, "utf-8");
|
|
3123
|
+
await response.body(buffer);
|
|
2462
3124
|
}
|
|
2463
|
-
}
|
|
2464
|
-
|
|
2465
|
-
async ({ response }, _next) => {
|
|
2466
|
-
response.statusCode = 404;
|
|
2467
|
-
await response.end(import_node_http2.default.STATUS_CODES[404]);
|
|
3125
|
+
} else {
|
|
3126
|
+
await next();
|
|
2468
3127
|
}
|
|
2469
|
-
|
|
2470
|
-
return
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
3128
|
+
};
|
|
3129
|
+
return [sockets];
|
|
3130
|
+
}
|
|
3131
|
+
|
|
3132
|
+
// src/server/ws.ts
|
|
3133
|
+
var ExtendedWebSocket = class extends import_ws.WebSocket {
|
|
3134
|
+
constructor(first, second, options) {
|
|
3135
|
+
super(null, void 0, options);
|
|
3136
|
+
}
|
|
3137
|
+
connected;
|
|
3138
|
+
};
|
|
3139
|
+
var logger5 = getLogger("ws");
|
|
3140
|
+
function upgradeStrategy(path, route, wss, onSocketError) {
|
|
3141
|
+
return (exchange) => {
|
|
3142
|
+
const { logPrefix, request } = exchange;
|
|
3143
|
+
const req = ServerHttpRequestDecorator.getNativeRequest(request);
|
|
3144
|
+
req.exchange = exchange;
|
|
3145
|
+
const { socket, upgradeHead } = req;
|
|
3146
|
+
const host = request.host;
|
|
3147
|
+
socket.removeListener("error", onSocketError);
|
|
3148
|
+
if (route.maxConnections !== void 0 && wss.clients?.size >= route.maxConnections) {
|
|
3149
|
+
logger5.warn(`${logPrefix}dropping ws connection request on ${host}${path}. max connections exceeded.`);
|
|
3150
|
+
socket.destroy();
|
|
3151
|
+
return;
|
|
3152
|
+
}
|
|
3153
|
+
const origin = request.headers.one("origin");
|
|
3154
|
+
if (!acceptsOrigin(origin, route.originFilters)) {
|
|
3155
|
+
if (logger5.enabledFor("info")) {
|
|
3156
|
+
logger5.info(`${logPrefix}dropping ws connection request on ${host}${path}. origin ${origin ?? "<missing>"}`);
|
|
2480
3157
|
}
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
3158
|
+
socket.destroy();
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
if (logger5.enabledFor("debug")) {
|
|
3162
|
+
logger5.debug(`${logPrefix}accepted new ws connection request on ${host}${path}`);
|
|
3163
|
+
}
|
|
3164
|
+
wss.handleUpgrade(req, socket, upgradeHead, (client, req2) => {
|
|
3165
|
+
wss.emit("connection", client, req2);
|
|
3166
|
+
});
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3169
|
+
function applyHandshakeHeaders(headers2, response) {
|
|
3170
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3171
|
+
headers2.forEach((header, index) => {
|
|
3172
|
+
if (index === 0 && header.startsWith("HTTP/1.1 101 ")) {
|
|
3173
|
+
response.setStatusCode(HttpStatus.SWITCHING_PROTOCOLS);
|
|
3174
|
+
return;
|
|
3175
|
+
}
|
|
3176
|
+
const [name, value] = header.split(": ");
|
|
3177
|
+
if (response.headers.has(name)) {
|
|
3178
|
+
headers2[index] = `${name}: ${response.headers.one(name)}`;
|
|
3179
|
+
} else {
|
|
3180
|
+
response.headers.set(name, value);
|
|
3181
|
+
}
|
|
3182
|
+
seen.add(name.toLowerCase());
|
|
3183
|
+
});
|
|
3184
|
+
const nativeResponse = ServerHttpResponseDecorator.getNativeResponse(response);
|
|
3185
|
+
for (const name of nativeResponse.getRawHeaderNames()) {
|
|
3186
|
+
const nameLowerCase = name.toLowerCase();
|
|
3187
|
+
if (!seen.has(nameLowerCase)) {
|
|
3188
|
+
const value = response.headers.get(nameLowerCase);
|
|
3189
|
+
if (value !== void 0) {
|
|
3190
|
+
headers2.push(`${name}: ${value}`);
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
nativeResponse.markHeadersSent();
|
|
3195
|
+
}
|
|
3196
|
+
async function initRoute(path, route, endpoint, storage, onSocketError) {
|
|
3197
|
+
try {
|
|
3198
|
+
logger5.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
|
|
3199
|
+
const wss = new import_ws.WebSocketServer({
|
|
3200
|
+
noServer: true,
|
|
3201
|
+
WebSocket: ExtendedWebSocket,
|
|
3202
|
+
autoPong: false
|
|
3203
|
+
});
|
|
3204
|
+
const handler = await route.factory({ endpoint, storage });
|
|
3205
|
+
wss.on("error", (err) => {
|
|
3206
|
+
logger5.error(`error starting the ws server for [${path}]`, err);
|
|
3207
|
+
}).on("listening", () => {
|
|
3208
|
+
logger5.info(`ws server for [${path}] is listening`);
|
|
3209
|
+
}).on("headers", (headers2, request) => {
|
|
3210
|
+
if (request.exchange !== void 0) {
|
|
3211
|
+
const { response } = request.exchange;
|
|
3212
|
+
applyHandshakeHeaders(headers2, response);
|
|
2489
3213
|
}
|
|
3214
|
+
}).on("connection", (socket, request) => {
|
|
3215
|
+
const handshake = createHandshakeInfo(request, socket.protocol);
|
|
3216
|
+
socket.on("pong", () => socket.connected = true);
|
|
3217
|
+
socket.on("ping", () => {
|
|
3218
|
+
});
|
|
3219
|
+
handler({ socket, handshake });
|
|
2490
3220
|
});
|
|
3221
|
+
const pingInterval = route.ping;
|
|
3222
|
+
if (pingInterval) {
|
|
3223
|
+
const pingIntervalId = setInterval(() => {
|
|
3224
|
+
for (const client of wss.clients) {
|
|
3225
|
+
if (client.connected === false) {
|
|
3226
|
+
client.terminate();
|
|
3227
|
+
}
|
|
3228
|
+
client.connected = false;
|
|
3229
|
+
client.ping();
|
|
3230
|
+
}
|
|
3231
|
+
}, pingInterval);
|
|
3232
|
+
wss.on("close", () => {
|
|
3233
|
+
clearInterval(pingIntervalId);
|
|
3234
|
+
});
|
|
3235
|
+
}
|
|
3236
|
+
route.upgradeStrategy = upgradeStrategy(path, route, wss, onSocketError);
|
|
3237
|
+
route.close = async () => {
|
|
3238
|
+
await handler.close?.call(handler);
|
|
3239
|
+
logger5.info(`stopping ws server for [${path}]. clients: ${wss.clients?.size ?? 0}`);
|
|
3240
|
+
wss.clients?.forEach((client) => {
|
|
3241
|
+
client.terminate();
|
|
3242
|
+
});
|
|
3243
|
+
wss.close();
|
|
3244
|
+
};
|
|
3245
|
+
} catch (e) {
|
|
3246
|
+
logger5.warn(`failed to init route ${path}`, e);
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3250
|
+
// src/server.ts
|
|
3251
|
+
var logger6 = getLogger("app");
|
|
3252
|
+
function secureContextOptions(ssl) {
|
|
3253
|
+
const options = {};
|
|
3254
|
+
if (ssl.key) options.key = (0, import_node_fs.readFileSync)(ssl.key);
|
|
3255
|
+
if (ssl.cert) options.cert = (0, import_node_fs.readFileSync)(ssl.cert);
|
|
3256
|
+
if (ssl.ca) options.ca = (0, import_node_fs.readFileSync)(ssl.ca);
|
|
3257
|
+
return options;
|
|
3258
|
+
}
|
|
3259
|
+
async function createListener(builder, onSocketError) {
|
|
3260
|
+
const httpHandler = builder.build();
|
|
3261
|
+
return async (req, resOrUpgradeHead) => {
|
|
3262
|
+
req.socket.addListener("error", onSocketError);
|
|
3263
|
+
let res;
|
|
3264
|
+
if (resOrUpgradeHead instanceof ExtendedHttpServerResponse) {
|
|
3265
|
+
res = resOrUpgradeHead;
|
|
3266
|
+
} else {
|
|
3267
|
+
req.upgradeHead = resOrUpgradeHead;
|
|
3268
|
+
res = new ExtendedHttpServerResponse(req);
|
|
3269
|
+
res.assignSocket(req.socket);
|
|
3270
|
+
}
|
|
3271
|
+
const request = new HttpServerRequest(req);
|
|
3272
|
+
const response = new HttpServerResponse(res);
|
|
3273
|
+
const decoratedResponse = request.method === "HEAD" ? new HttpHeadResponseDecorator(response) : response;
|
|
3274
|
+
await httpHandler(request, decoratedResponse);
|
|
2491
3275
|
};
|
|
2492
3276
|
}
|
|
2493
3277
|
function promisify(fn) {
|
|
@@ -2512,8 +3296,44 @@ function memoryMonitor(config) {
|
|
|
2512
3296
|
});
|
|
2513
3297
|
}
|
|
2514
3298
|
}
|
|
2515
|
-
function
|
|
2516
|
-
|
|
3299
|
+
async function initBuilder(context) {
|
|
3300
|
+
const storage = context.storage;
|
|
3301
|
+
const security = await httpSecurity(context);
|
|
3302
|
+
const sockets = webSockets(context);
|
|
3303
|
+
const handler = compose(
|
|
3304
|
+
server_header_default(context.serverHeader),
|
|
3305
|
+
...security,
|
|
3306
|
+
...sockets,
|
|
3307
|
+
...context.middleware,
|
|
3308
|
+
// health check
|
|
3309
|
+
async ({ request, response }, next) => {
|
|
3310
|
+
if (request.method === "GET" && request.path === "/health") {
|
|
3311
|
+
response.setStatusCode(HttpStatus.OK);
|
|
3312
|
+
const buffer = Buffer.from("UP", "utf-8");
|
|
3313
|
+
response.headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
3314
|
+
await response.body(buffer);
|
|
3315
|
+
} else {
|
|
3316
|
+
await next();
|
|
3317
|
+
}
|
|
3318
|
+
},
|
|
3319
|
+
// home page
|
|
3320
|
+
async ({ request, response }, next) => {
|
|
3321
|
+
if (request.method === "GET" && request.path === "/") {
|
|
3322
|
+
response.setStatusCode(HttpStatus.OK);
|
|
3323
|
+
const buffer = Buffer.from("io.Gateway Server", "utf-8");
|
|
3324
|
+
response.headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
3325
|
+
await response.body(buffer);
|
|
3326
|
+
} else {
|
|
3327
|
+
await next();
|
|
3328
|
+
}
|
|
3329
|
+
},
|
|
3330
|
+
// not found
|
|
3331
|
+
async ({ response }, _next) => {
|
|
3332
|
+
response.setStatusCode(HttpStatus.NOT_FOUND);
|
|
3333
|
+
await response.end();
|
|
3334
|
+
}
|
|
3335
|
+
);
|
|
3336
|
+
return new WebHttpHandlerBuilder(handler).storage(storage);
|
|
2517
3337
|
}
|
|
2518
3338
|
var Factory = async (options) => {
|
|
2519
3339
|
const ssl = options.ssl;
|
|
@@ -2525,7 +3345,7 @@ var Factory = async (options) => {
|
|
|
2525
3345
|
cors: [],
|
|
2526
3346
|
authConfig: options.auth,
|
|
2527
3347
|
authorize: [],
|
|
2528
|
-
storage: new
|
|
3348
|
+
storage: new import_node_async_hooks4.AsyncLocalStorage(),
|
|
2529
3349
|
sockets: /* @__PURE__ */ new Map()
|
|
2530
3350
|
};
|
|
2531
3351
|
const gw = import_gateway6.IOGateway.Factory({ ...options.gateway });
|
|
@@ -2540,8 +3360,9 @@ var Factory = async (options) => {
|
|
|
2540
3360
|
}
|
|
2541
3361
|
const ports = portRange(options.port ?? 0);
|
|
2542
3362
|
const host = options.host;
|
|
2543
|
-
const onSocketError = (err) =>
|
|
2544
|
-
const
|
|
3363
|
+
const onSocketError = (err) => logger6.error(`socket error: ${err}`, err);
|
|
3364
|
+
const builder = await initBuilder(context);
|
|
3365
|
+
const listener = await createListener(builder, onSocketError);
|
|
2545
3366
|
const serverP = new Promise((resolve, reject) => {
|
|
2546
3367
|
const server2 = createServer({
|
|
2547
3368
|
IncomingMessage: ExtendedHttpIncomingMessage,
|
|
@@ -2549,95 +3370,45 @@ var Factory = async (options) => {
|
|
|
2549
3370
|
}, listener);
|
|
2550
3371
|
server2.on("error", (e) => {
|
|
2551
3372
|
if (e["code"] === "EADDRINUSE") {
|
|
2552
|
-
|
|
3373
|
+
logger6.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
|
|
2553
3374
|
const { value: port } = ports.next();
|
|
2554
3375
|
if (port) {
|
|
2555
|
-
|
|
3376
|
+
logger6.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
|
|
2556
3377
|
server2.close();
|
|
2557
3378
|
server2.listen(port, host);
|
|
2558
3379
|
} else {
|
|
2559
|
-
|
|
3380
|
+
logger6.warn(`all configured port(s) ${options.port} are in use. closing...`);
|
|
2560
3381
|
server2.close();
|
|
2561
3382
|
reject(e);
|
|
2562
3383
|
}
|
|
2563
3384
|
} else {
|
|
2564
|
-
|
|
3385
|
+
logger6.error(`server error: ${e.message}`, e);
|
|
2565
3386
|
reject(e);
|
|
2566
3387
|
}
|
|
2567
3388
|
});
|
|
2568
3389
|
server2.on("listening", async () => {
|
|
2569
3390
|
const info2 = server2.address();
|
|
2570
3391
|
for (const [path, route] of context.sockets) {
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
const wss = new ws.WebSocketServer({ noServer: true });
|
|
2574
|
-
const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info2.port}${path}`;
|
|
2575
|
-
const handler = await route.factory({ endpoint });
|
|
2576
|
-
wss.on("error", (err) => {
|
|
2577
|
-
logger5.error(`error starting the ws server for [${path}]`, err);
|
|
2578
|
-
}).on("listening", () => {
|
|
2579
|
-
logger5.info(`ws server for [${path}] is listening`);
|
|
2580
|
-
}).on("connection", (socket, req) => {
|
|
2581
|
-
const { request, principal } = req.exchange;
|
|
2582
|
-
const url = request.URL;
|
|
2583
|
-
const headers2 = new MapHttpHeaders();
|
|
2584
|
-
for (const key of request.headers.keys()) {
|
|
2585
|
-
headers2.set(key, request.headers.get(key));
|
|
2586
|
-
}
|
|
2587
|
-
const cookies = request.cookies;
|
|
2588
|
-
const handshake = {
|
|
2589
|
-
url,
|
|
2590
|
-
headers: headers2,
|
|
2591
|
-
cookies,
|
|
2592
|
-
principal,
|
|
2593
|
-
protocol: socket.protocol,
|
|
2594
|
-
remoteAddress: request.socket.remoteAddress,
|
|
2595
|
-
remoteFamily: request.socket.remoteFamily,
|
|
2596
|
-
remotePort: request.socket.remotePort,
|
|
2597
|
-
logPrefix: socketKey(request.socket)
|
|
2598
|
-
};
|
|
2599
|
-
handler(socket, handshake);
|
|
2600
|
-
});
|
|
2601
|
-
const pingInterval = route.ping;
|
|
2602
|
-
if (pingInterval) {
|
|
2603
|
-
const pingIntervalId = setInterval(() => {
|
|
2604
|
-
for (const client of wss.clients) {
|
|
2605
|
-
if (client["connected"] === false) {
|
|
2606
|
-
client.terminate();
|
|
2607
|
-
}
|
|
2608
|
-
client["connected"] = false;
|
|
2609
|
-
client.ping();
|
|
2610
|
-
}
|
|
2611
|
-
}, pingInterval);
|
|
2612
|
-
wss.on("close", () => {
|
|
2613
|
-
clearInterval(pingIntervalId);
|
|
2614
|
-
});
|
|
2615
|
-
}
|
|
2616
|
-
route.wss = wss;
|
|
2617
|
-
route.close = handler.close?.bind(handler);
|
|
2618
|
-
} catch (e) {
|
|
2619
|
-
logger5.warn(`failed to init route ${path}`, e);
|
|
2620
|
-
}
|
|
3392
|
+
const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info2.port}${path}`;
|
|
3393
|
+
await initRoute(path, route, endpoint, context.storage, onSocketError);
|
|
2621
3394
|
}
|
|
2622
|
-
|
|
3395
|
+
logger6.info(`http server listening on ${info2.address}:${info2.port}`);
|
|
2623
3396
|
resolve(server2);
|
|
2624
3397
|
});
|
|
2625
|
-
server2.on("upgrade", (req,
|
|
2626
|
-
socket.on("error", onSocketError);
|
|
3398
|
+
server2.on("upgrade", (req, _socket, head) => {
|
|
2627
3399
|
try {
|
|
2628
|
-
|
|
2629
|
-
listener(req, res);
|
|
3400
|
+
listener(req, head);
|
|
2630
3401
|
} catch (err) {
|
|
2631
|
-
|
|
3402
|
+
logger6.error(`upgrade error: ${err}`, err);
|
|
2632
3403
|
}
|
|
2633
3404
|
}).on("close", async () => {
|
|
2634
|
-
|
|
3405
|
+
logger6.info(`http server closed.`);
|
|
2635
3406
|
});
|
|
2636
3407
|
try {
|
|
2637
3408
|
const { value: port } = ports.next();
|
|
2638
3409
|
server2.listen(port, host);
|
|
2639
3410
|
} catch (e) {
|
|
2640
|
-
|
|
3411
|
+
logger6.error(`error starting web socket server`, e);
|
|
2641
3412
|
reject(e instanceof Error ? e : new Error(`listen failed: ${e}`));
|
|
2642
3413
|
}
|
|
2643
3414
|
});
|
|
@@ -2647,16 +3418,11 @@ var Factory = async (options) => {
|
|
|
2647
3418
|
async close() {
|
|
2648
3419
|
for (const [path, route] of context.sockets) {
|
|
2649
3420
|
try {
|
|
2650
|
-
if (route.close) {
|
|
3421
|
+
if (route.close !== void 0) {
|
|
2651
3422
|
await route.close();
|
|
2652
3423
|
}
|
|
2653
|
-
logger5.info(`stopping ws server for [${path}]. clients: ${route.wss?.clients?.size ?? 0}`);
|
|
2654
|
-
route.wss?.clients?.forEach((client) => {
|
|
2655
|
-
client.terminate();
|
|
2656
|
-
});
|
|
2657
|
-
route.wss?.close();
|
|
2658
3424
|
} catch (e) {
|
|
2659
|
-
|
|
3425
|
+
logger6.warn(`error closing route ${path}`, e);
|
|
2660
3426
|
}
|
|
2661
3427
|
}
|
|
2662
3428
|
await promisify((cb) => {
|