Merge "s3api: Increase max body size for Delete Multiple Objects requests"
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
from swift.common.constraints import MAX_OBJECT_NAME_LENGTH
|
||||||
from swift.common.utils import public
|
from swift.common.utils import public
|
||||||
|
|
||||||
from swift.common.middleware.s3api.controllers.base import Controller, \
|
from swift.common.middleware.s3api.controllers.base import Controller, \
|
||||||
@@ -23,8 +24,6 @@ from swift.common.middleware.s3api.s3response import HTTPOk, S3NotImplemented, \
|
|||||||
NoSuchKey, ErrorResponse, MalformedXML, UserKeyMustBeSpecified, \
|
NoSuchKey, ErrorResponse, MalformedXML, UserKeyMustBeSpecified, \
|
||||||
AccessDenied, MissingRequestBodyError
|
AccessDenied, MissingRequestBodyError
|
||||||
|
|
||||||
MAX_MULTI_DELETE_BODY_SIZE = 61365
|
|
||||||
|
|
||||||
|
|
||||||
class MultiObjectDeleteController(Controller):
|
class MultiObjectDeleteController(Controller):
|
||||||
"""
|
"""
|
||||||
@@ -61,8 +60,16 @@ class MultiObjectDeleteController(Controller):
|
|||||||
|
|
||||||
yield key, version
|
yield key, version
|
||||||
|
|
||||||
|
max_body_size = min(
|
||||||
|
# FWIW, AWS limits multideletes to 1000 keys, and swift limits
|
||||||
|
# object names to 1024 bytes (by default). Add a factor of two to
|
||||||
|
# allow some slop.
|
||||||
|
2 * self.conf.max_multi_delete_objects * MAX_OBJECT_NAME_LENGTH,
|
||||||
|
# But, don't let operators shoot themselves in the foot
|
||||||
|
10 * 1024 * 1024)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
xml = req.xml(MAX_MULTI_DELETE_BODY_SIZE)
|
xml = req.xml(max_body_size)
|
||||||
if not xml:
|
if not xml:
|
||||||
raise MissingRequestBodyError()
|
raise MissingRequestBodyError()
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import os
|
|||||||
import test.functional as tf
|
import test.functional as tf
|
||||||
from swift.common.middleware.s3api.etree import fromstring, tostring, Element, \
|
from swift.common.middleware.s3api.etree import fromstring, tostring, Element, \
|
||||||
SubElement
|
SubElement
|
||||||
from swift.common.middleware.s3api.controllers.multi_delete import \
|
|
||||||
MAX_MULTI_DELETE_BODY_SIZE
|
|
||||||
|
|
||||||
from test.functional.s3api import S3ApiBase
|
from test.functional.s3api import S3ApiBase
|
||||||
from test.functional.s3api.s3_test_client import Connection
|
from test.functional.s3api.s3_test_client import Connection
|
||||||
@@ -172,11 +170,12 @@ class TestS3ApiMultiDelete(S3ApiBase):
|
|||||||
query=query)
|
query=query)
|
||||||
self.assertEqual(get_error_code(body), 'UserKeyMustBeSpecified')
|
self.assertEqual(get_error_code(body), 'UserKeyMustBeSpecified')
|
||||||
|
|
||||||
|
max_deletes = tf.cluster_info.get('s3api', {}).get(
|
||||||
|
'max_multi_delete_objects', 1000)
|
||||||
# specified number of objects are over max_multi_delete_objects
|
# specified number of objects are over max_multi_delete_objects
|
||||||
# (Default 1000), but xml size is smaller than 61365 bytes.
|
# (Default 1000), but xml size is relatively small
|
||||||
req_objects = ['obj%s' for var in xrange(1001)]
|
req_objects = ['obj%s' for var in xrange(max_deletes + 1)]
|
||||||
xml = self._gen_multi_delete_xml(req_objects)
|
xml = self._gen_multi_delete_xml(req_objects)
|
||||||
self.assertTrue(len(xml.encode('utf-8')) <= MAX_MULTI_DELETE_BODY_SIZE)
|
|
||||||
content_md5 = calculate_md5(xml)
|
content_md5 = calculate_md5(xml)
|
||||||
status, headers, body = \
|
status, headers, body = \
|
||||||
self.conn.make_request('POST', bucket, body=xml,
|
self.conn.make_request('POST', bucket, body=xml,
|
||||||
@@ -184,12 +183,11 @@ class TestS3ApiMultiDelete(S3ApiBase):
|
|||||||
query=query)
|
query=query)
|
||||||
self.assertEqual(get_error_code(body), 'MalformedXML')
|
self.assertEqual(get_error_code(body), 'MalformedXML')
|
||||||
|
|
||||||
# specified xml size is over 61365 bytes, but number of objects are
|
# specified xml size is large, but number of objects are
|
||||||
# smaller than max_multi_delete_objects.
|
# smaller than max_multi_delete_objects.
|
||||||
obj = 'a' * 1024
|
obj = 'a' * 102400
|
||||||
req_objects = [obj + str(var) for var in xrange(999)]
|
req_objects = [obj + str(var) for var in xrange(max_deletes - 1)]
|
||||||
xml = self._gen_multi_delete_xml(req_objects)
|
xml = self._gen_multi_delete_xml(req_objects)
|
||||||
self.assertTrue(len(xml.encode('utf-8')) > MAX_MULTI_DELETE_BODY_SIZE)
|
|
||||||
content_md5 = calculate_md5(xml)
|
content_md5 = calculate_md5(xml)
|
||||||
status, headers, body = \
|
status, headers, body = \
|
||||||
self.conn.make_request('POST', bucket, body=xml,
|
self.conn.make_request('POST', bucket, body=xml,
|
||||||
|
|||||||
@@ -179,12 +179,37 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
|
|||||||
status, headers, body = self.call_s3api(req)
|
status, headers, body = self.call_s3api(req)
|
||||||
self.assertEqual(self._get_error_code(body), 'InvalidRequest')
|
self.assertEqual(self._get_error_code(body), 'InvalidRequest')
|
||||||
|
|
||||||
|
@s3acl
|
||||||
|
def test_object_multi_DELETE_lots_of_keys(self):
|
||||||
|
elem = Element('Delete')
|
||||||
|
for i in range(self.conf.max_multi_delete_objects):
|
||||||
|
name = 'x' * 1000 + str(i)
|
||||||
|
self.swift.register('HEAD', '/v1/AUTH_test/bucket/%s' % name,
|
||||||
|
swob.HTTPNotFound, {}, None)
|
||||||
|
obj = SubElement(elem, 'Object')
|
||||||
|
SubElement(obj, 'Key').text = name
|
||||||
|
body = tostring(elem, use_s3ns=False)
|
||||||
|
content_md5 = md5(body).digest().encode('base64').strip()
|
||||||
|
|
||||||
|
req = Request.blank('/bucket?delete',
|
||||||
|
environ={'REQUEST_METHOD': 'POST'},
|
||||||
|
headers={'Authorization': 'AWS test:tester:hmac',
|
||||||
|
'Date': self.get_date_header(),
|
||||||
|
'Content-MD5': content_md5},
|
||||||
|
body=body)
|
||||||
|
status, headers, body = self.call_s3api(req)
|
||||||
|
self.assertEqual('200 OK', status)
|
||||||
|
|
||||||
|
elem = fromstring(body)
|
||||||
|
self.assertEqual(len(elem.findall('Deleted')),
|
||||||
|
self.conf.max_multi_delete_objects)
|
||||||
|
|
||||||
@s3acl
|
@s3acl
|
||||||
def test_object_multi_DELETE_too_many_keys(self):
|
def test_object_multi_DELETE_too_many_keys(self):
|
||||||
elem = Element('Delete')
|
elem = Element('Delete')
|
||||||
for i in range(self.conf.max_multi_delete_objects + 1):
|
for i in range(self.conf.max_multi_delete_objects + 1):
|
||||||
obj = SubElement(elem, 'Object')
|
obj = SubElement(elem, 'Object')
|
||||||
SubElement(obj, 'Key').text = str(i)
|
SubElement(obj, 'Key').text = 'x' * 1000 + str(i)
|
||||||
body = tostring(elem, use_s3ns=False)
|
body = tostring(elem, use_s3ns=False)
|
||||||
content_md5 = md5(body).digest().encode('base64').strip()
|
content_md5 = md5(body).digest().encode('base64').strip()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user