rezo 1.0.12 → 1.0.14
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/dist/adapters/curl.cjs +89 -22
- package/dist/adapters/curl.js +89 -22
- package/dist/adapters/entries/curl.d.ts +8 -0
- package/dist/adapters/entries/fetch.d.ts +8 -0
- package/dist/adapters/entries/http.d.ts +8 -0
- package/dist/adapters/entries/http2.d.ts +8 -0
- package/dist/adapters/entries/react-native.d.ts +8 -0
- package/dist/adapters/entries/xhr.d.ts +8 -0
- package/dist/adapters/fetch.cjs +128 -58
- package/dist/adapters/fetch.js +128 -58
- package/dist/adapters/http.cjs +43 -13
- package/dist/adapters/http.js +43 -13
- package/dist/adapters/http2.cjs +136 -58
- package/dist/adapters/http2.js +136 -58
- package/dist/adapters/index.cjs +6 -6
- package/dist/cache/index.cjs +13 -13
- package/dist/core/rezo.cjs +27 -6
- package/dist/core/rezo.js +27 -6
- package/dist/crawler.d.ts +8 -0
- package/dist/entries/crawler.cjs +5 -5
- package/dist/index.cjs +24 -24
- package/dist/index.d.ts +8 -0
- package/dist/platform/browser.d.ts +8 -0
- package/dist/platform/bun.d.ts +8 -0
- package/dist/platform/deno.d.ts +8 -0
- package/dist/platform/node.d.ts +8 -0
- package/dist/platform/react-native.d.ts +8 -0
- package/dist/platform/worker.d.ts +8 -0
- package/dist/plugin/index.cjs +36 -36
- package/dist/proxy/index.cjs +2 -2
- package/dist/queue/index.cjs +8 -8
- package/dist/responses/buildError.cjs +5 -1
- package/dist/responses/buildError.js +5 -1
- package/dist/responses/buildResponse.cjs +2 -0
- package/dist/responses/buildResponse.js +2 -0
- package/dist/utils/compression.cjs +6 -6
- package/dist/utils/compression.js +6 -6
- package/dist/utils/form-data.cjs +64 -7
- package/dist/utils/form-data.js +64 -7
- package/dist/utils/headers.cjs +17 -0
- package/dist/utils/headers.js +17 -1
- package/dist/utils/http-config.cjs +39 -4
- package/dist/utils/http-config.js +39 -4
- package/package.json +1 -1
package/dist/adapters/fetch.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { URL } = require("node:url");
|
|
2
2
|
const { RezoError } = require('../errors/rezo-error.cjs');
|
|
3
3
|
const { buildSmartError, builErrorFromResponse, buildDownloadError } = require('../responses/buildError.cjs');
|
|
4
|
-
const {
|
|
4
|
+
const { RezoCookieJar } = require('../utils/cookies.cjs');
|
|
5
5
|
const RezoFormData = require('../utils/form-data.cjs');
|
|
6
6
|
const { getDefaultConfig, prepareHTTPOptions } = require('../utils/http-config.cjs');
|
|
7
7
|
const { RezoHeaders } = require('../utils/headers.cjs');
|
|
@@ -105,39 +105,69 @@ function sanitizeConfig(config) {
|
|
|
105
105
|
delete sanitized.data;
|
|
106
106
|
return sanitized;
|
|
107
107
|
}
|
|
108
|
-
function parseCookiesFromHeaders(headers, url) {
|
|
109
|
-
|
|
108
|
+
function parseCookiesFromHeaders(headers, url, config) {
|
|
109
|
+
let setCookieHeaders = [];
|
|
110
|
+
if (typeof headers.getSetCookie === "function") {
|
|
111
|
+
setCookieHeaders = headers.getSetCookie() || [];
|
|
112
|
+
} else {
|
|
113
|
+
const setCookieRaw = headers.get("set-cookie");
|
|
114
|
+
if (setCookieRaw) {
|
|
115
|
+
const splitPattern = /,(?=\s*[A-Za-z0-9_-]+=)/;
|
|
116
|
+
setCookieHeaders = setCookieRaw.split(splitPattern).map((s) => s.trim()).filter(Boolean);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (setCookieHeaders.length === 0) {
|
|
120
|
+
return {
|
|
121
|
+
array: [],
|
|
122
|
+
serialized: [],
|
|
123
|
+
netscape: "",
|
|
124
|
+
string: "",
|
|
125
|
+
setCookiesString: []
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const jar = new RezoCookieJar;
|
|
129
|
+
jar.setCookiesSync(setCookieHeaders, url);
|
|
130
|
+
if (config?.enableCookieJar && config?.cookieJar) {
|
|
131
|
+
config.cookieJar.setCookiesSync(setCookieHeaders, url);
|
|
132
|
+
}
|
|
133
|
+
const cookies = jar.cookies();
|
|
134
|
+
cookies.setCookiesString = setCookieHeaders;
|
|
135
|
+
return cookies;
|
|
136
|
+
}
|
|
137
|
+
function mergeRequestAndResponseCookies(config, responseCookies, url) {
|
|
138
|
+
const mergedCookiesArray = [];
|
|
139
|
+
const cookieKeyDomainMap = new Map;
|
|
140
|
+
if (config.requestCookies && config.requestCookies.length > 0) {
|
|
141
|
+
for (const cookie of config.requestCookies) {
|
|
142
|
+
const key = `${cookie.key}|${cookie.domain || ""}`;
|
|
143
|
+
mergedCookiesArray.push(cookie);
|
|
144
|
+
cookieKeyDomainMap.set(key, mergedCookiesArray.length - 1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
for (const cookie of responseCookies.array) {
|
|
148
|
+
const key = `${cookie.key}|${cookie.domain || ""}`;
|
|
149
|
+
const existingIndex = cookieKeyDomainMap.get(key);
|
|
150
|
+
if (existingIndex !== undefined) {
|
|
151
|
+
mergedCookiesArray[existingIndex] = cookie;
|
|
152
|
+
} else {
|
|
153
|
+
mergedCookiesArray.push(cookie);
|
|
154
|
+
cookieKeyDomainMap.set(key, mergedCookiesArray.length - 1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (mergedCookiesArray.length > 0) {
|
|
158
|
+
const mergedJar = new RezoCookieJar(mergedCookiesArray, url);
|
|
159
|
+
return mergedJar.cookies();
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
110
162
|
array: [],
|
|
111
163
|
serialized: [],
|
|
112
|
-
netscape:
|
|
164
|
+
netscape: `# Netscape HTTP Cookie File
|
|
165
|
+
# This file was generated by Rezo HTTP client
|
|
166
|
+
# Based on uniqhtt cookie implementation
|
|
167
|
+
`,
|
|
113
168
|
string: "",
|
|
114
169
|
setCookiesString: []
|
|
115
170
|
};
|
|
116
|
-
const setCookieHeaders = headers.getSetCookie?.() || [];
|
|
117
|
-
for (const cookieStr of setCookieHeaders) {
|
|
118
|
-
cookies.setCookiesString.push(cookieStr);
|
|
119
|
-
const parts = cookieStr.split(";");
|
|
120
|
-
const [nameValue] = parts;
|
|
121
|
-
const [name, ...valueParts] = nameValue.split("=");
|
|
122
|
-
const value = valueParts.join("=");
|
|
123
|
-
if (name && value !== undefined) {
|
|
124
|
-
const cookie = new Cookie({
|
|
125
|
-
key: name.trim(),
|
|
126
|
-
value: value.trim(),
|
|
127
|
-
domain: new URL(url).hostname,
|
|
128
|
-
path: "/",
|
|
129
|
-
httpOnly: cookieStr.toLowerCase().includes("httponly"),
|
|
130
|
-
secure: cookieStr.toLowerCase().includes("secure"),
|
|
131
|
-
sameSite: "lax"
|
|
132
|
-
});
|
|
133
|
-
cookies.array.push(cookie);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
cookies.string = cookies.array.map((c) => `${c.key}=${c.value}`).join("; ");
|
|
137
|
-
cookies.serialized = cookies.array.map((c) => c.toJSON());
|
|
138
|
-
cookies.netscape = cookies.array.map((c) => c.toNetscapeFormat()).join(`
|
|
139
|
-
`);
|
|
140
|
-
return cookies;
|
|
141
171
|
}
|
|
142
172
|
function toFetchHeaders(headers) {
|
|
143
173
|
const fetchHeaders = new Headers;
|
|
@@ -181,7 +211,7 @@ async function prepareFetchBody(body) {
|
|
|
181
211
|
return new Uint8Array(buffer);
|
|
182
212
|
}
|
|
183
213
|
}
|
|
184
|
-
const nativeForm = body.toNativeFormData();
|
|
214
|
+
const nativeForm = await body.toNativeFormData();
|
|
185
215
|
if (nativeForm) {
|
|
186
216
|
return nativeForm;
|
|
187
217
|
}
|
|
@@ -306,6 +336,8 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
|
|
|
306
336
|
const response = await executeSingleFetchRequest(config, fetchOptions, requestCount, timing, _stats, streamResult, downloadResult, uploadResult);
|
|
307
337
|
const statusOnNext = _stats.statusOnNext;
|
|
308
338
|
if (response instanceof RezoError) {
|
|
339
|
+
if (!config.errors)
|
|
340
|
+
config.errors = [];
|
|
309
341
|
config.errors.push({
|
|
310
342
|
attempt: config.retryAttempts + 1,
|
|
311
343
|
error: response,
|
|
@@ -316,30 +348,50 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
|
|
|
316
348
|
throw response;
|
|
317
349
|
}
|
|
318
350
|
if (config.retry) {
|
|
351
|
+
let shouldRetry = false;
|
|
319
352
|
if (config.retry.condition) {
|
|
320
353
|
const isPassed = await config.retry.condition(response);
|
|
321
|
-
|
|
322
|
-
throw response;
|
|
323
|
-
}
|
|
354
|
+
shouldRetry = isPassed === true;
|
|
324
355
|
} else {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
throw response;
|
|
330
|
-
}
|
|
331
|
-
retries++;
|
|
332
|
-
if (retryDelay > 0) {
|
|
333
|
-
await new Promise((resolve) => setTimeout(resolve, incrementDelay ? retryDelay * retries : retryDelay));
|
|
334
|
-
}
|
|
356
|
+
shouldRetry = statusCodes ? statusCodes.includes(response.status || 0) : true;
|
|
357
|
+
}
|
|
358
|
+
if (!shouldRetry || retries >= maxRetries) {
|
|
359
|
+
throw response;
|
|
335
360
|
}
|
|
361
|
+
retries++;
|
|
336
362
|
config.retryAttempts++;
|
|
363
|
+
if (retryDelay > 0) {
|
|
364
|
+
await new Promise((resolve) => setTimeout(resolve, incrementDelay ? retryDelay * retries : retryDelay));
|
|
365
|
+
}
|
|
337
366
|
}
|
|
338
367
|
continue;
|
|
339
368
|
}
|
|
340
369
|
if (statusOnNext === "success") {
|
|
341
370
|
return response;
|
|
342
371
|
}
|
|
372
|
+
if (statusOnNext === "error") {
|
|
373
|
+
const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, fetchOptions);
|
|
374
|
+
if (config.retry && statusCodes?.includes(response.status)) {
|
|
375
|
+
if (maxRetries > retries) {
|
|
376
|
+
retries++;
|
|
377
|
+
config.retryAttempts++;
|
|
378
|
+
config.errors.push({
|
|
379
|
+
attempt: config.retryAttempts,
|
|
380
|
+
error: httpError,
|
|
381
|
+
duration: perform.now()
|
|
382
|
+
});
|
|
383
|
+
perform.reset();
|
|
384
|
+
if (config.debug) {
|
|
385
|
+
console.log(`Request failed with status code ${response.status}, retrying...${retryDelay > 0 ? " in " + (incrementDelay ? retryDelay * retries : retryDelay) + "ms" : ""}`);
|
|
386
|
+
}
|
|
387
|
+
if (retryDelay > 0) {
|
|
388
|
+
await new Promise((resolve) => setTimeout(resolve, incrementDelay ? retryDelay * retries : retryDelay));
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
throw httpError;
|
|
394
|
+
}
|
|
343
395
|
if (statusOnNext === "redirect") {
|
|
344
396
|
const location = _stats.redirectUrl;
|
|
345
397
|
if (!location) {
|
|
@@ -418,7 +470,13 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
418
470
|
config.isSecure = isSecure;
|
|
419
471
|
config.finalUrl = url.href;
|
|
420
472
|
config.network.protocol = isSecure ? "https" : "http";
|
|
473
|
+
config.network.httpVersion = undefined;
|
|
421
474
|
config.timing.startTimestamp = timing.startTimestamp;
|
|
475
|
+
if (!config.transfer) {
|
|
476
|
+
config.transfer = { requestSize: 0, responseSize: 0, headerSize: 0, bodySize: 0 };
|
|
477
|
+
} else if (config.transfer.requestSize === undefined) {
|
|
478
|
+
config.transfer.requestSize = 0;
|
|
479
|
+
}
|
|
422
480
|
}
|
|
423
481
|
const reqHeaders = fetchOptions.headers instanceof RezoHeaders ? fetchOptions.headers.toObject() : fetchOptions.headers || {};
|
|
424
482
|
const headers = toFetchHeaders(reqHeaders);
|
|
@@ -448,7 +506,26 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
448
506
|
clearTimeout(timeoutId);
|
|
449
507
|
});
|
|
450
508
|
}
|
|
509
|
+
if (body instanceof RezoFormData) {
|
|
510
|
+
const contentType = body.getContentType();
|
|
511
|
+
if (contentType && !headers.has("content-type")) {
|
|
512
|
+
headers.set("content-type", contentType);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
451
515
|
const preparedBody = await prepareFetchBody(body);
|
|
516
|
+
if (config.transfer && body) {
|
|
517
|
+
if (typeof body === "string") {
|
|
518
|
+
config.transfer.requestSize = body.length;
|
|
519
|
+
} else if (body instanceof ArrayBuffer || body instanceof Uint8Array) {
|
|
520
|
+
config.transfer.requestSize = body.byteLength || body.length;
|
|
521
|
+
} else if (body instanceof URLSearchParams || body instanceof RezoURLSearchParams) {
|
|
522
|
+
config.transfer.requestSize = body.toString().length;
|
|
523
|
+
} else if (body instanceof RezoFormData) {
|
|
524
|
+
config.transfer.requestSize = body.getLengthSync();
|
|
525
|
+
} else if (typeof body === "object" && !(body instanceof Blob) && !(body instanceof ReadableStream)) {
|
|
526
|
+
config.transfer.requestSize = JSON.stringify(body).length;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
452
529
|
const fetchInit = {
|
|
453
530
|
method: fetchOptions.method.toUpperCase(),
|
|
454
531
|
headers,
|
|
@@ -482,7 +559,7 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
482
559
|
const responseHeaders = fromFetchHeaders(response.headers);
|
|
483
560
|
const contentType = response.headers.get("content-type") || "";
|
|
484
561
|
const contentLength = response.headers.get("content-length");
|
|
485
|
-
const cookies = parseCookiesFromHeaders(response.headers, url.href);
|
|
562
|
+
const cookies = parseCookiesFromHeaders(response.headers, url.href, config);
|
|
486
563
|
config.responseCookies = cookies;
|
|
487
564
|
const location = response.headers.get("location");
|
|
488
565
|
const isRedirect = status >= 300 && status < 400 && location;
|
|
@@ -564,23 +641,16 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
564
641
|
const bodySize = bodyBuffer?.byteLength || (typeof responseData === "string" ? responseData.length : 0);
|
|
565
642
|
config.transfer.bodySize = bodySize;
|
|
566
643
|
config.transfer.responseSize = bodySize;
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
headers: responseHeaders,
|
|
572
|
-
data: responseData
|
|
573
|
-
}, config, fetchOptions);
|
|
574
|
-
_stats.statusOnNext = "error";
|
|
575
|
-
return error;
|
|
576
|
-
}
|
|
577
|
-
_stats.statusOnNext = "success";
|
|
644
|
+
config.status = status;
|
|
645
|
+
config.statusText = statusText;
|
|
646
|
+
_stats.statusOnNext = status >= 400 ? "error" : "success";
|
|
647
|
+
const mergedCookies = mergeRequestAndResponseCookies(config, cookies, url.href);
|
|
578
648
|
const finalResponse = {
|
|
579
649
|
data: responseData,
|
|
580
650
|
status,
|
|
581
651
|
statusText,
|
|
582
652
|
headers: responseHeaders,
|
|
583
|
-
cookies,
|
|
653
|
+
cookies: mergedCookies,
|
|
584
654
|
config,
|
|
585
655
|
contentType,
|
|
586
656
|
contentLength: bodySize,
|
|
@@ -599,7 +669,7 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
599
669
|
contentType,
|
|
600
670
|
contentLength: buffer.length,
|
|
601
671
|
finalUrl: url.href,
|
|
602
|
-
cookies,
|
|
672
|
+
cookies: mergedCookies,
|
|
603
673
|
urls: buildUrlTree(config, url.href),
|
|
604
674
|
fileName: config.fileName,
|
|
605
675
|
fileSize: buffer.length,
|
|
@@ -646,7 +716,7 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
646
716
|
contentLength: bodySize
|
|
647
717
|
},
|
|
648
718
|
finalUrl: url.href,
|
|
649
|
-
cookies,
|
|
719
|
+
cookies: mergedCookies,
|
|
650
720
|
urls: buildUrlTree(config, url.href),
|
|
651
721
|
uploadSize: config.transfer.requestSize || 0,
|
|
652
722
|
timing: {
|
package/dist/adapters/fetch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { URL } from "node:url";
|
|
2
2
|
import { RezoError } from '../errors/rezo-error.js';
|
|
3
3
|
import { buildSmartError, builErrorFromResponse, buildDownloadError } from '../responses/buildError.js';
|
|
4
|
-
import {
|
|
4
|
+
import { RezoCookieJar } from '../utils/cookies.js';
|
|
5
5
|
import RezoFormData from '../utils/form-data.js';
|
|
6
6
|
import { getDefaultConfig, prepareHTTPOptions } from '../utils/http-config.js';
|
|
7
7
|
import { RezoHeaders } from '../utils/headers.js';
|
|
@@ -105,39 +105,69 @@ function sanitizeConfig(config) {
|
|
|
105
105
|
delete sanitized.data;
|
|
106
106
|
return sanitized;
|
|
107
107
|
}
|
|
108
|
-
function parseCookiesFromHeaders(headers, url) {
|
|
109
|
-
|
|
108
|
+
function parseCookiesFromHeaders(headers, url, config) {
|
|
109
|
+
let setCookieHeaders = [];
|
|
110
|
+
if (typeof headers.getSetCookie === "function") {
|
|
111
|
+
setCookieHeaders = headers.getSetCookie() || [];
|
|
112
|
+
} else {
|
|
113
|
+
const setCookieRaw = headers.get("set-cookie");
|
|
114
|
+
if (setCookieRaw) {
|
|
115
|
+
const splitPattern = /,(?=\s*[A-Za-z0-9_-]+=)/;
|
|
116
|
+
setCookieHeaders = setCookieRaw.split(splitPattern).map((s) => s.trim()).filter(Boolean);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (setCookieHeaders.length === 0) {
|
|
120
|
+
return {
|
|
121
|
+
array: [],
|
|
122
|
+
serialized: [],
|
|
123
|
+
netscape: "",
|
|
124
|
+
string: "",
|
|
125
|
+
setCookiesString: []
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const jar = new RezoCookieJar;
|
|
129
|
+
jar.setCookiesSync(setCookieHeaders, url);
|
|
130
|
+
if (config?.enableCookieJar && config?.cookieJar) {
|
|
131
|
+
config.cookieJar.setCookiesSync(setCookieHeaders, url);
|
|
132
|
+
}
|
|
133
|
+
const cookies = jar.cookies();
|
|
134
|
+
cookies.setCookiesString = setCookieHeaders;
|
|
135
|
+
return cookies;
|
|
136
|
+
}
|
|
137
|
+
function mergeRequestAndResponseCookies(config, responseCookies, url) {
|
|
138
|
+
const mergedCookiesArray = [];
|
|
139
|
+
const cookieKeyDomainMap = new Map;
|
|
140
|
+
if (config.requestCookies && config.requestCookies.length > 0) {
|
|
141
|
+
for (const cookie of config.requestCookies) {
|
|
142
|
+
const key = `${cookie.key}|${cookie.domain || ""}`;
|
|
143
|
+
mergedCookiesArray.push(cookie);
|
|
144
|
+
cookieKeyDomainMap.set(key, mergedCookiesArray.length - 1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
for (const cookie of responseCookies.array) {
|
|
148
|
+
const key = `${cookie.key}|${cookie.domain || ""}`;
|
|
149
|
+
const existingIndex = cookieKeyDomainMap.get(key);
|
|
150
|
+
if (existingIndex !== undefined) {
|
|
151
|
+
mergedCookiesArray[existingIndex] = cookie;
|
|
152
|
+
} else {
|
|
153
|
+
mergedCookiesArray.push(cookie);
|
|
154
|
+
cookieKeyDomainMap.set(key, mergedCookiesArray.length - 1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (mergedCookiesArray.length > 0) {
|
|
158
|
+
const mergedJar = new RezoCookieJar(mergedCookiesArray, url);
|
|
159
|
+
return mergedJar.cookies();
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
110
162
|
array: [],
|
|
111
163
|
serialized: [],
|
|
112
|
-
netscape:
|
|
164
|
+
netscape: `# Netscape HTTP Cookie File
|
|
165
|
+
# This file was generated by Rezo HTTP client
|
|
166
|
+
# Based on uniqhtt cookie implementation
|
|
167
|
+
`,
|
|
113
168
|
string: "",
|
|
114
169
|
setCookiesString: []
|
|
115
170
|
};
|
|
116
|
-
const setCookieHeaders = headers.getSetCookie?.() || [];
|
|
117
|
-
for (const cookieStr of setCookieHeaders) {
|
|
118
|
-
cookies.setCookiesString.push(cookieStr);
|
|
119
|
-
const parts = cookieStr.split(";");
|
|
120
|
-
const [nameValue] = parts;
|
|
121
|
-
const [name, ...valueParts] = nameValue.split("=");
|
|
122
|
-
const value = valueParts.join("=");
|
|
123
|
-
if (name && value !== undefined) {
|
|
124
|
-
const cookie = new Cookie({
|
|
125
|
-
key: name.trim(),
|
|
126
|
-
value: value.trim(),
|
|
127
|
-
domain: new URL(url).hostname,
|
|
128
|
-
path: "/",
|
|
129
|
-
httpOnly: cookieStr.toLowerCase().includes("httponly"),
|
|
130
|
-
secure: cookieStr.toLowerCase().includes("secure"),
|
|
131
|
-
sameSite: "lax"
|
|
132
|
-
});
|
|
133
|
-
cookies.array.push(cookie);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
cookies.string = cookies.array.map((c) => `${c.key}=${c.value}`).join("; ");
|
|
137
|
-
cookies.serialized = cookies.array.map((c) => c.toJSON());
|
|
138
|
-
cookies.netscape = cookies.array.map((c) => c.toNetscapeFormat()).join(`
|
|
139
|
-
`);
|
|
140
|
-
return cookies;
|
|
141
171
|
}
|
|
142
172
|
function toFetchHeaders(headers) {
|
|
143
173
|
const fetchHeaders = new Headers;
|
|
@@ -181,7 +211,7 @@ async function prepareFetchBody(body) {
|
|
|
181
211
|
return new Uint8Array(buffer);
|
|
182
212
|
}
|
|
183
213
|
}
|
|
184
|
-
const nativeForm = body.toNativeFormData();
|
|
214
|
+
const nativeForm = await body.toNativeFormData();
|
|
185
215
|
if (nativeForm) {
|
|
186
216
|
return nativeForm;
|
|
187
217
|
}
|
|
@@ -306,6 +336,8 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
|
|
|
306
336
|
const response = await executeSingleFetchRequest(config, fetchOptions, requestCount, timing, _stats, streamResult, downloadResult, uploadResult);
|
|
307
337
|
const statusOnNext = _stats.statusOnNext;
|
|
308
338
|
if (response instanceof RezoError) {
|
|
339
|
+
if (!config.errors)
|
|
340
|
+
config.errors = [];
|
|
309
341
|
config.errors.push({
|
|
310
342
|
attempt: config.retryAttempts + 1,
|
|
311
343
|
error: response,
|
|
@@ -316,30 +348,50 @@ async function executeFetchRequest(fetchOptions, config, options, perform, strea
|
|
|
316
348
|
throw response;
|
|
317
349
|
}
|
|
318
350
|
if (config.retry) {
|
|
351
|
+
let shouldRetry = false;
|
|
319
352
|
if (config.retry.condition) {
|
|
320
353
|
const isPassed = await config.retry.condition(response);
|
|
321
|
-
|
|
322
|
-
throw response;
|
|
323
|
-
}
|
|
354
|
+
shouldRetry = isPassed === true;
|
|
324
355
|
} else {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
throw response;
|
|
330
|
-
}
|
|
331
|
-
retries++;
|
|
332
|
-
if (retryDelay > 0) {
|
|
333
|
-
await new Promise((resolve) => setTimeout(resolve, incrementDelay ? retryDelay * retries : retryDelay));
|
|
334
|
-
}
|
|
356
|
+
shouldRetry = statusCodes ? statusCodes.includes(response.status || 0) : true;
|
|
357
|
+
}
|
|
358
|
+
if (!shouldRetry || retries >= maxRetries) {
|
|
359
|
+
throw response;
|
|
335
360
|
}
|
|
361
|
+
retries++;
|
|
336
362
|
config.retryAttempts++;
|
|
363
|
+
if (retryDelay > 0) {
|
|
364
|
+
await new Promise((resolve) => setTimeout(resolve, incrementDelay ? retryDelay * retries : retryDelay));
|
|
365
|
+
}
|
|
337
366
|
}
|
|
338
367
|
continue;
|
|
339
368
|
}
|
|
340
369
|
if (statusOnNext === "success") {
|
|
341
370
|
return response;
|
|
342
371
|
}
|
|
372
|
+
if (statusOnNext === "error") {
|
|
373
|
+
const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, fetchOptions);
|
|
374
|
+
if (config.retry && statusCodes?.includes(response.status)) {
|
|
375
|
+
if (maxRetries > retries) {
|
|
376
|
+
retries++;
|
|
377
|
+
config.retryAttempts++;
|
|
378
|
+
config.errors.push({
|
|
379
|
+
attempt: config.retryAttempts,
|
|
380
|
+
error: httpError,
|
|
381
|
+
duration: perform.now()
|
|
382
|
+
});
|
|
383
|
+
perform.reset();
|
|
384
|
+
if (config.debug) {
|
|
385
|
+
console.log(`Request failed with status code ${response.status}, retrying...${retryDelay > 0 ? " in " + (incrementDelay ? retryDelay * retries : retryDelay) + "ms" : ""}`);
|
|
386
|
+
}
|
|
387
|
+
if (retryDelay > 0) {
|
|
388
|
+
await new Promise((resolve) => setTimeout(resolve, incrementDelay ? retryDelay * retries : retryDelay));
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
throw httpError;
|
|
394
|
+
}
|
|
343
395
|
if (statusOnNext === "redirect") {
|
|
344
396
|
const location = _stats.redirectUrl;
|
|
345
397
|
if (!location) {
|
|
@@ -418,7 +470,13 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
418
470
|
config.isSecure = isSecure;
|
|
419
471
|
config.finalUrl = url.href;
|
|
420
472
|
config.network.protocol = isSecure ? "https" : "http";
|
|
473
|
+
config.network.httpVersion = undefined;
|
|
421
474
|
config.timing.startTimestamp = timing.startTimestamp;
|
|
475
|
+
if (!config.transfer) {
|
|
476
|
+
config.transfer = { requestSize: 0, responseSize: 0, headerSize: 0, bodySize: 0 };
|
|
477
|
+
} else if (config.transfer.requestSize === undefined) {
|
|
478
|
+
config.transfer.requestSize = 0;
|
|
479
|
+
}
|
|
422
480
|
}
|
|
423
481
|
const reqHeaders = fetchOptions.headers instanceof RezoHeaders ? fetchOptions.headers.toObject() : fetchOptions.headers || {};
|
|
424
482
|
const headers = toFetchHeaders(reqHeaders);
|
|
@@ -448,7 +506,26 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
448
506
|
clearTimeout(timeoutId);
|
|
449
507
|
});
|
|
450
508
|
}
|
|
509
|
+
if (body instanceof RezoFormData) {
|
|
510
|
+
const contentType = body.getContentType();
|
|
511
|
+
if (contentType && !headers.has("content-type")) {
|
|
512
|
+
headers.set("content-type", contentType);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
451
515
|
const preparedBody = await prepareFetchBody(body);
|
|
516
|
+
if (config.transfer && body) {
|
|
517
|
+
if (typeof body === "string") {
|
|
518
|
+
config.transfer.requestSize = body.length;
|
|
519
|
+
} else if (body instanceof ArrayBuffer || body instanceof Uint8Array) {
|
|
520
|
+
config.transfer.requestSize = body.byteLength || body.length;
|
|
521
|
+
} else if (body instanceof URLSearchParams || body instanceof RezoURLSearchParams) {
|
|
522
|
+
config.transfer.requestSize = body.toString().length;
|
|
523
|
+
} else if (body instanceof RezoFormData) {
|
|
524
|
+
config.transfer.requestSize = body.getLengthSync();
|
|
525
|
+
} else if (typeof body === "object" && !(body instanceof Blob) && !(body instanceof ReadableStream)) {
|
|
526
|
+
config.transfer.requestSize = JSON.stringify(body).length;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
452
529
|
const fetchInit = {
|
|
453
530
|
method: fetchOptions.method.toUpperCase(),
|
|
454
531
|
headers,
|
|
@@ -482,7 +559,7 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
482
559
|
const responseHeaders = fromFetchHeaders(response.headers);
|
|
483
560
|
const contentType = response.headers.get("content-type") || "";
|
|
484
561
|
const contentLength = response.headers.get("content-length");
|
|
485
|
-
const cookies = parseCookiesFromHeaders(response.headers, url.href);
|
|
562
|
+
const cookies = parseCookiesFromHeaders(response.headers, url.href, config);
|
|
486
563
|
config.responseCookies = cookies;
|
|
487
564
|
const location = response.headers.get("location");
|
|
488
565
|
const isRedirect = status >= 300 && status < 400 && location;
|
|
@@ -564,23 +641,16 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
564
641
|
const bodySize = bodyBuffer?.byteLength || (typeof responseData === "string" ? responseData.length : 0);
|
|
565
642
|
config.transfer.bodySize = bodySize;
|
|
566
643
|
config.transfer.responseSize = bodySize;
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
headers: responseHeaders,
|
|
572
|
-
data: responseData
|
|
573
|
-
}, config, fetchOptions);
|
|
574
|
-
_stats.statusOnNext = "error";
|
|
575
|
-
return error;
|
|
576
|
-
}
|
|
577
|
-
_stats.statusOnNext = "success";
|
|
644
|
+
config.status = status;
|
|
645
|
+
config.statusText = statusText;
|
|
646
|
+
_stats.statusOnNext = status >= 400 ? "error" : "success";
|
|
647
|
+
const mergedCookies = mergeRequestAndResponseCookies(config, cookies, url.href);
|
|
578
648
|
const finalResponse = {
|
|
579
649
|
data: responseData,
|
|
580
650
|
status,
|
|
581
651
|
statusText,
|
|
582
652
|
headers: responseHeaders,
|
|
583
|
-
cookies,
|
|
653
|
+
cookies: mergedCookies,
|
|
584
654
|
config,
|
|
585
655
|
contentType,
|
|
586
656
|
contentLength: bodySize,
|
|
@@ -599,7 +669,7 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
599
669
|
contentType,
|
|
600
670
|
contentLength: buffer.length,
|
|
601
671
|
finalUrl: url.href,
|
|
602
|
-
cookies,
|
|
672
|
+
cookies: mergedCookies,
|
|
603
673
|
urls: buildUrlTree(config, url.href),
|
|
604
674
|
fileName: config.fileName,
|
|
605
675
|
fileSize: buffer.length,
|
|
@@ -646,7 +716,7 @@ async function executeSingleFetchRequest(config, fetchOptions, requestCount, tim
|
|
|
646
716
|
contentLength: bodySize
|
|
647
717
|
},
|
|
648
718
|
finalUrl: url.href,
|
|
649
|
-
cookies,
|
|
719
|
+
cookies: mergedCookies,
|
|
650
720
|
urls: buildUrlTree(config, url.href),
|
|
651
721
|
uploadSize: config.transfer.requestSize || 0,
|
|
652
722
|
timing: {
|
package/dist/adapters/http.cjs
CHANGED
|
@@ -402,6 +402,29 @@ Redirecting to: ${fetchOptions.fullUrl} using GET method`);
|
|
|
402
402
|
options = __.options;
|
|
403
403
|
continue;
|
|
404
404
|
}
|
|
405
|
+
if (statusOnNext === "error") {
|
|
406
|
+
const httpError = builErrorFromResponse(`Request failed with status code ${response.status}`, response, config, fetchOptions);
|
|
407
|
+
if (config.retry && statusCodes?.includes(response.status)) {
|
|
408
|
+
if (maxRetries > retries) {
|
|
409
|
+
retries++;
|
|
410
|
+
config.retryAttempts++;
|
|
411
|
+
config.errors.push({
|
|
412
|
+
attempt: config.retryAttempts,
|
|
413
|
+
error: httpError,
|
|
414
|
+
duration: perform.now()
|
|
415
|
+
});
|
|
416
|
+
perform.reset();
|
|
417
|
+
if (config.debug) {
|
|
418
|
+
console.log(`Request failed with status code ${response.status}, retrying...${retryDelay > 0 ? " in " + (incrementDelay ? retryDelay * retries : retryDelay) + "ms" : ""}`);
|
|
419
|
+
}
|
|
420
|
+
if (retryDelay > 0) {
|
|
421
|
+
await new Promise((resolve) => setTimeout(resolve, incrementDelay ? retryDelay * retries : retryDelay));
|
|
422
|
+
}
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
throw httpError;
|
|
427
|
+
}
|
|
405
428
|
delete config.beforeRedirect;
|
|
406
429
|
config.setSignal = () => {};
|
|
407
430
|
return response;
|
|
@@ -821,10 +844,23 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
|
|
|
821
844
|
}
|
|
822
845
|
});
|
|
823
846
|
});
|
|
847
|
+
req.on("error", (error) => {
|
|
848
|
+
_stats.statusOnNext = "error";
|
|
849
|
+
updateTiming(config, timing, "", 0);
|
|
850
|
+
const e = buildSmartError(config, fetchOptions, error);
|
|
851
|
+
const eventEmitter = streamResult || downloadResult || uploadResult;
|
|
852
|
+
if (eventEmitter) {
|
|
853
|
+
eventEmitter.emit("error", e);
|
|
854
|
+
}
|
|
855
|
+
resolve(e);
|
|
856
|
+
return;
|
|
857
|
+
});
|
|
858
|
+
let bodyPiped = false;
|
|
824
859
|
if (body) {
|
|
825
860
|
if (body instanceof URLSearchParams || body instanceof RezoURLSearchParams) {
|
|
826
861
|
req.write(body.toString());
|
|
827
862
|
} else if (body instanceof FormData || body instanceof RezoFormData) {
|
|
863
|
+
bodyPiped = true;
|
|
828
864
|
if (body instanceof RezoFormData) {
|
|
829
865
|
req.setHeader("Content-Type", `multipart/form-data; boundary=${body.getBoundary()}`);
|
|
830
866
|
body.pipe(req);
|
|
@@ -833,24 +869,18 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
|
|
|
833
869
|
req.setHeader("Content-Type", `multipart/form-data; boundary=${form.getBoundary()}`);
|
|
834
870
|
form.pipe(req);
|
|
835
871
|
}
|
|
836
|
-
} else if (
|
|
872
|
+
} else if (body instanceof Readable) {
|
|
873
|
+
bodyPiped = true;
|
|
874
|
+
body.pipe(req);
|
|
875
|
+
} else if (typeof body === "object" && !(body instanceof Buffer) && !(body instanceof Uint8Array)) {
|
|
837
876
|
req.write(JSON.stringify(body));
|
|
838
877
|
} else {
|
|
839
878
|
req.write(body);
|
|
840
879
|
}
|
|
841
880
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
updateTiming(config, timing, "", 0);
|
|
846
|
-
const e = buildSmartError(config, fetchOptions, error);
|
|
847
|
-
const eventEmitter = streamResult || downloadResult || uploadResult;
|
|
848
|
-
if (eventEmitter) {
|
|
849
|
-
eventEmitter.emit("error", e);
|
|
850
|
-
}
|
|
851
|
-
resolve(e);
|
|
852
|
-
return;
|
|
853
|
-
});
|
|
881
|
+
if (!bodyPiped) {
|
|
882
|
+
req.end();
|
|
883
|
+
}
|
|
854
884
|
} catch (error) {
|
|
855
885
|
_stats.statusOnNext = "error";
|
|
856
886
|
updateTiming(config, timing, "", 0);
|