Repository: localstack/localstack

Issue:
Localstack doesn't return some metadata for S3 Range requests
Using s3 range request with localstack causes incorrect content type to be returned & some metadata to be missing:
To reproduce, put object:
`aws s3api put-object --endpoint http://localhost:3006 --bucket some-bucket --key some-dir/some-key --body text.txt --content-type text/plain --content-language en-US --cache-control no-cache`

And compare regular request vs range one:
`aws s3api get-object --endpoint http://localhost:3006 --bucket some-bucket --key some-dir/some-key output.txt --range bytes=0-3`
`aws s3api get-object --endpoint http://localhost:3006 --bucket some-bucket --key some-dir/some-key output.txt`

This works correctly (returns the same metadata regardless of using `--range bytes=0-3`) on aws s3.

Also:
Getting object with/without `range bytes=0-3` should return header "AcceptRanges: bytes" but it doesn't.
Localstack doesn't return some metadata for S3 Range requests
Using s3 range request with localstack causes incorrect content type to be returned & some metadata to be missing:
To reproduce, put object:
`aws s3api put-object --endpoint http://localhost:3006 --bucket some-bucket --key some-dir/some-key --body text.txt --content-type text/plain --content-language en-US --cache-control no-cache`

And compare regular request vs range one:
`aws s3api get-object --endpoint http://localhost:3006 --bucket some-bucket --key some-dir/some-key output.txt --range bytes=0-3`
`aws s3api get-object --endpoint http://localhost:3006 --bucket some-bucket --key some-dir/some-key output.txt`

This works correctly (returns the same metadata regardless of using `--range bytes=0-3`) on aws s3.

Also:
Getting object with/without `range bytes=0-3` should return header "AcceptRanges: bytes" but it doesn't.


Discussion:

Resolving patch:
diff --git a/localstack/services/s3/s3_listener.py b/localstack/services/s3/s3_listener.py
--- a/localstack/services/s3/s3_listener.py
+++ b/localstack/services/s3/s3_listener.py
@@ -320,6 +320,15 @@ def add_accept_range_header(response):
         response.headers['accept-ranges'] = 'bytes'
 
 
+def add_reponse_metadata_headers(response):
+    if response.headers.get('content-language') is None:
+        response.headers['content-language'] = 'en-US'
+    if response.headers.get('cache-control') is None:
+        response.headers['cache-control'] = 'no-cache'
+    if response.headers.get('content-encoding') is None:
+        response.headers['content-encoding'] = 'identity'
+
+
 def append_last_modified_headers(response, content=None):
     """Add Last-Modified header with current time
     (if the response content is an XML containing <LastModified>, add that instead)"""
@@ -1054,6 +1063,7 @@ def return_response(self, method, path, data, headers, response):
             # https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
             if method == 'GET':
                 add_accept_range_header(response)
+                add_reponse_metadata_headers(response)
                 query_map = urlparse.parse_qs(parsed.query, keep_blank_values=True)
                 for param_name, header_name in ALLOWED_HEADER_OVERRIDES.items():
                     if param_name in query_map:


Test patch:
diff --git a/tests/integration/test_s3.py b/tests/integration/test_s3.py
--- a/tests/integration/test_s3.py
+++ b/tests/integration/test_s3.py
@@ -320,6 +320,10 @@ def test_s3_get_get_object_headers(self):
         self.assertEqual(resp.get('AcceptRanges'), 'bytes')
         self.assertIn('x-amz-request-id', resp['ResponseMetadata']['HTTPHeaders'])
         self.assertIn('x-amz-id-2', resp['ResponseMetadata']['HTTPHeaders'])
+        self.assertIn('content-language', resp['ResponseMetadata']['HTTPHeaders'])
+        self.assertIn('cache-control', resp['ResponseMetadata']['HTTPHeaders'])
+        self.assertIn('content-encoding', resp['ResponseMetadata']['HTTPHeaders'])
+
         # clean up
         self._delete_bucket(bucket_name, [object_key])