Skip to content
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: don't write empty data on req/res end() #41116

Merged
merged 1 commit into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -878,9 +878,10 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) {

if (this._hasBody && this.chunkedEncoding) {
this._send('0\r\n' + this._trailer + '\r\n', 'latin1', finish);
} else {
// Force a flush, HACK.
} else if (!this._headerSent || this.writableLength || chunk) {
this._send('', 'latin1', finish);
} else {
process.nextTick(finish);
Copy link
Member

@lpinca lpinca Dec 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem I see here is that the 'finish' event might be emitted before buffered data is flushed and similarly the outgoingMessage.end() callback might be called before previous outgoingMessage.write() callbacks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, but I think at this point there should not buffered data and a consecutive call to end() should not reach here, or am I missing smthg? Thanks

Copy link
Member

@lpinca lpinca Dec 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about adding a check for socket.writableLength to the above else if branch?

...
} else if (!this._headerSent || this.outputSize || chunk || this.socket?.writableLength) {
  this._send('', 'latin1', finish);
} else {
...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good. We might even use OutgoingMessage.writableLength directly. Thanks!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lpinca pushed the changes

Copy link
Member

@lpinca lpinca Dec 8, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think at this point there should not buffered data and a consecutive call to end() should not reach here

I think you are right because the transfer encoding would be chunked but I'm not sure if there are cases where that path can be taken anyway. For example in #41062 the user specifies the Content-Length header so the chunked encoding is not used.

}

if (this.socket) {
Expand Down
4 changes: 2 additions & 2 deletions test/parallel/test-http-sync-write-error-during-continue.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ Connection: close
// parser.finish() to be called while we are here in the 'continue'
// callback, which is inside a parser.execute() call.

assert.strictEqual(chunk.length, 0);
assert.strictEqual(chunk.length, 4);
clientSide.destroy(new Error('sometimes the code just doesn’t work'), cb);
});
req.on('error', common.mustCall());
req.end();
req.end('data');

sync = false;
}));
Expand Down