This package patches http(s).Agent
to implement "happy eyeballs" (rfc8305), a standard published by the IETF.
It improves client performance and reliability by trying concurrently trying multiple ip addresses for a given host name. See Explanation for more detail.
npm i --save-dev happy-eyeballs
yarn add --dev happy-eyeballs
To use this library, simply import
import 'happy-eyeballs/eye-patch';
or
require('happy-eyeballs/eye-patch')
to the top of your .js
/.ts
entry file.
Note: this will replace http.Agent.prototype.createConnection
and https.Agent.prototype.createConnection
, but it tries to preserve existing functionality as much as possible.
If you want to be more explicit, you can explicitly patch the http(s).Agent
:
import { patch } from 'happy-eyeballs';
import { Agent as HttpAgent } from 'http';
import { Agent as HttpsAgent } from 'https';
patch(HttpAgent, {delay: 300});
patch(HttpsAgent, {delay: 300});
Although, this is exactly what the import 'happy-eyeballs/eye-patch';
does anyway.
Note that delay
refers to the delay before attempting the next addresses. This is the delay browsers use.
Later you can unpatch, which will undo the last patch:
unpatch([HttpsAgent, HttpAgent])
or reset to the original values of an agent:
reset([HttpsAgent, HttpAgent])
Also, note that the default is HttpAgent
and HttpsAgent
, so this has the same effect as
reset()
And that goes for patch
and unpatch
too.
You could also implement your own agent and replace the createConnection
method:
import { createConnection } from 'happy-eyeballs';
import { Agent as HttpAgent } from 'http';
import { Agent as HttpsAgent } from 'https';
export class MyHttpAgent extends HttpAgent {
createConnection = createConnection;
delay = 250;
}
export class MyHttpsAgent extends HttpsAgent {
createConnection = createConnection;
delay = 250;
}
This does basically the same thing as the previous examples though.
Essentially, the algorithm amounts to this:
- Receive a hostname
- Have we connected to this host before?
- If yes:
- Try the family (Ipv4 or IPv6) of last successful connection if we have connected to this host before
- If connection is not successful within 300ms, proceed to next step
- If no: proceed to next step.
- Did the DNS lookup return both IPv4 and IPv6 addresses?
- If yes: try, both addresses concurrently until all have been tried
- If no: just try the address of the existent family
- Proceed with each address in chain, trying both families concurrently until either: *. A connection is made *. All connection attempts time out *. All connection attempt fail
- If no connection was successful, return error of the first connection attempt or a "time out" error if all connections timed out