Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/tunnel/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,10 @@ export class TunnelClient {
duplex: hasBody ? "half" : undefined,
});

const response = await fetch(request);
// Don't follow redirects - pass them back to the browser
// Following redirects can cause TLS errors when the redirect target
// has different host requirements
const response = await fetch(request, { redirect: "manual" });

// Send response headers
const headers: Record<string, string> = {};
Expand Down
3 changes: 3 additions & 0 deletions packages/tunnel/src/server/cloudflare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,14 @@ async function handleProxyRequest(
const headers = new Headers(request.headers);
headers.set("x-tunnel-proxy-url", proxyUrl.toString());

// Use redirect: "manual" to prevent wrangler/workerd from following redirects
// The redirect response should be passed through to the client
return session.fetch(
new Request("https://tunnel/proxy", {
method: request.method,
headers,
body: request.body,
redirect: "manual",
})
);
}
Expand Down
40 changes: 40 additions & 0 deletions packages/tunnel/src/shared.test-suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,46 @@ export function runSharedTests(
// Should return 502 Bad Gateway for errors
assert.strictEqual(response.status, 502);
});

it("should handle redirect responses with Location header (no body)", async () => {
const redirectUrl = "https://example.com/redirected";
mockServer.setHandler((_req, res) => {
// Simulate a redirect like Hono's c.redirect() - status 302 with Location header
res.writeHead(302, { Location: redirectUrl });
res.end(); // No body
});

using _disposable = await connectClient({ secret: "redirect-test" });

const tunnelId = await getTunnelId("redirect-test", serverSecret);

// Use AbortController to timeout the request if it hangs
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);

try {
const response = await fetch(
getTunnelUrl(server, tunnelId, "/callback"),
{
redirect: "manual", // Don't follow redirects, we want to inspect the response
signal: controller.signal,
}
);

clearTimeout(timeout);

assert.strictEqual(response.status, 302);
assert.strictEqual(response.headers.get("Location"), redirectUrl);
} catch (err) {
clearTimeout(timeout);
if ((err as Error).name === "AbortError") {
throw new Error(
"Redirect response timed out - body stream may not be closing properly"
);
}
throw err;
}
});
});

describe("websocket proxying", () => {
Expand Down
28 changes: 8 additions & 20 deletions packages/tunnel/wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
# Example wrangler.toml for deploying the tunnel server.
#
# To deploy:
# 1. Copy this file to your project
# 2. Update the configuration values
# 3. Run: wrangler deploy

name = "tunnel-server"
main = "src/server/cloudflare.ts"
compatibility_date = "2025-01-01"

# For wildcard subdomains, configure routes like:
# routes = [
# { pattern = "*.tunnel.example.com/*", zone_name = "example.com" },
# { pattern = "tunnel.example.com/*", zone_name = "example.com" }
# ]
#
# For subpath mode, a single route suffices:
# routes = [
# { pattern = "example.com/tunnel/*", zone_name = "example.com" }
# ]
routes = [
{ pattern = "try.blink.host/*", zone_name = "blink.host" },
{ pattern = "*.try.blink.host/*", zone_name = "blink.host" },
]
workers_dev = true
preview_urls = true

[vars]
# Server secret for HMAC signing. Change this!
TUNNEL_SECRET = "change-me-to-a-secure-random-string"
# TUNNEL_SECRET = "change-me-to-a-secure-random-string"

# Base URL for generating public URLs
# For wildcard mode: https://tunnel.example.com
# For subpath mode: https://example.com
TUNNEL_BASE_URL = "https://tunnel.example.com"
TUNNEL_BASE_URL = "https://try.blink.host"

# Routing mode: "wildcard" or "subpath"
TUNNEL_MODE = "wildcard"
Expand Down
Loading