rekwest 5.0.2 → 5.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +241 -241
- package/dist/cookies.js +19 -15
- package/dist/formdata.js +3 -1
- package/dist/postflight.js +3 -1
- package/dist/preflight.js +3 -1
- package/dist/transfer.js +3 -1
- package/package.json +74 -74
- package/src/cookies.mjs +97 -92
- package/src/formdata.mjs +1 -1
- package/src/postflight.mjs +136 -134
- package/src/preflight.mjs +87 -87
- package/src/transfer.mjs +114 -114
package/package.json
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
{
|
|
2
|
-
"author": {
|
|
3
|
-
"name": "Yehor Sergeenko",
|
|
4
|
-
"email": "yehor.sergeenko@gmail.com",
|
|
5
|
-
"url": "https://github.com/bricss"
|
|
6
|
-
},
|
|
7
|
-
"bugs": {
|
|
8
|
-
"url": "https://github.com/bricss/rekwest/issues"
|
|
9
|
-
},
|
|
10
|
-
"devDependencies": {
|
|
11
|
-
"@babel/cli": "^7.23.0",
|
|
12
|
-
"@babel/core": "^7.23.
|
|
13
|
-
"@babel/eslint-parser": "^7.
|
|
14
|
-
"@babel/preset-env": "^7.23.
|
|
15
|
-
"@stylistic/eslint-plugin-js": "^1.
|
|
16
|
-
"c8": "^8.0.1",
|
|
17
|
-
"eslint": "^8.53.0",
|
|
18
|
-
"eslint-config-ultra-refined": "^2.19.
|
|
19
|
-
"mocha": "^10.2.0"
|
|
20
|
-
},
|
|
21
|
-
"description": "The robust request library that humanity deserves 🌐",
|
|
22
|
-
"engines": {
|
|
23
|
-
"node": ">=18.13.0"
|
|
24
|
-
},
|
|
25
|
-
"exports": {
|
|
26
|
-
"import": "./src/index.mjs",
|
|
27
|
-
"require": "./dist/index.js"
|
|
28
|
-
},
|
|
29
|
-
"files": [
|
|
30
|
-
"dist",
|
|
31
|
-
"src"
|
|
32
|
-
],
|
|
33
|
-
"homepage": "https://github.com/bricss/rekwest#readme",
|
|
34
|
-
"keywords": [
|
|
35
|
-
"alpn",
|
|
36
|
-
"backoff",
|
|
37
|
-
"brotli",
|
|
38
|
-
"cookie",
|
|
39
|
-
"deflate",
|
|
40
|
-
"fetch",
|
|
41
|
-
"formdata",
|
|
42
|
-
"gzip",
|
|
43
|
-
"http",
|
|
44
|
-
"https",
|
|
45
|
-
"h2",
|
|
46
|
-
"h2c",
|
|
47
|
-
"http2",
|
|
48
|
-
"multipart",
|
|
49
|
-
"request",
|
|
50
|
-
"redirect",
|
|
51
|
-
"retry",
|
|
52
|
-
"retry-after",
|
|
53
|
-
"stream",
|
|
54
|
-
"upload"
|
|
55
|
-
],
|
|
56
|
-
"license": "MIT",
|
|
57
|
-
"name": "rekwest",
|
|
58
|
-
"repository": {
|
|
59
|
-
"type": "git",
|
|
60
|
-
"url": "https://github.com/bricss/rekwest.git"
|
|
61
|
-
},
|
|
62
|
-
"scripts": {
|
|
63
|
-
"build": "rm -rf dist && npx babel src -d dist",
|
|
64
|
-
"cert:gen": "openssl req -days 365 -keyout localhost.key -newkey ec -nodes -pkeyopt ec_paramgen_curve:prime256v1 -subj //SKIP=1/CN=localhost -out localhost.cert -x509",
|
|
65
|
-
"cert:ken": "openssl x509 -in localhost.cert -noout -text",
|
|
66
|
-
"lint": "eslint . --ext .cjs,.js,.mjs",
|
|
67
|
-
"prepack": "npm run build && sh pony.sh",
|
|
68
|
-
"pretest": "rm -rf coverage && npm run cert:gen",
|
|
69
|
-
"test": "mocha",
|
|
70
|
-
"test:bail": "mocha --bail",
|
|
71
|
-
"test:cover": "c8 --include=src --reporter=lcov --reporter=text npm test"
|
|
72
|
-
},
|
|
73
|
-
"version": "5.0.
|
|
74
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"author": {
|
|
3
|
+
"name": "Yehor Sergeenko",
|
|
4
|
+
"email": "yehor.sergeenko@gmail.com",
|
|
5
|
+
"url": "https://github.com/bricss"
|
|
6
|
+
},
|
|
7
|
+
"bugs": {
|
|
8
|
+
"url": "https://github.com/bricss/rekwest/issues"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@babel/cli": "^7.23.0",
|
|
12
|
+
"@babel/core": "^7.23.3",
|
|
13
|
+
"@babel/eslint-parser": "^7.23.3",
|
|
14
|
+
"@babel/preset-env": "^7.23.3",
|
|
15
|
+
"@stylistic/eslint-plugin-js": "^1.1.0",
|
|
16
|
+
"c8": "^8.0.1",
|
|
17
|
+
"eslint": "^8.53.0",
|
|
18
|
+
"eslint-config-ultra-refined": "^2.19.1",
|
|
19
|
+
"mocha": "^10.2.0"
|
|
20
|
+
},
|
|
21
|
+
"description": "The robust request library that humanity deserves 🌐",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18.13.0"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
"import": "./src/index.mjs",
|
|
27
|
+
"require": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"src"
|
|
32
|
+
],
|
|
33
|
+
"homepage": "https://github.com/bricss/rekwest#readme",
|
|
34
|
+
"keywords": [
|
|
35
|
+
"alpn",
|
|
36
|
+
"backoff",
|
|
37
|
+
"brotli",
|
|
38
|
+
"cookie",
|
|
39
|
+
"deflate",
|
|
40
|
+
"fetch",
|
|
41
|
+
"formdata",
|
|
42
|
+
"gzip",
|
|
43
|
+
"http",
|
|
44
|
+
"https",
|
|
45
|
+
"h2",
|
|
46
|
+
"h2c",
|
|
47
|
+
"http2",
|
|
48
|
+
"multipart",
|
|
49
|
+
"request",
|
|
50
|
+
"redirect",
|
|
51
|
+
"retry",
|
|
52
|
+
"retry-after",
|
|
53
|
+
"stream",
|
|
54
|
+
"upload"
|
|
55
|
+
],
|
|
56
|
+
"license": "MIT",
|
|
57
|
+
"name": "rekwest",
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "git+https://github.com/bricss/rekwest.git"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "rm -rf dist && npx babel src -d dist",
|
|
64
|
+
"cert:gen": "openssl req -days 365 -keyout localhost.key -newkey ec -nodes -pkeyopt ec_paramgen_curve:prime256v1 -subj //SKIP=1/CN=localhost -out localhost.cert -x509",
|
|
65
|
+
"cert:ken": "openssl x509 -in localhost.cert -noout -text",
|
|
66
|
+
"lint": "eslint . --ext .cjs,.js,.mjs",
|
|
67
|
+
"prepack": "npm run build && sh pony.sh",
|
|
68
|
+
"pretest": "rm -rf coverage && npm run cert:gen",
|
|
69
|
+
"test": "mocha",
|
|
70
|
+
"test:bail": "mocha --bail",
|
|
71
|
+
"test:cover": "c8 --include=src --reporter=lcov --reporter=text npm test"
|
|
72
|
+
},
|
|
73
|
+
"version": "5.0.3"
|
|
74
|
+
}
|
package/src/cookies.mjs
CHANGED
|
@@ -1,92 +1,97 @@
|
|
|
1
|
-
import {
|
|
2
|
-
brandCheck,
|
|
3
|
-
toCamelCase,
|
|
4
|
-
} from './utils.mjs';
|
|
5
|
-
|
|
6
|
-
export class Cookies extends URLSearchParams {
|
|
7
|
-
|
|
8
|
-
static #finalizers = new Set();
|
|
9
|
-
static jar = new Map();
|
|
10
|
-
|
|
11
|
-
static #register(target, value) {
|
|
12
|
-
const finalizer = new FinalizationRegistry((heldValue) => {
|
|
13
|
-
clearTimeout(heldValue);
|
|
14
|
-
this.#finalizers.delete(finalizer);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
finalizer.register(target, value);
|
|
18
|
-
this.#finalizers.add(finalizer);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
#chronometry = new Map();
|
|
22
|
-
|
|
23
|
-
get [Symbol.toStringTag]() {
|
|
24
|
-
return this.constructor.name;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
constructor(input, { cookiesTTL } = { cookiesTTL: false }) {
|
|
28
|
-
if (Array.isArray(input) && input.every((it) => !Array.isArray(it))) {
|
|
29
|
-
input = input.map((it) => {
|
|
30
|
-
if (!cookiesTTL) {
|
|
31
|
-
return [it.split(';').at(0).trim()];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const [cookie, ...attrs] = it.split(';').map((it) => it.trim());
|
|
35
|
-
const ttl =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return [
|
|
46
|
-
cookie.replace(/\u0022/g, ''),
|
|
47
|
-
Object.keys(ttl).length ? ttl : null,
|
|
48
|
-
];
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
super(Array.isArray(input) ? input.map((it) => it.at(0)).join('&') : input);
|
|
53
|
-
|
|
54
|
-
if (Array.isArray(input) && cookiesTTL) {
|
|
55
|
-
input.filter((it) => it.at(1))
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
this.#chronometry.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
1
|
+
import {
|
|
2
|
+
brandCheck,
|
|
3
|
+
toCamelCase,
|
|
4
|
+
} from './utils.mjs';
|
|
5
|
+
|
|
6
|
+
export class Cookies extends URLSearchParams {
|
|
7
|
+
|
|
8
|
+
static #finalizers = new Set();
|
|
9
|
+
static jar = new Map();
|
|
10
|
+
|
|
11
|
+
static #register(target, value) {
|
|
12
|
+
const finalizer = new FinalizationRegistry((heldValue) => {
|
|
13
|
+
clearTimeout(heldValue);
|
|
14
|
+
this.#finalizers.delete(finalizer);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
finalizer.register(target, value);
|
|
18
|
+
this.#finalizers.add(finalizer);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#chronometry = new Map();
|
|
22
|
+
|
|
23
|
+
get [Symbol.toStringTag]() {
|
|
24
|
+
return this.constructor.name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor(input, { cookiesTTL } = { cookiesTTL: false }) {
|
|
28
|
+
if (Array.isArray(input) && input.every((it) => !Array.isArray(it))) {
|
|
29
|
+
input = input.map((it) => {
|
|
30
|
+
if (!cookiesTTL) {
|
|
31
|
+
return [it.split(';').at(0).trim()];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const [cookie, ...attrs] = it.split(';').map((it) => it.trim());
|
|
35
|
+
const ttl = {};
|
|
36
|
+
|
|
37
|
+
for (const val of attrs) {
|
|
38
|
+
if (/(?:Expires|Max-Age)=/i.test(val)) {
|
|
39
|
+
const [key, value] = val.toLowerCase().split('=');
|
|
40
|
+
|
|
41
|
+
ttl[toCamelCase(key)] = !Number.isNaN(Number(value)) ? value * 1e3 : Date.parse(value) - Date.now();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
cookie.replace(/\u0022/g, ''),
|
|
47
|
+
Object.keys(ttl).length ? ttl : null,
|
|
48
|
+
];
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
super(Array.isArray(input) ? input.map((it) => it.at(0)).join('&') : input);
|
|
53
|
+
|
|
54
|
+
if (Array.isArray(input) && cookiesTTL) {
|
|
55
|
+
for (const [cookie, ttl] of input.filter((it) => it.at(1))) {
|
|
56
|
+
const key = cookie.split('=').at(0);
|
|
57
|
+
|
|
58
|
+
if (this.#chronometry.has(key)) {
|
|
59
|
+
clearTimeout(this.#chronometry.get(key));
|
|
60
|
+
this.#chronometry.delete(key);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const { expires, maxAge } = ttl;
|
|
64
|
+
|
|
65
|
+
for (const ms of [
|
|
66
|
+
maxAge,
|
|
67
|
+
expires,
|
|
68
|
+
]) {
|
|
69
|
+
if (!Number.isInteger(ms)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ref = new WeakRef(this);
|
|
74
|
+
const tid = setTimeout(() => {
|
|
75
|
+
const ctx = ref.deref();
|
|
76
|
+
|
|
77
|
+
if (ctx) {
|
|
78
|
+
ctx.#chronometry.delete(key);
|
|
79
|
+
ctx.delete(key);
|
|
80
|
+
}
|
|
81
|
+
}, Math.max(ms, 0));
|
|
82
|
+
|
|
83
|
+
this.constructor.#register(this, tid);
|
|
84
|
+
this.#chronometry.set(key, tid);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
toString() {
|
|
92
|
+
brandCheck(this, Cookies);
|
|
93
|
+
|
|
94
|
+
return super.toString().split('&').join('; ').trim();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}
|
package/src/formdata.mjs
CHANGED
package/src/postflight.mjs
CHANGED
|
@@ -1,134 +1,136 @@
|
|
|
1
|
-
import http2 from 'node:http2';
|
|
2
|
-
import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
|
|
3
|
-
import {
|
|
4
|
-
requestCredentials,
|
|
5
|
-
requestRedirect,
|
|
6
|
-
requestRedirectCodes,
|
|
7
|
-
} from './constants.mjs';
|
|
8
|
-
import { Cookies } from './cookies.mjs';
|
|
9
|
-
import { RequestError } from './errors.mjs';
|
|
10
|
-
import rekwest from './index.mjs';
|
|
11
|
-
import { mixin } from './mixin.mjs';
|
|
12
|
-
import {
|
|
13
|
-
admix,
|
|
14
|
-
maxRetryAfterError,
|
|
15
|
-
sameOrigin,
|
|
16
|
-
} from './utils.mjs';
|
|
17
|
-
|
|
18
|
-
const {
|
|
19
|
-
HTTP2_HEADER_LOCATION,
|
|
20
|
-
HTTP2_HEADER_RETRY_AFTER,
|
|
21
|
-
HTTP2_HEADER_SET_COOKIE,
|
|
22
|
-
HTTP2_METHOD_GET,
|
|
23
|
-
HTTP2_METHOD_HEAD,
|
|
24
|
-
HTTP2_METHOD_POST,
|
|
25
|
-
HTTP_STATUS_BAD_REQUEST,
|
|
26
|
-
HTTP_STATUS_FOUND,
|
|
27
|
-
HTTP_STATUS_MOVED_PERMANENTLY,
|
|
28
|
-
HTTP_STATUS_SEE_OTHER,
|
|
29
|
-
} = http2.constants;
|
|
30
|
-
|
|
31
|
-
export const postflight = (req, res, options, { reject, resolve }) => {
|
|
32
|
-
const { cookies, credentials, follow, h2, redirect, url } = options;
|
|
33
|
-
let headers;
|
|
34
|
-
|
|
35
|
-
if (h2) {
|
|
36
|
-
headers = res;
|
|
37
|
-
res = req;
|
|
38
|
-
} else {
|
|
39
|
-
res.once('error', reject);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
admix(res, headers, options);
|
|
43
|
-
|
|
44
|
-
if (cookies !== false && res.headers[HTTP2_HEADER_SET_COOKIE]) {
|
|
45
|
-
if (Cookies.jar.has(url.origin)) {
|
|
46
|
-
const cookie = new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options);
|
|
47
|
-
|
|
48
|
-
Cookies.jar.get(url.origin).forEach((val, key) => {
|
|
49
|
-
if (!cookie.has(key)) {
|
|
50
|
-
cookie.set(key, val);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
Cookies.jar.set(url.origin, cookie);
|
|
54
|
-
} else {
|
|
55
|
-
Cookies.jar.set(url.origin, new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options));
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
Reflect.defineProperty(res, 'cookies', {
|
|
60
|
-
enumerable: true,
|
|
61
|
-
value: cookies !== false && Cookies.jar.has(url.origin)
|
|
62
|
-
? Cookies.jar.get(url.origin)
|
|
63
|
-
: void 0,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const { statusCode } = res;
|
|
67
|
-
|
|
68
|
-
if (follow && /3\d{2}/.test(statusCode) && res.headers[HTTP2_HEADER_LOCATION]) {
|
|
69
|
-
if (!requestRedirectCodes.includes(statusCode)) {
|
|
70
|
-
return res.emit('error', new RangeError(`Invalid status code: ${ statusCode }`));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (redirect === requestRedirect.error) {
|
|
74
|
-
return res.emit('error', new RequestError(`Unexpected redirect, redirect mode is set to '${ redirect }'.`));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (redirect === requestRedirect.follow) {
|
|
78
|
-
const location = new URL(res.headers[HTTP2_HEADER_LOCATION], url);
|
|
79
|
-
|
|
80
|
-
if (!/^https?:/i.test(location.protocol)) {
|
|
81
|
-
return res.emit('error', new RequestError('URL scheme must be "http" or "https".'));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (!sameOrigin(location, url)) {
|
|
85
|
-
if (credentials !== requestCredentials.include) {
|
|
86
|
-
options.credentials = requestCredentials.omit;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
options.h2 = false;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (statusCode !== HTTP_STATUS_SEE_OTHER && options.body?.pipe?.constructor === Function) {
|
|
93
|
-
return res.emit('error', new RequestError(`Unable to ${ redirect } redirect with streamable body.`));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (([
|
|
97
|
-
HTTP_STATUS_MOVED_PERMANENTLY,
|
|
98
|
-
HTTP_STATUS_FOUND,
|
|
99
|
-
].includes(statusCode) && options.method === HTTP2_METHOD_POST) || (statusCode === HTTP_STATUS_SEE_OTHER && ![
|
|
100
|
-
HTTP2_METHOD_GET,
|
|
101
|
-
HTTP2_METHOD_HEAD,
|
|
102
|
-
].includes(options.method))) {
|
|
103
|
-
Object.keys(options.headers).filter((
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
options.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
interval =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
1
|
+
import http2 from 'node:http2';
|
|
2
|
+
import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
|
|
3
|
+
import {
|
|
4
|
+
requestCredentials,
|
|
5
|
+
requestRedirect,
|
|
6
|
+
requestRedirectCodes,
|
|
7
|
+
} from './constants.mjs';
|
|
8
|
+
import { Cookies } from './cookies.mjs';
|
|
9
|
+
import { RequestError } from './errors.mjs';
|
|
10
|
+
import rekwest from './index.mjs';
|
|
11
|
+
import { mixin } from './mixin.mjs';
|
|
12
|
+
import {
|
|
13
|
+
admix,
|
|
14
|
+
maxRetryAfterError,
|
|
15
|
+
sameOrigin,
|
|
16
|
+
} from './utils.mjs';
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
HTTP2_HEADER_LOCATION,
|
|
20
|
+
HTTP2_HEADER_RETRY_AFTER,
|
|
21
|
+
HTTP2_HEADER_SET_COOKIE,
|
|
22
|
+
HTTP2_METHOD_GET,
|
|
23
|
+
HTTP2_METHOD_HEAD,
|
|
24
|
+
HTTP2_METHOD_POST,
|
|
25
|
+
HTTP_STATUS_BAD_REQUEST,
|
|
26
|
+
HTTP_STATUS_FOUND,
|
|
27
|
+
HTTP_STATUS_MOVED_PERMANENTLY,
|
|
28
|
+
HTTP_STATUS_SEE_OTHER,
|
|
29
|
+
} = http2.constants;
|
|
30
|
+
|
|
31
|
+
export const postflight = (req, res, options, { reject, resolve }) => {
|
|
32
|
+
const { cookies, credentials, follow, h2, redirect, url } = options;
|
|
33
|
+
let headers;
|
|
34
|
+
|
|
35
|
+
if (h2) {
|
|
36
|
+
headers = res;
|
|
37
|
+
res = req;
|
|
38
|
+
} else {
|
|
39
|
+
res.once('error', reject);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
admix(res, headers, options);
|
|
43
|
+
|
|
44
|
+
if (cookies !== false && res.headers[HTTP2_HEADER_SET_COOKIE]) {
|
|
45
|
+
if (Cookies.jar.has(url.origin)) {
|
|
46
|
+
const cookie = new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options);
|
|
47
|
+
|
|
48
|
+
Cookies.jar.get(url.origin).forEach((val, key) => {
|
|
49
|
+
if (!cookie.has(key)) {
|
|
50
|
+
cookie.set(key, val);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
Cookies.jar.set(url.origin, cookie);
|
|
54
|
+
} else {
|
|
55
|
+
Cookies.jar.set(url.origin, new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Reflect.defineProperty(res, 'cookies', {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
value: cookies !== false && Cookies.jar.has(url.origin)
|
|
62
|
+
? Cookies.jar.get(url.origin)
|
|
63
|
+
: void 0,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const { statusCode } = res;
|
|
67
|
+
|
|
68
|
+
if (follow && /3\d{2}/.test(statusCode) && res.headers[HTTP2_HEADER_LOCATION]) {
|
|
69
|
+
if (!requestRedirectCodes.includes(statusCode)) {
|
|
70
|
+
return res.emit('error', new RangeError(`Invalid status code: ${ statusCode }`));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (redirect === requestRedirect.error) {
|
|
74
|
+
return res.emit('error', new RequestError(`Unexpected redirect, redirect mode is set to '${ redirect }'.`));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (redirect === requestRedirect.follow) {
|
|
78
|
+
const location = new URL(res.headers[HTTP2_HEADER_LOCATION], url);
|
|
79
|
+
|
|
80
|
+
if (!/^https?:/i.test(location.protocol)) {
|
|
81
|
+
return res.emit('error', new RequestError('URL scheme must be "http" or "https".'));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!sameOrigin(location, url)) {
|
|
85
|
+
if (credentials !== requestCredentials.include) {
|
|
86
|
+
options.credentials = requestCredentials.omit;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
options.h2 = false;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (statusCode !== HTTP_STATUS_SEE_OTHER && options.body?.pipe?.constructor === Function) {
|
|
93
|
+
return res.emit('error', new RequestError(`Unable to ${ redirect } redirect with streamable body.`));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (([
|
|
97
|
+
HTTP_STATUS_MOVED_PERMANENTLY,
|
|
98
|
+
HTTP_STATUS_FOUND,
|
|
99
|
+
].includes(statusCode) && options.method === HTTP2_METHOD_POST) || (statusCode === HTTP_STATUS_SEE_OTHER && ![
|
|
100
|
+
HTTP2_METHOD_GET,
|
|
101
|
+
HTTP2_METHOD_HEAD,
|
|
102
|
+
].includes(options.method))) {
|
|
103
|
+
for (const it of Object.keys(options.headers).filter((val) => /^content-/i.test(val))) {
|
|
104
|
+
Reflect.deleteProperty(options.headers, it);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
options.body = null;
|
|
108
|
+
options.method = HTTP2_METHOD_GET;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
options.follow--;
|
|
112
|
+
options.redirected = true;
|
|
113
|
+
options.url = location;
|
|
114
|
+
|
|
115
|
+
if (statusCode === HTTP_STATUS_MOVED_PERMANENTLY && res.headers[HTTP2_HEADER_RETRY_AFTER]) {
|
|
116
|
+
let interval = res.headers[HTTP2_HEADER_RETRY_AFTER];
|
|
117
|
+
|
|
118
|
+
interval = Number(interval) * 1000 || new Date(interval) - Date.now();
|
|
119
|
+
|
|
120
|
+
if (interval > options.maxRetryAfter) {
|
|
121
|
+
return res.emit('error', maxRetryAfterError(interval, { cause: mixin(res, options) }));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return setTimeoutPromise(interval).then(() => rekwest(options.url, options).then(resolve, reject));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return rekwest(options.url, options).then(resolve, reject);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (statusCode >= HTTP_STATUS_BAD_REQUEST) {
|
|
132
|
+
return reject(mixin(res, options));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
resolve(mixin(res, options));
|
|
136
|
+
};
|