recker 1.0.2-0 → 1.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/LICENSE +0 -2
- package/README.md +121 -72
- package/dist/cache/memory-storage.d.ts.map +1 -1
- package/dist/cache/memory-storage.js +7 -1
- package/dist/constants/http-status.d.ts +74 -0
- package/dist/constants/http-status.d.ts.map +1 -0
- package/dist/constants/http-status.js +156 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +6 -6
- package/dist/cookies/memory-cookie-jar.d.ts +31 -0
- package/dist/cookies/memory-cookie-jar.d.ts.map +1 -0
- package/dist/cookies/memory-cookie-jar.js +210 -0
- package/dist/core/client.d.ts +9 -0
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +252 -53
- package/dist/core/errors.d.ts +18 -2
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +66 -5
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +5 -0
- package/dist/core/request-promise.d.ts.map +1 -1
- package/dist/core/request-promise.js +8 -2
- package/dist/core/request.d.ts +7 -1
- package/dist/core/request.d.ts.map +1 -1
- package/dist/core/request.js +32 -0
- package/dist/core/response.d.ts +2 -0
- package/dist/core/response.d.ts.map +1 -1
- package/dist/core/response.js +44 -19
- package/dist/events/request-events.d.ts +48 -0
- package/dist/events/request-events.d.ts.map +1 -0
- package/dist/events/request-events.js +85 -0
- package/dist/index.d.ts +28 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -2
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +16 -5
- package/dist/mcp/contract.d.ts +77 -0
- package/dist/mcp/contract.d.ts.map +1 -0
- package/dist/mcp/contract.js +278 -0
- package/dist/mcp/types.d.ts +1 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/plugins/auth.d.ts +45 -0
- package/dist/plugins/auth.d.ts.map +1 -0
- package/dist/plugins/auth.js +268 -0
- package/dist/plugins/cache.d.ts +7 -1
- package/dist/plugins/cache.d.ts.map +1 -1
- package/dist/plugins/cache.js +470 -49
- package/dist/plugins/circuit-breaker.js +1 -1
- package/dist/plugins/compression.d.ts.map +1 -1
- package/dist/plugins/compression.js +3 -3
- package/dist/plugins/dedup.d.ts.map +1 -1
- package/dist/plugins/dedup.js +2 -1
- package/dist/plugins/graphql.d.ts +4 -3
- package/dist/plugins/graphql.d.ts.map +1 -1
- package/dist/plugins/graphql.js +24 -5
- package/dist/plugins/grpc-web.d.ts +80 -0
- package/dist/plugins/grpc-web.d.ts.map +1 -0
- package/dist/plugins/grpc-web.js +261 -0
- package/dist/plugins/har-player.d.ts.map +1 -1
- package/dist/plugins/har-player.js +11 -2
- package/dist/plugins/hls.d.ts +33 -0
- package/dist/plugins/hls.d.ts.map +1 -0
- package/dist/plugins/hls.js +225 -0
- package/dist/plugins/http2-push.d.ts +64 -0
- package/dist/plugins/http2-push.d.ts.map +1 -0
- package/dist/plugins/http2-push.js +274 -0
- package/dist/plugins/http3.d.ts +76 -0
- package/dist/plugins/http3.d.ts.map +1 -0
- package/dist/plugins/http3.js +231 -0
- package/dist/plugins/interface-rotator.d.ts +10 -0
- package/dist/plugins/interface-rotator.d.ts.map +1 -0
- package/dist/plugins/interface-rotator.js +57 -0
- package/dist/plugins/jsonrpc.d.ts +76 -0
- package/dist/plugins/jsonrpc.d.ts.map +1 -0
- package/dist/plugins/jsonrpc.js +143 -0
- package/dist/plugins/logger.d.ts +8 -5
- package/dist/plugins/logger.d.ts.map +1 -1
- package/dist/plugins/logger.js +66 -30
- package/dist/plugins/odata.d.ts +182 -0
- package/dist/plugins/odata.d.ts.map +1 -0
- package/dist/plugins/odata.js +561 -0
- package/dist/plugins/retry.d.ts +1 -0
- package/dist/plugins/retry.d.ts.map +1 -1
- package/dist/plugins/retry.js +26 -2
- package/dist/plugins/scrape.d.ts +22 -0
- package/dist/plugins/scrape.d.ts.map +1 -0
- package/dist/plugins/scrape.js +87 -0
- package/dist/plugins/soap.d.ts +73 -0
- package/dist/plugins/soap.d.ts.map +1 -0
- package/dist/plugins/soap.js +347 -0
- package/dist/plugins/user-agent.d.ts +8 -0
- package/dist/plugins/user-agent.d.ts.map +1 -0
- package/dist/plugins/user-agent.js +46 -0
- package/dist/plugins/xml.d.ts +10 -0
- package/dist/plugins/xml.d.ts.map +1 -0
- package/dist/plugins/xml.js +194 -0
- package/dist/presets/anthropic.d.ts +7 -0
- package/dist/presets/anthropic.d.ts.map +1 -0
- package/dist/presets/anthropic.js +17 -0
- package/dist/presets/azure-openai.d.ts +9 -0
- package/dist/presets/azure-openai.d.ts.map +1 -0
- package/dist/presets/azure-openai.js +25 -0
- package/dist/presets/cloudflare.d.ts +13 -0
- package/dist/presets/cloudflare.d.ts.map +1 -0
- package/dist/presets/cloudflare.js +39 -0
- package/dist/presets/cohere.d.ts +6 -0
- package/dist/presets/cohere.d.ts.map +1 -0
- package/dist/presets/cohere.js +16 -0
- package/dist/presets/deepseek.d.ts +6 -0
- package/dist/presets/deepseek.d.ts.map +1 -0
- package/dist/presets/deepseek.js +16 -0
- package/dist/presets/digitalocean.d.ts +6 -0
- package/dist/presets/digitalocean.d.ts.map +1 -0
- package/dist/presets/digitalocean.js +16 -0
- package/dist/presets/discord.d.ts +7 -0
- package/dist/presets/discord.d.ts.map +1 -0
- package/dist/presets/discord.js +17 -0
- package/dist/presets/fireworks.d.ts +6 -0
- package/dist/presets/fireworks.d.ts.map +1 -0
- package/dist/presets/fireworks.js +16 -0
- package/dist/presets/gemini.d.ts +6 -0
- package/dist/presets/gemini.d.ts.map +1 -0
- package/dist/presets/gemini.js +16 -0
- package/dist/presets/github.d.ts +7 -0
- package/dist/presets/github.d.ts.map +1 -0
- package/dist/presets/github.js +17 -0
- package/dist/presets/gitlab.d.ts +7 -0
- package/dist/presets/gitlab.d.ts.map +1 -0
- package/dist/presets/gitlab.js +16 -0
- package/dist/presets/groq.d.ts +6 -0
- package/dist/presets/groq.d.ts.map +1 -0
- package/dist/presets/groq.js +16 -0
- package/dist/presets/huggingface.d.ts +6 -0
- package/dist/presets/huggingface.d.ts.map +1 -0
- package/dist/presets/huggingface.js +16 -0
- package/dist/presets/index.d.ts +28 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +27 -0
- package/dist/presets/linear.d.ts +6 -0
- package/dist/presets/linear.d.ts.map +1 -0
- package/dist/presets/linear.js +16 -0
- package/dist/presets/mistral.d.ts +6 -0
- package/dist/presets/mistral.d.ts.map +1 -0
- package/dist/presets/mistral.js +16 -0
- package/dist/presets/notion.d.ts +7 -0
- package/dist/presets/notion.d.ts.map +1 -0
- package/dist/presets/notion.js +17 -0
- package/dist/presets/openai.d.ts +8 -0
- package/dist/presets/openai.d.ts.map +1 -0
- package/dist/presets/openai.js +23 -0
- package/dist/presets/perplexity.d.ts +6 -0
- package/dist/presets/perplexity.d.ts.map +1 -0
- package/dist/presets/perplexity.js +16 -0
- package/dist/presets/registry.d.ts +20 -0
- package/dist/presets/registry.d.ts.map +1 -0
- package/dist/presets/registry.js +311 -0
- package/dist/presets/replicate.d.ts +6 -0
- package/dist/presets/replicate.d.ts.map +1 -0
- package/dist/presets/replicate.js +16 -0
- package/dist/presets/slack.d.ts +6 -0
- package/dist/presets/slack.d.ts.map +1 -0
- package/dist/presets/slack.js +16 -0
- package/dist/presets/stripe.d.ts +8 -0
- package/dist/presets/stripe.d.ts.map +1 -0
- package/dist/presets/stripe.js +23 -0
- package/dist/presets/supabase.d.ts +7 -0
- package/dist/presets/supabase.d.ts.map +1 -0
- package/dist/presets/supabase.js +18 -0
- package/dist/presets/together.d.ts +6 -0
- package/dist/presets/together.d.ts.map +1 -0
- package/dist/presets/together.js +16 -0
- package/dist/presets/twilio.d.ts +7 -0
- package/dist/presets/twilio.d.ts.map +1 -0
- package/dist/presets/twilio.js +17 -0
- package/dist/presets/vercel.d.ts +7 -0
- package/dist/presets/vercel.d.ts.map +1 -0
- package/dist/presets/vercel.js +23 -0
- package/dist/presets/xai.d.ts +7 -0
- package/dist/presets/xai.d.ts.map +1 -0
- package/dist/presets/xai.js +17 -0
- package/dist/protocols/ftp.d.ts +63 -0
- package/dist/protocols/ftp.d.ts.map +1 -0
- package/dist/protocols/ftp.js +388 -0
- package/dist/protocols/index.d.ts +4 -0
- package/dist/protocols/index.d.ts.map +1 -0
- package/dist/protocols/index.js +3 -0
- package/dist/protocols/sftp.d.ts +65 -0
- package/dist/protocols/sftp.d.ts.map +1 -0
- package/dist/protocols/sftp.js +346 -0
- package/dist/protocols/telnet.d.ts +50 -0
- package/dist/protocols/telnet.d.ts.map +1 -0
- package/dist/protocols/telnet.js +139 -0
- package/dist/runner/request-runner.d.ts.map +1 -1
- package/dist/runner/request-runner.js +1 -0
- package/dist/scrape/document.d.ts +44 -0
- package/dist/scrape/document.d.ts.map +1 -0
- package/dist/scrape/document.js +198 -0
- package/dist/scrape/element.d.ts +50 -0
- package/dist/scrape/element.d.ts.map +1 -0
- package/dist/scrape/element.js +176 -0
- package/dist/scrape/extractors.d.ts +17 -0
- package/dist/scrape/extractors.d.ts.map +1 -0
- package/dist/scrape/extractors.js +356 -0
- package/dist/scrape/index.d.ts +5 -0
- package/dist/scrape/index.d.ts.map +1 -0
- package/dist/scrape/index.js +3 -0
- package/dist/scrape/types.d.ts +108 -0
- package/dist/scrape/types.d.ts.map +1 -0
- package/dist/scrape/types.js +1 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/mock.d.ts +58 -0
- package/dist/testing/mock.d.ts.map +1 -0
- package/dist/testing/mock.js +252 -0
- package/dist/transport/fetch.d.ts.map +1 -1
- package/dist/transport/fetch.js +12 -4
- package/dist/transport/undici.d.ts +17 -1
- package/dist/transport/undici.d.ts.map +1 -1
- package/dist/transport/undici.js +708 -47
- package/dist/types/index.d.ts +111 -10
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/logger.d.ts +17 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/logger.js +66 -0
- package/dist/utils/agent-manager.d.ts.map +1 -1
- package/dist/utils/agent-manager.js +20 -4
- package/dist/utils/body.d.ts.map +1 -1
- package/dist/utils/body.js +14 -2
- package/dist/utils/charset.d.ts +16 -0
- package/dist/utils/charset.d.ts.map +1 -0
- package/dist/utils/charset.js +169 -0
- package/dist/utils/client-pool.d.ts +21 -0
- package/dist/utils/client-pool.d.ts.map +1 -0
- package/dist/utils/client-pool.js +49 -0
- package/dist/utils/concurrency.d.ts.map +1 -1
- package/dist/utils/concurrency.js +8 -4
- package/dist/utils/dns-toolkit.d.ts +13 -0
- package/dist/utils/dns-toolkit.d.ts.map +1 -0
- package/dist/utils/dns-toolkit.js +48 -0
- package/dist/utils/doh.d.ts.map +1 -1
- package/dist/utils/doh.js +16 -3
- package/dist/utils/download.d.ts +15 -0
- package/dist/utils/download.d.ts.map +1 -0
- package/dist/utils/download.js +44 -0
- package/dist/utils/env-proxy.d.ts +13 -0
- package/dist/utils/env-proxy.d.ts.map +1 -0
- package/dist/utils/env-proxy.js +105 -0
- package/dist/utils/header-parser.d.ts +15 -1
- package/dist/utils/header-parser.d.ts.map +1 -1
- package/dist/utils/header-parser.js +161 -1
- package/dist/utils/link-header.d.ts +70 -0
- package/dist/utils/link-header.d.ts.map +1 -0
- package/dist/utils/link-header.js +190 -0
- package/dist/utils/progress.d.ts +7 -2
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +48 -15
- package/dist/utils/rdap.d.ts +17 -0
- package/dist/utils/rdap.d.ts.map +1 -0
- package/dist/utils/rdap.js +32 -0
- package/dist/utils/request-pool.d.ts.map +1 -1
- package/dist/utils/request-pool.js +4 -3
- package/dist/utils/sse.d.ts.map +1 -1
- package/dist/utils/sse.js +8 -2
- package/dist/utils/status-codes.d.ts +84 -0
- package/dist/utils/status-codes.d.ts.map +1 -0
- package/dist/utils/status-codes.js +204 -0
- package/dist/utils/streaming.d.ts.map +1 -1
- package/dist/utils/streaming.js +1 -0
- package/dist/utils/tls-inspector.d.ts +21 -0
- package/dist/utils/tls-inspector.d.ts.map +1 -0
- package/dist/utils/tls-inspector.js +39 -0
- package/dist/utils/try-fn.d.ts.map +1 -1
- package/dist/utils/try-fn.js +11 -5
- package/dist/utils/upload.d.ts +1 -0
- package/dist/utils/upload.d.ts.map +1 -1
- package/dist/utils/upload.js +20 -3
- package/dist/utils/user-agent.d.ts +9 -9
- package/dist/utils/user-agent.js +9 -9
- package/dist/utils/whois.d.ts.map +1 -1
- package/dist/utils/whois.js +11 -2
- package/dist/websocket/client.d.ts +29 -1
- package/dist/websocket/client.d.ts.map +1 -1
- package/dist/websocket/client.js +145 -13
- package/package.json +45 -8
package/dist/transport/undici.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { request as undiciRequest, errors as undiciErrors, ProxyAgent, Agent } from 'undici';
|
|
1
|
+
import { request as undiciRequest, errors as undiciErrors, ProxyAgent, Agent, Client } from 'undici';
|
|
2
2
|
import { HttpResponse } from '../core/response.js';
|
|
3
|
-
import { NetworkError, TimeoutError } from '../core/errors.js';
|
|
3
|
+
import { NetworkError, TimeoutError, ReckerError } from '../core/errors.js';
|
|
4
4
|
import { performance } from 'perf_hooks';
|
|
5
5
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
6
6
|
import { channel } from 'node:diagnostics_channel';
|
|
7
7
|
import { createLookupFunction } from '../utils/dns.js';
|
|
8
|
+
import { AgentManager } from '../utils/agent-manager.js';
|
|
9
|
+
import { createProgressStream } from '../utils/progress.js';
|
|
10
|
+
import { nodeToWebStream } from '../utils/streaming.js';
|
|
8
11
|
const undiciRequestChannel = channel('undici:request:create');
|
|
9
12
|
const undiciBodySentChannel = channel('undici:request:bodySent');
|
|
10
13
|
const undiciHeadersChannel = channel('undici:request:headers');
|
|
@@ -116,24 +119,68 @@ undiciConnectChannel.subscribe((message) => {
|
|
|
116
119
|
store.connection.reused = Boolean(socket.reused);
|
|
117
120
|
}
|
|
118
121
|
});
|
|
122
|
+
function mapTimeoutOptions(requestTimeout, transportDefaults) {
|
|
123
|
+
return {
|
|
124
|
+
connectTimeout: requestTimeout?.connect ??
|
|
125
|
+
requestTimeout?.secureConnect ??
|
|
126
|
+
transportDefaults?.connectTimeout,
|
|
127
|
+
headersTimeout: requestTimeout?.response ??
|
|
128
|
+
transportDefaults?.headersTimeout,
|
|
129
|
+
bodyTimeout: requestTimeout?.send ??
|
|
130
|
+
transportDefaults?.bodyTimeout,
|
|
131
|
+
totalTimeout: requestTimeout?.request
|
|
132
|
+
};
|
|
133
|
+
}
|
|
119
134
|
export class UndiciTransport {
|
|
135
|
+
static requestCounter = 0;
|
|
120
136
|
baseUrl;
|
|
121
137
|
options;
|
|
122
138
|
proxyAgent;
|
|
123
139
|
dnsAgent;
|
|
124
140
|
agentManager;
|
|
141
|
+
proxyBypassList;
|
|
142
|
+
tlsOptions;
|
|
143
|
+
socketClient;
|
|
144
|
+
observability;
|
|
125
145
|
constructor(baseUrl, options = {}) {
|
|
126
146
|
this.baseUrl = baseUrl;
|
|
127
147
|
this.options = options;
|
|
148
|
+
this.tlsOptions = options.tls;
|
|
149
|
+
this.observability = options.observability !== false;
|
|
128
150
|
if (options.proxy) {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
151
|
+
const proxyConfig = typeof options.proxy === 'string'
|
|
152
|
+
? { url: options.proxy }
|
|
153
|
+
: options.proxy;
|
|
154
|
+
const proxyUrl = new URL(proxyConfig.url);
|
|
155
|
+
const proxyType = proxyConfig.type || detectProxyType(proxyUrl.protocol);
|
|
156
|
+
if (proxyType?.startsWith('socks')) {
|
|
157
|
+
throw new NetworkError(`SOCKS proxy (${proxyType}) is not supported. Use an HTTP/HTTPS proxy or a SOCKS-to-HTTP bridge.`, 'ERR_UNSUPPORTED_PROXY_TYPE');
|
|
158
|
+
}
|
|
159
|
+
const proxyAuth = proxyConfig.auth
|
|
160
|
+
? `${proxyConfig.auth.username}:${proxyConfig.auth.password}`
|
|
132
161
|
: undefined;
|
|
133
162
|
const finalProxyUrl = proxyAuth
|
|
134
|
-
?
|
|
135
|
-
:
|
|
136
|
-
this.
|
|
163
|
+
? proxyConfig.url.replace('://', `://${proxyAuth}@`)
|
|
164
|
+
: proxyConfig.url;
|
|
165
|
+
this.proxyBypassList = proxyConfig.bypass;
|
|
166
|
+
const proxyAgentOptions = {
|
|
167
|
+
uri: finalProxyUrl,
|
|
168
|
+
headers: proxyConfig.headers,
|
|
169
|
+
token: proxyConfig.token,
|
|
170
|
+
proxyTunnel: proxyConfig.tunnel,
|
|
171
|
+
requestTls: mapTlsOptions(proxyConfig.requestTls ?? options.tls),
|
|
172
|
+
proxyTls: mapTlsOptions(proxyConfig.proxyTls),
|
|
173
|
+
};
|
|
174
|
+
if (proxyConfig.connectTimeout) {
|
|
175
|
+
proxyAgentOptions.connectTimeout = proxyConfig.connectTimeout;
|
|
176
|
+
}
|
|
177
|
+
if (proxyConfig.http2) {
|
|
178
|
+
if (!proxyAgentOptions.requestTls) {
|
|
179
|
+
proxyAgentOptions.requestTls = {};
|
|
180
|
+
}
|
|
181
|
+
proxyAgentOptions.requestTls.ALPNProtocols = ['h2', 'http/1.1'];
|
|
182
|
+
}
|
|
183
|
+
this.proxyAgent = new ProxyAgent(proxyAgentOptions);
|
|
137
184
|
}
|
|
138
185
|
this.agentManager = options.agent;
|
|
139
186
|
if (options.dns && !this.agentManager) {
|
|
@@ -144,19 +191,48 @@ export class UndiciTransport {
|
|
|
144
191
|
},
|
|
145
192
|
});
|
|
146
193
|
}
|
|
194
|
+
if (!this.agentManager) {
|
|
195
|
+
this.agentManager = new AgentManager({
|
|
196
|
+
connections: options.connections,
|
|
197
|
+
pipelining: options.pipelining,
|
|
198
|
+
keepAlive: options.keepAlive,
|
|
199
|
+
keepAliveTimeout: options.keepAliveTimeout,
|
|
200
|
+
keepAliveMaxTimeout: options.keepAliveMaxTimeout,
|
|
201
|
+
connectTimeout: options.connectTimeout,
|
|
202
|
+
perDomainPooling: options.perDomainPooling,
|
|
203
|
+
localAddress: options.localAddress,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (options.socketPath) {
|
|
207
|
+
this.socketClient = new Client(baseUrl, {
|
|
208
|
+
socketPath: options.socketPath
|
|
209
|
+
});
|
|
210
|
+
}
|
|
147
211
|
}
|
|
148
212
|
async dispatch(req) {
|
|
149
|
-
const headers =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
});
|
|
213
|
+
const headers = Object.fromEntries(req.headers);
|
|
214
|
+
const contentLengthHeader = headers['content-length'];
|
|
215
|
+
const uploadTotal = contentLengthHeader ? parseInt(contentLengthHeader, 10) : undefined;
|
|
153
216
|
const path = req.url.startsWith(this.baseUrl) ? req.url.substring(this.baseUrl.length) : req.url;
|
|
154
|
-
|
|
217
|
+
let currentUrl = new URL(path, this.baseUrl).toString();
|
|
218
|
+
const handleRedirectsManually = Boolean(req.beforeRedirect);
|
|
219
|
+
const maxRedirects = req.maxRedirects ?? 20;
|
|
220
|
+
const followRedirects = req.followRedirects !== false;
|
|
221
|
+
const timeouts = mapTimeoutOptions(req.timeout, {
|
|
222
|
+
connectTimeout: this.options.connectTimeout,
|
|
223
|
+
headersTimeout: this.options.headersTimeout,
|
|
224
|
+
bodyTimeout: this.options.bodyTimeout
|
|
225
|
+
});
|
|
226
|
+
let timeoutController;
|
|
227
|
+
let timeoutId;
|
|
228
|
+
if (!this.observability) {
|
|
229
|
+
return this.dispatchFast(req, headers, currentUrl, timeouts, handleRedirectsManually, maxRedirects, followRedirects, uploadTotal);
|
|
230
|
+
}
|
|
155
231
|
const requestContext = {
|
|
156
232
|
timings: {},
|
|
157
233
|
connection: {},
|
|
158
234
|
requestStartTime: 0,
|
|
159
|
-
requestCorrelationId:
|
|
235
|
+
requestCorrelationId: `r${++UndiciTransport.requestCounter}`,
|
|
160
236
|
hooks: req._hooks
|
|
161
237
|
};
|
|
162
238
|
return requestStorage.run(requestContext, async () => {
|
|
@@ -165,22 +241,304 @@ export class UndiciTransport {
|
|
|
165
241
|
if (requestContext.requestStartTime === 0) {
|
|
166
242
|
requestContext.requestStartTime = startTime;
|
|
167
243
|
}
|
|
168
|
-
|
|
244
|
+
let redirectCount = 0;
|
|
245
|
+
let currentMethod = req.method;
|
|
246
|
+
let currentBody = req.body;
|
|
247
|
+
let currentHeaders = { ...headers };
|
|
248
|
+
let effectiveSignal = req.signal;
|
|
249
|
+
if (timeouts.totalTimeout) {
|
|
250
|
+
timeoutController = new AbortController();
|
|
251
|
+
if (req.signal) {
|
|
252
|
+
const originalSignal = req.signal;
|
|
253
|
+
effectiveSignal = timeoutController.signal;
|
|
254
|
+
const onOriginalAbort = () => {
|
|
255
|
+
timeoutController.abort();
|
|
256
|
+
};
|
|
257
|
+
if (originalSignal.aborted) {
|
|
258
|
+
timeoutController.abort();
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
originalSignal.addEventListener('abort', onOriginalAbort, { once: true });
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
effectiveSignal = timeoutController.signal;
|
|
266
|
+
}
|
|
267
|
+
timeoutId = setTimeout(() => {
|
|
268
|
+
timeoutController.abort();
|
|
269
|
+
}, timeouts.totalTimeout);
|
|
270
|
+
}
|
|
271
|
+
while (true) {
|
|
272
|
+
const dispatcher = this.socketClient || determineDispatcher({
|
|
273
|
+
explicit: req._dispatcher,
|
|
274
|
+
proxyAgent: this.proxyAgent,
|
|
275
|
+
agentManager: this.agentManager,
|
|
276
|
+
dnsAgent: this.dnsAgent,
|
|
277
|
+
url: currentUrl,
|
|
278
|
+
bypass: this.proxyBypassList
|
|
279
|
+
});
|
|
280
|
+
const bodyWithProgress = redirectCount === 0
|
|
281
|
+
? wrapUploadBody(currentBody, req.onUploadProgress, uploadTotal)
|
|
282
|
+
: currentBody;
|
|
283
|
+
let finalBody = bodyWithProgress;
|
|
284
|
+
if (finalBody instanceof FormData) {
|
|
285
|
+
const tempResponse = new Response(finalBody);
|
|
286
|
+
finalBody = tempResponse.body;
|
|
287
|
+
tempResponse.headers.forEach((value, key) => {
|
|
288
|
+
if (key.toLowerCase() === 'content-type') {
|
|
289
|
+
currentHeaders[key] = value;
|
|
290
|
+
delete currentHeaders['Content-Type'];
|
|
291
|
+
}
|
|
292
|
+
else if (!currentHeaders[key]) {
|
|
293
|
+
currentHeaders[key] = value;
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
const undiciOptions = {
|
|
298
|
+
method: currentMethod,
|
|
299
|
+
headers: currentHeaders,
|
|
300
|
+
body: finalBody,
|
|
301
|
+
signal: effectiveSignal,
|
|
302
|
+
dispatcher: dispatcher,
|
|
303
|
+
connectTimeout: timeouts.connectTimeout,
|
|
304
|
+
headersTimeout: timeouts.headersTimeout,
|
|
305
|
+
bodyTimeout: timeouts.bodyTimeout,
|
|
306
|
+
maxRedirections: handleRedirectsManually ? 0 : this.options.maxRedirections,
|
|
307
|
+
};
|
|
308
|
+
if (finalBody && (finalBody instanceof ReadableStream ||
|
|
309
|
+
(typeof finalBody.pipe === 'function') ||
|
|
310
|
+
finalBody[Symbol.asyncIterator])) {
|
|
311
|
+
undiciOptions.duplex = 'half';
|
|
312
|
+
}
|
|
313
|
+
const tlsOptions = mapTlsOptions(this.tlsOptions);
|
|
314
|
+
if (tlsOptions) {
|
|
315
|
+
undiciOptions.tls = tlsOptions;
|
|
316
|
+
}
|
|
317
|
+
const http2Enabled = req.http2 !== undefined
|
|
318
|
+
? req.http2
|
|
319
|
+
: this.options.http2?.enabled;
|
|
320
|
+
if (http2Enabled) {
|
|
321
|
+
undiciOptions.allowH2 = true;
|
|
322
|
+
}
|
|
323
|
+
else if (req.http2 === false) {
|
|
324
|
+
undiciOptions.allowH2 = false;
|
|
325
|
+
}
|
|
326
|
+
if (http2Enabled && this.options.http2) {
|
|
327
|
+
if (this.options.http2.maxConcurrentStreams !== undefined) {
|
|
328
|
+
undiciOptions.maxConcurrentStreams = this.options.http2.maxConcurrentStreams;
|
|
329
|
+
}
|
|
330
|
+
if (this.options.http2.pipelining !== undefined) {
|
|
331
|
+
undiciOptions.pipelining = this.options.http2.pipelining;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
let undiciResponse;
|
|
335
|
+
if (this.socketClient) {
|
|
336
|
+
const urlPath = new URL(currentUrl).pathname + new URL(currentUrl).search;
|
|
337
|
+
undiciResponse = await this.socketClient.request({
|
|
338
|
+
path: urlPath || '/',
|
|
339
|
+
method: currentMethod,
|
|
340
|
+
headers: currentHeaders,
|
|
341
|
+
body: bodyWithProgress,
|
|
342
|
+
signal: req.signal,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
undiciResponse = await undiciRequest(currentUrl, undiciOptions);
|
|
347
|
+
}
|
|
348
|
+
const statusCode = undiciResponse.statusCode;
|
|
349
|
+
const isRedirect = statusCode >= 300 && statusCode < 400;
|
|
350
|
+
if (handleRedirectsManually && isRedirect && followRedirects && redirectCount < maxRedirects) {
|
|
351
|
+
const locationHeader = undiciResponse.headers['location'];
|
|
352
|
+
const location = Array.isArray(locationHeader) ? locationHeader[0] : locationHeader;
|
|
353
|
+
if (location) {
|
|
354
|
+
const nextUrl = new URL(location, currentUrl).toString();
|
|
355
|
+
const responseHeaders = new Headers();
|
|
356
|
+
for (const [key, value] of Object.entries(undiciResponse.headers)) {
|
|
357
|
+
if (value !== undefined) {
|
|
358
|
+
if (Array.isArray(value)) {
|
|
359
|
+
value.forEach(v => responseHeaders.append(key, v));
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
responseHeaders.set(key, value);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const redirectInfo = {
|
|
367
|
+
from: currentUrl,
|
|
368
|
+
to: nextUrl,
|
|
369
|
+
status: statusCode,
|
|
370
|
+
headers: responseHeaders,
|
|
371
|
+
};
|
|
372
|
+
const hookResult = await req.beforeRedirect(redirectInfo);
|
|
373
|
+
if (hookResult === false) {
|
|
374
|
+
const finalResponse = req.onDownloadProgress
|
|
375
|
+
? wrapDownloadResponse(undiciResponse, req.onDownloadProgress)
|
|
376
|
+
: undiciResponse;
|
|
377
|
+
return new HttpResponse(finalResponse, {
|
|
378
|
+
timings: requestContext.timings,
|
|
379
|
+
connection: requestContext.connection
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
currentUrl = typeof hookResult === 'string' ? hookResult : nextUrl;
|
|
383
|
+
if (statusCode === 303 || ((statusCode === 301 || statusCode === 302) && currentMethod !== 'GET' && currentMethod !== 'HEAD')) {
|
|
384
|
+
currentMethod = 'GET';
|
|
385
|
+
currentBody = null;
|
|
386
|
+
delete currentHeaders['content-type'];
|
|
387
|
+
delete currentHeaders['content-length'];
|
|
388
|
+
delete currentHeaders['Content-Type'];
|
|
389
|
+
delete currentHeaders['Content-Length'];
|
|
390
|
+
}
|
|
391
|
+
await undiciResponse.body.arrayBuffer().catch(() => { });
|
|
392
|
+
redirectCount++;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const finalResponse = req.onDownloadProgress
|
|
397
|
+
? wrapDownloadResponse(undiciResponse, req.onDownloadProgress)
|
|
398
|
+
: undiciResponse;
|
|
399
|
+
const ttfb = performance.now() - startTime;
|
|
400
|
+
const totalTime = performance.now() - requestContext.requestStartTime;
|
|
401
|
+
if (!requestContext.timings.firstByte) {
|
|
402
|
+
requestContext.timings.firstByte = ttfb;
|
|
403
|
+
}
|
|
404
|
+
if (!requestContext.timings.total) {
|
|
405
|
+
requestContext.timings.total = totalTime;
|
|
406
|
+
}
|
|
407
|
+
return new HttpResponse(finalResponse, {
|
|
408
|
+
timings: requestContext.timings,
|
|
409
|
+
connection: requestContext.connection
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
if (error instanceof undiciErrors.ConnectTimeoutError || error.code === 'UND_ERR_CONNECT_TIMEOUT') {
|
|
415
|
+
throw new TimeoutError(req, {
|
|
416
|
+
phase: 'connect',
|
|
417
|
+
timeout: timeouts.connectTimeout
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
if (error instanceof undiciErrors.HeadersTimeoutError || error.code === 'UND_ERR_HEADERS_TIMEOUT') {
|
|
421
|
+
throw new TimeoutError(req, {
|
|
422
|
+
phase: 'response',
|
|
423
|
+
timeout: timeouts.headersTimeout
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
if (error instanceof undiciErrors.BodyTimeoutError || error.code === 'UND_ERR_BODY_TIMEOUT') {
|
|
427
|
+
throw new TimeoutError(req, {
|
|
428
|
+
phase: 'send',
|
|
429
|
+
timeout: timeouts.bodyTimeout
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
if (error.name === 'AbortError' || error.code === 'ABORT_ERR') {
|
|
433
|
+
throw new TimeoutError(req, {
|
|
434
|
+
phase: 'request',
|
|
435
|
+
timeout: timeouts.totalTimeout
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
const code = error.code || error?.cause?.code;
|
|
439
|
+
if (code === 'UND_ERR_HEADERS_OVERFLOW') {
|
|
440
|
+
throw new ReckerError('Response headers exceeded the maximum allowed size', req, undefined, [
|
|
441
|
+
'Reduce response header size or adjust maxHeaderSize in agent configuration.',
|
|
442
|
+
'Check for runaway set-cookie or debug headers.',
|
|
443
|
+
'Verify upstream is not sending excessive headers.'
|
|
444
|
+
], false);
|
|
445
|
+
}
|
|
446
|
+
throw new NetworkError(error.message, code, req);
|
|
447
|
+
}
|
|
448
|
+
finally {
|
|
449
|
+
if (timeoutId) {
|
|
450
|
+
clearTimeout(timeoutId);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
async dispatchFast(req, headers, currentUrl, timeouts, handleRedirectsManually, maxRedirects, followRedirects, uploadTotal) {
|
|
456
|
+
let timeoutController;
|
|
457
|
+
let timeoutId;
|
|
458
|
+
try {
|
|
459
|
+
let redirectCount = 0;
|
|
460
|
+
let currentMethod = req.method;
|
|
461
|
+
let currentBody = req.body;
|
|
462
|
+
let currentHeaders = { ...headers };
|
|
463
|
+
let effectiveSignal = req.signal;
|
|
464
|
+
if (timeouts.totalTimeout) {
|
|
465
|
+
timeoutController = new AbortController();
|
|
466
|
+
if (req.signal) {
|
|
467
|
+
const originalSignal = req.signal;
|
|
468
|
+
effectiveSignal = timeoutController.signal;
|
|
469
|
+
const onOriginalAbort = () => {
|
|
470
|
+
timeoutController.abort();
|
|
471
|
+
};
|
|
472
|
+
if (originalSignal.aborted) {
|
|
473
|
+
timeoutController.abort();
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
originalSignal.addEventListener('abort', onOriginalAbort, { once: true });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
effectiveSignal = timeoutController.signal;
|
|
481
|
+
}
|
|
482
|
+
timeoutId = setTimeout(() => {
|
|
483
|
+
timeoutController.abort();
|
|
484
|
+
}, timeouts.totalTimeout);
|
|
485
|
+
}
|
|
486
|
+
while (true) {
|
|
487
|
+
const dispatcher = this.socketClient || determineDispatcher({
|
|
488
|
+
explicit: req._dispatcher,
|
|
489
|
+
proxyAgent: this.proxyAgent,
|
|
490
|
+
agentManager: this.agentManager,
|
|
491
|
+
dnsAgent: this.dnsAgent,
|
|
492
|
+
url: currentUrl,
|
|
493
|
+
bypass: this.proxyBypassList
|
|
494
|
+
});
|
|
495
|
+
const bodyWithProgress = redirectCount === 0
|
|
496
|
+
? wrapUploadBody(currentBody, req.onUploadProgress, uploadTotal)
|
|
497
|
+
: currentBody;
|
|
498
|
+
let finalBody = bodyWithProgress;
|
|
499
|
+
if (finalBody instanceof FormData) {
|
|
500
|
+
const tempResponse = new Response(finalBody);
|
|
501
|
+
finalBody = tempResponse.body;
|
|
502
|
+
tempResponse.headers.forEach((value, key) => {
|
|
503
|
+
if (key.toLowerCase() === 'content-type') {
|
|
504
|
+
currentHeaders[key] = value;
|
|
505
|
+
delete currentHeaders['Content-Type'];
|
|
506
|
+
}
|
|
507
|
+
else if (!currentHeaders[key]) {
|
|
508
|
+
currentHeaders[key] = value;
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
}
|
|
169
512
|
const undiciOptions = {
|
|
170
|
-
method:
|
|
171
|
-
headers:
|
|
172
|
-
body:
|
|
173
|
-
signal:
|
|
513
|
+
method: currentMethod,
|
|
514
|
+
headers: currentHeaders,
|
|
515
|
+
body: finalBody,
|
|
516
|
+
signal: effectiveSignal,
|
|
174
517
|
dispatcher: dispatcher,
|
|
175
|
-
connectTimeout:
|
|
176
|
-
headersTimeout:
|
|
177
|
-
bodyTimeout:
|
|
178
|
-
maxRedirections: this.options.maxRedirections,
|
|
518
|
+
connectTimeout: timeouts.connectTimeout,
|
|
519
|
+
headersTimeout: timeouts.headersTimeout,
|
|
520
|
+
bodyTimeout: timeouts.bodyTimeout,
|
|
521
|
+
maxRedirections: handleRedirectsManually ? 0 : this.options.maxRedirections,
|
|
179
522
|
};
|
|
180
|
-
if (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
523
|
+
if (finalBody && (finalBody instanceof ReadableStream ||
|
|
524
|
+
(typeof finalBody.pipe === 'function') ||
|
|
525
|
+
finalBody[Symbol.asyncIterator])) {
|
|
526
|
+
undiciOptions.duplex = 'half';
|
|
527
|
+
}
|
|
528
|
+
const tlsOptions = mapTlsOptions(this.tlsOptions);
|
|
529
|
+
if (tlsOptions) {
|
|
530
|
+
undiciOptions.tls = tlsOptions;
|
|
531
|
+
}
|
|
532
|
+
const http2Enabled = req.http2 !== undefined
|
|
533
|
+
? req.http2
|
|
534
|
+
: this.options.http2?.enabled;
|
|
535
|
+
if (http2Enabled) {
|
|
536
|
+
undiciOptions.allowH2 = true;
|
|
537
|
+
}
|
|
538
|
+
else if (req.http2 === false) {
|
|
539
|
+
undiciOptions.allowH2 = false;
|
|
540
|
+
}
|
|
541
|
+
if (http2Enabled && this.options.http2) {
|
|
184
542
|
if (this.options.http2.maxConcurrentStreams !== undefined) {
|
|
185
543
|
undiciOptions.maxConcurrentStreams = this.options.http2.maxConcurrentStreams;
|
|
186
544
|
}
|
|
@@ -188,31 +546,334 @@ export class UndiciTransport {
|
|
|
188
546
|
undiciOptions.pipelining = this.options.http2.pipelining;
|
|
189
547
|
}
|
|
190
548
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
549
|
+
let undiciResponse;
|
|
550
|
+
if (this.socketClient) {
|
|
551
|
+
const urlPath = new URL(currentUrl).pathname + new URL(currentUrl).search;
|
|
552
|
+
undiciResponse = await this.socketClient.request({
|
|
553
|
+
path: urlPath || '/',
|
|
554
|
+
method: currentMethod,
|
|
555
|
+
headers: currentHeaders,
|
|
556
|
+
body: bodyWithProgress,
|
|
557
|
+
signal: req.signal,
|
|
558
|
+
});
|
|
196
559
|
}
|
|
197
|
-
|
|
198
|
-
|
|
560
|
+
else {
|
|
561
|
+
undiciResponse = await undiciRequest(currentUrl, undiciOptions);
|
|
199
562
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
563
|
+
const statusCode = undiciResponse.statusCode;
|
|
564
|
+
const isRedirect = statusCode >= 300 && statusCode < 400;
|
|
565
|
+
if (handleRedirectsManually && isRedirect && followRedirects && redirectCount < maxRedirects) {
|
|
566
|
+
const locationHeader = undiciResponse.headers['location'];
|
|
567
|
+
const location = Array.isArray(locationHeader) ? locationHeader[0] : locationHeader;
|
|
568
|
+
if (location) {
|
|
569
|
+
const nextUrl = new URL(location, currentUrl).toString();
|
|
570
|
+
const responseHeaders = new Headers();
|
|
571
|
+
for (const [key, value] of Object.entries(undiciResponse.headers)) {
|
|
572
|
+
if (value !== undefined) {
|
|
573
|
+
if (Array.isArray(value)) {
|
|
574
|
+
value.forEach(v => responseHeaders.append(key, v));
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
responseHeaders.set(key, value);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
const redirectInfo = {
|
|
582
|
+
from: currentUrl,
|
|
583
|
+
to: nextUrl,
|
|
584
|
+
status: statusCode,
|
|
585
|
+
headers: responseHeaders,
|
|
586
|
+
};
|
|
587
|
+
const hookResult = await req.beforeRedirect(redirectInfo);
|
|
588
|
+
if (hookResult === false) {
|
|
589
|
+
const finalResponse = req.onDownloadProgress
|
|
590
|
+
? wrapDownloadResponse(undiciResponse, req.onDownloadProgress)
|
|
591
|
+
: undiciResponse;
|
|
592
|
+
return new HttpResponse(finalResponse, {
|
|
593
|
+
timings: {},
|
|
594
|
+
connection: {}
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
currentUrl = typeof hookResult === 'string' ? hookResult : nextUrl;
|
|
598
|
+
if (statusCode === 303 || ((statusCode === 301 || statusCode === 302) && currentMethod !== 'GET' && currentMethod !== 'HEAD')) {
|
|
599
|
+
currentMethod = 'GET';
|
|
600
|
+
currentBody = null;
|
|
601
|
+
delete currentHeaders['content-type'];
|
|
602
|
+
delete currentHeaders['content-length'];
|
|
603
|
+
delete currentHeaders['Content-Type'];
|
|
604
|
+
delete currentHeaders['Content-Length'];
|
|
605
|
+
}
|
|
606
|
+
await undiciResponse.body.arrayBuffer().catch(() => { });
|
|
607
|
+
redirectCount++;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const finalResponse = req.onDownloadProgress
|
|
612
|
+
? wrapDownloadResponse(undiciResponse, req.onDownloadProgress)
|
|
613
|
+
: undiciResponse;
|
|
614
|
+
return new HttpResponse(finalResponse, {
|
|
615
|
+
timings: {},
|
|
616
|
+
connection: {}
|
|
203
617
|
});
|
|
204
618
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
throw new
|
|
619
|
+
}
|
|
620
|
+
catch (error) {
|
|
621
|
+
if (error instanceof undiciErrors.ConnectTimeoutError || error.code === 'UND_ERR_CONNECT_TIMEOUT') {
|
|
622
|
+
throw new TimeoutError(req, {
|
|
623
|
+
phase: 'connect',
|
|
624
|
+
timeout: timeouts.connectTimeout
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
if (error instanceof undiciErrors.HeadersTimeoutError || error.code === 'UND_ERR_HEADERS_TIMEOUT') {
|
|
628
|
+
throw new TimeoutError(req, {
|
|
629
|
+
phase: 'response',
|
|
630
|
+
timeout: timeouts.headersTimeout
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
if (error instanceof undiciErrors.BodyTimeoutError || error.code === 'UND_ERR_BODY_TIMEOUT') {
|
|
634
|
+
throw new TimeoutError(req, {
|
|
635
|
+
phase: 'send',
|
|
636
|
+
timeout: timeouts.bodyTimeout
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
if (error.name === 'AbortError' || error.code === 'ABORT_ERR') {
|
|
640
|
+
throw new TimeoutError(req, {
|
|
641
|
+
phase: 'request',
|
|
642
|
+
timeout: timeouts.totalTimeout
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
const code = error.code || error?.cause?.code;
|
|
646
|
+
if (code === 'UND_ERR_HEADERS_OVERFLOW') {
|
|
647
|
+
throw new ReckerError('Response headers exceeded the maximum allowed size', req, undefined, [
|
|
648
|
+
'Reduce response header size or adjust maxHeaderSize in agent configuration.',
|
|
649
|
+
'Check for runaway set-cookie or debug headers.',
|
|
650
|
+
'Verify upstream is not sending excessive headers.'
|
|
651
|
+
], false);
|
|
215
652
|
}
|
|
653
|
+
throw new NetworkError(error.message, code, req);
|
|
654
|
+
}
|
|
655
|
+
finally {
|
|
656
|
+
if (timeoutId) {
|
|
657
|
+
clearTimeout(timeoutId);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function mapTlsOptions(options) {
|
|
663
|
+
if (!options)
|
|
664
|
+
return undefined;
|
|
665
|
+
const tls = {};
|
|
666
|
+
if (options.minVersion)
|
|
667
|
+
tls.minVersion = options.minVersion;
|
|
668
|
+
if (options.maxVersion)
|
|
669
|
+
tls.maxVersion = options.maxVersion;
|
|
670
|
+
if (options.ciphers)
|
|
671
|
+
tls.ciphers = options.ciphers;
|
|
672
|
+
if (options.honorCipherOrder !== undefined)
|
|
673
|
+
tls.honorCipherOrder = options.honorCipherOrder;
|
|
674
|
+
if (options.ca)
|
|
675
|
+
tls.ca = options.ca;
|
|
676
|
+
if (options.cert)
|
|
677
|
+
tls.cert = options.cert;
|
|
678
|
+
if (options.key)
|
|
679
|
+
tls.key = options.key;
|
|
680
|
+
if (options.passphrase)
|
|
681
|
+
tls.passphrase = options.passphrase;
|
|
682
|
+
if (options.rejectUnauthorized !== undefined)
|
|
683
|
+
tls.rejectUnauthorized = options.rejectUnauthorized;
|
|
684
|
+
if (options.alpnProtocols)
|
|
685
|
+
tls.ALPNProtocols = options.alpnProtocols;
|
|
686
|
+
if (options.sessionTimeout !== undefined)
|
|
687
|
+
tls.sessionTimeout = options.sessionTimeout;
|
|
688
|
+
if (options.sessionIdContext)
|
|
689
|
+
tls.sessionIdContext = options.sessionIdContext;
|
|
690
|
+
if (options.servername !== undefined) {
|
|
691
|
+
tls.servername = options.servername === false ? '' : options.servername;
|
|
692
|
+
}
|
|
693
|
+
return tls;
|
|
694
|
+
}
|
|
695
|
+
function determineDispatcher(params) {
|
|
696
|
+
if (params.explicit)
|
|
697
|
+
return params.explicit;
|
|
698
|
+
const { proxyAgent, agentManager, dnsAgent, url, bypass } = params;
|
|
699
|
+
const bypassProxy = shouldBypassProxy(url, bypass);
|
|
700
|
+
if (proxyAgent && !bypassProxy)
|
|
701
|
+
return proxyAgent;
|
|
702
|
+
if (agentManager)
|
|
703
|
+
return agentManager.getAgentForUrl(url);
|
|
704
|
+
if (dnsAgent)
|
|
705
|
+
return dnsAgent;
|
|
706
|
+
return undefined;
|
|
707
|
+
}
|
|
708
|
+
function detectProxyType(protocol) {
|
|
709
|
+
const p = protocol.toLowerCase().replace(':', '');
|
|
710
|
+
switch (p) {
|
|
711
|
+
case 'http':
|
|
712
|
+
return 'http';
|
|
713
|
+
case 'https':
|
|
714
|
+
return 'https';
|
|
715
|
+
case 'socks4':
|
|
716
|
+
return 'socks4';
|
|
717
|
+
case 'socks4a':
|
|
718
|
+
return 'socks4a';
|
|
719
|
+
case 'socks5':
|
|
720
|
+
case 'socks':
|
|
721
|
+
return 'socks5';
|
|
722
|
+
default:
|
|
723
|
+
return undefined;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function matchesCIDR(ip, cidr) {
|
|
727
|
+
const [range, bits] = cidr.split('/');
|
|
728
|
+
if (!bits)
|
|
729
|
+
return ip === range;
|
|
730
|
+
const mask = parseInt(bits, 10);
|
|
731
|
+
if (isNaN(mask))
|
|
732
|
+
return false;
|
|
733
|
+
const ipParts = ip.split('.').map(Number);
|
|
734
|
+
const rangeParts = range.split('.').map(Number);
|
|
735
|
+
if (ipParts.length !== 4 || rangeParts.length !== 4)
|
|
736
|
+
return false;
|
|
737
|
+
if (ipParts.some(isNaN) || rangeParts.some(isNaN))
|
|
738
|
+
return false;
|
|
739
|
+
const ipNum = (ipParts[0] << 24) | (ipParts[1] << 16) | (ipParts[2] << 8) | ipParts[3];
|
|
740
|
+
const rangeNum = (rangeParts[0] << 24) | (rangeParts[1] << 16) | (rangeParts[2] << 8) | rangeParts[3];
|
|
741
|
+
const maskNum = ~((1 << (32 - mask)) - 1);
|
|
742
|
+
return (ipNum & maskNum) === (rangeNum & maskNum);
|
|
743
|
+
}
|
|
744
|
+
function shouldBypassProxy(url, bypass) {
|
|
745
|
+
if (!bypass || bypass.length === 0)
|
|
746
|
+
return false;
|
|
747
|
+
let hostname = '';
|
|
748
|
+
let port = '';
|
|
749
|
+
try {
|
|
750
|
+
const parsed = new URL(url);
|
|
751
|
+
hostname = parsed.hostname;
|
|
752
|
+
port = parsed.port;
|
|
753
|
+
}
|
|
754
|
+
catch {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
for (const rule of bypass) {
|
|
758
|
+
if (rule === '*')
|
|
759
|
+
return true;
|
|
760
|
+
if (rule.includes('/')) {
|
|
761
|
+
if (matchesCIDR(hostname, rule))
|
|
762
|
+
return true;
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if (rule.includes(':') && !rule.includes('/')) {
|
|
766
|
+
const [hostRule, portRule] = rule.split(':');
|
|
767
|
+
if (hostname === hostRule && (!portRule || port === portRule))
|
|
768
|
+
return true;
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (rule.startsWith('*.')) {
|
|
772
|
+
const suffix = rule.slice(1);
|
|
773
|
+
if (hostname.endsWith(suffix))
|
|
774
|
+
return true;
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
if (rule.startsWith('.')) {
|
|
778
|
+
if (hostname.endsWith(rule))
|
|
779
|
+
return true;
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
if (hostname === rule) {
|
|
783
|
+
return true;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
function parseContentLength(headers) {
|
|
789
|
+
if (!headers)
|
|
790
|
+
return undefined;
|
|
791
|
+
if (typeof headers.get === 'function') {
|
|
792
|
+
const raw = headers.get('content-length');
|
|
793
|
+
return raw ? parseInt(raw, 10) : undefined;
|
|
794
|
+
}
|
|
795
|
+
const raw = headers['content-length'] ?? headers['Content-Length'];
|
|
796
|
+
if (raw === undefined)
|
|
797
|
+
return undefined;
|
|
798
|
+
const parsed = parseInt(Array.isArray(raw) ? raw[0] : raw, 10);
|
|
799
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
800
|
+
}
|
|
801
|
+
function wrapDownloadResponse(response, onProgress) {
|
|
802
|
+
if (!onProgress)
|
|
803
|
+
return response;
|
|
804
|
+
if (typeof Response !== 'undefined' && response instanceof Response) {
|
|
805
|
+
if (!response.body)
|
|
806
|
+
return response;
|
|
807
|
+
const total = parseContentLength(response.headers);
|
|
808
|
+
const body = createProgressStream(response.body, onProgress, {
|
|
809
|
+
total,
|
|
810
|
+
direction: 'download'
|
|
216
811
|
});
|
|
812
|
+
return new Response(body, {
|
|
813
|
+
status: response.status,
|
|
814
|
+
statusText: response.statusText,
|
|
815
|
+
headers: response.headers
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
const total = parseContentLength(response.headers);
|
|
819
|
+
const nodeBody = response.body;
|
|
820
|
+
if (!nodeBody)
|
|
821
|
+
return response;
|
|
822
|
+
const webBody = createProgressStream(nodeToWebStream(nodeBody), onProgress, {
|
|
823
|
+
total,
|
|
824
|
+
direction: 'download'
|
|
825
|
+
});
|
|
826
|
+
return new Response(webBody, {
|
|
827
|
+
status: response.statusCode,
|
|
828
|
+
statusText: String(response.statusCode),
|
|
829
|
+
headers: response.headers
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
function wrapUploadBody(body, onProgress, total) {
|
|
833
|
+
if (body instanceof FormData) {
|
|
834
|
+
return body;
|
|
835
|
+
}
|
|
836
|
+
if (!onProgress || !body)
|
|
837
|
+
return body;
|
|
838
|
+
if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {
|
|
839
|
+
return createProgressStream(body, onProgress, { total, direction: 'upload' });
|
|
840
|
+
}
|
|
841
|
+
if (typeof Blob !== 'undefined' && body instanceof Blob) {
|
|
842
|
+
return createProgressStream(body.stream(), onProgress, { total: body.size, direction: 'upload' });
|
|
843
|
+
}
|
|
844
|
+
if (isNodeReadable(body)) {
|
|
845
|
+
const webStream = nodeToWebStream(body);
|
|
846
|
+
return createProgressStream(webStream, onProgress, { total, direction: 'upload' });
|
|
217
847
|
}
|
|
848
|
+
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
|
|
849
|
+
const view = body instanceof ArrayBuffer
|
|
850
|
+
? new Uint8Array(body)
|
|
851
|
+
: new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
|
|
852
|
+
return bufferToProgressStream(view, onProgress);
|
|
853
|
+
}
|
|
854
|
+
if (typeof body === 'string') {
|
|
855
|
+
const encoder = new TextEncoder();
|
|
856
|
+
const view = encoder.encode(body);
|
|
857
|
+
return bufferToProgressStream(view, onProgress);
|
|
858
|
+
}
|
|
859
|
+
return body;
|
|
860
|
+
}
|
|
861
|
+
function bufferToProgressStream(buffer, onProgress, chunkSize = 64 * 1024) {
|
|
862
|
+
let offset = 0;
|
|
863
|
+
const total = buffer.byteLength;
|
|
864
|
+
const stream = new ReadableStream({
|
|
865
|
+
pull(controller) {
|
|
866
|
+
if (offset >= total) {
|
|
867
|
+
controller.close();
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
const end = Math.min(offset + chunkSize, total);
|
|
871
|
+
controller.enqueue(buffer.slice(offset, end));
|
|
872
|
+
offset = end;
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
return createProgressStream(stream, onProgress, { total, direction: 'upload' });
|
|
876
|
+
}
|
|
877
|
+
function isNodeReadable(obj) {
|
|
878
|
+
return obj && typeof obj.pipe === 'function' && typeof obj.on === 'function';
|
|
218
879
|
}
|