Merge "mpu: fix last-modified time in UploadPartCopy response" into feature/mpu
This commit is contained in:
@@ -729,7 +729,10 @@ class MPUSessionHandler(BaseMPUHandler):
|
||||
for k, v in self.req.headers.items():
|
||||
# TODO: should we return 400 is client sends x-delete-at with a
|
||||
# part upload, or just ignore it?
|
||||
if k.lower() in ('content-length', 'transfer-encoding', 'etag'):
|
||||
if k.lower() in ('content-length',
|
||||
'transfer-encoding',
|
||||
'etag',
|
||||
'x-timestamp'):
|
||||
headers[k] = v
|
||||
part_req = self.make_subrequest(
|
||||
path=part_path, method='PUT', body=self.req.body, headers=headers)
|
||||
|
||||
@@ -193,17 +193,14 @@ class PartController(Controller):
|
||||
|
||||
part_number = req.validate_part_number()
|
||||
upload_id = get_valid_upload_id(req)
|
||||
# TODO: why is timestamp set here?
|
||||
req_timestamp = S3Timestamp.now()
|
||||
req.headers['X-Timestamp'] = req_timestamp.internal
|
||||
self._check_copy_source_range(req)
|
||||
query = {'upload-id': upload_id,
|
||||
'part-number': part_number}
|
||||
resp = req.get_response(self.app, query=query)
|
||||
|
||||
if 'X-Amz-Copy-Source' in req.headers:
|
||||
resp.append_copy_resp_body(req.controller_name,
|
||||
req_timestamp.s3xmlformat)
|
||||
ts = S3Timestamp.from_http_date(resp.headers['Last-Modified'])
|
||||
resp.append_copy_resp_body(req.controller_name, ts.s3xmlformat)
|
||||
|
||||
resp.status = 200
|
||||
return resp
|
||||
|
||||
@@ -23,6 +23,8 @@ import time
|
||||
|
||||
import six
|
||||
|
||||
from email.utils import parsedate
|
||||
|
||||
|
||||
NORMAL_FORMAT = "%016.05f"
|
||||
INTERNAL_FORMAT = NORMAL_FORMAT + '_%016x'
|
||||
@@ -218,6 +220,21 @@ class Timestamp(object):
|
||||
# function delta_to_microseconds(), but written in Python.
|
||||
return cls(delta.total_seconds())
|
||||
|
||||
@classmethod
|
||||
def from_http_date(cls, http_date_string):
|
||||
"""
|
||||
Parse an HTTP date header (e.g. 'Fri, 16 Jan 1970 06:56:07 GMT') to
|
||||
a Timestamp. The timezone is assumed to be GMT (UTC).
|
||||
|
||||
:param http_date_string: a string formatted as per an RFC2822 date
|
||||
header.
|
||||
:return: an instance of this class.
|
||||
"""
|
||||
parts = parsedate(http_date_string)
|
||||
# PY2 does not have datetime.timestamp...
|
||||
delta = datetime.datetime(*(parts[:7])) - EPOCH
|
||||
return cls(delta.total_seconds())
|
||||
|
||||
def ceil(self):
|
||||
"""
|
||||
Return the 'normal' part of the timestamp rounded up to the nearest
|
||||
@@ -372,7 +389,7 @@ EPOCH = datetime.datetime(1970, 1, 1)
|
||||
def last_modified_date_to_timestamp(last_modified_date_str):
|
||||
"""
|
||||
Convert a last modified date (like you'd get from a container listing,
|
||||
e.g. 2014-02-28T23:22:36.698390) to a float.
|
||||
e.g. 2014-02-28T23:22:36.698390) to a Timestamp.
|
||||
"""
|
||||
return Timestamp.from_isoformat(last_modified_date_str)
|
||||
|
||||
|
||||
@@ -628,7 +628,11 @@ class TestMPUMiddleware(BaseTestMPUMiddleware):
|
||||
headers={'Etag': 'test-etag',
|
||||
'Transfer-Encoding': 'test-encoding'},
|
||||
body=b'testing')
|
||||
resp = req.get_response(self.mw)
|
||||
|
||||
ts_now = Timestamp.now()
|
||||
with mock.patch('swift.common.utils.Timestamp.now',
|
||||
return_value=ts_now):
|
||||
resp = req.get_response(self.mw)
|
||||
self.assertEqual(201, resp.status_int)
|
||||
exp_etag = md5(b'testing', usedforsecurity=False).hexdigest()
|
||||
self.assertEqual('"%s"' % exp_etag, resp.headers.get('Etag'))
|
||||
@@ -641,7 +645,8 @@ class TestMPUMiddleware(BaseTestMPUMiddleware):
|
||||
'User-Agent': 'Swift',
|
||||
'Etag': 'test-etag',
|
||||
'Transfer-Encoding': 'test-encoding',
|
||||
'X-Backend-Allow-Reserved-Names': 'true'}
|
||||
'X-Backend-Allow-Reserved-Names': 'true',
|
||||
'X-Timestamp': ts_now.normal}
|
||||
self.assertEqual(exp_put_hdrs, actual_put_hdrs)
|
||||
|
||||
def test_upload_part(self):
|
||||
|
||||
@@ -1208,6 +1208,25 @@ class TestResponse(unittest.TestCase):
|
||||
req = swob.Request.blank('/')
|
||||
return req.get_response(test_app)
|
||||
|
||||
def test_last_modified(self):
|
||||
resp = self._get_response()
|
||||
self.assertIsNone(resp.last_modified)
|
||||
self.assertNotIn('Last-Modified', resp.headers)
|
||||
|
||||
resp.last_modified = 1234567.12345
|
||||
self.assertEqual(
|
||||
datetime.datetime.fromtimestamp(
|
||||
1234567, tz=utils.timestamp.UTC),
|
||||
resp.last_modified)
|
||||
self.assertEqual('Thu, 15 Jan 1970 06:56:07 GMT',
|
||||
resp.headers['Last-Modified'])
|
||||
|
||||
resp.headers['Last-Modified'] = 'Fri, 16 Jan 1970 06:56:07 GMT'
|
||||
self.assertEqual(
|
||||
datetime.datetime.fromtimestamp(
|
||||
1320967, tz=utils.timestamp.UTC),
|
||||
resp.last_modified)
|
||||
|
||||
def test_properties(self):
|
||||
resp = self._get_response()
|
||||
|
||||
|
||||
@@ -148,6 +148,27 @@ class TestTimestamp(unittest.TestCase):
|
||||
self.assertIsInstance(ts, timestamp.Timestamp)
|
||||
self.assertEqual(ts, timestamp.Timestamp.from_isoformat(ts.isoformat))
|
||||
|
||||
def test_from_http_date(self):
|
||||
ts = timestamp.Timestamp.from_http_date(
|
||||
'Thu, 15 Jan 1970 06:56:07 GMT')
|
||||
self.assertIsInstance(ts, timestamp.Timestamp)
|
||||
self.assertEqual(1234567.0, float(ts))
|
||||
|
||||
ts = timestamp.Timestamp.from_http_date(
|
||||
'Thu, 15 Jan 1970 06:56:07')
|
||||
self.assertIsInstance(ts, timestamp.Timestamp)
|
||||
self.assertEqual(1234567.0, float(ts))
|
||||
|
||||
ts = timestamp.Timestamp.from_http_date(
|
||||
'Thu 15 Jan 1970 06:56:07 GMT')
|
||||
self.assertIsInstance(ts, timestamp.Timestamp)
|
||||
self.assertEqual(1234567.0, float(ts))
|
||||
|
||||
ts = timestamp.Timestamp.from_http_date(
|
||||
'Thursday, 15 Jan 1970 06:56:07 GMT')
|
||||
self.assertIsInstance(ts, timestamp.Timestamp)
|
||||
self.assertEqual(1234567.0, float(ts))
|
||||
|
||||
def test_ceil(self):
|
||||
self.assertEqual(0.0, timestamp.Timestamp(0).ceil())
|
||||
self.assertEqual(1.0, timestamp.Timestamp(0.00001).ceil())
|
||||
|
||||
Reference in New Issue
Block a user