URL: http://github.com/encode/httpcore/pull/1055.diff
local_address: Local address to connect from. Can also be used to - connect using a particular address family. Using - `local_address="0.0.0.0"` will connect using an `AF_INET` address - (IPv4), while using `local_address="::"` will connect using an - `AF_INET6` address (IPv6). - uds: Path to a Unix Domain Socket to use instead of TCP sockets. - network_backend: A backend instance to use for handling network I/O. """ super().__init__( ssl_context=ssl_context, @@ -237,6 +217,7 @@ def handle_request(self, request: Request) -> Response: "host": self._remote_origen.host.decode("ascii"), "port": self._remote_origen.port, "auth": self._proxy_auth, + "timeout": timeout, # <--- FIX 8: Pass timeout argument } with Trace( "setup_socks5_connection", logger, request, kwargs diff --git a/reproduce_httpcore.py b/reproduce_httpcore.py new file mode 100644 index 00000000..be45af37 --- /dev/null +++ b/reproduce_httpcore.py @@ -0,0 +1,63 @@ +import httpcore +import socket +import threading +import time + +# --- Same Server Setup as before --- +TIMEOUT = 2.0 +HANG_TIME = 20 + +def get_free_port(): + with socket.socket() as s: + s.bind(('', 0)) + return s.getsockname()[1] + +def blackhole_proxy_server(port, stop_event): + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server.bind(('127.0.0.1', port)) + server.listen(1) + server.settimeout(1.0) + while not stop_event.is_set(): + try: + client, _ = server.accept() + time.sleep(HANG_TIME) # Hang the handshake + client.close() + except socket.timeout: continue + except: break + server.close() + +# --- The Test --- +def run_test(): + proxy_port = get_free_port() + stop_event = threading.Event() + t = threading.Thread(target=blackhole_proxy_server, args=(proxy_port, stop_event)) + t.start() + time.sleep(0.5) + + print(f"[*] Testing httpcore SOCKSProxy with {TIMEOUT}s timeout...") + start_time = time.time() + + # We use the low-level SOCKSProxy directly + with httpcore.SOCKSProxy( + proxy_url=f"socks5://127.0.0.1:{proxy_port}" + ) as pool: + try: + # We assume httpcore 1.0+ style request + pool.request( + "GET", + "http://example.com", + extensions={'timeout': {'connect': TIMEOUT, 'read': TIMEOUT}} + ) + except httpcore.TimeoutException: + print("[SUCCESS] Caught timeout correctly!") + except Exception as e: + print(f"[ERROR] {e}") + finally: + duration = time.time() - start_time + print(f"[*] Duration: {duration:.2f}s") + stop_event.set() + t.join() + +if __name__ == "__main__": + run_test() \ No newline at end of file diff --git a/reproduce_httpcore_async.py b/reproduce_httpcore_async.py new file mode 100644 index 00000000..3e577cfe --- /dev/null +++ b/reproduce_httpcore_async.py @@ -0,0 +1,69 @@ +import httpcore +import socket +import threading +import time +import asyncio + +# --- Server Setup (Same as before) --- +HANG_TIME = 20 +TIMEOUT = 2.0 + +def get_free_port(): + with socket.socket() as s: + s.bind(('', 0)) + return s.getsockname()[1] + +def blackhole_proxy_server(port, stop_event): + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server.bind(('127.0.0.1', port)) + server.listen(1) + server.settimeout(1.0) + + while not stop_event.is_set(): + try: + client, _ = server.accept() + # print("[Server] Accepted connection, sleeping...") + time.sleep(HANG_TIME) + client.close() + except socket.timeout: continue + except: break + server.close() + +# --- Async Test --- +async def run_async_test(port): + print(f"[*] Testing ASYNC httpcore SOCKSProxy with {TIMEOUT}s timeout...") + start_time = time.time() + + async with httpcore.AsyncSOCKSProxy( + proxy_url=f"socks5://127.0.0.1:{port}" + ) as pool: + try: + await pool.request( + "GET", + "http://example.com", + extensions={'timeout': {'connect': TIMEOUT, 'read': TIMEOUT}} + ) + except httpcore.TimeoutException: + print("[SUCCESS] Caught timeout correctly!") + except Exception as e: + print(f"[ERROR] {type(e).__name__}: {e}") + finally: + duration = time.time() - start_time + print(f"[*] Duration: {duration:.2f}s") + +def main(): + port = get_free_port() + stop_event = threading.Event() + t = threading.Thread(target=blackhole_proxy_server, args=(port, stop_event)) + t.start() + time.sleep(0.5) + + try: + asyncio.run(run_async_test(port)) + finally: + stop_event.set() + t.join() + +if __name__ == "__main__": + main() \ No newline at end of fileNote: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: