( wsUrl: string, authHeader: string, wsAuthHeader: string, )
| 243 | // Exported so tests can exercise the Node path directly — the test runner is |
| 244 | // Bun, so the runtime dispatch in startUpstreamProxyRelay always picks Bun. |
| 245 | export async function startNodeRelay( |
| 246 | wsUrl: string, |
| 247 | authHeader: string, |
| 248 | wsAuthHeader: string, |
| 249 | ): Promise<UpstreamProxyRelay> { |
| 250 | nodeWSCtor = (await import('ws')).default |
| 251 | const states = new WeakMap<NodeSocket, ConnState>() |
| 252 | |
| 253 | const server = createServer(sock => { |
| 254 | const st = newConnState() |
| 255 | states.set(sock, st) |
| 256 | // Node's sock.write() buffers internally — a false return signals |
| 257 | // backpressure but the bytes are already queued, so no tail-tracking |
| 258 | // needed for correctness. Week-1 payloads won't stress the buffer. |
| 259 | const adapter: ClientSocket = { |
| 260 | write: payload => { |
| 261 | sock.write(typeof payload === 'string' ? payload : Buffer.from(payload)) |
| 262 | }, |
| 263 | end: () => sock.end(), |
| 264 | } |
| 265 | sock.on('data', data => |
| 266 | handleData(adapter, st, data, wsUrl, authHeader, wsAuthHeader), |
| 267 | ) |
| 268 | sock.on('close', () => cleanupConn(states.get(sock))) |
| 269 | sock.on('error', err => { |
| 270 | logForDebugging(`[upstreamproxy] client socket error: ${err.message}`) |
| 271 | cleanupConn(states.get(sock)) |
| 272 | }) |
| 273 | }) |
| 274 | |
| 275 | return new Promise((resolve, reject) => { |
| 276 | server.once('error', reject) |
| 277 | server.listen(0, '127.0.0.1', () => { |
| 278 | const addr = server.address() |
| 279 | if (addr === null || typeof addr === 'string') { |
| 280 | reject(new Error('upstreamproxy: server has no TCP address')) |
| 281 | return |
| 282 | } |
| 283 | resolve({ |
| 284 | port: addr.port, |
| 285 | stop: () => server.close(), |
| 286 | }) |
| 287 | }) |
| 288 | }) |
| 289 | } |
| 290 | |
| 291 | /** |
| 292 | * Shared per-connection data handler. Phase 1 accumulates the CONNECT request; |
no test coverage detected