diff --git a/marconi/queues/transport/validation.py b/marconi/queues/transport/validation.py index a6d791ff5..36e440ed9 100644 --- a/marconi/queues/transport/validation.py +++ b/marconi/queues/transport/validation.py @@ -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=(',', ':'))) diff --git a/marconi/queues/transport/wsgi/messages.py b/marconi/queues/transport/wsgi/messages.py index 1c670411f..03e71678f 100644 --- a/marconi/queues/transport/wsgi/messages.py +++ b/marconi/queues/transport/wsgi/messages.py @@ -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, diff --git a/marconi/queues/transport/wsgi/metadata.py b/marconi/queues/transport/wsgi/metadata.py index a3851f090..f2c00092b 100644 --- a/marconi/queues/transport/wsgi/metadata.py +++ b/marconi/queues/transport/wsgi/metadata.py @@ -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)