@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
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import http from "node:http"
|
|
2
|
-
import { Http2ServerResponse } from "node:http2"
|
|
3
|
-
import { raceCallbacks } from "@jsenv/abort"
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import { Http2ServerResponse } from "node:http2";
|
|
3
|
+
import { raceCallbacks } from "@jsenv/abort";
|
|
4
4
|
|
|
5
|
-
import { normalizeBodyMethods } from "./body.js"
|
|
5
|
+
import { normalizeBodyMethods } from "./body.js";
|
|
6
6
|
|
|
7
7
|
export const writeNodeResponse = async (
|
|
8
8
|
responseStream,
|
|
9
9
|
{ status, statusText, headers, body, bodyEncoding },
|
|
10
10
|
{ signal, ignoreBody, onAbort, onError, onHeadersSent, onEnd } = {},
|
|
11
11
|
) => {
|
|
12
|
-
body = await body
|
|
13
|
-
const bodyMethods = normalizeBodyMethods(body)
|
|
12
|
+
body = await body;
|
|
13
|
+
const bodyMethods = normalizeBodyMethods(body);
|
|
14
14
|
|
|
15
15
|
if (signal.aborted) {
|
|
16
|
-
bodyMethods.destroy()
|
|
17
|
-
responseStream.destroy()
|
|
18
|
-
onAbort()
|
|
19
|
-
return
|
|
16
|
+
bodyMethods.destroy();
|
|
17
|
+
responseStream.destroy();
|
|
18
|
+
onAbort();
|
|
19
|
+
return;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
writeHead(responseStream, {
|
|
@@ -24,31 +24,31 @@ export const writeNodeResponse = async (
|
|
|
24
24
|
statusText,
|
|
25
25
|
headers,
|
|
26
26
|
onHeadersSent,
|
|
27
|
-
})
|
|
27
|
+
});
|
|
28
28
|
|
|
29
29
|
if (!body) {
|
|
30
|
-
onEnd()
|
|
31
|
-
responseStream.end()
|
|
32
|
-
return
|
|
30
|
+
onEnd();
|
|
31
|
+
responseStream.end();
|
|
32
|
+
return;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (ignoreBody) {
|
|
36
|
-
onEnd()
|
|
37
|
-
bodyMethods.destroy()
|
|
38
|
-
responseStream.end()
|
|
39
|
-
return
|
|
36
|
+
onEnd();
|
|
37
|
+
bodyMethods.destroy();
|
|
38
|
+
responseStream.end();
|
|
39
|
+
return;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
if (bodyEncoding) {
|
|
43
|
-
responseStream.setEncoding(bodyEncoding)
|
|
43
|
+
responseStream.setEncoding(bodyEncoding);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
await new Promise((resolve) => {
|
|
47
|
-
const observable = bodyMethods.asObservable()
|
|
47
|
+
const observable = bodyMethods.asObservable();
|
|
48
48
|
const subscription = observable.subscribe({
|
|
49
49
|
next: (data) => {
|
|
50
50
|
try {
|
|
51
|
-
responseStream.write(data)
|
|
51
|
+
responseStream.write(data);
|
|
52
52
|
} catch (e) {
|
|
53
53
|
// Something inside Node.js sometimes puts stream
|
|
54
54
|
// in a state where .write() throw despites nodeResponse.destroyed
|
|
@@ -59,59 +59,59 @@ export const writeNodeResponse = async (
|
|
|
59
59
|
// For the record it's "hard" to reproduce but can be by running
|
|
60
60
|
// a lot of tests against a browser in the context of @jsenv/core testing
|
|
61
61
|
if (e.code === "ERR_HTTP2_INVALID_STREAM") {
|
|
62
|
-
return
|
|
62
|
+
return;
|
|
63
63
|
}
|
|
64
|
-
responseStream.emit("error", e)
|
|
64
|
+
responseStream.emit("error", e);
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
error: (value) => {
|
|
68
|
-
responseStream.emit("error", value)
|
|
68
|
+
responseStream.emit("error", value);
|
|
69
69
|
},
|
|
70
70
|
complete: () => {
|
|
71
|
-
responseStream.end()
|
|
71
|
+
responseStream.end();
|
|
72
72
|
},
|
|
73
|
-
})
|
|
73
|
+
});
|
|
74
74
|
|
|
75
75
|
raceCallbacks(
|
|
76
76
|
{
|
|
77
77
|
abort: (cb) => {
|
|
78
|
-
signal.addEventListener("abort", cb)
|
|
78
|
+
signal.addEventListener("abort", cb);
|
|
79
79
|
return () => {
|
|
80
|
-
signal.removeEventListener("abort", cb)
|
|
81
|
-
}
|
|
80
|
+
signal.removeEventListener("abort", cb);
|
|
81
|
+
};
|
|
82
82
|
},
|
|
83
83
|
error: (cb) => {
|
|
84
|
-
responseStream.on("error", cb)
|
|
84
|
+
responseStream.on("error", cb);
|
|
85
85
|
return () => {
|
|
86
|
-
responseStream.removeListener("error", cb)
|
|
87
|
-
}
|
|
86
|
+
responseStream.removeListener("error", cb);
|
|
87
|
+
};
|
|
88
88
|
},
|
|
89
89
|
close: (cb) => {
|
|
90
|
-
responseStream.on("close", cb)
|
|
90
|
+
responseStream.on("close", cb);
|
|
91
91
|
return () => {
|
|
92
|
-
responseStream.removeListener("close", cb)
|
|
93
|
-
}
|
|
92
|
+
responseStream.removeListener("close", cb);
|
|
93
|
+
};
|
|
94
94
|
},
|
|
95
95
|
finish: (cb) => {
|
|
96
|
-
responseStream.on("finish", cb)
|
|
96
|
+
responseStream.on("finish", cb);
|
|
97
97
|
return () => {
|
|
98
|
-
responseStream.removeListener("finish", cb)
|
|
99
|
-
}
|
|
98
|
+
responseStream.removeListener("finish", cb);
|
|
99
|
+
};
|
|
100
100
|
},
|
|
101
101
|
},
|
|
102
102
|
(winner) => {
|
|
103
103
|
const raceEffects = {
|
|
104
104
|
abort: () => {
|
|
105
|
-
subscription.unsubscribe()
|
|
106
|
-
responseStream.destroy()
|
|
107
|
-
onAbort()
|
|
108
|
-
resolve()
|
|
105
|
+
subscription.unsubscribe();
|
|
106
|
+
responseStream.destroy();
|
|
107
|
+
onAbort();
|
|
108
|
+
resolve();
|
|
109
109
|
},
|
|
110
110
|
error: (error) => {
|
|
111
|
-
subscription.unsubscribe()
|
|
112
|
-
responseStream.destroy()
|
|
113
|
-
onError(error)
|
|
114
|
-
resolve()
|
|
111
|
+
subscription.unsubscribe();
|
|
112
|
+
responseStream.destroy();
|
|
113
|
+
onError(error);
|
|
114
|
+
resolve();
|
|
115
115
|
},
|
|
116
116
|
close: () => {
|
|
117
117
|
// close body in case nodeResponse is prematurely closed
|
|
@@ -119,46 +119,46 @@ export const writeNodeResponse = async (
|
|
|
119
119
|
// it may happen in case of server sent event
|
|
120
120
|
// where body is kept open to write to client
|
|
121
121
|
// and the browser is reloaded or closed for instance
|
|
122
|
-
subscription.unsubscribe()
|
|
123
|
-
responseStream.destroy()
|
|
124
|
-
onAbort()
|
|
125
|
-
resolve()
|
|
122
|
+
subscription.unsubscribe();
|
|
123
|
+
responseStream.destroy();
|
|
124
|
+
onAbort();
|
|
125
|
+
resolve();
|
|
126
126
|
},
|
|
127
127
|
finish: () => {
|
|
128
|
-
onEnd()
|
|
129
|
-
resolve()
|
|
128
|
+
onEnd();
|
|
129
|
+
resolve();
|
|
130
130
|
},
|
|
131
|
-
}
|
|
132
|
-
raceEffects[winner.name](winner.data)
|
|
131
|
+
};
|
|
132
|
+
raceEffects[winner.name](winner.data);
|
|
133
133
|
},
|
|
134
|
-
)
|
|
135
|
-
})
|
|
136
|
-
}
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
137
|
|
|
138
138
|
const writeHead = (
|
|
139
139
|
responseStream,
|
|
140
140
|
{ status, statusText, headers, onHeadersSent },
|
|
141
141
|
) => {
|
|
142
142
|
const responseIsHttp2ServerResponse =
|
|
143
|
-
responseStream instanceof Http2ServerResponse
|
|
143
|
+
responseStream instanceof Http2ServerResponse;
|
|
144
144
|
const responseIsServerHttp2Stream =
|
|
145
|
-
responseStream.constructor.name === "ServerHttp2Stream"
|
|
145
|
+
responseStream.constructor.name === "ServerHttp2Stream";
|
|
146
146
|
let nodeHeaders = headersToNodeHeaders(headers, {
|
|
147
147
|
// https://github.com/nodejs/node/blob/79296dc2d02c0b9872bbfcbb89148ea036a546d0/lib/internal/http2/compat.js#L112
|
|
148
148
|
ignoreConnectionHeader:
|
|
149
149
|
responseIsHttp2ServerResponse || responseIsServerHttp2Stream,
|
|
150
|
-
})
|
|
150
|
+
});
|
|
151
151
|
if (statusText === undefined) {
|
|
152
|
-
statusText = statusTextFromStatus(status)
|
|
152
|
+
statusText = statusTextFromStatus(status);
|
|
153
153
|
}
|
|
154
154
|
if (responseIsServerHttp2Stream) {
|
|
155
155
|
nodeHeaders = {
|
|
156
156
|
...nodeHeaders,
|
|
157
157
|
":status": status,
|
|
158
|
-
}
|
|
159
|
-
responseStream.respond(nodeHeaders)
|
|
160
|
-
onHeadersSent({ nodeHeaders, status, statusText })
|
|
161
|
-
return
|
|
158
|
+
};
|
|
159
|
+
responseStream.respond(nodeHeaders);
|
|
160
|
+
onHeadersSent({ nodeHeaders, status, statusText });
|
|
161
|
+
return;
|
|
162
162
|
}
|
|
163
163
|
// nodejs strange signature for writeHead force this
|
|
164
164
|
// https://nodejs.org/api/http.html#http_response_writehead_statuscode_statusmessage_headers
|
|
@@ -166,13 +166,13 @@ const writeHead = (
|
|
|
166
166
|
// https://github.com/nodejs/node/blob/79296dc2d02c0b9872bbfcbb89148ea036a546d0/lib/internal/http2/compat.js#L97
|
|
167
167
|
responseIsHttp2ServerResponse
|
|
168
168
|
) {
|
|
169
|
-
responseStream.writeHead(status, nodeHeaders)
|
|
170
|
-
onHeadersSent({ nodeHeaders, status, statusText })
|
|
171
|
-
return
|
|
169
|
+
responseStream.writeHead(status, nodeHeaders);
|
|
170
|
+
onHeadersSent({ nodeHeaders, status, statusText });
|
|
171
|
+
return;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
try {
|
|
175
|
-
responseStream.writeHead(status, statusText, nodeHeaders)
|
|
175
|
+
responseStream.writeHead(status, statusText, nodeHeaders);
|
|
176
176
|
} catch (e) {
|
|
177
177
|
if (
|
|
178
178
|
e.code === "ERR_INVALID_CHAR" &&
|
|
@@ -180,29 +180,29 @@ const writeHead = (
|
|
|
180
180
|
) {
|
|
181
181
|
throw new Error(`Invalid character in statusMessage
|
|
182
182
|
--- status message ---
|
|
183
|
-
${statusText}`)
|
|
183
|
+
${statusText}`);
|
|
184
184
|
}
|
|
185
|
-
throw e
|
|
185
|
+
throw e;
|
|
186
186
|
}
|
|
187
|
-
onHeadersSent({ nodeHeaders, status, statusText })
|
|
188
|
-
}
|
|
187
|
+
onHeadersSent({ nodeHeaders, status, statusText });
|
|
188
|
+
};
|
|
189
189
|
|
|
190
190
|
const statusTextFromStatus = (status) =>
|
|
191
|
-
http.STATUS_CODES[status] || "not specified"
|
|
191
|
+
http.STATUS_CODES[status] || "not specified";
|
|
192
192
|
|
|
193
193
|
const headersToNodeHeaders = (headers, { ignoreConnectionHeader }) => {
|
|
194
|
-
const nodeHeaders = {}
|
|
194
|
+
const nodeHeaders = {};
|
|
195
195
|
|
|
196
196
|
Object.keys(headers).forEach((name) => {
|
|
197
|
-
if (name === "connection" && ignoreConnectionHeader) return
|
|
198
|
-
const nodeHeaderName = name in mapping ? mapping[name] : name
|
|
199
|
-
nodeHeaders[nodeHeaderName] = headers[name]
|
|
200
|
-
})
|
|
197
|
+
if (name === "connection" && ignoreConnectionHeader) return;
|
|
198
|
+
const nodeHeaderName = name in mapping ? mapping[name] : name;
|
|
199
|
+
nodeHeaders[nodeHeaderName] = headers[name];
|
|
200
|
+
});
|
|
201
201
|
|
|
202
|
-
return nodeHeaders
|
|
203
|
-
}
|
|
202
|
+
return nodeHeaders;
|
|
203
|
+
};
|
|
204
204
|
|
|
205
205
|
const mapping = {
|
|
206
206
|
// "content-type": "Content-Type",
|
|
207
207
|
// "last-modified": "Last-Modified",
|
|
208
|
-
}
|
|
208
|
+
};
|
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
// https://github.com/Marak/colors.js/blob/b63ef88e521b42920a9e908848de340b31e68c9d/lib/styles.js#L29
|
|
2
2
|
|
|
3
|
-
const close = "\x1b[0m"
|
|
4
|
-
const red = "\x1b[31m"
|
|
5
|
-
const green = "\x1b[32m"
|
|
6
|
-
const yellow = "\x1b[33m"
|
|
3
|
+
const close = "\x1b[0m";
|
|
4
|
+
const red = "\x1b[31m";
|
|
5
|
+
const green = "\x1b[32m";
|
|
6
|
+
const yellow = "\x1b[33m";
|
|
7
7
|
// const blue = "\x1b[34m"
|
|
8
|
-
const magenta = "\x1b[35m"
|
|
9
|
-
const cyan = "\x1b[36m"
|
|
8
|
+
const magenta = "\x1b[35m";
|
|
9
|
+
const cyan = "\x1b[36m";
|
|
10
10
|
// const white = "\x1b[37m"
|
|
11
11
|
|
|
12
12
|
export const colorizeResponseStatus = (status) => {
|
|
13
|
-
const statusType = statusToType(status)
|
|
14
|
-
if (statusType === "information") return `${cyan}${status}${close}
|
|
15
|
-
if (statusType === "success") return `${green}${status}${close}
|
|
16
|
-
if (statusType === "redirection") return `${magenta}${status}${close}
|
|
17
|
-
if (statusType === "client_error") return `${yellow}${status}${close}
|
|
18
|
-
if (statusType === "server_error") return `${red}${status}${close}
|
|
19
|
-
return status
|
|
20
|
-
}
|
|
13
|
+
const statusType = statusToType(status);
|
|
14
|
+
if (statusType === "information") return `${cyan}${status}${close}`;
|
|
15
|
+
if (statusType === "success") return `${green}${status}${close}`;
|
|
16
|
+
if (statusType === "redirection") return `${magenta}${status}${close}`;
|
|
17
|
+
if (statusType === "client_error") return `${yellow}${status}${close}`;
|
|
18
|
+
if (statusType === "server_error") return `${red}${status}${close}`;
|
|
19
|
+
return status;
|
|
20
|
+
};
|
|
21
21
|
|
|
22
22
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
|
|
23
23
|
export const statusToType = (status) => {
|
|
24
|
-
if (statusIsInformation(status)) return "information"
|
|
25
|
-
if (statusIsSuccess(status)) return "success"
|
|
26
|
-
if (statusIsRedirection(status)) return "redirection"
|
|
27
|
-
if (statusIsClientError(status)) return "client_error"
|
|
28
|
-
if (statusIsServerError(status)) return "server_error"
|
|
29
|
-
return "unknown"
|
|
30
|
-
}
|
|
24
|
+
if (statusIsInformation(status)) return "information";
|
|
25
|
+
if (statusIsSuccess(status)) return "success";
|
|
26
|
+
if (statusIsRedirection(status)) return "redirection";
|
|
27
|
+
if (statusIsClientError(status)) return "client_error";
|
|
28
|
+
if (statusIsServerError(status)) return "server_error";
|
|
29
|
+
return "unknown";
|
|
30
|
+
};
|
|
31
31
|
|
|
32
|
-
const statusIsInformation = (status) => status >= 100 && status < 200
|
|
32
|
+
const statusIsInformation = (status) => status >= 100 && status < 200;
|
|
33
33
|
|
|
34
|
-
const statusIsSuccess = (status) => status >= 200 && status < 300
|
|
34
|
+
const statusIsSuccess = (status) => status >= 200 && status < 300;
|
|
35
35
|
|
|
36
|
-
const statusIsRedirection = (status) => status >= 300 && status < 400
|
|
36
|
+
const statusIsRedirection = (status) => status >= 300 && status < 400;
|
|
37
37
|
|
|
38
|
-
const statusIsClientError = (status) => status >= 400 && status < 500
|
|
38
|
+
const statusIsClientError = (status) => status >= 400 && status < 500;
|
|
39
39
|
|
|
40
|
-
const statusIsServerError = (status) => status >= 500 && status < 600
|
|
40
|
+
const statusIsServerError = (status) => status >= 500 && status < 600;
|
|
@@ -4,19 +4,19 @@ export const convertFileSystemErrorToResponseProperties = (error) => {
|
|
|
4
4
|
return {
|
|
5
5
|
status: 403,
|
|
6
6
|
statusText: `EACCES: No permission to read file at ${error.path}`,
|
|
7
|
-
}
|
|
7
|
+
};
|
|
8
8
|
}
|
|
9
9
|
if (isErrorWithCode(error, "EPERM")) {
|
|
10
10
|
return {
|
|
11
11
|
status: 403,
|
|
12
12
|
statusText: `EPERM: No permission to read file at ${error.path}`,
|
|
13
|
-
}
|
|
13
|
+
};
|
|
14
14
|
}
|
|
15
15
|
if (isErrorWithCode(error, "ENOENT")) {
|
|
16
16
|
return {
|
|
17
17
|
status: 404,
|
|
18
18
|
statusText: `ENOENT: File not found at ${error.path}`,
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
20
|
}
|
|
21
21
|
// file access may be temporarily blocked
|
|
22
22
|
// (by an antivirus scanning it because recently modified for instance)
|
|
@@ -27,7 +27,7 @@ export const convertFileSystemErrorToResponseProperties = (error) => {
|
|
|
27
27
|
headers: {
|
|
28
28
|
"retry-after": 0.01, // retry in 10ms
|
|
29
29
|
},
|
|
30
|
-
}
|
|
30
|
+
};
|
|
31
31
|
}
|
|
32
32
|
// emfile means there is too many files currently opened
|
|
33
33
|
if (isErrorWithCode(error, "EMFILE")) {
|
|
@@ -37,17 +37,17 @@ export const convertFileSystemErrorToResponseProperties = (error) => {
|
|
|
37
37
|
headers: {
|
|
38
38
|
"retry-after": 0.1, // retry in 100ms
|
|
39
39
|
},
|
|
40
|
-
}
|
|
40
|
+
};
|
|
41
41
|
}
|
|
42
42
|
if (isErrorWithCode(error, "EISDIR")) {
|
|
43
43
|
return {
|
|
44
44
|
status: 500,
|
|
45
45
|
statusText: `EISDIR: Unexpected directory operation at ${error.path}`,
|
|
46
|
-
}
|
|
46
|
+
};
|
|
47
47
|
}
|
|
48
|
-
return null
|
|
49
|
-
}
|
|
48
|
+
return null;
|
|
49
|
+
};
|
|
50
50
|
|
|
51
51
|
const isErrorWithCode = (error, code) => {
|
|
52
|
-
return typeof error === "object" && error.code === code
|
|
53
|
-
}
|
|
52
|
+
return typeof error === "object" && error.code === code;
|
|
53
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { lookup } from "node:dns"
|
|
1
|
+
import { lookup } from "node:dns";
|
|
2
2
|
|
|
3
3
|
export const applyDnsResolution = async (
|
|
4
4
|
hostname,
|
|
@@ -7,11 +7,11 @@ export const applyDnsResolution = async (
|
|
|
7
7
|
const dnsResolution = await new Promise((resolve, reject) => {
|
|
8
8
|
lookup(hostname, { verbatim }, (error, address, family) => {
|
|
9
9
|
if (error) {
|
|
10
|
-
reject(error)
|
|
10
|
+
reject(error);
|
|
11
11
|
} else {
|
|
12
|
-
resolve({ address, family })
|
|
12
|
+
resolve({ address, family });
|
|
13
13
|
}
|
|
14
|
-
})
|
|
15
|
-
})
|
|
16
|
-
return dnsResolution
|
|
17
|
-
}
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
return dnsResolution;
|
|
17
|
+
};
|
package/src/internal/etag.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { createHash } from "node:crypto"
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
2
|
|
|
3
|
-
const ETAG_FOR_EMPTY_CONTENT = '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"'
|
|
3
|
+
const ETAG_FOR_EMPTY_CONTENT = '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
|
|
4
4
|
|
|
5
5
|
export const bufferToEtag = (buffer) => {
|
|
6
6
|
if (!Buffer.isBuffer(buffer)) {
|
|
7
|
-
throw new TypeError(`buffer expected, got ${buffer}`)
|
|
7
|
+
throw new TypeError(`buffer expected, got ${buffer}`);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
if (buffer.length === 0) {
|
|
11
|
-
return ETAG_FOR_EMPTY_CONTENT
|
|
11
|
+
return ETAG_FOR_EMPTY_CONTENT;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const hash = createHash("sha1")
|
|
15
|
-
hash.update(buffer, "utf8")
|
|
14
|
+
const hash = createHash("sha1");
|
|
15
|
+
hash.update(buffer, "utf8");
|
|
16
16
|
|
|
17
|
-
const hashBase64String = hash.digest("base64")
|
|
18
|
-
const hashBase64StringSubset = hashBase64String.slice(0, 27)
|
|
19
|
-
const length = buffer.length
|
|
17
|
+
const hashBase64String = hash.digest("base64");
|
|
18
|
+
const hashBase64StringSubset = hashBase64String.slice(0, 27);
|
|
19
|
+
const length = buffer.length;
|
|
20
20
|
|
|
21
|
-
return `"${length.toString(16)}-${hashBase64StringSubset}"
|
|
22
|
-
}
|
|
21
|
+
return `"${length.toString(16)}-${hashBase64StringSubset}"`;
|
|
22
|
+
};
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import { pathToFileURL, fileURLToPath } from "node:url"
|
|
1
|
+
import { pathToFileURL, fileURLToPath } from "node:url";
|
|
2
2
|
|
|
3
3
|
export const isFileSystemPath = (value) => {
|
|
4
4
|
if (typeof value !== "string") {
|
|
5
5
|
throw new TypeError(
|
|
6
6
|
`isFileSystemPath first arg must be a string, got ${value}`,
|
|
7
|
-
)
|
|
7
|
+
);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
if (value[0] === "/") {
|
|
11
|
-
return true
|
|
11
|
+
return true;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
return startsWithWindowsDriveLetter(value)
|
|
15
|
-
}
|
|
14
|
+
return startsWithWindowsDriveLetter(value);
|
|
15
|
+
};
|
|
16
16
|
|
|
17
17
|
const startsWithWindowsDriveLetter = (string) => {
|
|
18
|
-
const firstChar = string[0]
|
|
19
|
-
if (!/[a-zA-Z]/.test(firstChar)) return false
|
|
18
|
+
const firstChar = string[0];
|
|
19
|
+
if (!/[a-zA-Z]/.test(firstChar)) return false;
|
|
20
20
|
|
|
21
|
-
const secondChar = string[1]
|
|
22
|
-
if (secondChar !== ":") return false
|
|
21
|
+
const secondChar = string[1];
|
|
22
|
+
if (secondChar !== ":") return false;
|
|
23
23
|
|
|
24
|
-
return true
|
|
25
|
-
}
|
|
24
|
+
return true;
|
|
25
|
+
};
|
|
26
26
|
|
|
27
27
|
export const fileSystemPathToUrl = (value) => {
|
|
28
28
|
if (!isFileSystemPath(value)) {
|
|
29
|
-
throw new Error(`received an invalid value for fileSystemPath: ${value}`)
|
|
29
|
+
throw new Error(`received an invalid value for fileSystemPath: ${value}`);
|
|
30
30
|
}
|
|
31
|
-
return String(pathToFileURL(value))
|
|
32
|
-
}
|
|
31
|
+
return String(pathToFileURL(value));
|
|
32
|
+
};
|
|
33
33
|
|
|
34
34
|
export const urlToFileSystemPath = (url) => {
|
|
35
|
-
let urlString = String(url)
|
|
35
|
+
let urlString = String(url);
|
|
36
36
|
if (urlString[urlString.length - 1] === "/") {
|
|
37
37
|
// remove trailing / so that nodejs path becomes predictable otherwise it logs
|
|
38
38
|
// the trailing slash on linux but does not on windows
|
|
39
|
-
urlString = urlString.slice(0, -1)
|
|
39
|
+
urlString = urlString.slice(0, -1);
|
|
40
40
|
}
|
|
41
|
-
const fileSystemPath = fileURLToPath(urlString)
|
|
42
|
-
return fileSystemPath
|
|
43
|
-
}
|
|
41
|
+
const fileSystemPath = fileURLToPath(urlString);
|
|
42
|
+
return fileSystemPath;
|
|
43
|
+
};
|
|
@@ -3,21 +3,21 @@ https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
|
|
3
3
|
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { normalizeHeaderName } from "./normalizeHeaderName.js"
|
|
7
|
-
import { normalizeHeaderValue } from "./normalizeHeaderValue.js"
|
|
6
|
+
import { normalizeHeaderName } from "./normalizeHeaderName.js";
|
|
7
|
+
import { normalizeHeaderValue } from "./normalizeHeaderValue.js";
|
|
8
8
|
|
|
9
9
|
export const headersFromObject = (headersObject) => {
|
|
10
|
-
const headers = {}
|
|
10
|
+
const headers = {};
|
|
11
11
|
|
|
12
12
|
Object.keys(headersObject).forEach((headerName) => {
|
|
13
13
|
if (headerName[0] === ":") {
|
|
14
14
|
// exclude http2 headers
|
|
15
|
-
return
|
|
15
|
+
return;
|
|
16
16
|
}
|
|
17
17
|
headers[normalizeHeaderName(headerName)] = normalizeHeaderValue(
|
|
18
18
|
headersObject[headerName],
|
|
19
|
-
)
|
|
20
|
-
})
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
21
|
|
|
22
|
-
return headers
|
|
23
|
-
}
|
|
22
|
+
return headers;
|
|
23
|
+
};
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { normalizeHeaderName } from "./normalizeHeaderName.js"
|
|
2
|
-
import { normalizeHeaderValue } from "./normalizeHeaderValue.js"
|
|
1
|
+
import { normalizeHeaderName } from "./normalizeHeaderName.js";
|
|
2
|
+
import { normalizeHeaderValue } from "./normalizeHeaderValue.js";
|
|
3
3
|
|
|
4
4
|
// https://gist.github.com/mmazer/5404301
|
|
5
5
|
export const headersFromString = (headerString) => {
|
|
6
|
-
const headers = {}
|
|
6
|
+
const headers = {};
|
|
7
7
|
|
|
8
8
|
if (headerString) {
|
|
9
|
-
const pairs = headerString.split("\r\n")
|
|
9
|
+
const pairs = headerString.split("\r\n");
|
|
10
10
|
pairs.forEach((pair) => {
|
|
11
|
-
const index = pair.indexOf(": ")
|
|
11
|
+
const index = pair.indexOf(": ");
|
|
12
12
|
if (index > 0) {
|
|
13
|
-
const key = pair.slice(0, index)
|
|
14
|
-
const value = pair.slice(index + 2)
|
|
15
|
-
headers[normalizeHeaderName(key)] = normalizeHeaderValue(value)
|
|
13
|
+
const key = pair.slice(0, index);
|
|
14
|
+
const value = pair.slice(index + 2);
|
|
15
|
+
headers[normalizeHeaderName(key)] = normalizeHeaderValue(value);
|
|
16
16
|
}
|
|
17
|
-
})
|
|
17
|
+
});
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
return headers
|
|
21
|
-
}
|
|
20
|
+
return headers;
|
|
21
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const headersToObject = (headers) => {
|
|
2
|
-
const headersObject = {}
|
|
2
|
+
const headersObject = {};
|
|
3
3
|
headers.forEach((value, name) => {
|
|
4
|
-
headersObject[name] = value
|
|
5
|
-
})
|
|
6
|
-
return headersObject
|
|
7
|
-
}
|
|
4
|
+
headersObject[name] = value;
|
|
5
|
+
});
|
|
6
|
+
return headersObject;
|
|
7
|
+
};
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
export const headersToString = (headers, { convertName = (name) => name }) => {
|
|
2
2
|
const headersString = headersToArray(headers).map(({ name, value }) => {
|
|
3
|
-
return `${convertName(name)}: ${value}
|
|
4
|
-
})
|
|
3
|
+
return `${convertName(name)}: ${value}`;
|
|
4
|
+
});
|
|
5
5
|
|
|
6
|
-
return headersString.join("\r\n")
|
|
7
|
-
}
|
|
6
|
+
return headersString.join("\r\n");
|
|
7
|
+
};
|
|
8
8
|
|
|
9
9
|
const headersToArray = (headers) => {
|
|
10
10
|
return Object.keys(headers).map((name) => {
|
|
11
11
|
return {
|
|
12
12
|
name,
|
|
13
13
|
value: headers[name],
|
|
14
|
-
}
|
|
15
|
-
})
|
|
16
|
-
}
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
};
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { composeTwoObjects } from "./object_composition.js"
|
|
1
|
+
import { composeTwoObjects } from "./object_composition.js";
|
|
2
2
|
|
|
3
3
|
export const composeTwoHeaders = (firstHeaders, secondHeaders) => {
|
|
4
4
|
return composeTwoObjects(firstHeaders, secondHeaders, {
|
|
5
5
|
keysComposition: HEADER_NAMES_COMPOSITION,
|
|
6
6
|
forceLowerCase: true,
|
|
7
|
-
})
|
|
8
|
-
}
|
|
7
|
+
});
|
|
8
|
+
};
|
|
9
9
|
|
|
10
10
|
const composeHeaderValues = (value, nextValue) => {
|
|
11
|
-
const headerValues = value.split(", ")
|
|
11
|
+
const headerValues = value.split(", ");
|
|
12
12
|
nextValue.split(", ").forEach((value) => {
|
|
13
13
|
if (!headerValues.includes(value)) {
|
|
14
|
-
headerValues.push(value)
|
|
14
|
+
headerValues.push(value);
|
|
15
15
|
}
|
|
16
|
-
})
|
|
17
|
-
return headerValues.join(", ")
|
|
18
|
-
}
|
|
16
|
+
});
|
|
17
|
+
return headerValues.join(", ");
|
|
18
|
+
};
|
|
19
19
|
|
|
20
20
|
const HEADER_NAMES_COMPOSITION = {
|
|
21
21
|
"accept": composeHeaderValues,
|
|
@@ -29,4 +29,4 @@ const HEADER_NAMES_COMPOSITION = {
|
|
|
29
29
|
"server-timing": composeHeaderValues,
|
|
30
30
|
// 'content-type', // https://github.com/ninenines/cowboy/issues/1230
|
|
31
31
|
"vary": composeHeaderValues,
|
|
32
|
-
}
|
|
32
|
+
};
|