Merge "Only url-quote Keys when encoding-type=url"

This commit is contained in:
Zuul 2018-11-16 13:34:06 +00:00 committed by Gerrit Code Review
commit 6f699d877e
4 changed files with 61 additions and 28 deletions

View File

@ -16,6 +16,8 @@
from base64 import standard_b64encode as b64encode from base64 import standard_b64encode as b64encode
from base64 import standard_b64decode as b64decode from base64 import standard_b64decode as b64decode
from six.moves.urllib.parse import quote
from swift.common.http import HTTP_OK from swift.common.http import HTTP_OK
from swift.common.utils import json, public, config_true_value from swift.common.utils import json, public, config_true_value
@ -171,11 +173,12 @@ class BucketController(Controller):
SubElement(elem, 'Marker').text = req.params.get('marker') SubElement(elem, 'Marker').text = req.params.get('marker')
if is_truncated and 'delimiter' in req.params: if is_truncated and 'delimiter' in req.params:
if 'name' in objects[-1]: if 'name' in objects[-1]:
SubElement(elem, 'NextMarker').text = \ name = objects[-1]['name']
objects[-1]['name'] else:
if 'subdir' in objects[-1]: name = objects[-1]['subdir']
SubElement(elem, 'NextMarker').text = \ if encoding_type == 'url':
objects[-1]['subdir'] name = quote(name)
SubElement(elem, 'NextMarker').text = name
elif listing_type == 'version-2': elif listing_type == 'version-2':
if is_truncated: if is_truncated:
if 'name' in objects[-1]: if 'name' in objects[-1]:
@ -197,7 +200,7 @@ class BucketController(Controller):
if 'delimiter' in req.params: if 'delimiter' in req.params:
SubElement(elem, 'Delimiter').text = req.params['delimiter'] SubElement(elem, 'Delimiter').text = req.params['delimiter']
if encoding_type is not None: if encoding_type == 'url':
SubElement(elem, 'EncodingType').text = encoding_type SubElement(elem, 'EncodingType').text = encoding_type
SubElement(elem, 'IsTruncated').text = \ SubElement(elem, 'IsTruncated').text = \
@ -205,14 +208,18 @@ class BucketController(Controller):
for o in objects: for o in objects:
if 'subdir' not in o: if 'subdir' not in o:
name = o['name']
if encoding_type == 'url':
name = quote(name)
if listing_type == 'object-versions': if listing_type == 'object-versions':
contents = SubElement(elem, 'Version') contents = SubElement(elem, 'Version')
SubElement(contents, 'Key').text = o['name'] SubElement(contents, 'Key').text = name
SubElement(contents, 'VersionId').text = 'null' SubElement(contents, 'VersionId').text = 'null'
SubElement(contents, 'IsLatest').text = 'true' SubElement(contents, 'IsLatest').text = 'true'
else: else:
contents = SubElement(elem, 'Contents') contents = SubElement(elem, 'Contents')
SubElement(contents, 'Key').text = o['name'] SubElement(contents, 'Key').text = name
SubElement(contents, 'LastModified').text = \ SubElement(contents, 'LastModified').text = \
o['last_modified'][:-3] + 'Z' o['last_modified'][:-3] + 'Z'
if 's3_etag' in o: if 's3_etag' in o:
@ -231,9 +238,12 @@ class BucketController(Controller):
for o in objects: for o in objects:
if 'subdir' in o: if 'subdir' in o:
common_prefixes = SubElement(elem, 'CommonPrefixes') common_prefixes = SubElement(elem, 'CommonPrefixes')
SubElement(common_prefixes, 'Prefix').text = o['subdir'] name = o['subdir']
if encoding_type == 'url':
name = quote(name)
SubElement(common_prefixes, 'Prefix').text = name
body = tostring(elem, encoding_type=encoding_type) body = tostring(elem)
return HTTPOk(body=body, content_type='application/xml') return HTTPOk(body=body, content_type='application/xml')

View File

