Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34.2k
http: OutgoingMessage change writable after end#14024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
http: OutgoingMessage change writable after end #14024
Uh oh!
There was an error while loading. Please reload this page.
Conversation
gireeshpunathil commented Jul 2, 2017
Thank you! Does it make sense to add another test case which asserts that response refuses to write after end()? While your test is fine, it looks to me more directed, rather than testing the function of Outgoing Message. Here is a code which writes after end() with the existing code - you may tune this into a test case: varhttp=require('http')constnet=require('net')varserver=http.createServer(function(req,res){res.write('hello ')res.end('write after ')res._send('end!')})server.listen(25000,()=>{vardata='GET / HTTP/1.0\r\nContent-Type: text/plain\r\nContent-Length: 0\r\n\r\n'constclient=net.createConnection({port: 25000},()=>{client.write(data)})client.on('data',(data)=>{console.log(data.toString())})client.on('end',()=>{server.close()})}) |
Kasher commented Jul 2, 2017
@gireeshpunathil , Thanks for your response. I totally agree with what you wrote (my test is quite too strict to the case I fixed), but I wasn't able to write a less-strict test that reproduces this issue. Unfortunately, the code you attached doesn't reproduce the issue. When I tried to execute the code you wrote, I got the following result: Which is fine in my opinion. Any call to Any suggestion on how to reproduce this consistently will be much appreciated. I would be more than happy to write a less-strict test that reproduces this :) Thanks again! |
gireeshpunathil commented Jul 3, 2017
@Kasher - does this fail for you? (where data is a huge file in the disc) consthttp=require('http')constfs=require('fs')constserver=http.createServer((req,res)=>{req.on('end',()=>{console.log("Server: Client request was closed, closing server's request and client's response.");res.end()})fs.createReadStream('data').pipe(res)})server.listen(25000,()=>{constclient=http.get('http://localhost:25000/',(res)=>{res.pipe(process.stdout)})}) |
641175f to 554d987CompareKasher commented Jul 7, 2017
@gireeshpunathil Thanks again. |
554d987 to 28b4f4dCompareWhen an OutgoingMessage is closed (for example, using the `end` method), its 'writable' property should be changed to false - since it is not writable anymore. The 'writable' property should have the opposite value of the 'finished' property.
28b4f4d to 20b6326Comparegireeshpunathil commented Jul 7, 2017
thank you! |
Kasher commented Jul 8, 2017
Thanks for approving this @gireeshpunathil . |
gireeshpunathil commented Jul 9, 2017
@Kasher - thanks for checking. Let us wait for one or two more reviews. |
mcollina left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot. I think this should be fixed, but I also think that OutgoingMessage.pipe() should throw an exception.
| port: server.address().port, | ||
| method: 'GET', | ||
| path: '/' | ||
| }).end(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What http.request returns is an OutgoingMessage as well, we might want to change that it there as well.
| // If we got here - 'write after end' wasn't raised and the test passed. | ||
| setTimeout(() => server.close(), 10); | ||
| }, 10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to be very flaky. Can you refactor the test to not rely on timers?
KasherJul 10, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing. I totally agree with you. Basically I needed setTimeout with a timeout of zero (or process.nextTick). I'll change to one of these.
| const server = http.createServer(common.mustCall(function(req, res){ | ||
| console.log('Got a request, piping an OutgoingMessage to it.'); | ||
| const outgointMessage = new OutgoingMessage(); | ||
| outgointMessage.pipe(res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OutgoingMessage should really be write-only, and not be piped. The fact that OutgoingMessage has a pipe method is related to Node.js history, and it should not be used. Why Does this use case matter to you?
KasherJul 10, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. OutgoingMessage is just an example of a type that inherits from Stream. A better practice would be to create a new type that inherits from Stream and use this in the test. I would change this.
I chose OutgoingMessage since if I pipe from it, the outcome will be very similar to what happens when I use request npm .
I guess this should either be disabled (by throwing an exception as you suggested), or it should work.
Here is what they do in request npm:
- They call to
pipeon a type that inherits fromStream(the type is calledRequest):
https://github.com/request/request/blob/master/request.js#L1489 - Upon data on an
IncomingResponse, they emit this on theRequest:
https://github.com/request/request/blob/master/request.js#L1082
For more detailed explanation you can read my reply: #14024 (comment)
Kasher commented Jul 10, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
@mcollina, Thank you very much for you review. (taken from their main page). We can see from their source code, that when I call Then, they create a In this method, we can see that they register to the reponse's This might raise the I will try to refactor my unit tests so they won't use However, I guess their current implementation shouldn't cause the Should I throw an exception in Thanks again, waiting for your reply for further instructions :) |
mcollina commented Jul 10, 2017
@Kasher looking at request code, the problem you are facing does not come down from OutgoingMessage, but from the |
Kasher commented Jul 10, 2017
@mcollina Thanks, but I'm not sure I fully understand what you mean. I pipe a Did I miss something? Thanks again :) |
mcollina commented Jul 10, 2017
I think your request example is calling |
Kasher commented Jul 10, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
@mcollina Thanks again, but I'm not sure I get it. I use In this call, Later on, when data is emitted on in the stack supports this). In addition, when I added the fix, the error is never thrown (in other words, I couldn't reproduce this issue). What did I miss? Thanks. |
mcollina commented Jul 10, 2017
@Kasher in this line https://github.com/nodejs/node/pull/14024/files#diff-f48f06a77b4005be724d14b42ab7f87dR9, you are testing the reverse of that |
Kasher commented Jul 10, 2017 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
@mcollina Oh, you're talking about the test! Regarding the test - You're right. Should I add throw an exception in Thanks again, and sorry for the inconvenience. |
mcollina commented Jul 10, 2017
Let's separate the .pipe() change to the .writable = false change as the first would be semver-major. |
Kasher commented Jul 11, 2017
@mcollina , I've made the required changes. Thanks! |
mcollina left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There a bit more nits on the test, but this looks good.
LGTM when those are solved.
| assert(res.writable, 'Res should be writable when it is received \ | ||
| and opened.'); | ||
| assert(!res.finished, 'Res shouldn\'t be finished when it is received \ | ||
| and opened.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use assert.strictEqual without a message here and in the rest of the tests?
| const common = require('../common'); | ||
| const assert = require('assert'); | ||
| const http = require('http'); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you add a little comment explaining what this test is about here?
| myStream.pipe(res); | ||
| process.nextTick(() =>{ | ||
| console.log('Closing response.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you remove the console.log statements?
| // If we got here - 'write after end' wasn't raised and the test passed. | ||
| process.nextTick(() => server.close()); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also add common.mustCall on both the process.nextTick.
Kasher commented Jul 11, 2017
@mcollina done :) |
| const http = require('http'); | ||
| const util = require('util'); | ||
| const stream = require('stream'); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a little explanation here as well?
mcollina commented Jul 30, 2017
This is ok to be backported to 6.x when the time is due. |
Kasher commented Jul 30, 2017
Thank you very much @mcollina@TimothyGu ! |
MylesBorins commented Aug 16, 2017
@mcollina what about v4.x? would you consider this a major bug? |
When an OutgoingMessage is closed (for example, using the `end` method), its 'writable' property should be changed to false - since it is not writable anymore. The 'writable' property should have the opposite value of the 'finished' property. PR-URL: #14024Fixes: #14023 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
mcollina commented Aug 16, 2017
I'm ok with backporting to 6.x and 4.x. |
When an OutgoingMessage is closed (for example, using the `end` method), its 'writable' property should be changed to false - since it is not writable anymore. The 'writable' property should have the opposite value of the 'finished' property. PR-URL: #14024Fixes: #14023 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
mcollina commented Sep 14, 2017
Unfortunately we need to revert this: #15404. |
Setting writable = false in IncomingMessage.end made some errors being swallowed in some very popular OSS libraries that we must support. This commit add some of those use cases to the tests, so we avoid further regressions. We should reevaluate how to set writable = false in IncomingMessage in a way that does not break the ecosystem. See: nodejs#14024Fixes: nodejs#15029
Setting writable = false in IncomingMessage.end made some errors being swallowed in some very popular OSS libraries that we must support. This commit add some of those use cases to the tests, so we avoid further regressions. We should reevaluate how to set writable = false in IncomingMessage in a way that does not break the ecosystem. See: #14024Fixes: #15029 PR-URL: #15404 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
Setting writable = false in IncomingMessage.end made some errors being swallowed in some very popular OSS libraries that we must support. This commit add some of those use cases to the tests, so we avoid further regressions. We should reevaluate how to set writable = false in IncomingMessage in a way that does not break the ecosystem. See: #14024Fixes: #15029 PR-URL: #15404 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
Setting writable = false in IncomingMessage.end made some errors being swallowed in some very popular OSS libraries that we must support. This commit add some of those use cases to the tests, so we avoid further regressions. We should reevaluate how to set writable = false in IncomingMessage in a way that does not break the ecosystem. See: nodejs/node#14024Fixes: nodejs/node#15029 PR-URL: nodejs/node#15404 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
Setting writable = false in IncomingMessage.end made some errors being swallowed in some very popular OSS libraries that we must support. This commit add some of those use cases to the tests, so we avoid further regressions. We should reevaluate how to set writable = false in IncomingMessage in a way that does not break the ecosystem. See: nodejs/node#14024Fixes: nodejs/node#15029 PR-URL: nodejs/node#15404 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
MylesBorins commented Oct 16, 2017
Looks like this never made it to v4.x or 6.x phew |
mcollina commented Oct 16, 2017
This was close. |
missbruni commented Apr 24, 2018
How should we be solving this issue? Is there another resolution? I am having the same bug when trying to pipe streams. |

When an OutgoingMessage is closed (for example, using the
endmethod), its 'writable' property should be changed to false - since it
is not writable anymore. The 'writable' property should have the
opposite value of the 'finished' property.
Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passesAffected core subsystem(s)
http
Issue: #14023