Merge "mpu: fix last-modified time in UploadPartCopy response" into feature/mpu

This commit is contained in:
Zuul
2024-11-28 17:59:20 +00:00
committed by Gerrit Code Review
6 changed files with 71 additions and 9 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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):

View File

@@ -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()

View File

@@ -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())