diff --git a/swift/proxy/server.py b/swift/proxy/server.py index 9838f86802..322294ecaa 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -1149,19 +1149,17 @@ class ObjectController(Controller): return HTTPPreconditionFailed(request=req, body='Destination header must be of the form ' '/') - new_source = '/' + self.container_name + '/' + self.object_name + source = '/' + self.container_name + '/' + self.object_name self.container_name = dest_container self.object_name = dest_object - new_headers = {} - for k, v in req.headers.items(): - new_headers[k] = v - new_headers['X-Copy-From'] = new_source - new_headers['Content-Length'] = 0 - del new_headers['Destination'] - new_path = '/' + self.account_name + dest - new_req = Request.blank(new_path, environ=req.environ, - headers=new_headers) - return self.PUT(new_req) + # re-write the existing request as a PUT instead of creating a new one + # since this one is already attached to the posthooklogger + req.method = 'PUT' + req.path_info = '/' + self.account_name + dest + req.headers['Content-Length'] = 0 + req.headers['X-Copy-From'] = source + del req.headers['Destination'] + return self.PUT(req) class ContainerController(Controller): diff --git a/test/functionalnosetests/test_container.py b/test/functionalnosetests/test_container.py index 96c0be91e6..8690fcde5d 100755 --- a/test/functionalnosetests/test_container.py +++ b/test/functionalnosetests/test_container.py @@ -115,6 +115,10 @@ class TestContainer(unittest.TestCase): resp.read() self.assert_(resp.status in (200, 204), resp.status) self.assertEquals(resp.getheader('x-container-meta-test'), 'Value') + resp = retry(delete, name) + resp.read() + self.assertEquals(resp.status, 204) + name = uuid4().hex resp = retry(put, name, '') resp.read() diff --git a/test/functionalnosetests/test_object.py b/test/functionalnosetests/test_object.py index 2e1668db0e..5975cf16a2 100644 --- a/test/functionalnosetests/test_object.py +++ b/test/functionalnosetests/test_object.py @@ -38,21 +38,110 @@ class TestObject(unittest.TestCase): if skip: raise SkipTest + def delete(url, token, parsed, conn, obj): + conn.request('DELETE', + '%s/%s/%s' % (parsed.path, self.container, obj), + '', {'X-Auth-Token': token}) + return check_response(conn) + + # get list of objects in container + def list(url, token, parsed, conn): + conn.request('GET', + '%s/%s' % (parsed.path, self.container), + '', {'X-Auth-Token': token}) + return check_response(conn) + resp = retry(list) + object_listing = resp.read() + self.assertEquals(resp.status, 200) + + # iterate over object listing and delete all objects + for obj in object_listing.splitlines(): + resp = retry(delete, obj) + resp.read() + self.assertEquals(resp.status, 204) + + # delete the container def delete(url, token, parsed, conn): - conn.request('DELETE', '%s/%s/%s' % (parsed.path, self.container, - self.obj), '', {'X-Auth-Token': token}) + conn.request('DELETE', parsed.path + '/' + self.container, '', + {'X-Auth-Token': token}) return check_response(conn) resp = retry(delete) resp.read() self.assertEquals(resp.status, 204) + def test_copy_object(self): + if skip: + raise SkipTest + + source = '%s/%s' % (self.container, self.obj) + dest = '%s/%s' % (self.container, 'test_copy') + + # get contents of source + def get_source(url, token, parsed, conn): + conn.request('GET', + '%s/%s' % (parsed.path, source), + '', {'X-Auth-Token': token}) + return check_response(conn) + resp = retry(get_source) + source_contents = resp.read() + self.assertEquals(resp.status, 200) + self.assertEquals(source_contents, 'test') + + # copy source to dest with X-Copy-From + def put(url, token, parsed, conn): + conn.request('PUT', '%s/%s' % (parsed.path, dest), '', + {'X-Auth-Token': token, + 'Content-Length': '0', + 'X-Copy-From': source}) + return check_response(conn) + resp = retry(put) + contents = resp.read() + self.assertEquals(resp.status, 201) + + # contents of dest should be the same as source + def get_dest(url, token, parsed, conn): + conn.request('GET', + '%s/%s' % (parsed.path, dest), + '', {'X-Auth-Token': token}) + return check_response(conn) + resp = retry(get_dest) + dest_contents = resp.read() + self.assertEquals(resp.status, 200) + self.assertEquals(dest_contents, source_contents) + + # delete the copy def delete(url, token, parsed, conn): - conn.request('DELETE', parsed.path + '/' + self.container, '', + conn.request('DELETE', '%s/%s' % (parsed.path, dest), '', {'X-Auth-Token': token}) return check_response(conn) resp = retry(delete) resp.read() self.assertEquals(resp.status, 204) + # verify dest does not exist + resp = retry(get_dest) + resp.read() + self.assertEquals(resp.status, 404) + + # copy source to dest with COPY + def copy(url, token, parsed, conn): + conn.request('COPY', '%s/%s' % (parsed.path, source), '', + {'X-Auth-Token': token, + 'Destination': dest}) + return check_response(conn) + resp = retry(copy) + contents = resp.read() + self.assertEquals(resp.status, 201) + + # contents of dest should be the same as source + resp = retry(get_dest) + dest_contents = resp.read() + self.assertEquals(resp.status, 200) + self.assertEquals(dest_contents, source_contents) + + # delete the copy + resp = retry(delete) + resp.read() + self.assertEquals(resp.status, 204) def test_public_object(self): if skip: