From aec5e6e01594a59136c3a5b2d219691bb9c88440 Mon Sep 17 00:00:00 2001 From: Qiming Tang Date: Tue, 29 Aug 2023 15:16:06 +0800 Subject: [PATCH 1/2] fix: header parsing in a 100 continue response. Restore `response_class` attribute unconditionally, regardless of whether header `Expect: 100-continue` appears. --- botocore/awsrequest.py | 2 +- tests/unit/test_awsrequest.py | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/botocore/awsrequest.py b/botocore/awsrequest.py index 9123e65c9d..84a1959bd2 100644 --- a/botocore/awsrequest.py +++ b/botocore/awsrequest.py @@ -92,7 +92,7 @@ def request(self, method, url, body=None, headers=None, *args, **kwargs): self._expect_header_set = True else: self._expect_header_set = False - self.response_class = self._original_response_cls + self.response_class = self._original_response_cls rval = super().request(method, url, body, headers, *args, **kwargs) self._expect_header_set = False return rval diff --git a/tests/unit/test_awsrequest.py b/tests/unit/test_awsrequest.py index a29a846116..978a4b5119 100644 --- a/tests/unit/test_awsrequest.py +++ b/tests/unit/test_awsrequest.py @@ -486,6 +486,46 @@ def test_expect_100_continue_no_response_from_server(self): response = conn.getresponse() self.assertEqual(response.status, 307) + def test_expect_100_continue_followed_by_another_request(self): + with mock.patch('urllib3.util.wait_for_read') as wait_mock: + # Shows the server first sending a 200 ok response + # then a 200 ok response. + s = FakeSocket( + b'HTTP/1.1 200 OK\r\n' + b'Content-Length: 0\r\n' + b'\r\n' + b'HTTP/1.1 100 Continue\r\n' + b'\r\n' + b'HTTP/1.1 200 OK\r\n' + b'Content-Length: 0\r\n' + ) + conn = AWSHTTPConnection('s3.amazonaws.com', 443) + conn.sock = s + wait_mock.return_value = True + + # The first request expecting a 100 continue response. + # But the server will send a 200 ok instead. + conn.request( + 'PUT', '/bucket/foo', b'body', {'Expect': b'100-continue'} + ) + # Assert that we waited for the 100-continue response + self.assertEqual(wait_mock.call_count, 1) + response = conn.getresponse() + self.assertEqual(response.status, 200) + response.close() + + # The client send the second request, expecting 100 continue either, + # using the same connection + conn.request( + 'PUT', '/bucket/foo', b'body', {'Expect': b'100-continue'} + ) + self.assertEqual(wait_mock.call_count, 2) + response = conn.getresponse() + + # The second 'HTTP/1.1 200 OK\r\n' should be consumed by http.client.HTTPResponse._read_status(). + # Only after this, the following header will be parsed correctly by email.parser.Parser.parsestr(). + assert not getattr(response.headers, "defects", []) + def test_message_body_is_file_like_object(self): # Shows the server first sending a 100 continue response # then a 200 ok response. From e90910c15025aac40d68dbdcc604b2c9cd9c8c7f Mon Sep 17 00:00:00 2001 From: Qiming Tang Date: Thu, 31 Aug 2023 16:12:42 +0800 Subject: [PATCH 2/2] fix trailing whitespace --- tests/unit/test_awsrequest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_awsrequest.py b/tests/unit/test_awsrequest.py index 978a4b5119..be2e76aa79 100644 --- a/tests/unit/test_awsrequest.py +++ b/tests/unit/test_awsrequest.py @@ -514,7 +514,7 @@ def test_expect_100_continue_followed_by_another_request(self): self.assertEqual(response.status, 200) response.close() - # The client send the second request, expecting 100 continue either, + # The client send the second request, expecting 100 continue either, # using the same connection conn.request( 'PUT', '/bucket/foo', b'body', {'Expect': b'100-continue'}