S3: Allow keynames with spaces (#5701)

This commit is contained in:
Bert Blommers 2022-11-22 22:41:02 -01:00 committed by GitHub
parent d6c438400e
commit 740f1f103e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 18 deletions

View File

@ -261,7 +261,7 @@ class S3Response(BaseResponse):
return status_code, headers, response_content
def _bucket_response(self, request, full_url):
querystring = self._get_querystring(full_url)
querystring = self._get_querystring(request, full_url)
method = request.method
region_name = parse_region_from_url(full_url, use_default_region=False)
if region_name is None:
@ -297,7 +297,17 @@ class S3Response(BaseResponse):
)
@staticmethod
def _get_querystring(full_url):
def _get_querystring(request, full_url):
# Flask's Request has the querystring already parsed
# In ServerMode, we can use this, instead of manually parsing this
if hasattr(request, "args"):
query_dict = dict()
for key, val in dict(request.args).items():
# The parse_qs-method returns List[str, List[Any]]
# Ensure that we confirm to the same response-type here
query_dict[key] = val if isinstance(val, list) else [val]
return query_dict
parsed_url = urlparse(full_url)
# full_url can be one of two formats, depending on the version of werkzeug used:
# http://foobaz.localhost:5000/?prefix=bar%2Bbaz

View File

@ -3285,7 +3285,9 @@ if settings.TEST_SERVER_MODE:
@mock_s3
@pytest.mark.parametrize("prefix", ["file", "file+else", "file&another"])
@pytest.mark.parametrize(
"prefix", ["file", "file+else", "file&another", "file another"]
)
def test_get_object_versions_with_prefix(prefix):
bucket_name = "testbucket-3113"
s3_resource = boto3.resource("s3", region_name=DEFAULT_REGION_NAME)

View File

@ -1,6 +1,8 @@
import io
from urllib.parse import urlparse, parse_qs
import sure # noqa # pylint: disable=unused-import
import pytest
import xmltodict
from flask.testing import FlaskClient
import moto.server as server
@ -32,7 +34,8 @@ def test_s3_server_get():
res.data.should.contain(b"ListAllMyBucketsResult")
def test_s3_server_bucket_create():
@pytest.mark.parametrize("key_name", ["bar_baz", "bar+baz", "baz bar"])
def test_s3_server_bucket_create(key_name):
test_client = authenticated_client()
res = test_client.put("/", "http://foobaz.localhost:5000/")
@ -45,22 +48,25 @@ def test_s3_server_bucket_create():
res.status_code.should.equal(200)
res.data.should.contain(b"ListBucketResult")
for key_name in ("bar_baz", "bar+baz"):
res = test_client.put(
f"/{key_name}", "http://foobaz.localhost:5000/", data="test value"
)
res.status_code.should.equal(200)
assert "ETag" in dict(res.headers)
res = test_client.put(
f"/{key_name}", "http://foobaz.localhost:5000/", data="test value"
)
res.status_code.should.equal(200)
assert "ETag" in dict(res.headers)
res = test_client.get(
"/", "http://foobaz.localhost:5000/", query_string={"prefix": key_name}
)
res.status_code.should.equal(200)
res.data.should.contain(b"Contents")
res = test_client.get(
"/", "http://foobaz.localhost:5000/", query_string={"prefix": key_name}
)
res.status_code.should.equal(200)
content = xmltodict.parse(res.data)["ListBucketResult"]["Contents"]
# If we receive a dict, we only received one result
# If content is of type list, our call returned multiple results - which is not correct
content.should.be.a(dict)
content["Key"].should.equal(key_name)
res = test_client.get(f"/{key_name}", "http://foobaz.localhost:5000/")
res.status_code.should.equal(200)
res.data.should.equal(b"test value")
res = test_client.get(f"/{key_name}", "http://foobaz.localhost:5000/")
res.status_code.should.equal(200)
res.data.should.equal(b"test value")
def test_s3_server_ignore_subdomain_for_bucketnames():