diff --git a/moto/s3/utils.py b/moto/s3/utils.py index 7294c9ca..36ff56b5 100644 --- a/moto/s3/utils.py +++ b/moto/s3/utils.py @@ -73,7 +73,7 @@ def parse_region_from_url(url): def metadata_from_headers(headers): metadata = CaseInsensitiveDict() - meta_regex = re.compile(r"^x-amz-meta-([a-zA-Z0-9\-_]+)$", flags=re.IGNORECASE) + meta_regex = re.compile(r"^x-amz-meta-([a-zA-Z0-9\-_.]+)$", flags=re.IGNORECASE) for header, value in headers.items(): if isinstance(header, six.string_types): result = meta_regex.match(header) diff --git a/tests/test_s3/test_s3.py b/tests/test_s3/test_s3.py index 2f665f5e..e2ba9d8d 100644 --- a/tests/test_s3/test_s3.py +++ b/tests/test_s3/test_s3.py @@ -65,13 +65,16 @@ def reduced_min_part_size(f): class MyModel(object): - def __init__(self, name, value): + def __init__(self, name, value, metadata={}): self.name = name self.value = value + self.metadata = metadata def save(self): s3 = boto3.client("s3", region_name=DEFAULT_REGION_NAME) - s3.put_object(Bucket="mybucket", Key=self.name, Body=self.value) + s3.put_object( + Bucket="mybucket", Key=self.name, Body=self.value, Metadata=self.metadata + ) @mock_s3 @@ -133,6 +136,24 @@ def test_my_model_save(): assert body == "is awesome" +@mock_s3 +def test_object_metadata(): + """Metadata keys can contain certain special characters like dash and dot""" + # Create Bucket so that test can run + conn = boto3.resource("s3", region_name=DEFAULT_REGION_NAME) + conn.create_bucket(Bucket="mybucket") + #################################### + + metadata = {"meta": "simple", "my-meta": "dash", "meta.data": "namespaced"} + + model_instance = MyModel("steve", "is awesome", metadata=metadata) + model_instance.save() + + meta = conn.Object("mybucket", "steve").get()["Metadata"] + + assert meta == metadata + + @mock_s3 def test_key_etag(): conn = boto3.resource("s3", region_name=DEFAULT_REGION_NAME)