Title: Socket timeouts are checked before pending incoming requests are assigned sockets, causing `ECONNRESET`s · Issue #43456 · nodejs/node · GitHub
Open Graph Title: Socket timeouts are checked before pending incoming requests are assigned sockets, causing `ECONNRESET`s · Issue #43456 · nodejs/node
X Title: Socket timeouts are checked before pending incoming requests are assigned sockets, causing `ECONNRESET`s · Issue #43456 · nodejs/node
Description: Version 14.19.1 and others Platform Linux qa01-api-1 5.13.0-1024-gcp #29~20.04.1-Ubuntu SMP Thu Apr 14 23:15:00 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Subsystem net What steps will reproduce the bug? Process 1: const crypto = require("c...
Open Graph Description: Version 14.19.1 and others Platform Linux qa01-api-1 5.13.0-1024-gcp #29~20.04.1-Ubuntu SMP Thu Apr 14 23:15:00 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Subsystem net What steps will reproduce the b...
X Description: Version 14.19.1 and others Platform Linux qa01-api-1 5.13.0-1024-gcp #29~20.04.1-Ubuntu SMP Thu Apr 14 23:15:00 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Subsystem net What steps will reproduce the b...
Opengraph URL: https://github.com/nodejs/node/issues/43456
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Socket timeouts are checked before pending incoming requests are assigned sockets, causing `ECONNRESET`s","articleBody":"### Version\n\n14.19.1 and others\n\n### Platform\n\nLinux qa01-api-1 5.13.0-1024-gcp #29~20.04.1-Ubuntu SMP Thu Apr 14 23:15:00 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux\n\n### Subsystem\n\nnet\n\n### What steps will reproduce the bug?\n\n##### Process 1:\r\n```js\r\nconst crypto = require(\"crypto\");\r\nconst http = require('http');\r\n\r\nconst a = Buffer.alloc(1e8);\r\nfunction doWork(ms) {\r\n\tconst start = Date.now();\r\n\twhile (Date.now() - start \u003c ms) {\r\n\t\tcrypto.randomFillSync(a);\r\n\t}\r\n}\r\n\r\nlet socketId = 0;\r\nconst server = http.createServer((req, res) => {\r\n\tif (req.socket.__socketId === undefined) {\r\n\t\treq.socket.__socketId = socketId++;\r\n\t\tconst st = req.socket.setTimeout;\r\n\t\treq.socket.setTimeout = (a, b) => {\r\n\t\t\tconsole.log(new Date(), \"Setting timeout on socket \", req.socket.__socketId);\r\n\t\t\treturn st.call(req.socket, a, b);\r\n\t\t};\r\n\t\treq.socket.once(\"close\", () => console.log(new Date(), `Closing ${req.socket.__socketId}`));\r\n\t}\r\n\r\n\tconst reqId = req.headers[\"x-request-id\"];\r\n\tconsole.log(new Date(), `[${reqId}] got request on socket ${req.socket.__socketId}`);\r\n\tdoWork(1500);\r\n\tconsole.log(new Date(), `[${reqId}] sending response`);\r\n\tres.end('hello world\\r\\n');\r\n});\r\n\r\nserver.keepAliveTimeout = 1000; // shortened from default 5000 to make test faster\r\n\r\nserver.listen(8080, '127.0.0.1');\r\n```\r\n\r\n##### Process 2:\r\n```js\r\nconst http = require('http');\r\n\r\nconst keepAliveAgent = new http.Agent({\r\n\tkeepAlive: true,\r\n\tmaxSockets: 2\r\n});\r\n\r\nlet socketId = 0;\r\n\r\nfor (let i = 0; i \u003c 3; i++) {\r\n\tconst req = http.request({\r\n\t\tagent: keepAliveAgent,\r\n\t\thost: '127.0.0.1',\r\n\t\tport: 8080,\r\n\t\tmethod: 'GET',\r\n\t\theaders: {\r\n\t\t\t\"X-Request-Id\": `${i}`\r\n\t\t}\r\n\t});\r\n\r\n\treq.end();\r\n\r\n\treq.once(\"socket\", socket => {\r\n\t\tif (socket.__id === undefined) socket.__id = socketId++;\r\n\t\tconsole.log(new Date(), `[${i}] assigned socket ${socket.__id}`);\r\n\t});\r\n\r\n\treq.on('response', function _onResponse(res) {\r\n\t\tres.on('data', function _onData(chunk) {});\r\n\r\n\t\tres.on('end', function _doneResponse() {\r\n\t\t\tconsole.log(new Date(), `[${i}] res.end`);\r\n\t\t});\r\n\t});\r\n\r\n\treq.on('error', function _onError(err) {\r\n\t\tconsole.log(new Date(), `[${i}] req.error`, err);\r\n\t});\r\n}\r\n```\n\n### How often does it reproduce? Is there a required condition?\n\n100%\n\n### What is the expected behavior?\n\nAll three requests should succeed.\n\n### What do you see instead?\n\nThe server logs will look like this:\r\n```\r\n2022-06-16T22:37:50.307Z [0] got request on socket 0\r\n2022-06-16T22:37:51.809Z [0] sending response\r\n2022-06-16T22:37:51.813Z Setting timeout on socket 0\r\n2022-06-16T22:37:51.814Z [1] got request on socket 1\r\n2022-06-16T22:37:53.317Z [1] sending response\r\n2022-06-16T22:37:53.318Z Setting timeout on socket 1\r\n2022-06-16T22:37:53.320Z Closing 0\r\n2022-06-16T22:37:53.323Z Closing 1\r\n```\r\n\r\nand client\r\n```\r\n2022-06-16T22:37:50.303Z [0] assigned socket 0\r\n2022-06-16T22:37:50.305Z [1] assigned socket 1\r\n2022-06-16T22:37:51.814Z [0] res.end\r\n2022-06-16T22:37:51.815Z [2] assigned socket 0\r\n2022-06-16T22:37:53.318Z [1] res.end\r\n2022-06-16T22:37:53.321Z [2] req.error Error: read ECONNRESET\r\n at TCP.onStreamRead (internal/stream_base_commons.js:209:20) {\r\n errno: -104,\r\n code: 'ECONNRESET',\r\n syscall: 'read'\r\n}\r\n```\r\n\r\nNotice: req[2] is assigned a socket at 51.815, just after req[1] is received by the server. Server socket[0] is closed at 53.320, just after the blocking work on req[2], even though there's a request that could be assigned to that socket. I think what's happening is that timers are checked *before* pending incoming messages are assigned sockets.\n\n### Additional information\n\n`ECONNRESET`s due to Keep-Alive races need to be handled by clients (see #20256, #38890 and several others), but I think this particular scenario can be fixed to avoid a subset of them.","author":{"url":"https://github.com/zbjornson","@type":"Person","name":"zbjornson"},"datePublished":"2022-06-16T22:46:37Z","url":"https://github.com/nodejs/node/issues/43456"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| 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:cbe63ff5-98d6-db3e-33b8-9f1728d755ac |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | D892:26D535:627421:80835A:6AED2DB9 |
| html-safe-nonce | b9437a513af8f17f359c6279f6baf0d86135d2b0f039546418d52482804f9203 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJEODkyOjI2RDUzNTo2Mjc0MjE6ODA4MzVBOjZBRUQyREI5IiwidmlzaXRvcl9pZCI6IjU3MjUwMjcwNDc1NTM0NDUzMDUiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | f445fb5288395de445622789981623aea6e18e50737d12585d75eca901fae81c |
| hovercard-subject-tag | issue:1274178433 |
| 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/43456/issue_layout |
| twitter:image | https://opengraph.githubassets.com/084ee098dd4802331b2d378f5a902ee0bf08c629cda6f1deb7b36a972d388d98/nodejs/node/issues/43456 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/084ee098dd4802331b2d378f5a902ee0bf08c629cda6f1deb7b36a972d388d98/nodejs/node/issues/43456 |
| og:image:alt | Version 14.19.1 and others Platform Linux qa01-api-1 5.13.0-1024-gcp #29~20.04.1-Ubuntu SMP Thu Apr 14 23:15:00 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Subsystem net What steps will reproduce the b... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | zbjornson |
| hostname | github.com |
| expected-hostname | github.com |
| None | e51d0b7b5ab34b1688c05b54c3ed17b8658b2fa9bca973a9c740683ccb12cf4a |
| 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 | 59ebb7b901cc154ecc179a44f266fcf69f343b30 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width