-
-
Notifications
You must be signed in to change notification settings - Fork 35.2k
Description
- Version: v7.4.0
- Platform: Darwin Lances-MacBook-Pro.local 16.3.0 Darwin Kernel Version 16.3.0: Thu Nov 17 20:23:58 PST 2016; root:xnu-3789.31.2~1/RELEASE_X86_64 x86_64
- Subsystem: http, net
Due to what I thought would be a simple documentation PR, I have recently started investigating how http.Agent works for both connection pooling and HTTP keep-alive behavior. I am much confused. I would like to know if my understanding of the existing code is correct, and if so, is this the intended behavior.
The following text is a copy/paste of one of my comments from that documentation pull request.
I have been reading the code all morning, and the keep alive logic is mind boggling, shared between _http_outgoing.js, _http_agent.js, _http_client.js, and net.js. If I'm reading it correctly, as far as clients go, by default, we're not implementing HTTP/1.1 persistence.
The HTTP/1.1 spec says,
A significant difference between HTTP/1.1 and earlier versions of HTTP is that persistent connections are the default behavior of any HTTP connection. That is, unless otherwise indicated, the client SHOULD assume that the server will maintain a persistent connection, even after error responses from the server.
Both the http.globalAgent and the default constructor behave so that multiple, simultaneous requests will reuse an existing connection, but once the Agent instance has no more pending requests for a given host/port, the socket is destroyed, instead of assuming the server will maintain the connection.
But really, I wonder if this is the intended behavior. It's not necessarily broken, but seems like an odd default. That, combined with the fact that the default also does not pool sockets -- you must specify keepAlive: true to enable this -- makes it seems like the Agent doesn't do much on its own, unless expressly created/configured to do so.
To summarize. If I read it correctly, the default behavior for all HTTP client requests using an Agent (even the default http._globalAgent is to:
- Always send HTTP/1.0
Connection: keep-aliveheaders. See: https://github.com/nodejs/node/blob/master/lib/_http_outgoing.js#L276-L279 - Destroy socket connections when there are no more pending requests for a given host/port. See: https://github.com/nodejs/node/blob/master/lib/_http_agent.js#L47-L81
- Not implement HTTP/1.1 persistent connections, since sockets are destroyed by default (see above)
- Not pool socket connections, unless there are multiple, simultaneous requests to the same host/port (see above)
- To use connection pooling, you must specify
{ keepAlive: true }as a constructor option in a newAgent - When using connection pooling, HTTP/1.0 connection persistence is enabled using the
Connection: keep-aliveheader. See above re: always sendingConnection: keep-alive - When using connection pooling, and therefore also HTTP/1.0 connection persistence via
Connection: keep-aliveheaders (because you can't have one without the other in this implementation), TCPSO_KEEPALIVEis used via lib_uv'suv_tcp_keepalivefunction. See: https://github.com/nodejs/node/blob/master/lib/_http_agent.js#L73, https://github.com/nodejs/node/blob/master/lib/net.js#L368, and https://github.com/nodejs/node/blob/master/src/tcp_wrap.cc#L161 - None of this matters at all if you provide a
createConnectionfunction to thehttp.request()function.
I would love for someone with better knowledge of the code to chime in here. My reading of the code could be wrong. But if it's correct, it sure is confusing and I find it hard to believe this is the true intent.