Skip to content

Commit 7a6d60d

Browse files
authored
fix: base64 body decoding and add logging (#5)
1 parent 0c428cc commit 7a6d60d

File tree

9 files changed

+102
-34
lines changed

9 files changed

+102
-34
lines changed

.projen/deps.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.projenrc.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ArrowParens, TrailingComma } from 'projen/lib/javascript';
44
const project = new awscdk.AwsCdkConstructLibrary({
55
author: 'rehanvdm',
66
authorAddress: '[email protected]',
7-
cdkVersion: '2.150.0',
7+
cdkVersion: '2.176.0',
88
defaultReleaseBranch: 'main',
99
jsiiVersion: '~5.7.0',
1010
name: 'tailscale-lambda-proxy',

API.md

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ export class MyStack extends cdk.Stack {
8484
// lambda: {
8585
// functionName: "tailscale-proxy",
8686
// }
87-
// }
87+
// },
88+
// debug: true, // Enable debug logging, show request + response, search for lines starting with "[tailscale-"
8889
});
8990

9091
const caller = new NodejsFunction(this, "tailscale-caller", {

package-lock.json

Lines changed: 55 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export interface TailscaleLambdaProxyProps {
2929
readonly tsHostname: string;
3030

3131
readonly options?: TailscaleLambdaProxyPropsOptions;
32+
33+
readonly debug?: boolean;
3234
}
3335

3436
export class TailscaleLambdaProxy extends Construct {
@@ -52,6 +54,7 @@ export class TailscaleLambdaProxy extends Construct {
5254
environment: {
5355
TS_SECRET_API_KEY: props.tsSecretApiKey.secretArn,
5456
TS_HOSTNAME: props.tsHostname,
57+
...(props?.debug) ? { DEBUG: 'true' } : { },
5558
...(props.options?.lambda?.nodeTlsRejectUnauthorized === false) ? { NODE_TLS_REJECT_UNAUTHORIZED: '0' } : { },
5659
},
5760
timeout: cdk.Duration.minutes(15),

src/lambda/tailscale-proxy/index.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,20 @@ const AWS_SPECIFIC_HEADERS = [
1111
'x-amz-content-sha256',
1212
];
1313

14+
export function logError(...args: any[]) {
15+
console.error('[tailscale-proxy] ERROR', ...args);
16+
}
17+
export function logInfo(...args: any[]) {
18+
console.log('[tailscale-proxy] INFO', ...args);
19+
}
20+
export function logDebug(...args: any[]) {
21+
if (process.env.DEBUG) {
22+
console.log('[tailscale-proxy] DEBUG', ...args);
23+
}
24+
}
25+
1426
export async function handler(event: APIGatewayProxyEventV2): Promise<APIGatewayProxyResultV2> {
27+
logDebug('Event:', JSON.stringify(event, null, 2));
1528
let metrics: Metrics | undefined;
1629
try {
1730
let isHttps = undefined; // Auto-detect, will be set for port 443
@@ -65,6 +78,9 @@ export async function handler(event: APIGatewayProxyEventV2): Promise<APIGateway
6578
targetHeaders['x-amz-content-sha256'] = event.headers['ts-x-amz-content-sha256'] as string;
6679
}
6780

81+
const body = event.body
82+
? (event.isBase64Encoded ? Buffer.from(event.body, 'base64').toString('utf-8') : event.body)
83+
: undefined;
6884
const response = await proxyHttpRequest({
6985
hostname: event.headers['ts-target-ip'],
7086
port: event.headers['ts-target-port'],
@@ -73,10 +89,12 @@ export async function handler(event: APIGatewayProxyEventV2): Promise<APIGateway
7389
path: event.requestContext.http.path,
7490
headers: targetHeaders,
7591
method: event.requestContext.http.method,
76-
body: event.body,
92+
body: body,
7793
});
7894

7995
metrics?.addMetric('success', MetricUnit.Count, 1);
96+
97+
logDebug('Response', JSON.stringify(response, null, 2));
8098
return response;
8199
} catch (_err) {
82100
metrics?.addMetric('error', MetricUnit.Count, 1);

src/lambda/tailscale-proxy/proxy-http-request.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as http from 'http';
22
import * as https from 'https';
33
import { APIGatewayProxyResultV2 } from 'aws-lambda';
44
import { SocksProxyAgent } from 'socks-proxy-agent';
5+
import { logDebug, logInfo, logError } from './index';
56

67
export async function proxyHttpRequest(
78
target: Pick<http.RequestOptions, 'hostname' | 'port'>,
@@ -14,6 +15,7 @@ export async function proxyHttpRequest(
1415
},
1516
): Promise<APIGatewayProxyResultV2> {
1617
async function requestPromise(): Promise<APIGatewayProxyResultV2> {
18+
logDebug('proxyHttpRequest', JSON.stringify({ target, isHttps, request }, null, 2));
1719
const socksProxyAgent = new SocksProxyAgent('socks://localhost:1055');
1820
return new Promise((resolve, reject) => {
1921
const chunks: Buffer[] = [];
@@ -32,6 +34,7 @@ export async function proxyHttpRequest(
3234
});
3335
res.on('end', () => {
3436
const responseBody = Buffer.concat(chunks);
37+
logDebug('requestPromise.end responseBody', responseBody);
3538
resolve({
3639
statusCode: res.statusCode || 500,
3740
headers: res.headers as Record<string, string>,
@@ -40,13 +43,13 @@ export async function proxyHttpRequest(
4043
});
4144
});
4245
res.on('error', (error: Error): void => {
43-
console.error('Error receiving response:', error);
46+
logError('Error receiving response:', error);
4447
reject(error);
4548
});
4649
});
4750

4851
apiRequest.on('error', (error: Error): void => {
49-
console.error('Error sending request:', error);
52+
logError('Error sending request:', error);
5053
reject(error);
5154
});
5255

@@ -68,8 +71,8 @@ export async function proxyHttpRequest(
6871
success = true;
6972
} catch (error) {
7073
if (error == 'Error: Socks5 proxy rejected connection - Failure' && attempt < connectionRetryDelays.length) {
71-
console.error('Error: Socks5 proxy rejected connection - Failure');
72-
console.log('Retrying in', connectionRetryDelays[attempt], 'ms');
74+
logError('Error: Socks5 proxy rejected connection - Failure');
75+
logInfo('Retrying in', connectionRetryDelays[attempt], 'ms');
7376
await new Promise((resolve) => setTimeout(resolve, connectionRetryDelays[attempt]));
7477
attempt++;
7578
} else {
@@ -79,7 +82,7 @@ export async function proxyHttpRequest(
7982
} while (!success && attempt < connectionRetryDelays.length);
8083

8184
if (attempt > 0) {
82-
console.log('Error: Socks5 proxy rejected connection - Failure - RESOLVED - attempt:', attempt, 'total delay time:', connectionRetryDelays.slice(0, attempt).reduce((a, b) => a + b, 0));
85+
logInfo('Error: Socks5 proxy rejected connection - Failure - RESOLVED - attempt:', attempt, 'total delay time:', connectionRetryDelays.slice(0, attempt).reduce((a, b) => a + b, 0));
8386
}
8487

8588
return response!;

0 commit comments

Comments
 (0)