1# Copyright (c) 2012 Red Hat, Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may 4# not use this file except in compliance with the License. You may obtain 5# a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations 13# under the License. 14 15""" 16Request Body limiting middleware. 17 18""" 19 20import logging 21 22from oslo_config import cfg 23import webob.dec 24import webob.exc 25 26from oslo_middleware._i18n import _ 27from oslo_middleware import base 28 29LOG = logging.getLogger(__name__) 30 31 32_oldopts = [cfg.DeprecatedOpt('osapi_max_request_body_size', 33 group='DEFAULT'), 34 cfg.DeprecatedOpt('max_request_body_size', 35 group='DEFAULT')] 36 37_opts = [ 38 # default request size is 112k 39 cfg.IntOpt('max_request_body_size', 40 default=114688, 41 help='The maximum body size for each ' 42 ' request, in bytes.', 43 deprecated_opts=_oldopts) 44] 45 46 47class LimitingReader(object): 48 """Reader to limit the size of an incoming request.""" 49 def __init__(self, data, limit): 50 """Initiates LimitingReader object. 51 52 :param data: Underlying data object 53 :param limit: maximum number of bytes the reader should allow 54 """ 55 self.data = data 56 self.limit = limit 57 self.bytes_read = 0 58 59 def __iter__(self): 60 for chunk in self.data: 61 self.bytes_read += len(chunk) 62 if self.bytes_read > self.limit: 63 msg = _("Request is too large. Larger than %s") % self.limit 64 raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg) 65 else: 66 yield chunk 67 68 def read(self, i=None): 69 # NOTE(jamielennox): We can't simply provide the default to the read() 70 # call as the expected default differs between mod_wsgi and eventlet 71 if i is None: 72 result = self.data.read() 73 else: 74 result = self.data.read(i) 75 self.bytes_read += len(result) 76 if self.bytes_read > self.limit: 77 msg = _("Request is too large. Larger than %s.") % self.limit 78 raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg) 79 return result 80 81 82class RequestBodySizeLimiter(base.ConfigurableMiddleware): 83 """Limit the size of incoming requests.""" 84 85 def __init__(self, application, conf=None): 86 super(RequestBodySizeLimiter, self).__init__(application, conf) 87 self.oslo_conf.register_opts(_opts, group='oslo_middleware') 88 89 @webob.dec.wsgify 90 def __call__(self, req): 91 max_size = self._conf_get('max_request_body_size') 92 if (req.content_length is not None and 93 req.content_length > max_size): 94 msg = _("Request is too large. " 95 "Larger than max_request_body_size (%s).") % max_size 96 LOG.info(msg) 97 raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg) 98 if req.content_length is None: 99 limiter = LimitingReader(req.body_file, max_size) 100 req.body_file = limiter 101 return self.application 102