From 57a10aac00b74a01ff029318f60fc20b8436ebdd Mon Sep 17 00:00:00 2001 From: Kevin Conway Date: Fri, 14 Jun 2013 16:19:40 -0500 Subject: [PATCH] Fix faulty 404 errors when requesting bad versions The problem came down to the `_dispatch` method of the openstack common wsgi router object. When a match was not found it returned a webob 404 exception directly rather than allowing it to be wrapped in a serializer. The fix involved extending the `Router` object and reimplementing the `_dispatch()` method to use the `Fault` object that serializes exceptions. Change-Id: I24a590f65ff655b25cfd7d84786df3055af701f1 Fixes: bug #1174960 --- reddwarf/common/wsgi.py | 21 +++++++- reddwarf/tests/unittests/router/__init__.py | 13 +++++ .../tests/unittests/router/test_router.py | 52 +++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 reddwarf/tests/unittests/router/__init__.py create mode 100644 reddwarf/tests/unittests/router/test_router.py diff --git a/reddwarf/common/wsgi.py b/reddwarf/common/wsgi.py index c3255029bd..511c5bb82b 100644 --- a/reddwarf/common/wsgi.py +++ b/reddwarf/common/wsgi.py @@ -43,7 +43,6 @@ from reddwarf.openstack.common import log as logging from reddwarf.common import cfg CONTEXT_KEY = 'reddwarf.context' -Router = openstack_wsgi.Router Debug = openstack_wsgi.Debug Middleware = openstack_wsgi.Middleware JSONDictSerializer = openstack_wsgi.JSONDictSerializer @@ -207,6 +206,26 @@ class VersionedURLMap(object): return app(environ, start_response) +class Router(openstack_wsgi.Router): + + # Original router did not allow for serialization of the 404 error. + # To fix this the _dispatch was modified to use Fault() objects. + @staticmethod + @webob.dec.wsgify + def _dispatch(req): + """ + Called by self._router after matching the incoming request to a route + and putting the information into req.environ. Either returns 404 + or the routed WSGI app's response. + """ + + match = req.environ['wsgiorg.routing_args'][1] + if not match: + return Fault(webob.exc.HTTPNotFound()) + app = match['controller'] + return app + + class Request(openstack_wsgi.Request): @property diff --git a/reddwarf/tests/unittests/router/__init__.py b/reddwarf/tests/unittests/router/__init__.py new file mode 100644 index 0000000000..f0ec14305d --- /dev/null +++ b/reddwarf/tests/unittests/router/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2013 OpenStack Foundation +# +# 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. diff --git a/reddwarf/tests/unittests/router/test_router.py b/reddwarf/tests/unittests/router/test_router.py new file mode 100644 index 0000000000..c9deeb2a5c --- /dev/null +++ b/reddwarf/tests/unittests/router/test_router.py @@ -0,0 +1,52 @@ +# Copyright 2013 OpenStack Foundation +# +# 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 testtools + +from reddwarf.common.wsgi import Router, Fault + +from routes import Mapper + + +class FakeRequst(object): + """A fake webob request object designed to cause 404. + + The dispatcher actually checks if the given request is a dict and throws + an error if it is. This object wrapper tricks the dispatcher into + handling the request like a regular request. + """ + + environ = { + "wsgiorg.routing_args": [ + False, + False + ] + } + + +class TestRouter(testtools.TestCase): + """Test case for trove `Router` extensions.""" + + def setUp(self): + super(TestRouter, self).setUp() + self.mapper = Mapper() + + def test_404_is_fault(self): + """Test that the dispatcher wraps 404's in a `Fault`.""" + + fake_request = FakeRequst() + + response = Router._dispatch(fake_request) + + assert isinstance(response, Fault)