8000 Ensure pool timeout is applied, even after attempts leading to Connec… · encode/httpcore@f8ff1c4 · GitHub
[go: up one dir, main page]

Skip to content

Commit f8ff1c4

Browse files
authored
Ensure pool timeout is applied, even after attempts leading to ConnectionNotAvailable (#823)
* add failing tests * fix pooltimeout * Revert "add failing tests" This reverts commit cacc248. * mark `if timeout < 0` as not covered, since the test is reverted * Update CHANGELOG.md
1 parent 8780c9c commit f8ff1c4

File tree

3 files changed

+30
-6
lines changed

3 files changed

+30
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ 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+
## Unreleased
8+
9+
- Fix pool timeout to account for the total time spent retrying. (#823)
10+
711
## 1.0.0 (November 6th, 2023)
812

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

httpcore/_async/connection_pool.py

Lines changed: 13 additions & 3 deletions
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.

httpcore/_sync/connection_pool.py

Lines changed: 13 additions & 3 deletions
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 Iterable, Iterator, Iterable, List, Optional, Type
56

67
from .._backends.sync import SyncBackend
78
from .._backends.base import SOCKET_OPTION, NetworkBackend
8-
from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol
9+
from .._exceptions import ConnectionNotAvailable, PoolTimeout, UnsupportedProtocol
910
from .._models import Origin, Request, Response
1011
from .._synchronization import Event, Lock, ShieldCancellation
1112
from .connection import HTTPConnection
@@ -220,15 +221,20 @@ def handle_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
with self._pool_lock:
225233
self._requests.append(status)
226234
self._close_expired_connections()
227235
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 = status.wait_for_connection(timeout=timeout)
234240
except BaseException as exc:
@@ -263,6 +269,10 @@ def handle_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)
0