@interopio/gateway-server 0.7.0-beta → 0.8.0-beta

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/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 import_node_async_hooks2 = require("node:async_hooks");
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 initClient(socket, authenticationPromise, key, host) {
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.name };
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(server) {
103
- log.info(`starting gateway on ${server.endpoint}`);
104
- await this.start({ endpoint: server.endpoint });
105
- return async (socket, handshake) => {
106
- const key = handshake.logPrefix;
107
- const host = handshake.remoteAddress;
108
- log.info(`${key} connected on gw from ${host}`);
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(`${key} gw client init failed`);
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(`${key} websocket error: ${err}`, err);
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
- client.send(data);
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(`${key} disconnected from gw. code: ${code}`);
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(ctx, nextFn);
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/server/exchange.ts
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 requestToProtocol(request, defaultProtocol) {
176
- let proto = request.headers.get("x-forwarded-proto");
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 = request.headers.get("x-forwarded-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 = request.headers.one("x-forwarded-port");
213
+ const port = headers2.one("x-forwarded-port");
194
214
  if (port) {
195
215
  host = `${host}:${port}`;
196
216
  }
197
217
  }
198
- host ??= request.headers.one("host");
218
+ host ??= headers2.one("host");
199
219
  }
200
220
  if (Array.isArray(host)) {
201
221
  host = host[0];
@@ -205,15 +225,180 @@ function requestToHost(request, defaultHost) {
205
225
  }
206
226
  return defaultHost;
207
227
  }
208
- function parseCookies(request) {
209
- return request.headers.list("cookie").map((s) => s.split(";").map((cs) => import_tough_cookie.Cookie.parse(cs))).flat(1).filter((tc) => tc !== void 0).map((tc) => {
210
- const result = { name: tc.key, value: tc.value };
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/server/exchange.ts
397
+ var import_node_http = __toESM(require("node:http"), 1);
214
398
  var ExtendedHttpIncomingMessage = class extends import_node_http.default.IncomingMessage {
215
399
  // circular reference to the exchange
216
400
  exchange;
401
+ upgradeHead;
217
402
  get urlBang() {
218
403
  return this.url;
219
404
  }
@@ -221,29 +406,115 @@ var ExtendedHttpIncomingMessage = class extends import_node_http.default.Incomin
221
406
  return this.socket["encrypted"] === true;
222
407
  }
223
408
  };
224
- var ExtendedHttpServerResponse = class _ExtendedHttpServerResponse extends import_node_http.default.ServerResponse {
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;
409
+ var ExtendedHttpServerResponse = class extends import_node_http.default.ServerResponse {
232
410
  markHeadersSent() {
233
411
  this["_header"] = true;
234
412
  }
413
+ getRawHeaderNames() {
414
+ return super["getRawHeaderNames"]();
415
+ }
235
416
  };
236
- var HttpServerRequest = class {
237
- _body;
238
- _url;
239
- _cookies;
240
- #headers;
417
+ var AbstractServerHttpRequest = class extends AbstractHttpRequest {
418
+ };
419
+ var AbstractServerHttpResponse = class extends AbstractHttpResponse {
420
+ #cookies = [];
421
+ #statusCode;
422
+ #state = "new";
423
+ #commitActions = [];
424
+ setStatusCode(statusCode) {
425
+ if (this.#state === "committed") {
426
+ return false;
427
+ } else {
428
+ this.#statusCode = statusCode;
429
+ return true;
430
+ }
431
+ }
432
+ get statusCode() {
433
+ return this.#statusCode;
434
+ }
435
+ addCookie(cookie) {
436
+ if (this.#state === "committed") {
437
+ throw new Error(`Cannot add cookie ${JSON.stringify(cookie)} because HTTP response has already been committed`);
438
+ }
439
+ this.#cookies.push(cookie);
440
+ return this;
441
+ }
442
+ beforeCommit(action) {
443
+ this.#commitActions.push(action);
444
+ }
445
+ get commited() {
446
+ const state = this.#state;
447
+ return state !== "new" && state !== "commit-action-failed";
448
+ }
449
+ async body(body) {
450
+ if (body instanceof ReadableStream) {
451
+ throw new Error("ReadableStream body not supported yet");
452
+ }
453
+ const buffer = await body;
454
+ try {
455
+ return await this.doCommit(async () => {
456
+ return await this.bodyInternal(Promise.resolve(buffer));
457
+ }).catch((error) => {
458
+ throw error;
459
+ });
460
+ } catch (error) {
461
+ throw error;
462
+ }
463
+ }
464
+ async end() {
465
+ if (!this.commited) {
466
+ return this.doCommit();
467
+ } else {
468
+ return Promise.resolve(false);
469
+ }
470
+ }
471
+ doCommit(writeAction) {
472
+ const state = this.#state;
473
+ let allActions = Promise.resolve();
474
+ if (state === "new") {
475
+ this.#state = "committing";
476
+ if (this.#commitActions.length > 0) {
477
+ allActions = this.#commitActions.reduce(
478
+ (acc, cur) => acc.then(() => cur()),
479
+ Promise.resolve()
480
+ ).catch((error) => {
481
+ const state2 = this.#state;
482
+ if (state2 === "committing") {
483
+ this.#state = "commit-action-failed";
484
+ }
485
+ });
486
+ }
487
+ } else if (state === "commit-action-failed") {
488
+ this.#state = "committing";
489
+ } else {
490
+ return Promise.resolve(false);
491
+ }
492
+ allActions = allActions.then(() => {
493
+ this.applyStatusCode();
494
+ this.applyHeaders();
495
+ this.applyCookies();
496
+ this.#state = "committed";
497
+ });
498
+ return allActions.then(async () => {
499
+ return writeAction !== void 0 ? await writeAction() : true;
500
+ });
501
+ }
502
+ applyStatusCode() {
503
+ }
504
+ applyHeaders() {
505
+ }
506
+ applyCookies() {
507
+ }
508
+ };
509
+ var HttpServerRequest = class extends AbstractServerHttpRequest {
510
+ #url;
511
+ #cookies;
241
512
  #req;
242
513
  constructor(req) {
514
+ super(new IncomingMessageHeaders(req));
243
515
  this.#req = req;
244
- this.#headers = new IncomingMessageHeaders(this.#req);
245
516
  }
246
- get unsafeIncomingMessage() {
517
+ getNativeRequest() {
247
518
  return this.#req;
248
519
  }
249
520
  get upgrade() {
@@ -252,15 +523,12 @@ var HttpServerRequest = class {
252
523
  get http2() {
253
524
  return this.#req.httpVersionMajor >= 2;
254
525
  }
255
- get headers() {
256
- return this.#headers;
257
- }
258
526
  get path() {
259
527
  return this.URL?.pathname;
260
528
  }
261
529
  get URL() {
262
- this._url ??= new URL(this.#req.urlBang, `${this.protocol}://${this.host}`);
263
- return this._url;
530
+ this.#url ??= new URL(this.#req.urlBang, `${this.protocol}://${this.host}`);
531
+ return this.#url;
264
532
  }
265
533
  get query() {
266
534
  return this.URL?.search;
@@ -274,7 +542,7 @@ var HttpServerRequest = class {
274
542
  dh = this.#req.headers[":authority"];
275
543
  }
276
544
  dh ??= this.#req.socket.remoteAddress;
277
- return requestToHost(this, dh);
545
+ return super.parseHost(dh);
278
546
  }
279
547
  get protocol() {
280
548
  let dp = void 0;
@@ -282,47 +550,65 @@ var HttpServerRequest = class {
282
550
  dp = this.#req.headers[":scheme"];
283
551
  }
284
552
  dp ??= this.#req.socketEncrypted ? "https" : "http";
285
- return requestToProtocol(this, dp);
553
+ return super.parseProtocol(dp);
286
554
  }
287
555
  get socket() {
288
556
  return this.#req.socket;
289
557
  }
558
+ get remoteAddress() {
559
+ const family = this.#req.socket.remoteFamily;
560
+ const address = this.#req.socket.remoteAddress;
561
+ const port = this.#req.socket.remotePort;
562
+ if (!family || !address || !port) {
563
+ return void 0;
564
+ }
565
+ return { family, address, port };
566
+ }
290
567
  get cookies() {
291
- this._cookies ??= parseCookies(this);
292
- return this._cookies;
568
+ this.#cookies ??= super.cookies;
569
+ return this.#cookies;
293
570
  }
294
571
  get body() {
295
- this._body ??= new Promise((resolve, reject) => {
296
- const chunks = [];
297
- this.#req.on("error", (err) => reject(err)).on("data", (chunk) => chunks.push(chunk)).on("end", () => {
298
- resolve(new Blob(chunks, { type: this.headers.one("content-type") }));
299
- });
300
- });
301
- return this._body;
572
+ return import_node_http.default.IncomingMessage.toWeb(this.#req);
573
+ }
574
+ async blob() {
575
+ const chunks = [];
576
+ if (this.body !== void 0) {
577
+ for await (const chunk of this.body) {
578
+ chunks.push(chunk);
579
+ }
580
+ }
581
+ return new Blob(chunks, { type: this.headers.one("content-type") || "application/octet-stream" });
302
582
  }
303
- get text() {
304
- return this.body.then(async (blob) => await blob.text());
583
+ async text() {
584
+ const blob = await this.blob();
585
+ return await blob.text();
305
586
  }
306
- get formData() {
307
- return this.body.then(async (blob) => {
308
- const text = await blob.text();
309
- const formData = new URLSearchParams(text);
310
- return formData;
311
- });
587
+ async formData() {
588
+ const blob = await this.blob();
589
+ const text = await blob.text();
590
+ return new URLSearchParams(text);
312
591
  }
313
- get json() {
314
- return this.body.then(async (blob) => {
315
- if (blob.size === 0) {
316
- return void 0;
317
- }
318
- const text = await blob.text();
319
- return JSON.parse(text);
320
- });
592
+ async json() {
593
+ const blob = await this.blob();
594
+ if (blob.size === 0) {
595
+ return void 0;
596
+ }
597
+ const text = await blob.text();
598
+ return JSON.parse(text);
599
+ }
600
+ initId() {
601
+ const remoteIp = this.#req.socket.remoteAddress;
602
+ if (!remoteIp) {
603
+ throw new Error("Socket has no remote address");
604
+ }
605
+ return `${remoteIp}:${this.#req.socket.remotePort}`;
321
606
  }
322
607
  };
323
- var IncomingMessageHeaders = class {
608
+ var IncomingMessageHeaders = class extends AbstractHttpHeaders {
324
609
  #msg;
325
610
  constructor(msg) {
611
+ super();
326
612
  this.#msg = msg;
327
613
  }
328
614
  has(name) {
@@ -332,7 +618,7 @@ var IncomingMessageHeaders = class {
332
618
  return this.#msg.headers[name];
333
619
  }
334
620
  list(name) {
335
- return toList(this.#msg.headers[name]);
621
+ return super.toList(name);
336
622
  }
337
623
  one(name) {
338
624
  const value = this.#msg.headers[name];
@@ -345,9 +631,10 @@ var IncomingMessageHeaders = class {
345
631
  return Object.keys(this.#msg.headers).values();
346
632
  }
347
633
  };
348
- var OutgoingMessageHeaders = class {
634
+ var OutgoingMessageHeaders = class extends AbstractHttpHeaders {
349
635
  #msg;
350
636
  constructor(msg) {
637
+ super();
351
638
  this.#msg = msg;
352
639
  }
353
640
  has(name) {
@@ -388,277 +675,238 @@ var OutgoingMessageHeaders = class {
388
675
  return this;
389
676
  }
390
677
  list(name) {
391
- const values = this.get(name);
392
- return toList(values);
678
+ return super.toList(name);
393
679
  }
394
680
  };
395
- var HttpServerResponse = class {
396
- #headers;
681
+ var HttpServerResponse = class extends AbstractServerHttpResponse {
397
682
  #res;
398
683
  constructor(res) {
684
+ super(new OutgoingMessageHeaders(res));
399
685
  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
686
  }
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);
687
+ getNativeResponse() {
688
+ return this.#res;
689
+ }
690
+ get statusCode() {
691
+ const status = super.statusCode;
692
+ return status ?? { value: this.#res.statusCode };
693
+ }
694
+ applyStatusCode() {
695
+ const status = super.statusCode;
696
+ if (status !== void 0) {
697
+ this.#res.statusCode = status.value;
561
698
  }
562
- this.set(name, value);
699
+ }
700
+ addCookie(cookie) {
701
+ this.headers.add("Set-Cookie", super.setCookieValue(cookie));
563
702
  return this;
564
703
  }
565
- };
566
- var MockHttpRequest = class {
567
- #url;
568
- #body;
569
- headers = new MapHttpHeaders();
570
- upgrade = false;
571
- constructor(url, method) {
572
- if (typeof url === "string") {
573
- if (URL.canParse(url)) {
574
- url = new URL(url);
575
- } else if (URL.canParse(url, "http://localhost")) {
576
- url = new URL(url, "http://localhost");
704
+ async bodyInternal(body) {
705
+ if (!this.#res.headersSent) {
706
+ if (body instanceof ReadableStream) {
707
+ throw new Error("ReadableStream body not supported in response");
577
708
  } else {
578
- throw new TypeError("URL cannot parse url");
709
+ const chunk = await body;
710
+ return await new Promise((resolve, reject) => {
711
+ try {
712
+ if (chunk === void 0) {
713
+ this.#res.end(() => {
714
+ resolve(true);
715
+ });
716
+ } else {
717
+ if (!this.headers.has("content-length")) {
718
+ if (typeof chunk === "string") {
719
+ this.headers.set("content-length", Buffer.byteLength(chunk));
720
+ } else {
721
+ this.headers.set("content-length", chunk.byteLength);
722
+ }
723
+ }
724
+ this.#res.end(chunk, () => {
725
+ resolve(true);
726
+ });
727
+ }
728
+ } catch (e) {
729
+ reject(e instanceof Error ? e : new Error(`end failed: ${e}`));
730
+ }
731
+ });
579
732
  }
733
+ } else {
734
+ return false;
580
735
  }
581
- this.#url = url;
582
- this.method = method ?? "GET";
583
- this.headers.set("Host", this.#url.hostname);
584
- this.path = this.#url.pathname ?? "/";
585
736
  }
586
- method;
587
- path;
588
- get host() {
589
- return requestToHost(this, this.#url.host);
737
+ };
738
+ var ServerHttpRequestDecorator = class _ServerHttpRequestDecorator {
739
+ #delegate;
740
+ constructor(request) {
741
+ this.#delegate = request;
742
+ }
743
+ get delegate() {
744
+ return this.#delegate;
745
+ }
746
+ get id() {
747
+ return this.#delegate.id;
748
+ }
749
+ get method() {
750
+ return this.#delegate.method;
751
+ }
752
+ get path() {
753
+ return this.#delegate.path;
590
754
  }
591
755
  get protocol() {
592
- return requestToProtocol(this, this.#url.protocol.slice(0, -1));
756
+ return this.#delegate.protocol;
757
+ }
758
+ get host() {
759
+ return this.#delegate.host;
760
+ }
761
+ get URL() {
762
+ return this.#delegate.URL;
763
+ }
764
+ get headers() {
765
+ return this.#delegate.headers;
593
766
  }
594
767
  get cookies() {
595
- return parseCookies(this);
768
+ return this.#delegate.cookies;
769
+ }
770
+ get remoteAddress() {
771
+ return this.#delegate.remoteAddress;
772
+ }
773
+ get upgrade() {
774
+ return this.#delegate.upgrade;
596
775
  }
597
776
  get body() {
598
- const body = this.#body;
599
- return body ? Promise.resolve(body) : Promise.reject(new Error(`no body set`));
777
+ return this.#delegate.body;
600
778
  }
601
- get json() {
602
- return this.body.then(async (blob) => JSON.parse(await blob.text()));
779
+ async blob() {
780
+ return await this.#delegate.blob();
603
781
  }
604
- get text() {
605
- return this.body.then(async (blob) => await blob.text());
782
+ async text() {
783
+ return await this.#delegate.text();
606
784
  }
607
- set body(value) {
608
- this.#body = value;
609
- if (!this.headers.has("content-type")) {
610
- this.headers.set("content-type", value.type || "application/octet-stream");
611
- }
785
+ async formData() {
786
+ return await this.#delegate.formData();
612
787
  }
613
- get formData() {
614
- return this.body.then(async (b) => new URLSearchParams(await b.text()));
788
+ async json() {
789
+ return await this.#delegate.json();
615
790
  }
616
- get URL() {
617
- return new URL(this.path + this.#url.search + this.#url.hash, `${this.protocol}://${this.host}`);
791
+ toString() {
792
+ return `${_ServerHttpRequestDecorator.name} [delegate: ${this.delegate.toString()}]`;
793
+ }
794
+ static getNativeRequest(request) {
795
+ if (request instanceof AbstractServerHttpRequest) {
796
+ return request.getNativeRequest();
797
+ } else if (request instanceof _ServerHttpRequestDecorator) {
798
+ return _ServerHttpRequestDecorator.getNativeRequest(request.delegate);
799
+ } else {
800
+ throw new Error(`Cannot get native request from ${request.constructor.name}`);
801
+ }
618
802
  }
619
803
  };
620
- var MockHttpResponse = class {
621
- statusCode;
622
- headers = new MapHttpHeaders();
623
- cookies = [];
804
+ var ServerHttpResponseDecorator = class _ServerHttpResponseDecorator {
805
+ #delegate;
806
+ constructor(response) {
807
+ this.#delegate = response;
808
+ }
809
+ get delegate() {
810
+ return this.#delegate;
811
+ }
812
+ setStatusCode(statusCode) {
813
+ return this.#delegate.setStatusCode(statusCode);
814
+ }
815
+ get statusCode() {
816
+ return this.#delegate.statusCode;
817
+ }
818
+ get cookies() {
819
+ return this.#delegate.cookies;
820
+ }
624
821
  addCookie(cookie) {
625
- this.cookies.push(cookie);
822
+ this.#delegate.addCookie(cookie);
626
823
  return this;
627
824
  }
628
- _body;
629
- async end(chunk) {
630
- if (this._body) {
631
- return false;
632
- }
633
- switch (typeof chunk) {
634
- case "string":
635
- this._body = new Blob([chunk], { type: "text/plain" });
636
- break;
637
- case "object":
638
- if (chunk instanceof Blob) {
639
- this._body = chunk;
640
- } else if (chunk !== null) {
641
- this._body = new Blob([JSON.stringify(chunk)], { type: "application/json" });
642
- }
643
- break;
644
- case "undefined":
645
- this._body = new Blob([]);
646
- break;
647
- default:
648
- throw new Error(`Unsupported chunk type: ${typeof chunk}`);
825
+ async end() {
826
+ return await this.#delegate.end();
827
+ }
828
+ async body(body) {
829
+ return await this.#delegate.body(body);
830
+ }
831
+ get headers() {
832
+ return this.#delegate.headers;
833
+ }
834
+ toString() {
835
+ return `${_ServerHttpResponseDecorator.name} [delegate: ${this.delegate.toString()}]`;
836
+ }
837
+ static getNativeResponse(response) {
838
+ if (response instanceof AbstractServerHttpResponse) {
839
+ return response.getNativeResponse();
840
+ } else if (response instanceof _ServerHttpResponseDecorator) {
841
+ return _ServerHttpResponseDecorator.getNativeResponse(response.delegate);
842
+ } else {
843
+ throw new Error(`Cannot get native response from ${response.constructor.name}`);
649
844
  }
650
- return true;
651
845
  }
652
846
  };
653
-
654
- // src/utils.ts
655
- function socketKey(socket) {
656
- const remoteIp = socket.remoteAddress;
657
- if (!remoteIp) {
658
- throw new Error("Socket has no remote address");
847
+ var ServerWebExchangeDecorator = class _ServerWebExchangeDecorator {
848
+ #delegate;
849
+ constructor(exchange) {
850
+ this.#delegate = exchange;
659
851
  }
660
- return `${remoteIp}:${socket.remotePort}`;
661
- }
852
+ get delegate() {
853
+ return this.#delegate;
854
+ }
855
+ get request() {
856
+ return this.#delegate.request;
857
+ }
858
+ get response() {
859
+ return this.#delegate.response;
860
+ }
861
+ attribute(name) {
862
+ return this.#delegate.attribute(name);
863
+ }
864
+ principal() {
865
+ return this.#delegate.principal();
866
+ }
867
+ get logPrefix() {
868
+ return this.#delegate.logPrefix;
869
+ }
870
+ toString() {
871
+ return `${_ServerWebExchangeDecorator.name} [delegate: ${this.delegate}]`;
872
+ }
873
+ };
874
+ var DefaultWebExchange = class {
875
+ request;
876
+ response;
877
+ #attributes = {};
878
+ #logId;
879
+ #logPrefix = "";
880
+ constructor(request, response) {
881
+ this.#attributes[LOG_ID_ATTRIBUTE] = request.id;
882
+ this.request = request;
883
+ this.response = response;
884
+ }
885
+ get method() {
886
+ return this.request.method;
887
+ }
888
+ get path() {
889
+ return this.request.path;
890
+ }
891
+ get attributes() {
892
+ return this.#attributes;
893
+ }
894
+ attribute(name) {
895
+ return this.attributes[name];
896
+ }
897
+ principal() {
898
+ return Promise.resolve(void 0);
899
+ }
900
+ get logPrefix() {
901
+ const value = this.attribute(LOG_ID_ATTRIBUTE);
902
+ if (this.#logId !== value) {
903
+ this.#logId = value;
904
+ this.#logPrefix = value !== void 0 ? `[${value}] ` : "";
905
+ }
906
+ return this.#logPrefix;
907
+ }
908
+ };
909
+ var LOG_ID_ATTRIBUTE = "io.interop.gateway.server.log_id";
662
910
 
663
911
  // src/server/address.ts
664
912
  var import_node_os = require("node:os");
@@ -840,7 +1088,20 @@ async function stop(m) {
840
1088
  return await run(m, "stop");
841
1089
  }
842
1090
 
843
- // src/app/ws-client-verify.ts
1091
+ // src/server/server-header.ts
1092
+ var import_package = __toESM(require("@interopio/gateway-server/package.json"), 1);
1093
+ var serverHeader = (server) => {
1094
+ server ??= `${import_package.default.name} - v${import_package.default.version}`;
1095
+ return async ({ response }, next) => {
1096
+ if (server !== false && !response.headers.has("server")) {
1097
+ response.headers.set("Server", server);
1098
+ }
1099
+ await next();
1100
+ };
1101
+ };
1102
+ var server_header_default = (server) => serverHeader(server);
1103
+
1104
+ // src/server/ws-client-verify.ts
844
1105
  var import_gateway3 = require("@interopio/gateway");
845
1106
  var log3 = getLogger("gateway.ws.client-verify");
846
1107
  function acceptsMissing(originFilters) {
@@ -912,19 +1173,6 @@ function regexifyOriginFilters(originFilters) {
912
1173
  }
913
1174
  }
914
1175
 
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
1176
  // src/server/util/matchers.ts
929
1177
  var or = (matchers) => {
930
1178
  return async (exchange) => {
@@ -1031,15 +1279,6 @@ upgradeMatcher.toString = () => "websocket upgrade";
1031
1279
 
1032
1280
  // src/app/route.ts
1033
1281
  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
1282
  async function configure(app, config, routes) {
1044
1283
  const applyCors = (matcher, requestMethod, options) => {
1045
1284
  if (options?.cors) {
@@ -1087,6 +1326,93 @@ async function configure(app, config, routes) {
1087
1326
  await app(configurer, config);
1088
1327
  }
1089
1328
 
1329
+ // src/http/status.ts
1330
+ var HttpStatus = class _HttpStatus {
1331
+ static CONTINUE = new _HttpStatus(100, "Continue");
1332
+ static SWITCHING_PROTOCOLS = new _HttpStatus(101, "Switching Protocols");
1333
+ // 2xx Success
1334
+ static OK = new _HttpStatus(200, "OK");
1335
+ static CREATED = new _HttpStatus(201, "Created");
1336
+ static ACCEPTED = new _HttpStatus(202, "Accepted");
1337
+ static NON_AUTHORITATIVE_INFORMATION = new _HttpStatus(203, "Non-Authoritative Information");
1338
+ static NO_CONTENT = new _HttpStatus(204, "No Content");
1339
+ static RESET_CONTENT = new _HttpStatus(205, "Reset Content");
1340
+ static PARTIAL_CONTENT = new _HttpStatus(206, "Partial Content");
1341
+ static MULTI_STATUS = new _HttpStatus(207, "Multi-Status");
1342
+ static IM_USED = new _HttpStatus(226, "IM Used");
1343
+ // 3xx Redirection
1344
+ static MULTIPLE_CHOICES = new _HttpStatus(300, "Multiple Choices");
1345
+ static MOVED_PERMANENTLY = new _HttpStatus(301, "Moved Permanently");
1346
+ // 4xx Client Error
1347
+ static BAD_REQUEST = new _HttpStatus(400, "Bad Request");
1348
+ static UNAUTHORIZED = new _HttpStatus(401, "Unauthorized");
1349
+ static FORBIDDEN = new _HttpStatus(403, "Forbidden");
1350
+ static NOT_FOUND = new _HttpStatus(404, "Not Found");
1351
+ static METHOD_NOT_ALLOWED = new _HttpStatus(405, "Method Not Allowed");
1352
+ static NOT_ACCEPTABLE = new _HttpStatus(406, "Not Acceptable");
1353
+ static PROXY_AUTHENTICATION_REQUIRED = new _HttpStatus(407, "Proxy Authentication Required");
1354
+ static REQUEST_TIMEOUT = new _HttpStatus(408, "Request Timeout");
1355
+ static CONFLICT = new _HttpStatus(409, "Conflict");
1356
+ static GONE = new _HttpStatus(410, "Gone");
1357
+ static LENGTH_REQUIRED = new _HttpStatus(411, "Length Required");
1358
+ static PRECONDITION_FAILED = new _HttpStatus(412, "Precondition Failed");
1359
+ static PAYLOAD_TOO_LARGE = new _HttpStatus(413, "Payload Too Large");
1360
+ static URI_TOO_LONG = new _HttpStatus(414, "URI Too Long");
1361
+ static UNSUPPORTED_MEDIA_TYPE = new _HttpStatus(415, "Unsupported Media Type");
1362
+ static EXPECTATION_FAILED = new _HttpStatus(417, "Expectation Failed");
1363
+ static IM_A_TEAPOT = new _HttpStatus(418, "I'm a teapot");
1364
+ static TOO_EARLY = new _HttpStatus(425, "Too Early");
1365
+ static UPGRADE_REQUIRED = new _HttpStatus(426, "Upgrade Required");
1366
+ static PRECONDITION_REQUIRED = new _HttpStatus(428, "Precondition Required");
1367
+ static TOO_MANY_REQUESTS = new _HttpStatus(429, "Too Many Requests");
1368
+ static REQUEST_HEADER_FIELDS_TOO_LARGE = new _HttpStatus(431, "Request Header Fields Too Large");
1369
+ static UNAVAILABLE_FOR_LEGAL_REASONS = new _HttpStatus(451, "Unavailable For Legal Reasons");
1370
+ // 5xx Server Error
1371
+ static INTERNAL_SERVER_ERROR = new _HttpStatus(500, "Internal Server Error");
1372
+ static NOT_IMPLEMENTED = new _HttpStatus(501, "Not Implemented");
1373
+ static BAD_GATEWAY = new _HttpStatus(502, "Bad Gateway");
1374
+ static SERVICE_UNAVAILABLE = new _HttpStatus(503, "Service Unavailable");
1375
+ static GATEWAY_TIMEOUT = new _HttpStatus(504, "Gateway Timeout");
1376
+ static HTTP_VERSION_NOT_SUPPORTED = new _HttpStatus(505, "HTTP Version Not Supported");
1377
+ static VARIANT_ALSO_NEGOTIATES = new _HttpStatus(506, "Variant Also Negotiates");
1378
+ static INSUFFICIENT_STORAGE = new _HttpStatus(507, "Insufficient Storage");
1379
+ static LOOP_DETECTED = new _HttpStatus(508, "Loop Detected");
1380
+ static NOT_EXTENDED = new _HttpStatus(510, "Not Extended");
1381
+ static NETWORK_AUTHENTICATION_REQUIRED = new _HttpStatus(511, "Network Authentication Required");
1382
+ static #VALUES = [];
1383
+ static {
1384
+ Object.keys(_HttpStatus).filter((key) => key !== "VALUES" && key !== "resolve").forEach((key) => {
1385
+ const value = _HttpStatus[key];
1386
+ if (value instanceof _HttpStatus) {
1387
+ Object.defineProperty(value, "name", { enumerable: true, value: key, writable: false });
1388
+ _HttpStatus.#VALUES.push(value);
1389
+ }
1390
+ });
1391
+ }
1392
+ static resolve(code) {
1393
+ for (const status of _HttpStatus.#VALUES) {
1394
+ if (status.value === code) {
1395
+ return status;
1396
+ }
1397
+ }
1398
+ }
1399
+ #value;
1400
+ #phrase;
1401
+ constructor(value, phrase) {
1402
+ this.#value = value;
1403
+ this.#phrase = phrase;
1404
+ }
1405
+ get value() {
1406
+ return this.#value;
1407
+ }
1408
+ get phrase() {
1409
+ return this.#phrase;
1410
+ }
1411
+ toString() {
1412
+ return `${this.#value} ${this["name"]}`;
1413
+ }
1414
+ };
1415
+
1090
1416
  // src/server/cors.ts
1091
1417
  var import_gateway5 = require("@interopio/gateway");
1092
1418
  function isSameOrigin(request) {
@@ -1241,7 +1567,7 @@ var corsFilter = (opts) => {
1241
1567
  var cors_default = corsFilter;
1242
1568
  var logger = getLogger("cors");
1243
1569
  function rejectRequest(response) {
1244
- response.statusCode = 403;
1570
+ response.setStatusCode(HttpStatus.FORBIDDEN);
1245
1571
  }
1246
1572
  function handleInternal(exchange, config, preFlightRequest) {
1247
1573
  const { request, response } = exchange;
@@ -1331,70 +1657,243 @@ function checkMethods(config, requestMethod) {
1331
1657
  if (allowedMethods === ALL) {
1332
1658
  return [requestMethod];
1333
1659
  }
1334
- if (import_gateway5.IOGateway.Filtering.valuesMatch(allowedMethods, requestMethod)) {
1335
- return allowedMethods;
1660
+ if (import_gateway5.IOGateway.Filtering.valuesMatch(allowedMethods, requestMethod)) {
1661
+ return allowedMethods;
1662
+ }
1663
+ }
1664
+ }
1665
+ function checkHeaders(config, requestHeaders) {
1666
+ if (requestHeaders === void 0) {
1667
+ return;
1668
+ }
1669
+ if (requestHeaders.length == 0) {
1670
+ return [];
1671
+ }
1672
+ const allowedHeaders = config.allowHeaders;
1673
+ if (allowedHeaders === void 0) {
1674
+ return;
1675
+ }
1676
+ const allowAnyHeader = allowedHeaders === ALL || allowedHeaders.includes(ALL);
1677
+ const result = [];
1678
+ for (const requestHeader of requestHeaders) {
1679
+ const value = requestHeader?.trim();
1680
+ if (value) {
1681
+ if (allowAnyHeader) {
1682
+ result.push(value);
1683
+ } else {
1684
+ for (const allowedHeader of allowedHeaders) {
1685
+ if (value.toLowerCase() === allowedHeader) {
1686
+ result.push(value);
1687
+ break;
1688
+ }
1689
+ }
1690
+ }
1691
+ }
1692
+ }
1693
+ if (result.length > 0) {
1694
+ return result;
1695
+ }
1696
+ }
1697
+ function trimTrailingSlash(origin) {
1698
+ return origin.endsWith("/") ? origin.slice(0, -1) : origin;
1699
+ }
1700
+ function getMethodToUse(request, isPreFlight) {
1701
+ return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
1702
+ }
1703
+ function getHeadersToUse(request, isPreFlight) {
1704
+ const headers2 = request.headers;
1705
+ return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
1706
+ }
1707
+ var matchingCorsConfigSource = (opts) => {
1708
+ return async (exchange) => {
1709
+ for (const [matcher, config] of opts.mappings) {
1710
+ if ((await matcher(exchange)).match) {
1711
+ logger.debug(`resolved cors config on '${exchange.request.path}' using ${matcher}: ${JSON.stringify(config)}`);
1712
+ return config;
1713
+ }
1714
+ }
1715
+ };
1716
+ };
1717
+
1718
+ // src/mock/server/exchange.ts
1719
+ var import_web = __toESM(require("node:stream/web"), 1);
1720
+ var MockServerHttpRequest = class extends AbstractHttpRequest {
1721
+ #url;
1722
+ #body;
1723
+ upgrade = false;
1724
+ constructor(url, method) {
1725
+ super(new MapHttpHeaders());
1726
+ if (typeof url === "string") {
1727
+ if (URL.canParse(url)) {
1728
+ url = new URL(url);
1729
+ } else if (URL.canParse(url, "http://localhost")) {
1730
+ url = new URL(url, "http://localhost");
1731
+ } else {
1732
+ throw new TypeError("URL cannot parse url");
1733
+ }
1734
+ }
1735
+ this.#url = url;
1736
+ this.method = method ?? "GET";
1737
+ this.setHeader("Host", this.#url.hostname);
1738
+ this.path = this.#url.pathname ?? "/";
1739
+ }
1740
+ method;
1741
+ path;
1742
+ get host() {
1743
+ return super.parseHost(this.#url.host);
1744
+ }
1745
+ get protocol() {
1746
+ return super.parseProtocol(this.#url.protocol.slice(0, -1));
1747
+ }
1748
+ get body() {
1749
+ if (this.#body !== void 0) {
1750
+ const blob = this.#body;
1751
+ const asyncIterator = async function* () {
1752
+ const bytes = await blob.bytes();
1753
+ yield bytes;
1754
+ }();
1755
+ return import_web.default.ReadableStream.from(asyncIterator);
1756
+ }
1757
+ }
1758
+ blob() {
1759
+ const body = this.#body;
1760
+ return body ? Promise.resolve(body) : Promise.reject(new Error(`no body set`));
1761
+ }
1762
+ async text() {
1763
+ const blob = await this.blob();
1764
+ return await blob.text();
1765
+ }
1766
+ async formData() {
1767
+ const blob = await this.blob();
1768
+ const text = await blob.text();
1769
+ return new URLSearchParams(text);
1770
+ }
1771
+ async json() {
1772
+ const blob = await this.blob();
1773
+ if (blob.size === 0) {
1774
+ return void 0;
1775
+ }
1776
+ const text = await blob.text();
1777
+ return JSON.parse(text);
1778
+ }
1779
+ async writeBody(body) {
1780
+ if (body === void 0) {
1781
+ this.#body = new Blob([]);
1782
+ return;
1783
+ }
1784
+ if (body instanceof ReadableStream) {
1785
+ const chunks = [];
1786
+ const reader = body.getReader();
1787
+ let done = false;
1788
+ while (!done) {
1789
+ const { value, done: readDone } = await reader.read();
1790
+ if (readDone) {
1791
+ done = true;
1792
+ } else {
1793
+ chunks.push(value);
1794
+ }
1795
+ }
1796
+ this.#body = new Blob(chunks);
1797
+ } else {
1798
+ body = await body;
1799
+ if (typeof body === "string") {
1800
+ this.#body = new Blob([body], { type: "text/plain" });
1801
+ } else {
1802
+ this.#body = new Blob([body]);
1803
+ }
1804
+ }
1805
+ if (!this.headers.has("content-type")) {
1806
+ this.setHeader("Content-Type", this.#body.type || "application/octet-stream");
1336
1807
  }
1337
1808
  }
1338
- }
1339
- function checkHeaders(config, requestHeaders) {
1340
- if (requestHeaders === void 0) {
1341
- return;
1809
+ get URL() {
1810
+ return new URL(this.path + this.#url.search + this.#url.hash, `${this.protocol}://${this.host}`);
1342
1811
  }
1343
- if (requestHeaders.length == 0) {
1344
- return [];
1812
+ addHeader(name, value) {
1813
+ this.headers.add(name, value);
1814
+ return this;
1345
1815
  }
1346
- const allowedHeaders = config.allowHeaders;
1347
- if (allowedHeaders === void 0) {
1348
- return;
1816
+ setHeader(name, value) {
1817
+ this.headers.set(name, value);
1818
+ return this;
1349
1819
  }
1350
- const allowAnyHeader = allowedHeaders === ALL || allowedHeaders.includes(ALL);
1351
- const result = [];
1352
- for (const requestHeader of requestHeaders) {
1353
- const value = requestHeader?.trim();
1354
- if (value) {
1355
- if (allowAnyHeader) {
1356
- result.push(value);
1357
- } else {
1358
- for (const allowedHeader of allowedHeaders) {
1359
- if (value.toLowerCase() === allowedHeader) {
1360
- result.push(value);
1361
- break;
1362
- }
1820
+ };
1821
+ var MockServerHttpResponse = class extends AbstractServerHttpResponse {
1822
+ #writeHandler;
1823
+ #body = () => {
1824
+ throw new Error("No content was written to the response body nor end was called on this response.");
1825
+ };
1826
+ constructor() {
1827
+ super(new MapHttpHeaders());
1828
+ this.#writeHandler = async (body) => {
1829
+ const chunks = [];
1830
+ let bodyStream;
1831
+ this.#body = () => {
1832
+ bodyStream ??= import_web.default.ReadableStream.from(chunks);
1833
+ return bodyStream;
1834
+ };
1835
+ const reader = body.getReader();
1836
+ let done = false;
1837
+ do {
1838
+ const { value, done: readDone } = await reader.read();
1839
+ if (readDone) {
1840
+ done = true;
1841
+ } else {
1842
+ chunks.push(value);
1363
1843
  }
1364
- }
1365
- }
1844
+ } while (!done);
1845
+ return true;
1846
+ };
1366
1847
  }
1367
- if (result.length > 0) {
1368
- return result;
1848
+ get statusCode() {
1849
+ return super.statusCode;
1369
1850
  }
1370
- }
1371
- function trimTrailingSlash(origin) {
1372
- return origin.endsWith("/") ? origin.slice(0, -1) : origin;
1373
- }
1374
- function getMethodToUse(request, isPreFlight) {
1375
- return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
1376
- }
1377
- function getHeadersToUse(request, isPreFlight) {
1378
- const headers2 = request.headers;
1379
- return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
1380
- }
1381
- var matchingCorsConfigSource = (opts) => {
1382
- return async (exchange) => {
1383
- for (const [matcher, config] of opts.mappings) {
1384
- if ((await matcher(exchange)).match) {
1385
- logger.debug(`resolved cors config on '${exchange.request.path}' using ${matcher}: ${JSON.stringify(config)}`);
1386
- return config;
1387
- }
1851
+ set writeHandler(handler) {
1852
+ this.#body = () => {
1853
+ throw new Error("Not available with custom write handler");
1854
+ };
1855
+ this.#writeHandler = handler;
1856
+ }
1857
+ getNativeResponse() {
1858
+ throw new Error("This is a mock. No running server, no native response.");
1859
+ }
1860
+ applyStatusCode() {
1861
+ }
1862
+ applyHeaders() {
1863
+ }
1864
+ applyCookies() {
1865
+ for (const cookie of this.cookies) {
1866
+ this.headers.add("Set-Cookie", super.setCookieValue(cookie));
1388
1867
  }
1389
- };
1868
+ }
1869
+ bodyInternal(body) {
1870
+ const it = async function* () {
1871
+ const resolved = await body;
1872
+ if (resolved === void 0) {
1873
+ return;
1874
+ }
1875
+ yield resolved;
1876
+ }();
1877
+ return this.#writeHandler(import_web.default.ReadableStream.from(it));
1878
+ }
1879
+ async end() {
1880
+ return this.doCommit(async () => {
1881
+ return await new Promise((resolve, reject) => {
1882
+ this.#writeHandler(import_web.default.ReadableStream.from([]));
1883
+ });
1884
+ });
1885
+ }
1886
+ getBody() {
1887
+ return this.#body();
1888
+ }
1390
1889
  };
1391
1890
 
1392
1891
  // src/app/cors.ts
1393
1892
  function mockUpgradeExchange(path) {
1394
- const request = new MockHttpRequest(path, "GET");
1395
- request.headers.set("Upgrade", "websocket");
1893
+ const request = new MockServerHttpRequest(path, "GET");
1894
+ request.setHeader("Upgrade", "websocket");
1396
1895
  request.upgrade = true;
1397
- return new DefaultWebExchange(request, new MockHttpResponse());
1896
+ return new DefaultWebExchange(request, new MockServerHttpResponse());
1398
1897
  }
1399
1898
  async function createCorsConfigSource(context) {
1400
1899
  const { sockets: routes, cors } = context;
@@ -1411,8 +1910,8 @@ async function createCorsConfigSource(context) {
1411
1910
  routeCorsConfig = combineCorsConfig(routeCorsConfig, {
1412
1911
  allowOrigins: route.originFilters?.allow,
1413
1912
  allowMethods: ["GET", "CONNECT"],
1414
- allowHeaders: ["upgrade", "connection", "origin", "sec-websocket-key", "sec-websocket-version", "sec-websocket-protocol", "sec-websocket-extensions"],
1415
- exposeHeaders: ["sec-websocket-accept", "sec-websocket-protocol", "sec-websocket-extensions"],
1913
+ allowHeaders: ["Upgrade", "Connection", "Origin", "Sec-Websocket-Key", "Sec-Websocket-Version", "Sec-Websocket-Protocol", "Sec-Websocket-Extensions"],
1914
+ exposeHeaders: ["Sec-Websocket-Accept", "Sec-Websocket-Protocol", "Sec-Websocket-Extensions"],
1416
1915
  allowCredentials: route.authorize?.access !== "permitted"
1417
1916
  });
1418
1917
  validatedConfigs.push([and([upgradeMatcher, pattern(path)]), validateCorsConfig(routeCorsConfig)]);
@@ -1655,7 +2154,7 @@ var httpBasicEntryPoint = (opts) => {
1655
2154
  const headerValue = createHeaderValue(opts?.realm ?? DEFAULT_REALM);
1656
2155
  return async (exchange, _error) => {
1657
2156
  const { response } = exchange;
1658
- response.statusCode = 401;
2157
+ response.setStatusCode(HttpStatus.UNAUTHORIZED);
1659
2158
  response.headers.set("WWW-Authenticate", headerValue);
1660
2159
  };
1661
2160
  };
@@ -1675,12 +2174,17 @@ var httpBasicAuthenticationConverter = (opts) => {
1675
2174
  if (parts.length !== 2) {
1676
2175
  return void 0;
1677
2176
  }
1678
- return { type: "UsernamePassword", authenticated: false, principal: parts[0], credentials: parts[1] };
2177
+ return {
2178
+ type: "UsernamePassword",
2179
+ authenticated: false,
2180
+ principal: parts[0],
2181
+ credentials: parts[1]
2182
+ };
1679
2183
  };
1680
2184
  };
1681
2185
 
1682
2186
  // src/server/security/security-context.ts
1683
- var import_node_async_hooks = require("node:async_hooks");
2187
+ var import_node_async_hooks2 = require("node:async_hooks");
1684
2188
  var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder {
1685
2189
  static hasSecurityContext(storage) {
1686
2190
  return storage.getStore()?.securityContext !== void 0;
@@ -1692,7 +2196,7 @@ var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder
1692
2196
  delete storage.getStore()?.securityContext;
1693
2197
  }
1694
2198
  static withSecurityContext(securityContext) {
1695
- return (storage = new import_node_async_hooks.AsyncLocalStorage()) => {
2199
+ return (storage = new import_node_async_hooks2.AsyncLocalStorage()) => {
1696
2200
  storage.getStore().securityContext = securityContext;
1697
2201
  return storage;
1698
2202
  };
@@ -1769,8 +2273,7 @@ function authenticationFilter(opts) {
1769
2273
  var httpStatusEntryPoint = (opts) => {
1770
2274
  return async (exchange, _error) => {
1771
2275
  const response = exchange.response;
1772
- response.statusCode = opts.httpStatus.code;
1773
- response.statusMessage = opts.httpStatus.message;
2276
+ response.setStatusCode(opts.httpStatus);
1774
2277
  };
1775
2278
  };
1776
2279
 
@@ -1778,7 +2281,7 @@ var httpStatusEntryPoint = (opts) => {
1778
2281
  var logger2 = getLogger("auth.entry-point");
1779
2282
  var delegatingEntryPoint = (opts) => {
1780
2283
  const defaultEntryPoint = opts.defaultEntryPoint ?? (async ({ response }, _error) => {
1781
- response.statusCode = 401;
2284
+ response.setStatusCode(HttpStatus.UNAUTHORIZED);
1782
2285
  await response.end();
1783
2286
  });
1784
2287
  return async (exchange, error) => {
@@ -1816,12 +2319,12 @@ function httpBasic(opts) {
1816
2319
  const headers2 = exchange.request.headers;
1817
2320
  const h = headers2.list("X-Requested-With");
1818
2321
  if (h.includes("XMLHttpRequest")) {
1819
- return { match: true };
2322
+ return match();
1820
2323
  }
1821
- return { match: false };
2324
+ return NO_MATCH;
1822
2325
  };
1823
2326
  const defaultEntryPoint = delegatingEntryPoint({
1824
- entryPoints: [[xhrMatcher, httpStatusEntryPoint({ httpStatus: { code: 401 } })]],
2327
+ entryPoints: [[xhrMatcher, httpStatusEntryPoint({ httpStatus: HttpStatus.UNAUTHORIZED })]],
1825
2328
  defaultEntryPoint: httpBasicEntryPoint({})
1826
2329
  });
1827
2330
  const entryPoint = opts.entryPoint ?? defaultEntryPoint;
@@ -1864,7 +2367,7 @@ var DEFAULT_URI = "https://tools.ietf.org/html/rfc6750#section-3.1";
1864
2367
  function invalidToken(message) {
1865
2368
  return {
1866
2369
  errorCode: BearerTokenErrorCodes.invalid_token,
1867
- httpStatus: 401,
2370
+ httpStatus: HttpStatus.UNAUTHORIZED,
1868
2371
  description: message,
1869
2372
  uri: DEFAULT_URI
1870
2373
  };
@@ -1872,7 +2375,7 @@ function invalidToken(message) {
1872
2375
  function invalidRequest(message) {
1873
2376
  return {
1874
2377
  errorCode: BearerTokenErrorCodes.invalid_request,
1875
- httpStatus: 400,
2378
+ httpStatus: HttpStatus.BAD_REQUEST,
1876
2379
  description: message,
1877
2380
  uri: DEFAULT_URI
1878
2381
  };
@@ -1948,7 +2451,10 @@ async function resolveFromBody(exchange, allow = false) {
1948
2451
  if (!allow || "application/x-www-form-urlencoded" !== request.headers.one("content-type") || request.method !== "POST") {
1949
2452
  return;
1950
2453
  }
1951
- return resolveTokens(await exchange.request.formData);
2454
+ const parameters = await exchange.request.formData();
2455
+ if (parameters) {
2456
+ return resolveTokens(parameters);
2457
+ }
1952
2458
  }
1953
2459
  var token_converter_default = serverBearerTokenAuthenticationConverter;
1954
2460
 
@@ -1978,7 +2484,7 @@ function getStatus(authError) {
1978
2484
  return error.httpStatus;
1979
2485
  }
1980
2486
  }
1981
- return 401;
2487
+ return HttpStatus.UNAUTHORIZED;
1982
2488
  }
1983
2489
  function createParameters(authError, realm) {
1984
2490
  const parameters = /* @__PURE__ */ new Map();
@@ -2007,7 +2513,7 @@ var bearerTokenServerAuthenticationEntryPoint = (opts) => {
2007
2513
  const wwwAuthenticate = computeWWWAuthenticate(parameters);
2008
2514
  const { response } = exchange;
2009
2515
  response.headers.set("WWW-Authenticate", wwwAuthenticate);
2010
- response.statusCode = status;
2516
+ response.setStatusCode(status);
2011
2517
  await response.end();
2012
2518
  };
2013
2519
  };
@@ -2094,18 +2600,17 @@ async function commenceAuthentication(exchange, authentication, entryPoint) {
2094
2600
  }
2095
2601
  await entryPoint(exchange, e);
2096
2602
  }
2097
- function httpStatusAccessDeniedHandler(status, message) {
2603
+ function httpStatusAccessDeniedHandler(httpStatus) {
2098
2604
  return async (exchange, _error) => {
2099
- exchange.response.statusCode = status;
2100
- exchange.response.statusMessage = message;
2101
- exchange.response.headers.set("Content-Type", "text/plain");
2102
- const bytes = Buffer.from("Access Denied", "utf-8");
2103
- exchange.response.headers.set("Content-Length", bytes.length);
2104
- await exchange.response.end(bytes);
2605
+ exchange.response.setStatusCode(httpStatus);
2606
+ exchange.response.headers.set("Content-Type", "text/plain; charset=utf-8");
2607
+ const buffer = Buffer.from("Access Denied", "utf-8");
2608
+ exchange.response.headers.set("Content-Length", buffer.length);
2609
+ await exchange.response.body(buffer);
2105
2610
  };
2106
2611
  }
2107
2612
  var errorFilter = (opts) => {
2108
- const accessDeniedHandler = httpStatusAccessDeniedHandler(403, "Forbidden");
2613
+ const accessDeniedHandler = httpStatusAccessDeniedHandler(HttpStatus.FORBIDDEN);
2109
2614
  const authenticationEntryPoint = opts.authenticationEntryPoint ?? httpBasicEntryPoint();
2110
2615
  return async (exchange, next) => {
2111
2616
  try {
@@ -2117,17 +2622,19 @@ var errorFilter = (opts) => {
2117
2622
  await commenceAuthentication(exchange, void 0, authenticationEntryPoint);
2118
2623
  } else {
2119
2624
  if (!principal.authenticated) {
2120
- return accessDeniedHandler(exchange, error);
2625
+ await accessDeniedHandler(exchange, error);
2121
2626
  }
2122
2627
  await commenceAuthentication(exchange, principal, authenticationEntryPoint);
2123
2628
  }
2629
+ return;
2124
2630
  }
2631
+ throw error;
2125
2632
  }
2126
2633
  };
2127
2634
  };
2128
2635
 
2129
2636
  // src/server/security/delegating-authorization-manager.ts
2130
- var logger3 = getLogger("auth");
2637
+ var logger3 = getLogger("security.auth");
2131
2638
  function delegatingAuthorizationManager(opts) {
2132
2639
  const check = async (authentication, exchange) => {
2133
2640
  let decision;
@@ -2170,6 +2677,25 @@ function authorizationFilter(opts) {
2170
2677
  };
2171
2678
  }
2172
2679
 
2680
+ // src/server/security/exchange-filter.ts
2681
+ var SecurityContextServerWebExchange = class extends ServerWebExchangeDecorator {
2682
+ #context;
2683
+ constructor(exchange, context) {
2684
+ super(exchange);
2685
+ this.#context = context;
2686
+ }
2687
+ async principal() {
2688
+ const context = await this.#context();
2689
+ return context?.authentication;
2690
+ }
2691
+ };
2692
+ var exchangeFilter = (opts) => {
2693
+ const storage = opts.storage;
2694
+ return async (exchange, next) => {
2695
+ await next(new SecurityContextServerWebExchange(exchange, async () => await AsyncStorageSecurityContextHolder.getContext(storage)));
2696
+ };
2697
+ };
2698
+
2173
2699
  // src/server/security/config.ts
2174
2700
  var filterOrder = {
2175
2701
  first: Number.MAX_SAFE_INTEGER,
@@ -2178,6 +2704,7 @@ var filterOrder = {
2178
2704
  cors: 3 * 100,
2179
2705
  http_basic: 6 * 100,
2180
2706
  authentication: 8 * 100,
2707
+ security_context_server_web_exchange: 15 * 100,
2181
2708
  error_translation: 18 * 100,
2182
2709
  authorization: 19 * 100,
2183
2710
  last: Number.MAX_SAFE_INTEGER
@@ -2264,9 +2791,12 @@ var config_default = (config, context) => {
2264
2791
  const authenticationConverterMatcher = async (exchange) => {
2265
2792
  try {
2266
2793
  const a = await authenticationConverter(exchange);
2267
- return { match: a !== void 0 };
2794
+ if (a === void 0) {
2795
+ return NO_MATCH;
2796
+ }
2797
+ return match();
2268
2798
  } catch (e) {
2269
- return { match: false };
2799
+ return NO_MATCH;
2270
2800
  }
2271
2801
  };
2272
2802
  const entryPoint = token_entry_point_default({});
@@ -2280,6 +2810,9 @@ var config_default = (config, context) => {
2280
2810
  filter[filterOrderSymbol] = filterOrder.authentication;
2281
2811
  middleware.push(filter);
2282
2812
  }
2813
+ const exchangeF = exchangeFilter({ storage: context.storage });
2814
+ middleware.push(exchangeF);
2815
+ exchangeF[filterOrderSymbol] = filterOrder.security_context_server_web_exchange;
2283
2816
  if (config.authorize !== void 0) {
2284
2817
  const errorFf = errorFilter({ authenticationEntryPoint: this.authenticationEntryPoint });
2285
2818
  errorFf[filterOrderSymbol] = filterOrder.error_translation;
@@ -2373,121 +2906,341 @@ async function httpSecurity(context) {
2373
2906
  return config_default(config, { storage, corsConfigSource });
2374
2907
  }
2375
2908
 
2376
- // src/server.ts
2377
- var logger5 = getLogger("app");
2378
- function secureContextOptions(ssl) {
2379
- const options = {};
2380
- if (ssl.key) options.key = (0, import_node_fs.readFileSync)(ssl.key);
2381
- if (ssl.cert) options.cert = (0, import_node_fs.readFileSync)(ssl.cert);
2382
- if (ssl.ca) options.ca = (0, import_node_fs.readFileSync)(ssl.ca);
2383
- return options;
2384
- }
2385
- async function createListener(context, onSocketError) {
2386
- const storage = context.storage;
2387
- const security = await httpSecurity(context);
2388
- const listener = compose(
2389
- server_header_default(context.serverHeader),
2390
- ...security,
2391
- // websocket upgrade handler
2392
- async (exchange, next) => {
2393
- const [route, path] = findSocketRoute(exchange, context);
2394
- if (route !== void 0) {
2395
- const { request, response } = exchange;
2396
- const upgradeMatchResult = await upgradeMatcher(exchange);
2397
- if ((request.method === "GET" || request.method === "CONNECT") && upgradeMatchResult.match) {
2398
- const socket = response.unsafeServerResponse.socket;
2399
- const host = request.host;
2400
- const info2 = socketKey(socket);
2401
- if (route.wss) {
2402
- socket.removeListener("error", onSocketError);
2403
- const wss = route.wss;
2404
- if (route.maxConnections !== void 0 && wss.clients?.size >= route.maxConnections) {
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
- }
2909
+ // src/server/handler.ts
2910
+ var import_node_async_hooks3 = require("node:async_hooks");
2911
+ var HttpHeadResponseDecorator = class extends ServerHttpResponseDecorator {
2912
+ };
2913
+ var HandlerAdapter = class {
2914
+ #logger;
2915
+ #enableLoggingRequestDetails = false;
2916
+ #delegate;
2917
+ #storage;
2918
+ constructor(logger7, delegate) {
2919
+ this.#logger = logger7;
2920
+ this.#delegate = delegate;
2921
+ }
2922
+ createExchange(request, response) {
2923
+ const exchange = new DefaultWebExchange(request, response);
2924
+ return exchange;
2925
+ }
2926
+ set storage(storage) {
2927
+ this.#storage = storage;
2928
+ }
2929
+ set enableLoggingRequestDetails(value) {
2930
+ this.#enableLoggingRequestDetails = value;
2931
+ }
2932
+ formatHeaders(headers2) {
2933
+ let result = "{";
2934
+ for (const key of headers2.keys()) {
2935
+ if (!this.#enableLoggingRequestDetails) {
2936
+ result += "masked, ";
2937
+ break;
2442
2938
  } else {
2443
- await next();
2939
+ const value = headers2.get(key);
2940
+ result += `"${key}": "${value}", `;
2444
2941
  }
2445
- },
2446
- ...context.middleware,
2447
- // health check
2448
- async ({ request, response }, next) => {
2449
- if (request.method === "GET" && request.path === "/health") {
2450
- response.statusCode = 200;
2451
- await response.end(import_node_http2.default.STATUS_CODES[200]);
2942
+ }
2943
+ if (result.endsWith(", ")) {
2944
+ result = result.slice(0, -2);
2945
+ }
2946
+ result += "}";
2947
+ return result;
2948
+ }
2949
+ formatRequest(request) {
2950
+ const query = request.URL.search;
2951
+ return `HTTP ${request.method} "${request.path}${query}`;
2952
+ }
2953
+ logRequest(exchange) {
2954
+ if (this.#logger.enabledFor("debug")) {
2955
+ const trace = this.#logger.enabledFor("trace");
2956
+ this.#logger.debug(`${exchange.logPrefix}${this.formatRequest(exchange.request)}${trace ? `, headers: ${this.formatHeaders(exchange.request.headers)}` : ""}"`);
2957
+ }
2958
+ }
2959
+ logResponse(exchange) {
2960
+ if (this.#logger.enabledFor("debug")) {
2961
+ const trace = this.#logger.enabledFor("trace");
2962
+ const status = exchange.response.statusCode;
2963
+ this.#logger.debug(`${exchange.logPrefix}Completed ${status ?? "200 OK"}${trace ? `, headers: ${this.formatHeaders(exchange.response.headers)}` : ""}"`);
2964
+ }
2965
+ }
2966
+ handleUnresolvedError(exchange, error) {
2967
+ const { request, response, logPrefix } = exchange;
2968
+ if (response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR)) {
2969
+ this.#logger.error(`${logPrefix}500 Server Error for ${this.formatRequest(request)}`, error);
2970
+ return;
2971
+ }
2972
+ this.#logger.error(`${logPrefix}Error [${error.message} for ${this.formatRequest(request)}, but already ended (${response.statusCode})`, error);
2973
+ throw error;
2974
+ }
2975
+ async web(exchange) {
2976
+ return await this.#delegate(exchange);
2977
+ }
2978
+ async http(request, response) {
2979
+ const exchange = this.createExchange(request, response);
2980
+ const callback = () => {
2981
+ this.logRequest(exchange);
2982
+ return this.web(exchange).then(() => {
2983
+ this.logResponse(exchange);
2984
+ }).catch((error) => {
2985
+ this.handleUnresolvedError(exchange, error);
2986
+ }).then(async () => {
2987
+ await exchange.response.end();
2988
+ });
2989
+ };
2990
+ await new Promise((resolve, reject) => {
2991
+ if (this.#storage !== void 0) {
2992
+ this.#storage.run({ exchange }, () => {
2993
+ callback().then(() => resolve()).catch((error) => reject(error));
2994
+ });
2452
2995
  } else {
2453
- await next();
2996
+ callback().then(() => resolve()).catch((error) => reject(error));
2454
2997
  }
2455
- },
2456
- // home page
2457
- async ({ request, response }, next) => {
2458
- if (request.method === "GET" && request.path === "/") {
2459
- await response.end(`io.Gateway Server`);
2998
+ });
2999
+ }
3000
+ };
3001
+ var WebHttpHandlerBuilder = class {
3002
+ #webHandler;
3003
+ #storage = new import_node_async_hooks3.AsyncLocalStorage();
3004
+ #handlerDecorator;
3005
+ storage(storage) {
3006
+ this.#storage = storage;
3007
+ return this;
3008
+ }
3009
+ httpHandlerDecorator(decorator) {
3010
+ if (this.#handlerDecorator === void 0) {
3011
+ this.#handlerDecorator = decorator;
3012
+ } else {
3013
+ const previousDecorator = this.#handlerDecorator;
3014
+ this.#handlerDecorator = (handler) => {
3015
+ handler = previousDecorator(handler);
3016
+ return decorator(handler);
3017
+ };
3018
+ }
3019
+ return this;
3020
+ }
3021
+ constructor(webHandler) {
3022
+ this.#webHandler = webHandler;
3023
+ }
3024
+ build() {
3025
+ const logger7 = getLogger("http");
3026
+ const adapter = new HandlerAdapter(logger7, this.#webHandler);
3027
+ if (this.#storage !== void 0) adapter.storage = this.#storage;
3028
+ adapter.enableLoggingRequestDetails = false;
3029
+ const adapted = async (request, response) => adapter.http(request, response);
3030
+ return this.#handlerDecorator ? this.#handlerDecorator(adapted) : adapted;
3031
+ }
3032
+ };
3033
+
3034
+ // src/server/ws.ts
3035
+ var import_ws = require("ws");
3036
+
3037
+ // src/server/socket.ts
3038
+ function createHandshakeInfo(req, protocol) {
3039
+ const exchange = req?.exchange;
3040
+ const request = exchange?.request ?? new HttpServerRequest(req);
3041
+ const principalPromiseProvider = exchange?.principal;
3042
+ const principal = principalPromiseProvider ? principalPromiseProvider.bind(exchange) : async function principal2() {
3043
+ return void 0;
3044
+ };
3045
+ const url = request.URL;
3046
+ const headers2 = new MapHttpHeaders();
3047
+ for (const key of request.headers.keys()) {
3048
+ headers2.set(key, request.headers.list(key));
3049
+ }
3050
+ const cookies = request.cookies;
3051
+ const logPrefix = exchange?.logPrefix ?? `[${request.id}] `;
3052
+ const remoteAddress = request.remoteAddress;
3053
+ const handshake = {
3054
+ url,
3055
+ headers: headers2,
3056
+ cookies,
3057
+ principal,
3058
+ protocol,
3059
+ remoteAddress,
3060
+ logPrefix
3061
+ };
3062
+ return handshake;
3063
+ }
3064
+ function webSockets(context) {
3065
+ const sockets = async (exchange, next) => {
3066
+ const request = exchange.request;
3067
+ const path = request.path ?? "/";
3068
+ const routes = context.sockets;
3069
+ const route = routes.get(path) ?? Array.from(routes.values()).find((route2) => {
3070
+ if (path === "/" && route2.default === true) {
3071
+ return true;
3072
+ }
3073
+ });
3074
+ if (route !== void 0) {
3075
+ const { request: request2, response } = exchange;
3076
+ const upgradeMatchResult = await upgradeMatcher(exchange);
3077
+ if ((request2.method === "GET" || request2.method === "CONNECT") && upgradeMatchResult.match) {
3078
+ if (route.upgradeStrategy !== void 0) {
3079
+ route.upgradeStrategy(exchange);
3080
+ return;
3081
+ } else {
3082
+ throw new Error(`No upgrade strategy defined for route on ${path}`);
3083
+ }
2460
3084
  } else {
2461
- await next();
3085
+ if (route.default) {
3086
+ await next();
3087
+ return;
3088
+ }
3089
+ response.setStatusCode(HttpStatus.UPGRADE_REQUIRED);
3090
+ response.headers.set("Upgrade", "websocket").set("Connection", "Upgrade").set("Content-Type", "text/plain");
3091
+ const buffer = Buffer.from(`This service [${request2.path}] requires use of the websocket protocol.`, "utf-8");
3092
+ await response.body(buffer);
2462
3093
  }
2463
- },
2464
- // not found
2465
- async ({ response }, _next) => {
2466
- response.statusCode = 404;
2467
- await response.end(import_node_http2.default.STATUS_CODES[404]);
3094
+ } else {
3095
+ await next();
2468
3096
  }
2469
- );
2470
- return async (request, response) => {
2471
- request.socket.addListener("error", onSocketError);
2472
- const exchange = new DefaultWebExchange(new HttpServerRequest(request), new HttpServerResponse(response));
2473
- request.exchange = exchange;
2474
- return await storage.run({ exchange }, async () => {
2475
- if (logger5.enabledFor("debug")) {
2476
- const socket = exchange.request.socket;
2477
- if (logger5.enabledFor("debug")) {
2478
- logger5.debug(`received ${exchange.method} request for ${exchange.path} from ${socket.remoteAddress}:${socket.remotePort}`);
2479
- }
3097
+ };
3098
+ return [sockets];
3099
+ }
3100
+
3101
+ // src/server/ws.ts
3102
+ var ExtendedWebSocket = class extends import_ws.WebSocket {
3103
+ constructor(first, second, options) {
3104
+ super(null, void 0, options);
3105
+ }
3106
+ connected;
3107
+ };
3108
+ var logger5 = getLogger("ws");
3109
+ function upgradeStrategy(path, route, wss, onSocketError) {
3110
+ return (exchange) => {
3111
+ const { logPrefix, request } = exchange;
3112
+ const req = ServerHttpRequestDecorator.getNativeRequest(request);
3113
+ req.exchange = exchange;
3114
+ const { socket, upgradeHead } = req;
3115
+ const host = request.host;
3116
+ socket.removeListener("error", onSocketError);
3117
+ if (route.maxConnections !== void 0 && wss.clients?.size >= route.maxConnections) {
3118
+ logger5.warn(`${logPrefix}dropping ws connection request on ${host}${path}. max connections exceeded.`);
3119
+ socket.destroy();
3120
+ return;
3121
+ }
3122
+ const origin = request.headers.one("origin");
3123
+ if (!acceptsOrigin(origin, route.originFilters)) {
3124
+ if (logger5.enabledFor("info")) {
3125
+ logger5.info(`${logPrefix}dropping ws connection request on ${host}${path}. origin ${origin ?? "<missing>"}`);
2480
3126
  }
2481
- try {
2482
- return await listener(exchange);
2483
- } catch (e) {
2484
- if (logger5.enabledFor("warn")) {
2485
- logger5.warn(`error processing request for ${exchange.path}`, e);
2486
- }
2487
- } finally {
2488
- await exchange.response.end();
3127
+ socket.destroy();
3128
+ return;
3129
+ }
3130
+ if (logger5.enabledFor("debug")) {
3131
+ logger5.debug(`${logPrefix}accepted new ws connection request on ${host}${path}`);
3132
+ }
3133
+ wss.handleUpgrade(req, socket, upgradeHead, (client, req2) => {
3134
+ wss.emit("connection", client, req2);
3135
+ });
3136
+ };
3137
+ }
3138
+ function applyHandshakeHeaders(headers2, response) {
3139
+ const seen = /* @__PURE__ */ new Set();
3140
+ headers2.forEach((header, index) => {
3141
+ if (index === 0 && header.startsWith("HTTP/1.1 101 ")) {
3142
+ response.setStatusCode(HttpStatus.SWITCHING_PROTOCOLS);
3143
+ return;
3144
+ }
3145
+ const [name, value] = header.split(": ");
3146
+ if (response.headers.has(name)) {
3147
+ headers2[index] = `${name}: ${response.headers.one(name)}`;
3148
+ } else {
3149
+ response.headers.set(name, value);
3150
+ }
3151
+ seen.add(name.toLowerCase());
3152
+ });
3153
+ const nativeResponse = ServerHttpResponseDecorator.getNativeResponse(response);
3154
+ for (const name of nativeResponse.getRawHeaderNames()) {
3155
+ const nameLowerCase = name.toLowerCase();
3156
+ if (!seen.has(nameLowerCase)) {
3157
+ const value = response.headers.get(nameLowerCase);
3158
+ if (value !== void 0) {
3159
+ headers2.push(`${name}: ${value}`);
3160
+ }
3161
+ }
3162
+ }
3163
+ nativeResponse.markHeadersSent();
3164
+ }
3165
+ async function initRoute(path, route, endpoint, storage, onSocketError) {
3166
+ try {
3167
+ logger5.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
3168
+ const wss = new import_ws.WebSocketServer({
3169
+ noServer: true,
3170
+ WebSocket: ExtendedWebSocket,
3171
+ autoPong: false
3172
+ });
3173
+ const handler = await route.factory({ endpoint, storage });
3174
+ wss.on("error", (err) => {
3175
+ logger5.error(`error starting the ws server for [${path}]`, err);
3176
+ }).on("listening", () => {
3177
+ logger5.info(`ws server for [${path}] is listening`);
3178
+ }).on("headers", (headers2, request) => {
3179
+ if (request.exchange !== void 0) {
3180
+ const { response } = request.exchange;
3181
+ applyHandshakeHeaders(headers2, response);
2489
3182
  }
3183
+ }).on("connection", (socket, request) => {
3184
+ const handshake = createHandshakeInfo(request, socket.protocol);
3185
+ socket.on("pong", () => socket.connected = true);
3186
+ socket.on("ping", () => {
3187
+ });
3188
+ handler({ socket, handshake });
2490
3189
  });
3190
+ const pingInterval = route.ping;
3191
+ if (pingInterval) {
3192
+ const pingIntervalId = setInterval(() => {
3193
+ for (const client of wss.clients) {
3194
+ if (client.connected === false) {
3195
+ client.terminate();
3196
+ }
3197
+ client.connected = false;
3198
+ client.ping();
3199
+ }
3200
+ }, pingInterval);
3201
+ wss.on("close", () => {
3202
+ clearInterval(pingIntervalId);
3203
+ });
3204
+ }
3205
+ route.upgradeStrategy = upgradeStrategy(path, route, wss, onSocketError);
3206
+ route.close = async () => {
3207
+ await handler.close?.call(handler);
3208
+ logger5.info(`stopping ws server for [${path}]. clients: ${wss.clients?.size ?? 0}`);
3209
+ wss.clients?.forEach((client) => {
3210
+ client.terminate();
3211
+ });
3212
+ wss.close();
3213
+ };
3214
+ } catch (e) {
3215
+ logger5.warn(`failed to init route ${path}`, e);
3216
+ }
3217
+ }
3218
+
3219
+ // src/server.ts
3220
+ var logger6 = getLogger("app");
3221
+ function secureContextOptions(ssl) {
3222
+ const options = {};
3223
+ if (ssl.key) options.key = (0, import_node_fs.readFileSync)(ssl.key);
3224
+ if (ssl.cert) options.cert = (0, import_node_fs.readFileSync)(ssl.cert);
3225
+ if (ssl.ca) options.ca = (0, import_node_fs.readFileSync)(ssl.ca);
3226
+ return options;
3227
+ }
3228
+ async function createListener(builder, onSocketError) {
3229
+ const httpHandler = builder.build();
3230
+ return async (req, resOrUpgradeHead) => {
3231
+ req.socket.addListener("error", onSocketError);
3232
+ let res;
3233
+ if (resOrUpgradeHead instanceof ExtendedHttpServerResponse) {
3234
+ res = resOrUpgradeHead;
3235
+ } else {
3236
+ req.upgradeHead = resOrUpgradeHead;
3237
+ res = new ExtendedHttpServerResponse(req);
3238
+ res.assignSocket(req.socket);
3239
+ }
3240
+ const request = new HttpServerRequest(req);
3241
+ const response = new HttpServerResponse(res);
3242
+ const decoratedResponse = request.method === "HEAD" ? new HttpHeadResponseDecorator(response) : response;
3243
+ await httpHandler(request, decoratedResponse);
2491
3244
  };
2492
3245
  }
2493
3246
  function promisify(fn) {
@@ -2512,8 +3265,44 @@ function memoryMonitor(config) {
2512
3265
  });
2513
3266
  }
2514
3267
  }
2515
- function regexAwareReplacer(_key, value) {
2516
- return value instanceof RegExp ? value.toString() : value;
3268
+ async function initBuilder(context) {
3269
+ const storage = context.storage;
3270
+ const security = await httpSecurity(context);
3271
+ const sockets = webSockets(context);
3272
+ const handler = compose(
3273
+ server_header_default(context.serverHeader),
3274
+ ...security,
3275
+ ...sockets,
3276
+ ...context.middleware,
3277
+ // health check
3278
+ async ({ request, response }, next) => {
3279
+ if (request.method === "GET" && request.path === "/health") {
3280
+ response.setStatusCode(HttpStatus.OK);
3281
+ const buffer = Buffer.from("UP", "utf-8");
3282
+ response.headers.set("Content-Type", "text/plain; charset=utf-8");
3283
+ await response.body(buffer);
3284
+ } else {
3285
+ await next();
3286
+ }
3287
+ },
3288
+ // home page
3289
+ async ({ request, response }, next) => {
3290
+ if (request.method === "GET" && request.path === "/") {
3291
+ response.setStatusCode(HttpStatus.OK);
3292
+ const buffer = Buffer.from("io.Gateway Server", "utf-8");
3293
+ response.headers.set("Content-Type", "text/plain; charset=utf-8");
3294
+ await response.body(buffer);
3295
+ } else {
3296
+ await next();
3297
+ }
3298
+ },
3299
+ // not found
3300
+ async ({ response }, _next) => {
3301
+ response.setStatusCode(HttpStatus.NOT_FOUND);
3302
+ await response.end();
3303
+ }
3304
+ );
3305
+ return new WebHttpHandlerBuilder(handler).storage(storage);
2517
3306
  }
2518
3307
  var Factory = async (options) => {
2519
3308
  const ssl = options.ssl;
@@ -2525,7 +3314,7 @@ var Factory = async (options) => {
2525
3314
  cors: [],
2526
3315
  authConfig: options.auth,
2527
3316
  authorize: [],
2528
- storage: new import_node_async_hooks2.AsyncLocalStorage(),
3317
+ storage: new import_node_async_hooks4.AsyncLocalStorage(),
2529
3318
  sockets: /* @__PURE__ */ new Map()
2530
3319
  };
2531
3320
  const gw = import_gateway6.IOGateway.Factory({ ...options.gateway });
@@ -2540,8 +3329,9 @@ var Factory = async (options) => {
2540
3329
  }
2541
3330
  const ports = portRange(options.port ?? 0);
2542
3331
  const host = options.host;
2543
- const onSocketError = (err) => logger5.error(`socket error: ${err}`, err);
2544
- const listener = await createListener(context, onSocketError);
3332
+ const onSocketError = (err) => logger6.error(`socket error: ${err}`, err);
3333
+ const builder = await initBuilder(context);
3334
+ const listener = await createListener(builder, onSocketError);
2545
3335
  const serverP = new Promise((resolve, reject) => {
2546
3336
  const server2 = createServer({
2547
3337
  IncomingMessage: ExtendedHttpIncomingMessage,
@@ -2549,95 +3339,45 @@ var Factory = async (options) => {
2549
3339
  }, listener);
2550
3340
  server2.on("error", (e) => {
2551
3341
  if (e["code"] === "EADDRINUSE") {
2552
- logger5.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
3342
+ logger6.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
2553
3343
  const { value: port } = ports.next();
2554
3344
  if (port) {
2555
- logger5.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
3345
+ logger6.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
2556
3346
  server2.close();
2557
3347
  server2.listen(port, host);
2558
3348
  } else {
2559
- logger5.warn(`all configured port(s) ${options.port} are in use. closing...`);
3349
+ logger6.warn(`all configured port(s) ${options.port} are in use. closing...`);
2560
3350
  server2.close();
2561
3351
  reject(e);
2562
3352
  }
2563
3353
  } else {
2564
- logger5.error(`server error: ${e.message}`, e);
3354
+ logger6.error(`server error: ${e.message}`, e);
2565
3355
  reject(e);
2566
3356
  }
2567
3357
  });
2568
3358
  server2.on("listening", async () => {
2569
3359
  const info2 = server2.address();
2570
3360
  for (const [path, route] of context.sockets) {
2571
- try {
2572
- logger5.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
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
- }
3361
+ const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info2.port}${path}`;
3362
+ await initRoute(path, route, endpoint, context.storage, onSocketError);
2621
3363
  }
2622
- logger5.info(`http server listening on ${info2.address}:${info2.port}`);
3364
+ logger6.info(`http server listening on ${info2.address}:${info2.port}`);
2623
3365
  resolve(server2);
2624
3366
  });
2625
- server2.on("upgrade", (req, socket, head) => {
2626
- socket.on("error", onSocketError);
3367
+ server2.on("upgrade", (req, _socket, head) => {
2627
3368
  try {
2628
- const res = ExtendedHttpServerResponse.forUpgrade(req, socket, head);
2629
- listener(req, res);
3369
+ listener(req, head);
2630
3370
  } catch (err) {
2631
- logger5.error(`upgrade error: ${err}`, err);
3371
+ logger6.error(`upgrade error: ${err}`, err);
2632
3372
  }
2633
3373
  }).on("close", async () => {
2634
- logger5.info(`http server closed.`);
3374
+ logger6.info(`http server closed.`);
2635
3375
  });
2636
3376
  try {
2637
3377
  const { value: port } = ports.next();
2638
3378
  server2.listen(port, host);
2639
3379
  } catch (e) {
2640
- logger5.error(`error starting web socket server`, e);
3380
+ logger6.error(`error starting web socket server`, e);
2641
3381
  reject(e instanceof Error ? e : new Error(`listen failed: ${e}`));
2642
3382
  }
2643
3383
  });
@@ -2647,16 +3387,11 @@ var Factory = async (options) => {
2647
3387
  async close() {
2648
3388
  for (const [path, route] of context.sockets) {
2649
3389
  try {
2650
- if (route.close) {
3390
+ if (route.close !== void 0) {
2651
3391
  await route.close();
2652
3392
  }
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
3393
  } catch (e) {
2659
- logger5.warn(`error closing route ${path}`, e);
3394
+ logger6.warn(`error closing route ${path}`, e);
2660
3395
  }
2661
3396
  }
2662
3397
  await promisify((cb) => {