#!/usr/bin/env python3
"""ssh ProxyCommand through an HTTP CONNECT proxy (stdlib only).

    ProxyCommand python3 /path/http_connect.py <proxy host> <proxy port> %h %p

Why (2026-11-04): macOS's own `nc -X connect` rejects squid 7's "HTTP/1.1 200 Connection established" reply, so the
lang2 ssh route through the Mac mini squid needs this instead of nc on a Mac.

ssh only: the server speaks first.  If a tunnel closes before the server sent one byte, the CONNECT is retried
(5 tries over about 20 s): on 2026-11-04 one tunnel in four to six through the Mac mini squid closed within 60 ms,
before lang2's sshd saw it (no sshd log line), in bad windows of several seconds (3 quick tries all failed once);
the retry is safe because nothing was relayed yet.
"""
import os
import socket
import sys
import threading
import time


def tunnel(proxy_host, proxy_port, host, port):
    """(socket, the server's first bytes); b'' when the tunnel closed before the server spoke."""
    s = socket.create_connection((proxy_host, proxy_port), timeout=30)
    s.sendall(f'CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n\r\n'.encode())
    buf = b''
    while b'\r\n\r\n' not in buf:
        chunk = s.recv(4096)
        if not chunk:
            sys.exit('http_connect: proxy closed the connection')
        buf += chunk
    head, rest = buf.split(b'\r\n\r\n', 1)
    status = head.split(b'\r\n')[0]
    if status.split()[1:2] != [b'200']:
        sys.exit(f'http_connect: {status.decode(errors="replace")}')
    try:
        return s, rest or s.recv(65536)
    except ConnectionResetError:
        return s, b''
    except socket.timeout:
        sys.exit('http_connect: no answer from the server within 30 s')


def main():
    if len(sys.argv) != 5:
        sys.exit(__doc__)
    proxy_host, proxy_port, host, port = sys.argv[1], int(sys.argv[2]), sys.argv[3], sys.argv[4]
    for attempt in range(1, 6):
        s, first = tunnel(proxy_host, proxy_port, host, port)
        if first:
            break
        s.close()
        sys.stderr.write(f'http_connect: tunnel closed before the server spoke (try {attempt} of 5)\n')
        sys.stderr.flush()  # os._exit at the end drops buffered stderr (Python < 3.9)
        if attempt < 5:
            time.sleep(2 * attempt)
    else:
        sys.exit('http_connect: the server closed the tunnel 5 times before speaking')
    s.settimeout(None)
    out = sys.stdout.buffer
    out.write(first)
    out.flush()

    def upstream():
        try:
            while True:
                data = sys.stdin.buffer.read1(65536)
                if not data:
                    break
                s.sendall(data)
        except OSError:
            pass
        try:
            s.shutdown(socket.SHUT_WR)
        except OSError:
            pass

    threading.Thread(target=upstream, daemon=True).start()
    while True:
        data = s.recv(65536)
        if not data:
            break
        out.write(data)
        out.flush()
    # Exit at once: a normal shutdown with the stdin thread still blocked in read1 aborts with "Fatal Python error"
    # (seen on Python 3.8, LTD 2026-11-04).
    os._exit(0)


if __name__ == '__main__':
    main()
