@jsenv/server 17.6.0 → 17.6.2
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 +47 -48
- package/dist/client/default_body_4xx_5xx/4xx.html +1 -1
- package/dist/jsenv_server.js +1332 -1269
- package/dist/ws/ws.js +3 -1
- package/package.json +5 -5
- package/src/content_negotiation/pick_content_encoding.js +11 -1
- package/src/content_negotiation/pick_content_language.js +11 -1
- package/src/content_negotiation/pick_content_type.js +11 -1
- package/src/content_negotiation/pick_content_version.js +1 -1
- package/src/interfacing_with_node/from_node_request.js +154 -178
- package/src/interfacing_with_node/observable_from_file_handle.js +1 -18
- package/src/interfacing_with_node/observable_from_node_stream.js +29 -65
- package/src/interfacing_with_node/write_node_response.js +59 -103
- package/src/internal/allowed_hosts.js +85 -0
- package/src/internal/escape_html.js +11 -0
- package/src/internal/{headersFromObject.js → headers_from_object.js} +2 -2
- package/src/internal/listen.js +32 -5
- package/src/internal/{listenRequest.js → listen_request.js} +1 -1
- package/src/internal/{listenServerConnectionError.js → listen_server_connection_error.js} +1 -1
- package/src/internal/{multiple-header.js → multiple_header.js} +32 -10
- package/src/internal/reason_phrase.js +17 -0
- package/src/internal/response_composition.js +11 -2
- package/src/internal/secure_server.js +22 -0
- package/src/internal/server_origins.js +78 -0
- package/src/internal/{server-polyglot.js → server_polyglot.js} +13 -38
- package/src/internal/track_server_pending_connections.js +50 -0
- package/src/internal/track_server_pending_requests.js +47 -0
- package/src/plugins/cors/server_plugin_cors.js +39 -6
- package/src/plugins/default_body_4xx_5xx/server_plugin_default_body_4xx_5xx.js +37 -29
- package/src/plugins/error_handler/server_plugin_error_handler.js +26 -10
- package/src/plugins/filesystem/fetch_directory.js +16 -3
- package/src/plugins/filesystem/fetch_file.js +80 -21
- package/src/plugins/filesystem/filesystem_error_to_response.js +12 -6
- package/src/plugins/request_aliases/server_plugin_request_aliases.js +8 -0
- package/src/plugins/response_acceptance_check/server_plugin_response_acceptance_check.js +11 -6
- package/src/plugins/route_inspector/server_plugin_route_inspector.js +11 -5
- package/src/plugins/server_plugins_controller.js +27 -13
- package/src/progressive_response.js +24 -0
- package/src/router/permissions.js +52 -0
- package/src/router/router.js +14 -79
- package/src/server_events.js +3 -1
- package/src/server_timing/timing_header.js +1 -1
- package/src/start_server.js +266 -416
- package/src/internal/headersFromString.js +0 -21
- package/src/internal/headersToObject.js +0 -7
- package/src/internal/headersToString.js +0 -16
- package/src/internal/listenClientError.js +0 -27
- package/src/internal/trackServerPendingConnections.js +0 -145
- package/src/internal/trackServerPendingRequests.js +0 -68
- package/src/request_body_handling.js +0 -22
- /package/src/internal/{colorizeResponseStatus.js → colorize_response_status.js} +0 -0
- /package/src/internal/{listenEvent.js → listen_event.js} +0 -0
- /package/src/internal/{normalizeHeaderName.js → normalize_header_name.js} +0 -0
- /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,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
|
-
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|