py3: Fix s3api header casing
Closes-Bug: #1853367 Change-Id: Ifb15ce50fc3bcfda9532a2c3dec542c272ea4933
This commit is contained in:
parent
cfb3ae6019
commit
b5c9dc1c9f
@ -16,13 +16,6 @@
|
||||
import six
|
||||
|
||||
|
||||
def _title(s):
|
||||
if six.PY2:
|
||||
return s.title()
|
||||
else:
|
||||
return s.encode('latin1').title().decode('latin1')
|
||||
|
||||
|
||||
class HeaderKeyDict(dict):
|
||||
"""
|
||||
A dict that title-cases all keys on the way in, so as to be
|
||||
@ -36,35 +29,43 @@ class HeaderKeyDict(dict):
|
||||
self.update(base_headers)
|
||||
self.update(kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _title(s):
|
||||
if six.PY2:
|
||||
return s.title()
|
||||
else:
|
||||
return s.encode('latin1').title().decode('latin1')
|
||||
|
||||
def update(self, other):
|
||||
if hasattr(other, 'keys'):
|
||||
for key in other.keys():
|
||||
self[_title(key)] = other[key]
|
||||
self[self._title(key)] = other[key]
|
||||
else:
|
||||
for key, value in other:
|
||||
self[_title(key)] = value
|
||||
self[self._title(key)] = value
|
||||
|
||||
def __getitem__(self, key):
|
||||
return dict.get(self, _title(key))
|
||||
return dict.get(self, self._title(key))
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
key = self._title(key)
|
||||
if value is None:
|
||||
self.pop(_title(key), None)
|
||||
self.pop(key, None)
|
||||
elif six.PY2 and isinstance(value, six.text_type):
|
||||
return dict.__setitem__(self, _title(key), value.encode('utf-8'))
|
||||
return dict.__setitem__(self, key, value.encode('utf-8'))
|
||||
elif six.PY3 and isinstance(value, six.binary_type):
|
||||
return dict.__setitem__(self, _title(key), value.decode('latin-1'))
|
||||
return dict.__setitem__(self, key, value.decode('latin-1'))
|
||||
else:
|
||||
return dict.__setitem__(self, _title(key), str(value))
|
||||
return dict.__setitem__(self, key, str(value))
|
||||
|
||||
def __contains__(self, key):
|
||||
return dict.__contains__(self, _title(key))
|
||||
return dict.__contains__(self, self._title(key))
|
||||
|
||||
def __delitem__(self, key):
|
||||
return dict.__delitem__(self, _title(key))
|
||||
return dict.__delitem__(self, self._title(key))
|
||||
|
||||
def get(self, key, default=None):
|
||||
return dict.get(self, _title(key), default)
|
||||
return dict.get(self, self._title(key), default)
|
||||
|
||||
def setdefault(self, key, value=None):
|
||||
if key not in self:
|
||||
@ -72,4 +73,4 @@ class HeaderKeyDict(dict):
|
||||
return self[key]
|
||||
|
||||
def pop(self, key, default=None):
|
||||
return dict.pop(self, _title(key), default)
|
||||
return dict.pop(self, self._title(key), default)
|
||||
|
@ -17,6 +17,7 @@ import re
|
||||
from collections import MutableMapping
|
||||
from functools import partial
|
||||
|
||||
from swift.common import header_key_dict
|
||||
from swift.common import swob
|
||||
from swift.common.utils import config_true_value
|
||||
from swift.common.request_helpers import is_sys_meta
|
||||
@ -26,42 +27,21 @@ from swift.common.middleware.s3api.utils import snake_to_camel, \
|
||||
from swift.common.middleware.s3api.etree import Element, SubElement, tostring
|
||||
|
||||
|
||||
class HeaderKey(str):
|
||||
class HeaderKeyDict(header_key_dict.HeaderKeyDict):
|
||||
"""
|
||||
A string object that normalizes string as S3 clients expect with title().
|
||||
Similar to the Swift's normal HeaderKeyDict class, but its key name is
|
||||
normalized as S3 clients expect.
|
||||
"""
|
||||
def title(self):
|
||||
if self.lower() == 'etag':
|
||||
@staticmethod
|
||||
def _title(s):
|
||||
s = header_key_dict.HeaderKeyDict._title(s)
|
||||
if s.lower() == 'etag':
|
||||
# AWS Java SDK expects only 'ETag'.
|
||||
return 'ETag'
|
||||
if self.lower().startswith('x-amz-'):
|
||||
if s.lower().startswith('x-amz-'):
|
||||
# AWS headers returned by S3 are lowercase.
|
||||
return self.lower()
|
||||
return str.title(self)
|
||||
|
||||
|
||||
class HeaderKeyDict(swob.HeaderKeyDict):
|
||||
"""
|
||||
Similar to the HeaderKeyDict class in Swift, but its key name is normalized
|
||||
as S3 clients expect.
|
||||
"""
|
||||
def __getitem__(self, key):
|
||||
return swob.HeaderKeyDict.__getitem__(self, HeaderKey(key))
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
return swob.HeaderKeyDict.__setitem__(self, HeaderKey(key), value)
|
||||
|
||||
def __contains__(self, key):
|
||||
return swob.HeaderKeyDict.__contains__(self, HeaderKey(key))
|
||||
|
||||
def __delitem__(self, key):
|
||||
return swob.HeaderKeyDict.__delitem__(self, HeaderKey(key))
|
||||
|
||||
def get(self, key, default=None):
|
||||
return swob.HeaderKeyDict.get(self, HeaderKey(key), default)
|
||||
|
||||
def pop(self, key, default=None):
|
||||
return swob.HeaderKeyDict.pop(self, HeaderKey(key), default)
|
||||
return swob.bytes_to_wsgi(swob.wsgi_to_bytes(s).lower())
|
||||
return s
|
||||
|
||||
|
||||
class S3ResponseBase(object):
|
||||
@ -116,7 +96,7 @@ class S3Response(S3ResponseBase, swob.Response):
|
||||
|
||||
# Handle swift headers
|
||||
for key, val in sw_headers.items():
|
||||
_key = key.lower()
|
||||
_key = swob.bytes_to_wsgi(swob.wsgi_to_bytes(key).lower())
|
||||
|
||||
if _key.startswith('x-object-meta-'):
|
||||
# Note that AWS ignores user-defined headers with '=' in the
|
||||
|
@ -35,6 +35,24 @@ class TestResponse(unittest.TestCase):
|
||||
else:
|
||||
self.assertEqual('"theetag"', s3resp.headers['ETag'])
|
||||
|
||||
def test_response_s3api_user_meta_headers(self):
|
||||
resp = Response(headers={
|
||||
'X-Object-Meta-Foo': 'Bar',
|
||||
'X-Object-Meta-Non-\xdcnicode-Value': '\xff',
|
||||
'X-Object-Sysmeta-Baz': 'quux',
|
||||
'Etag': 'unquoted',
|
||||
'Content-type': 'text/plain',
|
||||
'content-length': '0',
|
||||
})
|
||||
s3resp = S3Response.from_swift_resp(resp)
|
||||
self.assertEqual(dict(s3resp.headers), {
|
||||
'x-amz-meta-foo': 'Bar',
|
||||
'x-amz-meta-non-\xdcnicode-value': '\xff',
|
||||
'ETag': '"unquoted"',
|
||||
'Content-Type': 'text/plain',
|
||||
'Content-Length': '0',
|
||||
})
|
||||
|
||||
def test_response_s3api_sysmeta_headers(self):
|
||||
for _server_type in ('object', 'container'):
|
||||
swift_headers = HeaderKeyDict(
|
||||
|
Loading…
Reference in New Issue
Block a user