Repository: localstack/localstack

Issue:
S3 Static website Index.html returned as XML
Not sure if I'm doing something wrong  or missing something daft, but when I attempted to set up a static website on s3 and request the website, i get the html page rendered a xml in chrome. Is it because the Content-Type is application/xml? 

Expected behaviour
Using Chrome browse to http://192.168.99.100:4572/TestBucket/index.html returns my webpage rendered correctly

Steps to reproduce
from a working directory containing my index.html page.
aws --endpoint-url=http://192.168.99.100:4572 s3api create-bucket --bucket TestBucket
aws --endpoint-url=http://192.168.99.100:4752 s3 sync . s3://TestBucket --acl public-read
aws --endpoint-url=http://192.168.99.100:4752 s3 website s3://TestBucket --index-document index.html



Discussion:
same issue here. Is it because of this?
https://github.com/localstack/localstack/blob/dd47af6e4a5f753fba97eae0d32551321382b258/localstack/services/s3/s3_listener.py#L556-L557
Same issue here. I am using Developer Tools to see the Content-type in the Response. PDFs work with content-type application/pdf, images work with content-type image/jpeg. But html pages are using application/xml. I tried changing the metadata with `awslocal s3 cp s3://<your bucket name> s3://<your bucket name> --exclude "*" --include "*.html" --content-type "text/html" --metadata-directive "REPLACE" --no-guess-mime-type --recursive --acl public-read` based off of this [post](https://stackoverflow.com/questions/41015200/how-to-change-content-type-of-amazon-s3-objects) but that didn't do squat.
Hello, I have the same issue with signed urls that access html files in a S3 bucket.

The html files are served with an 'application/xml' content type.

Is there a reason for the piece of python code shown above to exist ?

EDIT: I just read the file history and saw the related issues that lead to that line of code,  but AWS S3 returns text/html content-type if we set text/html while uploading the file. localstack is not consistent with AWS IMO.
I made a short-term fix that works for my team. In s3_listener.py, change the return_response method from

    # fix content-type: https://github.com/localstack/localstack/issues/618
    #                   https://github.com/localstack/localstack/issues/549
    if 'text/html' in response.headers.get('Content-Type', ''):
        response.headers['Content-Type'] = 'application/xml; charset=utf-8'

to

    # fix content-type: https://github.com/localstack/localstack/issues/618
    #                   https://github.com/localstack/localstack/issues/549
    if 'text/html' in response.headers.get('Content-Type', '')\
            and not response_content_str.startswith('<!doctype html'):
        response.headers['Content-Type'] = 'application/xml; charset=utf-8'

This assumes your HTML file starts with `<!doctype html>`. It's not a good long-term solution, but can be used to work around the problem for the time being.

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
@@ -681,7 +681,9 @@ def return_response(self, method, path, data, headers, response):
 
                 # fix content-type: https://github.com/localstack/localstack/issues/618
                 #                   https://github.com/localstack/localstack/issues/549
-                if 'text/html' in response.headers.get('Content-Type', ''):
+                #                   https://github.com/localstack/localstack/issues/854
+                if 'text/html' in response.headers.get('Content-Type', '') \
+                        and not response_content_str.startswith('<!doctype html'):
                     response.headers['Content-Type'] = 'application/xml; charset=utf-8'
 
                 reset_content_length = True
diff --git a/localstack/services/sns/sns_listener.py b/localstack/services/sns/sns_listener.py
--- a/localstack/services/sns/sns_listener.py
+++ b/localstack/services/sns/sns_listener.py
@@ -204,6 +204,7 @@ def publish_message(topic_arn, req_data, subscription_arn=None):
                 subject=req_data.get('Subject', [None])[0]
             )
         elif subscriber['Protocol'] in ['http', 'https']:
+            msg_type = (req_data.get('Type') or ['Notification'])[0]
             try:
                 message_body = create_sns_message_body(subscriber, req_data)
             except Exception as exc:
@@ -212,7 +213,7 @@ def publish_message(topic_arn, req_data, subscription_arn=None):
                 subscriber['Endpoint'],
                 headers={
                     'Content-Type': 'text/plain',
-                    'x-amz-sns-message-type': 'Notification'
+                    'x-amz-sns-message-type': msg_type
                 },
                 data=message_body,
                 verify=False)


Test patch:
diff --git a/tests/integration/test_sns.py b/tests/integration/test_sns.py
--- a/tests/integration/test_sns.py
+++ b/tests/integration/test_sns.py
@@ -42,7 +42,7 @@ def test_subscribe_http_endpoint(self):
         # create HTTP endpoint and connect it to SNS topic
         class MyUpdateListener(ProxyListener):
             def forward_request(self, method, path, data, headers):
-                records.append(json.loads(to_str(data)))
+                records.append((json.loads(to_str(data)), headers))
                 return 200
 
         records = []
@@ -53,7 +53,8 @@ def forward_request(self, method, path, data, headers):
         self.sns_client.subscribe(TopicArn=self.topic_arn, Protocol='http', Endpoint=queue_arn)
 
         def received():
-            assert records[0]['Type'] == 'SubscriptionConfirmation'
+            assert records[0][0]['Type'] == 'SubscriptionConfirmation'
+            assert records[0][1]['x-amz-sns-message-type'] == 'SubscriptionConfirmation'
 
         retry(received, retries=5, sleep=1)
         proxy.stop()