@jsenv/server 17.6.1 → 17.6.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.
Files changed (52) hide show
  1. package/README.md +47 -48
  2. package/dist/jsenv_server.js +1336 -1270
  3. package/package.json +2 -2
  4. package/src/content_negotiation/pick_content_encoding.js +11 -1
  5. package/src/content_negotiation/pick_content_language.js +11 -1
  6. package/src/content_negotiation/pick_content_type.js +11 -1
  7. package/src/content_negotiation/pick_content_version.js +1 -1
  8. package/src/interfacing_with_node/from_node_request.js +154 -178
  9. package/src/interfacing_with_node/observable_from_file_handle.js +1 -18
  10. package/src/interfacing_with_node/observable_from_node_stream.js +29 -65
  11. package/src/interfacing_with_node/write_node_response.js +59 -103
  12. package/src/internal/allowed_hosts.js +85 -0
  13. package/src/internal/escape_html.js +11 -0
  14. package/src/internal/{headersFromObject.js → headers_from_object.js} +2 -2
  15. package/src/internal/listen.js +32 -5
  16. package/src/internal/{listenRequest.js → listen_request.js} +1 -1
  17. package/src/internal/{listenServerConnectionError.js → listen_server_connection_error.js} +1 -1
  18. package/src/internal/{multiple-header.js → multiple_header.js} +32 -10
  19. package/src/internal/reason_phrase.js +17 -0
  20. package/src/internal/response_composition.js +11 -2
  21. package/src/internal/secure_server.js +22 -0
  22. package/src/internal/server_origins.js +78 -0
  23. package/src/internal/{server-polyglot.js → server_polyglot.js} +13 -38
  24. package/src/internal/track_server_pending_connections.js +50 -0
  25. package/src/internal/track_server_pending_requests.js +47 -0
  26. package/src/plugins/cors/server_plugin_cors.js +39 -6
  27. package/src/plugins/default_body_4xx_5xx/server_plugin_default_body_4xx_5xx.js +37 -29
  28. package/src/plugins/error_handler/server_plugin_error_handler.js +26 -10
  29. package/src/plugins/filesystem/fetch_directory.js +16 -3
  30. package/src/plugins/filesystem/fetch_file.js +80 -21
  31. package/src/plugins/filesystem/filesystem_error_to_response.js +12 -6
  32. package/src/plugins/request_aliases/server_plugin_request_aliases.js +8 -0
  33. package/src/plugins/response_acceptance_check/server_plugin_response_acceptance_check.js +11 -6
  34. package/src/plugins/route_inspector/server_plugin_route_inspector.js +11 -5
  35. package/src/plugins/server_plugins_controller.js +27 -13
  36. package/src/progressive_response.js +24 -0
  37. package/src/router/permissions.js +52 -0
  38. package/src/router/router.js +18 -80
  39. package/src/server_events.js +3 -1
  40. package/src/server_timing/timing_header.js +1 -1
  41. package/src/start_server.js +266 -416
  42. package/src/internal/headersFromString.js +0 -21
  43. package/src/internal/headersToObject.js +0 -7
  44. package/src/internal/headersToString.js +0 -16
  45. package/src/internal/listenClientError.js +0 -27
  46. package/src/internal/trackServerPendingConnections.js +0 -145
  47. package/src/internal/trackServerPendingRequests.js +0 -68
  48. package/src/request_body_handling.js +0 -22
  49. /package/src/internal/{colorizeResponseStatus.js → colorize_response_status.js} +0 -0
  50. /package/src/internal/{listenEvent.js → listen_event.js} +0 -0
  51. /package/src/internal/{normalizeHeaderName.js → normalize_header_name.js} +0 -0
  52. /package/src/internal/{normalizeHeaderValue.js → normalize_header_value.js} +0 -0
