Repository navigation
Replies: 3 comments
|
This looks like a real lifecycle bug, not just a flaky pool symptom. The sequence in your reproducer makes sense: the outer pool stream gets marked closed, cancellation interrupts the HTTP/1.1 connection cleanup while it is waiting for the state lock, and the pool-request removal never runs. A second One thing I would check before preparing a PR is the exact current baseline. If it still reproduces, a focused regression test around |
|
Thanks for pointing me at the newer baseline. I reran the cancellation-after-establishment case against the published |
|
I independently reproduced this against There are two coupled failure points:
A pool-only fix is insufficient. If the request is merely removed, The fix needs two guarantees:
A local prototype with that ordering propagated The regression test should use native |
Uh oh!
There was an error while loading. Please reload this page.
Summary
In
httpcore2 2.12.0, cancellation during HTTP/1.1 response cleanup can leave the connection pool in an inconsistent state:ACTIVE; andresponse.aclose()does not resume the interrupted cleanup.The retained connection continues to count toward
max_connections. Withmax_connections=1, the next request fails withPoolTimeout.This appears distinct from [#982](#982) and [#983](#983). PR #983 handles cancellation before connection establishment, where an unreferenced
NEWconnection remains in the pool. This reproducer cancels cleanup after an HTTP/1.1 connection has been established and a response has been returned.Reproducer
The following standalone script uses
AsyncMockBackend, so it does not require a web framework, external server, or network access.Actual output
Expected behavior
After response cleanup completes or cancellation propagates:
A cancelled response close should not leave an unusable
ACTIVEconnection occupying a pool slot indefinitely.Observed control flow
The failure appears to occur in this sequence:
PoolByteStream.aclose()marks the outer pool stream as closed.HTTP11ConnectionByteStream.aclose().AsyncHTTP11Connection._response_closed()waits for_state_lock.IDLEorCLOSED.PoolByteStream.aclose()bookkeeping, so the pool request is not removed.response.aclose()is a no-op because the outer stream is already marked closed.This leaves the following retained state:
Difference from #982 / #983
PR #983 handles a connection cancelled before establishment:
This reproducer reaches a later lifecycle stage before cancellation:
After cancellation, the connection is still referenced by the retained pool request. Therefore, the garbage-connection condition introduced by #983 does not remove it:
In this case:
Requested behavior
Please make established HTTP/1.1 response cleanup cancellation-safe and ensure that pool bookkeeping completes even if underlying stream cleanup is cancelled.
Potentially relevant paths include:
PoolByteStream.aclose()HTTP11ConnectionByteStream.aclose()AsyncHTTP11Connection._response_closed()AsyncHTTP11Connection.aclose()The desired invariant is:
I can prepare a PR with a regression test and proposed fix if this diagnosis and expected behavior look correct.
Environment
3.11.4httpcore2:2.12.0AsyncMockBackendAll reactions