2016-04-05 10:43:12 -07:00
|
|
|
|
2015-06-03 21:05:01 -07:00
|
|
|
/**
|
2020-03-13 08:06:25 -07:00
|
|
|
* Request.js
|
2015-06-03 21:05:01 -07:00
|
|
|
*
|
|
|
|
* Request class contains server only options
|
2018-03-04 19:38:57 -08:00
|
|
|
*
|
|
|
|
* All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/.
|
2015-06-03 21:05:01 -07:00
|
|
|
*/
|
|
|
|
|
2020-03-13 08:06:25 -07:00
|
|
|
import {format as formatUrl} from 'url';
|
2020-05-25 07:43:10 -07:00
|
|
|
import Headers from './headers.js';
|
2020-05-20 23:50:31 -07:00
|
|
|
import Body, {clone, extractContentType, getTotalBytes} from './body.js';
|
|
|
|
import {isAbortSignal} from './utils/is.js';
|
|
|
|
import {getSearch} from './utils/get-search.js';
|
2016-11-23 13:39:35 -08:00
|
|
|
|
2018-01-27 12:28:56 -08:00
|
|
|
const INTERNALS = Symbol('Request internals');
|
|
|
|
|
|
|
|
/**
|
2020-03-13 08:06:25 -07:00
|
|
|
* Check if `obj` is an instance of Request.
|
2018-01-27 12:28:56 -08:00
|
|
|
*
|
2020-03-13 08:06:25 -07:00
|
|
|
* @param {*} obj
|
|
|
|
* @return {boolean}
|
2018-01-27 12:28:56 -08:00
|
|
|
*/
|
2020-05-25 08:11:56 -07:00
|
|
|
const isRequest = object => {
|
2018-01-27 12:28:56 -08:00
|
|
|
return (
|
2020-03-13 08:06:25 -07:00
|
|
|
typeof object === 'object' &&
|
|
|
|
typeof object[INTERNALS] === 'object'
|
2018-01-27 12:28:56 -08:00
|
|
|
);
|
2020-05-25 08:11:56 -07:00
|
|
|
};
|
2015-06-03 21:05:01 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Request class
|
|
|
|
*
|
|
|
|
* @param Mixed input Url or Request instance
|
|
|
|
* @param Object init Custom options
|
|
|
|
* @return Void
|
|
|
|
*/
|
2020-05-23 10:41:14 -07:00
|
|
|
export default class Request extends Body {
|
2016-10-10 11:50:04 -07:00
|
|
|
constructor(input, init = {}) {
|
2016-10-08 18:31:42 -07:00
|
|
|
let parsedURL;
|
2015-06-03 21:05:01 -07:00
|
|
|
|
2020-03-13 08:06:25 -07:00
|
|
|
// Normalize input and force URL to be encoded as UTF-8 (https://github.com/bitinn/node-fetch/issues/245)
|
2020-05-25 08:11:56 -07:00
|
|
|
if (isRequest(input)) {
|
2020-05-31 08:15:27 -07:00
|
|
|
parsedURL = new URL(input.url);
|
2020-05-25 08:11:56 -07:00
|
|
|
} else {
|
2020-05-31 08:15:27 -07:00
|
|
|
parsedURL = new URL(input);
|
2016-10-10 11:50:04 -07:00
|
|
|
input = {};
|
|
|
|
}
|
2015-06-03 21:05:01 -07:00
|
|
|
|
2016-12-04 13:13:51 -08:00
|
|
|
let method = init.method || input.method || 'GET';
|
2017-11-18 20:44:02 -08:00
|
|
|
method = method.toUpperCase();
|
2016-12-04 13:13:51 -08:00
|
|
|
|
2020-03-13 08:06:25 -07:00
|
|
|
// eslint-disable-next-line no-eq-null, eqeqeq
|
2020-05-25 08:11:56 -07:00
|
|
|
if (((init.body != null || isRequest(input)) && input.body !== null) &&
|
2016-12-04 13:13:51 -08:00
|
|
|
(method === 'GET' || method === 'HEAD')) {
|
|
|
|
throw new TypeError('Request with GET/HEAD method cannot have body');
|
|
|
|
}
|
|
|
|
|
2020-05-25 08:11:56 -07:00
|
|
|
const inputBody = init.body ?
|
2016-12-04 13:16:03 -08:00
|
|
|
init.body :
|
2020-03-13 08:06:25 -07:00
|
|
|
(isRequest(input) && input.body !== null ?
|
2016-12-04 13:16:03 -08:00
|
|
|
clone(input) :
|
2020-03-13 08:06:25 -07:00
|
|
|
null);
|
2016-12-04 13:16:03 -08:00
|
|
|
|
2020-05-23 10:41:14 -07:00
|
|
|
super(inputBody, {
|
2016-10-10 11:50:04 -07:00
|
|
|
size: init.size || input.size || 0
|
|
|
|
});
|
2015-06-03 21:05:01 -07:00
|
|
|
|
2018-01-27 12:28:56 -08:00
|
|
|
const headers = new Headers(init.headers || input.headers || {});
|
2015-06-03 21:40:01 -07:00
|
|
|
|
2020-03-13 08:06:25 -07:00
|
|
|
if (inputBody !== null && !headers.has('Content-Type')) {
|
2018-11-13 08:36:44 -08:00
|
|
|
const contentType = extractContentType(inputBody);
|
|
|
|
if (contentType) {
|
2018-01-27 12:28:56 -08:00
|
|
|
headers.append('Content-Type', contentType);
|
2016-11-23 12:42:24 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-13 08:06:25 -07:00
|
|
|
let signal = isRequest(input) ?
|
|
|
|
input.signal :
|
|
|
|
null;
|
|
|
|
if ('signal' in init) {
|
|
|
|
signal = init.signal;
|
|
|
|
}
|
2018-11-12 20:40:11 -08:00
|
|
|
|
2020-03-13 08:06:25 -07:00
|
|
|
if (signal !== null && !isAbortSignal(signal)) {
|
2018-11-12 20:40:11 -08:00
|
|
|
throw new TypeError('Expected signal to be an instanceof AbortSignal');
|
|
|
|
}
|
|
|
|
|
2018-01-27 12:28:56 -08:00
|
|
|
this[INTERNALS] = {
|
|
|
|
method,
|
|
|
|
redirect: init.redirect || input.redirect || 'follow',
|
|
|
|
headers,
|
2018-11-12 20:40:11 -08:00
|
|
|
parsedURL,
|
2020-03-13 08:06:25 -07:00
|
|
|
signal
|
2018-01-27 12:28:56 -08:00
|
|
|
};
|
|
|
|
|
2020-03-13 08:06:25 -07:00
|
|
|
// Node-fetch-only options
|
2020-05-25 08:11:56 -07:00
|
|
|
this.follow = init.follow === undefined ? (input.follow === undefined ? 20 : input.follow) : init.follow;
|
|
|
|
this.compress = init.compress === undefined ? (input.compress === undefined ? true : input.compress) : init.compress;
|
2016-10-10 11:50:04 -07:00
|
|
|
this.counter = init.counter || input.counter || 0;
|
|
|
|
this.agent = init.agent || input.agent;
|
2020-05-24 03:16:34 -07:00
|
|
|
this.highWaterMark = init.highWaterMark || input.highWaterMark || 16384;
|
2020-06-04 08:54:02 -07:00
|
|
|
this.insecureHTTPParser = init.insecureHTTPParser || input.insecureHTTPParser || false;
|
2018-01-27 12:28:56 -08:00
|
|
|
}
|
2015-08-10 12:35:01 -07:00
|
|
|
|
2018-01-27 12:28:56 -08:00
|
|
|
get method() {
|
|
|
|
return this[INTERNALS].method;
|
2016-10-08 18:31:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
get url() {
|
2020-03-13 08:06:25 -07:00
|
|
|
return formatUrl(this[INTERNALS].parsedURL);
|
2018-01-27 12:28:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
get headers() {
|
|
|
|
return this[INTERNALS].headers;
|
|
|
|
}
|
|
|
|
|
|
|
|
get redirect() {
|
|
|
|
return this[INTERNALS].redirect;
|
2016-10-10 11:50:04 -07:00
|
|
|
}
|
2015-08-10 12:35:01 -07:00
|
|
|
|
2018-11-12 20:40:11 -08:00
|
|
|
get signal() {
|
|
|
|
return this[INTERNALS].signal;
|
|
|
|
}
|
|
|
|
|
2016-10-10 11:50:04 -07:00
|
|
|
/**
|
|
|
|
* Clone this request
|
|
|
|
*
|
|
|
|
* @return Request
|
|
|
|
*/
|
|
|
|
clone() {
|
|
|
|
return new Request(this);
|
|
|
|
}
|
2017-02-26 14:29:40 -08:00
|
|
|
|
2020-05-23 10:41:14 -07:00
|
|
|
get [Symbol.toStringTag]() {
|
|
|
|
return 'Request';
|
|
|
|
}
|
|
|
|
}
|
2016-11-23 13:39:35 -08:00
|
|
|
|
2018-01-27 12:28:56 -08:00
|
|
|
Object.defineProperties(Request.prototype, {
|
2020-03-13 08:06:25 -07:00
|
|
|
method: {enumerable: true},
|
|
|
|
url: {enumerable: true},
|
|
|
|
headers: {enumerable: true},
|
|
|
|
redirect: {enumerable: true},
|
|
|
|
clone: {enumerable: true},
|
|
|
|
signal: {enumerable: true}
|
2018-01-27 12:28:56 -08:00
|
|
|
});
|
|
|
|
|
2018-01-27 11:49:12 -08:00
|
|
|
/**
|
|
|
|
* Convert a Request to Node.js http request options.
|
|
|
|
*
|
|
|
|
* @param Request A Request instance
|
|
|
|
* @return Object The options object to be passed to http.request
|
|
|
|
*/
|
2020-05-25 08:11:56 -07:00
|
|
|
export const getNodeRequestOptions = request => {
|
2020-03-13 08:06:25 -07:00
|
|
|
const {parsedURL} = request[INTERNALS];
|
2018-01-27 12:28:56 -08:00
|
|
|
const headers = new Headers(request[INTERNALS].headers);
|
2016-11-23 13:39:35 -08:00
|
|
|
|
2020-03-13 08:06:25 -07:00
|
|
|
// Fetch step 1.3
|
2017-01-29 09:56:19 -08:00
|
|
|
if (!headers.has('Accept')) {
|
|
|
|
headers.set('Accept', '*/*');
|
2016-11-23 13:39:35 -08:00
|
|
|
}
|
|
|
|
|
2018-03-04 19:38:57 -08:00
|
|
|
// HTTP-network-or-cache fetch steps 2.4-2.7
|
2017-01-29 09:56:19 -08:00
|
|
|
let contentLengthValue = null;
|
2020-03-13 08:06:25 -07:00
|
|
|
if (request.body === null && /^(post|put)$/i.test(request.method)) {
|
2017-01-29 09:56:19 -08:00
|
|
|
contentLengthValue = '0';
|
2016-11-23 13:39:35 -08:00
|
|
|
}
|
2020-03-13 08:06:25 -07:00
|
|
|
|
|
|
|
if (request.body !== null) {
|
2016-11-23 13:39:35 -08:00
|
|
|
const totalBytes = getTotalBytes(request);
|
|
|
|
if (typeof totalBytes === 'number') {
|
2017-01-29 09:56:19 -08:00
|
|
|
contentLengthValue = String(totalBytes);
|
2016-11-23 13:39:35 -08:00
|
|
|
}
|
|
|
|
}
|
2020-03-13 08:06:25 -07:00
|
|
|
|
2017-01-29 09:56:19 -08:00
|
|
|
if (contentLengthValue) {
|
|
|
|
headers.set('Content-Length', contentLengthValue);
|
|
|
|
}
|
2016-11-23 13:39:35 -08:00
|
|
|
|
2018-03-04 19:38:57 -08:00
|
|
|
// HTTP-network-or-cache fetch step 2.11
|
2017-01-29 09:56:19 -08:00
|
|
|
if (!headers.has('User-Agent')) {
|
2020-05-25 07:56:04 -07:00
|
|
|
headers.set('User-Agent', 'node-fetch');
|
2017-01-29 09:56:19 -08:00
|
|
|
}
|
|
|
|
|
2018-03-04 19:38:57 -08:00
|
|
|
// HTTP-network-or-cache fetch step 2.15
|
2018-11-05 01:42:51 -08:00
|
|
|
if (request.compress && !headers.has('Accept-Encoding')) {
|
2020-05-22 19:48:14 -07:00
|
|
|
headers.set('Accept-Encoding', 'gzip,deflate,br');
|
2017-01-29 09:56:19 -08:00
|
|
|
}
|
2018-11-05 01:42:51 -08:00
|
|
|
|
2020-03-13 08:06:25 -07:00
|
|
|
let {agent} = request;
|
2019-05-05 05:12:33 -07:00
|
|
|
if (typeof agent === 'function') {
|
|
|
|
agent = agent(parsedURL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!headers.has('Connection') && !agent) {
|
2017-01-29 09:56:19 -08:00
|
|
|
headers.set('Connection', 'close');
|
|
|
|
}
|
|
|
|
|
2018-03-04 19:38:57 -08:00
|
|
|
// HTTP-network fetch step 4.2
|
2017-01-29 09:56:19 -08:00
|
|
|
// chunked encoding is handled by Node.js
|
2016-11-23 13:39:35 -08:00
|
|
|
|
2020-04-20 12:42:51 -07:00
|
|
|
const search = getSearch(parsedURL);
|
|
|
|
|
|
|
|
// Manually spread the URL object instead of spread syntax
|
2020-03-13 08:06:25 -07:00
|
|
|
const requestOptions = {
|
2020-04-20 12:42:51 -07:00
|
|
|
path: parsedURL.pathname + search,
|
2020-03-13 08:06:25 -07:00
|
|
|
pathname: parsedURL.pathname,
|
|
|
|
hostname: parsedURL.hostname,
|
|
|
|
protocol: parsedURL.protocol,
|
|
|
|
port: parsedURL.port,
|
|
|
|
hash: parsedURL.hash,
|
|
|
|
search: parsedURL.search,
|
|
|
|
query: parsedURL.query,
|
|
|
|
href: parsedURL.href,
|
2016-11-23 13:39:35 -08:00
|
|
|
method: request.method,
|
2020-05-25 07:43:10 -07:00
|
|
|
headers: headers[Symbol.for('nodejs.util.inspect.custom')](),
|
2020-06-04 08:54:02 -07:00
|
|
|
insecureHTTPParser: request.insecureHTTPParser,
|
2019-05-05 05:12:33 -07:00
|
|
|
agent
|
2020-03-13 08:06:25 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
return requestOptions;
|
2020-05-25 08:11:56 -07:00
|
|
|
};
|