Skip to content

Commit 329eb67

Browse files
authored
Merge pull request #2 from rehanvdm/feature/fix-proxy-error
fix: socks5 proxy rejected connection - failure
2 parents 45a3a24 + 33aeb3f commit 329eb67

File tree

1 file changed

+66
-38
lines changed

1 file changed

+66
-38
lines changed

src/lambda/tailscale-proxy/index.ts

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
import { SocksProxyAgent } from 'socks-proxy-agent';
88

99
async function proxyHttpRequest(
10-
target: Pick<http.RequestOptions, 'hostname' | 'port' | 'agent'>,
10+
target: Pick<http.RequestOptions, 'hostname' | 'port'>,
1111
isHttps: boolean | undefined,
1212
request: {
1313
path: string;
@@ -16,53 +16,83 @@ async function proxyHttpRequest(
1616
body: string | undefined;
1717
},
1818
): Promise<APIGatewayProxyResultV2> {
19-
return new Promise((resolve, reject) => {
20-
const chunks: Buffer[] = [];
21-
const httpLib = isHttps == undefined ?
22-
(target.port == 443 ? https : http) :
23-
(isHttps ? https : http);
24-
const apiRequest = httpLib.request({
25-
...target,
26-
path: request.path,
27-
method: request.method,
28-
headers: request.headers,
29-
}, (res: http.IncomingMessage) => {
30-
res.on('data', (chunk: Buffer) => {
31-
chunks.push(chunk);
32-
});
33-
res.on('end', () => {
34-
const responseBody = Buffer.concat(chunks);
35-
resolve({
36-
statusCode: res.statusCode || 500,
37-
headers: res.headers as Record<string, string>,
38-
body: responseBody.toString('base64'),
39-
isBase64Encoded: true,
19+
20+
async function requestPromise(): Promise<APIGatewayProxyResultV2> {
21+
const socksProxyAgent = new SocksProxyAgent('socks://localhost:1055');
22+
return new Promise((resolve, reject) => {
23+
const chunks: Buffer[] = [];
24+
const httpLib = isHttps == undefined ?
25+
(target.port == 443 ? https : http) :
26+
(isHttps ? https : http);
27+
const apiRequest = httpLib.request({
28+
...target,
29+
agent: socksProxyAgent,
30+
path: request.path,
31+
method: request.method,
32+
headers: request.headers,
33+
}, (res: http.IncomingMessage) => {
34+
res.on('data', (chunk: Buffer) => {
35+
chunks.push(chunk);
36+
});
37+
res.on('end', () => {
38+
const responseBody = Buffer.concat(chunks);
39+
resolve({
40+
statusCode: res.statusCode || 500,
41+
headers: res.headers as Record<string, string>,
42+
body: responseBody.toString('base64'),
43+
isBase64Encoded: true,
44+
});
45+
});
46+
res.on('error', (error: Error): void => {
47+
console.error('Error receiving response:', error);
48+
reject(error);
4049
});
4150
});
42-
res.on('error', (error: Error): void => {
43-
console.error('Error receiving response:', error);
51+
52+
apiRequest.on('error', (error: Error): void => {
53+
console.error('Error sending request:', error);
4454
reject(error);
4555
});
46-
});
4756

48-
apiRequest.on('error', (error: Error): void => {
49-
console.error('Error sending request:', error);
50-
reject(error);
57+
if (request.body != null) {
58+
apiRequest.write(request.body);
59+
}
60+
apiRequest.end();
5161
});
62+
}
63+
5264

53-
if (request.body != null) {
54-
apiRequest.write(request.body);
65+
const connectionRetryDelays = [10, 50, 100, 500, 1000, 2000, 3000];
66+
let attempt = 0;
67+
let success = false;
68+
let response: APIGatewayProxyResultV2;
69+
70+
do {
71+
try {
72+
response = await requestPromise();
73+
success = true;
74+
} catch (error) {
75+
if (error == 'Error: Socks5 proxy rejected connection - Failure' && attempt < connectionRetryDelays.length) {
76+
console.error('Error: Socks5 proxy rejected connection - Failure');
77+
console.log('Retrying in', connectionRetryDelays[attempt], 'ms');
78+
await new Promise((resolve) => setTimeout(resolve, connectionRetryDelays[attempt]));
79+
attempt++;
80+
} else {
81+
throw error;
82+
}
5583
}
56-
apiRequest.end();
57-
});
84+
} while (!success && attempt < connectionRetryDelays.length);
85+
86+
if (attempt > 0) {
87+
console.log('Error: Socks5 proxy rejected connection - Failure - RESOLVED - attempt:', attempt, 'total delay time:', connectionRetryDelays.slice(0, attempt).reduce((a, b) => a + b, 0));
88+
}
89+
90+
return response!;
5891
}
5992

6093
export async function handler(event: APIGatewayProxyEventV2): Promise<APIGatewayProxyResultV2> {
61-
6294
let metrics: Metrics | undefined;
6395
try {
64-
const socksProxyAgent = new SocksProxyAgent('socks://localhost:1055');
65-
6696
let isHttps = undefined; // Auto-detect, will be set for port 443
6797
if (!event.headers['ts-target-ip']) {
6898
return {
@@ -102,15 +132,13 @@ export async function handler(event: APIGatewayProxyEventV2): Promise<APIGateway
102132
const response = await proxyHttpRequest({
103133
hostname: event.headers['ts-target-ip'],
104134
port: event.headers['ts-target-port'],
105-
agent: socksProxyAgent,
106135
}, isHttps,
107136
{
108137
path: event.requestContext.http.path,
109138
headers: targetHeaders,
110139
method: event.requestContext.http.method,
111140
body: event.body,
112-
},
113-
);
141+
});
114142

115143
metrics?.addMetric('success', MetricUnit.Count, 1);
116144
return response;

0 commit comments

Comments
 (0)