diff --git a/swift/proxy/server.py b/swift/proxy/server.py index de5f1cecae..b092f7f9a4 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -632,10 +632,10 @@ class Controller(object): if newest: ts = 0 if source: - ts = float(source.getheader('x-put-timestamp', - source.getheader('x-timestamp', 0))) - pts = float(possible_source.getheader('x-put-timestamp', - possible_source.getheader('x-timestamp', 0))) + ts = float(source.getheader('x-put-timestamp') or + source.getheader('x-timestamp') or 0) + pts = float(possible_source.getheader('x-put-timestamp') or + possible_source.getheader('x-timestamp') or 0) if pts > ts: source = possible_source continue @@ -956,6 +956,7 @@ class ObjectController(Controller): reader = req.environ['wsgi.input'].read data_source = iter(lambda: reader(self.app.client_chunk_size), '') source_header = req.headers.get('X-Copy-From') + source_resp = None if source_header: source_header = unquote(source_header) acct = req.path_info.split('/', 2)[1] @@ -971,6 +972,7 @@ class ObjectController(Controller): '/') source_req = req.copy_get() source_req.path_info = source_header + source_req.headers['X-Newest'] = 'true' orig_obj_name = self.object_name orig_container_name = self.container_name self.object_name = src_obj_name @@ -1103,6 +1105,9 @@ class ObjectController(Controller): if source_header: resp.headers['X-Copied-From'] = quote( source_header.split('/', 2)[2]) + if 'last-modified' in source_resp.headers: + resp.headers['X-Copied-From-Last-Modified'] = \ + source_resp.headers['last-modified'] for k, v in req.headers.items(): if k.lower().startswith('x-object-meta-'): resp.headers[k] = v diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index cbe8533f23..cc93877c45 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -211,8 +211,8 @@ def fake_http_connect(*code_iter, **kwargs): def getheader(self, name, default=None): return dict(self.getheaders()).get(name.lower(), default) - timestamps_iter = iter(kwargs.get('timestamps') or [None] * len(code_iter)) - etag_iter = iter(kwargs.get('etags') or ['1'] * len(code_iter)) + timestamps_iter = iter(kwargs.get('timestamps') or ['1'] * len(code_iter)) + etag_iter = iter(kwargs.get('etags') or [None] * len(code_iter)) x = kwargs.get('missing_container', [False] * len(code_iter)) if not isinstance(x, (tuple, list)): x = [x] * len(code_iter) @@ -1014,6 +1014,29 @@ class TestObjectController(unittest.TestCase): test_status_map((200, 200, 200), 200, ('1', '3', '1'), '3') test_status_map((200, 200, 200), 200, ('3', '3', '1'), '3') + def test_GET_newest(self): + with save_globals(): + controller = proxy_server.ObjectController(self.app, 'account', + 'container', 'object') + + def test_status_map(statuses, expected, timestamps, + expected_timestamp): + proxy_server.http_connect = \ + fake_http_connect(*statuses, timestamps=timestamps) + self.app.memcache.store = {} + req = Request.blank('/a/c/o', {}, headers={'x-newest': 'true'}) + self.app.update_request(req) + res = controller.GET(req) + self.assertEquals(res.status[:len(str(expected))], + str(expected)) + self.assertEquals(res.headers.get('last-modified'), + expected_timestamp) + + test_status_map((200, 200, 200), 200, ('1', '2', '3'), '3') + test_status_map((200, 200, 200), 200, ('1', '3', '2'), '3') + test_status_map((200, 200, 200), 200, ('1', '3', '1'), '3') + test_status_map((200, 200, 200), 200, ('3', '3', '1'), '3') + with save_globals(): controller = proxy_server.ObjectController(self.app, 'account', 'container', 'object') @@ -1725,8 +1748,10 @@ class TestObjectController(unittest.TestCase): headers={'Destination': 'c/o'}) req.account = 'a' proxy_server.http_connect = \ - fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201) - # acct cont acct cont objc obj obj obj + fake_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201, + 201) + # acct cont acct cont objc objc objc obj obj + # obj self.app.memcache.store = {} resp = controller.COPY(req) self.assertEquals(resp.status_int, 201) @@ -1738,8 +1763,10 @@ class TestObjectController(unittest.TestCase): req.account = 'a' controller.object_name = 'o/o2' proxy_server.http_connect = \ - fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201) - # acct cont acct cont objc obj obj obj + fake_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201, + 201) + # acct cont acct cont objc objc objc obj obj + # obj self.app.memcache.store = {} resp = controller.COPY(req) self.assertEquals(resp.status_int, 201) @@ -1750,8 +1777,10 @@ class TestObjectController(unittest.TestCase): req.account = 'a' controller.object_name = 'o' proxy_server.http_connect = \ - fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201) - # acct cont acct cont objc obj obj obj + fake_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201, + 201) + # acct cont acct cont objc objc objc obj obj + # obj self.app.memcache.store = {} resp = controller.COPY(req) self.assertEquals(resp.status_int, 201) @@ -1763,8 +1792,10 @@ class TestObjectController(unittest.TestCase): req.account = 'a' controller.object_name = 'o/o2' proxy_server.http_connect = \ - fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201) - # acct cont acct cont objc obj obj obj + fake_http_connect(200, 200, 200, 200, 200, 200, 200, 201, 201, + 201) + # acct cont acct cont objc objc objc obj obj + # obj self.app.memcache.store = {} resp = controller.COPY(req) self.assertEquals(resp.status_int, 201) @@ -1820,8 +1851,8 @@ class TestObjectController(unittest.TestCase): req.account = 'a' controller.object_name = 'o' proxy_server.http_connect = \ - fake_http_connect(200, 200, 200, 201, 201, 201) - # acct cont objc obj obj obj + fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201) + # acct cont objc objc objc obj obj obj self.app.memcache.store = {} resp = controller.COPY(req) self.assertEquals(resp.status_int, 201) @@ -1829,6 +1860,23 @@ class TestObjectController(unittest.TestCase): 'testing') self.assertEquals(resp.headers.get('x-object-meta-ours'), 'okay') + def test_COPY_newest(self): + with save_globals(): + controller = proxy_server.ObjectController(self.app, 'a', 'c', 'o') + req = Request.blank('/a/c/o', environ={'REQUEST_METHOD': 'COPY'}, + headers={'Destination': '/c/o'}) + req.account = 'a' + controller.object_name = 'o' + proxy_server.http_connect = \ + fake_http_connect(200, 200, 200, 200, 200, 201, 201, 201, + timestamps=('1', '1', '1', '3', '2', '4', '4', '4')) + # acct cont objc objc objc obj obj obj + self.app.memcache.store = {} + resp = controller.COPY(req) + self.assertEquals(resp.status_int, 201) + self.assertEquals(resp.headers['x-copied-from-last-modified'], + '3') + def test_chunked_put(self): class ChunkedFile():