Impose a size limit on JSON request body

The size limit on JSON request body is to ensure the server not being
overwhelmed by extremly large JSON request body.

Fixes bug #1215501

Change-Id: Ia58f6690e994d34212953c44821f7a4cc4c435fe
This commit is contained in:
Liang Chen 2013-08-31 14:53:43 +08:00
parent 20a8a08333
commit d899f8d9b7
4 changed files with 42 additions and 4 deletions

View File

@ -75,6 +75,15 @@
#auth_encryption_key=notgood but just long enough i think #auth_encryption_key=notgood but just long enough i think
#
# Options defined in heat.common.wsgi
#
# Maximum raw byte size of JSON request body. Should be larger
# than max_template_size. (integer value)
#max_json_body_size=1048576
# #
# Options defined in heat.db.api # Options defined in heat.db.api
# #

View File

@ -330,3 +330,7 @@ class StackRecursionLimitReached(HeatException):
def __init__(self, recursion_depth): def __init__(self, recursion_depth):
self.message = self.message % recursion_depth self.message = self.message % recursion_depth
super(StackRecursionLimitReached, self).__init__() super(StackRecursionLimitReached, self).__init__()
class RequestLimitExceeded(HeatException):
message = _('Request limit exceeded: %(message)s')

View File

@ -137,6 +137,12 @@ cfg.CONF.register_group(api_cw_group)
cfg.CONF.register_opts(api_cw_opts, cfg.CONF.register_opts(api_cw_opts,
group=api_cw_group) group=api_cw_group)
json_size_opt = cfg.IntOpt('max_json_body_size',
default=1048576,
help='Maximum raw byte size of JSON request body.'
' Should be larger than max_template_size.')
cfg.CONF.register_opt(json_size_opt)
class WritableLogger(object): class WritableLogger(object):
"""A thin wrapper that responds to `write` and logs.""" """A thin wrapper that responds to `write` and logs."""
@ -524,6 +530,12 @@ class JSONRequestDeserializer(object):
def from_json(self, datastring): def from_json(self, datastring):
try: try:
if len(datastring) > cfg.CONF.max_json_body_size:
msg = _('JSON body size (%(len)s bytes) exceeds maximum '
'allowed size (%(limit)s bytes).') % \
{'len': len(datastring),
'limit': cfg.CONF.max_json_body_size}
raise exception.RequestLimitExceeded(message=msg)
return json.loads(datastring) return json.loads(datastring)
except ValueError as ex: except ValueError as ex:
raise webob.exc.HTTPBadRequest(str(ex)) raise webob.exc.HTTPBadRequest(str(ex))
@ -638,11 +650,10 @@ class Resource(object):
# ContentType=JSON results in a JSON serialized response... # ContentType=JSON results in a JSON serialized response...
content_type = request.params.get("ContentType") content_type = request.params.get("ContentType")
deserialized_request = self.dispatch(self.deserializer,
action, request)
action_args.update(deserialized_request)
try: try:
deserialized_request = self.dispatch(self.deserializer,
action, request)
action_args.update(deserialized_request)
action_result = self.dispatch(self.controller, action, action_result = self.dispatch(self.controller, action,
request, **action_args) request, **action_args)
except TypeError as err: except TypeError as err:

View File

@ -17,6 +17,8 @@
import datetime import datetime
import json
from oslo.config import cfg
import stubout import stubout
import webob import webob
@ -380,3 +382,15 @@ class JSONRequestDeserializerTest(HeatTestCase):
actual = wsgi.JSONRequestDeserializer().default(request) actual = wsgi.JSONRequestDeserializer().default(request)
expected = {"body": {"key": "value"}} expected = {"body": {"key": "value"}}
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_from_json_exceeds_max_json_mb(self):
cfg.CONF.set_override('max_json_body_size', 10)
body = json.dumps(['a'] * cfg.CONF.max_json_body_size)
self.assertTrue(len(body) > cfg.CONF.max_json_body_size)
error = self.assertRaises(exception.RequestLimitExceeded,
wsgi.JSONRequestDeserializer().from_json,
body)
msg = 'Request limit exceeded: JSON body size ' + \
'(%s bytes) exceeds maximum allowed size (%s bytes).' % \
(len(body), cfg.CONF.max_json_body_size)
self.assertEqual(msg, str(error))