123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416 |
- 'use strict';
- Object.defineProperty(exports, '__esModule', { value: true });
- // Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js
- // (MIT licensed)
- const BUFFER = Symbol('buffer');
- const TYPE = Symbol('type');
- const CLOSED = Symbol('closed');
- class Blob {
- constructor() {
- Object.defineProperty(this, Symbol.toStringTag, {
- value: 'Blob',
- writable: false,
- enumerable: false,
- configurable: true
- });
- this[CLOSED] = false;
- this[TYPE] = '';
- const blobParts = arguments[0];
- const options = arguments[1];
- const buffers = [];
- if (blobParts) {
- const a = blobParts;
- const length = Number(a.length);
- for (let i = 0; i < length; i++) {
- const element = a[i];
- let buffer;
- if (element instanceof Buffer) {
- buffer = element;
- } else if (ArrayBuffer.isView(element)) {
- buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength);
- } else if (element instanceof ArrayBuffer) {
- buffer = Buffer.from(element);
- } else if (element instanceof Blob) {
- buffer = element[BUFFER];
- } else {
- buffer = Buffer.from(typeof element === 'string' ? element : String(element));
- }
- buffers.push(buffer);
- }
- }
- this[BUFFER] = Buffer.concat(buffers);
- let type = options && options.type !== undefined && String(options.type).toLowerCase();
- if (type && !/[^\u0020-\u007E]/.test(type)) {
- this[TYPE] = type;
- }
- }
- get size() {
- return this[CLOSED] ? 0 : this[BUFFER].length;
- }
- get type() {
- return this[TYPE];
- }
- get isClosed() {
- return this[CLOSED];
- }
- slice() {
- const size = this.size;
- const start = arguments[0];
- const end = arguments[1];
- let relativeStart, relativeEnd;
- if (start === undefined) {
- relativeStart = 0;
- } else if (start < 0) {
- relativeStart = Math.max(size + start, 0);
- } else {
- relativeStart = Math.min(start, size);
- }
- if (end === undefined) {
- relativeEnd = size;
- } else if (end < 0) {
- relativeEnd = Math.max(size + end, 0);
- } else {
- relativeEnd = Math.min(end, size);
- }
- const span = Math.max(relativeEnd - relativeStart, 0);
- const buffer = this[BUFFER];
- const slicedBuffer = buffer.slice(relativeStart, relativeStart + span);
- const blob = new Blob([], { type: arguments[2] });
- blob[BUFFER] = slicedBuffer;
- blob[CLOSED] = this[CLOSED];
- return blob;
- }
- close() {
- this[CLOSED] = true;
- }
- }
- Object.defineProperty(Blob.prototype, Symbol.toStringTag, {
- value: 'BlobPrototype',
- writable: false,
- enumerable: false,
- configurable: true
- });
- /**
- * fetch-error.js
- *
- * FetchError interface for operational errors
- */
- /**
- * Create FetchError instance
- *
- * @param String message Error message for human
- * @param String type Error type for machine
- * @param String systemError For Node.js system error
- * @return FetchError
- */
- function FetchError(message, type, systemError) {
- Error.call(this, message);
- this.message = message;
- this.type = type;
- // when err.type is `system`, err.code contains system error code
- if (systemError) {
- this.code = this.errno = systemError.code;
- }
- // hide custom error implementation details from end-users
- Error.captureStackTrace(this, this.constructor);
- }
- FetchError.prototype = Object.create(Error.prototype);
- FetchError.prototype.constructor = FetchError;
- FetchError.prototype.name = 'FetchError';
- /**
- * body.js
- *
- * Body interface provides common methods for Request and Response
- */
- const Stream = require('stream');
- var _require$1 = require('stream');
- const PassThrough$1 = _require$1.PassThrough;
- const DISTURBED = Symbol('disturbed');
- let convert;
- try {
- convert = require('encoding').convert;
- } catch (e) {}
- /**
- * Body class
- *
- * Cannot use ES6 class because Body must be called with .call().
- *
- * @param Stream body Readable stream
- * @param Object opts Response options
- * @return Void
- */
- function Body(body) {
- var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
- _ref$size = _ref.size;
- let size = _ref$size === undefined ? 0 : _ref$size;
- var _ref$timeout = _ref.timeout;
- let timeout = _ref$timeout === undefined ? 0 : _ref$timeout;
- if (body == null) {
- // body is undefined or null
- body = null;
- } else if (typeof body === 'string') {
- // body is string
- } else if (isURLSearchParams(body)) {
- // body is a URLSearchParams
- } else if (body instanceof Blob) {
- // body is blob
- } else if (Buffer.isBuffer(body)) {
- // body is buffer
- } else if (body instanceof Stream) {
- // body is stream
- } else {
- // none of the above
- // coerce to string
- body = String(body);
- }
- this.body = body;
- this[DISTURBED] = false;
- this.size = size;
- this.timeout = timeout;
- }
- Body.prototype = {
- get bodyUsed() {
- return this[DISTURBED];
- },
- /**
- * Decode response as ArrayBuffer
- *
- * @return Promise
- */
- arrayBuffer() {
- return consumeBody.call(this).then(function (buf) {
- return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
- });
- },
- /**
- * Return raw response as Blob
- *
- * @return Promise
- */
- blob() {
- let ct = this.headers && this.headers.get('content-type') || '';
- return consumeBody.call(this).then(function (buf) {
- return Object.assign(
- // Prevent copying
- new Blob([], {
- type: ct.toLowerCase()
- }), {
- [BUFFER]: buf
- });
- });
- },
- /**
- * Decode response as json
- *
- * @return Promise
- */
- json() {
- var _this = this;
- return consumeBody.call(this).then(function (buffer) {
- try {
- return JSON.parse(buffer.toString());
- } catch (err) {
- return Body.Promise.reject(new FetchError(`invalid json response body at ${_this.url} reason: ${err.message}`, 'invalid-json'));
- }
- });
- },
- /**
- * Decode response as text
- *
- * @return Promise
- */
- text() {
- return consumeBody.call(this).then(function (buffer) {
- return buffer.toString();
- });
- },
- /**
- * Decode response as buffer (non-spec api)
- *
- * @return Promise
- */
- buffer() {
- return consumeBody.call(this);
- },
- /**
- * Decode response as text, while automatically detecting the encoding and
- * trying to decode to UTF-8 (non-spec api)
- *
- * @return Promise
- */
- textConverted() {
- var _this2 = this;
- return consumeBody.call(this).then(function (buffer) {
- return convertBody(buffer, _this2.headers);
- });
- }
- };
- Body.mixIn = function (proto) {
- for (const name of Object.getOwnPropertyNames(Body.prototype)) {
- // istanbul ignore else: future proof
- if (!(name in proto)) {
- const desc = Object.getOwnPropertyDescriptor(Body.prototype, name);
- Object.defineProperty(proto, name, desc);
- }
- }
- };
- /**
- * Decode buffers into utf-8 string
- *
- * @return Promise
- */
- function consumeBody(body) {
- var _this3 = this;
- if (this[DISTURBED]) {
- return Body.Promise.reject(new Error(`body used already for: ${this.url}`));
- }
- this[DISTURBED] = true;
- // body is null
- if (this.body === null) {
- return Body.Promise.resolve(Buffer.alloc(0));
- }
- // body is string
- if (typeof this.body === 'string') {
- return Body.Promise.resolve(Buffer.from(this.body));
- }
- // body is blob
- if (this.body instanceof Blob) {
- return Body.Promise.resolve(this.body[BUFFER]);
- }
- // body is buffer
- if (Buffer.isBuffer(this.body)) {
- return Body.Promise.resolve(this.body);
- }
- // istanbul ignore if: should never happen
- if (!(this.body instanceof Stream)) {
- return Body.Promise.resolve(Buffer.alloc(0));
- }
- // body is stream
- // get ready to actually consume the body
- let accum = [];
- let accumBytes = 0;
- let abort = false;
- return new Body.Promise(function (resolve, reject) {
- let resTimeout;
- // allow timeout on slow response body
- if (_this3.timeout) {
- resTimeout = setTimeout(function () {
- abort = true;
- reject(new FetchError(`Response timeout while trying to fetch ${_this3.url} (over ${_this3.timeout}ms)`, 'body-timeout'));
- }, _this3.timeout);
- }
- // handle stream error, such as incorrect content-encoding
- _this3.body.on('error', function (err) {
- reject(new FetchError(`Invalid response body while trying to fetch ${_this3.url}: ${err.message}`, 'system', err));
- });
- _this3.body.on('data', function (chunk) {
- if (abort || chunk === null) {
- return;
- }
- if (_this3.size && accumBytes + chunk.length > _this3.size) {
- abort = true;
- reject(new FetchError(`content size at ${_this3.url} over limit: ${_this3.size}`, 'max-size'));
- return;
- }
- accumBytes += chunk.length;
- accum.push(chunk);
- });
- _this3.body.on('end', function () {
- if (abort) {
- return;
- }
- clearTimeout(resTimeout);
- resolve(Buffer.concat(accum));
- });
- });
- }
- /**
- * Detect buffer encoding and convert to target encoding
- * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding
- *
- * @param Buffer buffer Incoming buffer
- * @param String encoding Target encoding
- * @return String
- */
- function convertBody(buffer, headers) {
- if (typeof convert !== 'function') {
- throw new Error('The package `encoding` must be installed to use the textConverted() function');
- }
- const ct = headers.get('content-type');
- let charset = 'utf-8';
- let res, str;
- // header
- if (ct) {
- res = /charset=([^;]*)/i.exec(ct);
- }
- // no charset in content type, peek at response body for at most 1024 bytes
- str = buffer.slice(0, 1024).toString();
- // html5
- if (!res && str) {
- res = /<meta.+?charset=(['"])(.+?)\1/i.exec(str);
- }
- // html4
- if (!res && str) {
- res = /<meta[\s]+?http-equiv=(['"])content-type\1[\s]+?content=(['"])(.+?)\2/i.exec(str);
- if (res) {
- res = /charset=(.*)/i.exec(res.pop());
- }
- }
- // xml
- if (!res && str) {
- res = /<\?xml.+?encoding=(['"])(.+?)\1/i.exec(str);
- }
- // found charset
- if (res) {
- charset = res.pop();
- // prevent decode issues when sites use incorrect encoding
- // ref: https://hsivonen.fi/encoding-menu/
- if (charset === 'gb2312' || charset === 'gbk') {
- charset = 'gb18030';
- }
- }
- // turn raw buffers into a single utf-8 buffer
- return convert(buffer, 'UTF-8', charset).toString();
- }
- /**
- * Detect a URLSearchParams object
- * ref: https://github.com/bitinn/node-fetch/issues/296#issuecomment-307598143
- *
- * @param Object obj Object to detect by type or brand
- * @return String
- */
- function isURLSearchParams(obj) {
- // Duck-typing as a necessary condition.
- if (typeof obj !== 'object' || typeof obj.append !== 'function' || typeof obj.delete !== 'function' || typeof obj.get !== 'function' || typeof obj.getAll !== 'function' || typeof obj.has !== 'function' || typeof obj.set !== 'function') {
- return false;
- }
- // Brand-checking and more duck-typing as optional condition.
- return obj.constructor.name === 'URLSearchParams' || Object.prototype.toString.call(obj) === '[object URLSearchParams]' || typeof obj.sort === 'function';
- }
- /**
- * Clone body given Res/Req instance
- *
- * @param Mixed instance Response or Request instance
- * @return Mixed
- */
- function clone(instance) {
- let p1, p2;
- let body = instance.body;
- // don't allow cloning a used body
- if (instance.bodyUsed) {
- throw new Error('cannot clone body after it is used');
- }
- // check that body is a stream and not form-data object
- // note: we can't clone the form-data object without having it as a dependency
- if (body instanceof Stream && typeof body.getBoundary !== 'function') {
- // tee instance body
- p1 = new PassThrough$1();
- p2 = new PassThrough$1();
- body.pipe(p1);
- body.pipe(p2);
- // set instance body to teed body and return the other teed body
- instance.body = p1;
- body = p2;
- }
- return body;
- }
- /**
- * Performs the operation "extract a `Content-Type` value from |object|" as
- * specified in the specification:
- * https://fetch.spec.whatwg.org/#concept-bodyinit-extract
- *
- * This function assumes that instance.body is present and non-null.
- *
- * @param Mixed instance Response or Request instance
- */
- function extractContentType(instance) {
- const body = instance.body;
- // istanbul ignore if: Currently, because of a guard in Request, body
- // can never be null. Included here for completeness.
- if (body === null) {
- // body is null
- return null;
- } else if (typeof body === 'string') {
- // body is string
- return 'text/plain;charset=UTF-8';
- } else if (isURLSearchParams(body)) {
- // body is a URLSearchParams
- return 'application/x-www-form-urlencoded;charset=UTF-8';
- } else if (body instanceof Blob) {
- // body is blob
- return body.type || null;
- } else if (Buffer.isBuffer(body)) {
- // body is buffer
- return null;
- } else if (typeof body.getBoundary === 'function') {
- // detect form data input from form-data module
- return `multipart/form-data;boundary=${body.getBoundary()}`;
- } else {
- // body is stream
- // can't really do much about this
- return null;
- }
- }
- function getTotalBytes(instance) {
- const body = instance.body;
- // istanbul ignore if: included for completion
- if (body === null) {
- // body is null
- return 0;
- } else if (typeof body === 'string') {
- // body is string
- return Buffer.byteLength(body);
- } else if (isURLSearchParams(body)) {
- // body is URLSearchParams
- return Buffer.byteLength(String(body));
- } else if (body instanceof Blob) {
- // body is blob
- return body.size;
- } else if (Buffer.isBuffer(body)) {
- // body is buffer
- return body.length;
- } else if (body && typeof body.getLengthSync === 'function') {
- // detect form data input from form-data module
- if (body._lengthRetrievers && body._lengthRetrievers.length == 0 || // 1.x
- body.hasKnownLength && body.hasKnownLength()) {
- // 2.x
- return body.getLengthSync();
- }
- return null;
- } else {
- // body is stream
- // can't really do much about this
- return null;
- }
- }
- function writeToStream(dest, instance) {
- const body = instance.body;
- if (body === null) {
- // body is null
- dest.end();
- } else if (typeof body === 'string') {
- // body is string
- dest.write(body);
- dest.end();
- } else if (isURLSearchParams(body)) {
- // body is URLSearchParams
- dest.write(Buffer.from(String(body)));
- dest.end();
- } else if (body instanceof Blob) {
- // body is blob
- dest.write(body[BUFFER]);
- dest.end();
- } else if (Buffer.isBuffer(body)) {
- // body is buffer
- dest.write(body);
- dest.end();
- } else {
- // body is stream
- body.pipe(dest);
- }
- }
- // expose Promise
- Body.Promise = global.Promise;
- /**
- * A set of utilities borrowed from Node.js' _http_common.js
- */
- /**
- * Verifies that the given val is a valid HTTP token
- * per the rules defined in RFC 7230
- * See https://tools.ietf.org/html/rfc7230#section-3.2.6
- *
- * Allowed characters in an HTTP token:
- * ^_`a-z 94-122
- * A-Z 65-90
- * - 45
- * 0-9 48-57
- * ! 33
- * #$%&' 35-39
- * *+ 42-43
- * . 46
- * | 124
- * ~ 126
- *
- * This implementation of checkIsHttpToken() loops over the string instead of
- * using a regular expression since the former is up to 180% faster with v8 4.9
- * depending on the string length (the shorter the string, the larger the
- * performance difference)
- *
- * Additionally, checkIsHttpToken() is currently designed to be inlinable by v8,
- * so take care when making changes to the implementation so that the source
- * code size does not exceed v8's default max_inlined_source_size setting.
- **/
- /* istanbul ignore next */
- function isValidTokenChar(ch) {
- if (ch >= 94 && ch <= 122) return true;
- if (ch >= 65 && ch <= 90) return true;
- if (ch === 45) return true;
- if (ch >= 48 && ch <= 57) return true;
- if (ch === 34 || ch === 40 || ch === 41 || ch === 44) return false;
- if (ch >= 33 && ch <= 46) return true;
- if (ch === 124 || ch === 126) return true;
- return false;
- }
- /* istanbul ignore next */
- function checkIsHttpToken(val) {
- if (typeof val !== 'string' || val.length === 0) return false;
- if (!isValidTokenChar(val.charCodeAt(0))) return false;
- const len = val.length;
- if (len > 1) {
- if (!isValidTokenChar(val.charCodeAt(1))) return false;
- if (len > 2) {
- if (!isValidTokenChar(val.charCodeAt(2))) return false;
- if (len > 3) {
- if (!isValidTokenChar(val.charCodeAt(3))) return false;
- for (var i = 4; i < len; i++) {
- if (!isValidTokenChar(val.charCodeAt(i))) return false;
- }
- }
- }
- }
- return true;
- }
- /**
- * True if val contains an invalid field-vchar
- * field-value = *( field-content / obs-fold )
- * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
- * field-vchar = VCHAR / obs-text
- *
- * checkInvalidHeaderChar() is currently designed to be inlinable by v8,
- * so take care when making changes to the implementation so that the source
- * code size does not exceed v8's default max_inlined_source_size setting.
- **/
- /* istanbul ignore next */
- function checkInvalidHeaderChar(val) {
- val += '';
- if (val.length < 1) return false;
- var c = val.charCodeAt(0);
- if (c <= 31 && c !== 9 || c > 255 || c === 127) return true;
- if (val.length < 2) return false;
- c = val.charCodeAt(1);
- if (c <= 31 && c !== 9 || c > 255 || c === 127) return true;
- if (val.length < 3) return false;
- c = val.charCodeAt(2);
- if (c <= 31 && c !== 9 || c > 255 || c === 127) return true;
- for (var i = 3; i < val.length; ++i) {
- c = val.charCodeAt(i);
- if (c <= 31 && c !== 9 || c > 255 || c === 127) return true;
- }
- return false;
- }
- /**
- * headers.js
- *
- * Headers class offers convenient helpers
- */
- function sanitizeName(name) {
- name += '';
- if (!checkIsHttpToken(name)) {
- throw new TypeError(`${name} is not a legal HTTP header name`);
- }
- return name.toLowerCase();
- }
- function sanitizeValue(value) {
- value += '';
- if (checkInvalidHeaderChar(value)) {
- throw new TypeError(`${value} is not a legal HTTP header value`);
- }
- return value;
- }
- const MAP = Symbol('map');
- class Headers {
- /**
- * Headers class
- *
- * @param Object headers Response headers
- * @return Void
- */
- constructor() {
- let init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
- this[MAP] = Object.create(null);
- if (init instanceof Headers) {
- const rawHeaders = init.raw();
- const headerNames = Object.keys(rawHeaders);
- for (const headerName of headerNames) {
- for (const value of rawHeaders[headerName]) {
- this.append(headerName, value);
- }
- }
- return;
- }
- // We don't worry about converting prop to ByteString here as append()
- // will handle it.
- if (init == null) {
- // no op
- } else if (typeof init === 'object') {
- const method = init[Symbol.iterator];
- if (method != null) {
- if (typeof method !== 'function') {
- throw new TypeError('Header pairs must be iterable');
- }
- // sequence<sequence<ByteString>>
- // Note: per spec we have to first exhaust the lists then process them
- const pairs = [];
- for (const pair of init) {
- if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') {
- throw new TypeError('Each header pair must be iterable');
- }
- pairs.push(Array.from(pair));
- }
- for (const pair of pairs) {
- if (pair.length !== 2) {
- throw new TypeError('Each header pair must be a name/value tuple');
- }
- this.append(pair[0], pair[1]);
- }
- } else {
- // record<ByteString, ByteString>
- for (const key of Object.keys(init)) {
- const value = init[key];
- this.append(key, value);
- }
- }
- } else {
- throw new TypeError('Provided initializer must be an object');
- }
- Object.defineProperty(this, Symbol.toStringTag, {
- value: 'Headers',
- writable: false,
- enumerable: false,
- configurable: true
- });
- }
- /**
- * Return first header value given name
- *
- * @param String name Header name
- * @return Mixed
- */
- get(name) {
- const list = this[MAP][sanitizeName(name)];
- if (!list) {
- return null;
- }
- return list.join(', ');
- }
- /**
- * Iterate over all headers
- *
- * @param Function callback Executed for each item with parameters (value, name, thisArg)
- * @param Boolean thisArg `this` context for callback function
- * @return Void
- */
- forEach(callback) {
- let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
- let pairs = getHeaderPairs(this);
- let i = 0;
- while (i < pairs.length) {
- var _pairs$i = pairs[i];
- const name = _pairs$i[0],
- value = _pairs$i[1];
- callback.call(thisArg, value, name, this);
- pairs = getHeaderPairs(this);
- i++;
- }
- }
- /**
- * Overwrite header values given name
- *
- * @param String name Header name
- * @param String value Header value
- * @return Void
- */
- set(name, value) {
- this[MAP][sanitizeName(name)] = [sanitizeValue(value)];
- }
- /**
- * Append a value onto existing header
- *
- * @param String name Header name
- * @param String value Header value
- * @return Void
- */
- append(name, value) {
- if (!this.has(name)) {
- this.set(name, value);
- return;
- }
- this[MAP][sanitizeName(name)].push(sanitizeValue(value));
- }
- /**
- * Check for header name existence
- *
- * @param String name Header name
- * @return Boolean
- */
- has(name) {
- return !!this[MAP][sanitizeName(name)];
- }
- /**
- * Delete all header values given name
- *
- * @param String name Header name
- * @return Void
- */
- delete(name) {
- delete this[MAP][sanitizeName(name)];
- }
- /**
- * Return raw headers (non-spec api)
- *
- * @return Object
- */
- raw() {
- return this[MAP];
- }
- /**
- * Get an iterator on keys.
- *
- * @return Iterator
- */
- keys() {
- return createHeadersIterator(this, 'key');
- }
- /**
- * Get an iterator on values.
- *
- * @return Iterator
- */
- values() {
- return createHeadersIterator(this, 'value');
- }
- /**
- * Get an iterator on entries.
- *
- * This is the default iterator of the Headers object.
- *
- * @return Iterator
- */
- [Symbol.iterator]() {
- return createHeadersIterator(this, 'key+value');
- }
- }
- Headers.prototype.entries = Headers.prototype[Symbol.iterator];
- Object.defineProperty(Headers.prototype, Symbol.toStringTag, {
- value: 'HeadersPrototype',
- writable: false,
- enumerable: false,
- configurable: true
- });
- function getHeaderPairs(headers, kind) {
- const keys = Object.keys(headers[MAP]).sort();
- return keys.map(kind === 'key' ? function (k) {
- return [k];
- } : function (k) {
- return [k, headers.get(k)];
- });
- }
- const INTERNAL = Symbol('internal');
- function createHeadersIterator(target, kind) {
- const iterator = Object.create(HeadersIteratorPrototype);
- iterator[INTERNAL] = {
- target,
- kind,
- index: 0
- };
- return iterator;
- }
- const HeadersIteratorPrototype = Object.setPrototypeOf({
- next() {
- // istanbul ignore if
- if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) {
- throw new TypeError('Value of `this` is not a HeadersIterator');
- }
- var _INTERNAL = this[INTERNAL];
- const target = _INTERNAL.target,
- kind = _INTERNAL.kind,
- index = _INTERNAL.index;
- const values = getHeaderPairs(target, kind);
- const len = values.length;
- if (index >= len) {
- return {
- value: undefined,
- done: true
- };
- }
- const pair = values[index];
- this[INTERNAL].index = index + 1;
- let result;
- if (kind === 'key') {
- result = pair[0];
- } else if (kind === 'value') {
- result = pair[1];
- } else {
- result = pair;
- }
- return {
- value: result,
- done: false
- };
- }
- }, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())));
- Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, {
- value: 'HeadersIterator',
- writable: false,
- enumerable: false,
- configurable: true
- });
- /**
- * response.js
- *
- * Response class provides content decoding
- */
- var _require$2 = require('http');
- const STATUS_CODES = _require$2.STATUS_CODES;
- /**
- * Response class
- *
- * @param Stream body Readable stream
- * @param Object opts Response options
- * @return Void
- */
- class Response {
- constructor() {
- let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
- let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
- Body.call(this, body, opts);
- this.url = opts.url;
- this.status = opts.status || 200;
- this.statusText = opts.statusText || STATUS_CODES[this.status];
- this.headers = new Headers(opts.headers);
- Object.defineProperty(this, Symbol.toStringTag, {
- value: 'Response',
- writable: false,
- enumerable: false,
- configurable: true
- });
- }
- /**
- * Convenience property representing if the request ended normally
- */
- get ok() {
- return this.status >= 200 && this.status < 300;
- }
- /**
- * Clone this response
- *
- * @return Response
- */
- clone() {
- return new Response(clone(this), {
- url: this.url,
- status: this.status,
- statusText: this.statusText,
- headers: this.headers,
- ok: this.ok
- });
- }
- }
- Body.mixIn(Response.prototype);
- Object.defineProperty(Response.prototype, Symbol.toStringTag, {
- value: 'ResponsePrototype',
- writable: false,
- enumerable: false,
- configurable: true
- });
- /**
- * request.js
- *
- * Request class contains server only options
- */
- var _require$3 = require('url');
- const format_url = _require$3.format;
- const parse_url = _require$3.parse;
- const PARSED_URL = Symbol('url');
- /**
- * Request class
- *
- * @param Mixed input Url or Request instance
- * @param Object init Custom options
- * @return Void
- */
- class Request {
- constructor(input) {
- let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
- let parsedURL;
- // normalize input
- if (!(input instanceof Request)) {
- if (input && input.href) {
- // in order to support Node.js' Url objects; though WHATWG's URL objects
- // will fall into this branch also (since their `toString()` will return
- // `href` property anyway)
- parsedURL = parse_url(input.href);
- } else {
- // coerce input to a string before attempting to parse
- parsedURL = parse_url(`${input}`);
- }
- input = {};
- } else {
- parsedURL = parse_url(input.url);
- }
- let method = init.method || input.method || 'GET';
- if ((init.body != null || input instanceof Request && input.body !== null) && (method === 'GET' || method === 'HEAD')) {
- throw new TypeError('Request with GET/HEAD method cannot have body');
- }
- let inputBody = init.body != null ? init.body : input instanceof Request && input.body !== null ? clone(input) : null;
- Body.call(this, inputBody, {
- timeout: init.timeout || input.timeout || 0,
- size: init.size || input.size || 0
- });
- // fetch spec options
- this.method = method.toUpperCase();
- this.redirect = init.redirect || input.redirect || 'follow';
- this.headers = new Headers(init.headers || input.headers || {});
- if (init.body != null) {
- const contentType = extractContentType(this);
- if (contentType !== null && !this.headers.has('Content-Type')) {
- this.headers.append('Content-Type', contentType);
- }
- }
- // server only options
- this.follow = init.follow !== undefined ? init.follow : input.follow !== undefined ? input.follow : 20;
- this.compress = init.compress !== undefined ? init.compress : input.compress !== undefined ? input.compress : true;
- this.counter = init.counter || input.counter || 0;
- this.agent = init.agent || input.agent;
- this[PARSED_URL] = parsedURL;
- Object.defineProperty(this, Symbol.toStringTag, {
- value: 'Request',
- writable: false,
- enumerable: false,
- configurable: true
- });
- }
- get url() {
- return format_url(this[PARSED_URL]);
- }
- /**
- * Clone this request
- *
- * @return Request
- */
- clone() {
- return new Request(this);
- }
- }
- Body.mixIn(Request.prototype);
- Object.defineProperty(Request.prototype, Symbol.toStringTag, {
- value: 'RequestPrototype',
- writable: false,
- enumerable: false,
- configurable: true
- });
- function getNodeRequestOptions(request) {
- const parsedURL = request[PARSED_URL];
- const headers = new Headers(request.headers);
- // fetch step 3
- if (!headers.has('Accept')) {
- headers.set('Accept', '*/*');
- }
- // Basic fetch
- if (!parsedURL.protocol || !parsedURL.hostname) {
- throw new TypeError('Only absolute URLs are supported');
- }
- if (!/^https?:$/.test(parsedURL.protocol)) {
- throw new TypeError('Only HTTP(S) protocols are supported');
- }
- // HTTP-network-or-cache fetch steps 5-9
- let contentLengthValue = null;
- if (request.body == null && /^(POST|PUT)$/i.test(request.method)) {
- contentLengthValue = '0';
- }
- if (request.body != null) {
- const totalBytes = getTotalBytes(request);
- if (typeof totalBytes === 'number') {
- contentLengthValue = String(totalBytes);
- }
- }
- if (contentLengthValue) {
- headers.set('Content-Length', contentLengthValue);
- }
- // HTTP-network-or-cache fetch step 12
- if (!headers.has('User-Agent')) {
- headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)');
- }
- // HTTP-network-or-cache fetch step 16
- if (request.compress) {
- headers.set('Accept-Encoding', 'gzip,deflate');
- }
- if (!headers.has('Connection') && !request.agent) {
- headers.set('Connection', 'close');
- }
- // HTTP-network fetch step 4
- // chunked encoding is handled by Node.js
- return Object.assign({}, parsedURL, {
- method: request.method,
- headers: headers.raw(),
- agent: request.agent
- });
- }
- /**
- * index.js
- *
- * a request API compatible with window.fetch
- */
- const http = require('http');
- const https = require('https');
- var _require = require('stream');
- const PassThrough = _require.PassThrough;
- var _require2 = require('url');
- const resolve_url = _require2.resolve;
- const zlib = require('zlib');
- /**
- * Fetch function
- *
- * @param Mixed url Absolute url or Request instance
- * @param Object opts Fetch options
- * @return Promise
- */
- function fetch(url, opts) {
- // allow custom promise
- if (!fetch.Promise) {
- throw new Error('native promise missing, set fetch.Promise to your favorite alternative');
- }
- Body.Promise = fetch.Promise;
- // wrap http.request into fetch
- return new fetch.Promise(function (resolve, reject) {
- // build request object
- const request = new Request(url, opts);
- const options = getNodeRequestOptions(request);
- const send = (options.protocol === 'https:' ? https : http).request;
- // http.request only support string as host header, this hack make custom host header possible
- if (options.headers.host) {
- options.headers.host = options.headers.host[0];
- }
- // send request
- const req = send(options);
- let reqTimeout;
- if (request.timeout) {
- req.once('socket', function (socket) {
- reqTimeout = setTimeout(function () {
- req.abort();
- reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout'));
- }, request.timeout);
- });
- }
- req.on('error', function (err) {
- clearTimeout(reqTimeout);
- reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err));
- });
- req.on('response', function (res) {
- clearTimeout(reqTimeout);
- // handle redirect
- if (fetch.isRedirect(res.statusCode) && request.redirect !== 'manual') {
- if (request.redirect === 'error') {
- reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect'));
- return;
- }
- if (request.counter >= request.follow) {
- reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect'));
- return;
- }
- if (!res.headers.location) {
- reject(new FetchError(`redirect location header missing at: ${request.url}`, 'invalid-redirect'));
- return;
- }
- // per fetch spec, for POST request with 301/302 response, or any request with 303 response, use GET when following redirect
- if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') {
- request.method = 'GET';
- request.body = null;
- request.headers.delete('content-length');
- }
- request.counter++;
- resolve(fetch(resolve_url(request.url, res.headers.location), request));
- return;
- }
- // normalize location header for manual redirect mode
- const headers = new Headers();
- for (const name of Object.keys(res.headers)) {
- if (Array.isArray(res.headers[name])) {
- for (const val of res.headers[name]) {
- headers.append(name, val);
- }
- } else {
- headers.append(name, res.headers[name]);
- }
- }
- if (request.redirect === 'manual' && headers.has('location')) {
- headers.set('location', resolve_url(request.url, headers.get('location')));
- }
- // prepare response
- let body = res.pipe(new PassThrough());
- const response_options = {
- url: request.url,
- status: res.statusCode,
- statusText: res.statusMessage,
- headers: headers,
- size: request.size,
- timeout: request.timeout
- };
- // HTTP-network fetch step 16.1.2
- const codings = headers.get('Content-Encoding');
- // HTTP-network fetch step 16.1.3: handle content codings
- // in following scenarios we ignore compression support
- // 1. compression support is disabled
- // 2. HEAD request
- // 3. no Content-Encoding header
- // 4. no content response (204)
- // 5. content not modified response (304)
- if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) {
- resolve(new Response(body, response_options));
- return;
- }
- // For Node v6+
- // Be less strict when decoding compressed responses, since sometimes
- // servers send slightly invalid responses that are still accepted
- // by common browsers.
- // Always using Z_SYNC_FLUSH is what cURL does.
- const zlibOptions = {
- flush: zlib.Z_SYNC_FLUSH,
- finishFlush: zlib.Z_SYNC_FLUSH
- };
- // for gzip
- if (codings == 'gzip' || codings == 'x-gzip') {
- body = body.pipe(zlib.createGunzip(zlibOptions));
- resolve(new Response(body, response_options));
- return;
- }
- // for deflate
- if (codings == 'deflate' || codings == 'x-deflate') {
- // handle the infamous raw deflate response from old servers
- // a hack for old IIS and Apache servers
- const raw = res.pipe(new PassThrough());
- raw.once('data', function (chunk) {
- // see http://stackoverflow.com/questions/37519828
- if ((chunk[0] & 0x0F) === 0x08) {
- body = body.pipe(zlib.createInflate());
- } else {
- body = body.pipe(zlib.createInflateRaw());
- }
- resolve(new Response(body, response_options));
- });
- return;
- }
- // otherwise, use response as-is
- resolve(new Response(body, response_options));
- });
- writeToStream(req, request);
- });
- }
- /**
- * Redirect code matching
- *
- * @param Number code Status code
- * @return Boolean
- */
- fetch.isRedirect = function (code) {
- return code === 301 || code === 302 || code === 303 || code === 307 || code === 308;
- };
- // expose Promise
- fetch.Promise = global.Promise;
- module.exports = exports = fetch;
- exports.Headers = Headers;
- exports.Request = Request;
- exports.Response = Response;
- exports.FetchError = FetchError;
|