From 5b10a10743b8459f64fe83e0ff420f69da79c9a4 Mon Sep 17 00:00:00 2001 From: Yuchen Pei Date: Thu, 28 Jul 2022 15:32:10 +1000 Subject: Moving scripts and utilities into a new utilities dir --- hash_script/node_modules/node-fetch/lib/index.js | 1416 ---------------------- 1 file changed, 1416 deletions(-) delete mode 100644 hash_script/node_modules/node-fetch/lib/index.js (limited to 'hash_script/node_modules/node-fetch/lib/index.js') diff --git a/hash_script/node_modules/node-fetch/lib/index.js b/hash_script/node_modules/node-fetch/lib/index.js deleted file mode 100644 index f100854..0000000 --- a/hash_script/node_modules/node-fetch/lib/index.js +++ /dev/null @@ -1,1416 +0,0 @@ -'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 = /= 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> - // 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 - 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; -- cgit v1.2.3