Skip to content

Commit 92b1658

Browse files
authored
handle aiohttp errors (Azure#30779)
* handle aiohttp errors * add tests * update * get main
1 parent 9efde13 commit 92b1658

File tree

3 files changed

+50
-10
lines changed

3 files changed

+50
-10
lines changed

sdk/core/azure-core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
### Other Changes
1414

15+
- Catch aiohttp errors and translate them into azure-core errors.
16+
1517
## 1.27.1 (2023-06-13)
1618

1719
### Bugs Fixed

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,10 @@ async def send(self, request, **config):
266266
await response.load_body()
267267
except aiohttp.client_exceptions.ClientResponseError as err:
268268
raise ServiceResponseError(err, error=err) from err
269-
except aiohttp.client_exceptions.ClientError as err:
270-
raise ServiceRequestError(err, error=err) from err
271269
except asyncio.TimeoutError as err:
272270
raise ServiceResponseError(err, error=err) from err
271+
except aiohttp.client_exceptions.ClientError as err:
272+
raise ServiceRequestError(err, error=err) from err
273273
return response
274274

275275

@@ -324,6 +324,12 @@ async def __anext__(self):
324324
_LOGGER.warning("Incomplete download: %s", err)
325325
internal_response.close()
326326
raise IncompleteReadError(err, error=err)
327+
except aiohttp.client_exceptions.ClientResponseError as err:
328+
raise ServiceResponseError(err, error=err) from err
329+
except asyncio.TimeoutError as err:
330+
raise ServiceResponseError(err, error=err)
331+
except aiohttp.client_exceptions.ClientError as err:
332+
raise ServiceRequestError(err, error=err) from err
327333
except Exception as err:
328334
_LOGGER.warning("Unable to stream download: %s", err)
329335
internal_response.close()
@@ -411,6 +417,12 @@ async def load_body(self) -> None:
411417
# This is the case that server closes connection before we finish the reading. aiohttp library
412418
# raises ClientPayloadError.
413419
raise IncompleteReadError(err, error=err)
420+
except aiohttp.client_exceptions.ClientResponseError as err:
421+
raise ServiceResponseError(err, error=err) from err
422+
except asyncio.TimeoutError as err:
423+
raise ServiceResponseError(err, error=err)
424+
except aiohttp.client_exceptions.ClientError as err:
425+
raise ServiceRequestError(err, error=err) from err
414426

415427
def stream_download(self, pipeline, **kwargs) -> AsyncIteratorType[bytes]:
416428
"""Generator for streaming response body data.

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

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@
77
AsyncHttpResponse as PipelineTransportAsyncHttpResponse,
88
AsyncHttpTransport,
99
AioHttpTransport,
10+
HttpRequest,
11+
AioHttpTransportResponse,
1012
)
13+
from azure.core.pipeline.transport._aiohttp import AioHttpStreamDownloadGenerator
1114
from azure.core.rest._http_response_impl_async import AsyncHttpResponseImpl as RestAsyncHttpResponse
1215
from azure.core.pipeline.policies import HeadersPolicy
1316
from azure.core.pipeline import AsyncPipeline
14-
from azure.core.exceptions import HttpResponseError
17+
from azure.core.exceptions import HttpResponseError, ServiceResponseError
1518
from utils import HTTP_REQUESTS, request_and_responses_product
1619
import pytest
1720
import sys
21+
import aiohttp
1822

1923

2024
# transport = mock.MagicMock(spec=AsyncHttpTransport)
@@ -79,7 +83,6 @@ def content(self):
7983
@pytest.mark.asyncio
8084
@pytest.mark.parametrize("http_request", HTTP_REQUESTS)
8185
async def test_basic_options_aiohttp(port, http_request):
82-
8386
request = http_request("OPTIONS", "http://localhost:{}/basic/string".format(port))
8487
async with AsyncPipeline(AioHttpTransport(), policies=[]) as pipeline:
8588
response = await pipeline.run(request)
@@ -138,7 +141,6 @@ async def on_request(self, request):
138141
@pytest.mark.asyncio
139142
@pytest.mark.parametrize("http_request", HTTP_REQUESTS)
140143
async def test_multipart_send_with_context(http_request):
141-
142144
transport = MockAsyncHttpTransport()
143145
header_policy = HeadersPolicy()
144146

@@ -583,7 +585,6 @@ async def test_multipart_receive_with_one_changeset(http_request, mock_response)
583585
@pytest.mark.asyncio
584586
@pytest.mark.parametrize("http_request,mock_response", request_and_responses_product(MOCK_RESPONSES))
585587
async def test_multipart_receive_with_multiple_changesets(http_request, mock_response):
586-
587588
changeset1 = http_request("", "")
588589
changeset1.set_multipart_mixed(
589590
http_request("DELETE", "/container0/blob0"), http_request("DELETE", "/container1/blob1")
@@ -666,7 +667,6 @@ async def test_multipart_receive_with_multiple_changesets(http_request, mock_res
666667
@pytest.mark.asyncio
667668
@pytest.mark.parametrize("http_request,mock_response", request_and_responses_product(MOCK_RESPONSES))
668669
async def test_multipart_receive_with_combination_changeset_first(http_request, mock_response):
669-
670670
changeset = http_request("", "")
671671
changeset.set_multipart_mixed(
672672
http_request("DELETE", "/container0/blob0"), http_request("DELETE", "/container1/blob1")
@@ -744,7 +744,6 @@ def test_raise_for_status_good_response(mock_response):
744744
@pytest.mark.asyncio
745745
@pytest.mark.parametrize("http_request,mock_response", request_and_responses_product(MOCK_RESPONSES))
746746
async def test_multipart_receive_with_combination_changeset_middle(http_request, mock_response):
747-
748747
changeset = http_request("", "")
749748
changeset.set_multipart_mixed(http_request("DELETE", "/container1/blob1"))
750749

@@ -807,7 +806,6 @@ async def test_multipart_receive_with_combination_changeset_middle(http_request,
807806
@pytest.mark.asyncio
808807
@pytest.mark.parametrize("http_request,mock_response", request_and_responses_product(MOCK_RESPONSES))
809808
async def test_multipart_receive_with_combination_changeset_last(http_request, mock_response):
810-
811809
changeset = http_request("", "")
812810
changeset.set_multipart_mixed(
813811
http_request("DELETE", "/container1/blob1"), http_request("DELETE", "/container2/blob2")
@@ -871,7 +869,6 @@ async def test_multipart_receive_with_combination_changeset_last(http_request, m
871869
@pytest.mark.asyncio
872870
@pytest.mark.parametrize("http_request,mock_response", request_and_responses_product(MOCK_RESPONSES))
873871
async def test_multipart_receive_with_bom(http_request, mock_response):
874-
875872
req0 = http_request("DELETE", "/container0/blob0")
876873

877874
request = http_request("POST", "http://account.blob.core.windows.net/?comp=batch")
@@ -971,3 +968,32 @@ def test_aiohttp_loop():
971968
loop = asyncio.get_event_loop()
972969
with pytest.raises(ValueError):
973970
transport = AioHttpTransport(loop=loop)
971+
972+
973+
class MockAiohttpResponse:
974+
def __init__(self):
975+
self.status = 200
976+
self.reason = "OK"
977+
self.headers = {"content-type": "application/json"}
978+
self.content = MockAiohttpContent()
979+
980+
def read(self):
981+
request_info = aiohttp.RequestInfo("http://example.org", "GET", {})
982+
raise aiohttp.client_exceptions.ClientResponseError(request_info, None)
983+
984+
985+
class MockAiohttpContent:
986+
async def read(self, block_size):
987+
request_info = aiohttp.RequestInfo("http://example.org", "GET", {})
988+
raise aiohttp.client_exceptions.ClientResponseError(request_info, None)
989+
990+
991+
async def test_aiohttp_errors():
992+
request = HttpRequest("GET", "http://example.org")
993+
response = AioHttpTransportResponse(request, MockAiohttpResponse())
994+
with pytest.raises(ServiceResponseError):
995+
await response.load_body()
996+
997+
generator = AioHttpStreamDownloadGenerator(None, response)
998+
with pytest.raises(ServiceResponseError):
999+
await generator.__anext__()

0 commit comments

Comments
 (0)