Skip to content

Instantly share code, notes, and snippets.

@shuhei
Last active October 22, 2021 16:34
Show Gist options
  • Save shuhei/cb9517e2cd93c645018a44cf94c7f17d to your computer and use it in GitHub Desktop.
Save shuhei/cb9517e2cd93c645018a44cf94c7f17d to your computer and use it in GitHub Desktop.
Node.js HTTP timeout

Node.js HTTP timeout

Findings

  • http.request({ timeout }) is not working for response timeout.
    • Agent timeout supercedes the option
  • Doesn't leak because request.on('timeout') instead of socket.on('timeout'). It's easier because request is always thrown away.
    • Then why header leaks?
      • Print socket's listeners('timeout').length
      • Try dropRequestAfter together
  • agentkeepalive's timeout handler actually works without request.js's request.on('timeout')

request.setTimeout() can set timeout after connection.

for timeout of lookup and connect, we can use agent’s options.timeout or request’s options.timeout.

agentkeepalive and the builtin agent overwrite timeout option!!!

But you may need request timeout option to propagate socket timeout event to request timeout event. request.on(‘timeout’) is easier than socket.on(‘timeout’).

Having a short socket connection timeout

??? suggests to have a shorter timeout before a TCP connection timeout is established. This prevents ???. This timeout should be relatively shorter (200 ms - 300 ms) depending on the target server. If the timeout is shorter than expected response time of the API, we need to have different timeouts for one before TCP connection and one after it.

Without HTTP agent

const req = http.request({ timeout: shortTimeout });
// ClientRequest.setTimeout() sets a timeout after connection
// https://github.com/nodejs/node/blob/v9.2.0/lib/_http_client.js#L707-L741
req.setTimeout(longTimeout);
req.on('timeout', () => {
  // This handler will be called for both of shortTimeout and longTimeout.
  req.abort();
  reject(new Error('socket timeout'));
});
req.once('error', reject);

With agentkeepalive

const http = require('http');
const Agent = require('agentkeepalive');

const agent = new Agent({ timeout: shortTimeout });
const req = http.request({ agent });
req.setTimeout(longTimeout);
// We don't need `timeout` handler here because agentkeepalive destroys the socket on socket timeout event, we can catch it with the error handler below.
// https://github.com/node-modules/agentkeepalive/blob/3.3.0/lib/_http_agent.js#L309-L316
req.once('error', reject);

http.Agent

const http = require('http');
const Agent = http.Agent;

const agent = new Agent({ timeout: shortTimeout });
// This timeout option is ignored as a timeout, but this is necessary to make ClientRequest to propagate Socket's timeout event.
// ignored option in Node.js stdlib: https://github.com/nodejs/node/blob/v9.2.0/lib/_http_agent.js#L149-L150
// timeout propagation needs timeout: https://github.com/nodejs/node/blob/v9.2.0/lib/_http_client.js#L647-L655
const req = http.request({ timeout: shortTimeout, agent });
req.setTimeout(longTimeout);
req.once(‘timeout’, () => {
  req.abort();
  // TODO: Do we need to call reject?
});
req.once('error', reject);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment