Repository: localstack/localstack

Issue:
bug: Incorrect content-type for files from an s3 website bucket
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

When making a website request to an s3 bucket, the content-type response header is incorrent.

### Expected Behavior

The `content-type` header in the response from the s3 website endpoint for an object should be the same as the `ContentType` of the object.

### How are you starting LocalStack?

With a docker-compose file

### Steps To Reproduce

#### How are you starting localstack

A reduced compose file:

```yaml
version: "3.9"
services:
  s3:
    image: localstack/localstack:latest
    ports:
      - "4566-4583:4566-4583"
    environment:
      - AWS_DEFAULT_REGION=eu-west-1
      - EDGE_PORT=4566
      - SERVICES=s3
      - DEBUG=1
      - DATA_DIR=/tmp/localstack/data
      - S3_SKIP_SIGNATURE_VALIDATION=0
    volumes:
      - "./.localstack:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
```

#### Client commands

```shell
# bucket setup
awslocal s3api create-bucket --bucket public
awslocal s3api put-bucket-acl --bucket public --acl public-read
awslocal s3 website s3://public --index-document index.html

# sample object
echo  "'use strict;'" > script.js
awslocal s3api put-object --bucket public --body script.js --content-type "application/javascript; charset=utf-8" --key script.js
```

Confirm the metadata of the object has the expected `ContentType` metadata:

```shell
awslocal s3api head-object --bucket public --key script.js
```

The result will look like:

```json
{
    "LastModified": "Wed, 25 Aug 2021 13:36:20 GMT",
    "ContentLength": 14,
    "ETag": "\"63a55f759bd133b92f61ae079fd2cc73\"",
    "ContentType": "application/javascript; charset=utf-8",
    "Metadata": {}
}
```

A curl (either on the host machine or in the container) to the website endpoint for the object results in an incorrect content-type header (`text/html; charset=utf-8`):

```shell
curl --resolve public.s3-website.localhost.localstack.cloud:4566:127.0.0.1 public.s3-website.localhost.localstack.cloud:4566/script.js -v
```

The output:

```
* Added public.s3-website.localhost.localstack.cloud:4566:127.0.0.1 to DNS cache
* Hostname public.s3-website.localhost.localstack.cloud was found in DNS cache
*   Trying 127.0.0.1:4566...
* TCP_NODELAY set
* Connected to public.s3-website.localhost.localstack.cloud (127.0.0.1) port 4566 (#0)
> GET /script.js HTTP/1.1
> Host: public.s3-website.localhost.localstack.cloud:4566
> User-Agent: curl/7.67.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 
< content-type: text/html; charset=utf-8
< content-length: 14
< last-modified: Wed, 25 Aug 2021 13:36:43 GMT
< x-amz-request-id: 2E0649A71F21ACAC
< x-amz-id-2: MzRISOwyjmnup2E0649A71F21ACAC7/JypPGXLh0OVFGcJaaO3KW/hRAqKOpIEEp
< accept-ranges: bytes
< content-language: en-US
< access-control-allow-origin: *
< access-control-allow-methods: HEAD,GET,PUT,POST,DELETE,OPTIONS,PATCH
< access-control-allow-headers: authorization,content-type,content-length,content-md5,cache-control,x-amz-content-sha256,x-amz-date,x-amz-security-token,x-amz-user-agent,x-amz-target,x-amz-acl,x-amz-version-id,x-localstack-target,x-amz-tagging,amz-sdk-invocation-id,amz-sdk-request
< access-control-expose-headers: x-amz-version-id
< connection: close
< date: Wed, 25 Aug 2021 13:36:43 GMT
< server: hypercorn-h11
< 
'use strict;'
* Closing connection 0
```

### Environment

```markdown
- Host OS: macos big-sur 11.5.1
- LocalStack: latest
```


### Anything else?

_No response_


Discussion:
A crude fix for this is to replace: https://github.com/localstack/localstack/blob/e707aa07635d9a14e54dee002b4e83a21e7a58f2/localstack/services/s3/s3_listener.py#L1633-L1634 with:

```python
            object = s3_client.get_object(Bucket=bucket_name, Key=path)                               
            content = object["Body"].read()                                                                        
            headers = { "Content-Type": object["ContentType"] }                               
            return requests_response(status_code=200, content=content, headers=headers)
```

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
@@ -1630,8 +1630,10 @@ def serve_static_website(headers, path, bucket_name):
     try:
         if path != "/":
             path = path.lstrip("/")
-            content = s3_client.get_object(Bucket=bucket_name, Key=path)["Body"].read()
-            return requests_response(status_code=200, content=content)
+            obj = s3_client.get_object(Bucket=bucket_name, Key=path)
+            content = obj["Body"].read()
+            headers = {"Content-Type": obj["ContentType"]} if obj.get("ContentType") else {}
+            return requests_response(status_code=200, content=content, headers=headers)
     except ClientError:
         LOGGER.debug("No such key found. %s" % path)
 


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
@@ -1147,6 +1147,12 @@ def test_s3_static_website_hosting(self):
         self.s3_client.put_object(Bucket=bucket_name, Key="test/index.html", Body="index")
         self.s3_client.put_object(Bucket=bucket_name, Key="test/error.html", Body="error")
         self.s3_client.put_object(Bucket=bucket_name, Key="actual/key.html", Body="key")
+        self.s3_client.put_object(
+            Bucket=bucket_name,
+            Key="with-content-type/key.js",
+            Body="some js",
+            ContentType="application/javascript; charset=utf-8",
+        )
         self.s3_client.put_bucket_website(
             Bucket=bucket_name,
             WebsiteConfiguration={
@@ -1166,6 +1172,16 @@ def test_s3_static_website_hosting(self):
         self.assertEqual(200, response.status_code)
         self.assertEqual("key", response.text)
 
+        # key with specified content-type
+        url = "https://{}.{}:{}/with-content-type/key.js".format(
+            bucket_name, constants.S3_STATIC_WEBSITE_HOSTNAME, config.EDGE_PORT
+        )
+        response = requests.get(url, headers=headers, verify=False)
+        self.assertEqual(200, response.status_code)
+        self.assertEqual("some js", response.text)
+        self.assertIn("content-type", response.headers)
+        self.assertEqual("application/javascript; charset=utf-8", response.headers["content-type"])
+
         # index document
         url = "https://{}.{}:{}/test".format(
             bucket_name, constants.S3_STATIC_WEBSITE_HOSTNAME, config.EDGE_PORT