Enhance GET /v3 to handle Accept header

Keystone wasn't handling the Accept header for GET /v3 according to
the HTTP 1.1 spec[1]. This change enhances the Keystone server so
that it supports using multiple values and qvalues in the Accept
header for GET /v3. This allows requesting a JSON Home or JSON
response with a preference for JSON Home, for example.

[1] http://tools.ietf.org/html/rfc2616#section-14.1

bp json-home

Change-Id: Ifb204b6860c61a874ecbf92388878685ac437987
This commit is contained in:
Brant Knudson 2014-08-19 20:53:25 -05:00 committed by Dolph Mathews
parent 9077fdfe11
commit c2e40d3de7
3 changed files with 64 additions and 2 deletions

View File

@ -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)

View File

@ -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']

View File

@ -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):