-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
http: OutgoingMessage change writable after end #14024
Conversation
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: var http = require('http')
const net = require('net')
var server = http.createServer(function(req, res) {
res.write('hello ')
res.end('write after ')
res._send('end!')
})
server.listen(25000, () => {
var data = 'GET / HTTP/1.0\r\nContent-Type: text/plain\r\nContent-Length: 0\r\n\r\n'
const client = net.createConnection({ port: 25000}, () => {
client.write(data)
})
client.on('data', (data) => {
console.log(data.toString())
})
client.on('end', () => {
server.close()
})
}) |
@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! |
@Kasher - does this fail for you? (where data is a huge file in the disc) const http = require('http')
const fs = require('fs')
const server = 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, () => {
const client = http.get('http://localhost:25000/', (res) => {
res.pipe(process.stdout)
})
}) |
641175f
to
554d987
Compare
@gireeshpunathil Thanks again. |
554d987
to
28b4f4d
Compare
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.
28b4f4d
to
20b6326
Compare
thank you! |
Thanks for approving this @gireeshpunathil . |
@Kasher - thanks for checking. Let us wait for one or two more reviews. |
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?
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?
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
pipe
on 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)
@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 :) |
@Kasher looking at request code, the problem you are facing does not come down from OutgoingMessage, but from the |
@mcollina Thanks, but I'm not sure I fully understand what you mean. I pipe a Did I miss something? Thanks again :) |
I think your request example is calling |
@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. |
@Kasher in this line https://github.com/nodejs/node/pull/14024/files#diff-f48f06a77b4005be724d14b42ab7f87dR9, you are testing the reverse of that |
@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. |
Let's separate the .pipe() change to the .writable = false change as the first would be semver-major. |
@mcollina , I've made the required changes. Thanks! |
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
.
@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?
This is ok to be backported to 6.x when the time is due. |
Thank you very much @mcollina @TimothyGu ! |
@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: #14024 Fixes: #14023 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
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: #14024 Fixes: #14023 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]>
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#14024 Fixes: 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: #14024 Fixes: #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: #14024 Fixes: #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#14024 Fixes: 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#14024 Fixes: 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]>
Looks like this never made it to v4.x or 6.x phew |
This was close. |
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
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.
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
http
Issue: #14023