@ -67,7 +67,7 @@ from swift.common.swob import Range
from swift.common.utils import json, public from swift.common.utils import json, public
from swift.common.db import utf8encode from swift.common.db import utf8encode
from six.moves.urllib.parse import urlparse # pylint: disable=F0401 from six.moves.urllib.parse import quote, urlparse
from swift.common.middleware.s3api.controllers.base import Controller, \ from swift.common.middleware.s3api.controllers.base import Controller, \
bucket_operation, object_operation, check_container_existence bucket_operation, object_operation, check_container_existence
@ -319,7 +319,10 @@ class UploadsController(Controller):
# created. # created.
for u in uploads: for u in uploads:
upload_elem = SubElement(result_elem, 'Upload') upload_elem = SubElement(result_elem, 'Upload')
SubElement(upload_elem, 'Key').text = u['key'] name = u['key']
if encoding_type == 'url':
name = quote(name)
SubElement(upload_elem, 'Key').text = name
SubElement(upload_elem, 'UploadId').text = u['upload_id'] SubElement(upload_elem, 'UploadId').text = u['upload_id']
initiator_elem = SubElement(upload_elem, 'Initiator') initiator_elem = SubElement(upload_elem, 'Initiator')
SubElement(initiator_elem, 'ID').text = req.user_id SubElement(initiator_elem, 'ID').text = req.user_id
@ -335,7 +338,7 @@ class UploadsController(Controller):
elem = SubElement(result_elem, 'CommonPrefixes') elem = SubElement(result_elem, 'CommonPrefixes')
SubElement(elem, 'Prefix').text = p SubElement(elem, 'Prefix').text = p
body = tostring(result_elem, encoding_type=encoding_type) body = tostring(result_elem)
return HTTPOk(body=body, content_type='application/xml') return HTTPOk(body=body, content_type='application/xml')
@ -456,7 +459,10 @@ class UploadController(Controller):
result_elem = Element('ListPartsResult') result_elem = Element('ListPartsResult')
SubElement(result_elem, 'Bucket').text = req.container_name SubElement(result_elem, 'Bucket').text = req.container_name
SubElement(result_elem, 'Key').text = req.object_name name = req.object_name
if encoding_type == 'url':
name = quote(name)
SubElement(result_elem, 'Key').text = name
SubElement(result_elem, 'UploadId').text = upload_id SubElement(result_elem, 'UploadId').text = upload_id
initiator_elem = SubElement(result_elem, 'Initiator') initiator_elem = SubElement(result_elem, 'Initiator')
@ -484,7 +490,7 @@ class UploadController(Controller):
SubElement(part_elem, 'ETag').text = '"%s"' % i['hash'] SubElement(part_elem, 'ETag').text = '"%s"' % i['hash']
SubElement(part_elem, 'Size').text = str(i['bytes']) SubElement(part_elem, 'Size').text = str(i['bytes'])
body = tostring(result_elem, encoding_type=encoding_type) body = tostring(result_elem)
return HTTPOk(body=body, content_type='application/xml') return HTTPOk(body=body, content_type='application/xml')

View File

@ -14,7 +14,6 @@
# limitations under the License. # limitations under the License.
import lxml.etree import lxml.etree
from urllib import quote
from copy import deepcopy from copy import deepcopy
from pkg_resources import resource_stream # pylint: disable-msg=E0611 from pkg_resources import resource_stream # pylint: disable-msg=E0611
import six import six
@ -86,7 +85,7 @@ def fromstring(text, root_tag=None, logger=None):
return elem return elem
def tostring(tree, encoding_type=None, use_s3ns=True): def tostring(tree, use_s3ns=True):
if use_s3ns: if use_s3ns:
nsmap = tree.nsmap.copy() nsmap = tree.nsmap.copy()
nsmap[None] = XMLNS_S3 nsmap[None] = XMLNS_S3
@ -96,16 +95,6 @@ def tostring(tree, encoding_type=None, use_s3ns=True):
root.extend(deepcopy(tree.getchildren())) root.extend(deepcopy(tree.getchildren()))
tree = root tree = root
if encoding_type == 'url':
tree = deepcopy(tree)
for e in tree.iter():
# Some elements are not url-encoded even when we specify
# encoding_type=url.
blacklist = ['LastModified', 'ID', 'DisplayName', 'Initiated']
if e.tag not in blacklist:
if isinstance(e.text, six.string_types):
e.text = quote(e.text)
return lxml.etree.tostring(tree, xml_declaration=True, encoding='UTF-8') return lxml.etree.tostring(tree, xml_declaration=True, encoding='UTF-8')

View File

@ -17,6 +17,8 @@ import unittest
import cgi import cgi
import mock import mock
from six.moves.urllib.parse import quote
from swift.common import swob from swift.common import swob
from swift.common.swob import Request from swift.common.swob import Request
from swift.common.utils import json from swift.common.utils import json
@ -164,7 +166,33 @@ class TestS3ApiBucket(S3ApiTestCase):
self.assertEqual(len(names), len(self.objects)) self.assertEqual(len(names), len(self.objects))
for i in self.objects: for i in self.objects:
self.assertTrue(i[0] in names) self.assertIn(i[0], names)
def test_bucket_GET_url_encoded(self):
bucket_name = 'junk'
req = Request.blank('/%s?encoding-type=url' % bucket_name,
environ={'REQUEST_METHOD': 'GET'},
headers={'Authorization': 'AWS test:tester:hmac',
'Date': self.get_date_header()})
status, headers, body = self.call_s3api(req)
self.assertEqual(status.split()[0], '200')
elem = fromstring(body, 'ListBucketResult')
name = elem.find('./Name').text
self.assertEqual(name, bucket_name)
objects = elem.iterchildren('Contents')
names = []
for o in objects:
names.append(o.find('./Key').text)
self.assertEqual('2011-01-05T02:19:14.275Z',
o.find('./LastModified').text)
self.assertEqual('"0"', o.find('./ETag').text)
self.assertEqual(len(names), len(self.objects))
for i in self.objects:
self.assertIn(quote(i[0]), names)
def test_bucket_GET_subdir(self): def test_bucket_GET_subdir(self):
bucket_name = 'junk-subdir' bucket_name = 'junk-subdir'