@@ -314,6 +314,7 @@ import os
314314import socket
315315import struct
316316import time
317+ import urllib.parse
317318
318319HOST = {host:?}
319320PORT = {port}
@@ -362,13 +363,35 @@ def read_frame(sock):
362363 payload = bytes(byte ^ mask[index % 4] for index, byte in enumerate(payload))
363364 return first, payload
364365
366+ def proxy_parts():
367+ names = ("HTTP_PROXY", "http_proxy", "HTTPS_PROXY", "https_proxy", "ALL_PROXY", "all_proxy")
368+ proxy_url = next((os.environ.get(name) for name in names if os.environ.get(name)), None)
369+ if not proxy_url:
370+ raise RuntimeError("proxy environment is not configured")
371+ parsed = urllib.parse.urlparse(proxy_url)
372+ if not parsed.hostname:
373+ raise RuntimeError(f"invalid proxy URL: {{proxy_url!r}}")
374+ return parsed.hostname, parsed.port or 80
375+
365376def connect_with_retry(host, port, timeout_seconds=20):
377+ proxy_host, proxy_port = proxy_parts()
378+ target = f"{{host}}:{{port}}"
366379 deadline = time.monotonic() + timeout_seconds
367380 last_error = None
368381 while time.monotonic() < deadline:
382+ sock = None
369383 try:
370- return socket.create_connection((host, port), timeout=5)
371- except OSError as error:
384+ sock = socket.create_connection((proxy_host, proxy_port), timeout=5)
385+ request = f"CONNECT {{target}} HTTP/1.1\r\nHost: {{target}}\r\n\r\n"
386+ sock.sendall(request.encode("ascii"))
387+ response = recv_until(sock, b"\r\n\r\n").decode("iso-8859-1", "replace")
388+ if response.startswith("HTTP/1.1 200") or response.startswith("HTTP/1.0 200"):
389+ return sock
390+ first_line = response.splitlines()[0] if response else "<empty response>"
391+ raise RuntimeError(f"proxy CONNECT failed: {{first_line}}")
392+ except (OSError, RuntimeError) as error:
393+ if sock is not None:
394+ sock.close()
372395 last_error = error
373396 time.sleep(0.25)
374397 raise last_error
0 commit comments