From dbda82afd8ac0ae4373ea35d990e124bbac91eef Mon Sep 17 00:00:00 2001 From: Marian Horban Date: Mon, 14 Sep 2015 13:43:36 -0400 Subject: [PATCH] Fix Status-Line in HTTP response There is a strict rule about constructing status line for HTTP: '...Status-Line, consisting of the protocol version followed by a numeric status code and its associated textual phrase, with each element separated by SP characters' (http://www.faqs.org/rfcs/rfc2616.html) This patch ensures an associated textual phrase for a status code is always written in the HTTP response. Closes-Bug: #1496055 Change-Id: I68d4c41d0c04871eb032d23d7b16aa2bf2c0bb73 --- nova/exception.py | 20 ++++++++++++++++++-- nova/tests/unit/test_exception.py | 22 +++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/nova/exception.py b/nova/exception.py index a3fc61cdb731..8817ebf84ffc 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -30,6 +30,7 @@ from oslo_log import log as logging from oslo_utils import excutils import six import webob.exc +from webob import util as woutil from nova.i18n import _, _LE from nova import safe_utils @@ -47,9 +48,24 @@ CONF.register_opts(exc_log_opts) class ConvertedException(webob.exc.WSGIHTTPException): - def __init__(self, code=500, title="", explanation=""): + def __init__(self, code, title="", explanation=""): self.code = code - self.title = title + # There is a strict rule about constructing status line for HTTP: + # '...Status-Line, consisting of the protocol version followed by a + # numeric status code and its associated textual phrase, with each + # element separated by SP characters' + # (http://www.faqs.org/rfcs/rfc2616.html) + # 'code' and 'title' can not be empty because they correspond + # to numeric status code and its associated text + if title: + self.title = title + else: + try: + self.title = woutil.status_reasons[self.code] + except KeyError: + msg = _LE("Improper or unknown HTTP status code used: %d") + LOG.error(msg, code) + self.title = woutil.status_generic_reasons[self.code // 100] self.explanation = explanation super(ConvertedException, self).__init__() diff --git a/nova/tests/unit/test_exception.py b/nova/tests/unit/test_exception.py index 12c87894ba7e..fab2c5a3032a 100644 --- a/nova/tests/unit/test_exception.py +++ b/nova/tests/unit/test_exception.py @@ -17,6 +17,7 @@ import inspect import six +from webob.util import status_reasons from nova import context from nova import exception @@ -146,10 +147,29 @@ class NovaExceptionTestCase(test.NoDBTestCase): self.assertEqual("some message %(somearg)s", exc.format_message()) +class ConvertedExceptionTestCase(test.NoDBTestCase): + def test_instantiate(self): + exc = exception.ConvertedException(400, 'Bad Request', 'reason') + self.assertEqual(exc.code, 400) + self.assertEqual(exc.title, 'Bad Request') + self.assertEqual(exc.explanation, 'reason') + + def test_instantiate_without_title_known_code(self): + exc = exception.ConvertedException(500) + self.assertEqual(exc.title, status_reasons[500]) + + def test_instantiate_without_title_unknown_code(self): + exc = exception.ConvertedException(499) + self.assertEqual(exc.title, 'Unknown Client Error') + + def test_instantiate_bad_code(self): + self.assertRaises(KeyError, exception.ConvertedException, 10) + + class ExceptionTestCase(test.NoDBTestCase): @staticmethod def _raise_exc(exc): - raise exc() + raise exc(500) def test_exceptions_raise(self): # NOTE(dprince): disable format errors since we are not passing kwargs