Skip to content

Commit 1163ea4

Browse files
authored
[Core] Update aiohttp transport timeout errors (#41227)
* Update aiohttp transport timeout errors * Make backwards compatible * Added tests * Fix black * Feedback * Version bump + changelog
1 parent 422fe4a commit 1163ea4

File tree

4 files changed

+75
-4
lines changed

4 files changed

+75
-4
lines changed

sdk/core/azure-core/CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Release History
22

3-
## 1.34.1 (Unreleased)
3+
## 1.35.0 (Unreleased)
44

55
### Features Added
66

@@ -10,6 +10,9 @@
1010

1111
### Other Changes
1212

13+
- A timeout error when using the `aiohttp` transport (the default for async SDKs) will now be raised as a `azure.core.exceptions.ServiceResponseTimeoutError`, a subtype of the previously raised `ServiceResponseError`.
14+
- When using with `aiohttp` 3.10 or later, a connection timeout error will now be raised as a `azure.core.exceptions.ServiceRequestTimeoutError`, which can be retried.
15+
1316
## 1.34.0 (2025-05-01)
1417

1518
### Features Added

sdk/core/azure-core/azure/core/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
# regenerated.
1010
# --------------------------------------------------------------------------
1111

12-
VERSION = "1.34.1"
12+
VERSION = "1.35.0"

sdk/core/azure-core/azure/core/pipeline/transport/_aiohttp.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
from azure.core.configuration import ConnectionConfiguration
5050
from azure.core.exceptions import (
5151
ServiceRequestError,
52+
ServiceRequestTimeoutError,
5253
ServiceResponseError,
54+
ServiceResponseTimeoutError,
5355
IncompleteReadError,
5456
)
5557
from azure.core.pipeline import AsyncPipeline
@@ -76,6 +78,15 @@
7678
CONTENT_CHUNK_SIZE = 10 * 1024
7779
_LOGGER = logging.getLogger(__name__)
7880

81+
try:
82+
# ConnectionTimeoutError was only introduced in aiohttp 3.10 so we want to keep this
83+
# backwards compatible. If client is using aiohttp <3.10, the behaviour will safely
84+
# fall back to treating a TimeoutError as a ServiceResponseError (that wont be retried).
85+
from aiohttp.client_exceptions import ConnectionTimeoutError
86+
except ImportError:
87+
88+
class ConnectionTimeoutError(Exception): ... # type: ignore[no-redef]
89+
7990

8091
class AioHttpTransport(AsyncHttpTransport):
8192
"""AioHttp HTTP sender implementation.
@@ -344,8 +355,10 @@ async def send(
344355
raise
345356
except aiohttp.client_exceptions.ClientResponseError as err:
346357
raise ServiceResponseError(err, error=err) from err
358+
except ConnectionTimeoutError as err:
359+
raise ServiceRequestTimeoutError(err, error=err) from err
347360
except asyncio.TimeoutError as err:
348-
raise ServiceResponseError(err, error=err) from err
361+
raise ServiceResponseTimeoutError(err, error=err) from err
349362
except aiohttp.client_exceptions.ClientError as err:
350363
raise ServiceRequestError(err, error=err) from err
351364
return response

sdk/core/azure-core/tests/async_tests/test_basic_transport_async.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,19 @@
1414
from azure.core.rest._http_response_impl_async import AsyncHttpResponseImpl as RestAsyncHttpResponse
1515
from azure.core.pipeline.policies import HeadersPolicy
1616
from azure.core.pipeline import AsyncPipeline
17-
from azure.core.exceptions import HttpResponseError, ServiceResponseError
17+
from azure.core.exceptions import (
18+
HttpResponseError,
19+
ServiceResponseError,
20+
ServiceRequestError,
21+
ServiceRequestTimeoutError,
22+
ServiceResponseTimeoutError,
23+
)
1824
from utils import HTTP_REQUESTS, request_and_responses_product
1925
import pytest
2026
import sys
27+
import asyncio
28+
from unittest.mock import Mock
29+
from pkg_resources import parse_version
2130
import aiohttp
2231

2332

@@ -1038,3 +1047,49 @@ async def test_close_too_soon_works_fine(caplog, port, http_request):
10381047
result = await transport.send(request)
10391048

10401049
assert result # No exception is good enough here
1050+
1051+
1052+
@pytest.mark.skipif(
1053+
parse_version(aiohttp.__version__) >= parse_version("3.10"),
1054+
reason="aiohttp 3.10 introduced separate connection timeout",
1055+
)
1056+
@pytest.mark.parametrize("http_request", HTTP_REQUESTS)
1057+
@pytest.mark.asyncio
1058+
async def test_aiohttp_timeout_response(http_request):
1059+
async with AioHttpTransport() as transport:
1060+
transport.session._connector.connect = Mock(side_effect=asyncio.TimeoutError("Too slow!"))
1061+
1062+
request = http_request("GET", f"http://localhost:12345/basic/string")
1063+
1064+
with pytest.raises(ServiceResponseTimeoutError) as err:
1065+
await transport.send(request)
1066+
1067+
with pytest.raises(ServiceResponseError) as err:
1068+
await transport.send(request)
1069+
1070+
stream_request = http_request("GET", f"http://localhost:12345/streams/basic")
1071+
with pytest.raises(ServiceResponseTimeoutError) as err:
1072+
await transport.send(stream_request, stream=True)
1073+
1074+
1075+
@pytest.mark.skipif(
1076+
parse_version(aiohttp.__version__) < parse_version("3.10"),
1077+
reason="aiohttp 3.10 introduced separate connection timeout",
1078+
)
1079+
@pytest.mark.parametrize("http_request", HTTP_REQUESTS)
1080+
@pytest.mark.asyncio
1081+
async def test_aiohttp_timeout_request(http_request):
1082+
async with AioHttpTransport() as transport:
1083+
transport.session._connector.connect = Mock(side_effect=asyncio.TimeoutError("Too slow!"))
1084+
1085+
request = http_request("GET", f"http://localhost:12345/basic/string")
1086+
1087+
with pytest.raises(ServiceRequestTimeoutError) as err:
1088+
await transport.send(request)
1089+
1090+
with pytest.raises(ServiceRequestError) as err:
1091+
await transport.send(request)
1092+
1093+
stream_request = http_request("GET", f"http://localhost:12345/streams/basic")
1094+
with pytest.raises(ServiceRequestTimeoutError) as err:
1095+
await transport.send(stream_request, stream=True)

0 commit comments

Comments
 (0)