S3: Allow keynames with spaces (#5701)
This commit is contained in:
parent
d6c438400e
commit
740f1f103e
@ -261,7 +261,7 @@ class S3Response(BaseResponse):
|
|||||||
return status_code, headers, response_content
|
return status_code, headers, response_content
|
||||||
|
|
||||||
def _bucket_response(self, request, full_url):
|
def _bucket_response(self, request, full_url):
|
||||||
querystring = self._get_querystring(full_url)
|
querystring = self._get_querystring(request, full_url)
|
||||||
method = request.method
|
method = request.method
|
||||||
region_name = parse_region_from_url(full_url, use_default_region=False)
|
region_name = parse_region_from_url(full_url, use_default_region=False)
|
||||||
if region_name is None:
|
if region_name is None:
|
||||||
@ -297,7 +297,17 @@ class S3Response(BaseResponse):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@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)
|
parsed_url = urlparse(full_url)
|
||||||
# full_url can be one of two formats, depending on the version of werkzeug used:
|
# full_url can be one of two formats, depending on the version of werkzeug used:
|
||||||
# http://foobaz.localhost:5000/?prefix=bar%2Bbaz
|
# http://foobaz.localhost:5000/?prefix=bar%2Bbaz
|
||||||
|
@ -3285,7 +3285,9 @@ if settings.TEST_SERVER_MODE:
|
|||||||
|
|
||||||
|
|
||||||
@mock_s3
|
@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):
|
def test_get_object_versions_with_prefix(prefix):
|
||||||
bucket_name = "testbucket-3113"
|
bucket_name = "testbucket-3113"
|
||||||
s3_resource = boto3.resource("s3", region_name=DEFAULT_REGION_NAME)
|
s3_resource = boto3.resource("s3", region_name=DEFAULT_REGION_NAME)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import io
|
import io
|
||||||
from urllib.parse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
import sure # noqa # pylint: disable=unused-import
|
import sure # noqa # pylint: disable=unused-import
|
||||||
|
import pytest
|
||||||
|
import xmltodict
|
||||||
|
|
||||||
from flask.testing import FlaskClient
|
from flask.testing import FlaskClient
|
||||||
import moto.server as server
|
import moto.server as server
|
||||||
@ -32,7 +34,8 @@ def test_s3_server_get():
|
|||||||
res.data.should.contain(b"ListAllMyBucketsResult")
|
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()
|
test_client = authenticated_client()
|
||||||
|
|
||||||
res = test_client.put("/", "http://foobaz.localhost:5000/")
|
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.status_code.should.equal(200)
|
||||||
res.data.should.contain(b"ListBucketResult")
|
res.data.should.contain(b"ListBucketResult")
|
||||||
|
|
||||||
for key_name in ("bar_baz", "bar+baz"):
|
res = test_client.put(
|
||||||
res = test_client.put(
|
f"/{key_name}", "http://foobaz.localhost:5000/", data="test value"
|
||||||
f"/{key_name}", "http://foobaz.localhost:5000/", data="test value"
|
)
|
||||||
)
|
res.status_code.should.equal(200)
|
||||||
res.status_code.should.equal(200)
|
assert "ETag" in dict(res.headers)
|
||||||
assert "ETag" in dict(res.headers)
|
|
||||||
|
|
||||||
res = test_client.get(
|
res = test_client.get(
|
||||||
"/", "http://foobaz.localhost:5000/", query_string={"prefix": key_name}
|
"/", "http://foobaz.localhost:5000/", query_string={"prefix": key_name}
|
||||||
)
|
)
|
||||||
res.status_code.should.equal(200)
|
res.status_code.should.equal(200)
|
||||||
res.data.should.contain(b"Contents")
|
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 = test_client.get(f"/{key_name}", "http://foobaz.localhost:5000/")
|
||||||
res.status_code.should.equal(200)
|
res.status_code.should.equal(200)
|
||||||
res.data.should.equal(b"test value")
|
res.data.should.equal(b"test value")
|
||||||
|
|
||||||
|
|
||||||
def test_s3_server_ignore_subdomain_for_bucketnames():
|
def test_s3_server_ignore_subdomain_for_bucketnames():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user