Description
Looks like we're missing an edge case in our trio backend — getting an OSError, socket.gaierror, etc, instead of a properly wrapped httpcore.ConnectError when requesting an unknown host.
Reproduces when using backend="anyio" too, as well as various versions of trio (0.17.*, 0.16.*), and on Python 3.8 and Python 3.9.
To reproduce
Assuming there's no server running on localhost:8000, run this script…
importhttpcoreimporttrio@trio.runasyncdefmain() ->None:
asyncwithhttpcore.AsyncConnectionPool() ashttp:
method=b"GET"url= (b"http", b"localhost", 8000, b"/")
awaithttp.arequest(method, url)
Expected behavior
Should be like when running on asyncio, i.e. raise a proper httpcore.ConnectError():
importasyncioimporthttpcoreasyncdefmain() ->None:
asyncwithhttpcore.AsyncConnectionPool() ashttp:
method=b"GET"url= (b"http", b"localhost", 8000, b"/")
awaithttp.arequest(method, url)
asyncio.run(main())
Traceback (most recent call last): File "/Users/florimond/Developer/python-projects/httpcore/debug/client.py", line 14, in <module> asyncio.run(main()) File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/asyncio/runners.py", line 44, in run return loop.run_until_complete(main) File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete return future.result() File "/Users/florimond/Developer/python-projects/httpcore/debug/client.py", line 11, in main await http.arequest(method, url) File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_async/connection_pool.py", line 218, in arequest response = await connection.arequest( File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_async/connection.py", line 92, in arequest self.socket = await self._open_socket(timeout) File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_async/connection.py", line 118, in _open_socket return await self.backend.open_tcp_stream( File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_backends/auto.py", line 44, in open_tcp_stream return await self.backend.open_tcp_stream( File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_backends/asyncio.py", line 243, in open_tcp_stream return SocketStream( File "/Users/florimond/.pyenv/versions/3.9.0/lib/python3.9/contextlib.py", line 135, in __exit__ self.gen.throw(type, value, traceback) File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_exceptions.py", line 12, in map_exceptions raise to_exc(exc) from Nonehttpcore.ConnectError: Multiple exceptions: [Errno 61] Connect call failed ('127.0.0.1', 8000), [Errno 61] Connect call failed ('::1', 8000, 0, 0), [Errno 61] Connect call failed ('fe80::1', 8000, 0, 1)Actual behavior
We get a raw OSError:
trio.MultiError: ConnectionRefusedError(61, 'Error in connect: Connection refused'), ConnectionRefusedError(61, 'Error in connect: Connection refused'), ConnectionRefusedError(61, 'Error in connect: Connection refused')Details of embedded exception 1: Traceback (most recent call last): File "/Users/florimond/Developer/python-projects/httpcore/venv/lib/python3.9/site-packages/trio/_highlevel_open_tcp_stream.py", line 332, in attempt_connect await sock.connect(sockaddr) File "/Users/florimond/Developer/python-projects/httpcore/venv/lib/python3.9/site-packages/trio/_socket.py", line 682, in connect raise OSError(err, "Error in connect: " + os.strerror(err)) ConnectionRefusedError: [Errno 61] Error in connect: Connection refusedDetails of embedded exception 2: Traceback (most recent call last): File "/Users/florimond/Developer/python-projects/httpcore/venv/lib/python3.9/site-packages/trio/_highlevel_open_tcp_stream.py", line 332, in attempt_connect await sock.connect(sockaddr) File "/Users/florimond/Developer/python-projects/httpcore/venv/lib/python3.9/site-packages/trio/_socket.py", line 682, in connect raise OSError(err, "Error in connect: " + os.strerror(err)) ConnectionRefusedError: [Errno 61] Error in connect: Connection refusedDetails of embedded exception 3: Traceback (most recent call last): File "/Users/florimond/Developer/python-projects/httpcore/venv/lib/python3.9/site-packages/trio/_highlevel_open_tcp_stream.py", line 332, in attempt_connect await sock.connect(sockaddr) File "/Users/florimond/Developer/python-projects/httpcore/venv/lib/python3.9/site-packages/trio/_socket.py", line 682, in connect raise OSError(err, "Error in connect: " + os.strerror(err)) ConnectionRefusedError: [Errno 61] Error in connect: Connection refusedThe above exception was the direct cause of the following exception:Traceback (most recent call last): File "/Users/florimond/Developer/python-projects/httpcore/debug/client.py", line 6, in <module> async def main() -> None: File "/Users/florimond/Developer/python-projects/httpcore/venv/lib/python3.9/site-packages/trio/_core/_run.py", line 1928, in run raise runner.main_task_outcome.error File "/Users/florimond/Developer/python-projects/httpcore/debug/client.py", line 10, in main await http.arequest(method, url) File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_async/connection_pool.py", line 218, in arequest response = await connection.arequest( File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_async/connection.py", line 92, in arequest self.socket = await self._open_socket(timeout) File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_async/connection.py", line 118, in _open_socket return await self.backend.open_tcp_stream( File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_backends/auto.py", line 44, in open_tcp_stream return await self.backend.open_tcp_stream( File "/Users/florimond/Developer/python-projects/httpcore/httpcore/_backends/trio.py", line 154, in open_tcp_stream stream: trio.abc.Stream = await trio.open_tcp_stream( File "/Users/florimond/Developer/python-projects/httpcore/venv/lib/python3.9/site-packages/trio/_highlevel_open_tcp_stream.py", line 367, in open_tcp_stream raise OSError(msg) from trio.MultiError(oserrors)OSError: all attempts to connect to localhost:8000 failed
If we change the URL to point to an external host, like doesnotexistatall.org, we get a socket.gaierror:
[...]socket.gaierror: [Errno 8] nodename nor servname provided, or not known
Description
Looks like we're missing an edge case in our trio backend — getting an
OSError,socket.gaierror, etc, instead of a properly wrappedhttpcore.ConnectErrorwhen requesting an unknown host.Reproduces when using
backend="anyio"too, as well as various versions of trio (0.17.*,0.16.*), and on Python 3.8 and Python 3.9.To reproduce
Assuming there's no server running on
localhost:8000, run this script…Expected behavior
Should be like when running on
asyncio, i.e. raise a properhttpcore.ConnectError():Actual behavior
We get a raw
OSError:If we change the URL to point to an external host, like
doesnotexistatall.org, we get asocket.gaierror: