@interopio/gateway-server 0.5.2-beta → 0.6.1-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/changelog.md +17 -0
- package/dist/gateway-ent.cjs +6 -2
- package/dist/gateway-ent.cjs.map +2 -2
- package/dist/gateway-ent.js +6 -2
- package/dist/gateway-ent.js.map +2 -2
- package/dist/index.cjs +817 -507
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +818 -508
- package/dist/index.js.map +4 -4
- package/dist/metrics/publisher/rest.cjs.map +2 -2
- package/dist/metrics/publisher/rest.js.map +2 -2
- package/gateway-server.d.ts +32 -4
- package/package.json +2 -2
- package/readme.md +37 -0
package/dist/index.cjs
CHANGED
|
@@ -169,7 +169,7 @@ var HttpServerRequest = class {
|
|
|
169
169
|
this._body ??= new Promise((resolve, reject) => {
|
|
170
170
|
const chunks = [];
|
|
171
171
|
this._req.on("error", (err) => reject(err)).on("data", (chunk) => chunks.push(chunk)).on("end", () => {
|
|
172
|
-
resolve(new Blob(chunks));
|
|
172
|
+
resolve(new Blob(chunks, { type: this.headers.one("content-type") }));
|
|
173
173
|
});
|
|
174
174
|
});
|
|
175
175
|
return this._body;
|
|
@@ -186,74 +186,79 @@ var HttpServerRequest = class {
|
|
|
186
186
|
}
|
|
187
187
|
get json() {
|
|
188
188
|
return this.body.then(async (blob) => {
|
|
189
|
-
|
|
189
|
+
if (blob.size === 0) {
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
const text = await blob.text();
|
|
193
|
+
const json = JSON.parse(text);
|
|
190
194
|
return json;
|
|
191
195
|
});
|
|
192
196
|
}
|
|
193
197
|
};
|
|
194
198
|
var IncomingMessageHeaders = class {
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
#msg;
|
|
200
|
+
constructor(msg) {
|
|
201
|
+
this.#msg = msg;
|
|
197
202
|
}
|
|
198
203
|
has(name) {
|
|
199
|
-
return this.
|
|
204
|
+
return this.#msg.headers[name] !== void 0;
|
|
200
205
|
}
|
|
201
206
|
get(name) {
|
|
202
|
-
return this.
|
|
207
|
+
return this.#msg.headers[name];
|
|
203
208
|
}
|
|
204
209
|
list(name) {
|
|
205
|
-
return toList(this.
|
|
210
|
+
return toList(this.#msg.headers[name]);
|
|
206
211
|
}
|
|
207
212
|
one(name) {
|
|
208
|
-
const value = this.
|
|
213
|
+
const value = this.#msg.headers[name];
|
|
209
214
|
if (Array.isArray(value)) {
|
|
210
215
|
return value[0];
|
|
211
216
|
}
|
|
212
217
|
return value;
|
|
213
218
|
}
|
|
214
219
|
keys() {
|
|
215
|
-
return Object.keys(this.
|
|
220
|
+
return Object.keys(this.#msg.headers).values();
|
|
216
221
|
}
|
|
217
222
|
};
|
|
218
223
|
var OutgoingMessageHeaders = class {
|
|
219
|
-
|
|
224
|
+
#msg;
|
|
220
225
|
constructor(msg) {
|
|
221
|
-
this
|
|
226
|
+
this.#msg = msg;
|
|
222
227
|
}
|
|
223
228
|
has(name) {
|
|
224
|
-
return this.
|
|
229
|
+
return this.#msg.hasHeader(name);
|
|
225
230
|
}
|
|
226
231
|
keys() {
|
|
227
|
-
return this.
|
|
232
|
+
return this.#msg.getHeaderNames().values();
|
|
228
233
|
}
|
|
229
234
|
get(name) {
|
|
230
|
-
return this.
|
|
235
|
+
return this.#msg.getHeader(name);
|
|
231
236
|
}
|
|
232
237
|
one(name) {
|
|
233
|
-
const value = this.
|
|
238
|
+
const value = this.#msg.getHeader(name);
|
|
234
239
|
if (Array.isArray(value)) {
|
|
235
240
|
return value[0];
|
|
236
241
|
}
|
|
237
242
|
return value;
|
|
238
243
|
}
|
|
239
244
|
set(name, value) {
|
|
240
|
-
if (!this.
|
|
245
|
+
if (!this.#msg.headersSent) {
|
|
241
246
|
if (Array.isArray(value)) {
|
|
242
247
|
value = value.map((v) => typeof v === "number" ? String(v) : v);
|
|
243
248
|
} else if (typeof value === "number") {
|
|
244
249
|
value = String(value);
|
|
245
250
|
}
|
|
246
251
|
if (value) {
|
|
247
|
-
this.
|
|
252
|
+
this.#msg.setHeader(name, value);
|
|
248
253
|
} else {
|
|
249
|
-
this.
|
|
254
|
+
this.#msg.removeHeader(name);
|
|
250
255
|
}
|
|
251
256
|
}
|
|
252
257
|
return this;
|
|
253
258
|
}
|
|
254
259
|
add(name, value) {
|
|
255
|
-
if (!this.
|
|
256
|
-
this.
|
|
260
|
+
if (!this.#msg.headersSent) {
|
|
261
|
+
this.#msg.appendHeader(name, value);
|
|
257
262
|
}
|
|
258
263
|
return this;
|
|
259
264
|
}
|
|
@@ -301,7 +306,7 @@ var HttpServerResponse = class {
|
|
|
301
306
|
}
|
|
302
307
|
end(chunk) {
|
|
303
308
|
if (!this._res.headersSent) {
|
|
304
|
-
return new Promise((resolve
|
|
309
|
+
return new Promise((resolve) => {
|
|
305
310
|
if (chunk === void 0) {
|
|
306
311
|
this._res.end(() => {
|
|
307
312
|
resolve(true);
|
|
@@ -331,6 +336,8 @@ var HttpServerResponse = class {
|
|
|
331
336
|
}
|
|
332
337
|
};
|
|
333
338
|
var DefaultWebExchange = class extends WebExchange {
|
|
339
|
+
request;
|
|
340
|
+
response;
|
|
334
341
|
constructor(request, response) {
|
|
335
342
|
super();
|
|
336
343
|
this.request = request;
|
|
@@ -419,6 +426,92 @@ var MapHttpHeaders = class extends Map {
|
|
|
419
426
|
return this;
|
|
420
427
|
}
|
|
421
428
|
};
|
|
429
|
+
var MockHttpRequest = class {
|
|
430
|
+
#url;
|
|
431
|
+
#body;
|
|
432
|
+
headers = new MapHttpHeaders();
|
|
433
|
+
constructor(url, method) {
|
|
434
|
+
if (typeof url === "string") {
|
|
435
|
+
if (URL.canParse(url)) {
|
|
436
|
+
url = new URL(url);
|
|
437
|
+
} else if (URL.canParse(url, "http://localhost")) {
|
|
438
|
+
url = new URL(url, "http://localhost");
|
|
439
|
+
} else {
|
|
440
|
+
throw new TypeError("URL cannot parse url");
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
this.#url = url;
|
|
444
|
+
this.method = method ?? "GET";
|
|
445
|
+
this.headers.set("Host", this.#url.hostname);
|
|
446
|
+
this.path = this.#url.pathname ?? "/";
|
|
447
|
+
}
|
|
448
|
+
method;
|
|
449
|
+
path;
|
|
450
|
+
get host() {
|
|
451
|
+
return requestToHost(this, this.#url.host);
|
|
452
|
+
}
|
|
453
|
+
get protocol() {
|
|
454
|
+
return requestToProtocol(this, this.#url.protocol.slice(0, -1));
|
|
455
|
+
}
|
|
456
|
+
get cookies() {
|
|
457
|
+
return parseCookies(this);
|
|
458
|
+
}
|
|
459
|
+
get body() {
|
|
460
|
+
const body = this.#body;
|
|
461
|
+
return body ? Promise.resolve(body) : Promise.reject(new Error(`no body set`));
|
|
462
|
+
}
|
|
463
|
+
get json() {
|
|
464
|
+
return this.body.then(async (blob) => JSON.parse(await blob.text()));
|
|
465
|
+
}
|
|
466
|
+
get text() {
|
|
467
|
+
return this.body.then(async (blob) => await blob.text());
|
|
468
|
+
}
|
|
469
|
+
set body(value) {
|
|
470
|
+
this.#body = value;
|
|
471
|
+
if (!this.headers.has("content-type")) {
|
|
472
|
+
this.headers.set("content-type", value.type || "application/octet-stream");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
get formData() {
|
|
476
|
+
return this.body.then(async (b) => new URLSearchParams(await b.text()));
|
|
477
|
+
}
|
|
478
|
+
get URL() {
|
|
479
|
+
return new URL(this.path + this.#url.search + this.#url.hash, `${this.protocol}://${this.host}`);
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
var MockHttpResponse = class {
|
|
483
|
+
statusCode;
|
|
484
|
+
headers = new MapHttpHeaders();
|
|
485
|
+
cookies = [];
|
|
486
|
+
addCookie(cookie) {
|
|
487
|
+
this.cookies.push(cookie);
|
|
488
|
+
return this;
|
|
489
|
+
}
|
|
490
|
+
_body;
|
|
491
|
+
async end(chunk) {
|
|
492
|
+
if (this._body) {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
switch (typeof chunk) {
|
|
496
|
+
case "string":
|
|
497
|
+
this._body = new Blob([chunk], { type: "text/plain" });
|
|
498
|
+
break;
|
|
499
|
+
case "object":
|
|
500
|
+
if (chunk instanceof Blob) {
|
|
501
|
+
this._body = chunk;
|
|
502
|
+
} else if (chunk !== null) {
|
|
503
|
+
this._body = new Blob([JSON.stringify(chunk)], { type: "application/json" });
|
|
504
|
+
}
|
|
505
|
+
break;
|
|
506
|
+
case "undefined":
|
|
507
|
+
this._body = new Blob([]);
|
|
508
|
+
break;
|
|
509
|
+
default:
|
|
510
|
+
throw new Error(`Unsupported chunk type: ${typeof chunk}`);
|
|
511
|
+
}
|
|
512
|
+
return true;
|
|
513
|
+
}
|
|
514
|
+
};
|
|
422
515
|
|
|
423
516
|
// src/gateway/ws/core.ts
|
|
424
517
|
var import_gateway2 = require("@interopio/gateway");
|
|
@@ -589,22 +682,129 @@ var InMemoryNodeConnections = class {
|
|
|
589
682
|
}
|
|
590
683
|
};
|
|
591
684
|
|
|
685
|
+
// src/server/util/matchers.ts
|
|
686
|
+
var or = (matchers) => {
|
|
687
|
+
return async (exchange) => {
|
|
688
|
+
for (const matcher of matchers) {
|
|
689
|
+
const result = await matcher(exchange);
|
|
690
|
+
if (result.match) {
|
|
691
|
+
return { match: true };
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return { match: false };
|
|
695
|
+
};
|
|
696
|
+
};
|
|
697
|
+
var and = (matchers) => {
|
|
698
|
+
const matcher = async (exchange) => {
|
|
699
|
+
for (const matcher2 of matchers) {
|
|
700
|
+
const result = await matcher2(exchange);
|
|
701
|
+
if (!result.match) {
|
|
702
|
+
return { match: false };
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
return { match: true };
|
|
706
|
+
};
|
|
707
|
+
matcher.toString = () => `and(${matchers.map((m) => m.toString()).join(", ")})`;
|
|
708
|
+
return matcher;
|
|
709
|
+
};
|
|
710
|
+
var not = (matcher) => {
|
|
711
|
+
return async (exchange) => {
|
|
712
|
+
const result = await matcher(exchange);
|
|
713
|
+
return { match: !result.match };
|
|
714
|
+
};
|
|
715
|
+
};
|
|
716
|
+
var anyExchange = async (_exchange) => {
|
|
717
|
+
return { match: true };
|
|
718
|
+
};
|
|
719
|
+
anyExchange.toString = () => "any-exchange";
|
|
720
|
+
var pattern = (pattern2, opts) => {
|
|
721
|
+
const method = opts?.method;
|
|
722
|
+
const matcher = async (exchange) => {
|
|
723
|
+
const request = exchange.request;
|
|
724
|
+
const path = request.path;
|
|
725
|
+
if (method !== void 0 && request.method !== method) {
|
|
726
|
+
return { match: false };
|
|
727
|
+
}
|
|
728
|
+
if (typeof pattern2 === "string") {
|
|
729
|
+
if (path === pattern2) {
|
|
730
|
+
return { match: true };
|
|
731
|
+
} else {
|
|
732
|
+
return { match: false };
|
|
733
|
+
}
|
|
734
|
+
} else {
|
|
735
|
+
const match = pattern2.exec(path);
|
|
736
|
+
if (match !== null) {
|
|
737
|
+
return { match: true };
|
|
738
|
+
} else {
|
|
739
|
+
return { match: false };
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
matcher.toString = () => {
|
|
744
|
+
return `pattern(${pattern2.toString()})${method ? ` method=${method}` : ""}`;
|
|
745
|
+
};
|
|
746
|
+
return matcher;
|
|
747
|
+
};
|
|
748
|
+
var mediaType = (opts) => {
|
|
749
|
+
const shouldIgnore = (requestedMediaType) => {
|
|
750
|
+
if (opts.ignoredMediaTypes !== void 0) {
|
|
751
|
+
for (const ignoredMediaType of opts.ignoredMediaTypes) {
|
|
752
|
+
if (requestedMediaType === ignoredMediaType || ignoredMediaType === "*/*") {
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return false;
|
|
758
|
+
};
|
|
759
|
+
return async (exchange) => {
|
|
760
|
+
const request = exchange.request;
|
|
761
|
+
let requestMediaTypes;
|
|
762
|
+
try {
|
|
763
|
+
requestMediaTypes = request.headers.list("accept");
|
|
764
|
+
} catch (e) {
|
|
765
|
+
return { match: false };
|
|
766
|
+
}
|
|
767
|
+
for (const requestedMediaType of requestMediaTypes) {
|
|
768
|
+
if (shouldIgnore(requestedMediaType)) {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
for (const mediaType2 of opts.mediaTypes) {
|
|
772
|
+
if (requestedMediaType.startsWith(mediaType2)) {
|
|
773
|
+
return { match: true };
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return { match: false };
|
|
778
|
+
};
|
|
779
|
+
};
|
|
780
|
+
var upgradeMatcher = async ({ request }) => {
|
|
781
|
+
const match = request["_req"]?.["upgrade"] && request.headers.one("upgrade")?.toLowerCase() === "websocket";
|
|
782
|
+
return { match };
|
|
783
|
+
};
|
|
784
|
+
upgradeMatcher.toString = () => "websocket upgrade";
|
|
785
|
+
|
|
592
786
|
// src/mesh/rest-directory/routes.ts
|
|
593
|
-
function routes(connections) {
|
|
787
|
+
function routes(connections, config, authorize) {
|
|
788
|
+
config.cors.push(
|
|
789
|
+
[pattern("/api/nodes"), { allowMethods: ["GET", "POST"] }],
|
|
790
|
+
[pattern(/\/api\/nodes\/(?<nodeId>.*)/), { allowMethods: ["GET", "DELETE"] }]
|
|
791
|
+
);
|
|
792
|
+
if (authorize) {
|
|
793
|
+
config.authorize.push([pattern(/\/api\/nodes(\/.*)?/), authorize]);
|
|
794
|
+
}
|
|
594
795
|
return [
|
|
595
796
|
async (ctx, next) => {
|
|
596
797
|
if (ctx.method === "POST" && ctx.path === "/api/nodes") {
|
|
597
798
|
const json = await ctx.request.json;
|
|
598
799
|
if (!Array.isArray(json)) {
|
|
599
800
|
ctx.response.statusCode = 400;
|
|
600
|
-
ctx.response.
|
|
801
|
+
await ctx.response.end();
|
|
601
802
|
} else {
|
|
602
803
|
const nodes = json;
|
|
603
804
|
const result = connections.announce(nodes);
|
|
604
|
-
const body = JSON.stringify(result);
|
|
605
|
-
ctx.response.headers.set("content-type", "application/json");
|
|
805
|
+
const body = new Blob([JSON.stringify(result)], { type: "application/json" });
|
|
606
806
|
ctx.response.statusCode = 200;
|
|
607
|
-
ctx.response.
|
|
807
|
+
await ctx.response.end(body);
|
|
608
808
|
}
|
|
609
809
|
} else {
|
|
610
810
|
await next();
|
|
@@ -615,7 +815,7 @@ function routes(connections) {
|
|
|
615
815
|
const nodeId = path?.substring("/api/nodes/".length);
|
|
616
816
|
connections.remove(nodeId);
|
|
617
817
|
response.statusCode = 200;
|
|
618
|
-
response.
|
|
818
|
+
await response.end();
|
|
619
819
|
} else {
|
|
620
820
|
await next();
|
|
621
821
|
}
|
|
@@ -976,68 +1176,38 @@ var core_default4 = create4;
|
|
|
976
1176
|
|
|
977
1177
|
// src/metrics/routes.ts
|
|
978
1178
|
var logger5 = getLogger("metrics");
|
|
979
|
-
|
|
980
|
-
function loggedIn(auth, ctx) {
|
|
981
|
-
if (auth) {
|
|
982
|
-
const value = ctx.request.cookies.find((cookie) => cookie.name === COOKIE_NAME)?.value;
|
|
983
|
-
return value && parseInt(value) > Date.now();
|
|
984
|
-
}
|
|
985
|
-
return true;
|
|
986
|
-
}
|
|
987
|
-
async function routes2(config) {
|
|
1179
|
+
async function routes2(config, { cors, authorize }) {
|
|
988
1180
|
const { jsonFileAppender } = await import("@interopio/gateway/metrics/publisher/file");
|
|
989
1181
|
const appender = jsonFileAppender(logger5);
|
|
990
1182
|
await appender.open(config.file?.location ?? "metrics.ndjson", config.file?.append ?? true);
|
|
1183
|
+
cors.push([pattern("/api/metrics"), { allowMethods: ["GET", "POST"] }]);
|
|
1184
|
+
if (config.authorize) {
|
|
1185
|
+
authorize.push([pattern("/api/metrics"), config.authorize]);
|
|
1186
|
+
}
|
|
991
1187
|
return [
|
|
992
1188
|
async (ctx, next) => {
|
|
993
1189
|
if (ctx.method === "GET" && ctx.path === "/api/metrics") {
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
} else {
|
|
997
|
-
ctx.response.statusCode = 302;
|
|
998
|
-
ctx.response.headers.set("location", "/api/login?redirectTo=/api/metrics");
|
|
999
|
-
}
|
|
1000
|
-
ctx.response._res.end();
|
|
1001
|
-
} else {
|
|
1002
|
-
await next();
|
|
1003
|
-
}
|
|
1004
|
-
},
|
|
1005
|
-
async (ctx, next) => {
|
|
1006
|
-
if (ctx.method === "GET" && ctx.path === "/api/login") {
|
|
1007
|
-
const redirectTo = new URLSearchParams(ctx.request.query ?? void 0).get("redirectTo");
|
|
1008
|
-
const expires = Date.now() + 180 * 1e3;
|
|
1009
|
-
ctx.response.addCookie({ name: COOKIE_NAME, value: `${expires}`, maxAge: Infinity, path: "/api", sameSite: "strict" });
|
|
1010
|
-
if (redirectTo) {
|
|
1011
|
-
ctx.response.statusCode = 302;
|
|
1012
|
-
ctx.response.headers.set("location", redirectTo);
|
|
1013
|
-
} else {
|
|
1014
|
-
ctx.response.statusCode = 200;
|
|
1015
|
-
}
|
|
1016
|
-
ctx.response._res.end();
|
|
1190
|
+
ctx.response.statusCode = 200;
|
|
1191
|
+
await ctx.response.end();
|
|
1017
1192
|
} else {
|
|
1018
1193
|
await next();
|
|
1019
1194
|
}
|
|
1020
1195
|
},
|
|
1021
|
-
async (
|
|
1022
|
-
if (
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
logger5.debug(`${JSON.stringify(update)}`);
|
|
1031
|
-
}
|
|
1032
|
-
if ((config.file?.status ?? false) || update.status === void 0) {
|
|
1033
|
-
await appender.write(update);
|
|
1034
|
-
}
|
|
1035
|
-
} catch (e) {
|
|
1036
|
-
logger5.error(`error processing metrics`, e);
|
|
1196
|
+
async ({ request, response }, next) => {
|
|
1197
|
+
if (request.method === "POST" && request.path === "/api/metrics") {
|
|
1198
|
+
response.statusCode = 202;
|
|
1199
|
+
await response.end();
|
|
1200
|
+
try {
|
|
1201
|
+
const json = await request.json;
|
|
1202
|
+
const update = json;
|
|
1203
|
+
if (logger5.enabledFor("debug")) {
|
|
1204
|
+
logger5.debug(`${JSON.stringify(update)}`);
|
|
1037
1205
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1206
|
+
if ((config.file?.status ?? false) || update.status === void 0) {
|
|
1207
|
+
await appender.write(update);
|
|
1208
|
+
}
|
|
1209
|
+
} catch (e) {
|
|
1210
|
+
logger5.error(`error processing metrics`, e);
|
|
1041
1211
|
}
|
|
1042
1212
|
} else {
|
|
1043
1213
|
await next();
|
|
@@ -1118,9 +1288,9 @@ var localIp = (() => {
|
|
|
1118
1288
|
return a.length > 0 ? a[0] : void 0;
|
|
1119
1289
|
}
|
|
1120
1290
|
const addresses = Object.values((0, import_node_os.networkInterfaces)()).flatMap((details) => {
|
|
1121
|
-
return (details ?? []).filter((
|
|
1122
|
-
}).reduce((acc,
|
|
1123
|
-
acc[
|
|
1291
|
+
return (details ?? []).filter((info2) => info2.family === "IPv4");
|
|
1292
|
+
}).reduce((acc, info2) => {
|
|
1293
|
+
acc[info2.internal ? "internal" : "external"].push(info2);
|
|
1124
1294
|
return acc;
|
|
1125
1295
|
}, { internal: [], external: [] });
|
|
1126
1296
|
return (first(addresses.internal) ?? first(addresses.external))?.address;
|
|
@@ -1267,7 +1437,7 @@ async function stop(m) {
|
|
|
1267
1437
|
return await run(m, "stop");
|
|
1268
1438
|
}
|
|
1269
1439
|
|
|
1270
|
-
// src/
|
|
1440
|
+
// src/app/ws-client-verify.ts
|
|
1271
1441
|
var import_gateway5 = require("@interopio/gateway");
|
|
1272
1442
|
var log3 = getLogger("gateway.ws.client-verify");
|
|
1273
1443
|
function acceptsMissing(originFilters) {
|
|
@@ -1339,272 +1509,57 @@ function regexifyOriginFilters(originFilters) {
|
|
|
1339
1509
|
}
|
|
1340
1510
|
}
|
|
1341
1511
|
|
|
1342
|
-
// src/server/
|
|
1343
|
-
var
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
const url = request.URL;
|
|
1350
|
-
const actualProtocol = url.protocol;
|
|
1351
|
-
const actualHost = url.host;
|
|
1352
|
-
const originUrl = new URL(origin);
|
|
1353
|
-
const originHost = originUrl.host;
|
|
1354
|
-
const originProtocol = originUrl.protocol;
|
|
1355
|
-
return actualProtocol === originProtocol && actualHost === originHost;
|
|
1356
|
-
}
|
|
1357
|
-
function isCorsRequest(request) {
|
|
1358
|
-
return request.headers.has("origin") && !isSameOrigin(request);
|
|
1359
|
-
}
|
|
1360
|
-
function isPreFlightRequest(request) {
|
|
1361
|
-
return request.method === "OPTIONS" && request.headers.has("origin") && request.headers.has("access-control-request-method");
|
|
1362
|
-
}
|
|
1363
|
-
var VARY_HEADERS = ["Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"];
|
|
1364
|
-
function processRequest(exchange, config) {
|
|
1365
|
-
const { request, response } = exchange;
|
|
1366
|
-
const responseHeaders = response.headers;
|
|
1367
|
-
if (!responseHeaders.has("Vary")) {
|
|
1368
|
-
responseHeaders.set("Vary", VARY_HEADERS.join(", "));
|
|
1369
|
-
} else {
|
|
1370
|
-
const varyHeaders = responseHeaders.list("Vary");
|
|
1371
|
-
for (const header of VARY_HEADERS) {
|
|
1372
|
-
if (!varyHeaders.find((h) => h === header)) {
|
|
1373
|
-
varyHeaders.push(header);
|
|
1374
|
-
}
|
|
1512
|
+
// src/server/server-header.ts
|
|
1513
|
+
var import_package = __toESM(require("@interopio/gateway-server/package.json"), 1);
|
|
1514
|
+
var serverHeader = (server) => {
|
|
1515
|
+
server ??= `${import_package.default.name} - v${import_package.default.version}`;
|
|
1516
|
+
return async ({ response }, next) => {
|
|
1517
|
+
if (server !== false && !response.headers.has("server")) {
|
|
1518
|
+
response.headers.set("Server", server);
|
|
1375
1519
|
}
|
|
1376
|
-
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
|
|
1520
|
+
await next();
|
|
1521
|
+
};
|
|
1522
|
+
};
|
|
1523
|
+
var server_header_default = (server) => serverHeader(server);
|
|
1524
|
+
|
|
1525
|
+
// src/app/route.ts
|
|
1526
|
+
function findSocketRoute({ request }, { sockets: routes3 }) {
|
|
1527
|
+
const path = request.path ?? "/";
|
|
1528
|
+
const route = routes3.get(path) ?? Array.from(routes3.values()).find((route2) => {
|
|
1529
|
+
if (path === "/" && route2.default === true) {
|
|
1380
1530
|
return true;
|
|
1381
1531
|
}
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
logger6.debug(`reject: origin is malformed`);
|
|
1385
|
-
}
|
|
1386
|
-
rejectRequest(response);
|
|
1387
|
-
return false;
|
|
1388
|
-
}
|
|
1389
|
-
if (responseHeaders.has("access-control-allow-origin")) {
|
|
1390
|
-
logger6.trace(`skip: already contains "Access-Control-Allow-Origin"`);
|
|
1391
|
-
return true;
|
|
1392
|
-
}
|
|
1393
|
-
const preFlightRequest = isPreFlightRequest(request);
|
|
1394
|
-
if (config) {
|
|
1395
|
-
return handleInternal(exchange, config, preFlightRequest);
|
|
1396
|
-
}
|
|
1397
|
-
if (preFlightRequest) {
|
|
1398
|
-
rejectRequest(response);
|
|
1399
|
-
return false;
|
|
1400
|
-
}
|
|
1401
|
-
return true;
|
|
1532
|
+
});
|
|
1533
|
+
return [route, path];
|
|
1402
1534
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
}
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
return origin.toLowerCase();
|
|
1414
|
-
}
|
|
1415
|
-
return origin;
|
|
1416
|
-
});
|
|
1535
|
+
|
|
1536
|
+
// src/server/security/http-headers.ts
|
|
1537
|
+
var staticServerHttpHeadersWriter = (headers2) => {
|
|
1538
|
+
return async (exchange) => {
|
|
1539
|
+
let containsNoHeaders = true;
|
|
1540
|
+
const { response } = exchange;
|
|
1541
|
+
for (const name of headers2.keys()) {
|
|
1542
|
+
if (response.headers.has(name)) {
|
|
1543
|
+
containsNoHeaders = false;
|
|
1544
|
+
}
|
|
1417
1545
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
validateConfig(config);
|
|
1423
|
-
return async (ctx, next) => {
|
|
1424
|
-
const isValid = processRequest(ctx, config);
|
|
1425
|
-
if (!isValid || isPreFlightRequest(ctx.request)) {
|
|
1426
|
-
await ctx.response.end();
|
|
1427
|
-
} else {
|
|
1428
|
-
await next();
|
|
1546
|
+
if (containsNoHeaders) {
|
|
1547
|
+
for (const [name, value] of headers2) {
|
|
1548
|
+
response.headers.set(name, value);
|
|
1549
|
+
}
|
|
1429
1550
|
}
|
|
1430
1551
|
};
|
|
1431
1552
|
};
|
|
1432
|
-
var
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
if (allowOrigin === void 0) {
|
|
1443
|
-
if (logger6.enabledFor("debug")) {
|
|
1444
|
-
logger6.debug(`reject: '${requestOrigin}' origin is not allowed`);
|
|
1445
|
-
}
|
|
1446
|
-
rejectRequest(response);
|
|
1447
|
-
return false;
|
|
1448
|
-
}
|
|
1449
|
-
const requestMethod = getMethodToUse(request, preFlightRequest);
|
|
1450
|
-
const allowMethods = checkMethods(config, requestMethod);
|
|
1451
|
-
if (allowMethods === void 0) {
|
|
1452
|
-
if (logger6.enabledFor("debug")) {
|
|
1453
|
-
logger6.debug(`reject: HTTP '${requestMethod}' is not allowed`);
|
|
1454
|
-
}
|
|
1455
|
-
rejectRequest(response);
|
|
1456
|
-
return false;
|
|
1457
|
-
}
|
|
1458
|
-
const requestHeaders = getHeadersToUse(request, preFlightRequest);
|
|
1459
|
-
const allowHeaders = checkHeaders(config, requestHeaders);
|
|
1460
|
-
if (preFlightRequest && allowHeaders === void 0) {
|
|
1461
|
-
if (logger6.enabledFor("debug")) {
|
|
1462
|
-
logger6.debug(`reject: headers '${requestHeaders}' are not allowed`);
|
|
1463
|
-
}
|
|
1464
|
-
rejectRequest(response);
|
|
1465
|
-
return false;
|
|
1466
|
-
}
|
|
1467
|
-
responseHeaders.set("access-control-allow-origin", allowOrigin);
|
|
1468
|
-
if (preFlightRequest) {
|
|
1469
|
-
responseHeaders.set("access-control-allow-methods", allowMethods.join(","));
|
|
1470
|
-
}
|
|
1471
|
-
if (preFlightRequest && allowHeaders !== void 0 && allowHeaders.length > 0) {
|
|
1472
|
-
responseHeaders.set("access-control-allow-headers", allowHeaders.join(", "));
|
|
1473
|
-
}
|
|
1474
|
-
const exposeHeaders = config.headers?.expose;
|
|
1475
|
-
if (exposeHeaders && exposeHeaders.length > 0) {
|
|
1476
|
-
responseHeaders.set("access-control-expose-headers", exposeHeaders.join(", "));
|
|
1477
|
-
}
|
|
1478
|
-
if (config.credentials?.allow) {
|
|
1479
|
-
responseHeaders.set("access-control-allow-credentials", "true");
|
|
1480
|
-
}
|
|
1481
|
-
if (config.privateNetwork?.allow && request.headers.one("access-control-request-private-network") === "true") {
|
|
1482
|
-
responseHeaders.set("access-control-allow-private-network", "true");
|
|
1483
|
-
}
|
|
1484
|
-
return true;
|
|
1485
|
-
}
|
|
1486
|
-
var ALL = "*";
|
|
1487
|
-
var DEFAULT_METHODS = ["GET", "HEAD"];
|
|
1488
|
-
function validateAllowCredentials(config) {
|
|
1489
|
-
if (config.credentials?.allow === true && config.origins?.allow === ALL) {
|
|
1490
|
-
throw new Error(`when credentials.allow is true origins.allow cannot be "*"`);
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
function validateAllowPrivateNetwork(config) {
|
|
1494
|
-
if (config.privateNetwork?.allow === true && config.origins?.allow === ALL) {
|
|
1495
|
-
throw new Error(`when privateNetwork.allow is true origins.allow cannot be "*"`);
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
function checkOrigin(config, origin) {
|
|
1499
|
-
if (origin) {
|
|
1500
|
-
const allowedOrigins = config.origins?.allow;
|
|
1501
|
-
if (allowedOrigins) {
|
|
1502
|
-
if (allowedOrigins === ALL) {
|
|
1503
|
-
validateAllowCredentials(config);
|
|
1504
|
-
validateAllowPrivateNetwork(config);
|
|
1505
|
-
return ALL;
|
|
1506
|
-
}
|
|
1507
|
-
const originToCheck = trimTrailingSlash(origin.toLowerCase());
|
|
1508
|
-
for (const allowedOrigin of allowedOrigins) {
|
|
1509
|
-
if (allowedOrigin === ALL || import_gateway6.IOGateway.Filtering.valueMatches(allowedOrigin, originToCheck)) {
|
|
1510
|
-
return origin;
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
function checkMethods(config, requestMethod) {
|
|
1517
|
-
if (requestMethod) {
|
|
1518
|
-
const allowedMethods = config.methods?.allow ?? DEFAULT_METHODS;
|
|
1519
|
-
if (allowedMethods === ALL) {
|
|
1520
|
-
return [requestMethod];
|
|
1521
|
-
}
|
|
1522
|
-
if (import_gateway6.IOGateway.Filtering.valuesMatch(allowedMethods, requestMethod)) {
|
|
1523
|
-
return allowedMethods;
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
function checkHeaders(config, requestHeaders) {
|
|
1528
|
-
if (requestHeaders === void 0) {
|
|
1529
|
-
return;
|
|
1530
|
-
}
|
|
1531
|
-
if (requestHeaders.length == 0) {
|
|
1532
|
-
return [];
|
|
1533
|
-
}
|
|
1534
|
-
const allowedHeaders = config.headers?.allow;
|
|
1535
|
-
if (allowedHeaders === void 0) {
|
|
1536
|
-
return;
|
|
1537
|
-
}
|
|
1538
|
-
const allowAnyHeader = allowedHeaders === ALL;
|
|
1539
|
-
const result = [];
|
|
1540
|
-
for (const requestHeader of requestHeaders) {
|
|
1541
|
-
const value = requestHeader?.trim();
|
|
1542
|
-
if (value) {
|
|
1543
|
-
if (allowAnyHeader) {
|
|
1544
|
-
result.push(value);
|
|
1545
|
-
} else {
|
|
1546
|
-
for (const allowedHeader of allowedHeaders) {
|
|
1547
|
-
if (value.toLowerCase() == allowedHeader) {
|
|
1548
|
-
result.push(value);
|
|
1549
|
-
break;
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
if (result.length > 0) {
|
|
1556
|
-
return result;
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
function trimTrailingSlash(origin) {
|
|
1560
|
-
return origin.endsWith("/") ? origin.slice(0, -1) : origin;
|
|
1561
|
-
}
|
|
1562
|
-
function getMethodToUse(request, isPreFlight) {
|
|
1563
|
-
return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
|
|
1564
|
-
}
|
|
1565
|
-
function getHeadersToUse(request, isPreFlight) {
|
|
1566
|
-
const headers2 = request.headers;
|
|
1567
|
-
return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
|
|
1568
|
-
}
|
|
1569
|
-
|
|
1570
|
-
// src/server/server-header.ts
|
|
1571
|
-
var serverHeader = (server) => {
|
|
1572
|
-
return async ({ response }, next) => {
|
|
1573
|
-
if (!response.headers.has("server")) {
|
|
1574
|
-
response.headers.set("Server", server);
|
|
1575
|
-
}
|
|
1576
|
-
await next();
|
|
1577
|
-
};
|
|
1578
|
-
};
|
|
1579
|
-
var server_header_default = (server = "gateway-server") => serverHeader(server);
|
|
1580
|
-
|
|
1581
|
-
// src/server/security/http-headers.ts
|
|
1582
|
-
var staticServerHttpHeadersWriter = (headers2) => {
|
|
1583
|
-
return async (exchange) => {
|
|
1584
|
-
let containsNoHeaders = true;
|
|
1585
|
-
const { response } = exchange;
|
|
1586
|
-
for (const name of headers2.keys()) {
|
|
1587
|
-
if (response.headers.has(name)) {
|
|
1588
|
-
containsNoHeaders = false;
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
if (containsNoHeaders) {
|
|
1592
|
-
for (const [name, value] of headers2) {
|
|
1593
|
-
response.headers.set(name, value);
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
};
|
|
1597
|
-
};
|
|
1598
|
-
var cacheControlServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
|
|
1599
|
-
new MapHttpHeaders().add("cache-control", "no-cache, no-store, max-age=0, must-revalidate").add("pragma", "no-cache").add("expires", "0")
|
|
1600
|
-
);
|
|
1601
|
-
var contentTypeServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
|
|
1602
|
-
new MapHttpHeaders().add("x-content-type-options", "nosniff")
|
|
1603
|
-
);
|
|
1604
|
-
var strictTransportSecurityServerHttpHeadersWriter = (maxAgeInSeconds, includeSubDomains, preload) => {
|
|
1605
|
-
let headerValue = `max-age=${maxAgeInSeconds}`;
|
|
1606
|
-
if (includeSubDomains) {
|
|
1607
|
-
headerValue += " ; includeSubDomains";
|
|
1553
|
+
var cacheControlServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
|
|
1554
|
+
new MapHttpHeaders().add("cache-control", "no-cache, no-store, max-age=0, must-revalidate").add("pragma", "no-cache").add("expires", "0")
|
|
1555
|
+
);
|
|
1556
|
+
var contentTypeServerHttpHeadersWriter = () => staticServerHttpHeadersWriter(
|
|
1557
|
+
new MapHttpHeaders().add("x-content-type-options", "nosniff")
|
|
1558
|
+
);
|
|
1559
|
+
var strictTransportSecurityServerHttpHeadersWriter = (maxAgeInSeconds, includeSubDomains, preload) => {
|
|
1560
|
+
let headerValue = `max-age=${maxAgeInSeconds}`;
|
|
1561
|
+
if (includeSubDomains) {
|
|
1562
|
+
headerValue += " ; includeSubDomains";
|
|
1608
1563
|
}
|
|
1609
1564
|
if (preload) {
|
|
1610
1565
|
headerValue += " ; preload";
|
|
@@ -1764,18 +1719,18 @@ var AuthorizationDecision = class {
|
|
|
1764
1719
|
granted;
|
|
1765
1720
|
};
|
|
1766
1721
|
var DefaultAuthorizationManager = class {
|
|
1767
|
-
check;
|
|
1722
|
+
#check;
|
|
1768
1723
|
constructor(check) {
|
|
1769
|
-
this
|
|
1724
|
+
this.#check = check;
|
|
1770
1725
|
}
|
|
1771
1726
|
async verify(authentication, object) {
|
|
1772
|
-
const decision = await this
|
|
1727
|
+
const decision = await this.#check(authentication, object);
|
|
1773
1728
|
if (!decision?.granted) {
|
|
1774
1729
|
throw new AccessDeniedError("Access denied");
|
|
1775
1730
|
}
|
|
1776
1731
|
}
|
|
1777
1732
|
async authorize(authentication, object) {
|
|
1778
|
-
return await this
|
|
1733
|
+
return await this.#check(authentication, object);
|
|
1779
1734
|
}
|
|
1780
1735
|
};
|
|
1781
1736
|
var AuthenticationServiceError = class extends AuthenticationError {
|
|
@@ -1829,71 +1784,6 @@ var httpBasicAuthenticationConverter = (opts) => {
|
|
|
1829
1784
|
};
|
|
1830
1785
|
};
|
|
1831
1786
|
|
|
1832
|
-
// src/server/util/matchers.ts
|
|
1833
|
-
var or = (matchers) => {
|
|
1834
|
-
return async (exchange) => {
|
|
1835
|
-
for (const matcher of matchers) {
|
|
1836
|
-
const result = await matcher(exchange);
|
|
1837
|
-
if (result.match) {
|
|
1838
|
-
return { match: true };
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
return { match: false };
|
|
1842
|
-
};
|
|
1843
|
-
};
|
|
1844
|
-
var and = (matchers) => {
|
|
1845
|
-
return async (exchange) => {
|
|
1846
|
-
for (const matcher of matchers) {
|
|
1847
|
-
const result = await matcher(exchange);
|
|
1848
|
-
if (!result.match) {
|
|
1849
|
-
return { match: false };
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
return { match: true };
|
|
1853
|
-
};
|
|
1854
|
-
};
|
|
1855
|
-
var not = (matcher) => {
|
|
1856
|
-
return async (exchange) => {
|
|
1857
|
-
const result = await matcher(exchange);
|
|
1858
|
-
return { match: !result.match };
|
|
1859
|
-
};
|
|
1860
|
-
};
|
|
1861
|
-
var anyExchange = async (_exchange) => {
|
|
1862
|
-
return { match: true };
|
|
1863
|
-
};
|
|
1864
|
-
var mediaType = (opts) => {
|
|
1865
|
-
const shouldIgnore = (requestedMediaType) => {
|
|
1866
|
-
if (opts.ignoredMediaTypes !== void 0) {
|
|
1867
|
-
for (const ignoredMediaType of opts.ignoredMediaTypes) {
|
|
1868
|
-
if (requestedMediaType === ignoredMediaType || ignoredMediaType === "*/*") {
|
|
1869
|
-
return true;
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
return false;
|
|
1874
|
-
};
|
|
1875
|
-
return async (exchange) => {
|
|
1876
|
-
const request = exchange.request;
|
|
1877
|
-
let requestMediaTypes;
|
|
1878
|
-
try {
|
|
1879
|
-
requestMediaTypes = request.headers.list("accept");
|
|
1880
|
-
} catch (e) {
|
|
1881
|
-
return { match: false };
|
|
1882
|
-
}
|
|
1883
|
-
for (const requestedMediaType of requestMediaTypes) {
|
|
1884
|
-
if (shouldIgnore(requestedMediaType)) {
|
|
1885
|
-
continue;
|
|
1886
|
-
}
|
|
1887
|
-
for (const mediaType2 of opts.mediaTypes) {
|
|
1888
|
-
if (requestedMediaType.startsWith(mediaType2)) {
|
|
1889
|
-
return { match: true };
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
return { match: false };
|
|
1894
|
-
};
|
|
1895
|
-
};
|
|
1896
|
-
|
|
1897
1787
|
// src/server/security/security-context.ts
|
|
1898
1788
|
var import_node_async_hooks = require("node:async_hooks");
|
|
1899
1789
|
var AsyncStorageSecurityContextHolder = class _AsyncStorageSecurityContextHolder {
|
|
@@ -2207,6 +2097,7 @@ var httpStatusEntryPoint = (opts) => {
|
|
|
2207
2097
|
};
|
|
2208
2098
|
|
|
2209
2099
|
// src/server/security/delegating-entry-point.ts
|
|
2100
|
+
var logger6 = getLogger("auth.entry-point");
|
|
2210
2101
|
var delegatingEntryPoint = (opts) => {
|
|
2211
2102
|
const defaultEntryPoint = opts.defaultEntryPoint ?? (async ({ response }, _error) => {
|
|
2212
2103
|
response.statusCode = 401;
|
|
@@ -2214,11 +2105,20 @@ var delegatingEntryPoint = (opts) => {
|
|
|
2214
2105
|
});
|
|
2215
2106
|
return async (exchange, error) => {
|
|
2216
2107
|
for (const [matcher, entryPoint] of opts.entryPoints) {
|
|
2108
|
+
if (logger6.enabledFor("debug")) {
|
|
2109
|
+
logger6.debug(`trying to match using: ${matcher}`);
|
|
2110
|
+
}
|
|
2217
2111
|
const match = await matcher(exchange);
|
|
2218
2112
|
if (match.match) {
|
|
2113
|
+
if (logger6.enabledFor("debug")) {
|
|
2114
|
+
logger6.debug(`match found. using default entry point ${entryPoint}`);
|
|
2115
|
+
}
|
|
2219
2116
|
return entryPoint(exchange, error);
|
|
2220
2117
|
}
|
|
2221
2118
|
}
|
|
2119
|
+
if (logger6.enabledFor("debug")) {
|
|
2120
|
+
logger6.debug(`no match found. using default entry point ${defaultEntryPoint}`);
|
|
2121
|
+
}
|
|
2222
2122
|
return defaultEntryPoint(exchange, error);
|
|
2223
2123
|
};
|
|
2224
2124
|
};
|
|
@@ -2226,8 +2126,8 @@ var delegatingEntryPoint = (opts) => {
|
|
|
2226
2126
|
// src/server/security/delegating-success-handler.ts
|
|
2227
2127
|
var delegatingSuccessHandler = (handlers) => {
|
|
2228
2128
|
return async ({ exchange, next }, authentication) => {
|
|
2229
|
-
for (const
|
|
2230
|
-
await
|
|
2129
|
+
for (const handler of handlers) {
|
|
2130
|
+
await handler({ exchange, next }, authentication);
|
|
2231
2131
|
}
|
|
2232
2132
|
};
|
|
2233
2133
|
};
|
|
@@ -2321,14 +2221,21 @@ var errorFilter = (opts) => {
|
|
|
2321
2221
|
};
|
|
2322
2222
|
|
|
2323
2223
|
// src/server/security/authorization-filter.ts
|
|
2224
|
+
var logger7 = getLogger("security.auth");
|
|
2324
2225
|
function authorizationFilter(opts) {
|
|
2325
2226
|
const { manager, storage } = opts;
|
|
2326
2227
|
return async (exchange, next) => {
|
|
2327
2228
|
const promise = AsyncStorageSecurityContextHolder.getContext(storage).then((c) => c?.authentication);
|
|
2328
2229
|
try {
|
|
2329
2230
|
await manager.verify(promise, exchange);
|
|
2231
|
+
if (logger7.enabledFor("debug")) {
|
|
2232
|
+
logger7.debug("authorization successful");
|
|
2233
|
+
}
|
|
2330
2234
|
} catch (error) {
|
|
2331
2235
|
if (error instanceof AccessDeniedError) {
|
|
2236
|
+
if (logger7.enabledFor("debug")) {
|
|
2237
|
+
logger7.debug(`authorization failed: ${error.message}`);
|
|
2238
|
+
}
|
|
2332
2239
|
}
|
|
2333
2240
|
throw error;
|
|
2334
2241
|
}
|
|
@@ -2337,12 +2244,14 @@ function authorizationFilter(opts) {
|
|
|
2337
2244
|
}
|
|
2338
2245
|
|
|
2339
2246
|
// src/server/security/delegating-authorization-manager.ts
|
|
2247
|
+
var logger8 = getLogger("auth");
|
|
2340
2248
|
function delegatingAuthorizationManager(opts) {
|
|
2341
2249
|
const check = async (authentication, exchange) => {
|
|
2342
2250
|
let decision;
|
|
2343
2251
|
for (const [matcher, manager] of opts.mappings) {
|
|
2344
2252
|
if ((await matcher(exchange))?.match) {
|
|
2345
|
-
|
|
2253
|
+
logger8.debug(`checking authorization on '${exchange.path}' using [${matcher}, ${manager}]`);
|
|
2254
|
+
const checkResult = await manager.authorize(authentication, { exchange });
|
|
2346
2255
|
if (checkResult !== void 0) {
|
|
2347
2256
|
decision = checkResult;
|
|
2348
2257
|
break;
|
|
@@ -2355,6 +2264,308 @@ function delegatingAuthorizationManager(opts) {
|
|
|
2355
2264
|
return new DefaultAuthorizationManager(check);
|
|
2356
2265
|
}
|
|
2357
2266
|
|
|
2267
|
+
// src/server/cors.ts
|
|
2268
|
+
var import_gateway6 = require("@interopio/gateway");
|
|
2269
|
+
function isSameOrigin(request) {
|
|
2270
|
+
const origin = request.headers.one("origin");
|
|
2271
|
+
if (origin === void 0) {
|
|
2272
|
+
return true;
|
|
2273
|
+
}
|
|
2274
|
+
const url = request.URL;
|
|
2275
|
+
const actualProtocol = url.protocol;
|
|
2276
|
+
const actualHost = url.host;
|
|
2277
|
+
const originUrl = URL.parse(origin);
|
|
2278
|
+
const originHost = originUrl?.host;
|
|
2279
|
+
const originProtocol = originUrl?.protocol;
|
|
2280
|
+
return actualProtocol === originProtocol && actualHost === originHost;
|
|
2281
|
+
}
|
|
2282
|
+
function isCorsRequest(request) {
|
|
2283
|
+
return request.headers.has("origin") && !isSameOrigin(request);
|
|
2284
|
+
}
|
|
2285
|
+
function isPreFlightRequest(request) {
|
|
2286
|
+
return request.method === "OPTIONS" && request.headers.has("origin") && request.headers.has("access-control-request-method");
|
|
2287
|
+
}
|
|
2288
|
+
var VARY_HEADERS = ["Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"];
|
|
2289
|
+
var processRequest = (exchange, config) => {
|
|
2290
|
+
const { request, response } = exchange;
|
|
2291
|
+
const responseHeaders = response.headers;
|
|
2292
|
+
if (!responseHeaders.has("Vary")) {
|
|
2293
|
+
responseHeaders.set("Vary", VARY_HEADERS.join(", "));
|
|
2294
|
+
} else {
|
|
2295
|
+
const varyHeaders = responseHeaders.list("Vary");
|
|
2296
|
+
for (const header of VARY_HEADERS) {
|
|
2297
|
+
if (!varyHeaders.find((h) => h === header)) {
|
|
2298
|
+
varyHeaders.push(header);
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
responseHeaders.set("Vary", varyHeaders.join(", "));
|
|
2302
|
+
}
|
|
2303
|
+
try {
|
|
2304
|
+
if (!isCorsRequest(request)) {
|
|
2305
|
+
return true;
|
|
2306
|
+
}
|
|
2307
|
+
} catch (e) {
|
|
2308
|
+
if (logger9.enabledFor("debug")) {
|
|
2309
|
+
logger9.debug(`reject: origin is malformed`);
|
|
2310
|
+
}
|
|
2311
|
+
rejectRequest(response);
|
|
2312
|
+
return false;
|
|
2313
|
+
}
|
|
2314
|
+
if (responseHeaders.has("access-control-allow-origin")) {
|
|
2315
|
+
logger9.trace(`skip: already contains "Access-Control-Allow-Origin"`);
|
|
2316
|
+
return true;
|
|
2317
|
+
}
|
|
2318
|
+
const preFlightRequest = isPreFlightRequest(request);
|
|
2319
|
+
if (config) {
|
|
2320
|
+
return handleInternal(exchange, config, preFlightRequest);
|
|
2321
|
+
}
|
|
2322
|
+
if (preFlightRequest) {
|
|
2323
|
+
rejectRequest(response);
|
|
2324
|
+
return false;
|
|
2325
|
+
}
|
|
2326
|
+
return true;
|
|
2327
|
+
};
|
|
2328
|
+
var DEFAULT_PERMIT_ALL = ["*"];
|
|
2329
|
+
var DEFAULT_PERMIT_METHODS = ["GET", "HEAD", "POST"];
|
|
2330
|
+
var PERMIT_DEFAULT_CONFIG = {
|
|
2331
|
+
allowOrigins: DEFAULT_PERMIT_ALL,
|
|
2332
|
+
allowMethods: DEFAULT_PERMIT_METHODS,
|
|
2333
|
+
allowHeaders: DEFAULT_PERMIT_ALL,
|
|
2334
|
+
maxAge: 1800
|
|
2335
|
+
// 30 minutes
|
|
2336
|
+
};
|
|
2337
|
+
function validateCorsConfig(config) {
|
|
2338
|
+
if (config) {
|
|
2339
|
+
const allowHeaders = config.allowHeaders;
|
|
2340
|
+
if (allowHeaders && allowHeaders !== ALL) {
|
|
2341
|
+
config = {
|
|
2342
|
+
...config,
|
|
2343
|
+
allowHeaders: allowHeaders.map((header) => header.toLowerCase())
|
|
2344
|
+
};
|
|
2345
|
+
}
|
|
2346
|
+
const allowOrigins = config.allowOrigins;
|
|
2347
|
+
if (allowOrigins) {
|
|
2348
|
+
if (allowOrigins === "*") {
|
|
2349
|
+
validateAllowCredentials(config);
|
|
2350
|
+
validateAllowPrivateNetwork(config);
|
|
2351
|
+
} else {
|
|
2352
|
+
config = {
|
|
2353
|
+
...config,
|
|
2354
|
+
allowOrigins: allowOrigins.map((origin) => {
|
|
2355
|
+
if (typeof origin === "string" && origin !== ALL) {
|
|
2356
|
+
origin = import_gateway6.IOGateway.Filtering.regexify(origin);
|
|
2357
|
+
if (typeof origin === "string") {
|
|
2358
|
+
return trimTrailingSlash(origin).toLowerCase();
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
return origin;
|
|
2362
|
+
})
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
return config;
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
function combine(source, other) {
|
|
2370
|
+
if (other === void 0) {
|
|
2371
|
+
return source !== void 0 ? source === ALL ? [ALL] : source : [];
|
|
2372
|
+
}
|
|
2373
|
+
if (source === void 0) {
|
|
2374
|
+
return other === ALL ? [ALL] : other;
|
|
2375
|
+
}
|
|
2376
|
+
if (source == DEFAULT_PERMIT_ALL || source === DEFAULT_PERMIT_METHODS) {
|
|
2377
|
+
return other === ALL ? [ALL] : other;
|
|
2378
|
+
}
|
|
2379
|
+
if (other == DEFAULT_PERMIT_ALL || other === DEFAULT_PERMIT_METHODS) {
|
|
2380
|
+
return source === ALL ? [ALL] : source;
|
|
2381
|
+
}
|
|
2382
|
+
if (source === ALL || source.includes(ALL) || other === ALL || other.includes(ALL)) {
|
|
2383
|
+
return [ALL];
|
|
2384
|
+
}
|
|
2385
|
+
const combined = /* @__PURE__ */ new Set();
|
|
2386
|
+
source.forEach((v) => combined.add(v));
|
|
2387
|
+
other.forEach((v) => combined.add(v));
|
|
2388
|
+
return Array.from(combined);
|
|
2389
|
+
}
|
|
2390
|
+
var combineCorsConfig = (source, other) => {
|
|
2391
|
+
if (other === void 0) {
|
|
2392
|
+
return source;
|
|
2393
|
+
}
|
|
2394
|
+
const config = {
|
|
2395
|
+
allowOrigins: combine(source.allowOrigins, other?.allowOrigins),
|
|
2396
|
+
allowMethods: combine(source.allowMethods, other?.allowMethods),
|
|
2397
|
+
allowHeaders: combine(source.allowHeaders, other?.allowHeaders),
|
|
2398
|
+
exposeHeaders: combine(source.exposeHeaders, other?.exposeHeaders),
|
|
2399
|
+
allowCredentials: other?.allowCredentials ?? source.allowCredentials,
|
|
2400
|
+
allowPrivateNetwork: other?.allowPrivateNetwork ?? source.allowPrivateNetwork,
|
|
2401
|
+
maxAge: other?.maxAge ?? source.maxAge
|
|
2402
|
+
};
|
|
2403
|
+
return config;
|
|
2404
|
+
};
|
|
2405
|
+
var corsFilter = (opts) => {
|
|
2406
|
+
const source = opts.corsConfigSource;
|
|
2407
|
+
const processor = opts.corsProcessor ?? processRequest;
|
|
2408
|
+
return async (ctx, next) => {
|
|
2409
|
+
const config = await source(ctx);
|
|
2410
|
+
const isValid = processor(ctx, config);
|
|
2411
|
+
if (!isValid || isPreFlightRequest(ctx.request)) {
|
|
2412
|
+
return;
|
|
2413
|
+
} else {
|
|
2414
|
+
await next();
|
|
2415
|
+
}
|
|
2416
|
+
};
|
|
2417
|
+
};
|
|
2418
|
+
var cors_default = corsFilter;
|
|
2419
|
+
var logger9 = getLogger("cors");
|
|
2420
|
+
function rejectRequest(response) {
|
|
2421
|
+
response.statusCode = 403;
|
|
2422
|
+
}
|
|
2423
|
+
function handleInternal(exchange, config, preFlightRequest) {
|
|
2424
|
+
const { request, response } = exchange;
|
|
2425
|
+
const responseHeaders = response.headers;
|
|
2426
|
+
const requestOrigin = request.headers.one("origin");
|
|
2427
|
+
const allowOrigin = checkOrigin(config, requestOrigin);
|
|
2428
|
+
if (allowOrigin === void 0) {
|
|
2429
|
+
if (logger9.enabledFor("debug")) {
|
|
2430
|
+
logger9.debug(`reject: '${requestOrigin}' origin is not allowed`);
|
|
2431
|
+
}
|
|
2432
|
+
rejectRequest(response);
|
|
2433
|
+
return false;
|
|
2434
|
+
}
|
|
2435
|
+
const requestMethod = getMethodToUse(request, preFlightRequest);
|
|
2436
|
+
const allowMethods = checkMethods(config, requestMethod);
|
|
2437
|
+
if (allowMethods === void 0) {
|
|
2438
|
+
if (logger9.enabledFor("debug")) {
|
|
2439
|
+
logger9.debug(`reject: HTTP '${requestMethod}' is not allowed`);
|
|
2440
|
+
}
|
|
2441
|
+
rejectRequest(response);
|
|
2442
|
+
return false;
|
|
2443
|
+
}
|
|
2444
|
+
const requestHeaders = getHeadersToUse(request, preFlightRequest);
|
|
2445
|
+
const allowHeaders = checkHeaders(config, requestHeaders);
|
|
2446
|
+
if (preFlightRequest && allowHeaders === void 0) {
|
|
2447
|
+
if (logger9.enabledFor("debug")) {
|
|
2448
|
+
logger9.debug(`reject: headers '${requestHeaders}' are not allowed`);
|
|
2449
|
+
}
|
|
2450
|
+
rejectRequest(response);
|
|
2451
|
+
return false;
|
|
2452
|
+
}
|
|
2453
|
+
responseHeaders.set("Access-Control-Allow-Origin", allowOrigin);
|
|
2454
|
+
if (preFlightRequest) {
|
|
2455
|
+
responseHeaders.set("Access-Control-Allow-Methods", allowMethods.join(","));
|
|
2456
|
+
}
|
|
2457
|
+
if (preFlightRequest && allowHeaders !== void 0 && allowHeaders.length > 0) {
|
|
2458
|
+
responseHeaders.set("Access-Control-Allow-Headers", allowHeaders.join(", "));
|
|
2459
|
+
}
|
|
2460
|
+
const exposeHeaders = config.exposeHeaders;
|
|
2461
|
+
if (exposeHeaders && exposeHeaders.length > 0) {
|
|
2462
|
+
responseHeaders.set("Access-Control-Expose-Headers", exposeHeaders.join(", "));
|
|
2463
|
+
}
|
|
2464
|
+
if (config.allowCredentials) {
|
|
2465
|
+
responseHeaders.set("Access-Control-Allow-Credentials", "true");
|
|
2466
|
+
}
|
|
2467
|
+
if (config.allowPrivateNetwork && request.headers.one("access-control-request-private-network") === "true") {
|
|
2468
|
+
responseHeaders.set("Access-Control-Allow-Private-Network", "true");
|
|
2469
|
+
}
|
|
2470
|
+
if (preFlightRequest && config.maxAge !== void 0) {
|
|
2471
|
+
responseHeaders.set("Access-Control-Max-Age", config.maxAge.toString());
|
|
2472
|
+
}
|
|
2473
|
+
return true;
|
|
2474
|
+
}
|
|
2475
|
+
var ALL = "*";
|
|
2476
|
+
var DEFAULT_METHODS = ["GET", "HEAD"];
|
|
2477
|
+
function validateAllowCredentials(config) {
|
|
2478
|
+
if (config.allowCredentials === true && config.allowOrigins === ALL) {
|
|
2479
|
+
throw new Error(`when allowCredentials is true allowOrigins cannot be "*"`);
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
function validateAllowPrivateNetwork(config) {
|
|
2483
|
+
if (config.allowPrivateNetwork === true && config.allowOrigins === ALL) {
|
|
2484
|
+
throw new Error(`when allowPrivateNetwork is true allowOrigins cannot be "*"`);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
function checkOrigin(config, origin) {
|
|
2488
|
+
if (origin) {
|
|
2489
|
+
const allowedOrigins = config.allowOrigins;
|
|
2490
|
+
if (allowedOrigins) {
|
|
2491
|
+
if (allowedOrigins === ALL) {
|
|
2492
|
+
validateAllowCredentials(config);
|
|
2493
|
+
validateAllowPrivateNetwork(config);
|
|
2494
|
+
return ALL;
|
|
2495
|
+
}
|
|
2496
|
+
const originToCheck = trimTrailingSlash(origin.toLowerCase());
|
|
2497
|
+
for (const allowedOrigin of allowedOrigins) {
|
|
2498
|
+
if (allowedOrigin === ALL || import_gateway6.IOGateway.Filtering.valueMatches(allowedOrigin, originToCheck)) {
|
|
2499
|
+
return origin;
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
function checkMethods(config, requestMethod) {
|
|
2506
|
+
if (requestMethod) {
|
|
2507
|
+
const allowedMethods = config.allowMethods ?? DEFAULT_METHODS;
|
|
2508
|
+
if (allowedMethods === ALL) {
|
|
2509
|
+
return [requestMethod];
|
|
2510
|
+
}
|
|
2511
|
+
if (import_gateway6.IOGateway.Filtering.valuesMatch(allowedMethods, requestMethod)) {
|
|
2512
|
+
return allowedMethods;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
function checkHeaders(config, requestHeaders) {
|
|
2517
|
+
if (requestHeaders === void 0) {
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
if (requestHeaders.length == 0) {
|
|
2521
|
+
return [];
|
|
2522
|
+
}
|
|
2523
|
+
const allowedHeaders = config.allowHeaders;
|
|
2524
|
+
if (allowedHeaders === void 0) {
|
|
2525
|
+
return;
|
|
2526
|
+
}
|
|
2527
|
+
const allowAnyHeader = allowedHeaders === ALL || allowedHeaders.includes(ALL);
|
|
2528
|
+
const result = [];
|
|
2529
|
+
for (const requestHeader of requestHeaders) {
|
|
2530
|
+
const value = requestHeader?.trim();
|
|
2531
|
+
if (value) {
|
|
2532
|
+
if (allowAnyHeader) {
|
|
2533
|
+
result.push(value);
|
|
2534
|
+
} else {
|
|
2535
|
+
for (const allowedHeader of allowedHeaders) {
|
|
2536
|
+
if (value.toLowerCase() === allowedHeader) {
|
|
2537
|
+
result.push(value);
|
|
2538
|
+
break;
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
if (result.length > 0) {
|
|
2545
|
+
return result;
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
function trimTrailingSlash(origin) {
|
|
2549
|
+
return origin.endsWith("/") ? origin.slice(0, -1) : origin;
|
|
2550
|
+
}
|
|
2551
|
+
function getMethodToUse(request, isPreFlight) {
|
|
2552
|
+
return isPreFlight ? request.headers.one("access-control-request-method") : request.method;
|
|
2553
|
+
}
|
|
2554
|
+
function getHeadersToUse(request, isPreFlight) {
|
|
2555
|
+
const headers2 = request.headers;
|
|
2556
|
+
return isPreFlight ? headers2.list("access-control-request-headers") : Array.from(headers2.keys());
|
|
2557
|
+
}
|
|
2558
|
+
var matchingCorsConfigSource = (opts) => {
|
|
2559
|
+
return async (exchange) => {
|
|
2560
|
+
for (const [matcher, config] of opts.mappings) {
|
|
2561
|
+
if ((await matcher(exchange)).match) {
|
|
2562
|
+
logger9.debug(`resolved cors config on '${exchange.path}' using ${matcher}: ${JSON.stringify(config)}`);
|
|
2563
|
+
return config;
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
};
|
|
2567
|
+
};
|
|
2568
|
+
|
|
2358
2569
|
// src/server/security/config.ts
|
|
2359
2570
|
var filterOrder = {
|
|
2360
2571
|
first: Number.MAX_SAFE_INTEGER,
|
|
@@ -2368,7 +2579,7 @@ var filterOrder = {
|
|
|
2368
2579
|
last: Number.MAX_SAFE_INTEGER
|
|
2369
2580
|
};
|
|
2370
2581
|
var filterOrderSymbol = Symbol.for("filterOrder");
|
|
2371
|
-
var config_default = (config,
|
|
2582
|
+
var config_default = (config, context) => {
|
|
2372
2583
|
const middleware = [];
|
|
2373
2584
|
class ServerHttpSecurity {
|
|
2374
2585
|
#authenticationEntryPoint;
|
|
@@ -2392,6 +2603,11 @@ var config_default = (config, storage) => {
|
|
|
2392
2603
|
writer[filterOrderSymbol] = filterOrder.http_headers;
|
|
2393
2604
|
middleware.push(writer);
|
|
2394
2605
|
}
|
|
2606
|
+
if (config.cors?.disabled !== true && context.corsConfigSource !== void 0) {
|
|
2607
|
+
const filter = cors_default({ corsConfigSource: context.corsConfigSource });
|
|
2608
|
+
filter[filterOrderSymbol] = filterOrder.cors;
|
|
2609
|
+
middleware.push(filter);
|
|
2610
|
+
}
|
|
2395
2611
|
if (config.basic !== void 0 && config.basic?.disabled !== true) {
|
|
2396
2612
|
const username = config.basic.user?.name.toLowerCase();
|
|
2397
2613
|
const password = config.basic.user?.password ?? "";
|
|
@@ -2410,7 +2626,7 @@ var config_default = (config, storage) => {
|
|
|
2410
2626
|
}
|
|
2411
2627
|
];
|
|
2412
2628
|
const filter = httpBasic({
|
|
2413
|
-
storage,
|
|
2629
|
+
storage: context.storage,
|
|
2414
2630
|
manager,
|
|
2415
2631
|
defaultEntryPoints: this.#defaultEntryPoints,
|
|
2416
2632
|
defaultSuccessHandlers
|
|
@@ -2420,20 +2636,43 @@ var config_default = (config, storage) => {
|
|
|
2420
2636
|
}
|
|
2421
2637
|
if (config.jwt !== void 0 && config.jwt.disabled !== true) {
|
|
2422
2638
|
const verifier = (0, import_jwt.jwtVerifier)({
|
|
2423
|
-
issuerBaseUri: config.jwt.
|
|
2639
|
+
issuerBaseUri: config.jwt.issuerUri,
|
|
2424
2640
|
issuer: config.jwt.issuer,
|
|
2425
2641
|
audience: config.jwt.audience
|
|
2426
2642
|
});
|
|
2427
2643
|
const decoder = async (token) => {
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2644
|
+
try {
|
|
2645
|
+
const { payload } = await verifier(token);
|
|
2646
|
+
return {
|
|
2647
|
+
subject: payload.sub,
|
|
2648
|
+
getClaimAsString(claim) {
|
|
2649
|
+
return payload[claim];
|
|
2650
|
+
}
|
|
2651
|
+
};
|
|
2652
|
+
} catch (e) {
|
|
2653
|
+
if (e instanceof import_jwt.JwtVerifyError) {
|
|
2654
|
+
throw new BadJwtError(e.message, { cause: e });
|
|
2433
2655
|
}
|
|
2434
|
-
|
|
2656
|
+
throw new JwtError("error occurred while attempting to decoding jwt", { cause: e });
|
|
2657
|
+
}
|
|
2435
2658
|
};
|
|
2436
|
-
const
|
|
2659
|
+
const authenticationConverter = token_converter_default({ uriQueryParameter: true });
|
|
2660
|
+
const authenticationConverterMatcher = async (exchange) => {
|
|
2661
|
+
try {
|
|
2662
|
+
const a = await authenticationConverter(exchange);
|
|
2663
|
+
return { match: a !== void 0 };
|
|
2664
|
+
} catch (e) {
|
|
2665
|
+
return { match: false };
|
|
2666
|
+
}
|
|
2667
|
+
};
|
|
2668
|
+
const entryPoint = token_entry_point_default({});
|
|
2669
|
+
this.#defaultEntryPoints.push([authenticationConverterMatcher, entryPoint]);
|
|
2670
|
+
const filter = resourceServer({
|
|
2671
|
+
storage: context.storage,
|
|
2672
|
+
entryPoint,
|
|
2673
|
+
converter: authenticationConverter,
|
|
2674
|
+
jwt: { decoder }
|
|
2675
|
+
});
|
|
2437
2676
|
filter[filterOrderSymbol] = filterOrder.authentication;
|
|
2438
2677
|
middleware.push(filter);
|
|
2439
2678
|
}
|
|
@@ -2455,10 +2694,12 @@ var config_default = (config, storage) => {
|
|
|
2455
2694
|
serverMatcher = matcher;
|
|
2456
2695
|
}
|
|
2457
2696
|
let manager2;
|
|
2458
|
-
if (access2.access === "
|
|
2697
|
+
if (access2.access === "permitted") {
|
|
2459
2698
|
manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(true));
|
|
2460
|
-
|
|
2699
|
+
manager2.toString = () => "AuthorizationManager[permitted]";
|
|
2700
|
+
} else if (access2.access === "denied") {
|
|
2461
2701
|
manager2 = new DefaultAuthorizationManager(async () => new AuthorizationDecision(false));
|
|
2702
|
+
manager2.toString = () => "AuthorizationManager[denied]";
|
|
2462
2703
|
} else if (access2.access === "authenticated") {
|
|
2463
2704
|
manager2 = new DefaultAuthorizationManager(async (p) => {
|
|
2464
2705
|
const authentication = await p;
|
|
@@ -2467,6 +2708,7 @@ var config_default = (config, storage) => {
|
|
|
2467
2708
|
}
|
|
2468
2709
|
return new AuthorizationDecision(false);
|
|
2469
2710
|
});
|
|
2711
|
+
manager2.toString = () => "AuthorizationManager[authenticated]";
|
|
2470
2712
|
} else {
|
|
2471
2713
|
throw new Error(`Unknown access type: ${JSON.stringify(access2)}`);
|
|
2472
2714
|
}
|
|
@@ -2475,7 +2717,7 @@ var config_default = (config, storage) => {
|
|
|
2475
2717
|
return delegatingAuthorizationManager({ mappings });
|
|
2476
2718
|
};
|
|
2477
2719
|
const manager = buildAuthorizationManager(config.authorize);
|
|
2478
|
-
const filter = authorizationFilter({ manager, storage });
|
|
2720
|
+
const filter = authorizationFilter({ manager, storage: context.storage });
|
|
2479
2721
|
filter[filterOrderSymbol] = filterOrder.authorization;
|
|
2480
2722
|
middleware.push(filter);
|
|
2481
2723
|
}
|
|
@@ -2491,8 +2733,80 @@ var config_default = (config, storage) => {
|
|
|
2491
2733
|
return middleware;
|
|
2492
2734
|
};
|
|
2493
2735
|
|
|
2736
|
+
// src/app/cors.ts
|
|
2737
|
+
function mockUpgradeExchange(path) {
|
|
2738
|
+
const request = new MockHttpRequest(path, "GET");
|
|
2739
|
+
request.headers.set("upgrade", "websocket");
|
|
2740
|
+
request["_req"] = { upgrade: true };
|
|
2741
|
+
return new DefaultWebExchange(request, new MockHttpResponse());
|
|
2742
|
+
}
|
|
2743
|
+
async function createCorsConfigSource(context) {
|
|
2744
|
+
const { sockets: routes3, cors } = context;
|
|
2745
|
+
const defaultCorsConfig = combineCorsConfig(PERMIT_DEFAULT_CONFIG, context.corsConfig);
|
|
2746
|
+
const validatedConfigs = [];
|
|
2747
|
+
for (const [path, route] of routes3) {
|
|
2748
|
+
let routeCorsConfig = defaultCorsConfig;
|
|
2749
|
+
const upgradeExchange = mockUpgradeExchange(path);
|
|
2750
|
+
for (const [matcher, config] of cors) {
|
|
2751
|
+
if ((await matcher(upgradeExchange)).match) {
|
|
2752
|
+
routeCorsConfig = combineCorsConfig(routeCorsConfig, config);
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
routeCorsConfig = combineCorsConfig(routeCorsConfig, {
|
|
2756
|
+
allowOrigins: route.originFilters?.allow,
|
|
2757
|
+
allowMethods: ["GET", "CONNECT"],
|
|
2758
|
+
allowHeaders: ["upgrade", "connection", "origin", "sec-websocket-key", "sec-websocket-version", "sec-websocket-protocol", "sec-websocket-extensions"],
|
|
2759
|
+
exposeHeaders: ["sec-websocket-accept", "sec-websocket-protocol", "sec-websocket-extensions"],
|
|
2760
|
+
allowCredentials: route.authorize?.access !== "permitted"
|
|
2761
|
+
});
|
|
2762
|
+
validatedConfigs.push([and([upgradeMatcher, pattern(path)]), validateCorsConfig(routeCorsConfig)]);
|
|
2763
|
+
}
|
|
2764
|
+
for (const [matcher, config] of cors) {
|
|
2765
|
+
const routeCorsConfig = combineCorsConfig(defaultCorsConfig, config);
|
|
2766
|
+
validatedConfigs.push([matcher, validateCorsConfig(routeCorsConfig)]);
|
|
2767
|
+
}
|
|
2768
|
+
validatedConfigs.push([pattern(/\/api\/.*/), validateCorsConfig(defaultCorsConfig)]);
|
|
2769
|
+
return matchingCorsConfigSource({ mappings: validatedConfigs });
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
// src/app/auth.ts
|
|
2773
|
+
function createSecurityConfig(context) {
|
|
2774
|
+
const authorize = [];
|
|
2775
|
+
const defaultAccess = { access: context.authConfig?.type !== "none" ? "authenticated" : "permitted" };
|
|
2776
|
+
for (const [path, route] of context.sockets) {
|
|
2777
|
+
const rule = route.authorize ?? defaultAccess;
|
|
2778
|
+
let matcher = pattern(path, { method: "GET" });
|
|
2779
|
+
matcher = and([upgradeMatcher, matcher]);
|
|
2780
|
+
authorize.push([matcher, rule]);
|
|
2781
|
+
}
|
|
2782
|
+
authorize.push([pattern("/", { method: "GET" }), { access: "permitted" }]);
|
|
2783
|
+
authorize.push([pattern("/favicon.ico", { method: "GET" }), { access: "permitted" }]);
|
|
2784
|
+
authorize.push([pattern("/health", { method: "GET" }), { access: "permitted" }]);
|
|
2785
|
+
if (context.authorize.length > 0) {
|
|
2786
|
+
authorize.push(...context.authorize);
|
|
2787
|
+
}
|
|
2788
|
+
authorize.push(["any-exchange", defaultAccess]);
|
|
2789
|
+
return {
|
|
2790
|
+
authorize,
|
|
2791
|
+
basic: {
|
|
2792
|
+
disabled: context.authConfig?.type !== "basic",
|
|
2793
|
+
...context.authConfig?.basic
|
|
2794
|
+
},
|
|
2795
|
+
jwt: {
|
|
2796
|
+
disabled: context.authConfig?.type !== "oauth2",
|
|
2797
|
+
...context.authConfig?.oauth2?.jwt
|
|
2798
|
+
}
|
|
2799
|
+
};
|
|
2800
|
+
}
|
|
2801
|
+
async function httpSecurity(context) {
|
|
2802
|
+
const corsConfigSource = await createCorsConfigSource(context);
|
|
2803
|
+
const config = createSecurityConfig(context);
|
|
2804
|
+
const { storage } = context;
|
|
2805
|
+
return config_default(config, { storage, corsConfigSource });
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2494
2808
|
// src/server.ts
|
|
2495
|
-
var
|
|
2809
|
+
var logger10 = getLogger("app");
|
|
2496
2810
|
function secureContextOptions(ssl) {
|
|
2497
2811
|
const options = {};
|
|
2498
2812
|
if (ssl.key) options.key = (0, import_node_fs.readFileSync)(ssl.key);
|
|
@@ -2500,60 +2814,38 @@ function secureContextOptions(ssl) {
|
|
|
2500
2814
|
if (ssl.ca) options.ca = (0, import_node_fs.readFileSync)(ssl.ca);
|
|
2501
2815
|
return options;
|
|
2502
2816
|
}
|
|
2503
|
-
function createListener(
|
|
2817
|
+
async function createListener(middleware, context, onSocketError) {
|
|
2818
|
+
const storage = context.storage;
|
|
2819
|
+
const security = await httpSecurity(context);
|
|
2504
2820
|
const listener = compose(
|
|
2505
|
-
server_header_default(),
|
|
2506
|
-
...
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
basic: {
|
|
2515
|
-
disabled: true,
|
|
2516
|
-
realm: "Gateway Server"
|
|
2517
|
-
},
|
|
2518
|
-
jwt: {
|
|
2519
|
-
disabled: true
|
|
2520
|
-
}
|
|
2521
|
-
}, storage),
|
|
2522
|
-
...cors_default({
|
|
2523
|
-
origins: { allow: [/http:\/\/localhost(:\d+)?/, /file:\//] },
|
|
2524
|
-
methods: { allow: ["GET", "HEAD", "POST", "DELETE"] },
|
|
2525
|
-
headers: { allow: "*" },
|
|
2526
|
-
credentials: { allow: true }
|
|
2527
|
-
}),
|
|
2528
|
-
...middleware,
|
|
2529
|
-
async ({ request, response }, next) => {
|
|
2530
|
-
const path = request.path ?? "/";
|
|
2531
|
-
const route = routes3.get(path) ?? Array.from(routes3.values()).find((route2) => {
|
|
2532
|
-
if (path === "/" && route2.default === true) {
|
|
2533
|
-
return true;
|
|
2534
|
-
}
|
|
2535
|
-
});
|
|
2536
|
-
if (route) {
|
|
2537
|
-
if (request.method === "GET" && request._req["upgrade"] && request.headers.one("upgrade")?.toLowerCase() === "websocket") {
|
|
2821
|
+
server_header_default(context.serverHeader),
|
|
2822
|
+
...security,
|
|
2823
|
+
// websocket upgrade handler
|
|
2824
|
+
async (exchange, next) => {
|
|
2825
|
+
const [route, path] = findSocketRoute(exchange, context);
|
|
2826
|
+
if (route !== void 0) {
|
|
2827
|
+
const { request, response } = exchange;
|
|
2828
|
+
const upgradeMatchResult = await upgradeMatcher(exchange);
|
|
2829
|
+
if ((request.method === "GET" || request.method === "CONNECT") && upgradeMatchResult.match) {
|
|
2538
2830
|
const socket = request.socket;
|
|
2539
2831
|
const host = request.host;
|
|
2540
|
-
const
|
|
2541
|
-
if (route
|
|
2832
|
+
const info2 = socketKey(request._req.socket);
|
|
2833
|
+
if (route.wss) {
|
|
2542
2834
|
socket.removeListener("error", onSocketError);
|
|
2543
2835
|
const wss = route.wss;
|
|
2544
2836
|
if (route.maxConnections !== void 0 && wss.clients?.size >= route.maxConnections) {
|
|
2545
|
-
|
|
2837
|
+
logger10.warn(`${info2} dropping ws connection request from ${host} on ${path}. max connections exceeded.`);
|
|
2546
2838
|
socket.destroy();
|
|
2547
2839
|
return;
|
|
2548
2840
|
}
|
|
2549
|
-
const origin = request.headers
|
|
2841
|
+
const origin = request.headers.one("origin");
|
|
2550
2842
|
if (!acceptsOrigin(origin, route.originFilters)) {
|
|
2551
|
-
|
|
2843
|
+
logger10.info(`${info2} dropping ws connection request from ${host} on ${path}. origin ${origin ?? "<missing>"}`);
|
|
2552
2844
|
socket.destroy();
|
|
2553
2845
|
return;
|
|
2554
2846
|
}
|
|
2555
|
-
if (
|
|
2556
|
-
|
|
2847
|
+
if (logger10.enabledFor("debug")) {
|
|
2848
|
+
logger10.debug(`${info2} accepted new ws connection request from ${host} on ${path}`);
|
|
2557
2849
|
}
|
|
2558
2850
|
wss.handleUpgrade(request._req, socket, request._req["_upgradeHead"], (ws) => {
|
|
2559
2851
|
response._res["_header"] = true;
|
|
@@ -2563,29 +2855,36 @@ function createListener(storage, middleware, routes3, onSocketError) {
|
|
|
2563
2855
|
wss.emit("connection", ws, request._req);
|
|
2564
2856
|
});
|
|
2565
2857
|
} else {
|
|
2566
|
-
|
|
2858
|
+
logger10.warn(`${info2} rejected upgrade request from ${host} on ${path}`);
|
|
2567
2859
|
socket.destroy();
|
|
2568
2860
|
}
|
|
2569
2861
|
} else {
|
|
2570
|
-
if (
|
|
2571
|
-
|
|
2862
|
+
if (route.default) {
|
|
2863
|
+
await next();
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
if (logger10.enabledFor("debug")) {
|
|
2867
|
+
logger10.debug(`rejecting request for ${path} with method ${request.method} from ${request.socket.remoteAddress}:${request.socket.remotePort} with headers: ${JSON.stringify(request._req.rawHeaders)}`);
|
|
2572
2868
|
}
|
|
2573
2869
|
response.statusCode = 426;
|
|
2574
|
-
response.
|
|
2870
|
+
response.headers.set("Upgrade", "websocket").set("Connection", "Upgrade").set("Content-Type", "text/plain");
|
|
2575
2871
|
await response.end(`This service [${request.path}] requires use of the websocket protocol.`);
|
|
2576
2872
|
}
|
|
2577
2873
|
} else {
|
|
2578
2874
|
await next();
|
|
2579
2875
|
}
|
|
2580
2876
|
},
|
|
2877
|
+
...middleware,
|
|
2878
|
+
// helth check
|
|
2581
2879
|
async ({ request, response }, next) => {
|
|
2582
2880
|
if (request.method === "GET" && request.path === "/health") {
|
|
2583
2881
|
response.statusCode = 200;
|
|
2584
|
-
response.
|
|
2882
|
+
await response.end(import_node_http.default.STATUS_CODES[200]);
|
|
2585
2883
|
} else {
|
|
2586
2884
|
await next();
|
|
2587
2885
|
}
|
|
2588
2886
|
},
|
|
2887
|
+
// home page
|
|
2589
2888
|
async ({ request, response }, next) => {
|
|
2590
2889
|
if (request.method === "GET" && request.path === "/") {
|
|
2591
2890
|
await response.end(`io.Gateway Server`);
|
|
@@ -2593,7 +2892,8 @@ function createListener(storage, middleware, routes3, onSocketError) {
|
|
|
2593
2892
|
await next();
|
|
2594
2893
|
}
|
|
2595
2894
|
},
|
|
2596
|
-
|
|
2895
|
+
// not found
|
|
2896
|
+
async ({ response }, _next) => {
|
|
2597
2897
|
response.statusCode = 404;
|
|
2598
2898
|
await response.end(import_node_http.default.STATUS_CODES[404]);
|
|
2599
2899
|
}
|
|
@@ -2602,17 +2902,17 @@ function createListener(storage, middleware, routes3, onSocketError) {
|
|
|
2602
2902
|
request.socket.addListener("error", onSocketError);
|
|
2603
2903
|
const exchange = new DefaultWebExchange(new HttpServerRequest(request), new HttpServerResponse(response));
|
|
2604
2904
|
return storage.run({ exchange }, async () => {
|
|
2605
|
-
if (
|
|
2905
|
+
if (logger10.enabledFor("debug")) {
|
|
2606
2906
|
const socket = exchange.request._req.socket;
|
|
2607
|
-
if (
|
|
2608
|
-
|
|
2907
|
+
if (logger10.enabledFor("debug")) {
|
|
2908
|
+
logger10.debug(`received ${exchange.method} request for ${exchange.path} from ${socket.remoteAddress}:${socket.remotePort}`);
|
|
2609
2909
|
}
|
|
2610
2910
|
}
|
|
2611
2911
|
try {
|
|
2612
2912
|
return await listener(exchange);
|
|
2613
2913
|
} catch (e) {
|
|
2614
|
-
if (
|
|
2615
|
-
|
|
2914
|
+
if (logger10.enabledFor("warn")) {
|
|
2915
|
+
logger10.warn(`error processing request for ${exchange.path}`, e);
|
|
2616
2916
|
}
|
|
2617
2917
|
} finally {
|
|
2618
2918
|
await exchange.response.end();
|
|
@@ -2647,65 +2947,75 @@ function regexAwareReplacer(_key, value) {
|
|
|
2647
2947
|
}
|
|
2648
2948
|
var Factory = async (options) => {
|
|
2649
2949
|
const ssl = options.ssl;
|
|
2650
|
-
const createServer = ssl ? (options2,
|
|
2950
|
+
const createServer = ssl ? (options2, handler) => import_node_https.default.createServer({ ...options2, ...secureContextOptions(ssl) }, handler) : (options2, handler) => import_node_http.default.createServer(options2, handler);
|
|
2651
2951
|
const monitor = memoryMonitor(options.memory);
|
|
2652
2952
|
const middleware = [];
|
|
2653
|
-
const
|
|
2953
|
+
const context = {
|
|
2954
|
+
corsConfig: options.cors,
|
|
2955
|
+
cors: [],
|
|
2956
|
+
authConfig: options.auth,
|
|
2957
|
+
authorize: [],
|
|
2958
|
+
storage: new import_node_async_hooks2.AsyncLocalStorage(),
|
|
2959
|
+
sockets: /* @__PURE__ */ new Map()
|
|
2960
|
+
};
|
|
2654
2961
|
const gw = import_gateway7.IOGateway.Factory({ ...options.gateway });
|
|
2655
2962
|
if (options.gateway) {
|
|
2656
2963
|
const config = options.gateway;
|
|
2657
|
-
|
|
2964
|
+
const route = config.route ?? "/";
|
|
2965
|
+
context.sockets.set(route, {
|
|
2658
2966
|
default: config.route === void 0,
|
|
2659
2967
|
ping: options.gateway.ping,
|
|
2660
2968
|
factory: core_default.bind(gw),
|
|
2661
2969
|
maxConnections: config.limits?.max_connections,
|
|
2970
|
+
authorize: config.authorize,
|
|
2662
2971
|
originFilters: regexifyOriginFilters(config.origins)
|
|
2663
2972
|
});
|
|
2664
2973
|
}
|
|
2665
2974
|
if (options.mesh) {
|
|
2666
2975
|
const connections = new InMemoryNodeConnections(options.mesh.timeout ?? 6e4);
|
|
2667
|
-
|
|
2976
|
+
const authorize = options.mesh.authorize;
|
|
2977
|
+
middleware.push(...routes_default(connections, context, authorize));
|
|
2668
2978
|
const ping = options.mesh.ping ?? 3e4;
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2979
|
+
const originFilters = regexifyOriginFilters(options.mesh.origins);
|
|
2980
|
+
context.sockets.set("/broker", { factory: core_default2, ping, originFilters, authorize });
|
|
2981
|
+
context.sockets.set("/cluster", { factory: core_default4, ping, originFilters, authorize });
|
|
2982
|
+
context.sockets.set("/relays", { factory: core_default3, ping, originFilters, authorize });
|
|
2672
2983
|
}
|
|
2673
2984
|
if (options.metrics) {
|
|
2674
|
-
middleware.push(...await routes_default2(options.metrics));
|
|
2985
|
+
middleware.push(...await routes_default2(options.metrics, context));
|
|
2675
2986
|
}
|
|
2676
2987
|
const ports = portRange(options.port ?? 0);
|
|
2677
2988
|
const host = options.host;
|
|
2678
|
-
const
|
|
2989
|
+
const onSocketError = (err) => logger10.error(`socket error: ${err}`, err);
|
|
2990
|
+
const listener = await createListener(middleware, context, onSocketError);
|
|
2679
2991
|
const serverP = new Promise((resolve, reject) => {
|
|
2680
|
-
const onSocketError = (err) => logger7.error(`socket error: ${err}`, err);
|
|
2681
|
-
const listener = createListener(storage, middleware, routes3, onSocketError);
|
|
2682
2992
|
const server2 = createServer({}, listener);
|
|
2683
2993
|
server2.on("error", (e) => {
|
|
2684
2994
|
if (e["code"] === "EADDRINUSE") {
|
|
2685
|
-
|
|
2995
|
+
logger10.debug(`port ${e["port"]} already in use on address ${e["address"]}`);
|
|
2686
2996
|
const { value: port } = ports.next();
|
|
2687
2997
|
if (port) {
|
|
2688
|
-
|
|
2998
|
+
logger10.info(`retry starting server on port ${port} and host ${host ?? "<unspecified>"}`);
|
|
2689
2999
|
server2.close();
|
|
2690
3000
|
server2.listen(port, host);
|
|
2691
3001
|
} else {
|
|
2692
|
-
|
|
3002
|
+
logger10.warn(`all configured port(s) ${options.port} are in use. closing...`);
|
|
2693
3003
|
server2.close();
|
|
2694
3004
|
reject(e);
|
|
2695
3005
|
}
|
|
2696
3006
|
} else {
|
|
2697
|
-
|
|
3007
|
+
logger10.error(`server error: ${e.message}`, e);
|
|
2698
3008
|
reject(e);
|
|
2699
3009
|
}
|
|
2700
3010
|
});
|
|
2701
3011
|
server2.on("listening", async () => {
|
|
2702
|
-
const
|
|
2703
|
-
for (const [path, route] of
|
|
3012
|
+
const info2 = server2.address();
|
|
3013
|
+
for (const [path, route] of context.sockets) {
|
|
2704
3014
|
try {
|
|
2705
|
-
|
|
3015
|
+
logger10.info(`creating ws server for [${path}]. max connections: ${route.maxConnections ?? "<unlimited>"}, origin filters: ${route.originFilters ? JSON.stringify(route.originFilters, regexAwareReplacer) : "<none>"}`);
|
|
2706
3016
|
const wss = new import_ws.WebSocketServer({ noServer: true });
|
|
2707
|
-
const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${
|
|
2708
|
-
const
|
|
3017
|
+
const endpoint = `${ssl ? "wss" : "ws"}://${localIp}:${info2.port}${path}`;
|
|
3018
|
+
const handler = await route.factory({ endpoint, wss, storage: context.storage });
|
|
2709
3019
|
const pingInterval = route.ping;
|
|
2710
3020
|
if (pingInterval) {
|
|
2711
3021
|
const pingIntervalId = setInterval(() => {
|
|
@@ -2722,12 +3032,12 @@ var Factory = async (options) => {
|
|
|
2722
3032
|
});
|
|
2723
3033
|
}
|
|
2724
3034
|
route.wss = wss;
|
|
2725
|
-
route.close =
|
|
3035
|
+
route.close = handler.close?.bind(handler);
|
|
2726
3036
|
} catch (e) {
|
|
2727
|
-
|
|
3037
|
+
logger10.warn(`failed to init route ${path}`, e);
|
|
2728
3038
|
}
|
|
2729
3039
|
}
|
|
2730
|
-
|
|
3040
|
+
logger10.info(`http server listening on ${info2.address}:${info2.port}`);
|
|
2731
3041
|
resolve(server2);
|
|
2732
3042
|
});
|
|
2733
3043
|
server2.on("upgrade", (req, socket, head) => {
|
|
@@ -2738,16 +3048,16 @@ var Factory = async (options) => {
|
|
|
2738
3048
|
res.assignSocket(socket);
|
|
2739
3049
|
listener(req, res);
|
|
2740
3050
|
} catch (err) {
|
|
2741
|
-
|
|
3051
|
+
logger10.error(`upgrade error: ${err}`, err);
|
|
2742
3052
|
}
|
|
2743
3053
|
}).on("close", async () => {
|
|
2744
|
-
|
|
3054
|
+
logger10.info(`http server closed.`);
|
|
2745
3055
|
});
|
|
2746
3056
|
try {
|
|
2747
3057
|
const { value: port } = ports.next();
|
|
2748
3058
|
server2.listen(port, host);
|
|
2749
3059
|
} catch (e) {
|
|
2750
|
-
|
|
3060
|
+
logger10.error(`error starting web socket server`, e);
|
|
2751
3061
|
reject(e instanceof Error ? e : new Error(`listen failed: ${e}`));
|
|
2752
3062
|
}
|
|
2753
3063
|
});
|
|
@@ -2755,18 +3065,18 @@ var Factory = async (options) => {
|
|
|
2755
3065
|
return new class {
|
|
2756
3066
|
gateway = gw;
|
|
2757
3067
|
async close() {
|
|
2758
|
-
for (const [path, route] of
|
|
3068
|
+
for (const [path, route] of context.sockets) {
|
|
2759
3069
|
try {
|
|
2760
3070
|
if (route.close) {
|
|
2761
3071
|
await route.close();
|
|
2762
3072
|
}
|
|
2763
|
-
|
|
3073
|
+
logger10.info(`stopping ws server for [${path}]. clients: ${route.wss?.clients?.size ?? 0}`);
|
|
2764
3074
|
route.wss?.clients?.forEach((client) => {
|
|
2765
3075
|
client.terminate();
|
|
2766
3076
|
});
|
|
2767
3077
|
route.wss?.close();
|
|
2768
3078
|
} catch (e) {
|
|
2769
|
-
|
|
3079
|
+
logger10.warn(`error closing route ${path}`, e);
|
|
2770
3080
|
}
|
|
2771
3081
|
}
|
|
2772
3082
|
await promisify((cb) => {
|