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: Stream#write with an empty buffer does not call callback #22066

Closed
RubenVerborgh opened this issue Aug 1, 2018 · 18 comments
Closed

http: Stream#write with an empty buffer does not call callback #22066

RubenVerborgh opened this issue Aug 1, 2018 · 18 comments
Labels
http Issues or PRs related to the http subsystem.

Comments

@RubenVerborgh
Copy link
Contributor

When writing an empty buffer, Stream#write does not call the callback: https://github.com/nodejs/node/blob/v10.7.0/lib/_http_outgoing.js#L608

Is this intended? If so, this should probably be mentioned explicitly in the documentation.
If not, we might want to fix this.

@RubenVerborgh
Copy link
Contributor Author

Note: it only hangs on empty buffers, not empty strings.

@addaleax addaleax added the http Issues or PRs related to the http subsystem. label Aug 1, 2018
@addaleax addaleax changed the title Stream#write with an empty buffer does not call callback http: Stream#write with an empty buffer does not call callback Aug 1, 2018
@addaleax
Copy link
Member

addaleax commented Aug 1, 2018

I think this has come up before, in some form? It would be a breaking change but probably worth it.

/cc @nodejs/http

@amitzur
Copy link
Contributor

amitzur commented Aug 1, 2018

I reported the original issue to axios. I now wonder how this code does work (or in other words how to reproduce with a minimal example):

process.stdout.write(Buffer.from(''), err => {
  console.log('done!', err);
});

output:

done! undefined

@RubenVerborgh
Copy link
Contributor Author

@amitzur The code that returns early with an empty buffer is specific to HTTP, not to writable streams in general. Your example doesn't use HTTP, so that's why it works.

@amitzur
Copy link
Contributor

amitzur commented Aug 1, 2018

ahh, right right.
It hangs also on empty strings. Which makes sense from the code at https://github.com/nodejs/node/blob/v10.7.0/lib/_http_outgoing.js#L608 but it doesn't explain why follow-redirects doesn't hang on empty strings.

For example:

require('http')
  .request({
    hostname: 'www.google.com',
  })
  .write('', err => {
    console.log('done', err);
  });

@mcollina
Copy link
Member

Fixed in 413a7e1.

@RubenVerborgh
Copy link
Contributor Author

It does not explicitly mention that the callback will not be called, however. So this doesn't address my original issue.

Also, please note that

it only hangs on empty buffers, not empty strings.

so this is a problem as well.

@RubenVerborgh
Copy link
Contributor Author

@mcollina Do I create a new issue?

@mcollina
Copy link
Member

It does not explicitly mention that the callback will not be called, however. So this doesn't address my original issue.

@RubenVerborgh I think it does 413a7e1#diff-69e1ac0b5bfc06e74f2c1ab7b062c6afR753 - When I read that, I assume nothing means nothing, including not calling the callback.

Would you like to send a PR with the text that would be ok for you?

it only hangs on empty buffers, not empty strings.

Can you please upload a snippet that show this? The check and the logic is identical, so I don't see how it's possible that the two call will behave differently Buffer.from('').length and ''.length both return 0.

@RubenVerborgh
Copy link
Contributor Author

Would you like to send a PR with the text that would be ok for you?

Proposal in #22461.

That said, spelling things out explicitly begs the question whether it is a good design decision to have this exception for empty buffers. Basically, every call to write with a callback will need to be surrounded by an if statement if the caller did not create the buffer itself.

@RubenVerborgh
Copy link
Contributor Author

Can you please upload a snippet that show this?

I thought I had evidence for this in a test suite of a project, but I was mistaken. The behavior is identical indeed, as expected from the code.

@mcollina
Copy link
Member

@RubenVerborgh thanks for checking!

@addaleax
Copy link
Member

We still want to change the behaviour so that the callback does get called, right? I.e. #22461 is not a full resolution of this issue, right?

@mcollina
Copy link
Member

That matches the behavior of streams unfortunately, it’s not just http. There is no guarantee that the write callback is called.

@RubenVerborgh
Copy link
Contributor Author

There is no guarantee that the write callback is called.

In that case, we might want to update the documentation at https://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback.

@mcollina
Copy link
Member

mcollina commented Aug 22, 2018 via email

@RubenVerborgh
Copy link
Contributor Author

In #22493 (comment), @mcollina confirmed that Stream#write always calls the callback, even with empty chunks. So OutgoingMessage#write breaks that contract.

Can we fix OutgoingMessage#write to adhere to the contract (semver-major)?

@mcollina
Copy link
Member

Can we fix OutgoingMessage#write to adhere to the contract (semver-major)?

Yes.

lpinca added a commit to lpinca/node that referenced this issue May 15, 2019
Ensure that the callback of `OutgoingMessage.prototype.write()` is
called even when writing empty chunks.

Fixes: nodejs#22066
targos pushed a commit that referenced this issue May 20, 2019
Ensure that the callback of `OutgoingMessage.prototype.write()` is
called even when writing empty chunks.

Fixes: #22066

PR-URL: #27709
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
http Issues or PRs related to the http subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants