| | import { retry, type RetryOptions } from "@std/async"; |
| | import type { Config } from "./config.ts"; |
| | import { generateRandomIPv6 } from "./ipv6Rotation.ts"; |
| | import { getCurrentProxy } from "./proxyManager.ts"; |
| |
|
| | type FetchInputParameter = Parameters<typeof fetch>[0]; |
| | type FetchInitParameterWithClient = |
| | | RequestInit |
| | | RequestInit & { client: Deno.HttpClient }; |
| | type FetchReturn = ReturnType<typeof fetch>; |
| |
|
| | export const getFetchClient = (config: Config): { |
| | ( |
| | input: FetchInputParameter, |
| | init?: FetchInitParameterWithClient, |
| | ): FetchReturn; |
| | } => { |
| | return async ( |
| | input: FetchInputParameter, |
| | init?: RequestInit, |
| | ) => { |
| | |
| | const proxyAddress = config.networking.auto_proxy |
| | ? getCurrentProxy() |
| | : config.networking.proxy; |
| | const ipv6Block = config.networking.ipv6_block; |
| |
|
| | |
| | if (proxyAddress || ipv6Block) { |
| | const clientOptions: Deno.CreateHttpClientOptions = {}; |
| |
|
| | if (proxyAddress) { |
| | try { |
| | const proxyUrl = new URL(proxyAddress); |
| | |
| | if (proxyUrl.username && proxyUrl.password) { |
| | clientOptions.proxy = { |
| | url: `${proxyUrl.protocol}//${proxyUrl.host}`, |
| | basicAuth: { |
| | username: decodeURIComponent(proxyUrl.username), |
| | password: decodeURIComponent(proxyUrl.password), |
| | }, |
| | }; |
| | } else { |
| | clientOptions.proxy = { |
| | url: proxyAddress, |
| | }; |
| | } |
| | } catch { |
| | clientOptions.proxy = { |
| | url: proxyAddress, |
| | }; |
| | } |
| | } |
| |
|
| | if (ipv6Block) { |
| | clientOptions.localAddress = generateRandomIPv6(ipv6Block); |
| | } |
| |
|
| | const client = Deno.createHttpClient(clientOptions); |
| | const fetchRes = await fetchShim(config, input, { |
| | client, |
| | headers: init?.headers, |
| | method: init?.method, |
| | body: init?.body, |
| | }); |
| | client.close(); |
| | return new Response(fetchRes.body, { |
| | status: fetchRes.status, |
| | headers: fetchRes.headers, |
| | }); |
| | } |
| |
|
| | return fetchShim(config, input, init); |
| | }; |
| | }; |
| |
|
| | function fetchShim( |
| | config: Config, |
| | input: FetchInputParameter, |
| | init?: FetchInitParameterWithClient, |
| | ): FetchReturn { |
| | const fetchTimeout = config.networking.fetch?.timeout_ms; |
| | const fetchRetry = config.networking.fetch?.retry?.enabled; |
| | const fetchMaxAttempts = config.networking.fetch?.retry?.times; |
| | const fetchInitialDebounce = config.networking.fetch?.retry |
| | ?.initial_debounce; |
| | const fetchDebounceMultiplier = config.networking.fetch?.retry |
| | ?.debounce_multiplier; |
| | const retryOptions: RetryOptions = { |
| | maxAttempts: fetchMaxAttempts, |
| | minTimeout: fetchInitialDebounce, |
| | multiplier: fetchDebounceMultiplier, |
| | jitter: 0, |
| | }; |
| |
|
| | const callFetch = () => |
| | fetch(input, { |
| | |
| | signal: fetchTimeout |
| | ? AbortSignal.timeout(Number(fetchTimeout)) |
| | : null, |
| | ...(init || {}), |
| | }); |
| | |
| | return fetchRetry ? retry(callFetch, retryOptions) : callFetch(); |
| | } |
| |
|