Repository navigation
[Bug] http.IncomingMessage.destroyed is true after payload read since v15.5.0 #36617
Description
Activity
Update:
I added
request.socket.destroyedto the test, and seems like that's the correct value I should check against, and it's consistent since v12.I rechecked the HTTP API doc, there's no mention of
http.IncomingMessage.destroyedexcept the classExtends: <stream.Readable>, so maybe this should be treated as undocumented behavior change instead of bug?The test branch, updated CI run, and run output: test-output-update.zip
And the updated main diff shows
socket.destroyedbehaves consistently:diff --git a/linux-12.txt b/linux-15.5.txt index ff2b77d..cab1b33 100644 --- a/linux-12.txt +++ b/linux-15.5.txt @@ -1,71 +1,71 @@ -## TEST ENV v12.20.0 linux x64 ## +## TEST ENV v15.5.0 linux x64 ## ## testNormalPost ## [httpServer] created - [httpRequest] created [httpServer] same socket true -[httpServer] soc/req/res.destroyed false false undefined -[httpServer] soc/req/res.destroyed read false false undefined +[httpServer] soc/req/res.destroyed false false false +[httpServer] soc/req/res.destroyed read false true false [httpServer] requestBuffer.length 320 -[httpServer] soc/req/res.destroyed wait128 false false undefined -[httpServer] soc/req/res.destroyed end false false undefined +[httpServer] soc/req/res.destroyed wait128 false true false +[httpServer] soc/req/res.destroyed end false true false - [httpRequest] same socket true -- [httpRequest] soc/req/res.destroyed false undefined false -- [httpRequest] soc/req/res.destroyed read false undefined false +- [httpRequest] soc/req/res.destroyed false false false +- [httpRequest] soc/req/res.destroyed read false false true - [httpRequest] responseBuffer.length 320 -[httpServer] soc/req/res.destroyed close true false undefined +[httpServer] soc/req/res.destroyed close true true true
- addedhttpIssues and PRs related to the http subsystem.Issues and PRs related to the http subsystem.
on Dec 26, 2020 I think the change was introduced with #33035.
cc: @nodejs/http
cc @nodejs/streams
I think the behavior is correct. Is this actually a problem?
From a server point of view when:
- the
request/IncommingMessagehas received all header & payload,
sorequest.destroyedcan betrue, orfalse(old behavior) - the
response/ServerResponsestill waits for statusCode & payload,
soresponse.destroyedisfalse
But the underlying
response.socket/request.socketis the samesocket, andsocket.destroyedisfalse, for me the main confusing point is:- with old behavior, I think both as a wrapper of
socket, so each delegates the Readable/Writable part of Duplex, thus both reportsocket.destroyedto befalse - the new behavior is more like the
requestis a new Readable forked from thesocket, sorequest.destroyedcan be independent of thesocket.destroyedvalue
This behavior change caused a concept change for me, so maybe it should be marked a break change?
Or my concept for the old behavior is wrong.For me the code fix is to drop some unused
request.destroyed, and change others torequest.socket.destroyed.Update:
The doced property to check should beIncommingMessage.aborted/complete, I think I choose to checkStream.destroyedfor it seemed to work for bothrequest(IncommingMessage/ClientRequest).Reacted by aenp@viessmann.net- the
Don't use:
IncommingMessage.abortedIncommingMessage.completeIncommingMessage.socketIncommingMessage.on('aborted')
IMO all of them should be deprecated.
Do use:
IncommingMessage.destroyedIncommingMessage.readableEndedIncommingMessage.on('error')
Reacted by Drthe new behavior is more like the request is a new Readable forked from the socket.
This has always been true since Readable was introduced. IncomingMessage is a new Readable that gets its data from the underlining socket. This matches the reality as the same socket is reused multiple times in case of keep-alive.
I consider the new behavior to be less surprising given the current implementation.
In hindsight, #33035 could have been flagged semver-major - I'm not sure it's worth a revert in v15. Note that it has already flagged to not backport it to LTS lines.
Thanks for the explanation!
With my confusion solved and code fix done, I think I should close this issue, if this do not affect more code repo.Should we add some of the clarification for
IncomingMessageto the doc?Should we add some of the clarification for IncomingMessage to the doc?
PR welcome. Feel free to close issue.
PR added: #36641
And sorry for the late response.6 remaining items
- added a commit that references this issue
on Dec 30, 2020 - added a commit that references this issue
on Jan 12, 2021 - added a commit that references this issue
on Sep 27, 2021 - added a commit that references this issue
on Oct 9, 2021 - added a commit that references this issue
on Oct 12, 2021
What steps will reproduce the bug?
Can test with the following script: (added two more test on closing)
[test-nodejs-v15.5.0-http-stream-destroyed.js]
The GitHub Action run for 12/14/15.4/15.5 with linux/win32/darwin: https://github.com/dr-js/dr-js/actions/runs/442120897 (should last for 60 days)
The run output: test-output.zip
The main difference between 15.4 and 15.5 is: (used linux output)
How often does it reproduce? Is there a required condition?
Should always reproduce in v15.5.0.
What is the expected behavior?
No behavior change in minor release.
What do you see instead?
The issue is in Nodejs v15.5.0, the
http.IncomingMessage.destroyedor therequest.destroyedin serverresponse, is settrueas soon as the request payload is retrieved.In earlier versions, the change to
truewill not be sooner thanresponse.destroyed.Additional information
The new behavior makes sense in some way.
But when thinking the
requestandresponseas wrapper of the same underlyingsocket, I think both should bedestroyed(or not) at the same time.And
response.destroyedisundefinedin Nodejs v12, so onlyrequest.destroyedis checkable.For my usage, I check the
request.destroyedin serverresponseto see if the socket is still "alive", and this behavior change make the check pass and main logic/result get skipped.