UNPKG

recoder-code

Version:

🚀 AI-powered development platform - Chat with 32+ models, build projects, automate workflows. Free models included!

1,310 lines (1,190 loc) • 130 kB
"use strict"; function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } 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(_e2) { throw _e2; }, 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(_e3) { didErr = true; err = _e3; }, 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; } /** * Module dependencies. */ // eslint-disable-next-line node/no-deprecated-api const _require = require('url'), parse = _require.parse, format = _require.format, resolve = _require.resolve; const Stream = require('stream'); const https = require('https'); const http = require('http'); const fs = require('fs'); const zlib = require('zlib'); const util = require('util'); const qs = require('qs'); const mime = require('mime'); let methods = require('methods'); const FormData = require('form-data'); const formidable = require('formidable'); const debug = require('debug')('superagent'); const CookieJar = require('cookiejar'); const semverGte = require('semver/functions/gte'); const safeStringify = require('fast-safe-stringify'); const utils = require('../utils'); const RequestBase = require('../request-base'); const _require2 = require('./unzip'), unzip = _require2.unzip; const Response = require('./response'); const mixin = utils.mixin, hasOwn = utils.hasOwn; let http2; if (semverGte(process.version, 'v10.10.0')) http2 = require('./http2wrapper'); function request(method, url) { // callback if (typeof url === 'function') { return new exports.Request('GET', method).end(url); } // url first if (arguments.length === 1) { return new exports.Request('GET', method); } return new exports.Request(method, url); } module.exports = request; exports = module.exports; /** * Expose `Request`. */ exports.Request = Request; /** * Expose the agent function */ exports.agent = require('./agent'); /** * Noop. */ function noop() {} /** * Expose `Response`. */ exports.Response = Response; /** * Define "form" mime type. */ mime.define({ 'application/x-www-form-urlencoded': ['form', 'urlencoded', 'form-data'] }, true); /** * Protocol map. */ exports.protocols = { 'http:': http, 'https:': https, 'http2:': http2 }; /** * Default serialization map. * * superagent.serialize['application/xml'] = function(obj){ * return 'generated xml here'; * }; * */ exports.serialize = { 'application/x-www-form-urlencoded': qs.stringify, 'application/json': safeStringify }; /** * Default parsers. * * superagent.parse['application/xml'] = function(res, fn){ * fn(null, res); * }; * */ exports.parse = require('./parsers'); /** * Default buffering map. Can be used to set certain * response types to buffer/not buffer. * * superagent.buffer['application/xml'] = true; */ exports.buffer = {}; /** * Initialize internal header tracking properties on a request instance. * * @param {Object} req the instance * @api private */ function _initHeaders(request_) { request_._header = { // coerces header names to lowercase }; request_.header = { // preserves header name case }; } /** * Initialize a new `Request` with the given `method` and `url`. * * @param {String} method * @param {String|Object} url * @api public */ function Request(method, url) { Stream.call(this); if (typeof url !== 'string') url = format(url); this._enableHttp2 = Boolean(process.env.HTTP2_TEST); // internal only this._agent = false; this._formData = null; this.method = method; this.url = url; _initHeaders(this); this.writable = true; this._redirects = 0; this.redirects(method === 'HEAD' ? 0 : 5); this.cookies = ''; this.qs = {}; this._query = []; this.qsRaw = this._query; // Unused, for backwards compatibility only this._redirectList = []; this._streamRequest = false; this._lookup = undefined; this.once('end', this.clearTimeout.bind(this)); } /** * Inherit from `Stream` (which inherits from `EventEmitter`). * Mixin `RequestBase`. */ util.inherits(Request, Stream); mixin(Request.prototype, RequestBase.prototype); /** * Enable or Disable http2. * * Enable http2. * * ``` js * request.get('http://localhost/') * .http2() * .end(callback); * * request.get('http://localhost/') * .http2(true) * .end(callback); * ``` * * Disable http2. * * ``` js * request = request.http2(); * request.get('http://localhost/') * .http2(false) * .end(callback); * ``` * * @param {Boolean} enable * @return {Request} for chaining * @api public */ Request.prototype.http2 = function (bool) { if (exports.protocols['http2:'] === undefined) { throw new Error('superagent: this version of Node.js does not support http2'); } this._enableHttp2 = bool === undefined ? true : bool; return this; }; /** * Queue the given `file` as an attachment to the specified `field`, * with optional `options` (or filename). * * ``` js * request.post('http://localhost/upload') * .attach('field', Buffer.from('<b>Hello world</b>'), 'hello.html') * .end(callback); * ``` * * A filename may also be used: * * ``` js * request.post('http://localhost/upload') * .attach('files', 'image.jpg') * .end(callback); * ``` * * @param {String} field * @param {String|fs.ReadStream|Buffer} file * @param {String|Object} options * @return {Request} for chaining * @api public */ Request.prototype.attach = function (field, file, options) { if (file) { if (this._data) { throw new Error("superagent can't mix .send() and .attach()"); } let o = options || {}; if (typeof options === 'string') { o = { filename: options }; } if (typeof file === 'string') { if (!o.filename) o.filename = file; debug('creating `fs.ReadStream` instance for file: %s', file); file = fs.createReadStream(file); file.on('error', error => { const formData = this._getFormData(); formData.emit('error', error); }); } else if (!o.filename && file.path) { o.filename = file.path; } this._getFormData().append(field, file, o); } return this; }; Request.prototype._getFormData = function () { if (!this._formData) { this._formData = new FormData(); this._formData.on('error', error => { debug('FormData error', error); if (this.called) { // The request has already finished and the callback was called. // Silently ignore the error. return; } this.callback(error); this.abort(); }); } return this._formData; }; /** * Gets/sets the `Agent` to use for this HTTP request. The default (if this * function is not called) is to opt out of connection pooling (`agent: false`). * * @param {http.Agent} agent * @return {http.Agent} * @api public */ Request.prototype.agent = function (agent) { if (arguments.length === 0) return this._agent; this._agent = agent; return this; }; /** * Gets/sets the `lookup` function to use custom DNS resolver. * * @param {Function} lookup * @return {Function} * @api public */ Request.prototype.lookup = function (lookup) { if (arguments.length === 0) return this._lookup; this._lookup = lookup; return this; }; /** * Set _Content-Type_ response header passed through `mime.getType()`. * * Examples: * * request.post('/') * .type('xml') * .send(xmlstring) * .end(callback); * * request.post('/') * .type('json') * .send(jsonstring) * .end(callback); * * request.post('/') * .type('application/json') * .send(jsonstring) * .end(callback); * * @param {String} type * @return {Request} for chaining * @api public */ Request.prototype.type = function (type) { return this.set('Content-Type', type.includes('/') ? type : mime.getType(type)); }; /** * Set _Accept_ response header passed through `mime.getType()`. * * Examples: * * superagent.types.json = 'application/json'; * * request.get('/agent') * .accept('json') * .end(callback); * * request.get('/agent') * .accept('application/json') * .end(callback); * * @param {String} accept * @return {Request} for chaining * @api public */ Request.prototype.accept = function (type) { return this.set('Accept', type.includes('/') ? type : mime.getType(type)); }; /** * Add query-string `val`. * * Examples: * * request.get('/shoes') * .query('size=10') * .query({ color: 'blue' }) * * @param {Object|String} val * @return {Request} for chaining * @api public */ Request.prototype.query = function (value) { if (typeof value === 'string') { this._query.push(value); } else { Object.assign(this.qs, value); } return this; }; /** * Write raw `data` / `encoding` to the socket. * * @param {Buffer|String} data * @param {String} encoding * @return {Boolean} * @api public */ Request.prototype.write = function (data, encoding) { const request_ = this.request(); if (!this._streamRequest) { this._streamRequest = true; } return request_.write(data, encoding); }; /** * Pipe the request body to `stream`. * * @param {Stream} stream * @param {Object} options * @return {Stream} * @api public */ Request.prototype.pipe = function (stream, options) { this.piped = true; // HACK... this.buffer(false); this.end(); return this._pipeContinue(stream, options); }; Request.prototype._pipeContinue = function (stream, options) { this.req.once('response', res => { // redirect if (isRedirect(res.statusCode) && this._redirects++ !== this._maxRedirects) { return this._redirect(res) === this ? this._pipeContinue(stream, options) : undefined; } this.res = res; this._emitResponse(); if (this._aborted) return; if (this._shouldUnzip(res)) { const unzipObject = zlib.createUnzip(); unzipObject.on('error', error => { if (error && error.code === 'Z_BUF_ERROR') { // unexpected end of file is ignored by browsers and curl stream.emit('end'); return; } stream.emit('error', error); }); res.pipe(unzipObject).pipe(stream, options); // don't emit 'end' until unzipObject has completed writing all its data. unzipObject.once('end', () => this.emit('end')); } else { res.pipe(stream, options); res.once('end', () => this.emit('end')); } }); return stream; }; /** * Enable / disable buffering. * * @return {Boolean} [val] * @return {Request} for chaining * @api public */ Request.prototype.buffer = function (value) { this._buffer = value !== false; return this; }; /** * Redirect to `url * * @param {IncomingMessage} res * @return {Request} for chaining * @api private */ Request.prototype._redirect = function (res) { let url = res.headers.location; if (!url) { return this.callback(new Error('No location header for redirect'), res); } debug('redirect %s -> %s', this.url, url); // location url = resolve(this.url, url); // ensure the response is being consumed // this is required for Node v0.10+ res.resume(); let headers = this.req.getHeaders ? this.req.getHeaders() : this.req._headers; const changesOrigin = parse(url).host !== parse(this.url).host; // implementation of 302 following defacto standard if (res.statusCode === 301 || res.statusCode === 302) { // strip Content-* related fields // in case of POST etc headers = utils.cleanHeader(headers, changesOrigin); // force GET this.method = this.method === 'HEAD' ? 'HEAD' : 'GET'; // clear data this._data = null; } // 303 is always GET if (res.statusCode === 303) { // strip Content-* related fields // in case of POST etc headers = utils.cleanHeader(headers, changesOrigin); // force method this.method = 'GET'; // clear data this._data = null; } // 307 preserves method // 308 preserves method delete headers.host; delete this.req; delete this._formData; // remove all add header except User-Agent _initHeaders(this); // redirect this.res = res; this._endCalled = false; this.url = url; this.qs = {}; this._query.length = 0; this.set(headers); this._emitRedirect(); this._redirectList.push(this.url); this.end(this._callback); return this; }; /** * Set Authorization field value with `user` and `pass`. * * Examples: * * .auth('tobi', 'learnboost') * .auth('tobi:learnboost') * .auth('tobi') * .auth(accessToken, { type: 'bearer' }) * * @param {String} user * @param {String} [pass] * @param {Object} [options] options with authorization type 'basic' or 'bearer' ('basic' is default) * @return {Request} for chaining * @api public */ Request.prototype.auth = function (user, pass, options) { if (arguments.length === 1) pass = ''; if (typeof pass === 'object' && pass !== null) { // pass is optional and can be replaced with options options = pass; pass = ''; } if (!options) { options = { type: 'basic' }; } const encoder = string => Buffer.from(string).toString('base64'); return this._auth(user, pass, options, encoder); }; /** * Set the certificate authority option for https request. * * @param {Buffer | Array} cert * @return {Request} for chaining * @api public */ Request.prototype.ca = function (cert) { this._ca = cert; return this; }; /** * Set the client certificate key option for https request. * * @param {Buffer | String} cert * @return {Request} for chaining * @api public */ Request.prototype.key = function (cert) { this._key = cert; return this; }; /** * Set the key, certificate, and CA certs of the client in PFX or PKCS12 format. * * @param {Buffer | String} cert * @return {Request} for chaining * @api public */ Request.prototype.pfx = function (cert) { if (typeof cert === 'object' && !Buffer.isBuffer(cert)) { this._pfx = cert.pfx; this._passphrase = cert.passphrase; } else { this._pfx = cert; } return this; }; /** * Set the client certificate option for https request. * * @param {Buffer | String} cert * @return {Request} for chaining * @api public */ Request.prototype.cert = function (cert) { this._cert = cert; return this; }; /** * Do not reject expired or invalid TLS certs. * sets `rejectUnauthorized=true`. Be warned that this allows MITM attacks. * * @return {Request} for chaining * @api public */ Request.prototype.disableTLSCerts = function () { this._disableTLSCerts = true; return this; }; /** * Return an http[s] request. * * @return {OutgoingMessage} * @api private */ // eslint-disable-next-line complexity Request.prototype.request = function () { if (this.req) return this.req; const options = {}; try { const query = qs.stringify(this.qs, { indices: false, strictNullHandling: true }); if (query) { this.qs = {}; this._query.push(query); } this._finalizeQueryString(); } catch (err) { return this.emit('error', err); } let url = this.url; const retries = this._retries; // Capture backticks as-is from the final query string built above. // Note: this'll only find backticks entered in req.query(String) // calls, because qs.stringify unconditionally encodes backticks. let queryStringBackticks; if (url.includes('`')) { const queryStartIndex = url.indexOf('?'); if (queryStartIndex !== -1) { const queryString = url.slice(queryStartIndex + 1); queryStringBackticks = queryString.match(/`|%60/g); } } // default to http:// if (url.indexOf('http') !== 0) url = `http://${url}`; url = parse(url); // See https://github.com/ladjs/superagent/issues/1367 if (queryStringBackticks) { let i = 0; url.query = url.query.replace(/%60/g, () => queryStringBackticks[i++]); url.search = `?${url.query}`; url.path = url.pathname + url.search; } // support unix sockets if (/^https?\+unix:/.test(url.protocol) === true) { // get the protocol url.protocol = `${url.protocol.split('+')[0]}:`; // get the socket, path const unixParts = url.path.match(/^([^/]+)(.+)$/); options.socketPath = unixParts[1].replace(/%2F/g, '/'); url.path = unixParts[2]; } // Override IP address of a hostname if (this._connectOverride) { const _url = url, hostname = _url.hostname; const match = hostname in this._connectOverride ? this._connectOverride[hostname] : this._connectOverride['*']; if (match) { // backup the real host if (!this._header.host) { this.set('host', url.host); } let newHost; let newPort; if (typeof match === 'object') { newHost = match.host; newPort = match.port; } else { newHost = match; newPort = url.port; } // wrap [ipv6] url.host = /:/.test(newHost) ? `[${newHost}]` : newHost; if (newPort) { url.host += `:${newPort}`; url.port = newPort; } url.hostname = newHost; } } // options options.method = this.method; options.port = url.port; options.path = url.path; options.host = url.hostname; options.ca = this._ca; options.key = this._key; options.pfx = this._pfx; options.cert = this._cert; options.passphrase = this._passphrase; options.agent = this._agent; options.lookup = this._lookup; options.rejectUnauthorized = typeof this._disableTLSCerts === 'boolean' ? !this._disableTLSCerts : process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0'; // Allows request.get('https://1.2.3.4/').set('Host', 'example.com') if (this._header.host) { options.servername = this._header.host.replace(/:\d+$/, ''); } if (this._trustLocalhost && /^(?:localhost|127\.0\.0\.\d+|(0*:)+:0*1)$/.test(url.hostname)) { options.rejectUnauthorized = false; } // initiate request const module_ = this._enableHttp2 ? exports.protocols['http2:'].setProtocol(url.protocol) : exports.protocols[url.protocol]; // request this.req = module_.request(options); const req = this.req; // set tcp no delay req.setNoDelay(true); if (options.method !== 'HEAD') { req.setHeader('Accept-Encoding', 'gzip, deflate'); } this.protocol = url.protocol; this.host = url.host; // expose events req.once('drain', () => { this.emit('drain'); }); req.on('error', error => { // flag abortion here for out timeouts // because node will emit a faux-error "socket hang up" // when request is aborted before a connection is made if (this._aborted) return; // if not the same, we are in the **old** (cancelled) request, // so need to continue (same as for above) if (this._retries !== retries) return; // if we've received a response then we don't want to let // an error in the request blow up the response if (this.response) return; this.callback(error); }); // auth if (url.auth) { const auth = url.auth.split(':'); this.auth(auth[0], auth[1]); } if (this.username && this.password) { this.auth(this.username, this.password); } for (const key in this.header) { if (hasOwn(this.header, key)) req.setHeader(key, this.header[key]); } // add cookies if (this.cookies) { if (hasOwn(this._header, 'cookie')) { // merge const temporaryJar = new CookieJar.CookieJar(); temporaryJar.setCookies(this._header.cookie.split('; ')); temporaryJar.setCookies(this.cookies.split('; ')); req.setHeader('Cookie', temporaryJar.getCookies(CookieJar.CookieAccessInfo.All).toValueString()); } else { req.setHeader('Cookie', this.cookies); } } return req; }; /** * Invoke the callback with `err` and `res` * and handle arity check. * * @param {Error} err * @param {Response} res * @api private */ Request.prototype.callback = function (error, res) { if (this._shouldRetry(error, res)) { return this._retry(); } // Avoid the error which is emitted from 'socket hang up' to cause the fn undefined error on JS runtime. const fn = this._callback || noop; this.clearTimeout(); if (this.called) return console.warn('superagent: double callback bug'); this.called = true; if (!error) { try { if (!this._isResponseOK(res)) { let message = 'Unsuccessful HTTP response'; if (res) { message = http.STATUS_CODES[res.status] || message; } error = new Error(message); error.status = res ? res.status : undefined; } } catch (err) { error = err; error.status = error.status || (res ? res.status : undefined); } } // It's important that the callback is called outside try/catch // to avoid double callback if (!error) { return fn(null, res); } error.response = res; if (this._maxRetries) error.retries = this._retries - 1; // only emit error event if there is a listener // otherwise we assume the callback to `.end()` will get the error if (error && this.listeners('error').length > 0) { this.emit('error', error); } fn(error, res); }; /** * Check if `obj` is a host object, * * @param {Object} obj host object * @return {Boolean} is a host object * @api private */ Request.prototype._isHost = function (object) { return Buffer.isBuffer(object) || object instanceof Stream || object instanceof FormData; }; /** * Initiate request, invoking callback `fn(err, res)` * with an instanceof `Response`. * * @param {Function} fn * @return {Request} for chaining * @api public */ Request.prototype._emitResponse = function (body, files) { const response = new Response(this); this.response = response; response.redirects = this._redirectList; if (undefined !== body) { response.body = body; } response.files = files; if (this._endCalled) { response.pipe = function () { throw new Error("end() has already been called, so it's too late to start piping"); }; } this.emit('response', response); return response; }; /** * Emit `redirect` event, passing an instanceof `Response`. * * @api private */ Request.prototype._emitRedirect = function () { const response = new Response(this); response.redirects = this._redirectList; this.emit('redirect', response); }; Request.prototype.end = function (fn) { this.request(); debug('%s %s', this.method, this.url); if (this._endCalled) { throw new Error('.end() was called twice. This is not supported in superagent'); } this._endCalled = true; // store callback this._callback = fn || noop; this._end(); }; Request.prototype._end = function () { if (this._aborted) return this.callback(new Error('The request has been aborted even before .end() was called')); let data = this._data; const req = this.req; const method = this.method; this._setTimeouts(); // body if (method !== 'HEAD' && !req._headerSent) { // serialize stuff if (typeof data !== 'string') { let contentType = req.getHeader('Content-Type'); // Parse out just the content type from the header (ignore the charset) if (contentType) contentType = contentType.split(';')[0]; let serialize = this._serializer || exports.serialize[contentType]; if (!serialize && isJSON(contentType)) { serialize = exports.serialize['application/json']; } if (serialize) data = serialize(data); } // content-length if (data && !req.getHeader('Content-Length')) { req.setHeader('Content-Length', Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data)); } } // response // eslint-disable-next-line complexity req.once('response', res => { debug('%s %s -> %s', this.method, this.url, res.statusCode); if (this._responseTimeoutTimer) { clearTimeout(this._responseTimeoutTimer); } if (this.piped) { return; } const max = this._maxRedirects; const mime = utils.type(res.headers['content-type'] || '') || 'text/plain'; let type = mime.split('/')[0]; if (type) type = type.toLowerCase().trim(); const multipart = type === 'multipart'; const redirect = isRedirect(res.statusCode); const responseType = this._responseType; this.res = res; // redirect if (redirect && this._redirects++ !== max) { return this._redirect(res); } if (this.method === 'HEAD') { this.emit('end'); this.callback(null, this._emitResponse()); return; } // zlib support if (this._shouldUnzip(res)) { unzip(req, res); } let buffer = this._buffer; if (buffer === undefined && mime in exports.buffer) { buffer = Boolean(exports.buffer[mime]); } let parser = this._parser; if (undefined === buffer && parser) { console.warn("A custom superagent parser has been set, but buffering strategy for the parser hasn't been configured. Call `req.buffer(true or false)` or set `superagent.buffer[mime] = true or false`"); buffer = true; } if (!parser) { if (responseType) { parser = exports.parse.image; // It's actually a generic Buffer buffer = true; } else if (multipart) { const form = formidable(); parser = form.parse.bind(form); buffer = true; } else if (isBinary(mime)) { parser = exports.parse.image; buffer = true; // For backwards-compatibility buffering default is ad-hoc MIME-dependent } else if (exports.parse[mime]) { parser = exports.parse[mime]; } else if (type === 'text') { parser = exports.parse.text; buffer = buffer !== false; // everyone wants their own white-labeled json } else if (isJSON(mime)) { parser = exports.parse['application/json']; buffer = buffer !== false; } else if (buffer) { parser = exports.parse.text; } else if (undefined === buffer) { parser = exports.parse.image; // It's actually a generic Buffer buffer = true; } } // by default only buffer text/*, json and messed up thing from hell if (undefined === buffer && isText(mime) || isJSON(mime)) { buffer = true; } this._resBuffered = buffer; let parserHandlesEnd = false; if (buffer) { // Protectiona against zip bombs and other nuisance let responseBytesLeft = this._maxResponseSize || 200000000; res.on('data', buf => { responseBytesLeft -= buf.byteLength || buf.length > 0 ? buf.length : 0; if (responseBytesLeft < 0) { // This will propagate through error event const error = new Error('Maximum response size reached'); error.code = 'ETOOLARGE'; // Parsers aren't required to observe error event, // so would incorrectly report success parserHandlesEnd = false; // Will not emit error event res.destroy(error); // so we do callback now this.callback(error, null); } }); } if (parser) { try { // Unbuffered parsers are supposed to emit response early, // which is weird BTW, because response.body won't be there. parserHandlesEnd = buffer; parser(res, (error, object, files) => { if (this.timedout) { // Timeout has already handled all callbacks return; } // Intentional (non-timeout) abort is supposed to preserve partial response, // even if it doesn't parse. if (error && !this._aborted) { return this.callback(error); } if (parserHandlesEnd) { this.emit('end'); this.callback(null, this._emitResponse(object, files)); } }); } catch (err) { this.callback(err); return; } } this.res = res; // unbuffered if (!buffer) { debug('unbuffered %s %s', this.method, this.url); this.callback(null, this._emitResponse()); if (multipart) return; // allow multipart to handle end event res.once('end', () => { debug('end %s %s', this.method, this.url); this.emit('end'); }); return; } // terminating events res.once('error', error => { parserHandlesEnd = false; this.callback(error, null); }); if (!parserHandlesEnd) res.once('end', () => { debug('end %s %s', this.method, this.url); // TODO: unless buffering emit earlier to stream this.emit('end'); this.callback(null, this._emitResponse()); }); }); this.emit('request', this); const getProgressMonitor = () => { const lengthComputable = true; const total = req.getHeader('Content-Length'); let loaded = 0; const progress = new Stream.Transform(); progress._transform = (chunk, encoding, callback) => { loaded += chunk.length; this.emit('progress', { direction: 'upload', lengthComputable, loaded, total }); callback(null, chunk); }; return progress; }; const bufferToChunks = buffer => { const chunkSize = 16 * 1024; // default highWaterMark value const chunking = new Stream.Readable(); const totalLength = buffer.length; const remainder = totalLength % chunkSize; const cutoff = totalLength - remainder; for (let i = 0; i < cutoff; i += chunkSize) { const chunk = buffer.slice(i, i + chunkSize); chunking.push(chunk); } if (remainder > 0) { const remainderBuffer = buffer.slice(-remainder); chunking.push(remainderBuffer); } chunking.push(null); // no more data return chunking; }; // if a FormData instance got created, then we send that as the request body const formData = this._formData; if (formData) { // set headers const headers = formData.getHeaders(); for (const i in headers) { if (hasOwn(headers, i)) { debug('setting FormData header: "%s: %s"', i, headers[i]); req.setHeader(i, headers[i]); } } // attempt to get "Content-Length" header formData.getLength((error, length) => { // TODO: Add chunked encoding when no length (if err) if (error) debug('formData.getLength had error', error, length); debug('got FormData Content-Length: %s', length); if (typeof length === 'number') { req.setHeader('Content-Length', length); } formData.pipe(getProgressMonitor()).pipe(req); }); } else if (Buffer.isBuffer(data)) { bufferToChunks(data).pipe(getProgressMonitor()).pipe(req); } else { req.end(data); } }; // Check whether response has a non-0-sized gzip-encoded body Request.prototype._shouldUnzip = res => { if (res.statusCode === 204 || res.statusCode === 304) { // These aren't supposed to have any body return false; } // header content is a string, and distinction between 0 and no information is crucial if (res.headers['content-length'] === '0') { // We know that the body is empty (unfortunately, this check does not cover chunked encoding) return false; } // console.log(res); return /^\s*(?:deflate|gzip)\s*$/.test(res.headers['content-encoding']); }; /** * Overrides DNS for selected hostnames. Takes object mapping hostnames to IP addresses. * * When making a request to a URL with a hostname exactly matching a key in the object, * use the given IP address to connect, instead of using DNS to resolve the hostname. * * A special host `*` matches every hostname (keep redirects in mind!) * * request.connect({ * 'test.example.com': '127.0.0.1', * 'ipv6.example.com': '::1', * }) */ Request.prototype.connect = function (connectOverride) { if (typeof connectOverride === 'string') { this._connectOverride = { '*': connectOverride }; } else if (typeof connectOverride === 'object') { this._connectOverride = connectOverride; } else { this._connectOverride = undefined; } return this; }; Request.prototype.trustLocalhost = function (toggle) { this._trustLocalhost = toggle === undefined ? true : toggle; return this; }; // generate HTTP verb methods if (!methods.includes('del')) { // create a copy so we don't cause conflicts with // other packages using the methods package and // npm 3.x methods = [...methods]; methods.push('del'); } var _iterator = _createForOfIteratorHelper(methods), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { let method = _step.value; const name = method; method = method === 'del' ? 'delete' : method; method = method.toUpperCase(); request[name] = (url, data, fn) => { const request_ = request(method, url); if (typeof data === 'function') { fn = data; data = null; } if (data) { if (method === 'GET' || method === 'HEAD') { request_.query(data); } else { request_.send(data); } } if (fn) request_.end(fn); return request_; }; } /** * Check if `mime` is text and should be buffered. * * @param {String} mime * @return {Boolean} * @api public */ } catch (err) { _iterator.e(err); } finally { _iterator.f(); } function isText(mime) { const parts = mime.split('/'); let type = parts[0]; if (type) type = type.toLowerCase().trim(); let subtype = parts[1]; if (subtype) subtype = subtype.toLowerCase().trim(); return type === 'text' || subtype === 'x-www-form-urlencoded'; } // This is not a catchall, but a start. It might be useful // in the long run to have file that includes all binary // content types from https://www.iana.org/assignments/media-types/media-types.xhtml function isBinary(mime) { let _mime$split = mime.split('/'), _mime$split2 = _slicedToArray(_mime$split, 2), registry = _mime$split2[0], name = _mime$split2[1]; if (registry) registry = registry.toLowerCase().trim(); if (name) name = name.toLowerCase().trim(); return ['audio', 'font', 'image', 'video'].includes(registry) || ['gz', 'gzip'].includes(name); } /** * Check if `mime` is json or has +json structured syntax suffix. * * @param {String} mime * @return {Boolean} * @api private */ function isJSON(mime) { // should match /json or +json // but not /json-seq return /[/+]json($|[^-\w])/i.test(mime); } /** * Check if we should follow the redirect `code`. * * @param {Number} code * @return {Boolean} * @api private */ function isRedirect(code) { return [301, 302, 303, 305, 307, 308].includes(code); } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVxdWlyZSIsInJlcXVpcmUiLCJwYXJzZSIsImZvcm1hdCIsInJlc29sdmUiLCJTdHJlYW0iLCJodHRwcyIsImh0dHAiLCJmcyIsInpsaWIiLCJ1dGlsIiwicXMiLCJtaW1lIiwibWV0aG9kcyIsIkZvcm1EYXRhIiwiZm9ybWlkYWJsZSIsImRlYnVnIiwiQ29va2llSmFyIiwic2VtdmVyR3RlIiwic2FmZVN0cmluZ2lmeSIsInV0aWxzIiwiUmVxdWVzdEJhc2UiLCJfcmVxdWlyZTIiLCJ1bnppcCIsIlJlc3BvbnNlIiwibWl4aW4iLCJoYXNPd24iLCJodHRwMiIsInByb2Nlc3MiLCJ2ZXJzaW9uIiwicmVxdWVzdCIsIm1ldGhvZCIsInVybCIsImV4cG9ydHMiLCJSZXF1ZXN0IiwiZW5kIiwiYXJndW1lbnRzIiwibGVuZ3RoIiwibW9kdWxlIiwiYWdlbnQiLCJub29wIiwiZGVmaW5lIiwicHJvdG9jb2xzIiwic2VyaWFsaXplIiwic3RyaW5naWZ5IiwiYnVmZmVyIiwiX2luaXRIZWFkZXJzIiwicmVxdWVzdF8iLCJfaGVhZGVyIiwiaGVhZGVyIiwiY2FsbCIsIl9lbmFibGVIdHRwMiIsIkJvb2xlYW4iLCJlbnYiLCJIVFRQMl9URVNUIiwiX2FnZW50IiwiX2Zvcm1EYXRhIiwid3JpdGFibGUiLCJfcmVkaXJlY3RzIiwicmVkaXJlY3RzIiwiY29va2llcyIsIl9xdWVyeSIsInFzUmF3IiwiX3JlZGlyZWN0TGlzdCIsIl9zdHJlYW1SZXF1ZXN0IiwiX2xvb2t1cCIsInVuZGVmaW5lZCIsIm9uY2UiLCJjbGVhclRpbWVvdXQiLCJiaW5kIiwiaW5oZXJpdHMiLCJwcm90b3R5cGUiLCJib29sIiwiRXJyb3IiLCJhdHRhY2giLCJmaWVsZCIsImZpbGUiLCJvcHRpb25zIiwiX2RhdGEiLCJvIiwiZmlsZW5hbWUiLCJjcmVhdGVSZWFkU3RyZWFtIiwib24iLCJlcnJvciIsImZvcm1EYXRhIiwiX2dldEZvcm1EYXRhIiwiZW1pdCIsInBhdGgiLCJhcHBlbmQiLCJjYWxsZWQiLCJjYWxsYmFjayIsImFib3J0IiwibG9va3VwIiwidHlwZSIsInNldCIsImluY2x1ZGVzIiwiZ2V0VHlwZSIsImFjY2VwdCIsInF1ZXJ5IiwidmFsdWUiLCJwdXNoIiwiT2JqZWN0IiwiYXNzaWduIiwid3JpdGUiLCJkYXRhIiwiZW5jb2RpbmciLCJwaXBlIiwic3RyZWFtIiwicGlwZWQiLCJfcGlwZUNvbnRpbnVlIiwicmVxIiwicmVzIiwiaXNSZWRpcmVjdCIsInN0YXR1c0NvZGUiLCJfbWF4UmVkaXJlY3RzIiwiX3JlZGlyZWN0IiwiX2VtaXRSZXNwb25zZSIsIl9hYm9ydGVkIiwiX3Nob3VsZFVuemlwIiwidW56aXBPYmplY3QiLCJjcmVhdGVVbnppcCIsImNvZGUiLCJfYnVmZmVyIiwiaGVhZGVycyIsImxvY2F0aW9uIiwicmVzdW1lIiwiZ2V0SGVhZGVycyIsIl9oZWFkZXJzIiwiY2hhbmdlc09yaWdpbiIsImhvc3QiLCJjbGVhbkhlYWRlciIsIl9lbmRDYWxsZWQiLCJfZW1pdFJlZGlyZWN0IiwiX2NhbGxiYWNrIiwiYXV0aCIsInVzZXIiLCJwYXNzIiwiZW5jb2RlciIsInN0cmluZyIsIkJ1ZmZlciIsImZyb20iLCJ0b1N0cmluZyIsIl9hdXRoIiwiY2EiLCJjZXJ0IiwiX2NhIiwia2V5IiwiX2tleSIsInBmeCIsImlzQnVmZmVyIiwiX3BmeCIsIl9wYXNzcGhyYXNlIiwicGFzc3BocmFzZSIsIl9jZXJ0IiwiZGlzYWJsZVRMU0NlcnRzIiwiX2Rpc2FibGVUTFNDZXJ0cyIsImluZGljZXMiLCJzdHJpY3ROdWxsSGFuZGxpbmciLCJfZmluYWxpemVRdWVyeVN0cmluZyIsImVyciIsInJldHJpZXMiLCJfcmV0cmllcyIsInF1ZXJ5U3RyaW5nQmFja3RpY2tzIiwicXVlcnlTdGFydEluZGV4IiwiaW5kZXhPZiIsInF1ZXJ5U3RyaW5nIiwic2xpY2UiLCJtYXRjaCIsImkiLCJyZXBsYWNlIiwic2VhcmNoIiwicGF0aG5hbWUiLCJ0ZXN0IiwicHJvdG9jb2wiLCJzcGxpdCIsInVuaXhQYXJ0cyIsInNvY2tldFBhdGgiLCJfY29ubmVjdE92ZXJyaWRlIiwiX3VybCIsImhvc3RuYW1lIiwibmV3SG9zdCIsIm5ld1BvcnQiLCJwb3J0IiwicmVqZWN0VW5hdXRob3JpemVkIiwiTk9ERV9UTFNfUkVKRUNUX1VOQVVUSE9SSVpFRCIsInNlcnZlcm5hbWUiLCJfdHJ1c3RMb2NhbGhvc3QiLCJtb2R1bGVfIiwic2V0UHJvdG9jb2wiLCJzZXROb0RlbGF5Iiwic2V0SGVhZGVyIiwicmVzcG9uc2UiLCJ1c2VybmFtZSIsInBhc3N3b3JkIiwidGVtcG9yYXJ5SmFyIiwic2V0Q29va2llcyIsImNvb2tpZSIsImdldENvb2tpZXMiLCJDb29raWVBY2Nlc3NJbmZvIiwiQWxsIiwidG9WYWx1ZVN0cmluZyIsIl9zaG91bGRSZXRyeSIsIl9yZXRyeSIsImZuIiwiY29uc29sZSIsIndhcm4iLCJfaXNSZXNwb25zZU9LIiwibWVzc2FnZSIsIlNUQVRVU19DT0RFUyIsInN0YXR1cyIsIl9tYXhSZXRyaWVzIiwibGlzdGVuZXJzIiwiX2lzSG9zdCIsIm9iamVjdCIsImJvZHkiLCJmaWxlcyIsIl9lbmQiLCJfc2V0VGltZW91dHMiLCJfaGVhZGVyU2VudCIsImNvbnRlbnRUeXBlIiwiZ2V0SGVhZGVyIiwiX3NlcmlhbGl6ZXIiLCJpc0pTT04iLCJieXRlTGVuZ3RoIiwiX3Jlc3BvbnNlVGltZW91dFRpbWVyIiwibWF4IiwidG9Mb3dlckNhc2UiLCJ0cmltIiwibXVsdGlwYXJ0IiwicmVkaXJlY3QiLCJyZXNwb25zZVR5cGUiLCJfcmVzcG9uc2VUeXBlIiwicGFyc2VyIiwiX3BhcnNlciIsImltYWdlIiwiZm9ybSIsImlzQmluYXJ5IiwidGV4dCIsImlzVGV4dCIsIl9yZXNCdWZmZXJlZCIsInBhcnNlckhhbmRsZXNFbmQiLCJyZXNwb25zZUJ5dGVzTGVmdCIsIl9tYXhSZXNwb25zZVNpemUiLCJidWYiLCJkZXN0cm95IiwidGltZWRvdXQiLCJnZXRQcm9ncmVzc01vbml0b3IiLCJsZW5ndGhDb21wdXRhYmxlIiwidG90YWwiLCJsb2FkZWQiLCJwcm9ncmVzcyIsIlRyYW5zZm9ybSIsIl90cmFuc2Zvcm0iLCJjaHVuayIsImRpcmVjdGlvbiIsImJ1ZmZlclRvQ2h1bmtzIiwiY2h1bmtTaXplIiwiY2h1bmtpbmciLCJSZWFkYWJsZSIsInRvdGFsTGVuZ3RoIiwicmVtYWluZGVyIiwiY3V0b2ZmIiwicmVtYWluZGVyQnVmZmVyIiwiZ2V0TGVuZ3RoIiwiY29ubmVjdCIsImNvbm5lY3RPdmVycmlkZSIsInRydXN0TG9jYWxob3N0IiwidG9nZ2xlIiwiX2l0ZXJhdG9yIiwiX2NyZWF0ZUZvck9mSXRlcmF0b3JIZWxwZXIiLCJfc3RlcCIsInMiLCJuIiwiZG9uZSIsIm5hbWUiLCJ0b1VwcGVyQ2FzZSIsInNlbmQiLCJlIiwiZiIsInBhcnRzIiwic3VidHlwZSIsIl9taW1lJHNwbGl0IiwiX21pbWUkc3BsaXQyIiwiX3NsaWNlZFRvQXJyYXkiLCJyZWdpc3RyeSJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ub2RlL2luZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTW9kdWxlIGRlcGVuZGVuY2llcy5cbiAqL1xuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm9kZS9uby1kZXByZWNhdGVkLWFwaVxuY29uc3QgeyBwYXJzZSwgZm9ybWF0LCByZXNvbHZlIH0gPSByZXF1aXJlKCd1cmwnKTtcbmNvbnN0IFN0cmVhbSA9IHJlcXVpcmUoJ3N0cmVhbScpO1xuY29uc3QgaHR0cHMgPSByZXF1aXJlKCdodHRwcycpO1xuY29uc3QgaHR0cCA9IHJlcXVpcmUoJ2h0dHAnKTtcbmNvbnN0IGZzID0gcmVxdWlyZSgnZnMnKTtcbmNvbnN0IHpsaWIgPSByZXF1aXJlKCd6bGliJyk7XG5jb25zdCB1dGlsID0gcmVxdWlyZSgndXRpbCcpO1xuY29uc3QgcXMgPSByZXF1aXJlKCdxcycpO1xuY29uc3QgbWltZSA9IHJlcXVpcmUoJ21pbWUnKTtcbmxldCBtZXRob2RzID0gcmVxdWlyZSgnbWV0aG9kcycpO1xuY29uc3QgRm9ybURhdGEgPSByZXF1aXJlKCdmb3JtLWRhdGEnKTtcbmNvbnN0IGZvcm1pZGFibGUgPSByZXF1aXJlKCdmb3JtaWRhYmxlJyk7XG5jb25zdCBkZWJ1ZyA9IHJlcXVpcmUoJ2RlYnVnJykoJ3N1cGVyYWdlbnQnKTtcbmNvbnN0IENvb2tpZUphciA9IHJlcXVpcmUoJ2Nvb2tpZWphcicpO1xuY29uc3Qgc2VtdmVyR3RlID0gcmVxdWlyZSgnc2VtdmVyL2Z1bmN0aW9ucy9ndGUnKTtcbmNvbnN0IHNhZmVTdHJpbmdpZnkgPSByZXF1aXJlKCdmYXN0LXNhZmUtc3RyaW5naWZ5Jyk7XG5cbmNvbnN0IHV0aWxzID0gcmVxdWlyZSgnLi4vdXRpbHMnKTtcbmNvbnN0IFJlcXVlc3RCYXNlID0gcmVxdWlyZSgnLi4vcmVxdWVzdC1iYXNlJyk7XG5jb25zdCB7IHVuemlwIH0gPSByZXF1aXJlKCcuL3VuemlwJyk7XG5jb25zdCBSZXNwb25zZSA9IHJlcXVpcmUoJy4vcmVzcG9uc2UnKTtcblxuY29uc3QgeyBtaXhpbiwgaGFzT3duIH0gPSB1dGlscztcblxubGV0IGh0dHAyO1xuXG5pZiAoc2VtdmVyR3RlKHByb2Nlc3MudmVyc2lvbiwgJ3YxMC4xMC4wJykpIGh0dHAyID0gcmVxdWlyZSgnLi9odHRwMndyYXBwZXInKTtcblxuZnVuY3Rpb24gcmVxdWVzdChtZXRob2QsIHVybCkge1xuICAvLyBjYWxsYmFja1xuICBpZiAodHlwZW9mIHVybCA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIHJldHVybiBuZXcgZXhwb3J0cy5SZXF1ZXN0KCdHRVQnLCBtZXRob2QpLmVuZCh1cmwpO1xuICB9XG5cbiAgLy8gdXJsIGZpcnN0XG4gIGlmIChhcmd1bWVudHMubGVuZ3RoID09PSAxKSB7XG4gICAgcmV0dXJuIG5ldyBleHBvcnRzLlJlcXVlc3QoJ0dFVCcsIG1ldGhvZCk7XG4gIH1cblxuICByZXR1cm4gbmV3IGV4cG9ydHMuUmVxdWVzdChtZXRob2QsIHVybCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gcmVxdWVzdDtcbmV4cG9ydHMgPSBtb2R1bGUuZXhwb3J0cztcblxuLyoqXG4gKiBFeHBvc2UgYFJlcXVlc3RgLlxuICovXG5cbmV4cG9ydHMuUmVxdWVzdCA9IFJlcXVlc3Q7XG5cbi8qKlxuICogRXhwb3NlIHRoZSBhZ2VudCBmdW5jdGlvblxuICovXG5cbmV4cG9ydHMuYWdlbnQgPSByZXF1aXJlKCcuL2FnZW50Jyk7XG5cbi8qKlxuICogTm9vcC5cbiAqL1xuXG5mdW5jdGlvbiBub29wKCkge31cblxuLyoqXG4gKiBFeHBvc2UgYFJlc3BvbnNlYC5cbiAqL1xuXG5leHBvcnRzLlJlc3BvbnNlID0gUmVzcG9uc2U7XG5cbi8qKlxuICogRGVmaW5lIFwiZm9ybVwiIG1pbWUgdHlwZS5cbiAqL1xuXG5taW1lLmRlZmluZShcbiAge1xuICAgICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnOiBbJ2Zvcm0nLCAndXJsZW5jb2RlZCcsICdmb3JtLWRhdGEnXVxuICB9LFxuICB0cnVlXG4pO1xuXG4vKipcbiAqIFByb3RvY29sIG1hcC5cbiAqL1xuXG5leHBvcnRzLnByb3RvY29scyA9IHtcbiAgJ2h0dHA6JzogaHR0cCxcbiAgJ2h0dHBzOic6IGh0dHBzLFxuICAnaHR0cDI6JzogaHR0cDJcbn07XG5cbi8qKlxuICogRGVmYXVsdCBzZXJpYWxpemF0aW9uIG1hcC5cbiAqXG4gKiAgICAgc3VwZXJhZ2VudC5zZXJpYWxpemVbJ2FwcGxpY2F0aW9uL3htbCddID0gZnVuY3Rpb24ob2JqKXtcbiAqICAgICAgIHJldHVybiAnZ2VuZXJhdGVkIHhtbCBoZXJlJztcbiAqICAgICB9O1xuICpcbiAqL1xuXG5leHBvcnRzLnNlcmlhbGl6ZSA9IHtcbiAgJ2FwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCc6IHFzLnN0cmluZ2lmeSxcbiAgJ2FwcGxpY2F0aW9uL2pzb24nOiBzYWZlU3RyaW5naWZ5XG59O1xuXG4vKipcbiAqIERlZmF1bHQgcGFyc2Vycy5cbiAqXG4gKiAgICAgc3VwZXJhZ2VudC5wYXJzZVsnYXBwbGljYXRpb24veG1sJ10gPSBmdW5jdGlvbihyZXMsIGZuKXtcbiAqICAgICAgIGZuKG51bGwsIHJlcyk7XG4gKiAgICAgfTtcbiAqXG4gKi9cblxuZXhwb3J0cy5wYXJzZSA9IHJlcXVpcmUoJy4vcGFyc2VycycpO1xuXG4vKipcbiAqIERlZmF1bHQgYnVmZmVyaW5nIG1hcC4gQ2FuIGJlIHVzZWQgdG8gc2V0IGNlcnRhaW5cbiAqIHJlc3BvbnNlIHR5cGVzIHRvIGJ1ZmZlci9ub3QgYnVmZmVyLlxuICpcbiAqICAgICBzdXBlcmFnZW50LmJ1ZmZlclsnYXBwbGljYXRpb24veG1sJ10gPSB0cnVlO1xuICovXG5leHBvcnRzLmJ1ZmZlciA9IHt9O1xuXG4vKipcbiAqIEluaXRpYWxpemUgaW50ZXJuYWwgaGVhZGVyIHRyYWNraW5nIHByb3BlcnRpZXMgb24gYSByZXF1ZXN0IGluc3RhbmNlLlxuICpcbiAqIEBwYXJhbSB7T2JqZWN0fSByZXEgdGhlIGluc3RhbmNlXG4gKiBAYXBpIHByaXZhdGVcbiAqL1xuZnVuY3Rpb24gX2luaXRIZWFkZXJzKHJlcXVlc3RfKSB7XG4gIHJlcXVlc3RfLl9oZWFkZXIgPSB7XG4gICAgLy8gY29lcmNlcyBoZWFkZXIgbmFtZXMgdG8gbG93ZXJjYXNlXG4gIH07XG4gIHJlcXVlc3RfLmhlYWRlciA9IHtcbiAgICAvLyBwcmVzZXJ2ZXMgaGVhZGVyIG5hbWUgY2FzZVxuICB9O1xufVxuXG4vKipcbiAqIEluaXRpYWxpemUgYSBuZXcgYFJlcXVlc3RgIHdpdGggdGhlIGdpdmVuIGBtZXRob2RgIGFuZCBgdXJsYC5cbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gbWV0aG9kXG4gKiBAcGFyYW0ge1N0cmluZ3xPYmplY3R9IHVybFxuICogQGFwaSBwdWJsaWNcbiAqL1xuXG5mdW5jdGlvbiBSZXF1ZXN0KG1ldGhvZCwgdXJsKSB7XG4gIFN0cmVhbS5jYWxsKHRoaXMpO1xuICBpZiAodHlwZW9mIHVybCAhPT0gJ3N0cmluZycpIHVybCA9IGZvcm1hdCh1cmwpO1xuICB0aGlzLl9lbmFibGVIdHRwMiA9IEJvb2xlYW4ocHJvY2Vzcy5lbnYuSFRUUDJfVEVTVCk7IC8vIGludGVybmFsIG9ubHlcbiAgdGhpcy5fYWdlbnQgPSBmYWxzZTtcbiAgdGhpcy5fZm9ybURhdGEgPSBudWxsO1xuICB0aGlzLm1ldGhvZCA9IG1ldGhvZDtcbiAgdGhpcy51cmwgPSB1cmw7XG4gIF9pbml0SGVhZGVycyh0aGlzKTtcbiAgdGhpcy53cml0YWJsZSA9IHRydWU7XG4gIHRoaXMuX3JlZGlyZWN0cyA9IDA7XG4gIHRoaXMucmVkaXJlY3RzKG1ldGhvZCA9PT0gJ0hFQUQnID8gMCA6IDUpO1xuICB0aGlzLmNvb2tpZXMgPSAnJztcbiAgdGhpcy5xcyA9IHt9O1xuICB0aGlzLl9xdWVyeSA9IFtdO1xuICB0aGlzLnFzUmF3ID0gdGhpcy5fcXVlcnk7IC8vIFVudXNlZCwgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5IG9ubHlcbiAgdGhpcy5fcmVkaXJlY3RMaXN0ID0gW107XG4gIHRoaXMuX3N0cmVhbVJlcXVlc3QgPSBmYWxzZTtcbiAgdGhpcy5fbG9va3VwID0gdW5kZWZpbmVkO1xuICB0aGlzLm9uY2UoJ2VuZCcsIHRoaXMuY2xlYXJUaW1lb3V0LmJpbmQodGhpcykpO1xufVxuXG4vKipcbiAqIEluaGVyaXQgZnJvbSBgU3RyZWFtYCAod2hpY2ggaW5oZXJpdHMgZnJvbSBgRXZlbnRFbWl0dGVyYCkuXG4gKiBNaXhpbiBgUmVxdWVzdEJhc2VgLlxuICovXG51dGlsLmluaGVyaXRzKFJlcXVlc3QsIFN0cmVhbSk7XG5cbm1peGluKFJlcXVlc3QucHJvdG90eXBlLCBSZXF1ZXN0QmFzZS5wcm90b3R5cGUpO1xuXG4vKipcbiAqIEVuYWJsZSBvciBEaXNhYmxlIGh0dHAyLlxuICpcbiAqIEVuYWJsZSBodHRwMi5cbiAqXG4gKiBgYGAganNcbiAqIHJlcXVlc3QuZ2V0KCdodHRwOi8vbG9jYWxob3N0LycpXG4gKiAgIC5odHRwMigpXG4gKiAgIC5lbmQoY2FsbGJhY2spO1xuICpcbiAqIHJlcXVlc3QuZ2V0KCdodHRwOi8vbG9jYWxob3N0LycpXG4gKiAgIC5odHRwMih0cnVlKVxuICogICAuZW5kKGNhbGxiYWNrKTtcbiAqIGBgYFxuICpcbiAqIERpc2FibGUgaHR0cDIuXG4gKlxuICogYGBgIGpzXG4gKiByZXF1ZXN0ID0gcmVxdWVzdC5odHRwMigpO1xuICogcmVxdWVzdC5nZXQoJ2h0dHA6Ly9sb2NhbGhvc3QvJylcbiAqICAgLmh0dHAyKGZhbHNlKVxuICogICAuZW5kKGNhbGxiYWNrKTtcbiAqIGBgYFxuICpcbiAqIEBwYXJhbSB7Qm9vbGVhbn0gZW5hYmxlXG4gKiBAcmV0dXJuIHtSZXF1ZXN0fSBmb3IgY2hhaW5pbmdcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUuaHR0cDIgPSBmdW5jdGlvbiAoYm9vbCkge1xuICBpZiAoZXhwb3J0cy5wcm90b2NvbHNbJ2h0dHAyOiddID09PSB1bmRlZmluZWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAnc3VwZXJhZ2VudDogdGhpcyB2ZXJzaW9uIG9mIE5vZGUuanMgZG9lcyBub3Qgc3VwcG9ydCBodHRwMidcbiAgICApO1xuICB9XG5cbiAgdGhpcy5fZW5hYmxlSHR0cDIgPSBib29sID09PSB1bmRlZmluZWQgPyB0cnVlIDogYm9vbDtcbiAgcmV0dXJuIHRoaXM7XG59O1xuXG4vKipcbiAqIFF1ZXVlIHRoZSBnaXZlbiBgZmlsZWAgYXMgYW4gYXR0YWNobWVudCB0byB0aGUgc3BlY2lmaWVkIGBmaWVsZGAsXG4gKiB3aXRoIG9wdGlvbmFsIGBvcHRpb25zYCAob3IgZmlsZW5hbWUpLlxuICpcbiAqIGBgYCBqc1xuICogcmVxdWVzdC5wb3N0KCdodHRwOi8vbG9jYWxob3N0L3VwbG9hZCcpXG4gKiAgIC5hdHRhY2goJ2ZpZWxkJywgQnVmZmVyLmZyb20oJzxiPkhlbGxvIHdvcmxkPC9iPicpLCAnaGVsbG8uaHRtbCcpXG4gKiAgIC5lbmQoY2FsbGJhY2spO1xuICogYGBgXG4gKlxuICogQSBmaWxlbmFtZSBtYXkgYWxzbyBiZSB1c2VkOlxuICpcbiAqIGBgYCBqc1xuICogcmVxdWVzdC5wb3N0KCdodHRwOi8vbG9jYWxob3N0L3VwbG9hZCcpXG4gKiAgIC5hdHRhY2goJ2ZpbGVzJywgJ2ltYWdlLmpwZycpXG4gKiAgIC5lbmQoY2FsbGJhY2spO1xuICogYGBgXG4gKlxuICogQHBhcmFtIHtTdHJpbmd9IGZpZWxkXG4gKiBAcGFyYW0ge1N0cmluZ3xmcy5SZWFkU3RyZWFtfEJ1ZmZlcn0gZmlsZVxuICogQHBhcmFtIHtTdHJpbmd8T2JqZWN0fSBvcHRpb25zXG4gKiBAcmV0dXJuIHtSZXF1ZXN0fSBmb3IgY2hhaW5pbmdcbiAqIEBhcGkgcHVibGljXG4gKi9cblxuUmVxdWVzdC5wcm90b3R5cGUuYXR0YWNoID0gZnVuY3Rpb24gKGZpZWxkLCBmaWxlLCBvcHRpb25zKSB7XG4gIGlmIChmaWxlKSB7XG4gICAgaWYgKHRoaXMuX2RhdGEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcInN1cGVyYWdlbnQgY2FuJ3QgbWl4IC5zZW5kKCkgYW5kIC5hdHRhY2goKVwiKTtcbiAgICB9XG5cbiAgICBsZXQgbyA9IG9wdGlvbnMgfHwge307XG4gICAgaWYgKHR5cGVvZiBvcHRpb25zID09PSAnc3RyaW5nJykge1xuICAgICAgbyA9IHsgZmlsZW5hbWU6IG9wdGlvbnMgfTtcbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIGZpbGUgPT09ICdzdHJpbmcnKSB7XG4gICAgICBpZiAoIW8uZmlsZW5hbWUpIG8uZmlsZW5hbWUgPSBmaWxlO1xuICAgICAgZGVidWcoJ2NyZWF0aW5nIGBmcy5SZWFkU3RyZWFtYCBpbnN0YW5jZSBmb3IgZmlsZTogJXMnLCBmaWxlKTtcbiAgICAgIGZpbGUgPSBmcy5jcmVhdGVSZWFkU3RyZWFtKGZpbGUpO1xuICAgICAgZmlsZS5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcbiAgICAgICAgY29uc3QgZm9ybURhdGEgPSB0aGlzLl9nZXRGb3JtRGF0YSgpO1xuICAgICAgICBmb3JtRGF0YS5lbWl0KCdlcnJvcicsIGVycm9yKTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSBpZiAoIW8uZmlsZW5hbWUgJiYgZmlsZS5wYXRoKSB7XG4gICAgICBvLmZpbGVuYW1lID0gZmlsZS5wYXRoO1xuICAgIH1cblxuICAgIHRoaXMuX2dldEZvcm1EYXRhKCkuYXBwZW5kKGZpZWxkLCBmaWxlLCBvKTtcbiAgfVxuXG4gIHJldHVybiB0aGlzO1xufTtcblxuUmVxdWVzdC5wcm90b3R5cGUuX2dldEZvcm1EYXRhID0gZnVuY3Rpb24gKCkge1xuICBpZiAoIXRoaXMuX2Zvcm1EYXRhKSB7XG4gICAgdGhpcy5fZm9ybURhdGEgPSBuZXcgRm9ybURhdGEoKTtcbiAgICB0aGlzLl9mb3JtRGF0YS5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcbiAgICAgIGRlYnVnKCdGb3JtRGF0YSBlcnJvcicsIGVycm9yKTtcbiAgICAgIGlmICh0aGlzLmNhbGxlZCkge1xuICAgICAgICAvLyBUaGUgcmVxdWVzdCBoYXMgYWxyZWFkeSBmaW5pc2hlZCBhbmQgdGhlIGNhbGxiYWNrIHdhcyBjYWxsZWQuXG4gICAgICAgIC8vIFNpbGVudGx5IGlnbm9yZSB0aGUgZXJyb3IuXG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgdGhpcy5jYWxsYmFjayhlcnJvcik7XG4gICAgICB0aGlzLmFib3J0KCk7XG4gICAgfSk7XG4gIH1cblxuICByZXR1cm4gdGhpcy5fZm9ybURhdGE7XG59O1xuXG4vKipcbiAqIEdldHMvc2V0cyB0aGUgYEFnZW50YCB0byB1c2UgZm9yIHRoaXMgSFRUU