diff --git a/packages/http/httpx/kiota_http/middleware/retry_handler.py b/packages/http/httpx/kiota_http/middleware/retry_handler.py index fa105fc..2c06ee2 100644 --- a/packages/http/httpx/kiota_http/middleware/retry_handler.py +++ b/packages/http/httpx/kiota_http/middleware/retry_handler.py @@ -95,6 +95,8 @@ async def send(self, request: httpx.Request, transport: httpx.AsyncBaseTransport # and status code should_retry = self.should_retry(request, current_options, response) if all([should_retry, retry_valid, delay < RetryHandlerOption.MAX_DELAY]): + # release the discarded response's connection before retrying + await response.aclose() await asyncio.sleep(delay) # increment the count for retries retry_count += 1 diff --git a/packages/http/httpx/tests/middleware_tests/test_retry_handler.py b/packages/http/httpx/tests/middleware_tests/test_retry_handler.py index 9f2edda..e45a12b 100644 --- a/packages/http/httpx/tests/middleware_tests/test_retry_handler.py +++ b/packages/http/httpx/tests/middleware_tests/test_retry_handler.py @@ -298,3 +298,32 @@ def request_handler(request: httpx.Request): assert resp.status_code == GATEWAY_TIMEOUT assert 'request_2' in resp.request.headers assert resp.request.headers[RETRY_ATTEMPT] == '2' + + +@pytest.mark.asyncio +async def test_retried_responses_are_closed(): + """Discarded retry responses are closed so their connection is released.""" + responses = [] + + async def body(): + yield b'{}' + + statuses = [TOO_MANY_REQUESTS, TOO_MANY_REQUESTS, 200] + + def request_handler(request: httpx.Request): + status = statuses.pop(0) + headers = {RETRY_AFTER: '0'} if status == TOO_MANY_REQUESTS else {} + response = httpx.Response(status, headers=headers, content=body()) + responses.append(response) + return response + + handler = RetryHandler(RetryHandlerOption(delay=0, max_retries=2)) + request = httpx.Request('GET', BASE_URL) + mock_transport = httpx.MockTransport(request_handler) + resp = await handler.send(request, mock_transport) + + assert resp.status_code == 200 + # the two retried responses are closed, releasing their connections + assert responses[0].is_closed and responses[1].is_closed + # the returned response is left open for the caller to read + assert not responses[2].is_closed