auth: AsyncAuthorizedSession.request leaks response across retry attempts

Open Beginner friendly
#18,315 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

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

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.

Description

auth priority: p2

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:

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.

Dominant language
Python
Stars
5.4k
Forks
1.8k
Avg merge
1d 17h
Merged PRs (30d)
93

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from googleapis/google-cloud-python

All issues in googleapis/google-cloud-python

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.