Don't proxy certain headers across the Yalt boundary as it can mess-up proxied local requests

This commit is contained in:
Eric Allam
2023-12-18 10:52:31 +00:00
parent c01138b56e
commit dfa47b6201
7 changed files with 101 additions and 28 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@trigger.dev/yalt": patch
"@trigger.dev/cli": patch
---
Don't proxy certain headers across the Yalt boundary as it can mess-up proxied local requests
+1 -1
View File
@@ -9,7 +9,7 @@
},
"devDependencies": {
"@cloudflare/workers-types": "~4.20231121.0",
"wrangler": "^3.20.0"
"wrangler": "^3.21.0"
},
"dependencies": {
"@trigger.dev/yalt": "workspace:*"
+3 -2
View File
@@ -51,8 +51,9 @@ type RouteDecision =
| { type: 'not_found' };
function routeUrl(url: URL, env: Env): RouteDecision {
if (url.host.startsWith('localhost:')) {
const searchParams = new URLSearchParams(url.search);
const searchParams = new URLSearchParams(url.search);
if (searchParams.has('t')) {
const name = searchParams.get('t');
if (name) {
+10 -5
View File
@@ -516,11 +516,16 @@ async function createNativeTunnel(
// import WS dynamically
const WebSocket = await import("ws");
yaltTunnel = new YaltTunnel(response.url, `${hostname}:${port}`, {
WebSocket: WebSocket.default,
connectionTimeout: 1000,
maxRetries: 10,
});
yaltTunnel = new YaltTunnel(
response.url,
`${hostname}:${port}`,
{
WebSocket: WebSocket.default,
connectionTimeout: 1000,
maxRetries: 10,
},
{ verbose: process.env.TUNNEL_VERBOSE === "1" }
);
await yaltTunnel.connect();
+4 -3
View File
@@ -31,18 +31,19 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"zod": "3.22.3",
"partysocket": "^0.0.17"
"partysocket": "^0.0.17",
"zod": "3.22.3"
},
"devDependencies": {
"@trigger.dev/tsconfig": "workspace:*",
"@trigger.dev/tsup": "workspace:*",
"@types/debug": "^4.1.7",
"@types/jest": "^29.5.3",
"@types/node": "18",
"jest": "^29.6.2",
"rimraf": "^3.0.2",
"ts-jest": "^29.1.1",
"tsup": "^8.0.1",
"@trigger.dev/tsup": "workspace:*",
"typescript": "^5.3.0"
},
"engines": {
+70 -12
View File
@@ -36,7 +36,7 @@ export async function createRequestMessage(id: string, request: Request): Promis
return {
type: "request",
id,
headers: Object.fromEntries(headers),
headers: stripHeaders(Object.fromEntries(headers)),
method,
url,
body,
@@ -86,26 +86,48 @@ export class YaltApiClient {
}
}
export type YaltTunnelOptions = {
export type YaltTunnelSocketOptions = {
WebSocket?: any;
connectionTimeout?: number;
maxRetries?: number;
};
export type YaltTunnelOptions = {
verbose?: boolean;
};
export class YaltTunnel {
socket?: WebSocket;
constructor(
private url: string,
private address: string,
private socketOptions: YaltTunnelSocketOptions = {},
private options: YaltTunnelOptions = {}
) {}
private log(message: string, properties: Record<string, any> = {}) {
if (this.options.verbose) {
console.log(JSON.stringify({ message, ...properties }));
}
}
async connect() {
this.socket = new WebSocket(`wss://${this.url}/connect`, [], this.options);
this.log("Connecting to tunnel", {
url: this.url,
address: this.address,
socketOptions: this.socketOptions,
});
this.socket.addEventListener("open", () => {});
this.socket = new WebSocket(`wss://${this.url}/connect`, [], this.socketOptions);
this.socket.addEventListener("close", (event) => {});
this.socket.addEventListener("open", (args) => {
this.log("Connected to tunnel");
});
this.socket.addEventListener("close", (event) => {
this.log("Disconnected from tunnel", { event: event.code, reason: event.reason });
});
this.socket.addEventListener("message", async (event) => {
const data = JSON.parse(
@@ -115,7 +137,7 @@ export class YaltTunnel {
const message = ServerMessages.safeParse(data);
if (!message.success) {
console.error(message.error);
this.log("Received invalid message", { data });
return;
}
@@ -132,7 +154,7 @@ export class YaltTunnel {
});
this.socket.addEventListener("error", (event) => {
console.error(event);
this.log("Socket error", { error: event.message });
});
}
@@ -147,13 +169,31 @@ export class YaltTunnel {
let response: Response | null = null;
this.log("Sending local request", {
originalUrl: originalUrl.href,
requestId: request.id,
headers: request.headers,
});
try {
response = await fetch(originalUrl.href, {
method: request.method,
headers: request.headers,
headers: stripHeaders(request.headers),
body: request.body,
});
} catch (error) {
if (error instanceof Error) {
this.log("Error sending local request", {
error: error.message,
name: error.name,
stack: error.stack,
requestId: request.id,
cause: "cause" in error ? error.cause : undefined,
});
} else {
this.log("Error sending local request", { error, requestId: request.id });
}
// Return a 502 response
response = new Response(
JSON.stringify({
@@ -164,15 +204,33 @@ export class YaltTunnel {
}
try {
await sendResponse(request.id, response, this.socket);
await this.sendResponse(request.id, response, this.socket);
} catch (error) {
console.error(error);
}
}
private async sendResponse(id: string, response: Response, socket: WebSocket) {
const message = await createResponseMessage(id, response);
this.log("Sending response", { requestId: id, status: response.status });
return socket.send(JSON.stringify(message));
}
}
async function sendResponse(id: string, response: Response, socket: WebSocket) {
const message = await createResponseMessage(id, response);
// Remove headers that should not be included like connection, host, etc
function stripHeaders(headers: Record<string, string>) {
const blacklistHeaders = [
"connection",
"cf-ray",
"cf-connecting-ip",
"host",
"cf-ipcountry",
"content-length",
];
return socket.send(JSON.stringify(message));
return Object.fromEntries(
Object.entries(headers).filter(([key]) => !blacklistHeaders.includes(key.toLowerCase()))
);
}
+7 -5
View File
@@ -399,12 +399,12 @@ importers:
specifiers:
'@cloudflare/workers-types': ~4.20231121.0
'@trigger.dev/yalt': workspace:*
wrangler: ^3.20.0
wrangler: ^3.21.0
dependencies:
'@trigger.dev/yalt': link:../../packages/yalt
devDependencies:
'@cloudflare/workers-types': 4.20231121.0
wrangler: 3.20.0
wrangler: 3.21.0
config-packages/eslint-config-custom:
specifiers:
@@ -1312,6 +1312,7 @@ importers:
specifiers:
'@trigger.dev/tsconfig': workspace:*
'@trigger.dev/tsup': workspace:*
'@types/debug': ^4.1.7
'@types/jest': ^29.5.3
'@types/node': '18'
jest: ^29.6.2
@@ -1327,6 +1328,7 @@ importers:
devDependencies:
'@trigger.dev/tsconfig': link:../../config-packages/tsconfig
'@trigger.dev/tsup': link:../../config-packages/tsup
'@types/debug': 4.1.7
'@types/jest': 29.5.3
'@types/node': 18.17.1
jest: 29.6.2_@types+node@18.17.1
@@ -33143,12 +33145,13 @@ packages:
- utf-8-validate
dev: true
/wrangler/3.20.0:
resolution: {integrity: sha512-7mg25zJByhBmrfG+CbImSid7JNd5lxGovLA167ndtE8Yrqd3TUukrGWL8o0RCQIm0FUcgl2nCzWArJDShlZVKA==}
/wrangler/3.21.0:
resolution: {integrity: sha512-DLoo4XfjeyuGRAVWZFHmU1jWnZIfyLGDm6Ika9oy/CLCPfJzVJvf2jI70EU5BlEHWDZXMSJKw7FDdgSqwhaQXg==}
engines: {node: '>=16.17.0'}
hasBin: true
dependencies:
'@cloudflare/kv-asset-handler': 0.2.0
'@cspotcode/source-map-support': 0.8.1
'@esbuild-plugins/node-globals-polyfill': 0.2.3_esbuild@0.17.19
'@esbuild-plugins/node-modules-polyfill': 0.2.2_esbuild@0.17.19
blake3-wasm: 2.1.5
@@ -33160,7 +33163,6 @@ packages:
resolve.exports: 2.0.2
selfsigned: 2.4.1
source-map: 0.6.1
source-map-support: 0.5.21
xxhash-wasm: 1.0.2
optionalDependencies:
fsevents: 2.3.3