googleapis / googleapis/google-cloud-python

auth: AsyncAuthorizedSession.request leaks response across retry attempts

Open Beginner friendly
#18,315 0 comments 0 reactions 0 assignees View on GitHub
auth priority: p2
Dominant language
Python
Stars
5.4k
Forks
1.8k
Avg merge
3d 4h
Merged PRs (30d)
122

Description

In `google.auth.aio.transport.sessions.AsyncAuthorizedSession.request` (`packages/google-auth/google/auth/aio/transport/sessions.py:339-347`), the retry loop reassigns `response = await with_timeout(...)` across retry attempts without closing the previous response.

If the response payload hasn't reached EOF before the next retry fires (e.g., with chunked or streaming responses), `aiohttp` keeps the socket checked out in `connector._acquired`. The transport protocol's internal EOF callback holds a bound reference to `ClientResponse._response_eof`, which prevents Python's garbage collector from cleaning up the orphaned response while the connection is waiting for data. Under concurrency, multiple requests retrying against a degraded endpoint can quickly burn through the connector pool (`limit=100`) and block other outgoing requests across the session.

### Proposed Fix

Close any previous response before kicking off the next retry attempt, matching the pattern already used in `sessions.py:L501-L518`:

```python
response = None
async for _ in retries:
if response is not None and hasattr(response, "close"):
try:
res = response.close()
if inspect.isawaitable(res):
await res
except Exception:
pass

response = await with_timeout(
self._auth_request(
url, method, data, request_headers, actual_timeout, **kwargs
)
)

if response.status_code not in transport.DEFAULT_RETRYABLE_STATUS_CODES:
break
```

Unit tests should check that when a retryable status code (like a 503) is followed by a 200, `close()` is awaited on the initial response before the retry runs.

Contributor guide

Open the contributing guide

Research direction

Start in packages/google-auth/google/auth/aio/transport/sessions.py at AsyncAuthorizedSession.request, then compare its retry loop with the cleanup pattern at lines 501-518. Add unit coverage for a retryable 503 followed by a 200, verifying that the initial response's close() is awaited before the retry proceeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
networking
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.