--- /dev/null
+'use strict';
+require('./patch-core');
+const inherits = require('util').inherits;
+const promisify = require('es6-promisify');
+const EventEmitter = require('events').EventEmitter;
+
+module.exports = Agent;
+
+function isAgent(v) {
+ return v && typeof v.addRequest === 'function';
+}
+
+/**
+ * Base `http.Agent` implementation.
+ * No pooling/keep-alive is implemented by default.
+ *
+ * @param {Function} callback
+ * @api public
+ */
+function Agent(callback, _opts) {
+ if (!(this instanceof Agent)) {
+ return new Agent(callback, _opts);
+ }
+
+ EventEmitter.call(this);
+
+ // The callback gets promisified if it has 3 parameters
+ // (i.e. it has a callback function) lazily
+ this._promisifiedCallback = false;
+
+ let opts = _opts;
+ if ('function' === typeof callback) {
+ this.callback = callback;
+ } else if (callback) {
+ opts = callback;
+ }
+
+ // timeout for the socket to be returned from the callback
+ this.timeout = (opts && opts.timeout) || null;
+
+ this.options = opts;
+}
+inherits(Agent, EventEmitter);
+
+/**
+ * Override this function in your subclass!
+ */
+Agent.prototype.callback = function callback(req, opts) {
+ throw new Error(
+ '"agent-base" has no default implementation, you must subclass and override `callback()`'
+ );
+};
+
+/**
+ * Called by node-core's "_http_client.js" module when creating
+ * a new HTTP request with this Agent instance.
+ *
+ * @api public
+ */
+Agent.prototype.addRequest = function addRequest(req, _opts) {
+ const ownOpts = Object.assign({}, _opts);
+
+ // Set default `host` for HTTP to localhost
+ if (null == ownOpts.host) {
+ ownOpts.host = 'localhost';
+ }
+
+ // Set default `port` for HTTP if none was explicitly specified
+ if (null == ownOpts.port) {
+ ownOpts.port = ownOpts.secureEndpoint ? 443 : 80;
+ }
+
+ const opts = Object.assign({}, this.options, ownOpts);
+
+ if (opts.host && opts.path) {
+ // If both a `host` and `path` are specified then it's most likely the
+ // result of a `url.parse()` call... we need to remove the `path` portion so
+ // that `net.connect()` doesn't attempt to open that as a unix socket file.
+ delete opts.path;
+ }
+
+ delete opts.agent;
+ delete opts.hostname;
+ delete opts._defaultAgent;
+ delete opts.defaultPort;
+ delete opts.createConnection;
+
+ // Hint to use "Connection: close"
+ // XXX: non-documented `http` module API :(
+ req._last = true;
+ req.shouldKeepAlive = false;
+
+ // Create the `stream.Duplex` instance
+ let timeout;
+ let timedOut = false;
+ const timeoutMs = this.timeout;
+ const freeSocket = this.freeSocket;
+
+ function onerror(err) {
+ if (req._hadError) return;
+ req.emit('error', err);
+ // For Safety. Some additional errors might fire later on
+ // and we need to make sure we don't double-fire the error event.
+ req._hadError = true;
+ }
+
+ function ontimeout() {
+ timeout = null;
+ timedOut = true;
+ const err = new Error(
+ 'A "socket" was not created for HTTP request before ' + timeoutMs + 'ms'
+ );
+ err.code = 'ETIMEOUT';
+ onerror(err);
+ }
+
+ function callbackError(err) {
+ if (timedOut) return;
+ if (timeout != null) {
+ clearTimeout(timeout);
+ timeout = null;
+ }
+ onerror(err);
+ }
+
+ function onsocket(socket) {
+ if (timedOut) return;
+ if (timeout != null) {
+ clearTimeout(timeout);
+ timeout = null;
+ }
+ if (isAgent(socket)) {
+ // `socket` is actually an http.Agent instance, so relinquish
+ // responsibility for this `req` to the Agent from here on
+ socket.addRequest(req, opts);
+ } else if (socket) {
+ function onfree() {
+ freeSocket(socket, opts);
+ }
+ socket.on('free', onfree);
+ req.onSocket(socket);
+ } else {
+ const err = new Error(
+ 'no Duplex stream was returned to agent-base for `' + req.method + ' ' + req.path + '`'
+ );
+ onerror(err);
+ }
+ }
+
+ if (!this._promisifiedCallback && this.callback.length >= 3) {
+ // Legacy callback function - convert to a Promise
+ this.callback = promisify(this.callback, this);
+ this._promisifiedCallback = true;
+ }
+
+ if (timeoutMs > 0) {
+ timeout = setTimeout(ontimeout, timeoutMs);
+ }
+
+ try {
+ Promise.resolve(this.callback(req, opts)).then(onsocket, callbackError);
+ } catch (err) {
+ Promise.reject(err).catch(callbackError);
+ }
+};
+
+Agent.prototype.freeSocket = function freeSocket(socket, opts) {
+ // TODO reuse sockets
+ socket.destroy();
+};