diff --git a/src/lambda/tailscale-proxy/index.ts b/src/lambda/tailscale-proxy/index.ts index 1261f17..782cd01 100644 --- a/src/lambda/tailscale-proxy/index.ts +++ b/src/lambda/tailscale-proxy/index.ts @@ -7,7 +7,7 @@ import { import { SocksProxyAgent } from 'socks-proxy-agent'; async function proxyHttpRequest( - target: Pick, + target: Pick, isHttps: boolean | undefined, request: { path: string; @@ -16,53 +16,83 @@ async function proxyHttpRequest( body: string | undefined; }, ): Promise { - return new Promise((resolve, reject) => { - const chunks: Buffer[] = []; - const httpLib = isHttps == undefined ? - (target.port == 443 ? https : http) : - (isHttps ? https : http); - const apiRequest = httpLib.request({ - ...target, - path: request.path, - method: request.method, - headers: request.headers, - }, (res: http.IncomingMessage) => { - res.on('data', (chunk: Buffer) => { - chunks.push(chunk); - }); - res.on('end', () => { - const responseBody = Buffer.concat(chunks); - resolve({ - statusCode: res.statusCode || 500, - headers: res.headers as Record, - body: responseBody.toString('base64'), - isBase64Encoded: true, + + async function requestPromise(): Promise { + const socksProxyAgent = new SocksProxyAgent('socks://localhost:1055'); + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + const httpLib = isHttps == undefined ? + (target.port == 443 ? https : http) : + (isHttps ? https : http); + const apiRequest = httpLib.request({ + ...target, + agent: socksProxyAgent, + path: request.path, + method: request.method, + headers: request.headers, + }, (res: http.IncomingMessage) => { + res.on('data', (chunk: Buffer) => { + chunks.push(chunk); + }); + res.on('end', () => { + const responseBody = Buffer.concat(chunks); + resolve({ + statusCode: res.statusCode || 500, + headers: res.headers as Record, + body: responseBody.toString('base64'), + isBase64Encoded: true, + }); + }); + res.on('error', (error: Error): void => { + console.error('Error receiving response:', error); + reject(error); }); }); - res.on('error', (error: Error): void => { - console.error('Error receiving response:', error); + + apiRequest.on('error', (error: Error): void => { + console.error('Error sending request:', error); reject(error); }); - }); - apiRequest.on('error', (error: Error): void => { - console.error('Error sending request:', error); - reject(error); + if (request.body != null) { + apiRequest.write(request.body); + } + apiRequest.end(); }); + } + - if (request.body != null) { - apiRequest.write(request.body); + const connectionRetryDelays = [10, 50, 100, 500, 1000, 2000, 3000]; + let attempt = 0; + let success = false; + let response: APIGatewayProxyResultV2; + + do { + try { + response = await requestPromise(); + success = true; + } catch (error) { + if (error == 'Error: Socks5 proxy rejected connection - Failure' && attempt < connectionRetryDelays.length) { + console.error('Error: Socks5 proxy rejected connection - Failure'); + console.log('Retrying in', connectionRetryDelays[attempt], 'ms'); + await new Promise((resolve) => setTimeout(resolve, connectionRetryDelays[attempt])); + attempt++; + } else { + throw error; + } } - apiRequest.end(); - }); + } while (!success && attempt < connectionRetryDelays.length); + + if (attempt > 0) { + console.log('Error: Socks5 proxy rejected connection - Failure - RESOLVED - attempt:', attempt, 'total delay time:', connectionRetryDelays.slice(0, attempt).reduce((a, b) => a + b, 0)); + } + + return response!; } export async function handler(event: APIGatewayProxyEventV2): Promise { - let metrics: Metrics | undefined; try { - const socksProxyAgent = new SocksProxyAgent('socks://localhost:1055'); - let isHttps = undefined; // Auto-detect, will be set for port 443 if (!event.headers['ts-target-ip']) { return { @@ -102,15 +132,13 @@ export async function handler(event: APIGatewayProxyEventV2): Promise