index.js
  1  "use strict";
  2  var __importDefault = (this && this.__importDefault) || function (mod) {
  3      return (mod && mod.__esModule) ? mod : { "default": mod };
  4  };
  5  const events_1 = require("events");
  6  const debug_1 = __importDefault(require("debug"));
  7  const promisify_1 = __importDefault(require("./promisify"));
  8  const debug = debug_1.default('agent-base');
  9  function isAgent(v) {
 10      return Boolean(v) && typeof v.addRequest === 'function';
 11  }
 12  function isSecureEndpoint() {
 13      const { stack } = new Error();
 14      if (typeof stack !== 'string')
 15          return false;
 16      return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);
 17  }
 18  function createAgent(callback, opts) {
 19      return new createAgent.Agent(callback, opts);
 20  }
 21  (function (createAgent) {
 22      /**
 23       * Base `http.Agent` implementation.
 24       * No pooling/keep-alive is implemented by default.
 25       *
 26       * @param {Function} callback
 27       * @api public
 28       */
 29      class Agent extends events_1.EventEmitter {
 30          constructor(callback, _opts) {
 31              super();
 32              let opts = _opts;
 33              if (typeof callback === 'function') {
 34                  this.callback = callback;
 35              }
 36              else if (callback) {
 37                  opts = callback;
 38              }
 39              // Timeout for the socket to be returned from the callback
 40              this.timeout = null;
 41              if (opts && typeof opts.timeout === 'number') {
 42                  this.timeout = opts.timeout;
 43              }
 44              // These aren't actually used by `agent-base`, but are required
 45              // for the TypeScript definition files in `@types/node` :/
 46              this.maxFreeSockets = 1;
 47              this.maxSockets = 1;
 48              this.maxTotalSockets = Infinity;
 49              this.sockets = {};
 50              this.freeSockets = {};
 51              this.requests = {};
 52              this.options = {};
 53          }
 54          get defaultPort() {
 55              if (typeof this.explicitDefaultPort === 'number') {
 56                  return this.explicitDefaultPort;
 57              }
 58              return isSecureEndpoint() ? 443 : 80;
 59          }
 60          set defaultPort(v) {
 61              this.explicitDefaultPort = v;
 62          }
 63          get protocol() {
 64              if (typeof this.explicitProtocol === 'string') {
 65                  return this.explicitProtocol;
 66              }
 67              return isSecureEndpoint() ? 'https:' : 'http:';
 68          }
 69          set protocol(v) {
 70              this.explicitProtocol = v;
 71          }
 72          callback(req, opts, fn) {
 73              throw new Error('"agent-base" has no default implementation, you must subclass and override `callback()`');
 74          }
 75          /**
 76           * Called by node-core's "_http_client.js" module when creating
 77           * a new HTTP request with this Agent instance.
 78           *
 79           * @api public
 80           */
 81          addRequest(req, _opts) {
 82              const opts = Object.assign({}, _opts);
 83              if (typeof opts.secureEndpoint !== 'boolean') {
 84                  opts.secureEndpoint = isSecureEndpoint();
 85              }
 86              if (opts.host == null) {
 87                  opts.host = 'localhost';
 88              }
 89              if (opts.port == null) {
 90                  opts.port = opts.secureEndpoint ? 443 : 80;
 91              }
 92              if (opts.protocol == null) {
 93                  opts.protocol = opts.secureEndpoint ? 'https:' : 'http:';
 94              }
 95              if (opts.host && opts.path) {
 96                  // If both a `host` and `path` are specified then it's most
 97                  // likely the result of a `url.parse()` call... we need to
 98                  // remove the `path` portion so that `net.connect()` doesn't
 99                  // attempt to open that as a unix socket file.
100                  delete opts.path;
101              }
102              delete opts.agent;
103              delete opts.hostname;
104              delete opts._defaultAgent;
105              delete opts.defaultPort;
106              delete opts.createConnection;
107              // Hint to use "Connection: close"
108              // XXX: non-documented `http` module API :(
109              req._last = true;
110              req.shouldKeepAlive = false;
111              let timedOut = false;
112              let timeoutId = null;
113              const timeoutMs = opts.timeout || this.timeout;
114              const onerror = (err) => {
115                  if (req._hadError)
116                      return;
117                  req.emit('error', err);
118                  // For Safety. Some additional errors might fire later on
119                  // and we need to make sure we don't double-fire the error event.
120                  req._hadError = true;
121              };
122              const ontimeout = () => {
123                  timeoutId = null;
124                  timedOut = true;
125                  const err = new Error(`A "socket" was not created for HTTP request before ${timeoutMs}ms`);
126                  err.code = 'ETIMEOUT';
127                  onerror(err);
128              };
129              const callbackError = (err) => {
130                  if (timedOut)
131                      return;
132                  if (timeoutId !== null) {
133                      clearTimeout(timeoutId);
134                      timeoutId = null;
135                  }
136                  onerror(err);
137              };
138              const onsocket = (socket) => {
139                  if (timedOut)
140                      return;
141                  if (timeoutId != null) {
142                      clearTimeout(timeoutId);
143                      timeoutId = null;
144                  }
145                  if (isAgent(socket)) {
146                      // `socket` is actually an `http.Agent` instance, so
147                      // relinquish responsibility for this `req` to the Agent
148                      // from here on
149                      debug('Callback returned another Agent instance %o', socket.constructor.name);
150                      socket.addRequest(req, opts);
151                      return;
152                  }
153                  if (socket) {
154                      socket.once('free', () => {
155                          this.freeSocket(socket, opts);
156                      });
157                      req.onSocket(socket);
158                      return;
159                  }
160                  const err = new Error(`no Duplex stream was returned to agent-base for \`${req.method} ${req.path}\``);
161                  onerror(err);
162              };
163              if (typeof this.callback !== 'function') {
164                  onerror(new Error('`callback` is not defined'));
165                  return;
166              }
167              if (!this.promisifiedCallback) {
168                  if (this.callback.length >= 3) {
169                      debug('Converting legacy callback function to promise');
170                      this.promisifiedCallback = promisify_1.default(this.callback);
171                  }
172                  else {
173                      this.promisifiedCallback = this.callback;
174                  }
175              }
176              if (typeof timeoutMs === 'number' && timeoutMs > 0) {
177                  timeoutId = setTimeout(ontimeout, timeoutMs);
178              }
179              if ('port' in opts && typeof opts.port !== 'number') {
180                  opts.port = Number(opts.port);
181              }
182              try {
183                  debug('Resolving socket for %o request: %o', opts.protocol, `${req.method} ${req.path}`);
184                  Promise.resolve(this.promisifiedCallback(req, opts)).then(onsocket, callbackError);
185              }
186              catch (err) {
187                  Promise.reject(err).catch(callbackError);
188              }
189          }
190          freeSocket(socket, opts) {
191              debug('Freeing socket %o %o', socket.constructor.name, opts);
192              socket.destroy();
193          }
194          destroy() {
195              debug('Destroying agent %o', this.constructor.name);
196          }
197      }
198      createAgent.Agent = Agent;
199      // So that `instanceof` works correctly
200      createAgent.prototype = createAgent.Agent.prototype;
201  })(createAgent || (createAgent = {}));
202  module.exports = createAgent;
203  //# sourceMappingURL=index.js.map