From 49a409031b5b7214b5acf9773ef3cc7c55467184 Mon Sep 17 00:00:00 2001 From: Andy McCrae Date: Tue, 11 Apr 2017 14:19:44 +0100 Subject: [PATCH] Allow CONTENT_LENGTH to be present but empty The CONTENT_LENGTH environ can be present, but empty, which returns None, and causes a ValueError when attempting to use .int(). This patch removes the setting of CONTENT_LENGTH to an integer, but instead ensures that if CONTENT_LENGTH is not empty it is an integer, to prevent a situation where a bogus "CONTENT_LENGTH" header is specified. Additionally, as the CONTENT_TYPE environ can similarly be present but empty, we should .get() it in a similar fashion to ensure it isn't present but None when CONTENT_LENGTH is specified. Change-Id: I66b6f9afbea8bf037997a59ba0b976f83c9825fb Closes-Bug: #1681843 --- nova/api/openstack/placement/handler.py | 18 ++++++--- .../api/openstack/placement/test_handler.py | 39 +++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/nova/api/openstack/placement/handler.py b/nova/api/openstack/placement/handler.py index 8b0da199a..0df54a99a 100644 --- a/nova/api/openstack/placement/handler.py +++ b/nova/api/openstack/placement/handler.py @@ -177,13 +177,19 @@ class PlacementHandler(object): raise webob.exc.HTTPForbidden( _('admin required'), json_formatter=util.json_error_formatter) - # Check that an incoming request with a content-length - # header also has a content-type header. If not raise a 400. - if int(environ.get('CONTENT_LENGTH', 0)): - if 'CONTENT_TYPE' not in environ: + # Check that an incoming request with a content-length header + # that is an integer > 0 and not empty, also has a content-type + # header that is not empty. If not raise a 400. + clen = environ.get('CONTENT_LENGTH') + try: + if clen and (int(clen) > 0) and not environ.get('CONTENT_TYPE'): raise webob.exc.HTTPBadRequest( - _('content-type header required'), - json_formatter=util.json_error_formatter) + _('content-type header required when content-length > 0'), + json_formatter=util.json_error_formatter) + except ValueError as exc: + raise webob.exc.HTTPBadRequest( + _('content-length header must be an integer'), + json_formatter=util.json_error_formatter) try: return dispatch(environ, start_response, self._map) # Trap the NotFound exceptions raised by the objects used diff --git a/nova/tests/unit/api/openstack/placement/test_handler.py b/nova/tests/unit/api/openstack/placement/test_handler.py index b75b28f97..b77242109 100644 --- a/nova/tests/unit/api/openstack/placement/test_handler.py +++ b/nova/tests/unit/api/openstack/placement/test_handler.py @@ -142,3 +142,42 @@ class DeclarationsTest(test.NoDBTestCase): environ = _environ(path='') result = self.mapper.match(environ=environ) self.assertEqual(root.home, result['action']) + + +class ContentHeadersTest(test.NoDBTestCase): + + def setUp(self): + super(ContentHeadersTest, self).setUp() + self.environ = _environ(path='/') + self.app = handler.PlacementHandler() + + def test_no_content_type(self): + self.environ['CONTENT_LENGTH'] = '10' + self.assertRaisesRegex(webob.exc.HTTPBadRequest, + "content-type header required when " + "content-length > 0", self.app, + self.environ, start_response) + + def test_non_integer_content_length(self): + self.environ['CONTENT_LENGTH'] = 'foo' + self.assertRaisesRegex(webob.exc.HTTPBadRequest, + "content-length header must be an integer", + self.app, self.environ, start_response) + + def test_empty_content_type(self): + self.environ['CONTENT_LENGTH'] = '10' + self.environ['CONTENT_TYPE'] = '' + self.assertRaisesRegex(webob.exc.HTTPBadRequest, + "content-type header required when " + "content-length > 0", self.app, + self.environ, start_response) + + def test_empty_content_length_and_type_works(self): + self.environ['CONTENT_LENGTH'] = '' + self.environ['CONTENT_TYPE'] = '' + self.app(self.environ, start_response) + + def test_content_length_and_type_works(self): + self.environ['CONTENT_LENGTH'] = '10' + self.environ['CONTENT_TYPE'] = 'foo' + self.app(self.environ, start_response)