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 client strange behavior when server responds immediately without reading request body #9085

Closed
ghost opened this issue Oct 13, 2016 · 9 comments
Labels
http Issues or PRs related to the http subsystem.

Comments

@ghost
Copy link

ghost commented Oct 13, 2016

In the example below an HTTP server doesn't read the the request body, but returns the response immediately. If the client sends large data (e.g. 10,000,000 bytes) ECONNRESET error is emitted at client-side but on small data (e.g. 1,000,000 bytes) the programs runs silently.

var crypto = require('crypto'),
    http = require('http')

var port = 8080

var server = http.createServer((req, res) => {
    res.end(crypto.randomBytes(10000))
})
server.listen(port, () => {
    var req = http.request({
        port: port,
        method: 'post',
    })
    req.end(crypto.randomBytes(10000000)) // <- play this value, remove zeros, then add
})

I think the client still sends data after receiving the response. Thus the client violates HTTP request syntax and the connection gets reset. Is this the expected behavior? If so what's the correct way of handling this?

@ghost
Copy link
Author

ghost commented Oct 13, 2016

Maybe related to #8775

@mscdex mscdex added the http Issues or PRs related to the http subsystem. label Oct 13, 2016
@bnoordhuis
Copy link
Member

I'm curious, what would you expect to happen? This is basically a no-win situation for the client.

@ghost
Copy link
Author

ghost commented Oct 14, 2016

I expect the server to send Connection: close header and close the TCP connection. After which the error event to be emitted at client-side.

So far I came to the following solution which I'm no sure is the best way:

var req = http.request({
    port: port,
    method: 'post',
}, res => {
    res.resume() // receive all data
    res.on('end', () => {
        // attach empty listener to ignore ECONNRESET error
        req.on('error', () => {})
    })
})

@bnoordhuis
Copy link
Member

I expect the server to send Connection: close header and close the TCP connection.

Why? Keep-alive is the default in HTTP/1.1. If you want the server to send a Connection header, you should add it explicitly.

Let's make sure we are on the same page. Is your bug report against the built-in HTTP server or the client?

@ghost
Copy link
Author

ghost commented Oct 15, 2016

Sorry, I'm not a native English speaker, so sometimes I fail in explaining well. I tried to tell the story in short. Well...

My issue is about the client. If any server returns an early response, sometimes an error event is emitted at client, sometimes not, depends on how much data the client has sent in a request body.

In case of server sending an early response, I though explicitly sending Connection: close header would notify the client to stop sending more data and close the connection.

It may not be a bug. But an explanation of what's happening and why will be greatly appreciated.

@bnoordhuis
Copy link
Member

bnoordhuis commented Oct 16, 2016

Okay, I think I see what you mean. There are a couple of things here:

  1. The HTTP server is somewhat anti-social (but not in violation of the spec) in that it accepts but ignores the POST body. Most servers would reply with a 4xx status to signal that they don't want/expect a POST request.
  2. The HTTP client sends a Connection: close header that the server dutifully mirrors in its response. This may be a bug in the node.js client because I can see no reason why it should always send that header. In fact, it's detrimental to performance when multiple requests are made. /cc @nodejs/http
  3. If the client sends a Connection: keep-alive header, HTTP keep-alive will be enabled and you can listen for the 'end' event on the client's response object.
  4. You can however never completely prevent a server from closing the connection on you while you are still busy sending the request. You will always need to add an 'error' event listener and be prepared for ECONNRESET or EPIPE errors.

EDIT: Fixed typo in bullet point 4: s/response/request/

@ghost
Copy link
Author

ghost commented Oct 16, 2016

You will always need to add an 'error' event listener and be prepared for ECONNRESET or EPIPE errors.

And the listener should stay attached even after the response is fully received. I guess that's all that a client can do.

@bnoordhuis
Copy link
Member

Yes, that's correct.

@ghost
Copy link
Author

ghost commented Oct 17, 2016

Thank you for the explanation.

This issue was closed.
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

No branches or pull requests

2 participants