* Read the optional client-hello line a proxy sends after the daemon hello. * Always resolves (never rejects) — fail-safe by design, since every connection * funnels through here. Resolves with the peer pids when the first line is a * client-hello; otherwise resolves with null pids and unshifts t
( socket: net.Socket, )
| 761 | * straddling a chunk boundary in the unshifted tail is never corrupted. |
| 762 | */ |
| 763 | function readClientHello( |
| 764 | socket: net.Socket, |
| 765 | ): Promise<{ pid: number | null; hostPid: number | null }> { |
| 766 | return new Promise((resolve) => { |
| 767 | let chunks: Buffer[] = []; |
| 768 | let total = 0; |
| 769 | let settled = false; |
| 770 | const finish = ( |
| 771 | peers: { pid: number | null; hostPid: number | null }, |
| 772 | putBack?: Buffer, |
| 773 | ) => { |
| 774 | if (settled) return; |
| 775 | settled = true; |
| 776 | socket.removeListener('data', onData); |
| 777 | socket.removeListener('error', onEnd); |
| 778 | socket.removeListener('close', onEnd); |
| 779 | clearTimeout(timer); |
| 780 | if (putBack && putBack.length > 0 && !socket.destroyed) { |
| 781 | try { socket.unshift(putBack); } catch { /* stream already gone */ } |
| 782 | } |
| 783 | resolve(peers); |
| 784 | }; |
| 785 | const onData = (chunk: Buffer | string) => { |
| 786 | const buf = typeof chunk === 'string' ? Buffer.from(chunk, 'utf8') : chunk; |
| 787 | chunks.push(buf); |
| 788 | total += buf.length; |
| 789 | const all = chunks.length === 1 ? buf : Buffer.concat(chunks, total); |
| 790 | const nl = all.indexOf(0x0a); // '\n' |
| 791 | if (nl === -1) { |
| 792 | // No newline yet. If it's already too long to be a hello, it isn't one — |
| 793 | // hand the bytes back as data; otherwise keep accumulating. |
| 794 | if (total > MAX_HELLO_LINE_BYTES) finish({ pid: null, hostPid: null }, all); |
| 795 | else chunks = [all]; |
| 796 | return; |
| 797 | } |
| 798 | const peers = parseClientHelloLine(all.subarray(0, nl).toString('utf8')); |
| 799 | if (peers) { |
| 800 | const tail = all.subarray(nl + 1); |
| 801 | finish(peers, tail.length > 0 ? tail : undefined); |
| 802 | } else { |
| 803 | // First line is not a client-hello (legacy/direct client) — hand the |
| 804 | // whole buffer back so the transport sees the message verbatim. |
| 805 | finish({ pid: null, hostPid: null }, all); |
| 806 | } |
| 807 | }; |
| 808 | const onEnd = () => finish({ pid: null, hostPid: null }); |
| 809 | const timer = setTimeout(() => finish({ pid: null, hostPid: null }), CLIENT_HELLO_TIMEOUT_MS); |
| 810 | timer.unref?.(); |
| 811 | socket.on('data', onData); |
| 812 | socket.on('error', onEnd); |
| 813 | socket.on('close', onEnd); |
| 814 | }); |
| 815 | } |
| 816 | |
| 817 | /** Exported for test stubs that need to bound the hello-line read. */ |
| 818 | export { MAX_HELLO_LINE_BYTES }; |
no test coverage detected