fix: try direct connection first, fall back to proxy
Change proxy strategy: first attempt goes direct (no proxy). Only if direct fails, fall back to proxy pool with rotation. This way healthy upstreams don't pay the proxy latency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
03427cae8c
commit
c61f8d9db6
+38
-30
@@ -352,44 +352,52 @@ async function handleRelay(
|
|||||||
|
|
||||||
const targetUrlString = targetUrl.toString();
|
const targetUrlString = targetUrl.toString();
|
||||||
|
|
||||||
// ── Attach proxy (if pool is loaded) ────────────────────────────
|
// ── Execute upstream fetch ──────────────────────────────────────
|
||||||
const proxyUrl = proxyPool.getProxyUrl();
|
// Strategy: direct first → proxy on failure → rotate on failure
|
||||||
if (proxyUrl) fetchOptions.proxy = proxyUrl;
|
let response: Response | undefined;
|
||||||
|
let usedProxy = false;
|
||||||
|
|
||||||
// ── Execute upstream fetch (with proxy retry on failure) ─────────
|
for (let attempts = 0; attempts < 3; attempts++) {
|
||||||
let response: Response;
|
// Clear proxy on first attempt (direct)
|
||||||
let retried = false;
|
if (attempts === 0) {
|
||||||
|
delete fetchOptions.proxy;
|
||||||
|
} else if (attempts === 1 && proxyPool.size > 0) {
|
||||||
|
// Second attempt: use first proxy
|
||||||
|
usedProxy = true;
|
||||||
|
fetchOptions.proxy = proxyPool.getProxyUrl()!;
|
||||||
|
} else if (attempts === 2 && proxyPool.size > 0) {
|
||||||
|
// Third attempt: rotate to next proxy
|
||||||
|
const next = proxyPool.markFailed();
|
||||||
|
if (!next) break;
|
||||||
|
fetchOptions.proxy = proxyPool.getProxyUrl()!;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
try {
|
try {
|
||||||
response = await fetch(targetUrlString, fetchOptions);
|
response = await fetch(targetUrlString, fetchOptions);
|
||||||
|
if (usedProxy) proxyPool.markSuccess();
|
||||||
break;
|
break;
|
||||||
} catch (err) {
|
} catch {
|
||||||
// Rotate proxy on network failure and retry once
|
// Fall through to next attempt
|
||||||
if (proxyPool.size > 0 && !retried) {
|
|
||||||
retried = true;
|
|
||||||
const next = proxyPool.markFailed();
|
|
||||||
if (next) {
|
|
||||||
fetchOptions.proxy = proxyPool.getProxyUrl()!;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const classified = classifyFetchError(err);
|
|
||||||
logRelayEvent({
|
|
||||||
method,
|
|
||||||
url: requestUrl,
|
|
||||||
status: classified.status,
|
|
||||||
durationMs: Math.round(performance.now() - startTime),
|
|
||||||
error: classified.message,
|
|
||||||
targetUrl: targetUrlString,
|
|
||||||
ip: clientIP,
|
|
||||||
});
|
|
||||||
return createErrorResponse(classified);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proxyUrl) proxyPool.markSuccess();
|
// All attempts failed — classify the last error
|
||||||
|
if (!response) {
|
||||||
|
const lastErr = new Error("All connection attempts failed");
|
||||||
|
const classified = classifyFetchError(lastErr);
|
||||||
|
logRelayEvent({
|
||||||
|
method,
|
||||||
|
url: requestUrl,
|
||||||
|
status: classified.status,
|
||||||
|
durationMs: Math.round(performance.now() - startTime),
|
||||||
|
error: classified.message,
|
||||||
|
targetUrl: targetUrlString,
|
||||||
|
ip: clientIP,
|
||||||
|
});
|
||||||
|
return createErrorResponse(classified);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Build relay response ─────────────────────────────────────────
|
// ── Build relay response ─────────────────────────────────────────
|
||||||
const relayedResponse = createRelayResponse(response);
|
const relayedResponse = createRelayResponse(response);
|
||||||
|
|||||||
Reference in New Issue
Block a user