rekwest 6.2.1 → 7.0.1

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.
Files changed (44) hide show
  1. package/README.md +11 -10
  2. package/dist/codecs.cjs +55 -0
  3. package/dist/{config.js → config.cjs} +15 -19
  4. package/dist/{cookies.js → cookies.cjs} +2 -2
  5. package/dist/{formdata.js → formdata.cjs} +8 -11
  6. package/dist/{index.js → index.cjs} +27 -15
  7. package/dist/{mixin.js → mixin.cjs} +22 -17
  8. package/dist/postflight.cjs +60 -0
  9. package/dist/{preflight.js → preflight.cjs} +13 -23
  10. package/dist/redirects.cjs +63 -0
  11. package/dist/retries.cjs +57 -0
  12. package/dist/{transfer.js → transfer.cjs} +13 -40
  13. package/dist/transform.cjs +105 -0
  14. package/dist/{utils.js → utils.cjs} +57 -80
  15. package/dist/{validation.js → validation.cjs} +1 -1
  16. package/package.json +14 -13
  17. package/src/{ackn.mjs → ackn.js} +33 -33
  18. package/src/codecs.js +55 -0
  19. package/src/{config.mjs → config.js} +88 -93
  20. package/src/{constants.mjs → constants.js} +29 -29
  21. package/src/{cookies.mjs → cookies.js} +100 -100
  22. package/src/{formdata.mjs → formdata.js} +8 -14
  23. package/src/{index.mjs → index.js} +22 -22
  24. package/src/{mediatypes.mjs → mediatypes.js} +6 -6
  25. package/src/{mixin.mjs → mixin.js} +25 -26
  26. package/src/postflight.js +56 -0
  27. package/src/{preflight.mjs → preflight.js} +84 -91
  28. package/src/redirects.js +79 -0
  29. package/src/retries.js +51 -0
  30. package/src/transfer.js +92 -0
  31. package/src/transform.js +109 -0
  32. package/src/utils.js +187 -0
  33. package/src/{validation.mjs → validation.js} +33 -33
  34. package/dist/postflight.js +0 -117
  35. package/dist/transform.js +0 -79
  36. package/src/postflight.mjs +0 -136
  37. package/src/transfer.mjs +0 -121
  38. package/src/transform.mjs +0 -82
  39. package/src/utils.mjs +0 -205
  40. /package/dist/{ackn.js → ackn.cjs} +0 -0
  41. /package/dist/{constants.js → constants.cjs} +0 -0
  42. /package/dist/{errors.js → errors.cjs} +0 -0
  43. /package/dist/{mediatypes.js → mediatypes.cjs} +0 -0
  44. /package/src/{errors.mjs → errors.js} +0 -0
