diff --git a/src/http.zig b/src/http.zig index 096bbd5a07..6a003f0c87 100644 --- a/src/http.zig +++ b/src/http.zig @@ -1310,7 +1310,7 @@ pub fn closeAndFail(this: *HTTPClient, err: anyerror, comptime is_ssl: bool, soc fn startProxyHandshake(this: *HTTPClient, comptime is_ssl: bool, socket: NewHTTPContext(is_ssl).HTTPSocket, start_payload: []const u8) void { log("startProxyHandshake", .{}); - + if (this.http_proxy) |proxy| { if (proxy.isSOCKS()) { // Start SOCKS proxy handshake diff --git a/src/http/SOCKSProxy.zig b/src/http/SOCKSProxy.zig index 52d312de84..d224e25be0 100644 --- a/src/http/SOCKSProxy.zig +++ b/src/http/SOCKSProxy.zig @@ -62,7 +62,7 @@ pub fn create(allocator: std.mem.Allocator, proxy_url: URL, destination_host: [] .destination_port = destination_port, .allocator = allocator, }); - + return socks_proxy; } @@ -74,7 +74,7 @@ pub fn sendAuthHandshake(this: *SOCKSProxy, comptime is_ssl: bool, socket: NewHT // | 1 | 1 | 1 to 255 | // +----+----------+----------+ var auth_request = [_]u8{ @intFromEnum(SOCKSVersion.v5), 1, @intFromEnum(SOCKSAuthMethod.no_auth) }; - + _ = socket.write(&auth_request); this.state = .auth_handshake; } @@ -136,45 +136,45 @@ pub fn handleData(this: *SOCKSProxy, client: *HTTPClient, data: []const u8, comp if (data.len < 2) { return error.IncompleteSOCKSResponse; } - + const version = data[0]; const method = data[1]; - + if (version != @intFromEnum(SOCKSVersion.v5)) { return error.UnsupportedSOCKSVersion; } - + if (method == @intFromEnum(SOCKSAuthMethod.no_acceptable)) { return error.SOCKSAuthenticationFailed; } - + if (method == @intFromEnum(SOCKSAuthMethod.no_auth)) { this.state = .auth_complete; try this.sendConnectRequest(is_ssl, socket); } else { return error.UnsupportedSOCKSAuthMethod; } - + return true; // Data was consumed by SOCKS handshake }, .connect_request => { if (data.len < 4) { return error.IncompleteSOCKSResponse; } - + const version = data[0]; const reply = data[1]; // data[2] is reserved const atyp = data[3]; - + if (version != @intFromEnum(SOCKSVersion.v5)) { return error.UnsupportedSOCKSVersion; } - + if (reply != @intFromEnum(SOCKSReply.succeeded)) { return error.SOCKSConnectionFailed; } - + // Parse the bound address (we don't need it, but need to skip it) var offset: usize = 4; switch (atyp) { @@ -187,22 +187,22 @@ pub fn handleData(this: *SOCKSProxy, client: *HTTPClient, data: []const u8, comp else => return error.UnsupportedSOCKSAddressType, } offset += 2; // port - + if (data.len < offset) { return error.IncompleteSOCKSResponse; } - + this.state = .connected; log("SOCKS proxy connected successfully", .{}); - + // SOCKS handshake complete, HTTP traffic can now flow through the tunnel // Don't change proxy_tunneling flag - let the normal flow handle it - + // If there's any remaining data after the SOCKS response, process it as HTTP if (data.len > offset) { return false; // Let HTTP client process remaining data } - + return true; // Data was consumed by SOCKS handshake }, .connected => { @@ -237,4 +237,4 @@ const strings = bun.strings; const NewHTTPContext = bun.http.NewHTTPContext; const HTTPClient = bun.http; const URL = bun.URL; -const log = bun.Output.scoped(.http_socks_proxy, false); \ No newline at end of file +const log = bun.Output.scoped(.http_socks_proxy, false); diff --git a/test/js/bun/http/socks-proxy-env.test.ts b/test/js/bun/http/socks-proxy-env.test.ts index 6bec60a8f6..dba8615bc3 100644 --- a/test/js/bun/http/socks-proxy-env.test.ts +++ b/test/js/bun/http/socks-proxy-env.test.ts @@ -1,5 +1,5 @@ -import { describe, test, expect, beforeAll, afterAll } from "bun:test"; import { spawn, type ChildProcess } from "bun"; +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; describe("SOCKS proxy environment variables", () => { let mockSocksServer: ChildProcess; @@ -13,7 +13,10 @@ describe("SOCKS proxy environment variables", () => { // Start a mock SOCKS5 server for testing mockSocksServer = spawn({ - cmd: ["node", "-e", ` + cmd: [ + "node", + "-e", + ` const net = require('net'); const server = net.createServer((socket) => { console.log('SOCKS connection received'); @@ -52,7 +55,8 @@ describe("SOCKS proxy environment variables", () => { server.listen(${socksPort}, () => { console.log('Mock SOCKS server listening on port ${socksPort}'); }); - `], + `, + ], stdout: "inherit", stderr: "inherit", }); @@ -80,12 +84,12 @@ describe("SOCKS proxy environment variables", () => { test("should connect through SOCKS5 proxy via http_proxy environment variable", async () => { const originalProxy = process.env.http_proxy; - + try { process.env.http_proxy = `socks5://127.0.0.1:${socksPort}`; - + const response = await fetch(`http://127.0.0.1:${httpPort}/test`); - + expect(response.status).toBe(200); expect(await response.text()).toBe("Hello from HTTP server via SOCKS"); } finally { @@ -99,12 +103,12 @@ describe("SOCKS proxy environment variables", () => { test("should connect through SOCKS5h proxy via http_proxy environment variable", async () => { const originalProxy = process.env.http_proxy; - + try { process.env.http_proxy = `socks5h://127.0.0.1:${socksPort}`; - + const response = await fetch(`http://localhost:${httpPort}/test`); - + expect(response.status).toBe(200); expect(await response.text()).toBe("Hello from HTTP server via SOCKS"); } finally { @@ -115,4 +119,4 @@ describe("SOCKS proxy environment variables", () => { } } }); -}); \ No newline at end of file +}); diff --git a/test/js/bun/http/socks-proxy.test.ts b/test/js/bun/http/socks-proxy.test.ts index a61130786b..8b9a21c946 100644 --- a/test/js/bun/http/socks-proxy.test.ts +++ b/test/js/bun/http/socks-proxy.test.ts @@ -1,5 +1,5 @@ -import { describe, test, expect, beforeAll, afterAll } from "bun:test"; import { spawn, type ChildProcess } from "bun"; +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; describe("SOCKS proxy", () => { let mockSocksServer: ChildProcess; @@ -13,7 +13,10 @@ describe("SOCKS proxy", () => { // Start a mock SOCKS5 server for testing mockSocksServer = spawn({ - cmd: ["node", "-e", ` + cmd: [ + "node", + "-e", + ` const net = require('net'); const server = net.createServer((socket) => { console.log('SOCKS connection received'); @@ -52,7 +55,8 @@ describe("SOCKS proxy", () => { server.listen(${socksPort}, () => { console.log('Mock SOCKS server listening on port ${socksPort}'); }); - `], + `, + ], stdout: "inherit", stderr: "inherit", }); @@ -90,7 +94,7 @@ describe("SOCKS proxy", () => { test("should connect through SOCKS5h proxy", async () => { const response = await fetch(`http://localhost:${httpPort}/test`, { - // @ts-ignore - This might not be typed yet + // @ts-ignore - This might not be typed yet proxy: `socks5h://127.0.0.1:${socksPort}`, }); @@ -100,12 +104,12 @@ describe("SOCKS proxy", () => { test("should handle SOCKS proxy via environment variable", async () => { const originalProxy = process.env.http_proxy; - + try { process.env.http_proxy = `socks5://127.0.0.1:${socksPort}`; - + const response = await fetch(`http://127.0.0.1:${httpPort}/test`); - + expect(response.status).toBe(200); expect(await response.text()).toBe("Hello from HTTP server"); } finally { @@ -119,7 +123,7 @@ describe("SOCKS proxy", () => { test("should handle SOCKS proxy connection failure", async () => { const invalidPort = 65000; - + const promise = fetch(`http://127.0.0.1:${httpPort}/test`, { // @ts-ignore proxy: `socks5://127.0.0.1:${invalidPort}`, @@ -127,4 +131,4 @@ describe("SOCKS proxy", () => { await expect(promise).rejects.toThrow(); }); -}); \ No newline at end of file +});