diff --git a/swift/common/middleware/container_quotas.py b/swift/common/middleware/container_quotas.py index 35a9b98eb7..3edaf1fb2a 100644 --- a/swift/common/middleware/container_quotas.py +++ b/swift/common/middleware/container_quotas.py @@ -79,9 +79,21 @@ class ContainerQuotaMiddleware(object): return HTTPBadRequest(body='Invalid count quota.') # check user uploads against quotas - elif obj and req.method == 'PUT': - container_info = get_container_info( - req.environ, self.app, swift_source='CQ') + elif obj and req.method in ('PUT', 'COPY'): + container_info = None + if req.method == 'PUT': + container_info = get_container_info( + req.environ, self.app, swift_source='CQ') + if req.method == 'COPY' and 'Destination' in req.headers: + dest = req.headers.get('Destination').lstrip('/') + path_info = req.environ['PATH_INFO'] + req.environ['PATH_INFO'] = "/%s/%s/%s" % ( + version, account, dest) + try: + container_info = get_container_info( + req.environ, self.app, swift_source='CQ') + finally: + req.environ['PATH_INFO'] = path_info if not container_info or not is_success(container_info['status']): # this will hopefully 404 later return self.app @@ -90,10 +102,11 @@ class ContainerQuotaMiddleware(object): 'bytes' in container_info and \ container_info['meta']['quota-bytes'].isdigit(): content_length = (req.content_length or 0) - if 'x-copy-from' in req.headers: - src_cont, src_obj = check_copy_from_header(req) + if 'x-copy-from' in req.headers or req.method == 'COPY': + if 'x-copy-from' in req.headers: + container, obj = check_copy_from_header(req) path = '/%s/%s/%s/%s' % (version, account, - src_cont, src_obj) + container, obj) object_info = get_object_info(req.environ, self.app, path) if not object_info or not object_info['length']: content_length = 0 diff --git a/test/unit/common/middleware/test_quotas.py b/test/unit/common/middleware/test_quotas.py index 1afcbf78c9..da060e1229 100644 --- a/test/unit/common/middleware/test_quotas.py +++ b/test/unit/common/middleware/test_quotas.py @@ -106,6 +106,18 @@ class TestContainerQuotas(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 413) + def test_exceed_bytes_quota_copy_verb(self): + app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) + cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '2'}}) + + req = Request.blank('/v1/a/c2/o2', + environ={'REQUEST_METHOD': 'COPY', + 'swift.object/a/c2/o2': {'length': 10}, + 'swift.cache': cache}, + headers={'Destination': '/c/o'}) + res = req.get_response(app) + self.assertEquals(res.status_int, 413) + def test_not_exceed_bytes_quota(self): app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '100'}}) @@ -127,6 +139,17 @@ class TestContainerQuotas(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 200) + def test_not_exceed_bytes_quota_copy_verb(self): + app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) + cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '100'}}) + req = Request.blank('/v1/a/c2/o2', + environ={'REQUEST_METHOD': 'COPY', + 'swift.object/a/c2/o2': {'length': 10}, + 'swift.cache': cache}, + headers={'Destination': '/c/o'}) + res = req.get_response(app) + self.assertEquals(res.status_int, 200) + def test_bytes_quota_copy_from_no_src(self): app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '100'}}) @@ -148,6 +171,17 @@ class TestContainerQuotas(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 412) + def test_bytes_quota_copy_verb_no_src(self): + app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) + cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '100'}}) + req = Request.blank('/v1/a/c2/o3', + environ={'REQUEST_METHOD': 'COPY', + 'swift.object/a/c2/o2': {'length': 10}, + 'swift.cache': cache}, + headers={'Destination': '/c/o'}) + res = req.get_response(app) + self.assertEquals(res.status_int, 200) + def test_exceed_counts_quota(self): app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '1'}}) @@ -169,6 +203,16 @@ class TestContainerQuotas(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 413) + def test_exceed_counts_quota_copy_verb(self): + app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) + cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '1'}}) + req = Request.blank('/v1/a/c2/o2', + environ={'REQUEST_METHOD': 'COPY', + 'swift.cache': cache}, + headers={'Destination': '/c/o'}) + res = req.get_response(app) + self.assertEquals(res.status_int, 413) + def test_not_exceed_counts_quota(self): app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '2'}}) @@ -189,6 +233,16 @@ class TestContainerQuotas(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 200) + def test_not_exceed_counts_quota_copy_verb(self): + app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) + cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '2'}}) + req = Request.blank('/v1/a/c2/o2', + environ={'REQUEST_METHOD': 'COPY', + 'swift.cache': cache}, + headers={'Destination': '/c/o'}) + res = req.get_response(app) + self.assertEquals(res.status_int, 200) + def test_invalid_quotas(self): req = Request.blank( '/v1/a/c',