Repository navigation
Agent Keep-Alive behavior #26357
Description
Activity
/cc @mcollina
I think this is buggy, and when passing
keepAlive: false, maxSockets: 256, we should not be opening a keep alive connection.cc @nodejs/http
- addedhttpIssues and PRs related to the http subsystem.Issues and PRs related to the http subsystem.
on Feb 28, 2019 Update: I've noted that if
keepAliveisfalseandmaxSocketsis0, the connection header has the expected value (close).const agentFalse = new http.Agent({ keepAlive: false, keepAliveMsecs: 1000, maxSockets: 0, maxFreeSockets: 256 })
I can reproduce this behavior in all the latest releases of 6, 8, 10, and 11.
I think this could be a nice good-first-issue. Maybe @delvedor would like to send a PR.
Despite the confusing options, I'm not sure if it is a bug, I think it is working as intended. Consider the following example:
const assert = require('assert'); const { Agent, get } = require('http'); const agent = new Agent({ keepAlive: false, maxSockets: 1 }); let socket; function onResponse(response) { response.resume(); } function onSocket(sock) { if (socket === undefined) { socket = sock; } else { assert(sock === socket); } } get({ host: 'example.com', agent }, onResponse).on('socket', onSocket); get({ host: 'example.com', agent }, onResponse).on('socket', onSocket);
If
Connection: Keep-Aliveis not sent, the socket is closed, and a new one must be created for the second request. If it is sent the same socket is used and when there are no more pending requests, the socket is destroyed because thekeepAliveoption isfalse. It doesn't end in the free socket list as it would have if thekeepAliveoption wastrue.@lpinca it seems you are implying that setting keepAlive: false has nothing to do with sending the keep alive header, but it rather reflects the internal state of the queue.
I think this is very confusing, and at least it should be documented if not changed.
Yes exactly I think the
keepAliveoption does not mean "send theConnection: Keep-Aliveheader". The documentation is accurate:Keep sockets around even when there are no outstanding requests, so they can be used for future requests without having to reestablish a TCP connection.
Maybe we can improve it somehow.
@lpinca we are in disagreement. If the
keepAlivecontrols if we are sending theConnection: Keep-Aliveheader, then it should send it independently of the number of requests that are queued up. Our current behavior instead ties this up to the internal queue state, e.g. "no outstanding request" as the docs states.In other terms, if we pass
keepAlive: false, theConnection: Keep-Aliveheader would be sent if there is another request available to send. However this behavior is triggered only ifmaxSocketis set and notInfinity, and the number of maximum available sockets are filled. I think this behavior is correct.
In the example @delvedor has posted, there are more complicated combinations.
For example:
{ keepAlive: false, keepAliveMsecs: 1000, maxSockets: 256, maxFreeSockets: 256 }
Is going to generate a
Connection: Keep-Aliveheader even if there is no socket waiting, I assume because there is amaxFreeSocketoption. This essentially overrides thekeepAliveflag. From my point of view, setting bothmaxFreeSocketsandkeepAliveshows a conflicting intent: the way we keep sockets free is by sendingConnection: Keep-Alivewhich the user asks us not to do, but specifyingmaxFreeSocketstells us that they want to do it.I'm a bit puzzled by this issue.
Sorry forgot to add "not" in my previous comment, please re-read, I've edited it. I don't think we are in disagreement.
The example with
{ keepAlive: false, maxSockets: 256 }is the same example I posted above, but allowing up to 256 sockets per host.Based on our discussion, should
const agentFalseInf = new http.Agent({ keepAlive: false, maxSockets: Infinity })
send
Connection: Keep-Aliveheader? Note that this is the default, and it's currently sendingconnection: 'close'.Based on our interpretation, should we ever send
connection: 'close'?Basically
Connection: Keep-Aliveis always sent, with a single exception being{ keepAlive: false, maxSockets: Infinity }which makes sense as in this case the socket is never reused, so it's ok if it is closed by the server.The
keepAliveoption controls whether or not to put the socket in the "freeSockets" list when there are no outstanding requests (or at least this is my understanding).Basically
Connection: Keep-Aliveis always sent, with a single exception being{ keepAlive: false, maxSockets: Infinity }which makes sense as in this case the socket is never reused, so it's ok if it is closed by the server.This matches our current behavior, but is it coherent with the meaning that we have given to the
keepAliveoption?Keep sockets around even when there are no outstanding requests, so they can be used for future requests without having to reestablish a TCP connection.
This states that
maxSocketsshould not impact at all why we are sending or notConnection: Keep-Alive. Setting it toInfinitywould not change the fact that there can be outstanding requests when the current one finishes.If
maxSocketsis set toInfinitya new socket is created per request so there can't be outstanding requests.Oks, so the naming generates this misunderstanding.
At this point how can I disable theConnection: keep-aliveheader?Reacted by Luigi Pinca@delvedor if you don't need connection pooling, don't use an agent and use the
createConnectionoption instead. In this wayConnection: closeis used.If maxSockets is set to Infinity a new socket is created per request so there can't be outstanding requests.
That's not what our docs says:
maxSockets Maximum number of sockets to allow per host. Default: Infinity.
Maybe we should update this? Considering that setting
keepalive: false, maxSockets: Infinityis the default.
Possibly we should include a table in the docs to explain the different options and behaviors, and how they effect
Connection: keep-alive?Reacted by Luigi Pinca- added a commit that references this issue
on Mar 5, 2019 - added a commit that references this issue
on Mar 12, 2019 - added a commit that references this issue
on Apr 16, 2019
I've encountered a strange behavior while using Agent Keep-Alive, and I'm not sure it is correct.
I've created a code example to show what I've discovered, pay attention to the configurations of the agents.
And this is the log:
If this is the expected behavior, can you explain why it works like so?