When PUTting an S3 object to Localstack with attached Metadata, if a key contains an underscore it is replaced with a dash.

However, when the same request is performed against the real S3, the key is not modified.

Example Input:
key_with_underscores => test

Localstack Result:
key-with-underscores => test

S3 Result:
key_with_underscores => test

Localstack Version: 0.10.5

Tested using AWS v2 S3 client
if 'text/html' in response.headers.get('Content-Type', '') \
                        and not response_content_str.lower().startswith('<!doctype html'):
                    response.headers['Content-Type'] = 'application/xml; charset=utf-8'
                reset_content_length = True
            
            if method == 'DELETE':
                reset_content_length = True
            if reset_content_length:
                response.headers['content-length'] = len(response._content)
    def _update_location(self, content, bucket_name):
        bucket_name = normalize_bucket_name(bucket_name)
        host = config.HOSTNAME_EXTERNAL
        if ':' not in host:
            host = '%s:%s' % (host, config.PORT_S3)
        return re.sub(r'<Location>\s*([a-zA-Z0-9\-]+)://[^/]+/([^<]+)\s*</Location>',
            r'<Location>%s://%s/%s/\2</Location>' % (get_service_protocol(), host, bucket_name),
            content, flags=re.MULTILINE)
    @staticmethod
    def is_query_allowable(method, query):
        
        if not query:
            return True
        
        contains_cred = 'X-Amz-Credential' in query and 'X-Amz-Signature' in query
        contains_key = 'AWSAccessKeyId' in query and 'Signature' in query
        if (method == 'POST' and query.startswith('uploadId')) or contains_cred or contains_key:
            return True
UPDATE_S3 = ProxyListenerS3()