feat(websocket): add HTTP/HTTPS proxy support (#25614)

## Summary

Add `proxy` option to WebSocket constructor for connecting through HTTP
CONNECT proxies.

### Features
- Support for `ws://` and `wss://` through HTTP proxies
- Support for `ws://` and `wss://` through HTTPS proxies (with
`rejectUnauthorized: false`)
- Proxy authentication via URL credentials (Basic auth)
- Custom proxy headers support
- Full TLS options (`ca`, `cert`, `key`, etc.) for target connections
using `SSLConfig.fromJS`

### API

```javascript
// String format
new WebSocket("wss://example.com", { proxy: "http://proxy:8080" })

// With credentials
new WebSocket("wss://example.com", { proxy: "http://user:pass@proxy:8080" })

// Object format with custom headers
new WebSocket("wss://example.com", {
  proxy: { url: "http://proxy:8080", headers: { "X-Custom": "value" } }
})

// HTTPS proxy
new WebSocket("ws://example.com", {
  proxy: "https://proxy:8443",
  tls: { rejectUnauthorized: false }
})
```

### Implementation

| File | Changes |
|------|---------|
| `WebSocketUpgradeClient.zig` | Proxy state machine and CONNECT
handling |
| `WebSocketProxyTunnel.zig` | **New** - TLS tunnel inside CONNECT for
wss:// through HTTP proxy |
| `JSWebSocket.cpp` | Parse proxy option and TLS options using
`SSLConfig.fromJS` |
| `WebSocket.cpp` | Pass proxy parameters to Zig, handle HTTPS proxy
socket selection |
| `bun.d.ts` | Add `proxy` and full TLS options to WebSocket types |

### Supported Scenarios

| Scenario | Status |
|----------|--------|
| ws:// through HTTP proxy |  Working |
| wss:// through HTTP proxy |  Working (TLS tunnel) |
| ws:// through HTTPS proxy |  Working (with `rejectUnauthorized:
false`) |
| wss:// through HTTPS proxy |  Working (with `rejectUnauthorized:
false`) |
| Proxy authentication (Basic) |  Working |
| Custom proxy headers |  Working |
| Custom CA for HTTPS proxy |   Working |

## Test plan

- [x] API tests verify proxy option is accepted in various formats
- [x] Functional tests with local HTTP CONNECT proxy server
- [x] Proxy authentication tests (Basic auth)
- [x] HTTPS proxy tests with `rejectUnauthorized: false`
- [x] Error handling tests (auth failures, wrong credentials)

Run tests: `bun test test/js/web/websocket/websocket-proxy.test.ts`

## Changelog

- Added `proxy` option to `WebSocket` constructor for HTTP/HTTPS proxy
support
- Added full TLS options (`ca`, `cert`, `key`, `passphrase`, etc.) to
`WebSocket` constructor

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Ciro Spaciari
2026-01-08 16:21:34 -08:00
committed by GitHub
parent 24b97994e3
commit c90c0e69cb
29 changed files with 3635 additions and 170 deletions

View File

@@ -3290,16 +3290,29 @@ declare module "bun" {
type WebSocketOptionsTLS = {
/**
* Options for the TLS connection
* Options for the TLS connection.
*
* Supports full TLS configuration including custom CA certificates,
* client certificates, and other TLS settings (same as fetch).
*
* @example
* ```ts
* // Using BunFile for certificates
* const ws = new WebSocket("wss://example.com", {
* tls: {
* ca: Bun.file("./ca.pem")
* }
* });
*
* // Using Buffer
* const ws = new WebSocket("wss://example.com", {
* tls: {
* ca: fs.readFileSync("./ca.pem")
* }
* });
* ```
*/
tls?: {
/**
* Whether to reject the connection if the certificate is not valid
*
* @default true
*/
rejectUnauthorized?: boolean;
};
tls?: TLSOptions;
};
type WebSocketOptionsHeaders = {
@@ -3309,10 +3322,57 @@ declare module "bun" {
headers?: import("node:http").OutgoingHttpHeaders;
};
type WebSocketOptionsProxy = {
/**
* HTTP proxy to use for the WebSocket connection.
*
* Can be a string URL or an object with `url` and optional `headers`.
*
* @example
* ```ts
* // String format
* const ws = new WebSocket("wss://example.com", {
* proxy: "http://proxy.example.com:8080"
* });
*
* // With credentials
* const ws = new WebSocket("wss://example.com", {
* proxy: "http://user:pass@proxy.example.com:8080"
* });
*
* // Object format with custom headers
* const ws = new WebSocket("wss://example.com", {
* proxy: {
* url: "http://proxy.example.com:8080",
* headers: {
* "Proxy-Authorization": "Bearer token"
* }
* }
* });
* ```
*/
proxy?:
| string
| {
/**
* The proxy URL (http:// or https://)
*/
url: string;
/**
* Custom headers to send to the proxy server.
* Supports plain objects or Headers class instances.
*/
headers?: import("node:http").OutgoingHttpHeaders | Headers;
};
};
/**
* Constructor options for the `Bun.WebSocket` client
*/
type WebSocketOptions = WebSocketOptionsProtocolsOrProtocol & WebSocketOptionsTLS & WebSocketOptionsHeaders;
type WebSocketOptions = WebSocketOptionsProtocolsOrProtocol &
WebSocketOptionsTLS &
WebSocketOptionsHeaders &
WebSocketOptionsProxy;
interface WebSocketEventMap {
close: CloseEvent;