@jsenv/server 17.6.6 → 17.6.7
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/jsenv_server.js
CHANGED
|
@@ -1150,6 +1150,62 @@ const stringifyHeaderProperty = ({ name, value }) => {
|
|
|
1150
1150
|
return `${name}=${value}`;
|
|
1151
1151
|
};
|
|
1152
1152
|
|
|
1153
|
+
/*
|
|
1154
|
+
* A request target is the text between the method and the http version on the
|
|
1155
|
+
* request line (RFC 9112 section 3.2), or the ":path" pseudo header in http2.
|
|
1156
|
+
* It is a path, not a url reference, and the two must never be confused:
|
|
1157
|
+
* "//wp-includes/x" is an origin-form target whose first path segment is
|
|
1158
|
+
* empty, but `new URL("//wp-includes/x", origin)` reads it as a
|
|
1159
|
+
* protocol-relative reference and turns "wp-includes" into the host. So the
|
|
1160
|
+
* path is assigned onto the origin instead, and what a client sends can never
|
|
1161
|
+
* replace that origin.
|
|
1162
|
+
*
|
|
1163
|
+
* The one form that legitimately names an origin is absolute-form
|
|
1164
|
+
* ("GET http://host/path", sent by proxies and open-proxy scanners); it is
|
|
1165
|
+
* read as such so the origin it names goes through the allowedHosts check
|
|
1166
|
+
* like any other host, instead of reaching the routes unchecked.
|
|
1167
|
+
*/
|
|
1168
|
+
|
|
1169
|
+
const ABSOLUTE_FORM_REGEX = /^[a-z][a-z0-9+\-.]*:\/\//i;
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* @param {string} target the raw request target
|
|
1173
|
+
* @returns {{ origin: string|null, host: string|null, resource: string }|null}
|
|
1174
|
+
* null when the target cannot be read; the server answers 400
|
|
1175
|
+
*/
|
|
1176
|
+
const readRequestTarget = (target) => {
|
|
1177
|
+
if (!ABSOLUTE_FORM_REGEX.test(target)) {
|
|
1178
|
+
// origin-form ("/path?query"), asterisk-form ("*") or authority-form
|
|
1179
|
+
return { origin: null, host: null, resource: target };
|
|
1180
|
+
}
|
|
1181
|
+
if (!URL.canParse(target)) {
|
|
1182
|
+
return null;
|
|
1183
|
+
}
|
|
1184
|
+
const urlObject = new URL(target);
|
|
1185
|
+
if (urlObject.protocol !== "http:" && urlObject.protocol !== "https:") {
|
|
1186
|
+
return null;
|
|
1187
|
+
}
|
|
1188
|
+
return {
|
|
1189
|
+
origin: urlObject.origin,
|
|
1190
|
+
host: urlObject.host,
|
|
1191
|
+
resource: `${urlObject.pathname}${urlObject.search}`,
|
|
1192
|
+
};
|
|
1193
|
+
};
|
|
1194
|
+
|
|
1195
|
+
// the search of baseUrl is dropped: the resource carries its own
|
|
1196
|
+
const resourceToUrlObject = (resource, baseUrl) => {
|
|
1197
|
+
const urlObject = new URL(baseUrl);
|
|
1198
|
+
const searchSeparatorIndex = resource.indexOf("?");
|
|
1199
|
+
if (searchSeparatorIndex === -1) {
|
|
1200
|
+
urlObject.pathname = resource;
|
|
1201
|
+
urlObject.search = "";
|
|
1202
|
+
} else {
|
|
1203
|
+
urlObject.pathname = resource.slice(0, searchSeparatorIndex);
|
|
1204
|
+
urlObject.search = resource.slice(searchSeparatorIndex);
|
|
1205
|
+
}
|
|
1206
|
+
return urlObject;
|
|
1207
|
+
};
|
|
1208
|
+
|
|
1153
1209
|
// https://wicg.github.io/observable/#core-infrastructure
|
|
1154
1210
|
|
|
1155
1211
|
if ("observable" in Symbol === false) {
|
|
@@ -1329,8 +1385,13 @@ const fromNodeRequest = (
|
|
|
1329
1385
|
nodeRequest.pause();
|
|
1330
1386
|
const body = observableFromNodeStream(nodeRequest);
|
|
1331
1387
|
|
|
1388
|
+
// an absolute-form target names the origin the request is for; the host
|
|
1389
|
+
// header is ignored in that case (RFC 9112 section 3.3)
|
|
1390
|
+
const requestTarget = readRequestTarget(nodeRequest.url);
|
|
1332
1391
|
let requestOrigin;
|
|
1333
|
-
if (
|
|
1392
|
+
if (requestTarget.origin) {
|
|
1393
|
+
requestOrigin = requestTarget.origin;
|
|
1394
|
+
} else if (nodeRequest.upgrade) {
|
|
1334
1395
|
requestOrigin = serverOrigin;
|
|
1335
1396
|
} else if (nodeRequest.authority) {
|
|
1336
1397
|
requestOrigin = nodeRequest.connection.encrypted
|
|
@@ -1463,7 +1524,7 @@ const fromNodeRequest = (
|
|
|
1463
1524
|
http2: Boolean(nodeRequest.stream),
|
|
1464
1525
|
origin: requestOrigin,
|
|
1465
1526
|
...getPropertiesFromResource({
|
|
1466
|
-
resource:
|
|
1527
|
+
resource: requestTarget.resource,
|
|
1467
1528
|
baseUrl: requestOrigin,
|
|
1468
1529
|
}),
|
|
1469
1530
|
method: nodeRequest.method,
|
|
@@ -1690,13 +1751,12 @@ const applyRedirectionToRequest = (
|
|
|
1690
1751
|
};
|
|
1691
1752
|
};
|
|
1692
1753
|
const getPropertiesFromResource = ({ resource, baseUrl }) => {
|
|
1693
|
-
const urlObject =
|
|
1694
|
-
let pathname = urlObject.pathname;
|
|
1754
|
+
const urlObject = resourceToUrlObject(resource, baseUrl);
|
|
1695
1755
|
|
|
1696
1756
|
return {
|
|
1697
1757
|
url: String(urlObject),
|
|
1698
1758
|
searchParams: urlObject.searchParams,
|
|
1699
|
-
pathname,
|
|
1759
|
+
pathname: urlObject.pathname,
|
|
1700
1760
|
resource,
|
|
1701
1761
|
};
|
|
1702
1762
|
};
|
|
@@ -5833,8 +5893,17 @@ const createResourcePattern = (pattern) => {
|
|
|
5833
5893
|
}
|
|
5834
5894
|
|
|
5835
5895
|
const patternEndsWithSlash = pathnamePatternString.endsWith("/");
|
|
5896
|
+
// a pattern constraining nothing: every resource matches it, so whoever uses
|
|
5897
|
+
// it cannot name the resource that matched
|
|
5898
|
+
const matchesEveryResource =
|
|
5899
|
+
!searchPattern &&
|
|
5900
|
+
!hashPattern &&
|
|
5901
|
+
(pathnamePatternString === "*" ||
|
|
5902
|
+
pathnamePatternString === "/" ||
|
|
5903
|
+
pathnamePatternString === "/*");
|
|
5836
5904
|
|
|
5837
5905
|
return {
|
|
5906
|
+
matchesEveryResource,
|
|
5838
5907
|
match: (resource) => {
|
|
5839
5908
|
const [pathname, search, hash] = resourceToParts(resource);
|
|
5840
5909
|
let decodedPathname = decodeURIComponent(pathname);
|
|
@@ -6477,9 +6546,9 @@ It should be should be one of route.${routePropertyName}: ${availableValues.join
|
|
|
6477
6546
|
}
|
|
6478
6547
|
if (!route.matchMethod(request.method)) {
|
|
6479
6548
|
// a 405 asserts the resource exists with other methods: a route
|
|
6480
|
-
// matching
|
|
6481
|
-
//
|
|
6482
|
-
if (!route.isFallback && route.
|
|
6549
|
+
// matching every resource ("GET *", "GET /") names no resource, so
|
|
6550
|
+
// it cannot make that assertion and an unknown address stays a 404
|
|
6551
|
+
if (!route.isFallback && !route.matchesEveryResource) {
|
|
6483
6552
|
wouldHaveMatched.methodSet.add(route.method);
|
|
6484
6553
|
}
|
|
6485
6554
|
continue;
|
|
@@ -6827,6 +6896,8 @@ const createRoute = ({
|
|
|
6827
6896
|
: (requestResource) => {
|
|
6828
6897
|
return resourcePattern.match(requestResource);
|
|
6829
6898
|
},
|
|
6899
|
+
matchesEveryResource:
|
|
6900
|
+
resource === "*" || resourcePattern.matchesEveryResource,
|
|
6830
6901
|
matchHeaders:
|
|
6831
6902
|
headers === undefined
|
|
6832
6903
|
? () => true
|
|
@@ -7972,7 +8043,14 @@ const startServer = async ({
|
|
|
7972
8043
|
|
|
7973
8044
|
{
|
|
7974
8045
|
const requestEventHandler = async (nodeRequest, nodeResponse) => {
|
|
7975
|
-
const
|
|
8046
|
+
const requestTarget = readRequestTarget(nodeRequest.url);
|
|
8047
|
+
if (!requestTarget) {
|
|
8048
|
+
nodeResponse.writeHead(400, "Request url is not supported");
|
|
8049
|
+
nodeResponse.end();
|
|
8050
|
+
return;
|
|
8051
|
+
}
|
|
8052
|
+
const requestHost =
|
|
8053
|
+
requestTarget.host || nodeRequest.authority || nodeRequest.headers.host;
|
|
7976
8054
|
if (!isHostAllowed(requestHost)) {
|
|
7977
8055
|
logger.warn(
|
|
7978
8056
|
`${nodeRequest.method} ${nodeRequest.url} refused: host "${requestHost}" is not allowed (see the allowedHosts option)`,
|
|
@@ -7983,19 +8061,11 @@ const startServer = async ({
|
|
|
7983
8061
|
}
|
|
7984
8062
|
if (redirectHttpToHttps && !nodeRequest.connection.encrypted) {
|
|
7985
8063
|
nodeResponse.writeHead(301, {
|
|
7986
|
-
location: `${serverOrigin}${
|
|
8064
|
+
location: `${serverOrigin}${requestTarget.resource}`,
|
|
7987
8065
|
});
|
|
7988
8066
|
nodeResponse.end();
|
|
7989
8067
|
return;
|
|
7990
8068
|
}
|
|
7991
|
-
try {
|
|
7992
|
-
// eslint-disable-next-line no-new
|
|
7993
|
-
new URL(nodeRequest.url, "http://example.com");
|
|
7994
|
-
} catch {
|
|
7995
|
-
nodeResponse.writeHead(400, "Request url is not supported");
|
|
7996
|
-
nodeResponse.end();
|
|
7997
|
-
return;
|
|
7998
|
-
}
|
|
7999
8069
|
|
|
8000
8070
|
const [receiveRequestOperation, sendResponseOperation] =
|
|
8001
8071
|
prepareHandleRequestOperations(nodeRequest, nodeResponse);
|
|
@@ -8072,7 +8142,13 @@ const startServer = async ({
|
|
|
8072
8142
|
};
|
|
8073
8143
|
// https://github.com/websockets/ws/blob/b92745a9d6760e6b4b2394bfac78cbcd258a8c8d/lib/websocket-server.js#L491
|
|
8074
8144
|
const upgradeEventHandler = async (nodeRequest, socket, head) => {
|
|
8075
|
-
const
|
|
8145
|
+
const requestTarget = readRequestTarget(nodeRequest.url);
|
|
8146
|
+
if (!requestTarget) {
|
|
8147
|
+
socket.write(`HTTP/1.1 400 Request url is not supported\r\n\r\n`);
|
|
8148
|
+
socket.destroy();
|
|
8149
|
+
return;
|
|
8150
|
+
}
|
|
8151
|
+
const requestHost = requestTarget.host || nodeRequest.headers.host;
|
|
8076
8152
|
if (!isHostAllowed(requestHost)) {
|
|
8077
8153
|
logger.warn(
|
|
8078
8154
|
`${nodeRequest.method} ${nodeRequest.url} refused: host "${requestHost}" is not allowed (see the allowedHosts option)`,
|
|
@@ -8987,8 +9063,11 @@ const fetchFileSystem = async (
|
|
|
8987
9063
|
if (request.params && "0" in request.params) {
|
|
8988
9064
|
resource = request.params["0"];
|
|
8989
9065
|
} else {
|
|
8990
|
-
resource = request.resource
|
|
9066
|
+
resource = request.resource;
|
|
8991
9067
|
}
|
|
9068
|
+
// the resource is resolved against the served directory, so it must stay a
|
|
9069
|
+
// relative reference: a leading slash would resolve to the filesystem root
|
|
9070
|
+
resource = resource.replace(/^\/+/, "");
|
|
8992
9071
|
const filesystemUrl = new URL(resource, directoryUrlString);
|
|
8993
9072
|
const urlString = asUrlString(filesystemUrl);
|
|
8994
9073
|
if (!urlIsOrIsInsideOf(urlString, directoryUrlString)) {
|
package/package.json
CHANGED
|
@@ -20,6 +20,10 @@ import {
|
|
|
20
20
|
} from "../internal/colorize_response_status.js";
|
|
21
21
|
import { headersFromObject } from "../internal/headers_from_object.js";
|
|
22
22
|
import { parseSingleHeaderWithAttributes } from "../internal/multiple_header.js";
|
|
23
|
+
import {
|
|
24
|
+
readRequestTarget,
|
|
25
|
+
resourceToUrlObject,
|
|
26
|
+
} from "../internal/request_target.js";
|
|
23
27
|
import { observableFromNodeStream } from "./observable_from_node_stream.js";
|
|
24
28
|
|
|
25
29
|
export const fromNodeRequest = (
|
|
@@ -66,8 +70,13 @@ export const fromNodeRequest = (
|
|
|
66
70
|
nodeRequest.pause();
|
|
67
71
|
const body = observableFromNodeStream(nodeRequest);
|
|
68
72
|
|
|
73
|
+
// an absolute-form target names the origin the request is for; the host
|
|
74
|
+
// header is ignored in that case (RFC 9112 section 3.3)
|
|
75
|
+
const requestTarget = readRequestTarget(nodeRequest.url);
|
|
69
76
|
let requestOrigin;
|
|
70
|
-
if (
|
|
77
|
+
if (requestTarget.origin) {
|
|
78
|
+
requestOrigin = requestTarget.origin;
|
|
79
|
+
} else if (nodeRequest.upgrade) {
|
|
71
80
|
requestOrigin = serverOrigin;
|
|
72
81
|
} else if (nodeRequest.authority) {
|
|
73
82
|
requestOrigin = nodeRequest.connection.encrypted
|
|
@@ -200,7 +209,7 @@ export const fromNodeRequest = (
|
|
|
200
209
|
http2: Boolean(nodeRequest.stream),
|
|
201
210
|
origin: requestOrigin,
|
|
202
211
|
...getPropertiesFromResource({
|
|
203
|
-
resource:
|
|
212
|
+
resource: requestTarget.resource,
|
|
204
213
|
baseUrl: requestOrigin,
|
|
205
214
|
}),
|
|
206
215
|
method: nodeRequest.method,
|
|
@@ -440,13 +449,12 @@ export const applyRedirectionToRequest = (
|
|
|
440
449
|
};
|
|
441
450
|
};
|
|
442
451
|
const getPropertiesFromResource = ({ resource, baseUrl }) => {
|
|
443
|
-
const urlObject =
|
|
444
|
-
let pathname = urlObject.pathname;
|
|
452
|
+
const urlObject = resourceToUrlObject(resource, baseUrl);
|
|
445
453
|
|
|
446
454
|
return {
|
|
447
455
|
url: String(urlObject),
|
|
448
456
|
searchParams: urlObject.searchParams,
|
|
449
|
-
pathname,
|
|
457
|
+
pathname: urlObject.pathname,
|
|
450
458
|
resource,
|
|
451
459
|
};
|
|
452
460
|
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A request target is the text between the method and the http version on the
|
|
3
|
+
* request line (RFC 9112 section 3.2), or the ":path" pseudo header in http2.
|
|
4
|
+
* It is a path, not a url reference, and the two must never be confused:
|
|
5
|
+
* "//wp-includes/x" is an origin-form target whose first path segment is
|
|
6
|
+
* empty, but `new URL("//wp-includes/x", origin)` reads it as a
|
|
7
|
+
* protocol-relative reference and turns "wp-includes" into the host. So the
|
|
8
|
+
* path is assigned onto the origin instead, and what a client sends can never
|
|
9
|
+
* replace that origin.
|
|
10
|
+
*
|
|
11
|
+
* The one form that legitimately names an origin is absolute-form
|
|
12
|
+
* ("GET http://host/path", sent by proxies and open-proxy scanners); it is
|
|
13
|
+
* read as such so the origin it names goes through the allowedHosts check
|
|
14
|
+
* like any other host, instead of reaching the routes unchecked.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const ABSOLUTE_FORM_REGEX = /^[a-z][a-z0-9+\-.]*:\/\//i;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} target the raw request target
|
|
21
|
+
* @returns {{ origin: string|null, host: string|null, resource: string }|null}
|
|
22
|
+
* null when the target cannot be read; the server answers 400
|
|
23
|
+
*/
|
|
24
|
+
export const readRequestTarget = (target) => {
|
|
25
|
+
if (!ABSOLUTE_FORM_REGEX.test(target)) {
|
|
26
|
+
// origin-form ("/path?query"), asterisk-form ("*") or authority-form
|
|
27
|
+
return { origin: null, host: null, resource: target };
|
|
28
|
+
}
|
|
29
|
+
if (!URL.canParse(target)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const urlObject = new URL(target);
|
|
33
|
+
if (urlObject.protocol !== "http:" && urlObject.protocol !== "https:") {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
origin: urlObject.origin,
|
|
38
|
+
host: urlObject.host,
|
|
39
|
+
resource: `${urlObject.pathname}${urlObject.search}`,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// the search of baseUrl is dropped: the resource carries its own
|
|
44
|
+
export const resourceToUrlObject = (resource, baseUrl) => {
|
|
45
|
+
const urlObject = new URL(baseUrl);
|
|
46
|
+
const searchSeparatorIndex = resource.indexOf("?");
|
|
47
|
+
if (searchSeparatorIndex === -1) {
|
|
48
|
+
urlObject.pathname = resource;
|
|
49
|
+
urlObject.search = "";
|
|
50
|
+
} else {
|
|
51
|
+
urlObject.pathname = resource.slice(0, searchSeparatorIndex);
|
|
52
|
+
urlObject.search = resource.slice(searchSeparatorIndex);
|
|
53
|
+
}
|
|
54
|
+
return urlObject;
|
|
55
|
+
};
|
|
@@ -112,8 +112,11 @@ export const fetchFileSystem = async (
|
|
|
112
112
|
if (request.params && "0" in request.params) {
|
|
113
113
|
resource = request.params["0"];
|
|
114
114
|
} else {
|
|
115
|
-
resource = request.resource
|
|
115
|
+
resource = request.resource;
|
|
116
116
|
}
|
|
117
|
+
// the resource is resolved against the served directory, so it must stay a
|
|
118
|
+
// relative reference: a leading slash would resolve to the filesystem root
|
|
119
|
+
resource = resource.replace(/^\/+/, "");
|
|
117
120
|
const filesystemUrl = new URL(resource, directoryUrlString);
|
|
118
121
|
const urlString = asUrlString(filesystemUrl);
|
|
119
122
|
if (!urlIsOrIsInsideOf(urlString, directoryUrlString)) {
|
package/src/router/router.js
CHANGED
|
@@ -356,9 +356,9 @@ It should be should be one of route.${routePropertyName}: ${availableValues.join
|
|
|
356
356
|
}
|
|
357
357
|
if (!route.matchMethod(request.method)) {
|
|
358
358
|
// a 405 asserts the resource exists with other methods: a route
|
|
359
|
-
// matching
|
|
360
|
-
//
|
|
361
|
-
if (!route.isFallback && route.
|
|
359
|
+
// matching every resource ("GET *", "GET /") names no resource, so
|
|
360
|
+
// it cannot make that assertion and an unknown address stays a 404
|
|
361
|
+
if (!route.isFallback && !route.matchesEveryResource) {
|
|
362
362
|
wouldHaveMatched.methodSet.add(route.method);
|
|
363
363
|
}
|
|
364
364
|
continue;
|
|
@@ -709,6 +709,8 @@ const createRoute = ({
|
|
|
709
709
|
: (requestResource) => {
|
|
710
710
|
return resourcePattern.match(requestResource);
|
|
711
711
|
},
|
|
712
|
+
matchesEveryResource:
|
|
713
|
+
resource === "*" || resourcePattern.matchesEveryResource,
|
|
712
714
|
matchHeaders:
|
|
713
715
|
headers === undefined
|
|
714
716
|
? () => true
|
package/src/start_server.js
CHANGED
|
@@ -18,6 +18,7 @@ import { listen, stopListening } from "./internal/listen.js";
|
|
|
18
18
|
import { listenEvent } from "./internal/listen_event.js";
|
|
19
19
|
import { listenRequest } from "./internal/listen_request.js";
|
|
20
20
|
import { listenServerConnectionError } from "./internal/listen_server_connection_error.js";
|
|
21
|
+
import { readRequestTarget } from "./internal/request_target.js";
|
|
21
22
|
import { composeTwoResponses } from "./internal/response_composition.js";
|
|
22
23
|
import { createSecureServer } from "./internal/secure_server.js";
|
|
23
24
|
import { resolveServerOrigins } from "./internal/server_origins.js";
|
|
@@ -792,7 +793,14 @@ export const startServer = async ({
|
|
|
792
793
|
|
|
793
794
|
request: {
|
|
794
795
|
const requestEventHandler = async (nodeRequest, nodeResponse) => {
|
|
795
|
-
const
|
|
796
|
+
const requestTarget = readRequestTarget(nodeRequest.url);
|
|
797
|
+
if (!requestTarget) {
|
|
798
|
+
nodeResponse.writeHead(400, "Request url is not supported");
|
|
799
|
+
nodeResponse.end();
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
const requestHost =
|
|
803
|
+
requestTarget.host || nodeRequest.authority || nodeRequest.headers.host;
|
|
796
804
|
if (!isHostAllowed(requestHost)) {
|
|
797
805
|
logger.warn(
|
|
798
806
|
`${nodeRequest.method} ${nodeRequest.url} refused: host "${requestHost}" is not allowed (see the allowedHosts option)`,
|
|
@@ -803,19 +811,11 @@ export const startServer = async ({
|
|
|
803
811
|
}
|
|
804
812
|
if (redirectHttpToHttps && !nodeRequest.connection.encrypted) {
|
|
805
813
|
nodeResponse.writeHead(301, {
|
|
806
|
-
location: `${serverOrigin}${
|
|
814
|
+
location: `${serverOrigin}${requestTarget.resource}`,
|
|
807
815
|
});
|
|
808
816
|
nodeResponse.end();
|
|
809
817
|
return;
|
|
810
818
|
}
|
|
811
|
-
try {
|
|
812
|
-
// eslint-disable-next-line no-new
|
|
813
|
-
new URL(nodeRequest.url, "http://example.com");
|
|
814
|
-
} catch {
|
|
815
|
-
nodeResponse.writeHead(400, "Request url is not supported");
|
|
816
|
-
nodeResponse.end();
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
819
819
|
|
|
820
820
|
const [receiveRequestOperation, sendResponseOperation] =
|
|
821
821
|
prepareHandleRequestOperations(nodeRequest, nodeResponse);
|
|
@@ -897,7 +897,13 @@ export const startServer = async ({
|
|
|
897
897
|
};
|
|
898
898
|
// https://github.com/websockets/ws/blob/b92745a9d6760e6b4b2394bfac78cbcd258a8c8d/lib/websocket-server.js#L491
|
|
899
899
|
const upgradeEventHandler = async (nodeRequest, socket, head) => {
|
|
900
|
-
const
|
|
900
|
+
const requestTarget = readRequestTarget(nodeRequest.url);
|
|
901
|
+
if (!requestTarget) {
|
|
902
|
+
socket.write(`HTTP/1.1 400 Request url is not supported\r\n\r\n`);
|
|
903
|
+
socket.destroy();
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const requestHost = requestTarget.host || nodeRequest.headers.host;
|
|
901
907
|
if (!isHostAllowed(requestHost)) {
|
|
902
908
|
logger.warn(
|
|
903
909
|
`${nodeRequest.method} ${nodeRequest.url} refused: host "${requestHost}" is not allowed (see the allowedHosts option)`,
|