package/src/utils.js ADDED
@@ -0,0 +1,187 @@
1
+ import {
2
+ Blob,
3
+ File,
4
+ } from 'node:buffer';
5
+ import http2 from 'node:http2';
6
+ import {
7
+ isReadable,
8
+ Readable,
9
+ } from 'node:stream';
10
+ import config, { isZstdSupported } from './config.js';
11
+ import { TimeoutError } from './errors.js';
12
+
13
+ const {
14
+ HTTP2_HEADER_ACCEPT_ENCODING,
15
+ HTTP2_HEADER_STATUS,
16
+ } = http2.constants;
17
+
18
+ export const augment = (res, headers, options) => {
19
+ const { h2 } = options;
20
+
21
+ if (h2) {
22
+ Reflect.defineProperty(res, 'headers', {
23
+ enumerable: true,
24
+ value: headers,
25
+ });
26
+
27
+ Reflect.defineProperty(res, 'httpVersion', {
28
+ enumerable: true,
29
+ value: `${ h2 + 1 }.0`,
30
+ });
31
+
32
+ Reflect.defineProperty(res, 'statusCode', {
33
+ enumerable: true,
34
+ value: headers[HTTP2_HEADER_STATUS],
35
+ });
36
+ }
37
+
38
+ Reflect.defineProperty(res, 'ok', {
39
+ enumerable: true,
40
+ value: /^2\d{2}$/.test(res.statusCode),
41
+ });
42
+
43
+ Reflect.defineProperty(res, 'redirected', {
44
+ enumerable: true,
45
+ value: !!options.redirected,
46
+ });
47
+ };
48
+
49
+ export const brandCheck = (value, ctor) => {
50
+ if (!(value instanceof ctor)) {
51
+ throw new TypeError('Illegal invocation.');
52
+ }
53
+ };
54
+
55
+ export const copyWithMerge = (target, ...rest) => {
56
+ target = structuredClone(target);
57
+ if (!rest.length) {
58
+ return target;
59
+ }
60
+
61
+ return merge(target, ...rest);
62
+ };
63
+
64
+ export const dispatch = (req, { body }) => {
65
+ if (isReadable(body)) {
66
+ body.pipe(req);
67
+ } else {
68
+ req.end(body);
69
+ }
70
+ };
71
+
72
+ export const isFileLike = (value) => {
73
+ return [
74
+ Blob,
75
+ File,
76
+ ].some((it) => value instanceof it);
77
+ };
78
+
79
+ export const isPipeStream = (value) => {
80
+ return value instanceof Readable;
81
+ };
82
+
83
+ export const isReadableStream = (value) => {
84
+ return value instanceof ReadableStream;
85
+ };
86
+
87
+ export const merge = (target, ...rest) => {
88
+ rest = rest.filter((it) => Object(it) === it);
89
+ for (const source of rest) {
90
+ for (const key of Object.getOwnPropertyNames(source)) {
91
+ const sv = source[key];
92
+ const tv = target[key];
93
+
94
+ if (Object(sv) === sv && Object(tv) === tv) {
95
+ target[key] = merge(tv, sv);
96
+ continue;
97
+ }
98
+
99
+ target[key] = source[key];
100
+ }
101
+ }
102
+
103
+ return target;
104
+ };
105
+
106
+ export const normalize = (url, options = {}) => {
107
+ if (!options.redirected) {
108
+ options = copyWithMerge(config.defaults, options);
109
+ }
110
+
111
+ if (options.trimTrailingSlashes) {
112
+ url = `${ url }`.replace(/(?<!:)\/+/g, '/');
113
+ }
114
+
115
+ if (options.stripTrailingSlash) {
116
+ url = `${ url }`.replace(/\/$|\/(?=#)|\/(?=\?)/g, '');
117
+ }
118
+
119
+ return Object.assign(options, {
120
+ headers: normalizeHeaders(options.headers),
121
+ method: options.method.toUpperCase(),
122
+ url: new URL(url, options.baseURL),
123
+ });
124
+ };
125
+
126
+ export const normalizeHeaders = (headers) => {
127
+ const collector = {};
128
+
129
+ for (const [key, value] of Object.entries(headers ?? {})) {
130
+ const name = key.toLowerCase();
131
+
132
+ collector[key] = value;
133
+
134
+ if (key === HTTP2_HEADER_ACCEPT_ENCODING && !isZstdSupported) {
135
+ const stripped = value.replace(/\s?zstd,?/gi, '').trim();
136
+
137
+ if (stripped) {
138
+ collector[key] = stripped;
139
+ } else {
140
+ Reflect.deleteProperty(collector, name);
141
+ }
142
+ }
143
+ }
144
+
145
+ return collector;
146
+ };
147
+
148
+ export const sameOrigin = (a, b) => a.protocol === b.protocol && a.hostname === b.hostname && a.port === b.port;
149
+
150
+ export const snoop = (client, req, options) => {
151
+ req.once('close', () => client?.close());
152
+ req.once('end', () => client?.close());
153
+ req.once('timeout', () => req.destroy(new TimeoutError(`Timed out after ${ options.timeout } ms.`)));
154
+ req.once('trailers', (trailers) => {
155
+ Reflect.defineProperty(req, 'trailers', {
156
+ enumerable: true,
157
+ value: trailers,
158
+ });
159
+ });
160
+ };
161
+
162
+ export const stripHeaders = (headers = {}, names = []) => {
163
+ names = new Set(names);
164
+
165
+ return Object.fromEntries(
166
+ Object.entries(headers).filter(
167
+ ([key]) => !names.has(key.toLowerCase()),
168
+ ),
169
+ );
170
+ };
171
+
172
+ export async function* tap(value) {
173
+ if (Reflect.has(value, Symbol.asyncIterator)) {
174
+ yield* value;
175
+ } else if (value.stream) {
176
+ yield* value.stream();
177
+ } else {
178
+ yield await value.arrayBuffer();
179
+ }
180
+ }
181
+
182
+ export const toCamelCase = (str) => str?.toLowerCase().replace(
183
+ /\p{Punctuation}.|\p{White_Space}./gu,
184
+ (val) => val.replace(/\p{Punctuation}+|\p{White_Space}+/gu, '').toUpperCase(),
185
+ );
186
+
187
+ export const unwind = (encodings) => encodings.split(',').map((it) => it.toLowerCase().trim());
@@ -1,33 +1,33 @@
1
- import http2 from 'node:http2';
2
- import {
3
- requestCredentials,
4
- requestRedirect,
5
- } from './constants.mjs';
6
-
7
- const {
8
- HTTP2_METHOD_GET,
9
- HTTP2_METHOD_HEAD,
10
- } = http2.constants;
11
-
12
- export const validation = (options = {}) => {
13
- if (options.body && [
14
- HTTP2_METHOD_GET,
15
- HTTP2_METHOD_HEAD,
16
- ].includes(options.method)) {
17
- throw new TypeError(`Request with ${ HTTP2_METHOD_GET }/${ HTTP2_METHOD_HEAD } method cannot have body.`);
18
- }
19
-
20
- if (!Object.values(requestCredentials).includes(options.credentials)) {
21
- throw new TypeError(`Failed to read the 'credentials' property from 'options': The provided value '${
22
- options.credentials
23
- }' is not a valid enum value.`);
24
- }
25
-
26
- if (!Reflect.has(requestRedirect, options.redirect)) {
27
- throw new TypeError(`Failed to read the 'redirect' property from 'options': The provided value '${
28
- options.redirect
29
- }' is not a valid enum value.`);
30
- }
31
-
32
- return options;
33
- };
1
+ import http2 from 'node:http2';
2
+ import {
3
+ requestCredentials,
4
+ requestRedirect,
5
+ } from './constants.js';
6
+
7
+ const {
8
+ HTTP2_METHOD_GET,
9
+ HTTP2_METHOD_HEAD,
10
+ } = http2.constants;
11
+
12
+ export const validation = (options = {}) => {
13
+ if (options.body && [
14
+ HTTP2_METHOD_GET,
15
+ HTTP2_METHOD_HEAD,
16
+ ].includes(options.method)) {
17
+ throw new TypeError(`Request with ${ HTTP2_METHOD_GET }/${ HTTP2_METHOD_HEAD } method cannot have body.`);
18
+ }
19
+
20
+ if (!Object.values(requestCredentials).includes(options.credentials)) {
21
+ throw new TypeError(`Failed to read the 'credentials' property from 'options': The provided value '${
22
+ options.credentials
23
+ }' is not a valid enum value.`);
24
+ }
25
+
26
+ if (!Reflect.has(requestRedirect, options.redirect)) {
27
+ throw new TypeError(`Failed to read the 'redirect' property from 'options': The provided value '${
28
+ options.redirect
29
+ }' is not a valid enum value.`);
30
+ }
31
+
32
+ return options;
33
+ };
@@ -1,117 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.postflight = void 0;
7
- var _nodeHttp = _interopRequireDefault(require("node:http2"));
8
- var _promises = require("node:timers/promises");
9
- var _constants = require("./constants");
10
- var _cookies = require("./cookies");
11
- var _errors = require("./errors");
12
- var _index = _interopRequireDefault(require("./index"));
13
- var _mixin = require("./mixin");
14
- var _utils = require("./utils");
15
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
- const {
17
- HTTP2_HEADER_LOCATION,
18
- HTTP2_HEADER_RETRY_AFTER,
19
- HTTP2_HEADER_SET_COOKIE,
20
- HTTP2_METHOD_GET,
21
- HTTP2_METHOD_HEAD,
22
- HTTP2_METHOD_POST,
23
- HTTP_STATUS_BAD_REQUEST,
24
- HTTP_STATUS_FOUND,
25
- HTTP_STATUS_MOVED_PERMANENTLY,
26
- HTTP_STATUS_SEE_OTHER
27
- } = _nodeHttp.default.constants;
28
- const postflight = (req, res, options, {
29
- reject,
30
- resolve
31
- }) => {
32
- const {
33
- cookies,
34
- credentials,
35
- follow,
36
- h2,
37
- redirect,
38
- url
39
- } = options;
40
- let headers;
41
- if (h2) {
42
- headers = res;
43
- res = req;
44
- } else {
45
- res.once('error', reject);
46
- }
47
- (0, _utils.admix)(res, headers, options);
48
- if (cookies !== false && res.headers[HTTP2_HEADER_SET_COOKIE]) {
49
- if (_cookies.Cookies.jar.has(url.origin)) {
50
- const cookie = new _cookies.Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options);
51
- _cookies.Cookies.jar.get(url.origin).forEach((val, key) => {
52
- if (!cookie.has(key)) {
53
- cookie.set(key, val);
54
- }
55
- });
56
- _cookies.Cookies.jar.set(url.origin, cookie);
57
- } else {
58
- _cookies.Cookies.jar.set(url.origin, new _cookies.Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options));
59
- }
60
- }
61
- Reflect.defineProperty(res, 'cookies', {
62
- enumerable: true,
63
- value: cookies !== false && _cookies.Cookies.jar.has(url.origin) ? _cookies.Cookies.jar.get(url.origin) : void 0
64
- });
65
- const {
66
- statusCode
67
- } = res;
68
- if (follow && /3\d{2}/.test(statusCode) && res.headers[HTTP2_HEADER_LOCATION]) {
69
- if (!_constants.requestRedirectCodes.includes(statusCode)) {
70
- return res.emit('error', new RangeError(`Invalid status code: ${statusCode}`));
71
- }
72
- if (redirect === _constants.requestRedirect.error) {
73
- return res.emit('error', new _errors.RequestError(`Unexpected redirect, redirect mode is set to '${redirect}'.`));
74
- }
75
- if (redirect === _constants.requestRedirect.follow) {
76
- const location = new URL(res.headers[HTTP2_HEADER_LOCATION], url);
77
- if (!/^https?:/i.test(location.protocol)) {
78
- return res.emit('error', new _errors.RequestError('URL scheme must be "http" or "https".'));
79
- }
80
- if (!(0, _utils.sameOrigin)(location, url)) {
81
- if (credentials !== _constants.requestCredentials.include) {
82
- options.credentials = _constants.requestCredentials.omit;
83
- }
84
- options.h2 = false;
85
- }
86
- if (statusCode !== HTTP_STATUS_SEE_OTHER && options.body?.pipe?.constructor === Function) {
87
- return res.emit('error', new _errors.RequestError(`Unable to ${redirect} redirect with streamable body.`));
88
- }
89
- if ([HTTP_STATUS_MOVED_PERMANENTLY, HTTP_STATUS_FOUND].includes(statusCode) && options.method === HTTP2_METHOD_POST || statusCode === HTTP_STATUS_SEE_OTHER && ![HTTP2_METHOD_GET, HTTP2_METHOD_HEAD].includes(options.method)) {
90
- for (const it of Object.keys(options.headers).filter(val => /^content-/i.test(val))) {
91
- Reflect.deleteProperty(options.headers, it);
92
- }
93
- options.body = null;
94
- options.method = HTTP2_METHOD_GET;
95
- }
96
- options.follow--;
97
- options.redirected = true;
98
- options.url = location;
99
- if (statusCode === HTTP_STATUS_MOVED_PERMANENTLY && res.headers[HTTP2_HEADER_RETRY_AFTER]) {
100
- let interval = res.headers[HTTP2_HEADER_RETRY_AFTER];
101
- interval = Number(interval) * 1e3 || new Date(interval) - Date.now();
102
- if (interval > options.maxRetryAfter) {
103
- return res.emit('error', (0, _utils.maxRetryAfterError)(interval, {
104
- cause: (0, _mixin.mixin)(res, options)
105
- }));
106
- }
107
- return (0, _promises.setTimeout)(interval).then(() => (0, _index.default)(options.url, options).then(resolve, reject));
108
- }
109
- return (0, _index.default)(options.url, options).then(resolve, reject);
110
- }
111
- }
112
- if (statusCode >= HTTP_STATUS_BAD_REQUEST) {
113
- return reject((0, _mixin.mixin)(res, options));
114
- }
115
- resolve((0, _mixin.mixin)(res, options));
116
- };
117
- exports.postflight = postflight;
package/dist/transform.js DELETED
@@ -1,79 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.transform = void 0;
7
- var _nodeHttp = _interopRequireDefault(require("node:http2"));
8
- var _nodeStream = require("node:stream");
9
- var _consumers = require("node:stream/consumers");
10
- var _nodeUtil = require("node:util");
11
- var _formdata = require("./formdata");
12
- var _mediatypes = require("./mediatypes");
13
- var _utils = require("./utils");
14
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
- const {
16
- HTTP2_HEADER_CONTENT_ENCODING,
17
- HTTP2_HEADER_CONTENT_LENGTH,
18
- HTTP2_HEADER_CONTENT_TYPE
19
- } = _nodeHttp.default.constants;
20
- const transform = async options => {
21
- let {
22
- body,
23
- headers
24
- } = options;
25
- if (!body) {
26
- return options;
27
- }
28
- if ((0, _utils.isFileLike)(body)) {
29
- headers = {
30
- [HTTP2_HEADER_CONTENT_LENGTH]: body.size,
31
- [HTTP2_HEADER_CONTENT_TYPE]: body.type || _mediatypes.APPLICATION_OCTET_STREAM
32
- };
33
- body = body.stream();
34
- } else if (_formdata.FormData.alike(body)) {
35
- body = _formdata.FormData.actuate(body);
36
- headers = {
37
- [HTTP2_HEADER_CONTENT_TYPE]: body.contentType
38
- };
39
- } else if (!Buffer.isBuffer(body)) {
40
- if (_nodeUtil.types.isAnyArrayBuffer(body)) {
41
- body = Buffer.from(body);
42
- } else if (_nodeUtil.types.isArrayBufferView(body)) {
43
- body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
44
- } else if (body === Object(body) && !Reflect.has(body, Symbol.asyncIterator)) {
45
- if (body.constructor === URLSearchParams) {
46
- headers = {
47
- [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes.APPLICATION_FORM_URLENCODED
48
- };
49
- body = body.toString();
50
- } else if (!(!Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
51
- headers = {
52
- [HTTP2_HEADER_CONTENT_TYPE]: _mediatypes.APPLICATION_JSON
53
- };
54
- body = JSON.stringify(body);
55
- }
56
- }
57
- }
58
- const encodings = options.headers[HTTP2_HEADER_CONTENT_ENCODING];
59
- if (body === Object(body) && (Reflect.has(body, Symbol.asyncIterator) || !Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
60
- body = (0, _nodeStream.isReadable)(body) ? (0, _utils.isReadableStream)(body) ? _nodeStream.Readable.fromWeb(body) : body : _nodeStream.Readable.from(body);
61
- body = encodings ? (0, _utils.compress)(body, encodings, options) : body;
62
- } else if (encodings) {
63
- body = await (0, _consumers.buffer)((0, _utils.compress)(_nodeStream.Readable.from(body), encodings, options));
64
- }
65
- Object.assign(options.headers, {
66
- ...headers,
67
- ...(!body[Symbol.asyncIterator] && {
68
- [HTTP2_HEADER_CONTENT_LENGTH]: Buffer.byteLength(body)
69
- }),
70
- ...(options.headers[HTTP2_HEADER_CONTENT_TYPE] && {
71
- [HTTP2_HEADER_CONTENT_TYPE]: options.headers[HTTP2_HEADER_CONTENT_TYPE]
72
- })
73
- });
74
- return {
75
- ...options,
76
- body
77
- };
78
- };
79
- exports.transform = transform;
@@ -1,136 +0,0 @@
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) * 1e3 || 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
- };
package/src/transfer.mjs DELETED
@@ -1,121 +0,0 @@
1
- import http from 'node:http';
2
- import http2 from 'node:http2';
3
- import https from 'node:https';
4
- import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
5
- import { ackn } from './ackn.mjs';
6
- import { RequestError } from './errors.mjs';
7
- import { postflight } from './postflight.mjs';
8
- import { preflight } from './preflight.mjs';
9
- import { transform } from './transform.mjs';
10
- import {
11
- affix,
12
- dispatch,
13
- maxRetryAfterError,
14
- } from './utils.mjs';
15
-
16
- const {
17
- HTTP2_HEADER_RETRY_AFTER,
18
- } = http2.constants;
19
-
20
- export const transfer = async (options, overact) => {
21
- const { digest, redirected, thenable, url } = options;
22
-
23
- if (options.follow === 0) {
24
- throw new RequestError(`Maximum redirect reached at: ${ url.href }`);
25
- }
26
-
27
- if (url.protocol === 'https:') {
28
- options = !options.h2 ? await ackn(options) : {
29
- ...options,
30
- createConnection: null,
31
- protocol: url.protocol,
32
- };
33
- } else if (Reflect.has(options, 'alpnProtocol')) {
34
- for (const it of [
35
- 'alpnProtocol',
36
- 'createConnection',
37
- 'h2',
38
- 'protocol',
39
- ]) { Reflect.deleteProperty(options, it); }
40
- }
41
-
42
- try {
43
- options = await transform(preflight(options));
44
- } catch (ex) {
45
- options.createConnection?.().destroy();
46
- throw ex;
47
- }
48
-
49
- const promise = new Promise((resolve, reject) => {
50
- let client, req;
51
-
52
- if (options.h2) {
53
- client = http2.connect(url.origin, options);
54
- req = client.request(options.headers, options);
55
- } else {
56
- const { request } = url.protocol === 'http:' ? http : https;
57
-
58
- req = request(url, options);
59
- }
60
-
61
- affix(client, req, options);
62
-
63
- req.once('aborted', reject);
64
- req.once('error', reject);
65
- req.once('frameError', reject);
66
- req.once('goaway', reject);
67
- req.once('response', (res) => postflight(req, res, options, {
68
- reject,
69
- resolve,
70
- }));
71
-
72
- dispatch(options, req);
73
- });
74
-
75
- try {
76
- const res = await promise;
77
-
78
- if (digest && !redirected) {
79
- res.body = await res.body();
80
- }
81
-
82
- return res;
83
- } catch (ex) {
84
- const { maxRetryAfter, retry } = options;
85
-
86
- if (retry?.attempts > 0) {
87
- if (retry.errorCodes?.includes(ex.code) || retry.statusCodes?.includes(ex.statusCode)) {
88
- let { interval } = retry;
89
-
90
- if (retry.retryAfter && ex.headers?.[HTTP2_HEADER_RETRY_AFTER]) {
91
- interval = ex.headers[HTTP2_HEADER_RETRY_AFTER];
92
- interval = Number(interval) * 1e3 || new Date(interval) - Date.now();
93
- if (interval > maxRetryAfter) {
94
- throw maxRetryAfterError(interval, { cause: ex });
95
- }
96
- } else {
97
- interval = new Function('interval', `return Math.ceil(${ retry.backoffStrategy });`)(interval);
98
- }
99
-
100
- if (interval < 0) {
101
- interval = 0;
102
- }
103
-
104
- retry.attempts--;
105
- retry.interval = interval;
106
-
107
- return setTimeoutPromise(interval).then(() => overact(url, options));
108
- }
109
- }
110
-
111
- if (digest && !redirected && ex.body) {
112
- ex.body = await ex.body();
113
- }
114
-
115
- if (!thenable) {
116
- throw ex;
117
- } else {
118
- return ex;
119
- }
120
- }
121
- };