From 5fdb9226d269af069bfcbd42cff401c9d089b7dd Mon Sep 17 00:00:00 2001 From: Chris Dent Date: Thu, 22 Sep 2016 12:58:41 +0000 Subject: [PATCH] [placement] ensure that allow headers are native strings mod-wsgi checks that response header values are what's described as "native strings". This means whatever `str` is in either python 2 or 3, but never `unicode`. When they are not mod-wsgi will 500. For the most part this is taken care of by webob, but in the case of the 405 handling, the webob response is not being fully massaged. mod-wsgi is doing this because it supposed to. Python WSGI server gateways have different expectations of headers depending on whether the Python is 2 or 3. See https://www.python.org/dev/peps/pep-3333/#a-note-on-string-types In addition to the unit test, the gabbi tests are now using a version of wsgi-intercept that will raise a TypeError when the application response headers are not using the correct form. This check needs to be done in wsgi-intercept rather than the gabbi tests because both wsgi-intercept and the http client makes requests transform the headers for their own purposes. This fix ensures that instead of a 500 the correct 405 response happens. Closes-Bug: #1626496 Depends-On: I3b8aabda929fe39b60e645abb6fabb9769554829 Change-Id: Ifa436e11e79adc2e159b4c5e7d3623d9a792b5f7 --- nova/api/openstack/placement/handler.py | 6 +++++- .../api/openstack/placement/test_placement_api.py | 5 +++++ .../unit/api/openstack/placement/test_handler.py | 14 +++++++++++++- test-requirements.txt | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/nova/api/openstack/placement/handler.py b/nova/api/openstack/placement/handler.py index 5d5377f4c835..d10fadd8855b 100644 --- a/nova/api/openstack/placement/handler.py +++ b/nova/api/openstack/placement/handler.py @@ -107,7 +107,11 @@ def handle_405(environ, start_response): _methods = util.wsgi_path_item(environ, '_methods') headers = {} if _methods: - headers['allow'] = _methods + # Ensure allow header is a python 2 or 3 native string (thus + # not unicode in python 2 but stay a string in python 3) + # In the process done by Routes to save the allowed methods + # to its routing table they become unicode in py2. + headers['allow'] = str(_methods) raise webob.exc.HTTPMethodNotAllowed( _('The method specified is not allowed for this resource.'), headers=headers, json_formatter=util.json_error_formatter) diff --git a/nova/tests/functional/api/openstack/placement/test_placement_api.py b/nova/tests/functional/api/openstack/placement/test_placement_api.py index b81b6c07177c..2e4c12cc5e3e 100644 --- a/nova/tests/functional/api/openstack/placement/test_placement_api.py +++ b/nova/tests/functional/api/openstack/placement/test_placement_api.py @@ -12,10 +12,15 @@ import os +import wsgi_intercept + from gabbi import driver from nova.tests.functional.api.openstack.placement import fixtures +# Check that wsgi application response headers are always +# native str. +wsgi_intercept.STRICT_RESPONSE_HEADERS = True TESTS_DIR = 'gabbits' diff --git a/nova/tests/unit/api/openstack/placement/test_handler.py b/nova/tests/unit/api/openstack/placement/test_handler.py index b0a81e15efcd..6a07343978f3 100644 --- a/nova/tests/unit/api/openstack/placement/test_handler.py +++ b/nova/tests/unit/api/openstack/placement/test_handler.py @@ -92,12 +92,24 @@ class MapperTest(test.NoDBTestCase): action = self.mapper.match(environ=environ)['action'] self.assertEqual('hello', action) - def test_405(self): + def test_405_methods(self): environ = _environ(path='/hello', method='POST') result = self.mapper.match(environ=environ) self.assertEqual(handler.handle_405, result['action']) self.assertEqual('GET', result['_methods']) + def test_405_headers(self): + environ = _environ(path='/hello', method='POST') + error = self.assertRaises(webob.exc.HTTPMethodNotAllowed, + handler.dispatch, + environ, start_response, + self.mapper) + allow_header = error.headers['allow'] + self.assertEqual('GET', allow_header) + # PEP 3333 requires that headers be whatever the native str + # is in that version of Python. Never unicode. + self.assertEqual(str, type(allow_header)) + class PlacementLoggingTest(test.NoDBTestCase): diff --git a/test-requirements.txt b/test-requirements.txt index c689af32f2e1..e7ed5dc51bbf 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -33,4 +33,4 @@ oslo.vmware>=2.11.0 # Apache-2.0 reno>=1.8.0 # Apache2 # placement functional tests -wsgi-intercept>=0.6.1 # MIT License +wsgi-intercept>=1.4.1 # MIT License