rekwest 6.2.0 → 7.0.0

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 (45) 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} +11 -9
  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.cjs +140 -0
  15. package/dist/{validation.js → validation.cjs} +1 -1
  16. package/package.json +15 -14
  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} +100 -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 +152 -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/dist/utils.js +0 -188
  37. package/src/postflight.mjs +0 -136
  38. package/src/transfer.mjs +0 -121
  39. package/src/transform.mjs +0 -82
  40. package/src/utils.mjs +0 -205
  41. /package/dist/{ackn.js → ackn.cjs} +0 -0
  42. /package/dist/{constants.js → constants.cjs} +0 -0
  43. /package/dist/{errors.js → errors.cjs} +0 -0
  44. /package/dist/{mediatypes.js → mediatypes.cjs} +0 -0
  45. /package/src/{errors.mjs → errors.js} +0 -0
@@ -0,0 +1,56 @@
1
+ import http2 from 'node:http2';
2
+ import { Cookies } from './cookies.js';
3
+ import { mixin } from './mixin.js';
4
+ import { redirects } from './redirects.js';
5
+ import { augment } from './utils.js';
6
+
7
+ const {
8
+ HTTP2_HEADER_SET_COOKIE,
9
+ HTTP_STATUS_BAD_REQUEST,
10
+ } = http2.constants;
11
+
12
+ export const postflight = (req, res, options, { reject, resolve }) => {
13
+ const { cookies, h2, url } = options;
14
+ let headers;
15
+
16
+ if (h2) {
17
+ headers = res;
18
+ res = req;
19
+ } else {
20
+ res.once('error', reject);
21
+ }
22
+
23
+ augment(res, headers, options);
24
+
25
+ if (cookies !== false && res.headers[HTTP2_HEADER_SET_COOKIE]) {
26
+ if (Cookies.jar.has(url.origin)) {
27
+ const cookie = new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options);
28
+
29
+ Cookies.jar.get(url.origin).forEach((val, key) => {
30
+ if (!cookie.has(key)) {
31
+ cookie.set(key, val);
32
+ }
33
+ });
34
+ Cookies.jar.set(url.origin, cookie);
35
+ } else {
36
+ Cookies.jar.set(url.origin, new Cookies(res.headers[HTTP2_HEADER_SET_COOKIE], options));
37
+ }
38
+ }
39
+
40
+ Reflect.defineProperty(res, 'cookies', {
41
+ enumerable: true,
42
+ value: cookies !== false && Cookies.jar.has(url.origin) ? Cookies.jar.get(url.origin) : void 0,
43
+ });
44
+
45
+ const willRedirect = redirects(res, options);
46
+
47
+ if (Object(willRedirect) === willRedirect) {
48
+ return willRedirect.then(resolve, reject);
49
+ }
50
+
51
+ if (res.statusCode >= HTTP_STATUS_BAD_REQUEST) {
52
+ return reject(mixin(res, options));
53
+ }
54
+
55
+ resolve(mixin(res, options));
56
+ };
@@ -1,91 +1,100 @@
1
- import http2 from 'node:http2';
2
- import { isZstdSupported } from './config.mjs';
3
- import { requestCredentials } from './constants.mjs';
4
- import { Cookies } from './cookies.mjs';
5
-
6
- const {
7
- HTTP2_HEADER_ACCEPT_ENCODING,
8
- HTTP2_HEADER_AUTHORITY,
9
- HTTP2_HEADER_AUTHORIZATION,
10
- HTTP2_HEADER_COOKIE,
11
- HTTP2_HEADER_METHOD,
12
- HTTP2_HEADER_PATH,
13
- HTTP2_HEADER_SCHEME,
14
- HTTP2_METHOD_GET,
15
- HTTP2_METHOD_HEAD,
16
- } = http2.constants;
17
-
18
- export const preflight = (options) => {
19
- const { cookies, credentials, h2, headers, method, url } = options;
20
-
21
- if (h2) {
22
- options.endStream = [
23
- HTTP2_METHOD_GET,
24
- HTTP2_METHOD_HEAD,
25
- ].includes(method);
26
- }
27
-
28
- if (cookies !== false && credentials !== requestCredentials.omit) {
29
- let cookie = Cookies.jar.has(url.origin);
30
-
31
- if (cookies === Object(cookies) && [
32
- requestCredentials.include,
33
- requestCredentials.sameOrigin,
34
- ].includes(credentials)) {
35
- if (cookie) {
36
- cookie = new Cookies(cookies, options);
37
-
38
- Cookies.jar.get(url.origin).forEach((val, key) => {
39
- if (!cookie.has(key)) {
40
- cookie.set(key, val);
41
- }
42
- });
43
- Cookies.jar.set(url.origin, cookie);
44
- } else {
45
- cookie = new Cookies(cookies, options);
46
- Cookies.jar.set(url.origin, cookie);
47
- }
48
- } else {
49
- cookie &&= Cookies.jar.get(url.origin);
50
- }
51
-
52
- options.headers = {
53
- ...cookie && { [HTTP2_HEADER_COOKIE]: cookie },
54
- ...headers,
55
- };
56
- }
57
-
58
- if (credentials === requestCredentials.omit) {
59
- options.cookies = false;
60
- for (const it of Object.keys(options.headers ?? {})
61
- .filter((val) => new RegExp(`^(${
62
- HTTP2_HEADER_AUTHORIZATION }|${ HTTP2_HEADER_COOKIE
63
- })$`, 'i').test(val))) { Reflect.deleteProperty(options.headers, it); }
64
-
65
- url.password = url.username = '';
66
- }
67
-
68
- options.headers = {
69
- ...Object.entries(options.headers ?? {})
70
- .reduce((acc, [key, val]) => {
71
- acc[key.toLowerCase()] = val;
72
-
73
- if (acc[HTTP2_HEADER_ACCEPT_ENCODING]?.match(/\bzstd\b/i) && !isZstdSupported) {
74
- acc[HTTP2_HEADER_ACCEPT_ENCODING] = val.replace(/\s?zstd,?/gi, '').trim();
75
- if (!acc[HTTP2_HEADER_ACCEPT_ENCODING]) {
76
- Reflect.deleteProperty(acc, HTTP2_HEADER_ACCEPT_ENCODING);
77
- }
78
- }
79
-
80
- return acc;
81
- }, {}),
82
- ...h2 && {
83
- [HTTP2_HEADER_AUTHORITY]: url.host,
84
- [HTTP2_HEADER_METHOD]: method,
85
- [HTTP2_HEADER_PATH]: `${ url.pathname }${ url.search }`,
86
- [HTTP2_HEADER_SCHEME]: url.protocol.replace(/\p{Punctuation}/gu, ''),
87
- },
88
- };
89
-
90
- return options;
91
- };
1
+ import http2 from 'node:http2';
2
+ import { isZstdSupported } from './config.js';
3
+ import { requestCredentials } from './constants.js';
4
+ import { Cookies } from './cookies.js';
5
+
6
+ const {
7
+ HTTP2_HEADER_ACCEPT_ENCODING,
8
+ HTTP2_HEADER_AUTHORITY,
9
+ HTTP2_HEADER_AUTHORIZATION,
10
+ HTTP2_HEADER_COOKIE,
11
+ HTTP2_HEADER_METHOD,
12
+ HTTP2_HEADER_PATH,
13
+ HTTP2_HEADER_SCHEME,
14
+ HTTP2_METHOD_GET,
15
+ HTTP2_METHOD_HEAD,
16
+ } = http2.constants;
17
+
18
+ export const preflight = (options) => {
19
+ const { cookies, credentials, h2, headers, method, url } = options;
20
+
21
+ if (h2) {
22
+ options.endStream = [
23
+ HTTP2_METHOD_GET,
24
+ HTTP2_METHOD_HEAD,
25
+ ].includes(method);
26
+ }
27
+
28
+ if (cookies !== false && credentials !== requestCredentials.omit) {
29
+ let cookie = Cookies.jar.has(url.origin);
30
+
31
+ if (Object(cookies) === cookies && [
32
+ requestCredentials.include,
33
+ requestCredentials.sameOrigin,
34
+ ].includes(credentials)) {
35
+ if (cookie) {
36
+ cookie = new Cookies(cookies, options);
37
+
38
+ Cookies.jar.get(url.origin).forEach((val, key) => {
39
+ if (!cookie.has(key)) {
40
+ cookie.set(key, val);
41
+ }
42
+ });
43
+ Cookies.jar.set(url.origin, cookie);
44
+ } else {
45
+ cookie = new Cookies(cookies, options);
46
+ Cookies.jar.set(url.origin, cookie);
47
+ }
48
+ } else {
49
+ cookie &&= Cookies.jar.get(url.origin);
50
+ }
51
+
52
+ options.headers = {
53
+ ...cookie && { [HTTP2_HEADER_COOKIE]: cookie },
54
+ ...headers,
55
+ };
56
+ }
57
+
58
+ if (credentials === requestCredentials.omit) {
59
+ [
60
+ HTTP2_HEADER_AUTHORIZATION,
61
+ HTTP2_HEADER_COOKIE,
62
+ ].forEach((key) => Reflect.deleteProperty(options.headers, key));
63
+ options.cookies = false;
64
+ url.password = url.username = '';
65
+ }
66
+
67
+ if (!h2) {
68
+ [
69
+ HTTP2_HEADER_AUTHORITY,
70
+ HTTP2_HEADER_METHOD,
71
+ HTTP2_HEADER_PATH,
72
+ HTTP2_HEADER_SCHEME,
73
+ ].forEach((key) => Reflect.deleteProperty(options.headers, key));
74
+ }
75
+
76
+ options.headers = {
77
+ ...Object.entries(options.headers ?? {})
78
+ .reduce((acc, [key, val]) => {
79
+ acc[key.toLowerCase()] = val;
80
+ const rex = /\s?zstd,?/gi;
81
+
82
+ if (acc[HTTP2_HEADER_ACCEPT_ENCODING]?.match(rex) && !isZstdSupported) {
83
+ acc[HTTP2_HEADER_ACCEPT_ENCODING] = val.replace(rex, '').trim();
84
+ if (!acc[HTTP2_HEADER_ACCEPT_ENCODING]) {
85
+ Reflect.deleteProperty(acc, HTTP2_HEADER_ACCEPT_ENCODING);
86
+ }
87
+ }
88
+
89
+ return acc;
90
+ }, {}),
91
+ ...h2 && {
92
+ [HTTP2_HEADER_AUTHORITY]: url.host,
93
+ [HTTP2_HEADER_METHOD]: method,
94
+ [HTTP2_HEADER_PATH]: `${ url.pathname }${ url.search }`,
95
+ [HTTP2_HEADER_SCHEME]: url.protocol.replace(/\p{Punctuation}/gu, ''),
96
+ },
97
+ };
98
+
99
+ return options;
100
+ };
@@ -0,0 +1,79 @@
1
+ import http2 from 'node:http2';
2
+ import { isReadable } from 'node:stream';
3
+ import {
4
+ requestCredentials,
5
+ requestRedirect,
6
+ requestRedirectCodes,
7
+ } from './constants.js';
8
+ import { RequestError } from './errors.js';
9
+ import rekwest from './index.js';
10
+ import {
11
+ isPipeStream,
12
+ sameOrigin,
13
+ } from './utils.js';
14
+
15
+ const {
16
+ HTTP2_HEADER_LOCATION,
17
+ HTTP2_METHOD_GET,
18
+ HTTP2_METHOD_HEAD,
19
+ HTTP2_METHOD_POST,
20
+ HTTP_STATUS_FOUND,
21
+ HTTP_STATUS_MOVED_PERMANENTLY,
22
+ HTTP_STATUS_PERMANENT_REDIRECT,
23
+ HTTP_STATUS_SEE_OTHER,
24
+ HTTP_STATUS_TEMPORARY_REDIRECT,
25
+ } = http2.constants;
26
+
27
+ export const redirects = (res, options) => {
28
+ const { credentials, follow, redirect, url } = options;
29
+
30
+ if (follow && /3\d{2}/.test(res.statusCode) && res.headers[HTTP2_HEADER_LOCATION]) {
31
+ if (!requestRedirectCodes.includes(res.statusCode)) {
32
+ return res.emit('error', new RangeError(`Invalid status code: ${ res.statusCode }`));
33
+ }
34
+
35
+ if (redirect === requestRedirect.error) {
36
+ return res.emit('error', new RequestError(`Unexpected redirect, redirect mode is set to '${ redirect }'.`));
37
+ }
38
+
39
+ if (redirect === requestRedirect.follow) {
40
+ const location = new URL(res.headers[HTTP2_HEADER_LOCATION], url);
41
+
42
+ if (!/^https?:/i.test(location.protocol)) {
43
+ return res.emit('error', new RequestError('URL scheme must be "http" or "https".'));
44
+ }
45
+
46
+ if (!sameOrigin(location, url)) {
47
+ if (credentials !== requestCredentials.include) {
48
+ options.credentials = requestCredentials.omit;
49
+ }
50
+
51
+ options.h2 = false;
52
+ }
53
+
54
+ if ([
55
+ HTTP_STATUS_PERMANENT_REDIRECT,
56
+ HTTP_STATUS_TEMPORARY_REDIRECT,
57
+ ].includes(res.statusCode) && isPipeStream(options.body) && !isReadable(options.body)) {
58
+ return res.emit('error', new RequestError(`Unable to ${ redirect } redirect with streamable body.`));
59
+ }
60
+
61
+ if (([
62
+ HTTP_STATUS_MOVED_PERMANENTLY,
63
+ HTTP_STATUS_FOUND,
64
+ ].includes(res.statusCode) && options.method === HTTP2_METHOD_POST)
65
+ || (res.statusCode === HTTP_STATUS_SEE_OTHER && ![
66
+ HTTP2_METHOD_GET,
67
+ HTTP2_METHOD_HEAD,
68
+ ].includes(options.method))) {
69
+ options.body = null;
70
+ options.method = HTTP2_METHOD_GET;
71
+ }
72
+
73
+ options.follow--;
74
+ options.redirected = true;
75
+
76
+ return rekwest(location, options);
77
+ }
78
+ }
79
+ };
package/src/retries.js ADDED
@@ -0,0 +1,51 @@
1
+ import http2 from 'node:http2';
2
+ import { isReadable } from 'node:stream';
3
+ import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
4
+ import { RequestError } from './errors.js';
5
+ import rekwest from './index.js';
6
+ import { isPipeStream } from './utils.js';
7
+
8
+ const {
9
+ HTTP2_HEADER_RETRY_AFTER,
10
+ HTTP2_METHOD_GET,
11
+ HTTP2_METHOD_HEAD,
12
+ } = http2.constants;
13
+
14
+ export const retries = (ex, options) => {
15
+ const { body, maxRetryAfter, method, retry, url } = options;
16
+
17
+ if (retry?.attempts > 0) {
18
+ if (![
19
+ HTTP2_METHOD_GET,
20
+ HTTP2_METHOD_HEAD,
21
+ ].includes(method) && isPipeStream(body) && !isReadable(body)) {
22
+ throw new RequestError('Request stream already read.', { cause: ex });
23
+ }
24
+
25
+ if (retry.errorCodes?.includes(ex.code) || retry.statusCodes?.includes(ex.statusCode)) {
26
+ let { interval } = retry;
27
+
28
+ if (retry.retryAfter && ex.headers?.[HTTP2_HEADER_RETRY_AFTER]) {
29
+ interval = ex.headers[HTTP2_HEADER_RETRY_AFTER];
30
+ interval = Math.abs(Number(interval) * 1e3 || new Date(interval) - Date.now()) || 0;
31
+ if (interval > maxRetryAfter) {
32
+ throw new RequestError(
33
+ `Maximum '${ HTTP2_HEADER_RETRY_AFTER }' limit exceeded: ${ interval } ms.`,
34
+ { cause: ex },
35
+ );
36
+ }
37
+ } else {
38
+ interval = new Function('interval', `return Math.ceil(${ retry.backoffStrategy });`)(interval);
39
+ }
40
+
41
+ if (interval < 0) {
42
+ interval = 0;
43
+ }
44
+
45
+ retry.attempts--;
46
+ retry.interval = interval;
47
+
48
+ return setTimeoutPromise(interval).then(() => rekwest(url, options));
49
+ }
50
+ }
51
+ };
@@ -0,0 +1,92 @@
1
+ import http from 'node:http';
2
+ import http2 from 'node:http2';
3
+ import https from 'node:https';
4
+ import { ackn } from './ackn.js';
5
+ import { RequestError } from './errors.js';
6
+ import { postflight } from './postflight.js';
7
+ import { preflight } from './preflight.js';
8
+ import { retries } from './retries.js';
9
+ import { transform } from './transform.js';
10
+ import {
11
+ dispatch,
12
+ snoop,
13
+ } from './utils.js';
14
+
15
+ export const transfer = async (options) => {
16
+ const { digest, redirected, thenable, url } = options;
17
+
18
+ if (options.follow === 0) {
19
+ throw new RequestError(`Maximum redirect reached at: ${ url.href }`);
20
+ }
21
+
22
+ if (url.protocol === 'https:') {
23
+ options = !options.h2 ? await ackn(options) : {
24
+ ...options, createConnection: null, protocol: url.protocol,
25
+ };
26
+ } else if (Reflect.has(options, 'alpnProtocol')) {
27
+ for (const it of [
28
+ 'alpnProtocol',
29
+ 'createConnection',
30
+ 'h2',
31
+ 'protocol',
32
+ ]) { Reflect.deleteProperty(options, it); }
33
+ }
34
+
35
+ try {
36
+ options = await transform(preflight(options));
37
+ } catch (ex) {
38
+ options.createConnection?.().destroy();
39
+ throw ex;
40
+ }
41
+
42
+ const promise = new Promise((resolve, reject) => {
43
+ let client, req;
44
+
45
+ if (options.h2) {
46
+ client = http2.connect(url.origin, options);
47
+ req = client.request(options.headers, options);
48
+ } else {
49
+ const { request } = url.protocol === 'http:' ? http : https;
50
+
51
+ req = request(url, options);
52
+ }
53
+
54
+ snoop(client, req, options);
55
+
56
+ req.once('aborted', reject);
57
+ req.once('error', reject);
58
+ req.once('frameError', reject);
59
+ req.once('goaway', reject);
60
+ req.once('response', (res) => postflight(req, res, options, {
61
+ reject, resolve,
62
+ }));
63
+
64
+ dispatch(req, options);
65
+ });
66
+
67
+ try {
68
+ const res = await promise;
69
+
70
+ if (digest && !redirected) {
71
+ res.body = await res.body();
72
+ }
73
+
74
+ return res;
75
+ } catch (ex) {
76
+ const willRetry = retries(ex, options);
77
+
78
+ if (willRetry) {
79
+ return willRetry;
80
+ }
81
+
82
+ if (digest && !redirected && ex.body) {
83
+ ex.body = await ex.body();
84
+ }
85
+
86
+ if (!thenable) {
87
+ throw ex;
88
+ } else {
89
+ return ex;
90
+ }
91
+ }
92
+ };
@@ -0,0 +1,109 @@
1
+ import http2 from 'node:http2';
2
+ import {
3
+ isReadable,
4
+ Readable,
5
+ } from 'node:stream';
6
+ import { buffer } from 'node:stream/consumers';
7
+ import { types } from 'node:util';
8
+ import { encode } from './codecs.js';
9
+ import { FormData } from './formdata.js';
10
+ import {
11
+ APPLICATION_FORM_URLENCODED,
12
+ APPLICATION_JSON,
13
+ APPLICATION_OCTET_STREAM,
14
+ } from './mediatypes.js';
15
+ import {
16
+ isFileLike,
17
+ isReadableStream,
18
+ } from './utils.js';
19
+
20
+ const {
21
+ HTTP2_HEADER_CONTENT_ENCODING,
22
+ HTTP2_HEADER_CONTENT_LENGTH,
23
+ HTTP2_HEADER_CONTENT_TYPE,
24
+ } = http2.constants;
25
+
26
+ export const transform = async (options) => {
27
+ let { body, headers } = options;
28
+
29
+ if (!body) {
30
+ return options;
31
+ }
32
+
33
+ if (!Buffer.isBuffer(body)) {
34
+ switch (true) {
35
+ case isFileLike(body): {
36
+ headers = {
37
+ [HTTP2_HEADER_CONTENT_LENGTH]: body.size,
38
+ [HTTP2_HEADER_CONTENT_TYPE]: body.type || APPLICATION_OCTET_STREAM,
39
+ };
40
+ body = body.stream();
41
+ break;
42
+ }
43
+
44
+ case FormData.alike(body): {
45
+ body = FormData.actuate(body);
46
+ headers = { [HTTP2_HEADER_CONTENT_TYPE]: body.contentType };
47
+ break;
48
+ }
49
+
50
+ case types.isAnyArrayBuffer(body): {
51
+ body = Buffer.from(body);
52
+ break;
53
+ }
54
+
55
+ case types.isArrayBufferView(body): {
56
+ body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
57
+ break;
58
+ }
59
+
60
+ case Object(body) === body && !Reflect.has(body, Symbol.asyncIterator): {
61
+ if (body.constructor === URLSearchParams) {
62
+ headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_FORM_URLENCODED };
63
+ body = body.toString();
64
+ } else if (!(!Array.isArray(body) && Reflect.has(body, Symbol.iterator))) {
65
+ headers = { [HTTP2_HEADER_CONTENT_TYPE]: APPLICATION_JSON };
66
+ body = JSON.stringify(body);
67
+ }
68
+
69
+ break;
70
+ }
71
+
72
+ default:
73
+ break;
74
+ }
75
+ }
76
+
77
+ const encodings = options.headers[HTTP2_HEADER_CONTENT_ENCODING];
78
+
79
+ if (Object(body) === body
80
+ && (Reflect.has(body, Symbol.asyncIterator) || (!Array.isArray(body) && Reflect.has(body, Symbol.iterator)))) {
81
+ body = isReadable(body) ? (isReadableStream(body) ? Readable.fromWeb(body) : body) : Readable.from(body);
82
+ body = encodings ? encode(body, encodings, options) : body;
83
+ } else if (encodings) {
84
+ body = await buffer(encode(Readable.from(body), encodings, options));
85
+ }
86
+
87
+ if (options.bufferBody && Object(body) === body) {
88
+ if (isReadable(body)) {
89
+ body = await buffer(body);
90
+ } else if (Reflect.has(body, Symbol.asyncIterator)) {
91
+ body = await buffer(body);
92
+ }
93
+ }
94
+
95
+ Object.assign(options.headers, {
96
+ ...headers,
97
+ ...!body[Symbol.asyncIterator] && {
98
+ [HTTP2_HEADER_CONTENT_LENGTH]: Buffer.byteLength(body),
99
+ },
100
+ ...options.headers[HTTP2_HEADER_CONTENT_TYPE] && {
101
+ [HTTP2_HEADER_CONTENT_TYPE]: options.headers[HTTP2_HEADER_CONTENT_TYPE],
102
+ },
103
+ });
104
+
105
+ return {
106
+ ...options,
107
+ body,
108
+ };
109
+ };