Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/enhancement-s3-98637.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "enhancement",
"category": "crt",
"description": "Stop adding a ``Content-Length: 0`` header to GetObject requests made by the CRT transfer client."
}
10 changes: 0 additions & 10 deletions awscli/s3transfer/crt.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,16 +968,6 @@ def _convert_to_crt_http_request(self, botocore_http_request):
if crt_request.headers.get('Content-MD5') is not None:
crt_request.headers.remove("Content-MD5")

# In general, the CRT S3 client expects a content length header. It
# only expects a missing content length header if the body is not
# seekable. However, botocore does not set the content length header
# for GetObject API requests and so we set the content length to zero
# to meet the CRT S3 client's expectation that the content length
# header is set even if there is no body.
if crt_request.headers.get('Content-Length') is None:
if botocore_http_request.body is None:
crt_request.headers.add('Content-Length', "0")

# Botocore sets the Transfer-Encoding header when it cannot determine
# the content length of the request body (e.g. it's not seekable).
# However, CRT does not support this header, but it supports
Expand Down
8 changes: 5 additions & 3 deletions tests/functional/s3transfer/test_crt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,7 @@ def test_download(self):
self._assert_expected_crt_http_request(
callargs_kwargs["request"],
expected_http_method='GET',
expected_content_length=0,
expected_missing_headers=['Content-Length'],
)
self._assert_subscribers_called(future)
with open(self.filename, 'rb') as f:
Expand Down Expand Up @@ -1099,7 +1099,7 @@ def test_download_to_seekable_stream(self):
self._assert_expected_crt_http_request(
callargs_kwargs["request"],
expected_http_method='GET',
expected_content_length=0,
expected_missing_headers=['Content-Length'],
)
self._assert_subscribers_called(future)
with open(self.filename, 'rb') as f:
Expand Down Expand Up @@ -1134,7 +1134,7 @@ def test_download_to_nonseekable_stream(self):
self._assert_expected_crt_http_request(
callargs_kwargs["request"],
expected_http_method='GET',
expected_content_length=0,
expected_missing_headers=['Content-Length'],
)
self._assert_subscribers_called(future)
self.assertEqual(
Expand Down Expand Up @@ -1363,6 +1363,8 @@ def test_within_threshold_downloads_in_single_request(
request_args = download(size, MULTIPART_THRESHOLD)
assert request_args['type'] == awscrt.s3.S3RequestType.DEFAULT
assert request_args['operation_name'] == 'GetObject'
assert request_args['request'].headers.get('Content-Length') is None
assert request_args['request'].body_stream is None

def test_above_threshold_splits_download(self, download):
request_args = download(MULTIPART_THRESHOLD + 1, MULTIPART_THRESHOLD)
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/s3transfer/test_crt.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ def test_download_request(self):
self.assertEqual(self.expected_path, crt_request.path)
self.assertEqual(self.expected_host, crt_request.headers.get("host"))
self.assertIsNone(crt_request.headers.get("Authorization"))
# A GET has no body, so it is sent without a Content-Length header,
# just as botocore sends it.
self.assertIsNone(crt_request.headers.get("Content-Length"))
self.assertIsNone(crt_request.body_stream)

def test_delete_request(self):
callargs = CallArgs(
Expand All @@ -170,6 +174,9 @@ def test_delete_request(self):
self.assertEqual(self.expected_path, crt_request.path)
self.assertEqual(self.expected_host, crt_request.headers.get("host"))
self.assertIsNone(crt_request.headers.get("Authorization"))
# Botocore sets a zero Content-Length on bodyless non-GET requests.
self.assertEqual("0", crt_request.headers.get("Content-Length"))
self.assertIsNone(crt_request.body_stream)

def _create_crt_response_error(
self, status_code, body, operation_name=None, headers=None
Expand Down
Loading