@jsenv/server 10.0.0 → 10.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/server",
3
- "version": "10.0.0",
3
+ "version": "10.0.4",
4
4
  "description": "Write your Node.js server using pure functions",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -46,7 +46,7 @@
46
46
  "postpublish": "node ./script/publish/restore_postinstall.mjs"
47
47
  },
48
48
  "dependencies": {
49
- "@jsenv/abort": "3.1.2",
49
+ "@jsenv/abort": "4.1.1",
50
50
  "@jsenv/logger": "4.0.1",
51
51
  "node-fetch": "2.6.2"
52
52
  },
@@ -55,15 +55,15 @@
55
55
  "@babel/eslint-parser": "7.16.0",
56
56
  "@jsenv/assert": "2.3.2",
57
57
  "@jsenv/codecov-upload": "3.5.0",
58
- "@jsenv/core": "23.2.1",
58
+ "@jsenv/core": "23.4.2",
59
59
  "@jsenv/eslint-config": "16.0.8",
60
- "@jsenv/filesystem": "2.3.1",
60
+ "@jsenv/filesystem": "2.5.1",
61
61
  "@jsenv/github-release-package": "1.2.3",
62
62
  "@jsenv/https-local": "1.0.2",
63
63
  "@jsenv/importmap-eslint-resolver": "5.1.2",
64
64
  "@jsenv/importmap-node-module": "2.4.1",
65
65
  "@jsenv/package-publish": "1.6.2",
66
- "@jsenv/performance-impact": "2.2.0",
66
+ "@jsenv/performance-impact": "2.2.1",
67
67
  "@jsenv/prettier-check-project": "5.6.1",
68
68
  "eslint": "7.32.0",
69
69
  "eslint-plugin-import": "2.25.2",
package/src/fetch_url.js CHANGED
@@ -4,10 +4,7 @@ import { Agent } from "node:https"
4
4
  import nodeFetch, { Response } from "node-fetch"
5
5
 
6
6
  import { fetchFileSystem } from "./fetch_filesystem.js"
7
- import {
8
- isFileHandle,
9
- fileHandleToReadableStream,
10
- } from "./internal/responseBody.js"
7
+ import { isFileHandle, fileHandleToReadableStream } from "./internal/body.js"
11
8
 
12
9
  export const fetchUrl = async (
13
10
  url,
@@ -4,7 +4,7 @@ import { createReadStream } from "node:fs"
4
4
  import { isObservable, observableFromValue } from "./observable.js"
5
5
  import { nodeStreamToObservable } from "./nodeStreamToObservable.js"
6
6
 
7
- export const normalizeResponseBodyMethods = (body) => {
7
+ export const normalizeBodyMethods = (body) => {
8
8
  if (isObservable(body)) {
9
9
  return {
10
10
  asObservable: () => body,
@@ -6,21 +6,7 @@ import { createObservable } from "./observable.js"
6
6
  const readableStreamLifetimeInSeconds = 120
7
7
 
8
8
  export const nodeStreamToObservable = (nodeStream) => {
9
- if (nodeStream instanceof Readable) {
10
- // safe measure, ensure the readable stream gets
11
- // used in the next ${readableStreamLifetimeInSeconds} otherwise destroys it
12
- const timeout = setTimeout(() => {
13
- console.warn(
14
- `readable stream still unused after ${readableStreamLifetimeInSeconds} seconds -> destroying it to release file handle`,
15
- )
16
- nodeStream.destroy()
17
- }, readableStreamLifetimeInSeconds * 1000)
18
- onceReadableStreamUsedOrClosed(nodeStream, () => {
19
- clearTimeout(timeout)
20
- })
21
- }
22
-
23
- return createObservable(({ next, error, complete }) => {
9
+ const observable = createObservable(({ next, error, complete }) => {
24
10
  const cleanup = () => {
25
11
  nodeStream.removeListener("data", next)
26
12
  nodeStream.removeListener("error", error)
@@ -37,6 +23,28 @@ export const nodeStreamToObservable = (nodeStream) => {
37
23
 
38
24
  return cleanup
39
25
  })
26
+
27
+ if (nodeStream instanceof Readable) {
28
+ // safe measure, ensure the readable stream gets
29
+ // used in the next ${readableStreamLifetimeInSeconds} otherwise destroys it
30
+ const timeout = setTimeout(() => {
31
+ process.emitWarning(
32
+ `Readable stream not used after ${readableStreamLifetimeInSeconds} seconds. It will be destroyed to release ressources`,
33
+ {
34
+ CODE: "READABLE_STREAM_TIMEOUT",
35
+ // url is for http client request
36
+ detail: `path: ${nodeStream.path}, fd: ${nodeStream.fd}, url: ${nodeStream.url}`,
37
+ },
38
+ )
39
+ nodeStream.destroy()
40
+ }, readableStreamLifetimeInSeconds * 1000)
41
+ observable.timeout = timeout
42
+ onceReadableStreamUsedOrClosed(nodeStream, () => {
43
+ clearTimeout(timeout)
44
+ })
45
+ }
46
+
47
+ return observable
40
48
  }
41
49
 
42
50
  const onceReadableStreamUsedOrClosed = (readableStream, callback) => {
@@ -1,7 +1,7 @@
1
1
  import { Http2ServerResponse } from "node:http2"
2
2
  import { raceCallbacks } from "@jsenv/abort"
3
3
 
4
- import { normalizeResponseBodyMethods } from "./responseBody.js"
4
+ import { normalizeBodyMethods } from "./body.js"
5
5
 
6
6
  export const populateNodeResponse = async (
7
7
  responseStream,
@@ -9,7 +9,7 @@ export const populateNodeResponse = async (
9
9
  { signal, ignoreBody, onAbort, onError, onHeadersSent, onEnd } = {},
10
10
  ) => {
11
11
  body = await body
12
- const bodyMethods = normalizeResponseBodyMethods(body)
12
+ const bodyMethods = normalizeBodyMethods(body)
13
13
 
14
14
  if (signal.aborted) {
15
15
  bodyMethods.destroy()
@@ -5,7 +5,7 @@ import { createDetailedMessage, createLogger } from "@jsenv/logger"
5
5
  import {
6
6
  Abort,
7
7
  raceProcessTeardownEvents,
8
- createCallbackListCalledOnce,
8
+ createCallbackListNotifiedOnce,
9
9
  } from "@jsenv/abort"
10
10
 
11
11
  import { createPolyglotServer } from "./internal/server-polyglot.js"
@@ -164,7 +164,7 @@ export const startServer = async ({
164
164
  // we can proceed to create a stop function to stop it gacefully
165
165
  // and add a request handler
166
166
 
167
- const stopCallbackList = createCallbackListCalledOnce()
167
+ const stopCallbackList = createCallbackListNotifiedOnce()
168
168
  stopCallbackList.add(({ reason }) => {
169
169
  logger.info(`${serverName} stopping server (reason: ${reason})`)
170
170
  })
@@ -178,7 +178,7 @@ export const startServer = async ({
178
178
  })
179
179
  const stop = memoize(async (reason = STOP_REASON_NOT_SPECIFIED) => {
180
180
  status = "stopping"
181
- await Promise.all(stopCallbackList.call({ reason }))
181
+ await Promise.all(stopCallbackList.notify({ reason }))
182
182
  status = "stopped"
183
183
  stoppedResolve(reason)
184
184
  })
@@ -636,6 +636,14 @@ export const startServer = async ({
636
636
  if (receiveRequestOperation.signal.aborted) {
637
637
  return
638
638
  }
639
+
640
+ // the node request readable stream is never closed because
641
+ // the response headers contains "connection: keep-alive"
642
+ // In this scenario we want to disable READABLE_STREAM_TIMEOUT warning
643
+ if (responseProperties.headers.connection === "keep-alive") {
644
+ clearTimeout(request.body.timeout)
645
+ }
646
+
639
647
  await sendResponse({
640
648
  signal: sendResponseOperation.signal,
641
649
  request,