index.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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