Title: Timers `headersTimeout` and `requestTimeout` not restarted on keep-alive HTTP connections · Issue #58140 · nodejs/node · GitHub
Open Graph Title: Timers `headersTimeout` and `requestTimeout` not restarted on keep-alive HTTP connections · Issue #58140 · nodejs/node
X Title: Timers `headersTimeout` and `requestTimeout` not restarted on keep-alive HTTP connections · Issue #58140 · nodejs/node
Description: Version v23.10.0 Platform Tested on Linux and macOS: Linux rimmer 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux ---- Darwin holly 24.4.0 Darwin Kernel Version 24.4.0: Wed Mar 19 21:17:25 PDT 2025; ro...
Open Graph Description: Version v23.10.0 Platform Tested on Linux and macOS: Linux rimmer 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux ---- Darwin holly 24.4.0 Darwin Kernel Version ...
X Description: Version v23.10.0 Platform Tested on Linux and macOS: Linux rimmer 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux ---- Darwin holly 24.4.0 Darwin Kernel Version ...
Opengraph URL: https://github.com/nodejs/node/issues/58140
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Timers `headersTimeout` and `requestTimeout` not restarted on keep-alive HTTP connections","articleBody":"### Version\n\nv23.10.0\n\n### Platform\n\n```text\nTested on Linux and macOS:\n\nLinux rimmer 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux\n----\nDarwin holly 24.4.0 Darwin Kernel Version 24.4.0: Wed Mar 19 21:17:25 PDT 2025; root:xnu-11417.101.15~1/RELEASE_ARM64_T6020 arm64\n```\n\n### Subsystem\n\nhttp\n\n### What steps will reproduce the bug?\n\nCreate a minimal Node HTTP server with configured `headersTimeout`, `requestTimeout`, and `keepAliveTimeout` timers:\n```js\nimport { createServer } from 'node:http';\n\nconst server = createServer({\n connectionsCheckingInterval: 100,\n headersTimeout: 1000,\n requestTimeout: 4000,\n keepAliveTimeout: 5000\n}, (req, res) =\u003e {\n res.writeHead(404);\n res.end();\n});\n\nserver.listen(3000);\n```\n\nSend an HTTP request followed by an empty line (CRLF) to the server with the following Python client:\n```python\nimport socket\n\nclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\nclient.connect(('localhost', 3000))\n\n# Send a minimal HTTP request to the server\nclient.send(b'GET / HTTP/1.1\\r\\nHost: localhost:3000\\r\\nContent-Length: 0\\r\\n\\r\\n')\n\n# Print received responses until the server closes the connection\nwhile True:\n data = client.recv(1024 * 1024)\n if data == b'': # Server closed connection\n return\n print(data)\n # Send an empty line (CRLF)\n client.send(b'\\r\\n')\n```\nThe Python client never terminates because the server never closes the connection, despite having set all three timers (`headersTimeout`, `requestTimeout`, 'keepAliveTimeout`).\n\nThe empty line cancels the `keepAliveTimeout` timer but does not start either the `headersTimeout` or `requestTimeout` timers. The server will keep the connection open forever.\n\nA malicious client could force the server to keep the connection open indefinitely.\n\n### How often does it reproduce? Is there a required condition?\n\nEvery time. I believe this is a design flaw in NodeJS.\n\n### What is the expected behavior? Why is that the expected behavior?\n\nIf the NodeJS server has all the connection-related timers set, it should never be possible for the connection to transition into a state where none of the timers are in effect. This is what seems to happen when the client sends a CRLF after the first request on a connection. What I think happens is that NodeJS stops the `keepAliveTimeout` timer upon receiving the CRLF, but does not start `headersTimeout` and `requestTimeout` timers until the first line of the next message arrives from the client.\n\nI think NodeJS should NOT stop the `keepAliveTimeout` timer until it really receives the beginning of the following HTTP request. In other words, receiving just CRLF (empty line) should not stop the timer.\n\nAn alternative fix would be to keep the `keepAliveTimeout` as it is, and instead start the `headersTimeout` and `requestTimeout` timers upon receiving ANY communication from the client, including CRLF.\n\nIn any case, `headerTimeout` and `requestTimeout` timers should be started whenever the `keepAliveTimeout` is stopped. This prevents clients from tricking the server into a timer-less connection state.\n\n### What do you see instead?\n\nWhen the attached Python client is run against the attached trivial NodeJS server, the Python client never terminates, despite the server having all connection timers configured.\n\n### Additional information\n\n_No response_","author":{"url":"https://github.com/janakj","@type":"Person","name":"janakj"},"datePublished":"2025-05-03T15:02:33.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":2},"url":"https://github.com/58140/node/issues/58140"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:b4dc0cfc-c599-a789-ce0f-473c8b4922bc |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | D34E:320964:DFC22F:12C9C03:6A4C5EDE |
| html-safe-nonce | 5bec37a15abbbd0ee6382907b6531513db0c5b94b351686293973a32e9efe7fb |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEMzRFOjMyMDk2NDpERkMyMkY6MTJDOUMwMzo2QTRDNUVERSIsInZpc2l0b3JfaWQiOiIzMDQ3NzE1NzE4NTEyMjcxMDcwIiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 045b5026598c1189869696e7b6c04cadf8c93b0b23ee97e1b5a946dd7c6447e9 |
| hovercard-subject-tag | issue:3037454798 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/58140/issue_layout |
| twitter:image | https://opengraph.githubassets.com/13e3a7730339907a7c62319af3bca84af559838a24bc1cf56c91bfe38c56e896/nodejs/node/issues/58140 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/13e3a7730339907a7c62319af3bca84af559838a24bc1cf56c91bfe38c56e896/nodejs/node/issues/58140 |
| og:image:alt | Version v23.10.0 Platform Tested on Linux and macOS: Linux rimmer 6.1.0-18-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64 GNU/Linux ---- Darwin holly 24.4.0 Darwin Kernel Version ... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | janakj |
| hostname | github.com |
| expected-hostname | github.com |
| None | 3d11bb817438277de2a940854450e83a7d32b6aeb5014e9e6b00a6423900251c |
| turbo-cache-control | no-preview |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 14099438da5379150f15a2892474c7c7e6c0e55e |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width