Skip to content

Commit e2af16a

Browse files
committed
Support HTTP/3
1 parent 8780c9c commit e2af16a

12 files changed

+1149
-11
lines changed

CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7-
## 1.0.0 (November 6th, 2023)
7+
## Unreleased
8+
9+
- Fix pool timeout to account for the total time spent retrying. (#823)
10+
11+
## 1.0.0 (October 6th, 2023)
812

913
From version 1.0 our async support is now optional, as the package has minimal dependencies by default.
1014

httpcore/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
AsyncConnectionInterface,
44
AsyncConnectionPool,
55
AsyncHTTP2Connection,
6+
AsyncHTTP3Connection,
67
AsyncHTTP11Connection,
78
AsyncHTTPConnection,
89
AsyncHTTPProxy,
@@ -40,6 +41,7 @@
4041
ConnectionInterface,
4142
ConnectionPool,
4243
HTTP2Connection,
44+
HTTP3Connection,
4345
HTTP11Connection,
4446
HTTPConnection,
4547
HTTPProxy,
@@ -85,6 +87,7 @@ def __init__(self, *args, **kwargs): # type: ignore
8587
"AsyncHTTPProxy",
8688
"AsyncHTTP11Connection",
8789
"AsyncHTTP2Connection",
90+
"AsyncHTTP3Connection",
8891
"AsyncConnectionInterface",
8992
"AsyncSOCKSProxy",
9093
# sync
@@ -93,6 +96,7 @@ def __init__(self, *args, **kwargs): # type: ignore
9396
"HTTPProxy",
9497
"HTTP11Connection",
9598
"HTTP2Connection",
99+
"HTTP3Connection",
96100
"ConnectionInterface",
97101
"SOCKSProxy",
98102
# network backends, implementations

httpcore/_async/__init__.py

+13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ def __init__(self, *args, **kwargs) -> None: # type: ignore
1616
)
1717

1818

19+
try:
20+
from .http3 import AsyncHTTP3Connection
21+
except ImportError: # pragma: nocover
22+
23+
class AsyncHTTP3Connection: # type: ignore
24+
def __init__(self, *args, **kwargs) -> None: # type: ignore
25+
raise RuntimeError(
26+
"Attempted to use http3 support, but the `aioquic` package is not "
27+
"installed. Use 'pip install httpcore[http3]'."
28+
)
29+
30+
1931
try:
2032
from .socks_proxy import AsyncSOCKSProxy
2133
except ImportError: # pragma: nocover
@@ -34,6 +46,7 @@ def __init__(self, *args, **kwargs) -> None: # type: ignore
3446
"AsyncHTTPProxy",
3547
"AsyncHTTP11Connection",
3648
"AsyncHTTP2Connection",
49+
"AsyncHTTP3Connection",
3750
"AsyncConnectionInterface",
3851
"AsyncSOCKSProxy",
3952
]

httpcore/_async/connection_pool.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import ssl
22
import sys
3+
import time
34
from types import TracebackType
45
from typing import AsyncIterable, AsyncIterator, Iterable, List, Optional, Type
56

67
from .._backends.auto import AutoBackend
78
from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend
8-
from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol
9+
from .._exceptions import ConnectionNotAvailable, PoolTimeout, UnsupportedProtocol
910
from .._models import Origin, Request, Response
1011
from .._synchronization import AsyncEvent, AsyncLock, AsyncShieldCancellation
1112
from .connection import AsyncHTTPConnection
@@ -220,15 +221,20 @@ async def handle_async_request(self, request: Request) -> Response:
220221
)
221222

222223
status = RequestStatus(request)
224+
timeouts = request.extensions.get("timeout", {})
225+
timeout = timeouts.get("pool", None)
226+
227+
if timeout is not None:
228+
deadline = time.monotonic() + timeout
229+
else:
230+
deadline = float("inf")
223231

224232
async with self._pool_lock:
225233
self._requests.append(status)
226234
await self._close_expired_connections()
227235
await self._attempt_to_acquire_connection(status)
228236

229237
while True:
230-
timeouts = request.extensions.get("timeout", {})
231-
timeout = timeouts.get("pool", None)
232238
try:
233239
connection = await status.wait_for_connection(timeout=timeout)
234240
except BaseException as exc:
@@ -263,6 +269,10 @@ async def handle_async_request(self, request: Request) -> Response:
263269
else:
264270
break
265271

272+
timeout = deadline - time.monotonic()
273+
if timeout < 0:
274+
raise PoolTimeout # pragma: nocover
275+
266276
# When we return the response, we wrap the stream in a special class
267277
# that handles notifying the connection pool once the response
268278
# has been released.

0 commit comments

Comments
 (0)