| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Emit an error when a ServerResponse is closed after end() but before the finish event is emitted. Fixes: nodejs#50656 Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com> Assisted-by: openai:gpt-5.5
|
Review requested:
|
Sorry, something went wrong.
Expect ECONNRESET when an HTTP response pipeline is closed before the response finish event is emitted.
Update HTTP tests that intentionally close the client connection to expect ECONNRESET from ServerResponse.
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #63448 +/- ##
==========================================
- Coverage 90.06% 90.03% -0.03%
==========================================
Files 714 714
Lines 225918 225930 +12
Branches 42734 42735 +1
==========================================
- Hits 203464 203422 -42
- Misses 14232 14281 +49
- Partials 8222 8227 +5
... and 34 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Sorry, something went wrong.
|
We need to link this up to #63249, which actively aims to match the current ServerResponse behaviour that this PR is now changing (neither erroring nor finishing if clients abort). These behaviours need to match. Do we definitely want to change this though? In the referenced issue (#50656) back in 2023, @ronag said:
But that's not generally true. There's plenty of counter-examples, most notably calling destroy() on any writables always emits close without error or finish. Similarly, we don't error or finish stdin if a child process cleanly exits: const { spawn } = require('node:child_process');
const child = spawn('node', ['-e', 'process.exit(0)']);
child.stdin.on('finish', () => console.log('stdin: finish'));
child.stdin.on('error', () => console.log('stdin: error'));
child.stdin.on('close', () => console.log('stdin: close'));In effect, closed && !errored && !writableFinished is our standard 'clean abort' check. This is sufficiently standard that we check for it explicitly here as part of stream.finished() - automatically turning any such clean abort into a ERR_STREAM_PREMATURE_CLOSE if you're actively waiting for finished as a promise. In the specific HTTP case this is a very common occurrence (clients abort responses all the time) so this will produce a lot of errors. It's good that it's guarded just for listened events, but still it'll be a very visible behavioural change and it's hard to predict the results of that. Currently, AFAICT most implementations largely happily ignore this scenario unless they're doing something expensive, and they can already detect this manually if they want to. I'm very open to exploring this more, we could certainly improve stream & HTTP client abort behaviour, but this isn't the existing invariant, and I can see arguments on it both ways so we shouldn't just accept it as given. If we really do want to change it, we should definitely change the HTTP/2 PR above before that's merged too, and then potentially look at changing lots of other Node APIs as well afterwards to try to make this invariant consistent. |
Sorry, something went wrong.
|
Any thoughts on the above @trivikr or @ronag? #63249 is basically ready to merge, but I'm currently holding on that given the open question here about how we want these APIs to work in general. Personally, I think the current behaviour makes sense (clean abort of a writable = close without finish or error) and is consistent with the rest of our stream APIs. I'd suggest we stick with it, and potentially document better if there are gaps there. Let me know what you think? |
Sorry, something went wrong.
|
Ok, no specific objections here, so I'm going to go ahead with #63249 for now. Happy to discuss and revisit the close-without-finish/error behaviour later on if there's appetite, but right now I do think that's our standard behaviour, and it's a reasonable solution. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
This updates ServerResponse socket-close handling so an ended response that
never emitted finish is marked with ECONNRESET and emits error before
close when an error listener is present.
A regression test covers the case where the request socket is destroyed before
a deferred res.end(), which previously emitted only close.
Fixes: #50656
Assisted-by: openai:gpt-5.5