From d0534262fd44eba672ae09ad647eef38eaa13619 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 19 Oct 2023 11:57:27 -0700 Subject: [PATCH 1/4] same aiohttp multipart/form-data fix applied to azure-core --- .../corehttp/transport/aiohttp/_aiohttp.py | 2 +- .../test_rest_http_response_async.py | 28 ++++++++++++----- .../coretestserver/test_routes/multipart.py | 31 ++++++++++++++++--- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/sdk/core/corehttp/corehttp/transport/aiohttp/_aiohttp.py b/sdk/core/corehttp/corehttp/transport/aiohttp/_aiohttp.py index 2021dbabe2fd..7a6df7356793 100644 --- a/sdk/core/corehttp/corehttp/transport/aiohttp/_aiohttp.py +++ b/sdk/core/corehttp/corehttp/transport/aiohttp/_aiohttp.py @@ -136,7 +136,7 @@ def _get_request_data(self, request: RestHttpRequest): :return: The request data """ if request._files: # pylint: disable=protected-access - form_data = aiohttp.FormData() + form_data = aiohttp.FormData(request.data or {}) for form_file, data in request._files.items(): # pylint: disable=protected-access content_type = data[2] if len(data) > 2 else None try: diff --git a/sdk/core/corehttp/tests/async_tests/test_rest_http_response_async.py b/sdk/core/corehttp/tests/async_tests/test_rest_http_response_async.py index 37d14e178d4a..fb68ad10d67e 100644 --- a/sdk/core/corehttp/tests/async_tests/test_rest_http_response_async.py +++ b/sdk/core/corehttp/tests/async_tests/test_rest_http_response_async.py @@ -6,6 +6,7 @@ # NOTE: These tests are heavily inspired from the httpx test suite: https://github.com/encode/httpx/tree/master/tests # Thank you httpx for your wonderful tests! +import io import pytest from corehttp.rest import HttpRequest, AsyncHttpResponse @@ -220,14 +221,25 @@ async def test_urlencoded_content(send_request): ) -# @pytest.mark.asyncio -# async def test_multipart_files_content(send_request): -# request = HttpRequest( -# "POST", -# "/multipart/basic", -# files={"fileContent": io.BytesIO(b"")}, -# ) -# await send_request(request) +@pytest.mark.asyncio +async def test_multipart_files_content(send_request): + request = HttpRequest( + "POST", + "/multipart/basic", + files={"fileContent": io.BytesIO(b"")}, + ) + await send_request(request) + + +@pytest.mark.asyncio +async def test_multipart_data_and_files_content(send_request): + request = HttpRequest( + "POST", + "/multipart/data-and-files", + data={"message": "Hello, world!"}, + files={"fileContent": io.BytesIO(b"")}, + ) + await send_request(request) @pytest.mark.asyncio diff --git a/sdk/core/corehttp/tests/testserver_tests/coretestserver/coretestserver/test_routes/multipart.py b/sdk/core/corehttp/tests/testserver_tests/coretestserver/coretestserver/test_routes/multipart.py index 958b2108e3a3..a69b63551e46 100644 --- a/sdk/core/corehttp/tests/testserver_tests/coretestserver/coretestserver/test_routes/multipart.py +++ b/sdk/core/corehttp/tests/testserver_tests/coretestserver/coretestserver/test_routes/multipart.py @@ -25,7 +25,7 @@ def basic(): assert_with_message("content type", multipart_header_start, request.content_type[: len(multipart_header_start)]) if request.files: # aiohttp - assert_with_message("content length", 258, request.content_length) + assert_with_message("content length", 228, request.content_length) assert_with_message("num files", 1, len(request.files)) assert_with_message("has file named fileContent", True, bool(request.files.get("fileContent"))) file_content = request.files["fileContent"] @@ -37,7 +37,7 @@ def basic(): ) assert_with_message( "content disposition", - 'form-data; name="fileContent"; filename="fileContent"; filename*=utf-8\'\'fileContent', + 'form-data; name="fileContent"; filename="fileContent"', file_content.headers["Content-Disposition"], ) elif request.form: @@ -51,9 +51,30 @@ def basic(): @multipart_api.route("/data-and-files", methods=["POST"]) def data_and_files(): - assert_with_message("content type", multipart_header_start, request.content_type[: len(multipart_header_start)]) - assert_with_message("message", "Hello, world!", request.form["message"]) - assert_with_message("message", "", request.form["fileContent"]) + if request.files: + # aiohttp + assert_with_message("content type", multipart_header_start, request.content_type[: len(multipart_header_start)]) + assert_with_message("has file named fileContent", True, bool(request.files.get("fileContent"))) + assert_with_message("message", "Hello, world!", request.form["message"]) + file_content = request.files["fileContent"] + assert_with_message("file content type", "application/octet-stream", file_content.content_type) + assert_with_message("file content length", 14, file_content.content_length) + assert_with_message("filename", "fileContent", file_content.filename) + assert_with_message( + "has content disposition header", True, bool(file_content.headers.get("Content-Disposition")) + ) + assert_with_message( + "content disposition", + 'form-data; name="fileContent"; filename="fileContent"', + file_content.headers["Content-Disposition"], + ) + elif request.form: + # requests + assert_with_message("content type", multipart_header_start, request.content_type[: len(multipart_header_start)]) + assert_with_message("message", "Hello, world!", request.form["message"]) + assert_with_message("message", "", request.form["fileContent"]) + else: + return Response(status=400) return Response(status=200) From 42275c0c937e70ce9e8fbc7d7ed93d519ab91b2c Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 19 Oct 2023 12:01:05 -0700 Subject: [PATCH 2/4] add changelog entry --- sdk/core/corehttp/CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sdk/core/corehttp/CHANGELOG.md b/sdk/core/corehttp/CHANGELOG.md index 344783d61a1c..abc31e4e7021 100644 --- a/sdk/core/corehttp/CHANGELOG.md +++ b/sdk/core/corehttp/CHANGELOG.md @@ -1,5 +1,17 @@ # Release History +## 1.0.0b2 (Unreleased) + +### Features Added + +### Breaking Changes + +### Bugs Fixed + +- Fixed an issue with `multipart/form-data` in the async transport where `data` was not getting encoded into the request body. #32473 + +### Other Changes + ## 1.0.0b1 (2023-10-18) * Initial Release From 7a1ec90a44e532c1633e9e839e1522eb98fc6cc0 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 19 Oct 2023 12:19:43 -0700 Subject: [PATCH 3/4] no backcompat in corehttp, need to access _data --- sdk/core/corehttp/corehttp/transport/aiohttp/_aiohttp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/corehttp/corehttp/transport/aiohttp/_aiohttp.py b/sdk/core/corehttp/corehttp/transport/aiohttp/_aiohttp.py index 7a6df7356793..fec2c7cac37b 100644 --- a/sdk/core/corehttp/corehttp/transport/aiohttp/_aiohttp.py +++ b/sdk/core/corehttp/corehttp/transport/aiohttp/_aiohttp.py @@ -136,7 +136,7 @@ def _get_request_data(self, request: RestHttpRequest): :return: The request data """ if request._files: # pylint: disable=protected-access - form_data = aiohttp.FormData(request.data or {}) + form_data = aiohttp.FormData(request._data or {}) # pylint: disable=protected-access for form_file, data in request._files.items(): # pylint: disable=protected-access content_type = data[2] if len(data) > 2 else None try: From 572142fe140a6d216552299c52a1c7e6f68dcad3 Mon Sep 17 00:00:00 2001 From: Krista Pratico Date: Thu, 19 Oct 2023 14:22:57 -0700 Subject: [PATCH 4/4] bump version manually --- sdk/core/corehttp/corehttp/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/corehttp/corehttp/_version.py b/sdk/core/corehttp/corehttp/_version.py index 4a4d6826a682..54c0a38d152f 100644 --- a/sdk/core/corehttp/corehttp/_version.py +++ b/sdk/core/corehttp/corehttp/_version.py @@ -9,4 +9,4 @@ # regenerated. # -------------------------------------------------------------------------- -VERSION = "1.0.0b1" +VERSION = "1.0.0b2"