From 56440eb95da79506cc27d92e07f0f5969cc683ce Mon Sep 17 00:00:00 2001 From: Christian Schwede Date: Wed, 18 Sep 2013 19:49:32 +0200 Subject: [PATCH] Handle X-Copy-From header in container_quota middleware Content length of the copied object is checked before allowing the copy request according to the container quota. Closes-Bug: #1201875 Change-Id: If44b916791e94ac6c66eee04a5727186ce0e56ae --- swift/common/middleware/container_quotas.py | 16 ++++-- test/unit/common/middleware/test_quotas.py | 55 +++++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/swift/common/middleware/container_quotas.py b/swift/common/middleware/container_quotas.py index d8d3df0316..119986fc3c 100644 --- a/swift/common/middleware/container_quotas.py +++ b/swift/common/middleware/container_quotas.py @@ -43,7 +43,7 @@ set: """ from swift.common.http import is_success -from swift.proxy.controllers.base import get_container_info +from swift.proxy.controllers.base import get_container_info, get_object_info from swift.common.swob import Response, HTTPBadRequest, wsgify @@ -84,11 +84,21 @@ class ContainerQuotaMiddleware(object): if not container_info or not is_success(container_info['status']): # this will hopefully 404 later return self.app + + content_length = (req.content_length or 0) + copy_from = req.headers.get('X-Copy-From') + if obj and copy_from: + path = '/%s/%s/%s' % (version, account, copy_from.lstrip('/')) + object_info = get_object_info(req.environ, self.app, path) + if not object_info or not object_info['length']: + content_length = 0 + else: + content_length = int(object_info['length']) + if 'quota-bytes' in container_info.get('meta', {}) and \ 'bytes' in container_info and \ container_info['meta']['quota-bytes'].isdigit(): - new_size = int(container_info['bytes']) + (req.content_length - or 0) + new_size = int(container_info['bytes']) + content_length if int(container_info['meta']['quota-bytes']) < new_size: return self.bad_response(req, container_info) if 'quota-count' in container_info.get('meta', {}) and \ diff --git a/test/unit/common/middleware/test_quotas.py b/test/unit/common/middleware/test_quotas.py index f33e7a21d0..2365615677 100644 --- a/test/unit/common/middleware/test_quotas.py +++ b/test/unit/common/middleware/test_quotas.py @@ -94,6 +94,18 @@ class TestContainerQuotas(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 413) + def test_exceed_bytes_quota_copy_from(self): + app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) + cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '2'}}) + + req = Request.blank('/v1/a/c/o', + environ={'REQUEST_METHOD': 'PUT', + 'swift.object/a/c2/o2': {'length': 10}, + 'swift.cache': cache}, + headers={'x-copy-from': '/c2/o2'}) + 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'}}) @@ -104,6 +116,28 @@ class TestContainerQuotas(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 200) + def test_not_exceed_bytes_quota_copy_from(self): + app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) + cache = FakeCache({'bytes': 0, 'meta': {'quota-bytes': '100'}}) + req = Request.blank('/v1/a/c/o', + environ={'REQUEST_METHOD': 'PUT', + 'swift.object/a/c2/o2': {'length': 10}, + 'swift.cache': cache}, + headers={'x-copy-from': '/c2/o2'}) + 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'}}) + req = Request.blank('/v1/a/c/o', + environ={'REQUEST_METHOD': 'PUT', + 'swift.object/a/c2/o2': {'length': 10}, + 'swift.cache': cache}, + headers={'x-copy-from': '/c2/o3'}) + 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'}}) @@ -114,6 +148,17 @@ class TestContainerQuotas(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 413) + def test_exceed_counts_quota_copy_from(self): + app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) + cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '1'}}) + req = Request.blank('/v1/a/c/o', + environ={'REQUEST_METHOD': 'PUT', + 'swift.object/a/c2/o2': {'length': 10}, + 'swift.cache': cache}, + headers={'x-copy-from': '/c2/o2'}) + 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'}}) @@ -124,6 +169,16 @@ class TestContainerQuotas(unittest.TestCase): res = req.get_response(app) self.assertEquals(res.status_int, 200) + def test_not_exceed_counts_quota_copy_from(self): + app = container_quotas.ContainerQuotaMiddleware(FakeApp(), {}) + cache = FakeCache({'object_count': 1, 'meta': {'quota-count': '2'}}) + req = Request.blank('/v1/a/c/o', + environ={'REQUEST_METHOD': 'PUT', + 'swift.cache': cache}, + headers={'x-copy-from': '/c2/o2'}) + res = req.get_response(app) + self.assertEquals(res.status_int, 200) + def test_invalid_quotas(self): req = Request.blank( '/v1/a/c',