Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
5bada3a669 fix: handle CONNECT method with authority-format path in node:http
Fixes #15499

The HTTP CONNECT method uses an authority-format path (host:port) instead
of a regular URL path. Previously, the code would concatenate the protocol,
host, port, and path together, resulting in invalid URLs like:
"http://localhost:9999example.com:443"

This caused fetch() to throw "ERR_INVALID_URL: fetch() URL is invalid"
when using libraries like https-proxy-agent and socks-proxy-agent.

The fix checks if the method is CONNECT and constructs the URL correctly
by using the path directly as the authority portion, since it's already
in "host:port" format.

Test plan:
- Added regression test in test/regression/issue/15499.test.ts
- Verified test fails with USE_SYSTEM_BUN=1
- Verified test passes with bun bd test
- Verified existing proxy tests still pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 12:02:15 +00:00
2 changed files with 67 additions and 1 deletions

View File

@@ -286,7 +286,12 @@ function ClientRequest(input, options, cb) {
return [path, `${protocol}//${host}${this[kUseDefaultPort] ? "" : ":" + this[kPort]}`];
} else {
let proxy: string | undefined;
const url = `${protocol}//${host}${this[kUseDefaultPort] ? "" : ":" + this[kPort]}${path}`;
// For CONNECT method, the path is in authority format (host:port), not a URL path
// We need to use a dummy URL for the fetch call since CONNECT establishes a tunnel
const url =
method === "CONNECT"
? `${protocol}//${path}` // path is already "host:port" for CONNECT
: `${protocol}//${host}${this[kUseDefaultPort] ? "" : ":" + this[kPort]}${path}`;
// support agent proxy url/string for http/https
try {
// getters can throw

View File

@@ -0,0 +1,61 @@
// https://github.com/oven-sh/bun/issues/15499
// https-proxy-agent and socks-proxy-agent not working
import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";
test("CONNECT method with authority-format path should not throw invalid URL error (issue #15499)", async () => {
// Test that CONNECT method with path="host:port" format doesn't throw "fetch() URL is invalid"
// This is a minimal test to verify the core issue is fixed
using dir = tempDir("issue-15499", {
"test.js": `
import https from 'node:https';
// Create a CONNECT request with authority-format path (host:port)
// This should not throw "fetch() URL is invalid" error
const req = https.request({
host: 'localhost',
port: 9999, // dummy port
method: 'CONNECT',
path: 'example.com:443', // Authority format - this was causing the bug
timeout: 100,
});
req.on('connect', (res, socket, head) => {
console.log('Connected successfully');
socket.end();
});
req.on('error', (err) => {
// We expect ECONNREFUSED since port 9999 isn't listening
// But we should NOT get ERR_INVALID_URL or "fetch() URL is invalid"
console.log('Error code:', err.code);
console.log('Error message:', err.message);
});
req.end();
// Keep process alive briefly
setTimeout(() => {}, 200);
`,
});
await using proc = Bun.spawn({
cmd: [bunExe(), "test.js"],
cwd: String(dir),
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
// The key assertion: should not get "fetch() URL is invalid" error
// Before the fix, this would throw: ERR_INVALID_URL: fetch() URL is invalid
// because the URL was constructed as "http://localhost:9999example.com:443" (invalid)
// After the fix, CONNECT method paths are handled correctly as authority format
expect(stderr).not.toContain("fetch() URL is invalid");
expect(stderr).not.toContain("ERR_INVALID_URL");
expect(stdout).not.toContain("ERR_INVALID_URL");
expect(exitCode).toBe(0);
});