summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Hellmann <doug@doughellmann.com>2015-01-06 12:04:31 -0500
committerDoug Hellmann <doug@doughellmann.com>2015-01-06 13:44:01 -0500
commit8e06ca5bd02660eb0b0980593f440ec7e46a00ac (patch)
tree072424b05294281e4aa70130b5289c0b37d74cd2
parent860cdee514e32cbad121a9d8b7076b8931ed22c1 (diff)
downloadoslo-middleware-0.3.0.tar.gz
Move files out of the namespace package0.3.0
Move the public API out of oslo.middleware to oslo_middleware. Retain the ability to import from the old namespace package for backwards compatibility for this release cycle. bp/drop-namespace-packages Change-Id: If88c65c82b64f096a02f7ec62e019aea4de2f9d3
-rw-r--r--doc/source/api.rst2
-rw-r--r--oslo/middleware/__init__.py25
-rw-r--r--oslo/middleware/base.py45
-rw-r--r--oslo/middleware/catch_errors.py32
-rw-r--r--oslo/middleware/correlation_id.py16
-rw-r--r--oslo/middleware/debug.py49
-rw-r--r--oslo/middleware/request_id.py29
-rw-r--r--oslo/middleware/sizelimit.py84
-rw-r--r--oslo_middleware/__init__.py23
-rw-r--r--oslo_middleware/base.py56
-rw-r--r--oslo_middleware/catch_errors.py43
-rw-r--r--oslo_middleware/correlation_id.py27
-rw-r--r--oslo_middleware/debug.py60
-rw-r--r--oslo_middleware/i18n.py (renamed from oslo/middleware/i18n.py)0
-rw-r--r--oslo_middleware/opts.py (renamed from oslo/middleware/opts.py)4
-rw-r--r--oslo_middleware/request_id.py40
-rw-r--r--oslo_middleware/sizelimit.py95
-rw-r--r--oslo_middleware/tests/__init__.py0
-rw-r--r--oslo_middleware/tests/test_catch_errors.py47
-rw-r--r--oslo_middleware/tests/test_correlation_id.py53
-rw-r--r--oslo_middleware/tests/test_request_id.py37
-rw-r--r--oslo_middleware/tests/test_sizelimit.py108
-rw-r--r--setup.cfg3
-rw-r--r--tests/test_warning.py61
-rw-r--r--tox.ini4
25 files changed, 678 insertions, 265 deletions
diff --git a/doc/source/api.rst b/doc/source/api.rst
index db58162..38cd96c 100644
--- a/doc/source/api.rst
+++ b/doc/source/api.rst
@@ -2,5 +2,5 @@
API
=====
-.. automodule:: oslo.middleware
+.. automodule:: oslo_middleware
:members:
diff --git a/oslo/middleware/__init__.py b/oslo/middleware/__init__.py
index 5289943..1407ce1 100644
--- a/oslo/middleware/__init__.py
+++ b/oslo/middleware/__init__.py
@@ -10,14 +10,19 @@
# License for the specific language governing permissions and limitations
# under the License.
-__all__ = ['CatchErrors',
- 'CorrelationId',
- 'Debug',
- 'RequestId',
- 'RequestBodySizeLimiter']
+import warnings
-from oslo.middleware.catch_errors import CatchErrors
-from oslo.middleware.correlation_id import CorrelationId
-from oslo.middleware.debug import Debug
-from oslo.middleware.request_id import RequestId
-from oslo.middleware.sizelimit import RequestBodySizeLimiter
+from oslo_middleware import *
+
+
+def deprecated():
+ new_name = __name__.replace('.', '_')
+ warnings.warn(
+ ('The oslo namespace package is deprecated. Please use %s instead.' %
+ new_name),
+ DeprecationWarning,
+ stacklevel=3,
+ )
+
+
+deprecated()
diff --git a/oslo/middleware/base.py b/oslo/middleware/base.py
index 464a1cc..53e25e9 100644
--- a/oslo/middleware/base.py
+++ b/oslo/middleware/base.py
@@ -1,6 +1,3 @@
-# Copyright 2011 OpenStack Foundation.
-# All Rights Reserved.
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -13,44 +10,4 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""Base class(es) for WSGI Middleware."""
-
-import webob.dec
-
-
-class Middleware(object):
- """Base WSGI middleware wrapper.
-
- These classes require an application to be initialized that will be called
- next. By default the middleware will simply call its wrapped app, or you
- can override __call__ to customize its behavior.
- """
-
- @classmethod
- def factory(cls, global_conf, **local_conf):
- """Factory method for paste.deploy."""
- return cls
-
- def __init__(self, application):
- self.application = application
-
- def process_request(self, req):
- """Called on each request.
-
- If this returns None, the next application down the stack will be
- executed. If it returns a response then that response will be returned
- and execution will stop here.
- """
- return None
-
- def process_response(self, response):
- """Do whatever you'd like to the response."""
- return response
-
- @webob.dec.wsgify
- def __call__(self, req):
- response = self.process_request(req)
- if response:
- return response
- response = req.get_response(self.application)
- return self.process_response(response)
+from oslo_middleware.base import * # noqa
diff --git a/oslo/middleware/catch_errors.py b/oslo/middleware/catch_errors.py
index ed57f8a..81e4c6c 100644
--- a/oslo/middleware/catch_errors.py
+++ b/oslo/middleware/catch_errors.py
@@ -1,6 +1,3 @@
-# Copyright (c) 2013 NEC Corporation
-# All Rights Reserved.
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -13,31 +10,4 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
-
-import webob.dec
-import webob.exc
-
-from oslo.middleware import base
-from oslo.middleware.i18n import _LE
-
-
-LOG = logging.getLogger(__name__)
-
-
-class CatchErrors(base.Middleware):
- """Middleware that provides high-level error handling.
-
- It catches all exceptions from subsequent applications in WSGI pipeline
- to hide internal errors from API response.
- """
-
- @webob.dec.wsgify
- def __call__(self, req):
- try:
- response = req.get_response(self.application)
- except Exception:
- LOG.exception(_LE('An error occurred during '
- 'processing the request: %s'))
- response = webob.exc.HTTPInternalServerError()
- return response
+from oslo_middleware.catch_errors import * # noqa
diff --git a/oslo/middleware/correlation_id.py b/oslo/middleware/correlation_id.py
index 2cde134..fff548c 100644
--- a/oslo/middleware/correlation_id.py
+++ b/oslo/middleware/correlation_id.py
@@ -1,6 +1,3 @@
-# Copyright (c) 2013 Rackspace Hosting
-# All Rights Reserved.
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -13,15 +10,4 @@
# License for the specific language governing permissions and limitations
# under the License.
-import uuid
-
-from oslo.middleware import base
-
-
-class CorrelationId(base.Middleware):
- "Middleware that attaches a correlation id to WSGI request"
-
- def process_request(self, req):
- correlation_id = (req.headers.get("X_CORRELATION_ID") or
- str(uuid.uuid4()))
- req.headers['X_CORRELATION_ID'] = correlation_id
+from oslo_middleware.correlation_id import * # noqa
diff --git a/oslo/middleware/debug.py b/oslo/middleware/debug.py
index 8244a02..2907289 100644
--- a/oslo/middleware/debug.py
+++ b/oslo/middleware/debug.py
@@ -1,6 +1,3 @@
-# Copyright 2011 OpenStack Foundation.
-# All Rights Reserved.
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -13,48 +10,4 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""Debug middleware"""
-
-from __future__ import print_function
-
-import sys
-
-import six
-import webob.dec
-
-from oslo.middleware import base
-
-
-class Debug(base.Middleware):
- """Helper class that returns debug information.
-
- Can be inserted into any WSGI application chain to get information about
- the request and response.
- """
-
- @webob.dec.wsgify
- def __call__(self, req):
- print(("*" * 40) + " REQUEST ENVIRON")
- for key, value in req.environ.items():
- print(key, "=", value)
- print()
- resp = req.get_response(self.application)
-
- print(("*" * 40) + " RESPONSE HEADERS")
- for (key, value) in six.iteritems(resp.headers):
- print(key, "=", value)
- print()
-
- resp.app_iter = self.print_generator(resp.app_iter)
-
- return resp
-
- @staticmethod
- def print_generator(app_iter):
- """Prints the contents of a wrapper string iterator when iterated."""
- print(("*" * 40) + " BODY")
- for part in app_iter:
- sys.stdout.write(part)
- sys.stdout.flush()
- yield part
- print()
+from oslo_middleware.debug import * # noqa
diff --git a/oslo/middleware/request_id.py b/oslo/middleware/request_id.py
index a8663f8..81e3164 100644
--- a/oslo/middleware/request_id.py
+++ b/oslo/middleware/request_id.py
@@ -1,6 +1,3 @@
-# Copyright (c) 2013 NEC Corporation
-# All Rights Reserved.
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -13,28 +10,4 @@
# License for the specific language governing permissions and limitations
# under the License.
-from oslo_context import context
-import webob.dec
-
-from oslo.middleware import base
-
-
-ENV_REQUEST_ID = 'openstack.request_id'
-HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id'
-
-
-class RequestId(base.Middleware):
- """Middleware that ensures request ID.
-
- It ensures to assign request ID for each API request and set it to
- request environment. The request ID is also added to API response.
- """
-
- @webob.dec.wsgify
- def __call__(self, req):
- req_id = context.generate_request_id()
- req.environ[ENV_REQUEST_ID] = req_id
- response = req.get_response(self.application)
- if HTTP_RESP_HEADER_REQUEST_ID not in response.headers:
- response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, req_id)
- return response
+from oslo_middleware.request_id import * # noqa
diff --git a/oslo/middleware/sizelimit.py b/oslo/middleware/sizelimit.py
index f42c9e6..c04c1cd 100644
--- a/oslo/middleware/sizelimit.py
+++ b/oslo/middleware/sizelimit.py
@@ -1,5 +1,3 @@
-# Copyright (c) 2012 Red Hat, Inc.
-#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
@@ -12,84 +10,4 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""
-Request Body limiting middleware.
-
-"""
-
-from oslo.config import cfg
-from oslo.config import cfgfilter
-import webob.dec
-import webob.exc
-
-from oslo.middleware import base
-from oslo.middleware.i18n import _
-
-
-_oldopts = [cfg.DeprecatedOpt('osapi_max_request_body_size',
- group='DEFAULT'),
- cfg.DeprecatedOpt('max_request_body_size',
- group='DEFAULT')]
-
-_opts = [
- # default request size is 112k
- cfg.IntOpt('max_request_body_size',
- default=114688,
- help='The maximum body size for each '
- ' request, in bytes.',
- deprecated_opts=_oldopts)
-]
-
-CONF = cfgfilter.ConfigFilter(cfg.CONF)
-CONF.register_opts(_opts, group='oslo_middleware')
-
-
-class LimitingReader(object):
- """Reader to limit the size of an incoming request."""
- def __init__(self, data, limit):
- """Initiates LimitingReader object.
-
- :param data: Underlying data object
- :param limit: maximum number of bytes the reader should allow
- """
- self.data = data
- self.limit = limit
- self.bytes_read = 0
-
- def __iter__(self):
- for chunk in self.data:
- self.bytes_read += len(chunk)
- if self.bytes_read > self.limit:
- msg = _("Request is too large.")
- raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
- else:
- yield chunk
-
- def read(self, i=None):
- # NOTE(jamielennox): We can't simply provide the default to the read()
- # call as the expected default differs between mod_wsgi and eventlet
- if i is None:
- result = self.data.read()
- else:
- result = self.data.read(i)
- self.bytes_read += len(result)
- if self.bytes_read > self.limit:
- msg = _("Request is too large.")
- raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
- return result
-
-
-class RequestBodySizeLimiter(base.Middleware):
- """Limit the size of incoming requests."""
-
- @webob.dec.wsgify
- def __call__(self, req):
- max_size = CONF.oslo_middleware.max_request_body_size
- if (req.content_length is not None and
- req.content_length > max_size):
- msg = _("Request is too large.")
- raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
- if req.content_length is None and req.is_body_readable:
- limiter = LimitingReader(req.body_file, max_size)
- req.body_file = limiter
- return self.application
+from oslo_middleware.sizelimit import * # noqa
diff --git a/oslo_middleware/__init__.py b/oslo_middleware/__init__.py
new file mode 100644
index 0000000..a64dae0
--- /dev/null
+++ b/oslo_middleware/__init__.py
@@ -0,0 +1,23 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+__all__ = ['CatchErrors',
+ 'CorrelationId',
+ 'Debug',
+ 'RequestId',
+ 'RequestBodySizeLimiter']
+
+from oslo_middleware.catch_errors import CatchErrors
+from oslo_middleware.correlation_id import CorrelationId
+from oslo_middleware.debug import Debug
+from oslo_middleware.request_id import RequestId
+from oslo_middleware.sizelimit import RequestBodySizeLimiter
diff --git a/oslo_middleware/base.py b/oslo_middleware/base.py
new file mode 100644
index 0000000..464a1cc
--- /dev/null
+++ b/oslo_middleware/base.py
@@ -0,0 +1,56 @@
+# Copyright 2011 OpenStack Foundation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Base class(es) for WSGI Middleware."""
+
+import webob.dec
+
+
+class Middleware(object):
+ """Base WSGI middleware wrapper.
+
+ These classes require an application to be initialized that will be called
+ next. By default the middleware will simply call its wrapped app, or you
+ can override __call__ to customize its behavior.
+ """
+
+ @classmethod
+ def factory(cls, global_conf, **local_conf):
+ """Factory method for paste.deploy."""
+ return cls
+
+ def __init__(self, application):
+ self.application = application
+
+ def process_request(self, req):
+ """Called on each request.
+
+ If this returns None, the next application down the stack will be
+ executed. If it returns a response then that response will be returned
+ and execution will stop here.
+ """
+ return None
+
+ def process_response(self, response):
+ """Do whatever you'd like to the response."""
+ return response
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ response = self.process_request(req)
+ if response:
+ return response
+ response = req.get_response(self.application)
+ return self.process_response(response)
diff --git a/oslo_middleware/catch_errors.py b/oslo_middleware/catch_errors.py
new file mode 100644
index 0000000..89b4bd9
--- /dev/null
+++ b/oslo_middleware/catch_errors.py
@@ -0,0 +1,43 @@
+# Copyright (c) 2013 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import logging
+
+import webob.dec
+import webob.exc
+
+from oslo_middleware import base
+from oslo_middleware.i18n import _LE
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CatchErrors(base.Middleware):
+ """Middleware that provides high-level error handling.
+
+ It catches all exceptions from subsequent applications in WSGI pipeline
+ to hide internal errors from API response.
+ """
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ try:
+ response = req.get_response(self.application)
+ except Exception:
+ LOG.exception(_LE('An error occurred during '
+ 'processing the request: %s'))
+ response = webob.exc.HTTPInternalServerError()
+ return response
diff --git a/oslo_middleware/correlation_id.py b/oslo_middleware/correlation_id.py
new file mode 100644
index 0000000..54d62ec
--- /dev/null
+++ b/oslo_middleware/correlation_id.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2013 Rackspace Hosting
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import uuid
+
+from oslo_middleware import base
+
+
+class CorrelationId(base.Middleware):
+ "Middleware that attaches a correlation id to WSGI request"
+
+ def process_request(self, req):
+ correlation_id = (req.headers.get("X_CORRELATION_ID") or
+ str(uuid.uuid4()))
+ req.headers['X_CORRELATION_ID'] = correlation_id
diff --git a/oslo_middleware/debug.py b/oslo_middleware/debug.py
new file mode 100644
index 0000000..bdbf539
--- /dev/null
+++ b/oslo_middleware/debug.py
@@ -0,0 +1,60 @@
+# Copyright 2011 OpenStack Foundation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Debug middleware"""
+
+from __future__ import print_function
+
+import sys
+
+import six
+import webob.dec
+
+from oslo_middleware import base
+
+
+class Debug(base.Middleware):
+ """Helper class that returns debug information.
+
+ Can be inserted into any WSGI application chain to get information about
+ the request and response.
+ """
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ print(("*" * 40) + " REQUEST ENVIRON")
+ for key, value in req.environ.items():
+ print(key, "=", value)
+ print()
+ resp = req.get_response(self.application)
+
+ print(("*" * 40) + " RESPONSE HEADERS")
+ for (key, value) in six.iteritems(resp.headers):
+ print(key, "=", value)
+ print()
+
+ resp.app_iter = self.print_generator(resp.app_iter)
+
+ return resp
+
+ @staticmethod
+ def print_generator(app_iter):
+ """Prints the contents of a wrapper string iterator when iterated."""
+ print(("*" * 40) + " BODY")
+ for part in app_iter:
+ sys.stdout.write(part)
+ sys.stdout.flush()
+ yield part
+ print()
diff --git a/oslo/middleware/i18n.py b/oslo_middleware/i18n.py
index 307c8f5..307c8f5 100644
--- a/oslo/middleware/i18n.py
+++ b/oslo_middleware/i18n.py
diff --git a/oslo/middleware/opts.py b/oslo_middleware/opts.py
index 3cc035f..d01fd05 100644
--- a/oslo/middleware/opts.py
+++ b/oslo_middleware/opts.py
@@ -20,7 +20,7 @@ __all__ = [
import copy
-from oslo.middleware import sizelimit
+from oslo_middleware import sizelimit
def list_opts():
@@ -42,4 +42,4 @@ def list_opts():
:returns: a list of (group_name, opts) tuples
"""
- return [('oslo_middleware', copy.deepcopy(sizelimit._opts))] \ No newline at end of file
+ return [('oslo_middleware', copy.deepcopy(sizelimit._opts))]
diff --git a/oslo_middleware/request_id.py b/oslo_middleware/request_id.py
new file mode 100644
index 0000000..be9ae49
--- /dev/null
+++ b/oslo_middleware/request_id.py
@@ -0,0 +1,40 @@
+# Copyright (c) 2013 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslo_context import context
+import webob.dec
+
+from oslo_middleware import base
+
+
+ENV_REQUEST_ID = 'openstack.request_id'
+HTTP_RESP_HEADER_REQUEST_ID = 'x-openstack-request-id'
+
+
+class RequestId(base.Middleware):
+ """Middleware that ensures request ID.
+
+ It ensures to assign request ID for each API request and set it to
+ request environment. The request ID is also added to API response.
+ """
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ req_id = context.generate_request_id()
+ req.environ[ENV_REQUEST_ID] = req_id
+ response = req.get_response(self.application)
+ if HTTP_RESP_HEADER_REQUEST_ID not in response.headers:
+ response.headers.add(HTTP_RESP_HEADER_REQUEST_ID, req_id)
+ return response
diff --git a/oslo_middleware/sizelimit.py b/oslo_middleware/sizelimit.py
new file mode 100644
index 0000000..42b0fd1
--- /dev/null
+++ b/oslo_middleware/sizelimit.py
@@ -0,0 +1,95 @@
+# Copyright (c) 2012 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Request Body limiting middleware.
+
+"""
+
+from oslo.config import cfg
+from oslo.config import cfgfilter
+import webob.dec
+import webob.exc
+
+from oslo_middleware import base
+from oslo_middleware.i18n import _
+
+
+_oldopts = [cfg.DeprecatedOpt('osapi_max_request_body_size',
+ group='DEFAULT'),
+ cfg.DeprecatedOpt('max_request_body_size',
+ group='DEFAULT')]
+
+_opts = [
+ # default request size is 112k
+ cfg.IntOpt('max_request_body_size',
+ default=114688,
+ help='The maximum body size for each '
+ ' request, in bytes.',
+ deprecated_opts=_oldopts)
+]
+
+CONF = cfgfilter.ConfigFilter(cfg.CONF)
+CONF.register_opts(_opts, group='oslo_middleware')
+
+
+class LimitingReader(object):
+ """Reader to limit the size of an incoming request."""
+ def __init__(self, data, limit):
+ """Initiates LimitingReader object.
+
+ :param data: Underlying data object
+ :param limit: maximum number of bytes the reader should allow
+ """
+ self.data = data
+ self.limit = limit
+ self.bytes_read = 0
+
+ def __iter__(self):
+ for chunk in self.data:
+ self.bytes_read += len(chunk)
+ if self.bytes_read > self.limit:
+ msg = _("Request is too large.")
+ raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
+ else:
+ yield chunk
+
+ def read(self, i=None):
+ # NOTE(jamielennox): We can't simply provide the default to the read()
+ # call as the expected default differs between mod_wsgi and eventlet
+ if i is None:
+ result = self.data.read()
+ else:
+ result = self.data.read(i)
+ self.bytes_read += len(result)
+ if self.bytes_read > self.limit:
+ msg = _("Request is too large.")
+ raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
+ return result
+
+
+class RequestBodySizeLimiter(base.Middleware):
+ """Limit the size of incoming requests."""
+
+ @webob.dec.wsgify
+ def __call__(self, req):
+ max_size = CONF.oslo_middleware.max_request_body_size
+ if (req.content_length is not None and
+ req.content_length > max_size):
+ msg = _("Request is too large.")
+ raise webob.exc.HTTPRequestEntityTooLarge(explanation=msg)
+ if req.content_length is None and req.is_body_readable:
+ limiter = LimitingReader(req.body_file, max_size)
+ req.body_file = limiter
+ return self.application
diff --git a/oslo_middleware/tests/__init__.py b/oslo_middleware/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/oslo_middleware/tests/__init__.py
diff --git a/oslo_middleware/tests/test_catch_errors.py b/oslo_middleware/tests/test_catch_errors.py
new file mode 100644
index 0000000..920bbe2
--- /dev/null
+++ b/oslo_middleware/tests/test_catch_errors.py
@@ -0,0 +1,47 @@
+# Copyright (c) 2013 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import mock
+from oslotest import base as test_base
+import webob.dec
+import webob.exc
+
+from oslo_middleware import catch_errors
+
+
+class CatchErrorsTest(test_base.BaseTestCase):
+
+ def _test_has_request_id(self, application, expected_code=None):
+ app = catch_errors.CatchErrors(application)
+ req = webob.Request.blank('/test')
+ res = req.get_response(app)
+ self.assertEqual(expected_code, res.status_int)
+
+ def test_success_response(self):
+ @webob.dec.wsgify
+ def application(req):
+ return 'Hello, World!!!'
+
+ self._test_has_request_id(application, webob.exc.HTTPOk.code)
+
+ def test_internal_server_error(self):
+ @webob.dec.wsgify
+ def application(req):
+ raise Exception()
+
+ with mock.patch.object(catch_errors.LOG, 'exception') as log_exc:
+ self._test_has_request_id(application,
+ webob.exc.HTTPInternalServerError.code)
+ self.assertEqual(1, log_exc.call_count)
diff --git a/oslo_middleware/tests/test_correlation_id.py b/oslo_middleware/tests/test_correlation_id.py
new file mode 100644
index 0000000..6dde5d8
--- /dev/null
+++ b/oslo_middleware/tests/test_correlation_id.py
@@ -0,0 +1,53 @@
+# Copyright (c) 2013 Rackspace Hosting
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import uuid
+
+import mock
+from oslotest import base as test_base
+from oslotest import moxstubout
+
+from oslo_middleware import correlation_id
+
+
+class CorrelationIdTest(test_base.BaseTestCase):
+
+ def setUp(self):
+ super(CorrelationIdTest, self).setUp()
+ self.stubs = self.useFixture(moxstubout.MoxStubout()).stubs
+
+ def test_process_request(self):
+ app = mock.Mock()
+ req = mock.Mock()
+ req.headers = {}
+
+ mock_uuid4 = mock.Mock()
+ mock_uuid4.return_value = "fake_uuid"
+ self.stubs.Set(uuid, 'uuid4', mock_uuid4)
+
+ middleware = correlation_id.CorrelationId(app)
+ middleware(req)
+
+ self.assertEqual(req.headers.get("X_CORRELATION_ID"), "fake_uuid")
+
+ def test_process_request_should_not_regenerate_correlation_id(self):
+ app = mock.Mock()
+ req = mock.Mock()
+ req.headers = {"X_CORRELATION_ID": "correlation_id"}
+
+ middleware = correlation_id.CorrelationId(app)
+ middleware(req)
+
+ self.assertEqual(req.headers.get("X_CORRELATION_ID"), "correlation_id")
diff --git a/oslo_middleware/tests/test_request_id.py b/oslo_middleware/tests/test_request_id.py
new file mode 100644
index 0000000..09bdd32
--- /dev/null
+++ b/oslo_middleware/tests/test_request_id.py
@@ -0,0 +1,37 @@
+# Copyright (c) 2013 NEC Corporation
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+from oslotest import base as test_base
+from testtools import matchers
+import webob
+import webob.dec
+
+from oslo_middleware import request_id
+
+
+class RequestIdTest(test_base.BaseTestCase):
+ def test_generate_request_id(self):
+ @webob.dec.wsgify
+ def application(req):
+ return req.environ[request_id.ENV_REQUEST_ID]
+
+ app = request_id.RequestId(application)
+ req = webob.Request.blank('/test')
+ res = req.get_response(app)
+ res_req_id = res.headers.get(request_id.HTTP_RESP_HEADER_REQUEST_ID)
+ self.assertThat(res_req_id, matchers.StartsWith(b'req-'))
+ # request-id in request environ is returned as response body
+ self.assertEqual(res_req_id, res.body)
diff --git a/oslo_middleware/tests/test_sizelimit.py b/oslo_middleware/tests/test_sizelimit.py
new file mode 100644
index 0000000..4611758
--- /dev/null
+++ b/oslo_middleware/tests/test_sizelimit.py
@@ -0,0 +1,108 @@
+# Copyright (c) 2012 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from oslotest import base as test_base
+import six
+import webob
+
+from oslo.config import fixture as config
+from oslo_middleware import sizelimit
+
+
+class TestLimitingReader(test_base.BaseTestCase):
+
+ def test_limiting_reader(self):
+ BYTES = 1024
+ bytes_read = 0
+ data = six.StringIO("*" * BYTES)
+ for chunk in sizelimit.LimitingReader(data, BYTES):
+ bytes_read += len(chunk)
+
+ self.assertEqual(bytes_read, BYTES)
+
+ bytes_read = 0
+ data = six.StringIO("*" * BYTES)
+ reader = sizelimit.LimitingReader(data, BYTES)
+ byte = reader.read(1)
+ while len(byte) != 0:
+ bytes_read += 1
+ byte = reader.read(1)
+
+ self.assertEqual(bytes_read, BYTES)
+
+ def test_read_default_value(self):
+ BYTES = 1024
+ data_str = "*" * BYTES
+ data = six.StringIO(data_str)
+ reader = sizelimit.LimitingReader(data, BYTES)
+ res = reader.read()
+ self.assertEqual(data_str, res)
+
+ def test_limiting_reader_fails(self):
+ BYTES = 1024
+
+ def _consume_all_iter():
+ bytes_read = 0
+ data = six.StringIO("*" * BYTES)
+ for chunk in sizelimit.LimitingReader(data, BYTES - 1):
+ bytes_read += len(chunk)
+
+ self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
+ _consume_all_iter)
+
+ def _consume_all_read():
+ bytes_read = 0
+ data = six.StringIO("*" * BYTES)
+ reader = sizelimit.LimitingReader(data, BYTES - 1)
+ byte = reader.read(1)
+ while len(byte) != 0:
+ bytes_read += 1
+ byte = reader.read(1)
+
+ self.assertRaises(webob.exc.HTTPRequestEntityTooLarge,
+ _consume_all_read)
+
+
+class TestRequestBodySizeLimiter(test_base.BaseTestCase):
+
+ def setUp(self):
+ super(TestRequestBodySizeLimiter, self).setUp()
+ fixture = self.useFixture(config.Config(sizelimit.CONF))
+ self.MAX_REQUEST_BODY_SIZE = \
+ fixture.conf.oslo_middleware.max_request_body_size
+
+ @webob.dec.wsgify()
+ def fake_app(req):
+ return webob.Response(req.body)
+
+ self.middleware = sizelimit.RequestBodySizeLimiter(fake_app)
+ self.request = webob.Request.blank('/', method='POST')
+
+ def test_content_length_acceptable(self):
+ self.request.headers['Content-Length'] = self.MAX_REQUEST_BODY_SIZE
+ self.request.body = b"0" * self.MAX_REQUEST_BODY_SIZE
+ response = self.request.get_response(self.middleware)
+ self.assertEqual(response.status_int, 200)
+
+ def test_content_length_too_large(self):
+ self.request.headers['Content-Length'] = self.MAX_REQUEST_BODY_SIZE + 1
+ self.request.body = b"0" * (self.MAX_REQUEST_BODY_SIZE + 1)
+ response = self.request.get_response(self.middleware)
+ self.assertEqual(response.status_int, 413)
+
+ def test_request_too_large_no_content_length(self):
+ self.request.body = b"0" * (self.MAX_REQUEST_BODY_SIZE + 1)
+ self.request.headers['Content-Length'] = None
+ response = self.request.get_response(self.middleware)
+ self.assertEqual(response.status_int, 413)
diff --git a/setup.cfg b/setup.cfg
index 320dbcc..b44853b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -22,12 +22,13 @@ classifier =
[files]
packages =
oslo
+ oslo_middleware
namespace_packages =
oslo
[entry_points]
oslo.config.opts =
- oslo.middleware = oslo.middleware.opts:list_opts
+ oslo.middleware = oslo_middleware.opts:list_opts
[build_sphinx]
source-dir = doc/source
diff --git a/tests/test_warning.py b/tests/test_warning.py
new file mode 100644
index 0000000..8e7d96c
--- /dev/null
+++ b/tests/test_warning.py
@@ -0,0 +1,61 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import imp
+import os
+import warnings
+
+import mock
+from oslotest import base as test_base
+import six
+
+
+class DeprecationWarningTest(test_base.BaseTestCase):
+
+ @mock.patch('warnings.warn')
+ def test_warning(self, mock_warn):
+ import oslo.middleware
+ imp.reload(oslo.middleware)
+ self.assertTrue(mock_warn.called)
+ args = mock_warn.call_args
+ self.assertIn('oslo_middleware', args[0][0])
+ self.assertIn('deprecated', args[0][0])
+ self.assertTrue(issubclass(args[0][1], DeprecationWarning))
+
+ def test_real_warning(self):
+ with warnings.catch_warnings(record=True) as warning_msgs:
+ warnings.resetwarnings()
+ warnings.simplefilter('always', DeprecationWarning)
+ import oslo.middleware
+
+ # Use a separate function to get the stack level correct
+ # so we know the message points back to this file. This
+ # corresponds to an import or reload, which isn't working
+ # inside the test under Python 3.3. That may be due to a
+ # difference in the import implementation not triggering
+ # warnings properly when the module is reloaded, or
+ # because the warnings module is mostly implemented in C
+ # and something isn't cleanly resetting the global state
+ # used to track whether a warning needs to be
+ # emitted. Whatever the cause, we definitely see the
+ # warnings.warn() being invoked on a reload (see the test
+ # above) and warnings are reported on the console when we
+ # run the tests. A simpler test script run outside of
+ # testr does correctly report the warnings.
+ def foo():
+ oslo.middleware.deprecated()
+
+ foo()
+ self.assertEqual(1, len(warning_msgs))
+ msg = warning_msgs[0]
+ self.assertIn('oslo_middleware', six.text_type(msg.message))
+ self.assertEqual('test_warning.py', os.path.basename(msg.filename))
diff --git a/tox.ini b/tox.ini
index 5895c1d..ea321a6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
[tox]
minversion = 1.6
-envlist = py26,py27,py33,py34,pypy,pep8
+envlist = py33,py34,py26,py27,pypy,pep8
# NOTE(dhellmann): We cannot set skipdist=True
# for oslo libraries because of the namespace package.
#skipsdist = True
@@ -37,4 +37,4 @@ ignore = E123,E125,H305,H803,H904
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,__init__.py
[hacking]
-import_exceptions = oslo.middleware.i18n
+import_exceptions = oslo_middleware.i18n