From 31b88b87b9b6faa51de18877603f16a9b14b5a04 Mon Sep 17 00:00:00 2001 From: Gordon Chung Date: Thu, 14 Mar 2013 13:07:14 -0400 Subject: [PATCH] enable xml error message response return xml error message when Accept:application/xml request received. default to json response when none specified. Change-Id: Idd454c5bc76adb583bc0a9afce9f31659e7fe2ac --- ceilometer/api/middleware.py | 28 +++++++++++++--- tests/api/v2/test_app.py | 64 ++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/ceilometer/api/middleware.py b/ceilometer/api/middleware.py index 76676613d..9674e5eb9 100644 --- a/ceilometer/api/middleware.py +++ b/ceilometer/api/middleware.py @@ -22,6 +22,12 @@ Based on pecan.middleware.errordocument """ import json +import webob +from xml import etree as et + +from ceilometer.openstack.common import log + +LOG = log.getLogger(__name__) class ParsableErrorMiddleware(object): @@ -61,12 +67,24 @@ class ParsableErrorMiddleware(object): app_iter = self.app(environ, replacement_start_response) if (state['status_code'] / 100) not in (2, 3): - # FIXME(dhellmann): Always returns errors as JSON, - # but should look at the environ to determine - # the desired type. - body = [json.dumps({'error_message': '\n'.join(app_iter)})] + req = webob.Request(environ) + if (req.accept.best_match(['application/json', 'application/xml']) + == 'application/xml'): + try: + # simple check xml is valid + body = [et.ElementTree.tostring( + et.ElementTree.fromstring('' + + '\n'.join(app_iter) + + ''))] + except et.ElementTree.ParseError as err: + LOG.error('Error parsing HTTP response: %s' % err) + body = ['%s' % state['status_code'] + + ''] + state['headers'].append(('Content-Type', 'application/xml')) + else: + body = [json.dumps({'error_message': '\n'.join(app_iter)})] + state['headers'].append(('Content-Type', 'application/json')) state['headers'].append(('Content-Length', len(body[0]))) - state['headers'].append(('Content-Type', 'application/json')) else: body = app_iter return body diff --git a/tests/api/v2/test_app.py b/tests/api/v2/test_app.py index ebb9974f6..b5666e4f5 100644 --- a/tests/api/v2/test_app.py +++ b/tests/api/v2/test_app.py @@ -26,6 +26,7 @@ from oslo.config import cfg from ceilometer.api import app from ceilometer.api import acl from ceilometer import service +from .base import FunctionalTest class TestApp(unittest.TestCase): @@ -50,3 +51,66 @@ class TestApp(unittest.TestCase): api_app = app.setup_app() self.assertEqual(api_app.auth_protocol, 'barttp') os.unlink(tmpfile) + + +class TestApiMiddleware(FunctionalTest): + + def test_json_parsable_error_middleware_404(self): + response = self.get_json('/invalid_path', + expect_errors=True, + headers={"Accept": + "application/json"} + ) + self.assertEqual(response.status_int, 404) + self.assertEqual(response.content_type, "application/json") + self.assertTrue(response.json['error_message']) + response = self.get_json('/invalid_path', + expect_errors=True, + headers={"Accept": + "application/json,application/xml"} + ) + self.assertEqual(response.status_int, 404) + self.assertEqual(response.content_type, "application/json") + self.assertTrue(response.json['error_message']) + response = self.get_json('/invalid_path', + expect_errors=True, + headers={"Accept": + "application/xml;q=0.8, \ + application/json"} + ) + self.assertEqual(response.status_int, 404) + self.assertEqual(response.content_type, "application/json") + self.assertTrue(response.json['error_message']) + response = self.get_json('/invalid_path', + expect_errors=True + ) + self.assertEqual(response.status_int, 404) + self.assertEqual(response.content_type, "application/json") + self.assertTrue(response.json['error_message']) + response = self.get_json('/invalid_path', + expect_errors=True, + headers={"Accept": + "text/html,*/*"} + ) + self.assertEqual(response.status_int, 404) + self.assertEqual(response.content_type, "application/json") + self.assertTrue(response.json['error_message']) + + def test_xml_parsable_error_middleware_404(self): + response = self.get_json('/invalid_path', + expect_errors=True, + headers={"Accept": + "application/xml,*/*"} + ) + self.assertEqual(response.status_int, 404) + self.assertEqual(response.content_type, "application/xml") + self.assertEqual(response.xml.tag, 'error_message') + response = self.get_json('/invalid_path', + expect_errors=True, + headers={"Accept": + "application/json;q=0.8 \ + ,application/xml"} + ) + self.assertEqual(response.status_int, 404) + self.assertEqual(response.content_type, "application/xml") + self.assertEqual(response.xml.tag, 'error_message')