@dividenconquer/cloudflare-proxy-fetch
Version:
A robust HTTP/HTTPS proxy client implementation for Cloudflare Workers with automatic retries and proxy rotation
94 lines • 3.72 kB
JavaScript
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
import { makeProxyRequest } from "./core/proxy-request";
import { handleRedirects } from "./core/redirect-handler";
import { isProxyConnectionError } from "./utils";
// Re-export types and utilities
export * from "./types";
export * from "./utils";
/**
* Performs an HTTP(S) request through a proxy server using Cloudflare Workers' Sockets API.
*
* This function implements a full HTTP client that works with HTTP/HTTPS proxies,
* supporting features like:
* - Proxy authentication
* - Automatic retry with proxy rotation
* - Chunked transfer encoding
* - Gzip compression
* - Detailed connection logging
* - Automatic redirect following
*
* @example
* ```typescript
* // Simple usage with a single proxy
* const response = await proxyFetch('https://api.example.com/data', {
* proxy: 'http://username:password@proxy.example.com:8080',
* method: 'POST',
* headers: {
* 'Content-Type': 'application/json'
* },
* body: JSON.stringify({ key: 'value' })
* });
*
* const data = await response.json();
*
* // Advanced usage with proxy rotation and redirects
* const response = await proxyFetch('https://api.example.com/data', {
* proxy: async (attempt) => {
* const proxies = ['proxy1', 'proxy2', 'proxy3'];
* return proxies[attempt % proxies.length];
* },
* maxRetries: 3,
* maxRedirects: 5,
* verbose: true
* });
* ```
*
* @param url - The URL to fetch
* @param options - Combined standard fetch options and proxy-specific options
* @returns A standard Response object with enhanced json() and text() methods
* @throws {Error} If all proxy attempts fail or for other network/protocol errors
*/
export async function proxyFetch(url, { proxy, verbose = false, maxRetries = 3, retryDelay = 1000, maxRedirects = 10, ...options }) {
const targetUrl = url instanceof URL ? url : new URL(url);
let lastError = null;
// Main retry loop for handling proxy failures
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
if (verbose && attempt > 1) {
console.log(`\n=== Retry Attempt ${attempt}/${maxRetries} ===`);
}
const currentUrl = new URL(targetUrl.href);
// Handle redirects if maxRedirects > 0, otherwise make single request
if (maxRedirects > 0) {
return await handleRedirects(currentUrl, proxy, attempt, options, verbose, maxRedirects);
}
else {
return await makeProxyRequest(currentUrl, proxy, attempt, options, verbose);
}
}
catch (error) {
lastError = error;
if (verbose) {
console.error(`\n=== Error on attempt ${attempt}/${maxRetries} ===`);
console.error(error);
}
// Check if the error is related to proxy blocking
if (!isProxyConnectionError(error)) {
throw error; // Rethrow non-proxy related errors
}
if (verbose) {
console.log("Proxy blocked, will retry with new proxy");
}
// If we've exhausted all retry attempts, throw the final error
if (attempt === maxRetries) {
throw new Error(`All proxy attempts failed: ${error instanceof Error ? error.message : String(error)}`);
}
// Wait before the next retry attempt
await new Promise((resolve) => setTimeout(resolve, retryDelay));
}
}
// This code is unreachable but required for TypeScript
throw lastError || new Error("Unexpected error");
}
//# sourceMappingURL=index.js.map