@@ -1,21 +0,0 @@
1
- import { normalizeHeaderName } from "./normalizeHeaderName.js";
2
- import { normalizeHeaderValue } from "./normalizeHeaderValue.js";
3
-
4
- // https://gist.github.com/mmazer/5404301
5
- export const headersFromString = (headerString) => {
6
- const headers = {};
7
-
8
- if (headerString) {
9
- const pairs = headerString.split("\r\n");
10
- pairs.forEach((pair) => {
11
- const index = pair.indexOf(": ");
12
- if (index > 0) {
13
- const key = pair.slice(0, index);
14
- const value = pair.slice(index + 2);
15
- headers[normalizeHeaderName(key)] = normalizeHeaderValue(value);
16
- }
17
- });
18
- }
19
-
20
- return headers;
21
- };
@@ -1,7 +0,0 @@
1
- export const headersToObject = (headers) => {
2
- const headersObject = {};
3
- headers.forEach((value, name) => {
4
- headersObject[name] = value;
5
- });
6
- return headersObject;
7
- };
@@ -1,16 +0,0 @@
1
- export const headersToString = (headers, { convertName = (name) => name }) => {
2
- const headersString = headersToArray(headers).map(({ name, value }) => {
3
- return `${convertName(name)}: ${value}`;
4
- });
5
-
6
- return headersString.join("\r\n");
7
- };
8
-
9
- const headersToArray = (headers) => {
10
- return Object.keys(headers).map((name) => {
11
- return {
12
- name,
13
- value: headers[name],
14
- };
15
- });
16
- };
@@ -1,27 +0,0 @@
1
- import { listenEvent } from "./listenEvent.js";
2
-
3
- export const listenClientError = (nodeServer, clientErrorCallback) => {
4
- if (nodeServer._httpServer) {
5
- const removeNetClientError = listenEvent(
6
- nodeServer,
7
- "clientError",
8
- clientErrorCallback,
9
- );
10
- const removeHttpClientError = listenEvent(
11
- nodeServer._httpServer,
12
- "clientError",
13
- clientErrorCallback,
14
- );
15
- const removeTlsClientError = listenEvent(
16
- nodeServer._tlsServer,
17
- "clientError",
18
- clientErrorCallback,
19
- );
20
- return () => {
21
- removeNetClientError();
22
- removeHttpClientError();
23
- removeTlsClientError();
24
- };
25
- }
26
- return listenEvent(nodeServer, "clientError", clientErrorCallback);
27
- };
@@ -1,145 +0,0 @@
1
- import { listenEvent } from "./listenEvent.js";
2
-
3
- export const trackServerPendingConnections = (nodeServer, { http2 }) => {
4
- if (http2) {
5
- // see http2.js: we rely on https://nodejs.org/api/http2.html#http2_compatibility_api
6
- return trackHttp1ServerPendingConnections(nodeServer);
7
- }
8
- return trackHttp1ServerPendingConnections(nodeServer);
9
- };
10
-
11
- // const trackHttp2ServerPendingSessions = () => {}
12
-
13
- const trackHttp1ServerPendingConnections = (nodeServer) => {
14
- const pendingConnections = new Set();
15
-
16
- const removeConnectionListener = listenEvent(
17
- nodeServer,
18
- "connection",
19
- (connection) => {
20
- pendingConnections.add(connection);
21
- listenEvent(
22
- connection,
23
- "close",
24
- () => {
25
- pendingConnections.delete(connection);
26
- },
27
- { once: true },
28
- );
29
- },
30
- );
31
-
32
- const stop = async (reason) => {
33
- removeConnectionListener();
34
- const pendingConnectionsArray = Array.from(pendingConnections);
35
- pendingConnections.clear();
36
-
37
- await Promise.all(
38
- pendingConnectionsArray.map(async (pendingConnection) => {
39
- await destroyConnection(pendingConnection, reason);
40
- }),
41
- );
42
- };
43
-
44
- return { stop };
45
- };
46
-
47
- const destroyConnection = (connection, reason) => {
48
- return new Promise((resolve, reject) => {
49
- connection.destroy(reason, (error) => {
50
- if (error) {
51
- if (error === reason || error.code === "ENOTCONN") {
52
- resolve();
53
- } else {
54
- reject(error);
55
- }
56
- } else {
57
- resolve();
58
- }
59
- });
60
- });
61
- };
62
-
63
- // export const trackServerPendingStreams = (nodeServer) => {
64
- // const pendingClients = new Set()
65
-
66
- // const streamListener = (http2Stream, headers, flags) => {
67
- // const client = { http2Stream, headers, flags }
68
-
69
- // pendingClients.add(client)
70
- // http2Stream.on("close", () => {
71
- // pendingClients.delete(client)
72
- // })
73
- // }
74
-
75
- // nodeServer.on("stream", streamListener)
76
-
77
- // const stop = ({
78
- // status,
79
- // // reason
80
- // }) => {
81
- // nodeServer.removeListener("stream", streamListener)
82
-
83
- // return Promise.all(
84
- // Array.from(pendingClients).map(({ http2Stream }) => {
85
- // if (http2Stream.sentHeaders === false) {
86
- // http2Stream.respond({ ":status": status }, { endStream: true })
87
- // }
88
-
89
- // return new Promise((resolve, reject) => {
90
- // if (http2Stream.closed) {
91
- // resolve()
92
- // } else {
93
- // http2Stream.close(NGHTTP2_NO_ERROR, (error) => {
94
- // if (error) {
95
- // reject(error)
96
- // } else {
97
- // resolve()
98
- // }
99
- // })
100
- // }
101
- // })
102
- // }),
103
- // )
104
- // }
105
-
106
- // return { stop }
107
- // }
108
-
109
- // export const trackServerPendingSessions = (nodeServer, { onSessionError }) => {
110
- // const pendingSessions = new Set()
111
-
112
- // const sessionListener = (session) => {
113
- // session.on("close", () => {
114
- // pendingSessions.delete(session)
115
- // })
116
- // session.on("error", onSessionError)
117
- // pendingSessions.add(session)
118
- // }
119
-
120
- // nodeServer.on("session", sessionListener)
121
-
122
- // const stop = async (reason) => {
123
- // nodeServer.removeListener("session", sessionListener)
124
-
125
- // await Promise.all(
126
- // Array.from(pendingSessions).map((pendingSession) => {
127
- // return new Promise((resolve, reject) => {
128
- // pendingSession.close((error) => {
129
- // if (error) {
130
- // if (error === reason || error.code === "ENOTCONN") {
131
- // resolve()
132
- // } else {
133
- // reject(error)
134
- // }
135
- // } else {
136
- // resolve()
137
- // }
138
- // })
139
- // })
140
- // }),
141
- // )
142
- // }
143
-
144
- // return { stop }
145
- // }
@@ -1,68 +0,0 @@
1
- import { listenRequest } from "./listenRequest.js";
2
-
3
- export const trackServerPendingRequests = (nodeServer, { http2 }) => {
4
- if (http2) {
5
- // see http2.js: we rely on https://nodejs.org/api/http2.html#http2_compatibility_api
6
- return trackHttp1ServerPendingRequests(nodeServer);
7
- }
8
- return trackHttp1ServerPendingRequests(nodeServer);
9
- };
10
-
11
- const trackHttp1ServerPendingRequests = (nodeServer) => {
12
- const pendingClients = new Set();
13
-
14
- const removeRequestListener = listenRequest(
15
- nodeServer,
16
- (nodeRequest, nodeResponse) => {
17
- const client = { nodeRequest, nodeResponse };
18
- pendingClients.add(client);
19
- nodeResponse.once("close", () => {
20
- pendingClients.delete(client);
21
- });
22
- },
23
- );
24
-
25
- const stop = async ({ status, reason }) => {
26
- removeRequestListener();
27
- const pendingClientsArray = Array.from(pendingClients);
28
- pendingClients.clear();
29
- await Promise.all(
30
- pendingClientsArray.map(({ nodeResponse }) => {
31
- if (nodeResponse.headersSent === false) {
32
- nodeResponse.writeHead(status, String(reason));
33
- }
34
-
35
- // http2
36
- if (nodeResponse.close) {
37
- return new Promise((resolve, reject) => {
38
- if (nodeResponse.closed) {
39
- resolve();
40
- } else {
41
- nodeResponse.close((error) => {
42
- if (error) {
43
- reject(error);
44
- } else {
45
- resolve();
46
- }
47
- });
48
- }
49
- });
50
- }
51
-
52
- // http
53
- return new Promise((resolve) => {
54
- if (nodeResponse.destroyed) {
55
- resolve();
56
- } else {
57
- nodeResponse.once("close", () => {
58
- resolve();
59
- });
60
- nodeResponse.destroy();
61
- }
62
- });
63
- }),
64
- );
65
- };
66
-
67
- return { stop };
68
- };
@@ -1,22 +0,0 @@
1
- export const pickRequestContentType = (request, acceptedContentTypeArray) => {
2
- const requestBodyContentType = request.headers["content-type"];
3
- if (!requestBodyContentType) {
4
- return null;
5
- }
6
- for (const acceptedContentType of acceptedContentTypeArray) {
7
- if (requestBodyContentType.includes(acceptedContentType)) {
8
- return acceptedContentType;
9
- }
10
- }
11
- return null;
12
- };
13
-
14
- export const createUnsupportedMediaTypeResponse = (
15
- request,
16
- acceptedContentTypeArray = [],
17
- ) => {
18
- return {
19
- status: 415,
20
- [`accept-${request.method}`]: acceptedContentTypeArray.join(", "),
21
- };
22
- };