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
|
||||
# limitations under the License.
|
||||
|
||||
from swift.common.constraints import MAX_OBJECT_NAME_LENGTH
|
||||
from swift.common.utils import public
|
||||
|
||||
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, \
|
||||
AccessDenied, MissingRequestBodyError
|
||||
|
||||
MAX_MULTI_DELETE_BODY_SIZE = 61365
|
||||
|
||||
|
||||
class MultiObjectDeleteController(Controller):
|
||||
"""
|
||||
@@ -61,8 +60,16 @@ class MultiObjectDeleteController(Controller):
|
||||
|
||||
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:
|
||||
xml = req.xml(MAX_MULTI_DELETE_BODY_SIZE)
|
||||
xml = req.xml(max_body_size)
|
||||
if not xml:
|
||||
raise MissingRequestBodyError()
|
||||
|
||||
|
@@ -18,8 +18,6 @@ import os
|
||||
import test.functional as tf
|
||||
from swift.common.middleware.s3api.etree import fromstring, tostring, Element, \
|
||||
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.s3_test_client import Connection
|
||||
@@ -172,11 +170,12 @@ class TestS3ApiMultiDelete(S3ApiBase):
|
||||
query=query)
|
||||
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
|
||||
# (Default 1000), but xml size is smaller than 61365 bytes.
|
||||
req_objects = ['obj%s' for var in xrange(1001)]
|
||||
# (Default 1000), but xml size is relatively small
|
||||
req_objects = ['obj%s' for var in xrange(max_deletes + 1)]
|
||||
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)
|
||||
status, headers, body = \
|
||||
self.conn.make_request('POST', bucket, body=xml,
|
||||
@@ -184,12 +183,11 @@ class TestS3ApiMultiDelete(S3ApiBase):
|
||||
query=query)
|
||||
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.
|
||||
obj = 'a' * 1024
|
||||
req_objects = [obj + str(var) for var in xrange(999)]
|
||||
obj = 'a' * 102400
|
||||
req_objects = [obj + str(var) for var in xrange(max_deletes - 1)]
|
||||
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)
|
||||
status, headers, body = \
|
||||
self.conn.make_request('POST', bucket, body=xml,
|
||||
|
@@ -179,12 +179,37 @@ class TestS3ApiMultiDelete(S3ApiTestCase):
|
||||
status, headers, body = self.call_s3api(req)
|
||||
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
|
||||
def test_object_multi_DELETE_too_many_keys(self):
|
||||
elem = Element('Delete')
|
||||
for i in range(self.conf.max_multi_delete_objects + 1):
|
||||
obj = SubElement(elem, 'Object')
|
||||
SubElement(obj, 'Key').text = str(i)
|
||||
SubElement(obj, 'Key').text = 'x' * 1000 + str(i)
|
||||
body = tostring(elem, use_s3ns=False)
|
||||
content_md5 = md5(body).digest().encode('base64').strip()
|
||||
|
||||
|
Reference in New Issue
Block a user