Enforce size verification on content-length

Closes-Bug: #1261579

Previously, Marconi used to enforce the message and queue size limit on
the resource itself. In the case of a Message, it'd verify that the
message body wouldn't exceed the message size limit. This enforcement
has been moved, by this patch, up to the request size.

For both cases, the content_length is required to be smaller than the
size limit configured by the service admin. None of the default values
were modified.

Change-Id: I06e28acaac234242a40cd2afafb12be067851c20
This commit is contained in:
Flavio Percoco 2014-01-09 13:15:51 +01:00
parent e82e5fe3c0
commit 9205b0c3a3
3 changed files with 36 additions and 53 deletions

View File

@ -13,11 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import re
from oslo.config import cfg
from marconi.openstack.common.gettextutils import _
_TRANSPORT_LIMITS_OPTIONS = [
cfg.IntOpt('queue_paging_uplimit', default=20),
@ -89,28 +90,21 @@ class Validator(object):
'Limit must be at least 1 and no greater than %d.' %
self._limits_conf.queue_paging_uplimit)
def queue_content(self, metadata, check_size):
"""Restrictions on queue metadata.
def queue_metadata_length(self, content_length):
"""Restrictions on queue's length.
:param metadata: Metadata as a Python dict
:param check_size: Whether this size checking is required
:param content_length: Queue request's length.
:raises: ValidationFailed if the metadata is oversize.
"""
if content_length > self._limits_conf.metadata_size_uplimit:
error = _(u'Queue request size is too large. Max size %s')
raise ValidationFailed(error %
self._limits_conf.metadata_size_uplimit)
if check_size:
length = _compact_json_length(metadata)
if length > self._limits_conf.metadata_size_uplimit:
raise ValidationFailed(
('Queue metadata may not exceed %d characters, '
'excluding whitespace.') %
self._limits_conf.metadata_size_uplimit)
def message_posting(self, messages, check_size=True):
def message_posting(self, messages):
"""Restrictions on a list of messages.
:param messages: A list of messages
:param check_size: Whether the size checking for each message
is required
:raises: ValidationFailed if any message has a out-of-range
TTL, or an oversize message body.
"""
@ -118,9 +112,20 @@ class Validator(object):
self.message_listing(limit=len(messages))
for msg in messages:
self.message_content(msg, check_size)
self.message_content(msg)
def message_content(self, message, check_size):
def message_length(self, content_length):
"""Restrictions on message post length.
:param content_length: Queue request's length.
:raises: ValidationFailed if the metadata is oversize.
"""
if content_length > self._limits_conf.message_size_uplimit:
error = _(u'Message collection size is too large. Max size %s')
raise ValidationFailed(error %
self._limits_conf.message_size_uplimit)
def message_content(self, message):
"""Restrictions on each message."""
if not (60 <= message['ttl'] <= self._limits_conf.message_ttl_max):
@ -129,14 +134,6 @@ class Validator(object):
'must be at least 60 seconds long.') %
self._limits_conf.message_ttl_max)
if check_size:
body_length = _compact_json_length(message['body'])
if body_length > self._limits_conf.message_size_uplimit:
raise ValidationFailed(
('Message bodies may not exceed %d characters, '
'excluding whitespace.') %
self._limits_conf.message_size_uplimit)
def message_listing(self, limit=None, **kwargs):
"""Restrictions involving a list of messages.
@ -182,12 +179,3 @@ class Validator(object):
('The TTL for a claim may not exceed %d seconds, and must be '
'at least 60 seconds long.') %
self._limits_conf.claim_ttl_max)
def _compact_json_length(obj):
# UTF-8 encoded, without whitespace
# TODO(zyuan): Replace this redundent re-serialization
# with a sizing-only parser.
return len(json.dumps(obj,
ensure_ascii=False,
separators=(',', ':')))

View File

@ -138,10 +138,12 @@ class CollectionResource(object):
client_uuid = wsgi_utils.get_client_uuid(req)
# Place JSON size restriction before parsing
if req.content_length > self._wsgi_conf.content_max_length:
description = _(u'Message collection size is too large.')
raise wsgi_errors.HTTPBadRequestBody(description)
try:
# Place JSON size restriction before parsing
self._validate.message_length(req.content_length)
except validation.ValidationFailed as ex:
LOG.debug(ex)
raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
# Pull out just the fields we care about
messages = wsgi_utils.filter_stream(
@ -154,12 +156,7 @@ class CollectionResource(object):
partial = False
try:
# No need to check each message's size if it
# can not exceed the request size limit
self._validate.message_posting(
messages, check_size=(
self._validate._limits_conf.message_size_uplimit <
self._wsgi_conf.content_max_length))
self._validate.message_posting(messages)
message_ids = self.message_controller.post(
queue_name,

View File

@ -63,10 +63,12 @@ class Resource(object):
u'project: %(project)s'),
{'queue': queue_name, 'project': project_id})
# Place JSON size restriction before parsing
if req.content_length > self._wsgi_conf.metadata_max_length:
description = _(u'Queue metadata size is too large.')
raise wsgi_errors.HTTPBadRequestBody(description)
try:
# Place JSON size restriction before parsing
self._validate.queue_metadata_length(req.content_length)
except validation.ValidationFailed as ex:
LOG.debug(ex)
raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
# Deserialize queue metadata
metadata, = wsgi_utils.filter_stream(req.stream,
@ -74,10 +76,6 @@ class Resource(object):
spec=None)
try:
self._validate.queue_content(
metadata, check_size=(
self._validate._limits_conf.metadata_size_uplimit <
self._wsgi_conf.metadata_max_length))
self.queue_ctrl.set_metadata(queue_name,
metadata=metadata,
project=project_id)