JezK
Edit File: request-manager.js
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; function _classCallCheck2() { var data = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); _classCallCheck2 = function _classCallCheck2() { return data; }; return data; } function _createClass2() { var data = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); _createClass2 = function _createClass2() { return data; }; return data; } function _defineProperty2() { var data = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); _defineProperty2 = function _defineProperty2() { return data; }; return data; } function _fs() { var data = _interopRequireDefault(require("fs")); _fs = function _fs() { return data; }; return data; } function _http() { var data = _interopRequireDefault(require("http")); _http = function _http() { return data; }; return data; } function _url() { var data = _interopRequireDefault(require("url")); _url = function _url() { return data; }; return data; } function _dnscache() { var data = _interopRequireDefault(require("dnscache")); _dnscache = function _dnscache() { return data; }; return data; } function _invariant() { var data = _interopRequireDefault(require("invariant")); _invariant = function _invariant() { return data; }; return data; } function _requestCaptureHar() { var data = _interopRequireDefault(require("request-capture-har")); _requestCaptureHar = function _requestCaptureHar() { return data; }; return data; } function _errors() { var data = require("../errors.js"); _errors = function _errors() { return data; }; return data; } function _blockingQueue() { var data = _interopRequireDefault(require("./blocking-queue.js")); _blockingQueue = function _blockingQueue() { return data; }; return data; } function constants() { var data = _interopRequireWildcard(require("../constants.js")); constants = function constants() { return data; }; return data; } function network() { var data = _interopRequireWildcard(require("./network.js")); network = function network() { return data; }; return data; } function _map() { var data = _interopRequireDefault(require("../util/map.js")); _map = function _map() { return data; }; return data; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } // Initialize DNS cache so we don't look up the same // domains like registry.yarnpkg.com over and over again // for each request. (0, _dnscache()["default"])({ enable: true, ttl: 300, cachesize: 10 }); var successHosts = (0, _map()["default"])(); var controlOffline = network().isOffline(); var RequestManager = /*#__PURE__*/function () { function RequestManager(reporter) { (0, _classCallCheck2()["default"])(this, RequestManager); (0, _defineProperty2()["default"])(this, "offlineNoRequests", void 0); (0, _defineProperty2()["default"])(this, "captureHar", void 0); (0, _defineProperty2()["default"])(this, "userAgent", void 0); (0, _defineProperty2()["default"])(this, "reporter", void 0); (0, _defineProperty2()["default"])(this, "running", void 0); (0, _defineProperty2()["default"])(this, "httpsProxy", void 0); (0, _defineProperty2()["default"])(this, "httpProxy", void 0); (0, _defineProperty2()["default"])(this, "strictSSL", void 0); (0, _defineProperty2()["default"])(this, "ca", void 0); (0, _defineProperty2()["default"])(this, "cert", void 0); (0, _defineProperty2()["default"])(this, "key", void 0); (0, _defineProperty2()["default"])(this, "offlineQueue", void 0); (0, _defineProperty2()["default"])(this, "queue", void 0); (0, _defineProperty2()["default"])(this, "max", void 0); (0, _defineProperty2()["default"])(this, "timeout", void 0); (0, _defineProperty2()["default"])(this, "maxRetryAttempts", void 0); (0, _defineProperty2()["default"])(this, "cache", void 0); (0, _defineProperty2()["default"])(this, "_requestCaptureHar", void 0); (0, _defineProperty2()["default"])(this, "_requestModule", void 0); this.offlineNoRequests = false; this._requestCaptureHar = null; this._requestModule = null; this.offlineQueue = []; this.captureHar = false; this.httpsProxy = ''; this.ca = null; this.httpProxy = ''; this.strictSSL = true; this.userAgent = ''; this.reporter = reporter; this.running = 0; this.queue = []; this.cache = {}; this.max = constants().NETWORK_CONCURRENCY; this.maxRetryAttempts = 5; } (0, _createClass2()["default"])(RequestManager, [{ key: "setOptions", value: function setOptions(opts) { if (opts.userAgent != null) { this.userAgent = opts.userAgent; } if (opts.offline != null) { this.offlineNoRequests = opts.offline; } if (opts.captureHar != null) { this.captureHar = opts.captureHar; } if (opts.httpProxy != null) { this.httpProxy = opts.httpProxy || ''; } if (opts.httpsProxy === '') { this.httpsProxy = opts.httpProxy || ''; } else if (opts.httpsProxy === false) { this.httpsProxy = false; } else { this.httpsProxy = opts.httpsProxy || ''; } if (opts.strictSSL !== null && typeof opts.strictSSL !== 'undefined') { this.strictSSL = opts.strictSSL; } if (opts.ca != null && opts.ca.length > 0) { this.ca = opts.ca; } if (opts.networkConcurrency != null) { this.max = opts.networkConcurrency; } if (opts.networkTimeout != null) { this.timeout = opts.networkTimeout; } if (opts.maxRetryAttempts != null) { this.maxRetryAttempts = opts.maxRetryAttempts; } if (opts.cafile != null && opts.cafile != '') { // The CA bundle file can contain one or more certificates with comments/text between each PEM block. // tls.connect wants an array of certificates without any comments/text, so we need to split the string // and strip out any text in between the certificates try { var bundle = _fs()["default"].readFileSync(opts.cafile).toString(); var hasPemPrefix = function hasPemPrefix(block) { return block.startsWith('-----BEGIN '); }; // opts.cafile overrides opts.ca, this matches with npm behavior this.ca = bundle.split(/(-----BEGIN .*\r?\n[^-]+\r?\n--.*)/).filter(hasPemPrefix); } catch (err) { this.reporter.error("Could not open cafile: ".concat(err.message)); } } if (opts.cert != null) { this.cert = opts.cert; } if (opts.key != null) { this.key = opts.key; } } /** * Lazy load `request` since it is exceptionally expensive to load and is * often not needed at all. */ }, { key: "_getRequestModule", value: function _getRequestModule() { if (!this._requestModule) { var request = require('request'); if (this.captureHar) { this._requestCaptureHar = new (_requestCaptureHar()["default"])(request); this._requestModule = this._requestCaptureHar.request.bind(this._requestCaptureHar); } else { this._requestModule = request; } } return this._requestModule; } /** * Queue up a request. */ }, { key: "request", value: function request(params) { var _this = this; if (this.offlineNoRequests) { return Promise.reject(new (_errors().MessageError)(this.reporter.lang('cantRequestOffline', params.url))); } var cached = this.cache[params.url]; if (cached) { return cached; } params.method = params.method || 'GET'; params.forever = true; params.retryAttempts = 0; params.strictSSL = this.strictSSL; params.headers = Object.assign({ 'User-Agent': this.userAgent }, params.headers); var promise = new Promise(function (resolve, reject) { _this.queue.push({ params: params, reject: reject, resolve: resolve }); _this.shiftQueue(); }); // we can't cache a request with a processor if (!params.process) { this.cache[params.url] = promise; } return promise; } /** * Clear the request cache. This is important as we cache all HTTP requests so you'll * want to do this as soon as you can. */ }, { key: "clearCache", value: function clearCache() { this.cache = {}; if (this._requestCaptureHar != null) { this._requestCaptureHar.clear(); } } /** * Check if an error is possibly due to lost or poor network connectivity. */ }, { key: "isPossibleOfflineError", value: function isPossibleOfflineError(err) { var code = err.code, hostname = err.hostname; if (!code) { return false; } // network was previously online but now we're offline var possibleOfflineChange = !controlOffline && !network().isOffline(); if (code === 'ENOTFOUND' && possibleOfflineChange) { // can't resolve a domain return true; } // used to be able to resolve this domain! something is wrong if (code === 'ENOTFOUND' && hostname && successHosts[hostname]) { // can't resolve this domain but we've successfully resolved it before return true; } // network was previously offline and we can't resolve the domain if (code === 'ENOTFOUND' && controlOffline) { return true; } // connection was reset or dropped if (code === 'ECONNRESET') { return true; } // TCP timeout if (code === 'ESOCKETTIMEDOUT' || code === 'ETIMEDOUT') { return true; } return false; } /** * Queue up request arguments to be retried. Start a network connectivity timer if there * isn't already one. */ }, { key: "queueForRetry", value: function queueForRetry(opts) { if (opts.retryReason) { var containsReason = false; var _iterator = _createForOfIteratorHelper(this.offlineQueue), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var queuedOpts = _step.value; if (queuedOpts.retryReason === opts.retryReason) { containsReason = true; break; } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } if (!containsReason) { this.reporter.info(opts.retryReason); } } if (!this.offlineQueue.length) { this.initOfflineRetry(); } this.offlineQueue.push(opts); } /** * Begin timers to retry failed requests when we possibly establish network connectivity * again. */ }, { key: "initOfflineRetry", value: function initOfflineRetry() { var _this2 = this; setTimeout(function () { var queue = _this2.offlineQueue; _this2.offlineQueue = []; var _iterator2 = _createForOfIteratorHelper(queue), _step2; try { for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var opts = _step2.value; _this2.execute(opts); } } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } }, 3000); } /** * Execute a request. */ }, { key: "execute", value: function execute(opts) { var _this3 = this; var params = opts.params; var reporter = this.reporter; var buildNext = function buildNext(fn) { return function (data) { fn(data); _this3.running--; _this3.shiftQueue(); }; }; var resolve = buildNext(opts.resolve); var rejectNext = buildNext(opts.reject); var reject = function reject(err) { err.message = "".concat(params.url, ": ").concat(err.message); rejectNext(err); }; var rejectWithoutUrl = function rejectWithoutUrl(err) { err.message = err.message; rejectNext(err); }; var queueForRetry = function queueForRetry(reason) { var attempts = params.retryAttempts || 0; if (attempts >= _this3.maxRetryAttempts - 1) { return false; } if (opts.params.method && opts.params.method.toUpperCase() !== 'GET') { return false; } params.retryAttempts = attempts + 1; if (typeof params.cleanup === 'function') { params.cleanup(); } opts.retryReason = reason; _this3.queueForRetry(opts); return true; }; var calledOnError = false; var onError = function onError(err) { if (calledOnError) { return; } calledOnError = true; if (_this3.isPossibleOfflineError(err)) { if (!queueForRetry(_this3.reporter.lang('offlineRetrying'))) { reject(err); } } else { reject(err); } }; if (!params.process) { var parts = _url()["default"].parse(params.url); params.callback = function (err, res, body) { if (err) { onError(err); return; } successHosts[parts.hostname] = true; _this3.reporter.verbose(_this3.reporter.lang('verboseRequestFinish', params.url, res.statusCode)); if (res.statusCode === 408 || res.statusCode >= 500) { var description = "".concat(res.statusCode, " ").concat(_http()["default"].STATUS_CODES[res.statusCode]); if (!queueForRetry(_this3.reporter.lang('internalServerErrorRetrying', description))) { throw new (_errors().ResponseError)(_this3.reporter.lang('requestFailed', description), res.statusCode); } else { return; } } if (res.statusCode === 401 && res.caseless && res.caseless.get('server') === 'GitHub.com') { var message = "".concat(res.body.message, ". If using GITHUB_TOKEN in your env, check that it is valid."); rejectWithoutUrl(new Error(_this3.reporter.lang('unauthorizedResponse', res.caseless.get('server'), message))); } if (res.statusCode === 401 && res.headers['www-authenticate']) { var authMethods = res.headers['www-authenticate'].split(/,\s*/).map(function (s) { return s.toLowerCase(); }); if (authMethods.indexOf('otp') !== -1) { reject(new (_errors().OneTimePasswordError)(res.headers['npm-notice'])); return; } } if (body && typeof body.error === 'string') { reject(new Error(body.error)); return; } if ([400, 401, 404].concat(params.rejectStatusCode || []).indexOf(res.statusCode) !== -1) { // So this is actually a rejection ... the hosted git resolver uses this to know whether http is supported resolve(false); } else if (res.statusCode >= 400) { var errMsg = body && body.message || reporter.lang('requestError', params.url, res.statusCode); reject(new Error(errMsg)); } else { resolve(body); } }; } if (params.buffer) { params.encoding = null; } var proxy = this.httpProxy; if (params.url.startsWith('https:')) { proxy = this.httpsProxy; } if (proxy) { // if no proxy is set, do not pass a proxy down to request. // the request library will internally check the HTTP_PROXY and HTTPS_PROXY env vars. params.proxy = String(proxy); } else if (proxy === false) { // passing empty string prevents the underlying library from falling back to the env vars. // an explicit false in the yarn config should override the env var. See #4546. params.proxy = ''; } if (this.ca != null) { params.ca = this.ca; } if (this.cert != null) { params.cert = this.cert; } if (this.key != null) { params.key = this.key; } if (this.timeout != null) { params.timeout = this.timeout; } var request = this._getRequestModule(); var req = request(params); this.reporter.verbose(this.reporter.lang('verboseRequestStart', params.method, params.url)); req.on('error', onError); var queue = params.queue; if (queue) { req.on('data', queue.stillActive.bind(queue)); } var process = params.process; if (process) { req.on('response', function (res) { if (res.statusCode >= 200 && res.statusCode < 300) { return; } var description = "".concat(res.statusCode, " ").concat(_http()["default"].STATUS_CODES[res.statusCode]); reject(new (_errors().ResponseError)(_this3.reporter.lang('requestFailed', description), res.statusCode)); req.abort(); }); process(req, resolve, reject); } } /** * Remove an item from the queue. Create it's request options and execute it. */ }, { key: "shiftQueue", value: function shiftQueue() { if (this.running >= this.max || !this.queue.length) { return; } var opts = this.queue.shift(); this.running++; this.execute(opts); } }, { key: "saveHar", value: function saveHar(filename) { if (!this.captureHar) { throw new Error(this.reporter.lang('requestManagerNotSetupHAR')); } // No request may have occurred at all. this._getRequestModule(); (0, _invariant()["default"])(this._requestCaptureHar != null, 'request-capture-har not setup'); this._requestCaptureHar.saveHar(filename); } }]); return RequestManager; }(); exports["default"] = RequestManager; //# sourceMappingURL=request-manager.js.map