* Shared per-connection data handler. Phase 1 accumulates the CONNECT request; * phase 2 forwards client bytes over the WS tunnel.
( sock: ClientSocket, st: ConnState, data: Buffer, wsUrl: string, authHeader: string, wsAuthHeader: string, )
| 293 | * phase 2 forwards client bytes over the WS tunnel. |
| 294 | */ |
| 295 | function handleData( |
| 296 | sock: ClientSocket, |
| 297 | st: ConnState, |
| 298 | data: Buffer, |
| 299 | wsUrl: string, |
| 300 | authHeader: string, |
| 301 | wsAuthHeader: string, |
| 302 | ): void { |
| 303 | // Phase 1: accumulate until we've seen the full CONNECT request |
| 304 | // (terminated by CRLF CRLF). curl/gh send this in one packet, but |
| 305 | // don't assume that. |
| 306 | if (!st.ws) { |
| 307 | st.connectBuf = Buffer.concat([st.connectBuf, data]) |
| 308 | const headerEnd = st.connectBuf.indexOf('\r\n\r\n') |
| 309 | if (headerEnd === -1) { |
| 310 | // Guard against a client that never sends CRLFCRLF. |
| 311 | if (st.connectBuf.length > 8192) { |
| 312 | sock.write('HTTP/1.1 400 Bad Request\r\n\r\n') |
| 313 | sock.end() |
| 314 | } |
| 315 | return |
| 316 | } |
| 317 | const reqHead = st.connectBuf.subarray(0, headerEnd).toString('utf8') |
| 318 | const firstLine = reqHead.split('\r\n')[0] ?? '' |
| 319 | const m = firstLine.match(/^CONNECT\s+(\S+)\s+HTTP\/1\.[01]$/i) |
| 320 | if (!m) { |
| 321 | sock.write('HTTP/1.1 405 Method Not Allowed\r\n\r\n') |
| 322 | sock.end() |
| 323 | return |
| 324 | } |
| 325 | // Stash any bytes that arrived after the CONNECT header so |
| 326 | // openTunnel can flush them once the WS is open. |
| 327 | const trailing = st.connectBuf.subarray(headerEnd + 4) |
| 328 | if (trailing.length > 0) { |
| 329 | st.pending.push(Buffer.from(trailing)) |
| 330 | } |
| 331 | st.connectBuf = Buffer.alloc(0) |
| 332 | openTunnel(sock, st, firstLine, wsUrl, authHeader, wsAuthHeader) |
| 333 | return |
| 334 | } |
| 335 | // Phase 2: WS exists. If it isn't OPEN yet, buffer; ws.onopen will |
| 336 | // flush. Once open, pump client bytes to WS in chunks. |
| 337 | if (!st.wsOpen) { |
| 338 | st.pending.push(Buffer.from(data)) |
| 339 | return |
| 340 | } |
| 341 | forwardToWs(st.ws, data) |
| 342 | } |
| 343 | |
| 344 | function openTunnel( |
| 345 | sock: ClientSocket, |
no test coverage detected