@jsenv/server 12.0.0 → 12.3.0
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 +2 -2
- package/package.json +22 -27
- package/src/cors/plugin_cors.js +1 -3
- package/src/internal/memoize.js +24 -0
- package/src/internal/server-polyglot.js +10 -10
- package/src/sse/createSSERoom.js +16 -0
- package/src/startServer.js +144 -96
package/README.md
CHANGED
|
@@ -92,8 +92,8 @@ import { startServer } from "@jsenv/server"
|
|
|
92
92
|
|
|
93
93
|
await startServer({
|
|
94
94
|
protocol: "https",
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
certificate: readFileSyncAsString("./server.crt"),
|
|
96
|
+
privateKey: readFileSyncAsString("./server.key"),
|
|
97
97
|
allowHttpRequestOnHttps: true,
|
|
98
98
|
requestToResponse: (request) => {
|
|
99
99
|
const clientUsesHttp = request.origin.startsWith("http:")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/server",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.3.0",
|
|
4
4
|
"description": "Write your Node.js server using pure functions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"url": "https://github.com/jsenv/server"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=16.
|
|
16
|
+
"node": ">=16.13.0"
|
|
17
17
|
},
|
|
18
18
|
"publishConfig": {
|
|
19
19
|
"access": "public"
|
|
@@ -31,45 +31,40 @@
|
|
|
31
31
|
"/main.js"
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"test": "node
|
|
34
|
+
"importmap": "node ./script/importmap/importmap.mjs",
|
|
35
|
+
"eslint": "node ./node_modules/eslint/bin/eslint.js . --ext=.js,.mjs",
|
|
36
|
+
"performance": "node --expose-gc ./script/performance/performance.mjs --log",
|
|
37
|
+
"test": "node ./script/test/test.mjs",
|
|
38
38
|
"test-with-coverage": "npm run test -- --coverage",
|
|
39
39
|
"generate-html-snapshots": "node ./test/internal_error/generate_html_snapshot_files.mjs",
|
|
40
|
-
"prettier
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"certificate-authority-install": "node ./script/certificate/certificate_authority_install.mjs",
|
|
44
|
-
"playwright-install": "npx playwright install-deps && npx playwright install",
|
|
45
|
-
"prepublishOnly": "node ./script/publish/remove_postinstall.mjs",
|
|
46
|
-
"postpublish": "node ./script/publish/restore_postinstall.mjs"
|
|
40
|
+
"prettier": "prettier --write .",
|
|
41
|
+
"certificate-install": "node ./script/certificate/certificate_install.mjs",
|
|
42
|
+
"playwright-install": "npx playwright install-deps && npx playwright install"
|
|
47
43
|
},
|
|
48
44
|
"dependencies": {
|
|
49
|
-
"@jsenv/abort": "4.1.
|
|
45
|
+
"@jsenv/abort": "4.1.2",
|
|
50
46
|
"@jsenv/logger": "4.0.1",
|
|
51
|
-
"@jsenv/url-meta": "6.0.
|
|
47
|
+
"@jsenv/url-meta": "6.0.3",
|
|
52
48
|
"node-fetch": "2.6.2",
|
|
53
49
|
"path-to-regexp": "6.2.0"
|
|
54
50
|
},
|
|
55
51
|
"devDependencies": {
|
|
56
|
-
"@jsenv/assert": "2.
|
|
57
|
-
"@jsenv/core": "
|
|
52
|
+
"@jsenv/assert": "2.4.1",
|
|
53
|
+
"@jsenv/core": "25.1.0",
|
|
58
54
|
"@jsenv/eslint-config": "16.0.9",
|
|
59
|
-
"@jsenv/filesystem": "2.
|
|
55
|
+
"@jsenv/filesystem": "2.6.1",
|
|
60
56
|
"@jsenv/github-release-package": "1.2.3",
|
|
61
57
|
"@jsenv/https-local": "1.0.3",
|
|
62
|
-
"@jsenv/importmap-eslint-resolver": "5.
|
|
63
|
-
"@jsenv/importmap-node-module": "
|
|
58
|
+
"@jsenv/importmap-eslint-resolver": "5.2.2",
|
|
59
|
+
"@jsenv/importmap-node-module": "5.1.0",
|
|
64
60
|
"@jsenv/package-publish": "1.6.2",
|
|
65
61
|
"@jsenv/performance-impact": "2.2.1",
|
|
66
|
-
"
|
|
67
|
-
"eslint": "
|
|
68
|
-
"eslint-plugin-import": "2.25.3",
|
|
62
|
+
"eslint": "8.7.0",
|
|
63
|
+
"eslint-plugin-import": "2.25.4",
|
|
69
64
|
"eventsource": "1.1.0",
|
|
70
|
-
"fastify": "3.
|
|
65
|
+
"fastify": "3.25.3",
|
|
71
66
|
"fastify-static": "4.5.0",
|
|
72
|
-
"playwright": "1.
|
|
73
|
-
"prettier": "2.
|
|
67
|
+
"playwright": "1.17.2",
|
|
68
|
+
"prettier": "2.5.1"
|
|
74
69
|
}
|
|
75
|
-
}
|
|
70
|
+
}
|
package/src/cors/plugin_cors.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { urlToOrigin } from "@jsenv/filesystem"
|
|
2
|
-
|
|
3
1
|
export const jsenvAccessControlAllowedHeaders = ["x-requested-with"]
|
|
4
2
|
|
|
5
3
|
export const jsenvAccessControlAllowedMethods = [
|
|
@@ -107,7 +105,7 @@ const generateAccessControlHeaders = ({
|
|
|
107
105
|
allowedOriginArray.push(headers.origin)
|
|
108
106
|
vary.push("origin")
|
|
109
107
|
} else if ("referer" in headers) {
|
|
110
|
-
allowedOriginArray.push(
|
|
108
|
+
allowedOriginArray.push(new URL(headers.referer).origin)
|
|
111
109
|
vary.push("referer")
|
|
112
110
|
} else {
|
|
113
111
|
allowedOriginArray.push("*")
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const memoize = (compute) => {
|
|
2
|
+
let memoized = false
|
|
3
|
+
let memoizedValue
|
|
4
|
+
|
|
5
|
+
const fnWithMemoization = (...args) => {
|
|
6
|
+
if (memoized) {
|
|
7
|
+
return memoizedValue
|
|
8
|
+
}
|
|
9
|
+
// if compute is recursive wait for it to be fully done before storing the value
|
|
10
|
+
// so set memoized boolean after the call
|
|
11
|
+
memoizedValue = compute(...args)
|
|
12
|
+
memoized = true
|
|
13
|
+
return memoizedValue
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
fnWithMemoization.forget = () => {
|
|
17
|
+
const value = memoizedValue
|
|
18
|
+
memoized = false
|
|
19
|
+
memoizedValue = undefined
|
|
20
|
+
return value
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return fnWithMemoization
|
|
24
|
+
}
|
|
@@ -11,13 +11,13 @@ import { listenEvent } from "./listenEvent.js"
|
|
|
11
11
|
export const createPolyglotServer = async ({
|
|
12
12
|
http2 = false,
|
|
13
13
|
http1Allowed = true,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
certificate,
|
|
15
|
+
privateKey,
|
|
16
16
|
}) => {
|
|
17
17
|
const httpServer = http.createServer()
|
|
18
18
|
const tlsServer = await createSecureServer({
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
certificate,
|
|
20
|
+
privateKey,
|
|
21
21
|
http2,
|
|
22
22
|
http1Allowed,
|
|
23
23
|
})
|
|
@@ -63,24 +63,24 @@ export const createPolyglotServer = async ({
|
|
|
63
63
|
// The async part is just to lazyly import "http2" or "https"
|
|
64
64
|
// so that these module are parsed only if used.
|
|
65
65
|
const createSecureServer = async ({
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
certificate,
|
|
67
|
+
privateKey,
|
|
68
68
|
http2,
|
|
69
69
|
http1Allowed,
|
|
70
70
|
}) => {
|
|
71
71
|
if (http2) {
|
|
72
72
|
const { createSecureServer } = await import("http2")
|
|
73
73
|
return createSecureServer({
|
|
74
|
-
cert:
|
|
75
|
-
key:
|
|
74
|
+
cert: certificate,
|
|
75
|
+
key: privateKey,
|
|
76
76
|
allowHTTP1: http1Allowed,
|
|
77
77
|
})
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
const { createServer } = await import("https")
|
|
81
81
|
return createServer({
|
|
82
|
-
cert:
|
|
83
|
-
key:
|
|
82
|
+
cert: certificate,
|
|
83
|
+
key: privateKey,
|
|
84
84
|
})
|
|
85
85
|
}
|
|
86
86
|
|
package/src/sse/createSSERoom.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
// https://www.html5rocks.com/en/tutorials/eventsource/basics/
|
|
9
9
|
export const createSSERoom = ({
|
|
10
10
|
logLevel,
|
|
11
|
+
effect = () => {},
|
|
11
12
|
// do not keep process alive because of rooms, something else must keep it alive
|
|
12
13
|
keepProcessAlive = false,
|
|
13
14
|
keepaliveDuration = 30 * 1000,
|
|
@@ -29,6 +30,7 @@ export const createSSERoom = ({
|
|
|
29
30
|
let previousEventId = 0
|
|
30
31
|
let opened = false
|
|
31
32
|
let interval
|
|
33
|
+
let cleanupEffect = CLEANUP_NOOP
|
|
32
34
|
|
|
33
35
|
const join = (request) => {
|
|
34
36
|
// should we ensure a given request can join a room only once?
|
|
@@ -56,6 +58,14 @@ export const createSSERoom = ({
|
|
|
56
58
|
next,
|
|
57
59
|
complete,
|
|
58
60
|
}
|
|
61
|
+
if (clients.size === 0) {
|
|
62
|
+
const effectReturnValue = effect()
|
|
63
|
+
if (typeof effectReturnValue === "function") {
|
|
64
|
+
cleanupEffect = effectReturnValue
|
|
65
|
+
} else {
|
|
66
|
+
cleanupEffect = CLEANUP_NOOP
|
|
67
|
+
}
|
|
68
|
+
}
|
|
59
69
|
clients.add(client)
|
|
60
70
|
logger.debug(
|
|
61
71
|
`A client has joined. Number of client in room: ${clients.size}`,
|
|
@@ -101,6 +111,10 @@ export const createSSERoom = ({
|
|
|
101
111
|
|
|
102
112
|
return () => {
|
|
103
113
|
clients.delete(client)
|
|
114
|
+
if (clients.size === 0) {
|
|
115
|
+
cleanupEffect()
|
|
116
|
+
cleanupEffect = CLEANUP_NOOP
|
|
117
|
+
}
|
|
104
118
|
logger.debug(`A client left. Number of client in room: ${clients.size}`)
|
|
105
119
|
}
|
|
106
120
|
})
|
|
@@ -210,6 +224,8 @@ export const createSSERoom = ({
|
|
|
210
224
|
return room
|
|
211
225
|
}
|
|
212
226
|
|
|
227
|
+
const CLEANUP_NOOP = () => {}
|
|
228
|
+
|
|
213
229
|
const requestMap = new Map()
|
|
214
230
|
|
|
215
231
|
const connectRequestAndRoom = (request, room, roomObservable) => {
|
package/src/startServer.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import http from "node:http"
|
|
2
2
|
import cluster from "node:cluster"
|
|
3
|
-
import { memoize } from "@jsenv/filesystem"
|
|
4
3
|
import { createDetailedMessage, createLogger } from "@jsenv/logger"
|
|
5
4
|
import {
|
|
6
5
|
Abort,
|
|
@@ -23,6 +22,7 @@ import { listen, stopListening } from "./internal/listen.js"
|
|
|
23
22
|
import { composeTwoResponses } from "./internal/response_composition.js"
|
|
24
23
|
import { listenRequest } from "./internal/listenRequest.js"
|
|
25
24
|
import { listenServerConnectionError } from "./internal/listenServerConnectionError.js"
|
|
25
|
+
import { memoize } from "./internal/memoize.js"
|
|
26
26
|
import { jsenvServerInternalErrorToResponse } from "./jsenvServerInternalErrorToResponse.js"
|
|
27
27
|
import {
|
|
28
28
|
STOP_REASON_INTERNAL_ERROR,
|
|
@@ -47,8 +47,8 @@ export const startServer = async ({
|
|
|
47
47
|
ip = "0.0.0.0", // will it work on windows ? https://github.com/nodejs/node/issues/14900
|
|
48
48
|
port = 0, // assign a random available port
|
|
49
49
|
portHint,
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
privateKey,
|
|
51
|
+
certificate,
|
|
52
52
|
|
|
53
53
|
// when inside a worker, we should not try to stop server on SIGINT
|
|
54
54
|
// otherwise it can create an EPIPE error while primary process tries
|
|
@@ -74,11 +74,11 @@ export const startServer = async ({
|
|
|
74
74
|
throw new Error(`protocol must be http or https, got ${protocol}`)
|
|
75
75
|
}
|
|
76
76
|
if (protocol === "https") {
|
|
77
|
-
if (!
|
|
78
|
-
throw new Error(`missing
|
|
77
|
+
if (!certificate) {
|
|
78
|
+
throw new Error(`missing certificate for https server`)
|
|
79
79
|
}
|
|
80
|
-
if (!
|
|
81
|
-
throw new Error(`missing
|
|
80
|
+
if (!privateKey) {
|
|
81
|
+
throw new Error(`missing privateKey for https server`)
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
if (http2 && protocol !== "https") {
|
|
@@ -134,8 +134,8 @@ export const startServer = async ({
|
|
|
134
134
|
protocol,
|
|
135
135
|
redirectHttpToHttps,
|
|
136
136
|
allowHttpRequestOnHttps,
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
certificate,
|
|
138
|
+
privateKey,
|
|
139
139
|
http2,
|
|
140
140
|
http1Allowed,
|
|
141
141
|
})
|
|
@@ -332,29 +332,6 @@ export const startServer = async ({
|
|
|
332
332
|
const pushResponse = async ({ path, method }, { requestNode }) => {
|
|
333
333
|
const http2Stream = nodeResponse.stream
|
|
334
334
|
|
|
335
|
-
if (typeof path !== "string" || path[0] !== "/") {
|
|
336
|
-
addRequestLog(requestNode, {
|
|
337
|
-
type: "warn",
|
|
338
|
-
value: `pushResponse call ignored because path is invalid (must be a string starting with "/", found ${path})`,
|
|
339
|
-
})
|
|
340
|
-
return
|
|
341
|
-
}
|
|
342
|
-
if (!request.http2) {
|
|
343
|
-
addRequestLog(requestNode, {
|
|
344
|
-
type: "warn",
|
|
345
|
-
value: `pushResponse call ignored because request is not http2`,
|
|
346
|
-
})
|
|
347
|
-
return
|
|
348
|
-
}
|
|
349
|
-
const canPushStream = testCanPushStream(http2Stream)
|
|
350
|
-
if (!canPushStream.can) {
|
|
351
|
-
addRequestLog(requestNode, {
|
|
352
|
-
type: "debug",
|
|
353
|
-
value: `Ignoring call to pushResponse because ${canPushStream.reason}`,
|
|
354
|
-
})
|
|
355
|
-
return
|
|
356
|
-
}
|
|
357
|
-
|
|
358
335
|
// being able to push a stream is nice to have
|
|
359
336
|
// so when it fails it's not critical
|
|
360
337
|
const onPushStreamError = (e) => {
|
|
@@ -488,19 +465,65 @@ export const startServer = async ({
|
|
|
488
465
|
redirectRequest,
|
|
489
466
|
})
|
|
490
467
|
if (returnValue) {
|
|
491
|
-
requestPlugins.push(
|
|
468
|
+
requestPlugins.push({
|
|
469
|
+
pluginName,
|
|
470
|
+
...returnValue,
|
|
471
|
+
})
|
|
492
472
|
}
|
|
493
473
|
}
|
|
494
474
|
})
|
|
495
|
-
|
|
496
|
-
? {
|
|
475
|
+
const result = responseFromShortcircuit
|
|
476
|
+
? { returnValue: responseFromShortcircuit }
|
|
497
477
|
: await generateResponseDescription({
|
|
498
478
|
requestToResponse,
|
|
499
479
|
request,
|
|
500
|
-
pushResponse: ({ path, method }) => {
|
|
480
|
+
pushResponse: async ({ path, method }) => {
|
|
481
|
+
if (typeof path !== "string" || path[0] !== "/") {
|
|
482
|
+
addRequestLog(requestNode, {
|
|
483
|
+
type: "warn",
|
|
484
|
+
value: `response push ignored because path is invalid (must be a string starting with "/", found ${path})`,
|
|
485
|
+
})
|
|
486
|
+
return
|
|
487
|
+
}
|
|
488
|
+
if (!request.http2) {
|
|
489
|
+
addRequestLog(requestNode, {
|
|
490
|
+
type: "warn",
|
|
491
|
+
value: `response push ignored because request is not http2`,
|
|
492
|
+
})
|
|
493
|
+
return
|
|
494
|
+
}
|
|
495
|
+
const canPushStream = testCanPushStream(nodeResponse.stream)
|
|
496
|
+
if (!canPushStream.can) {
|
|
497
|
+
addRequestLog(requestNode, {
|
|
498
|
+
type: "debug",
|
|
499
|
+
value: `response push ignored because ${canPushStream.reason}`,
|
|
500
|
+
})
|
|
501
|
+
return
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
let prevented = false
|
|
505
|
+
const prevent = () => {
|
|
506
|
+
prevented = true
|
|
507
|
+
}
|
|
508
|
+
const preventedByPlugin = requestPlugins.find((requestPlugin) => {
|
|
509
|
+
const { onPushResponse } = requestPlugin
|
|
510
|
+
if (!onPushResponse) {
|
|
511
|
+
return false
|
|
512
|
+
}
|
|
513
|
+
onPushResponse({ path, method }, { prevent })
|
|
514
|
+
return prevented
|
|
515
|
+
})
|
|
516
|
+
if (prevented) {
|
|
517
|
+
addRequestLog(requestNode, {
|
|
518
|
+
type: "debug",
|
|
519
|
+
value: `response push prevented by "${preventedByPlugin.pluginName}" plugin`,
|
|
520
|
+
})
|
|
521
|
+
return
|
|
522
|
+
}
|
|
523
|
+
|
|
501
524
|
const requestChildNode = { logs: [], children: [] }
|
|
502
525
|
requestNode.children.push(requestChildNode)
|
|
503
|
-
|
|
526
|
+
await pushResponse(
|
|
504
527
|
{ path, method },
|
|
505
528
|
{
|
|
506
529
|
requestNode: requestChildNode,
|
|
@@ -510,65 +533,60 @@ export const startServer = async ({
|
|
|
510
533
|
},
|
|
511
534
|
})
|
|
512
535
|
|
|
536
|
+
const { aborted } = result
|
|
513
537
|
if (aborted) {
|
|
514
538
|
return ABORTED_RESPONSE_PROPERTIES
|
|
515
539
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
if (
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
{
|
|
536
|
-
status: 500,
|
|
537
|
-
statusText: statusToStatusText(500),
|
|
538
|
-
headers: {
|
|
539
|
-
// ensure error are not cached
|
|
540
|
-
"cache-control": "no-store",
|
|
541
|
-
"content-type": "text/plain",
|
|
540
|
+
const readResponseProperties = async () => {
|
|
541
|
+
const { error } = result
|
|
542
|
+
// internal error, create 500 response
|
|
543
|
+
if (error) {
|
|
544
|
+
if (
|
|
545
|
+
// stopOnInternalError stops server only if requestToResponse generated
|
|
546
|
+
// a non controlled error (internal error).
|
|
547
|
+
// if requestToResponse gracefully produced a 500 response (it did not throw)
|
|
548
|
+
// then we can assume we are still in control of what we are doing
|
|
549
|
+
stopOnInternalError
|
|
550
|
+
) {
|
|
551
|
+
// il faudrais pouvoir stop que les autres response ?
|
|
552
|
+
stop(STOP_REASON_INTERNAL_ERROR)
|
|
553
|
+
}
|
|
554
|
+
const propertiesFromError = await serverInternalErrorToResponse(
|
|
555
|
+
error,
|
|
556
|
+
{
|
|
557
|
+
request,
|
|
558
|
+
sendServerInternalErrorDetails,
|
|
542
559
|
},
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
560
|
+
)
|
|
561
|
+
addRequestLog(requestNode, {
|
|
562
|
+
type: "error",
|
|
563
|
+
value: createDetailedMessage(
|
|
564
|
+
`internal error while handling request`,
|
|
565
|
+
{
|
|
566
|
+
"error stack": error.stack,
|
|
567
|
+
},
|
|
568
|
+
),
|
|
569
|
+
})
|
|
570
|
+
return composeTwoResponses(
|
|
550
571
|
{
|
|
551
|
-
|
|
572
|
+
status: 500,
|
|
573
|
+
statusText: statusToStatusText(500),
|
|
574
|
+
headers: {
|
|
575
|
+
// ensure error are not cached
|
|
576
|
+
"cache-control": "no-store",
|
|
577
|
+
"content-type": "text/plain",
|
|
578
|
+
},
|
|
552
579
|
},
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
} else {
|
|
556
|
-
const {
|
|
557
|
-
status = 501,
|
|
558
|
-
statusText = statusToStatusText(status),
|
|
559
|
-
headers = {},
|
|
560
|
-
body,
|
|
561
|
-
...rest
|
|
562
|
-
} = responseProperties || {}
|
|
563
|
-
responseProperties = {
|
|
564
|
-
status,
|
|
565
|
-
statusText,
|
|
566
|
-
headers,
|
|
567
|
-
body,
|
|
568
|
-
...rest,
|
|
580
|
+
propertiesFromError,
|
|
581
|
+
)
|
|
569
582
|
}
|
|
583
|
+
const responseProperties = normalizeResponseProperties(
|
|
584
|
+
result.returnValue,
|
|
585
|
+
)
|
|
586
|
+
return responseProperties
|
|
570
587
|
}
|
|
571
588
|
|
|
589
|
+
let responseProperties = await readResponseProperties()
|
|
572
590
|
// call every plugin "onResponse" hook
|
|
573
591
|
requestPlugins.forEach((requestPlugin) => {
|
|
574
592
|
const { onResponse } = requestPlugin
|
|
@@ -582,7 +600,6 @@ export const startServer = async ({
|
|
|
582
600
|
}
|
|
583
601
|
}
|
|
584
602
|
})
|
|
585
|
-
|
|
586
603
|
if (
|
|
587
604
|
request.method !== "HEAD" &&
|
|
588
605
|
responseProperties.headers["content-length"] > 0 &&
|
|
@@ -593,7 +610,6 @@ export const startServer = async ({
|
|
|
593
610
|
value: `content-length header is ${responseProperties.headers["content-length"]} but body is empty`,
|
|
594
611
|
})
|
|
595
612
|
}
|
|
596
|
-
|
|
597
613
|
return responseProperties
|
|
598
614
|
}
|
|
599
615
|
|
|
@@ -701,8 +717,8 @@ const createNodeServer = async ({
|
|
|
701
717
|
protocol,
|
|
702
718
|
redirectHttpToHttps,
|
|
703
719
|
allowHttpRequestOnHttps,
|
|
704
|
-
|
|
705
|
-
|
|
720
|
+
certificate,
|
|
721
|
+
privateKey,
|
|
706
722
|
http2,
|
|
707
723
|
http1Allowed,
|
|
708
724
|
}) => {
|
|
@@ -712,8 +728,8 @@ const createNodeServer = async ({
|
|
|
712
728
|
|
|
713
729
|
if (redirectHttpToHttps || allowHttpRequestOnHttps) {
|
|
714
730
|
return createPolyglotServer({
|
|
715
|
-
|
|
716
|
-
|
|
731
|
+
certificate,
|
|
732
|
+
privateKey,
|
|
717
733
|
http2,
|
|
718
734
|
http1Allowed,
|
|
719
735
|
})
|
|
@@ -721,11 +737,43 @@ const createNodeServer = async ({
|
|
|
721
737
|
|
|
722
738
|
const { createServer } = await import("https")
|
|
723
739
|
return createServer({
|
|
724
|
-
cert:
|
|
725
|
-
key:
|
|
740
|
+
cert: certificate,
|
|
741
|
+
key: privateKey,
|
|
726
742
|
})
|
|
727
743
|
}
|
|
728
744
|
|
|
745
|
+
const normalizeResponseProperties = (returnValue) => {
|
|
746
|
+
if (returnValue && returnValue.constructor.name === "Response") {
|
|
747
|
+
const responseHeaders = {}
|
|
748
|
+
const headersToIgnore = ["connection"]
|
|
749
|
+
returnValue.headers.forEach((value, name) => {
|
|
750
|
+
if (!headersToIgnore.includes(name)) {
|
|
751
|
+
responseHeaders[name] = value
|
|
752
|
+
}
|
|
753
|
+
})
|
|
754
|
+
return {
|
|
755
|
+
status: returnValue.status,
|
|
756
|
+
statusText: returnValue.statusText,
|
|
757
|
+
headers: responseHeaders,
|
|
758
|
+
body: returnValue.body, // node-fetch assumed
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const {
|
|
762
|
+
status = 501,
|
|
763
|
+
statusText = statusToStatusText(status),
|
|
764
|
+
headers = {},
|
|
765
|
+
body,
|
|
766
|
+
...rest
|
|
767
|
+
} = returnValue || {}
|
|
768
|
+
return {
|
|
769
|
+
status,
|
|
770
|
+
statusText,
|
|
771
|
+
headers,
|
|
772
|
+
body,
|
|
773
|
+
...rest,
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
729
777
|
const generateResponseDescription = async ({
|
|
730
778
|
requestToResponse,
|
|
731
779
|
request,
|
|
@@ -736,7 +784,7 @@ const generateResponseDescription = async ({
|
|
|
736
784
|
pushResponse,
|
|
737
785
|
})
|
|
738
786
|
return {
|
|
739
|
-
|
|
787
|
+
returnValue,
|
|
740
788
|
}
|
|
741
789
|
} catch (error) {
|
|
742
790
|
if (error.name === "AbortError" && request.signal.aborted) {
|