diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py index e77e6fa8c..024c78dfa 100644 --- a/keystone/common/wsgi.py +++ b/keystone/common/wsgi.py @@ -193,6 +193,7 @@ class Application(BaseApplication): # values by the container and processed by the pipeline. the complete # set is not yet know. context['environment'] = req.environ + context['accept_header'] = req.accept req.environ = None params.update(arg_dict) diff --git a/keystone/controllers.py b/keystone/controllers.py index d60dde188..edabd283d 100644 --- a/keystone/controllers.py +++ b/keystone/controllers.py @@ -60,6 +60,23 @@ def register_version(version): _VERSIONS.append(version) +class MimeTypes: + JSON = 'application/json' + JSON_HOME = 'application/json-home' + + +def v3_mime_type_best_match(context): + + # accept_header is a WebOb MIMEAccept object so supports best_match. + accept_header = context['accept_header'] + + if not accept_header: + return MimeTypes.JSON + + SUPPORTED_TYPES = [MimeTypes.JSON, MimeTypes.JSON_HOME] + return accept_header.best_match(SUPPORTED_TYPES) + + class Version(wsgi.Application): def __init__(self, version_type, routers=None): @@ -157,10 +174,12 @@ class Version(wsgi.Application): def get_version_v3(self, context): versions = self._get_versions_list(context) if 'v3' in _VERSIONS: - if context['headers'].get('Accept') == 'application/json-home': + req_mime_type = v3_mime_type_best_match(context) + + if req_mime_type == MimeTypes.JSON_HOME: return wsgi.render_response( body=self._get_json_home_v3(), - headers=(('Content-Type', 'application/json-home'),)) + headers=(('Content-Type', MimeTypes.JSON_HOME),)) return wsgi.render_response(body={ 'version': versions['v3'] diff --git a/keystone/tests/test_versions.py b/keystone/tests/test_versions.py index 0f4d167bd..240c3bc04 100644 --- a/keystone/tests/test_versions.py +++ b/keystone/tests/test_versions.py @@ -563,6 +563,48 @@ class VersionTestCase(tests.TestCase): self.assertThat(jsonutils.loads(resp.body), tt_matchers.Equals(exp_json_home_data)) + def test_accept_type_handling(self): + # Accept headers with multiple types and qvalues are handled. + + def make_request(accept_types=None): + client = self.client(self.public_app) + headers = None + if accept_types: + headers = {'Accept': accept_types} + resp = client.get('/v3', headers=headers) + self.assertThat(resp.status, tt_matchers.Equals('200 OK')) + return resp.headers['Content-Type'] + + JSON = controllers.MimeTypes.JSON + JSON_HOME = controllers.MimeTypes.JSON_HOME + + JSON_MATCHER = tt_matchers.Equals(JSON) + JSON_HOME_MATCHER = tt_matchers.Equals(JSON_HOME) + + # Default is JSON. + self.assertThat(make_request(), JSON_MATCHER) + + # Can request JSON and get JSON. + self.assertThat(make_request(JSON), JSON_MATCHER) + + # Can request JSONHome and get JSONHome. + self.assertThat(make_request(JSON_HOME), JSON_HOME_MATCHER) + + # If request JSON, JSON Home get JSON. + accept_types = '%s, %s' % (JSON, JSON_HOME) + self.assertThat(make_request(accept_types), JSON_MATCHER) + + # If request JSON Home, JSON get JSON. + accept_types = '%s, %s' % (JSON_HOME, JSON) + self.assertThat(make_request(accept_types), JSON_MATCHER) + + # If request JSON Home, JSON;q=0.5 get JSON Home. + accept_types = '%s, %s;q=0.5' % (JSON_HOME, JSON) + self.assertThat(make_request(accept_types), JSON_HOME_MATCHER) + + # If request some unknown mime-type, get JSON. + self.assertThat(make_request(self.getUniqueString()), JSON_MATCHER) + class VersionInheritEnabledTestCase(tests.TestCase): def setUp(self):