@jsenv/server
Advanced tools
Comparing version 14.1.4 to 14.1.5
{ | ||
"name": "@jsenv/server", | ||
"version": "14.1.4", | ||
"version": "14.1.5", | ||
"description": "Write your Node.js server using pure functions", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -6,3 +6,3 @@ import { createLogger } from "@jsenv/log" | ||
createCompositeProducer, | ||
} from "@jsenv/server/src/internal/observable.js" | ||
} from "@jsenv/server/src/interfacing_with_node/observable.js" | ||
@@ -9,0 +9,0 @@ // https://www.html5rocks.com/en/tutorials/eventsource/basics/ |
@@ -21,4 +21,4 @@ import { isIP } from "node:net" | ||
applyRedirectionToRequest, | ||
} from "./internal/request_factory.js" | ||
import { populateNodeResponse } from "./internal/populateNodeResponse.js" | ||
} from "./interfacing_with_node/request_factory.js" | ||
import { writeNodeResponse } from "./interfacing_with_node/write_node_response.js" | ||
import { | ||
@@ -91,2 +91,9 @@ statusToType, | ||
}, | ||
// timeAllocated to start responding to a request | ||
// after this delay the server will respond with 504 | ||
responseTimeout = 60_000 * 10, // 10s | ||
// time allocated to server code to start reading the request body | ||
// after this delay the underlying stream is destroyed, attempting to read it would throw | ||
// if used the stream stays opened, it's only if the stream is not read at all that it gets destroyed | ||
requestBodyLifetime = 60_000 * 2, // 2s | ||
} = {}) => { | ||
@@ -540,3 +547,4 @@ const logger = createLogger({ logLevel }) | ||
abortController.abort() | ||
} else if (responseProperties !== ABORTED_RESPONSE_PROPERTIES) { | ||
} else if (responseProperties.requestAborted) { | ||
} else { | ||
const responseLength = | ||
@@ -615,71 +623,91 @@ responseProperties.headers["content-length"] || 0 | ||
let handleRequestTimings = serverTiming ? {} : null | ||
try { | ||
handleRequestReturnValue = | ||
await serviceController.callAsyncHooksUntil( | ||
"handleRequest", | ||
request, | ||
{ | ||
timing: handleRequestTimings, | ||
warn, | ||
pushResponse: async ({ path, method }) => { | ||
if (typeof path !== "string" || path[0] !== "/") { | ||
addRequestLog(requestNode, { | ||
type: "warn", | ||
value: `response push ignored because path is invalid (must be a string starting with "/", found ${path})`, | ||
}) | ||
return | ||
} | ||
if (!request.http2) { | ||
addRequestLog(requestNode, { | ||
type: "warn", | ||
value: `response push ignored because request is not http2`, | ||
}) | ||
return | ||
} | ||
const canPushStream = testCanPushStream(nodeResponse.stream) | ||
if (!canPushStream.can) { | ||
addRequestLog(requestNode, { | ||
type: "debug", | ||
value: `response push ignored because ${canPushStream.reason}`, | ||
}) | ||
return | ||
} | ||
let preventedByService = null | ||
const prevent = () => { | ||
preventedByService = serviceController.getCurrentService() | ||
} | ||
serviceController.callHooksUntil( | ||
"onResponsePush", | ||
{ path, method }, | ||
{ | ||
request, | ||
warn, | ||
prevent, | ||
}, | ||
() => preventedByService, | ||
) | ||
if (preventedByService) { | ||
addRequestLog(requestNode, { | ||
type: "debug", | ||
value: `response push prevented by "${preventedByService.name}" service`, | ||
}) | ||
return | ||
} | ||
let timeout | ||
const timeoutPromise = new Promise((resolve) => { | ||
timeout = setTimeout(() => { | ||
resolve({ | ||
// the correct status code should be 500 because it's | ||
// we don't really know what takes time | ||
// in practice it's often because server is trying to reach an other server | ||
// that is not responding so 504 is more correct | ||
status: 504, | ||
statusText: `server timeout after ${ | ||
responseTimeout / 1000 | ||
}s waiting to handle request`, | ||
}) | ||
}, responseTimeout) | ||
}) | ||
const handleRequestPromise = serviceController.callAsyncHooksUntil( | ||
"handleRequest", | ||
request, | ||
{ | ||
timing: handleRequestTimings, | ||
warn, | ||
pushResponse: async ({ path, method }) => { | ||
if (typeof path !== "string" || path[0] !== "/") { | ||
addRequestLog(requestNode, { | ||
type: "warn", | ||
value: `response push ignored because path is invalid (must be a string starting with "/", found ${path})`, | ||
}) | ||
return | ||
} | ||
if (!request.http2) { | ||
addRequestLog(requestNode, { | ||
type: "warn", | ||
value: `response push ignored because request is not http2`, | ||
}) | ||
return | ||
} | ||
const canPushStream = testCanPushStream(nodeResponse.stream) | ||
if (!canPushStream.can) { | ||
addRequestLog(requestNode, { | ||
type: "debug", | ||
value: `response push ignored because ${canPushStream.reason}`, | ||
}) | ||
return | ||
} | ||
const requestChildNode = { logs: [], children: [] } | ||
requestNode.children.push(requestChildNode) | ||
await pushResponse( | ||
{ path, method }, | ||
{ | ||
requestNode: requestChildNode, | ||
parentHttp2Stream: nodeResponse.stream, | ||
}, | ||
) | ||
let preventedByService = null | ||
const prevent = () => { | ||
preventedByService = serviceController.getCurrentService() | ||
} | ||
serviceController.callHooksUntil( | ||
"onResponsePush", | ||
{ path, method }, | ||
{ | ||
request, | ||
warn, | ||
prevent, | ||
}, | ||
}, | ||
) | ||
} catch (error) { | ||
errorWhileHandlingRequest = error | ||
() => preventedByService, | ||
) | ||
if (preventedByService) { | ||
addRequestLog(requestNode, { | ||
type: "debug", | ||
value: `response push prevented by "${preventedByService.name}" service`, | ||
}) | ||
return | ||
} | ||
const requestChildNode = { logs: [], children: [] } | ||
requestNode.children.push(requestChildNode) | ||
await pushResponse( | ||
{ path, method }, | ||
{ | ||
requestNode: requestChildNode, | ||
parentHttp2Stream: nodeResponse.stream, | ||
}, | ||
) | ||
}, | ||
}, | ||
) | ||
try { | ||
handleRequestReturnValue = await Promise.race([ | ||
timeoutPromise, | ||
handleRequestPromise, | ||
]) | ||
} catch (e) { | ||
errorWhileHandlingRequest = e | ||
} | ||
clearTimeout(timeout) | ||
@@ -692,3 +720,3 @@ let responseProperties | ||
) { | ||
responseProperties = ABORTED_RESPONSE_PROPERTIES | ||
responseProperties = { requestAborted: true } | ||
} else { | ||
@@ -826,3 +854,3 @@ // internal error, create 500 response | ||
await populateNodeResponse(responseStream, responseProperties, { | ||
await writeNodeResponse(responseStream, responseProperties, { | ||
signal, | ||
@@ -936,2 +964,3 @@ ignoreBody, | ||
signal: new AbortController().signal, | ||
requestBodyLifetime, | ||
}) | ||
@@ -1049,4 +1078,2 @@ serviceController.callAsyncHooksUntil( | ||
const ABORTED_RESPONSE_PROPERTIES = {} | ||
const PROCESS_TEARDOWN_EVENTS_MAP = { | ||
@@ -1053,0 +1080,0 @@ SIGHUP: STOP_REASON_PROCESS_SIGHUP, |
150512
4769