@jsenv/server 15.0.1 → 15.0.3
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/README.md +23 -23
- package/package.json +4 -4
- package/src/content_negotiation/pick_accepted_content.js +13 -13
- package/src/content_negotiation/pick_content_encoding.js +25 -25
- package/src/content_negotiation/pick_content_language.js +35 -34
- package/src/content_negotiation/pick_content_type.js +29 -29
- package/src/fetch_filesystem.js +110 -107
- package/src/from_fetch_response.js +6 -6
- package/src/interfacing_with_node/body.js +22 -22
- package/src/interfacing_with_node/observable.js +80 -80
- package/src/interfacing_with_node/observable_from_node_stream.js +35 -35
- package/src/interfacing_with_node/request_factory.js +26 -26
- package/src/interfacing_with_node/write_node_response.js +82 -82
- package/src/internal/colorizeResponseStatus.js +26 -26
- package/src/internal/convertFileSystemErrorToResponseProperties.js +10 -10
- package/src/internal/dns_resolution.js +7 -7
- package/src/internal/etag.js +11 -11
- package/src/internal/filesystem.js +19 -19
- package/src/internal/headersFromObject.js +8 -8
- package/src/internal/headersFromString.js +11 -11
- package/src/internal/headersToObject.js +5 -5
- package/src/internal/headersToString.js +7 -7
- package/src/internal/headers_composition.js +9 -9
- package/src/internal/hostname_parser.js +9 -9
- package/src/internal/listen.js +47 -47
- package/src/internal/listenClientError.js +10 -10
- package/src/internal/listenEvent.js +5 -5
- package/src/internal/listenRequest.js +8 -8
- package/src/internal/listenServerConnectionError.js +19 -19
- package/src/internal/multiple-header.js +50 -50
- package/src/internal/normalizeHeaderName.js +4 -4
- package/src/internal/normalizeHeaderValue.js +2 -2
- package/src/internal/object_composition.js +41 -39
- package/src/internal/response_composition.js +6 -6
- package/src/internal/server-polyglot.js +42 -42
- package/src/internal/server_ips.js +20 -20
- package/src/internal/trackServerPendingConnections.js +23 -23
- package/src/internal/trackServerPendingRequests.js +28 -28
- package/src/main.js +18 -18
- package/src/readRequestBody.js +15 -15
- package/src/serve_directory.js +16 -16
- package/src/server_timing/timing_header.js +24 -24
- package/src/server_timing/timing_measure.js +14 -14
- package/src/service_composition/path_to_regexp.js +173 -168
- package/src/service_composition/routing.js +12 -12
- package/src/service_controller.js +77 -77
- package/src/services/cors/jsenv_service_cors.js +29 -29
- package/src/services/error_handler/jsenv_service_error_handler.js +18 -18
- package/src/services/request_aliases/jsenv_service_request_aliases.js +20 -20
- package/src/services/response_acceptance_check/jsenv_service_response_acceptance_check.js +16 -16
- package/src/sse/sse_room.js +117 -115
- package/src/start_server.js +328 -328
- package/src/stopReasons.js +9 -9
|
@@ -7,14 +7,14 @@ export const composeTwoObjects = (
|
|
|
7
7
|
return applyCompositionForcingLowerCase(firstObject, secondObject, {
|
|
8
8
|
keysComposition,
|
|
9
9
|
strict,
|
|
10
|
-
})
|
|
10
|
+
});
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
return applyCaseSensitiveComposition(firstObject, secondObject, {
|
|
14
14
|
keysComposition,
|
|
15
15
|
strict,
|
|
16
|
-
})
|
|
17
|
-
}
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
18
|
|
|
19
19
|
const applyCaseSensitiveComposition = (
|
|
20
20
|
firstObject,
|
|
@@ -22,7 +22,7 @@ const applyCaseSensitiveComposition = (
|
|
|
22
22
|
{ keysComposition, strict },
|
|
23
23
|
) => {
|
|
24
24
|
if (strict) {
|
|
25
|
-
const composed = {}
|
|
25
|
+
const composed = {};
|
|
26
26
|
Object.keys(keysComposition).forEach((key) => {
|
|
27
27
|
composed[key] = composeValueAtKey({
|
|
28
28
|
firstObject,
|
|
@@ -31,15 +31,15 @@ const applyCaseSensitiveComposition = (
|
|
|
31
31
|
key,
|
|
32
32
|
firstKey: keyExistsIn(key, firstObject) ? key : null,
|
|
33
33
|
secondKey: keyExistsIn(key, secondObject) ? key : null,
|
|
34
|
-
})
|
|
35
|
-
})
|
|
36
|
-
return composed
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
return composed;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const composed = {}
|
|
39
|
+
const composed = {};
|
|
40
40
|
Object.keys(firstObject).forEach((key) => {
|
|
41
|
-
composed[key] = firstObject[key]
|
|
42
|
-
})
|
|
41
|
+
composed[key] = firstObject[key];
|
|
42
|
+
});
|
|
43
43
|
Object.keys(secondObject).forEach((key) => {
|
|
44
44
|
composed[key] = composeValueAtKey({
|
|
45
45
|
firstObject,
|
|
@@ -48,10 +48,10 @@ const applyCaseSensitiveComposition = (
|
|
|
48
48
|
key,
|
|
49
49
|
firstKey: keyExistsIn(key, firstObject) ? key : null,
|
|
50
50
|
secondKey: keyExistsIn(key, secondObject) ? key : null,
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
return composed
|
|
54
|
-
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
return composed;
|
|
54
|
+
};
|
|
55
55
|
|
|
56
56
|
const applyCompositionForcingLowerCase = (
|
|
57
57
|
firstObject,
|
|
@@ -59,14 +59,14 @@ const applyCompositionForcingLowerCase = (
|
|
|
59
59
|
{ keysComposition, strict },
|
|
60
60
|
) => {
|
|
61
61
|
if (strict) {
|
|
62
|
-
const firstObjectKeyMapping = {}
|
|
62
|
+
const firstObjectKeyMapping = {};
|
|
63
63
|
Object.keys(firstObject).forEach((key) => {
|
|
64
|
-
firstObjectKeyMapping[key.toLowerCase()] = key
|
|
65
|
-
})
|
|
66
|
-
const secondObjectKeyMapping = {}
|
|
64
|
+
firstObjectKeyMapping[key.toLowerCase()] = key;
|
|
65
|
+
});
|
|
66
|
+
const secondObjectKeyMapping = {};
|
|
67
67
|
Object.keys(secondObject).forEach((key) => {
|
|
68
|
-
secondObjectKeyMapping[key.toLowerCase()] = key
|
|
69
|
-
})
|
|
68
|
+
secondObjectKeyMapping[key.toLowerCase()] = key;
|
|
69
|
+
});
|
|
70
70
|
Object.keys(keysComposition).forEach((key) => {
|
|
71
71
|
composed[key] = composeValueAtKey({
|
|
72
72
|
firstObject,
|
|
@@ -75,16 +75,16 @@ const applyCompositionForcingLowerCase = (
|
|
|
75
75
|
key,
|
|
76
76
|
firstKey: firstObjectKeyMapping[key] || null,
|
|
77
77
|
secondKey: secondObjectKeyMapping[key] || null,
|
|
78
|
-
})
|
|
79
|
-
})
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
const composed = {}
|
|
82
|
+
const composed = {};
|
|
83
83
|
Object.keys(firstObject).forEach((key) => {
|
|
84
|
-
composed[key.toLowerCase()] = firstObject[key]
|
|
85
|
-
})
|
|
84
|
+
composed[key.toLowerCase()] = firstObject[key];
|
|
85
|
+
});
|
|
86
86
|
Object.keys(secondObject).forEach((key) => {
|
|
87
|
-
const keyLowercased = key.toLowerCase()
|
|
87
|
+
const keyLowercased = key.toLowerCase();
|
|
88
88
|
|
|
89
89
|
composed[key.toLowerCase()] = composeValueAtKey({
|
|
90
90
|
firstObject,
|
|
@@ -101,10 +101,10 @@ const applyCompositionForcingLowerCase = (
|
|
|
101
101
|
: keyExistsIn(key, secondObject)
|
|
102
102
|
? key
|
|
103
103
|
: null,
|
|
104
|
-
})
|
|
105
|
-
})
|
|
106
|
-
return composed
|
|
107
|
-
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
return composed;
|
|
107
|
+
};
|
|
108
108
|
|
|
109
109
|
const composeValueAtKey = ({
|
|
110
110
|
firstObject,
|
|
@@ -115,22 +115,24 @@ const composeValueAtKey = ({
|
|
|
115
115
|
keysComposition,
|
|
116
116
|
}) => {
|
|
117
117
|
if (!firstKey) {
|
|
118
|
-
return secondObject[secondKey]
|
|
118
|
+
return secondObject[secondKey];
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
if (!secondKey) {
|
|
122
|
-
return firstObject[firstKey]
|
|
122
|
+
return firstObject[firstKey];
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
const keyForCustomComposition = keyExistsIn(key, keysComposition)
|
|
125
|
+
const keyForCustomComposition = keyExistsIn(key, keysComposition)
|
|
126
|
+
? key
|
|
127
|
+
: null;
|
|
126
128
|
if (!keyForCustomComposition) {
|
|
127
|
-
return secondObject[secondKey]
|
|
129
|
+
return secondObject[secondKey];
|
|
128
130
|
}
|
|
129
131
|
|
|
130
|
-
const composeTwoValues = keysComposition[keyForCustomComposition]
|
|
131
|
-
return composeTwoValues(firstObject[firstKey], secondObject[secondKey])
|
|
132
|
-
}
|
|
132
|
+
const composeTwoValues = keysComposition[keyForCustomComposition];
|
|
133
|
+
return composeTwoValues(firstObject[firstKey], secondObject[secondKey]);
|
|
134
|
+
};
|
|
133
135
|
|
|
134
136
|
const keyExistsIn = (key, object) => {
|
|
135
|
-
return Object.prototype.hasOwnProperty.call(object, key)
|
|
136
|
-
}
|
|
137
|
+
return Object.prototype.hasOwnProperty.call(object, key);
|
|
138
|
+
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { composeTwoObjects } from "./object_composition.js"
|
|
2
|
-
import { composeTwoHeaders } from "./headers_composition.js"
|
|
1
|
+
import { composeTwoObjects } from "./object_composition.js";
|
|
2
|
+
import { composeTwoHeaders } from "./headers_composition.js";
|
|
3
3
|
|
|
4
4
|
export const composeTwoResponses = (firstResponse, secondResponse) => {
|
|
5
5
|
return composeTwoObjects(firstResponse, secondResponse, {
|
|
6
6
|
keysComposition: RESPONSE_KEYS_COMPOSITION,
|
|
7
7
|
strict: true,
|
|
8
|
-
})
|
|
9
|
-
}
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
10
|
|
|
11
11
|
const RESPONSE_KEYS_COMPOSITION = {
|
|
12
12
|
status: (prevStatus, status) => status,
|
|
@@ -16,6 +16,6 @@ const RESPONSE_KEYS_COMPOSITION = {
|
|
|
16
16
|
body: (prevBody, body) => body,
|
|
17
17
|
bodyEncoding: (prevEncoding, encoding) => encoding,
|
|
18
18
|
timing: (prevTiming, timing) => {
|
|
19
|
-
return { ...prevTiming, ...timing }
|
|
19
|
+
return { ...prevTiming, ...timing };
|
|
20
20
|
},
|
|
21
|
-
}
|
|
21
|
+
};
|
|
@@ -4,9 +4,9 @@ https://stackoverflow.com/a/42019773/2634179
|
|
|
4
4
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import http from "node:http"
|
|
8
|
-
import net from "node:net"
|
|
9
|
-
import { listenEvent } from "./listenEvent.js"
|
|
7
|
+
import http from "node:http";
|
|
8
|
+
import net from "node:net";
|
|
9
|
+
import { listenEvent } from "./listenEvent.js";
|
|
10
10
|
|
|
11
11
|
export const createPolyglotServer = async ({
|
|
12
12
|
http2 = false,
|
|
@@ -14,27 +14,27 @@ export const createPolyglotServer = async ({
|
|
|
14
14
|
certificate,
|
|
15
15
|
privateKey,
|
|
16
16
|
}) => {
|
|
17
|
-
const httpServer = http.createServer()
|
|
17
|
+
const httpServer = http.createServer();
|
|
18
18
|
const tlsServer = await createSecureServer({
|
|
19
19
|
certificate,
|
|
20
20
|
privateKey,
|
|
21
21
|
http2,
|
|
22
22
|
http1Allowed,
|
|
23
|
-
})
|
|
23
|
+
});
|
|
24
24
|
const netServer = net.createServer({
|
|
25
25
|
allowHalfOpen: false,
|
|
26
|
-
})
|
|
26
|
+
});
|
|
27
27
|
|
|
28
28
|
listenEvent(netServer, "connection", (socket) => {
|
|
29
29
|
detectSocketProtocol(socket, (protocol) => {
|
|
30
30
|
if (protocol === "http") {
|
|
31
|
-
httpServer.emit("connection", socket)
|
|
32
|
-
return
|
|
31
|
+
httpServer.emit("connection", socket);
|
|
32
|
+
return;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (protocol === "tls") {
|
|
36
|
-
tlsServer.emit("connection", socket)
|
|
37
|
-
return
|
|
36
|
+
tlsServer.emit("connection", socket);
|
|
37
|
+
return;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const response = [
|
|
@@ -42,23 +42,23 @@ export const createPolyglotServer = async ({
|
|
|
42
42
|
`Content-Length: 0`,
|
|
43
43
|
"",
|
|
44
44
|
"",
|
|
45
|
-
].join("\r\n")
|
|
46
|
-
socket.write(response)
|
|
47
|
-
socket.end()
|
|
48
|
-
socket.destroy()
|
|
45
|
+
].join("\r\n");
|
|
46
|
+
socket.write(response);
|
|
47
|
+
socket.end();
|
|
48
|
+
socket.destroy();
|
|
49
49
|
netServer.emit(
|
|
50
50
|
"clientError",
|
|
51
51
|
new Error("protocol error, Neither http, nor tls"),
|
|
52
52
|
socket,
|
|
53
|
-
)
|
|
54
|
-
})
|
|
55
|
-
})
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
56
|
|
|
57
|
-
netServer._httpServer = httpServer
|
|
58
|
-
netServer._tlsServer = tlsServer
|
|
57
|
+
netServer._httpServer = httpServer;
|
|
58
|
+
netServer._tlsServer = tlsServer;
|
|
59
59
|
|
|
60
|
-
return netServer
|
|
61
|
-
}
|
|
60
|
+
return netServer;
|
|
61
|
+
};
|
|
62
62
|
|
|
63
63
|
// The async part is just to lazyly import "http2" or "https"
|
|
64
64
|
// so that these module are parsed only if used.
|
|
@@ -70,47 +70,47 @@ const createSecureServer = async ({
|
|
|
70
70
|
http1Allowed,
|
|
71
71
|
}) => {
|
|
72
72
|
if (http2) {
|
|
73
|
-
const { createSecureServer } = await import("node:http2")
|
|
73
|
+
const { createSecureServer } = await import("node:http2");
|
|
74
74
|
return createSecureServer({
|
|
75
75
|
cert: certificate,
|
|
76
76
|
key: privateKey,
|
|
77
77
|
allowHTTP1: http1Allowed,
|
|
78
|
-
})
|
|
78
|
+
});
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const { createServer } = await import("node:https")
|
|
81
|
+
const { createServer } = await import("node:https");
|
|
82
82
|
return createServer({
|
|
83
83
|
cert: certificate,
|
|
84
84
|
key: privateKey,
|
|
85
|
-
})
|
|
86
|
-
}
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
87
|
|
|
88
88
|
const detectSocketProtocol = (socket, protocolDetectedCallback) => {
|
|
89
|
-
let removeOnceReadableListener = () => {}
|
|
89
|
+
let removeOnceReadableListener = () => {};
|
|
90
90
|
|
|
91
91
|
const tryToRead = () => {
|
|
92
|
-
const buffer = socket.read(1)
|
|
92
|
+
const buffer = socket.read(1);
|
|
93
93
|
if (buffer === null) {
|
|
94
|
-
removeOnceReadableListener = socket.once("readable", tryToRead)
|
|
95
|
-
return
|
|
94
|
+
removeOnceReadableListener = socket.once("readable", tryToRead);
|
|
95
|
+
return;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
const firstByte = buffer[0]
|
|
99
|
-
socket.unshift(buffer)
|
|
98
|
+
const firstByte = buffer[0];
|
|
99
|
+
socket.unshift(buffer);
|
|
100
100
|
if (firstByte === 22) {
|
|
101
|
-
protocolDetectedCallback("tls")
|
|
102
|
-
return
|
|
101
|
+
protocolDetectedCallback("tls");
|
|
102
|
+
return;
|
|
103
103
|
}
|
|
104
104
|
if (firstByte > 32 && firstByte < 127) {
|
|
105
|
-
protocolDetectedCallback("http")
|
|
106
|
-
return
|
|
105
|
+
protocolDetectedCallback("http");
|
|
106
|
+
return;
|
|
107
107
|
}
|
|
108
|
-
protocolDetectedCallback(null)
|
|
109
|
-
}
|
|
108
|
+
protocolDetectedCallback(null);
|
|
109
|
+
};
|
|
110
110
|
|
|
111
|
-
tryToRead()
|
|
111
|
+
tryToRead();
|
|
112
112
|
|
|
113
113
|
return () => {
|
|
114
|
-
removeOnceReadableListener()
|
|
115
|
-
}
|
|
116
|
-
}
|
|
114
|
+
removeOnceReadableListener();
|
|
115
|
+
};
|
|
116
|
+
};
|
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
import { networkInterfaces } from "node:os"
|
|
1
|
+
import { networkInterfaces } from "node:os";
|
|
2
2
|
|
|
3
3
|
export const createIpGetters = () => {
|
|
4
|
-
const networkAddresses = []
|
|
5
|
-
const networkInterfaceMap = networkInterfaces()
|
|
4
|
+
const networkAddresses = [];
|
|
5
|
+
const networkInterfaceMap = networkInterfaces();
|
|
6
6
|
for (const key of Object.keys(networkInterfaceMap)) {
|
|
7
7
|
for (const networkAddress of networkInterfaceMap[key]) {
|
|
8
|
-
networkAddresses.push(networkAddress)
|
|
8
|
+
networkAddresses.push(networkAddress);
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
return {
|
|
12
12
|
getFirstInternalIp: ({ preferIpv6 }) => {
|
|
13
|
-
const isPref = preferIpv6 ? isIpV6 : isIpV4
|
|
14
|
-
let firstInternalIp
|
|
13
|
+
const isPref = preferIpv6 ? isIpV6 : isIpV4;
|
|
14
|
+
let firstInternalIp;
|
|
15
15
|
for (const networkAddress of networkAddresses) {
|
|
16
16
|
if (networkAddress.internal) {
|
|
17
|
-
firstInternalIp = networkAddress.address
|
|
17
|
+
firstInternalIp = networkAddress.address;
|
|
18
18
|
if (isPref(networkAddress)) {
|
|
19
|
-
break
|
|
19
|
+
break;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
return firstInternalIp
|
|
23
|
+
return firstInternalIp;
|
|
24
24
|
},
|
|
25
25
|
getFirstExternalIp: ({ preferIpv6 }) => {
|
|
26
|
-
const isPref = preferIpv6 ? isIpV6 : isIpV4
|
|
27
|
-
let firstExternalIp
|
|
26
|
+
const isPref = preferIpv6 ? isIpV6 : isIpV4;
|
|
27
|
+
let firstExternalIp;
|
|
28
28
|
for (const networkAddress of networkAddresses) {
|
|
29
29
|
if (!networkAddress.internal) {
|
|
30
|
-
firstExternalIp = networkAddress.address
|
|
30
|
+
firstExternalIp = networkAddress.address;
|
|
31
31
|
if (isPref(networkAddress)) {
|
|
32
|
-
break
|
|
32
|
+
break;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
return firstExternalIp
|
|
36
|
+
return firstExternalIp;
|
|
37
37
|
},
|
|
38
|
-
}
|
|
39
|
-
}
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
40
|
|
|
41
41
|
const isIpV4 = (networkAddress) => {
|
|
42
42
|
// node 18.5
|
|
43
43
|
if (typeof networkAddress.family === "number") {
|
|
44
|
-
return networkAddress.family === 4
|
|
44
|
+
return networkAddress.family === 4;
|
|
45
45
|
}
|
|
46
|
-
return networkAddress.family === "IPv4"
|
|
47
|
-
}
|
|
46
|
+
return networkAddress.family === "IPv4";
|
|
47
|
+
};
|
|
48
48
|
|
|
49
|
-
const isIpV6 = (networkAddress) => !isIpV4(networkAddress)
|
|
49
|
+
const isIpV6 = (networkAddress) => !isIpV4(networkAddress);
|
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
import { listenEvent } from "./listenEvent.js"
|
|
1
|
+
import { listenEvent } from "./listenEvent.js";
|
|
2
2
|
|
|
3
3
|
export const trackServerPendingConnections = (nodeServer, { http2 }) => {
|
|
4
4
|
if (http2) {
|
|
5
5
|
// see http2.js: we rely on https://nodejs.org/api/http2.html#http2_compatibility_api
|
|
6
|
-
return trackHttp1ServerPendingConnections(nodeServer)
|
|
6
|
+
return trackHttp1ServerPendingConnections(nodeServer);
|
|
7
7
|
}
|
|
8
|
-
return trackHttp1ServerPendingConnections(nodeServer)
|
|
9
|
-
}
|
|
8
|
+
return trackHttp1ServerPendingConnections(nodeServer);
|
|
9
|
+
};
|
|
10
10
|
|
|
11
11
|
// const trackHttp2ServerPendingSessions = () => {}
|
|
12
12
|
|
|
13
13
|
const trackHttp1ServerPendingConnections = (nodeServer) => {
|
|
14
|
-
const pendingConnections = new Set()
|
|
14
|
+
const pendingConnections = new Set();
|
|
15
15
|
|
|
16
16
|
const removeConnectionListener = listenEvent(
|
|
17
17
|
nodeServer,
|
|
18
18
|
"connection",
|
|
19
19
|
(connection) => {
|
|
20
|
-
pendingConnections.add(connection)
|
|
20
|
+
pendingConnections.add(connection);
|
|
21
21
|
listenEvent(
|
|
22
22
|
connection,
|
|
23
23
|
"close",
|
|
24
24
|
() => {
|
|
25
|
-
pendingConnections.delete(connection)
|
|
25
|
+
pendingConnections.delete(connection);
|
|
26
26
|
},
|
|
27
27
|
{ once: true },
|
|
28
|
-
)
|
|
28
|
+
);
|
|
29
29
|
},
|
|
30
|
-
)
|
|
30
|
+
);
|
|
31
31
|
|
|
32
32
|
const stop = async (reason) => {
|
|
33
|
-
removeConnectionListener()
|
|
34
|
-
const pendingConnectionsArray = Array.from(pendingConnections)
|
|
35
|
-
pendingConnections.clear()
|
|
33
|
+
removeConnectionListener();
|
|
34
|
+
const pendingConnectionsArray = Array.from(pendingConnections);
|
|
35
|
+
pendingConnections.clear();
|
|
36
36
|
|
|
37
37
|
await Promise.all(
|
|
38
38
|
pendingConnectionsArray.map(async (pendingConnection) => {
|
|
39
|
-
await destroyConnection(pendingConnection, reason)
|
|
39
|
+
await destroyConnection(pendingConnection, reason);
|
|
40
40
|
}),
|
|
41
|
-
)
|
|
42
|
-
}
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
43
|
|
|
44
|
-
return { stop }
|
|
45
|
-
}
|
|
44
|
+
return { stop };
|
|
45
|
+
};
|
|
46
46
|
|
|
47
47
|
const destroyConnection = (connection, reason) => {
|
|
48
48
|
return new Promise((resolve, reject) => {
|
|
49
49
|
connection.destroy(reason, (error) => {
|
|
50
50
|
if (error) {
|
|
51
51
|
if (error === reason || error.code === "ENOTCONN") {
|
|
52
|
-
resolve()
|
|
52
|
+
resolve();
|
|
53
53
|
} else {
|
|
54
|
-
reject(error)
|
|
54
|
+
reject(error);
|
|
55
55
|
}
|
|
56
56
|
} else {
|
|
57
|
-
resolve()
|
|
57
|
+
resolve();
|
|
58
58
|
}
|
|
59
|
-
})
|
|
60
|
-
})
|
|
61
|
-
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
62
|
|
|
63
63
|
// export const trackServerPendingStreams = (nodeServer) => {
|
|
64
64
|
// const pendingClients = new Set()
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import { listenRequest } from "./listenRequest.js"
|
|
1
|
+
import { listenRequest } from "./listenRequest.js";
|
|
2
2
|
|
|
3
3
|
export const trackServerPendingRequests = (nodeServer, { http2 }) => {
|
|
4
4
|
if (http2) {
|
|
5
5
|
// see http2.js: we rely on https://nodejs.org/api/http2.html#http2_compatibility_api
|
|
6
|
-
return trackHttp1ServerPendingRequests(nodeServer)
|
|
6
|
+
return trackHttp1ServerPendingRequests(nodeServer);
|
|
7
7
|
}
|
|
8
|
-
return trackHttp1ServerPendingRequests(nodeServer)
|
|
9
|
-
}
|
|
8
|
+
return trackHttp1ServerPendingRequests(nodeServer);
|
|
9
|
+
};
|
|
10
10
|
|
|
11
11
|
const trackHttp1ServerPendingRequests = (nodeServer) => {
|
|
12
|
-
const pendingClients = new Set()
|
|
12
|
+
const pendingClients = new Set();
|
|
13
13
|
|
|
14
14
|
const removeRequestListener = listenRequest(
|
|
15
15
|
nodeServer,
|
|
16
16
|
(nodeRequest, nodeResponse) => {
|
|
17
|
-
const client = { nodeRequest, nodeResponse }
|
|
18
|
-
pendingClients.add(client)
|
|
17
|
+
const client = { nodeRequest, nodeResponse };
|
|
18
|
+
pendingClients.add(client);
|
|
19
19
|
nodeResponse.once("close", () => {
|
|
20
|
-
pendingClients.delete(client)
|
|
21
|
-
})
|
|
20
|
+
pendingClients.delete(client);
|
|
21
|
+
});
|
|
22
22
|
},
|
|
23
|
-
)
|
|
23
|
+
);
|
|
24
24
|
|
|
25
25
|
const stop = async ({ status, reason }) => {
|
|
26
|
-
removeRequestListener()
|
|
27
|
-
const pendingClientsArray = Array.from(pendingClients)
|
|
28
|
-
pendingClients.clear()
|
|
26
|
+
removeRequestListener();
|
|
27
|
+
const pendingClientsArray = Array.from(pendingClients);
|
|
28
|
+
pendingClients.clear();
|
|
29
29
|
await Promise.all(
|
|
30
30
|
pendingClientsArray.map(({ nodeResponse }) => {
|
|
31
31
|
if (nodeResponse.headersSent === false) {
|
|
32
|
-
nodeResponse.writeHead(status, String(reason))
|
|
32
|
+
nodeResponse.writeHead(status, String(reason));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// http2
|
|
36
36
|
if (nodeResponse.close) {
|
|
37
37
|
return new Promise((resolve, reject) => {
|
|
38
38
|
if (nodeResponse.closed) {
|
|
39
|
-
resolve()
|
|
39
|
+
resolve();
|
|
40
40
|
} else {
|
|
41
41
|
nodeResponse.close((error) => {
|
|
42
42
|
if (error) {
|
|
43
|
-
reject(error)
|
|
43
|
+
reject(error);
|
|
44
44
|
} else {
|
|
45
|
-
resolve()
|
|
45
|
+
resolve();
|
|
46
46
|
}
|
|
47
|
-
})
|
|
47
|
+
});
|
|
48
48
|
}
|
|
49
|
-
})
|
|
49
|
+
});
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// http
|
|
53
53
|
return new Promise((resolve) => {
|
|
54
54
|
if (nodeResponse.destroyed) {
|
|
55
|
-
resolve()
|
|
55
|
+
resolve();
|
|
56
56
|
} else {
|
|
57
57
|
nodeResponse.once("close", () => {
|
|
58
|
-
resolve()
|
|
59
|
-
})
|
|
60
|
-
nodeResponse.destroy()
|
|
58
|
+
resolve();
|
|
59
|
+
});
|
|
60
|
+
nodeResponse.destroy();
|
|
61
61
|
}
|
|
62
|
-
})
|
|
62
|
+
});
|
|
63
63
|
}),
|
|
64
|
-
)
|
|
65
|
-
}
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
66
|
|
|
67
|
-
return { stop }
|
|
68
|
-
}
|
|
67
|
+
return { stop };
|
|
68
|
+
};
|
package/src/main.js
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Ideally this file should be kept simple to help discovering codebase progressively.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export { startServer } from "./start_server.js"
|
|
8
|
-
export { setupRoutes } from "./service_composition/routing.js"
|
|
9
|
-
export { readRequestBody } from "./readRequestBody.js"
|
|
10
|
-
export { fetchFileSystem } from "./fetch_filesystem.js"
|
|
7
|
+
export { startServer } from "./start_server.js";
|
|
8
|
+
export { setupRoutes } from "./service_composition/routing.js";
|
|
9
|
+
export { readRequestBody } from "./readRequestBody.js";
|
|
10
|
+
export { fetchFileSystem } from "./fetch_filesystem.js";
|
|
11
11
|
export {
|
|
12
12
|
STOP_REASON_INTERNAL_ERROR,
|
|
13
13
|
STOP_REASON_PROCESS_SIGHUP,
|
|
@@ -16,31 +16,31 @@ export {
|
|
|
16
16
|
STOP_REASON_PROCESS_BEFORE_EXIT,
|
|
17
17
|
STOP_REASON_PROCESS_EXIT,
|
|
18
18
|
STOP_REASON_NOT_SPECIFIED,
|
|
19
|
-
} from "./stopReasons.js"
|
|
20
|
-
export { jsenvServiceErrorHandler } from "./services/error_handler/jsenv_service_error_handler.js"
|
|
19
|
+
} from "./stopReasons.js";
|
|
20
|
+
export { jsenvServiceErrorHandler } from "./services/error_handler/jsenv_service_error_handler.js";
|
|
21
21
|
|
|
22
22
|
// CORS
|
|
23
23
|
export {
|
|
24
24
|
jsenvServiceCORS,
|
|
25
25
|
jsenvAccessControlAllowedHeaders,
|
|
26
26
|
jsenvAccessControlAllowedMethods,
|
|
27
|
-
} from "./services/cors/jsenv_service_cors.js"
|
|
27
|
+
} from "./services/cors/jsenv_service_cors.js";
|
|
28
28
|
|
|
29
29
|
// server timing
|
|
30
|
-
export { timeFunction, timeStart } from "./server_timing/timing_measure.js"
|
|
30
|
+
export { timeFunction, timeStart } from "./server_timing/timing_measure.js";
|
|
31
31
|
|
|
32
32
|
// SSE
|
|
33
|
-
export { createSSERoom } from "./sse/sse_room.js"
|
|
33
|
+
export { createSSERoom } from "./sse/sse_room.js";
|
|
34
34
|
|
|
35
35
|
// content-negotiation
|
|
36
|
-
export { pickContentType } from "./content_negotiation/pick_content_type.js"
|
|
37
|
-
export { pickContentEncoding } from "./content_negotiation/pick_content_encoding.js"
|
|
38
|
-
export { pickContentLanguage } from "./content_negotiation/pick_content_language.js"
|
|
39
|
-
export { jsenvServiceResponseAcceptanceCheck } from "./services/response_acceptance_check/jsenv_service_response_acceptance_check.js"
|
|
36
|
+
export { pickContentType } from "./content_negotiation/pick_content_type.js";
|
|
37
|
+
export { pickContentEncoding } from "./content_negotiation/pick_content_encoding.js";
|
|
38
|
+
export { pickContentLanguage } from "./content_negotiation/pick_content_language.js";
|
|
39
|
+
export { jsenvServiceResponseAcceptanceCheck } from "./services/response_acceptance_check/jsenv_service_response_acceptance_check.js";
|
|
40
40
|
|
|
41
41
|
// others
|
|
42
|
-
export { serveDirectory } from "./serve_directory.js"
|
|
43
|
-
export { fromFetchResponse } from "./from_fetch_response.js"
|
|
44
|
-
export { composeTwoResponses } from "./internal/response_composition.js"
|
|
45
|
-
export { jsenvServiceRequestAliases } from "./services/request_aliases/jsenv_service_request_aliases.js"
|
|
46
|
-
export { findFreePort } from "./internal/listen.js"
|
|
42
|
+
export { serveDirectory } from "./serve_directory.js";
|
|
43
|
+
export { fromFetchResponse } from "./from_fetch_response.js";
|
|
44
|
+
export { composeTwoResponses } from "./internal/response_composition.js";
|
|
45
|
+
export { jsenvServiceRequestAliases } from "./services/request_aliases/jsenv_service_request_aliases.js";
|
|
46
|
+
export { findFreePort } from "./internal/listen.js";
|