2 require('./patch-core');
3 const inherits = require('util').inherits;
4 const promisify = require('es6-promisify');
5 const EventEmitter = require('events').EventEmitter;
7 module.exports = Agent;
10 return v && typeof v.addRequest === 'function';
14 * Base `http.Agent` implementation.
15 * No pooling/keep-alive is implemented by default.
17 * @param {Function} callback
20 function Agent(callback, _opts) {
21 if (!(this instanceof Agent)) {
22 return new Agent(callback, _opts);
25 EventEmitter.call(this);
27 // The callback gets promisified if it has 3 parameters
28 // (i.e. it has a callback function) lazily
29 this._promisifiedCallback = false;
32 if ('function' === typeof callback) {
33 this.callback = callback;
34 } else if (callback) {
38 // timeout for the socket to be returned from the callback
39 this.timeout = (opts && opts.timeout) || null;
43 inherits(Agent, EventEmitter);
46 * Override this function in your subclass!
48 Agent.prototype.callback = function callback(req, opts) {
50 '"agent-base" has no default implementation, you must subclass and override `callback()`'
55 * Called by node-core's "_http_client.js" module when creating
56 * a new HTTP request with this Agent instance.
60 Agent.prototype.addRequest = function addRequest(req, _opts) {
61 const ownOpts = Object.assign({}, _opts);
63 // Set default `host` for HTTP to localhost
64 if (null == ownOpts.host) {
65 ownOpts.host = 'localhost';
68 // Set default `port` for HTTP if none was explicitly specified
69 if (null == ownOpts.port) {
70 ownOpts.port = ownOpts.secureEndpoint ? 443 : 80;
73 const opts = Object.assign({}, this.options, ownOpts);
75 if (opts.host && opts.path) {
76 // If both a `host` and `path` are specified then it's most likely the
77 // result of a `url.parse()` call... we need to remove the `path` portion so
78 // that `net.connect()` doesn't attempt to open that as a unix socket file.
84 delete opts._defaultAgent;
85 delete opts.defaultPort;
86 delete opts.createConnection;
88 // Hint to use "Connection: close"
89 // XXX: non-documented `http` module API :(
91 req.shouldKeepAlive = false;
93 // Create the `stream.Duplex` instance
96 const timeoutMs = this.timeout;
97 const freeSocket = this.freeSocket;
99 function onerror(err) {
100 if (req._hadError) return;
101 req.emit('error', err);
102 // For Safety. Some additional errors might fire later on
103 // and we need to make sure we don't double-fire the error event.
104 req._hadError = true;
107 function ontimeout() {
110 const err = new Error(
111 'A "socket" was not created for HTTP request before ' + timeoutMs + 'ms'
113 err.code = 'ETIMEOUT';
117 function callbackError(err) {
118 if (timedOut) return;
119 if (timeout != null) {
120 clearTimeout(timeout);
126 function onsocket(socket) {
127 if (timedOut) return;
128 if (timeout != null) {
129 clearTimeout(timeout);
132 if (isAgent(socket)) {
133 // `socket` is actually an http.Agent instance, so relinquish
134 // responsibility for this `req` to the Agent from here on
135 socket.addRequest(req, opts);
138 freeSocket(socket, opts);
140 socket.on('free', onfree);
141 req.onSocket(socket);
143 const err = new Error(
144 'no Duplex stream was returned to agent-base for `' + req.method + ' ' + req.path + '`'
150 if (!this._promisifiedCallback && this.callback.length >= 3) {
151 // Legacy callback function - convert to a Promise
152 this.callback = promisify(this.callback, this);
153 this._promisifiedCallback = true;
157 timeout = setTimeout(ontimeout, timeoutMs);
161 Promise.resolve(this.callback(req, opts)).then(onsocket, callbackError);
163 Promise.reject(err).catch(callbackError);
167 Agent.prototype.freeSocket = function freeSocket(socket, opts) {
168 // TODO reuse sockets