Wrap default responders with global hooks wrt #223

This enables global hooks for OPTIONS and Method Not Allowed (405)
default responses. This fixes issues with CORS preflight OPTIONS
requests not getting correct headers set by custom hooks.
This commit is contained in:
Sebastián Magrí
2014-03-19 23:47:41 -04:30
parent 36abafc8a2
commit f1c5e5034b
3 changed files with 112 additions and 7 deletions

View File

@@ -204,18 +204,20 @@ def create_http_method_map(resource, uri_fields, before, after):
# Attach a resource for unsupported HTTP methods
allowed_methods = sorted(list(method_map.keys()))
# NOTE(sebasmagri): We want the OPTIONS and 405 (Not Allowed) methods
# responders to be wrapped on global hooks
if 'OPTIONS' not in method_map:
# OPTIONS itself is intentionally excluded from the Allow header
# This default responder does not run the hooks
method_map['OPTIONS'] = responders.create_default_options(
responder = responders.create_default_options(
allowed_methods)
method_map['OPTIONS'] = _wrap_with_hooks(before, after, responder)
allowed_methods.append('OPTIONS')
na_responder = responders.create_method_not_allowed(allowed_methods)
for method in HTTP_METHODS:
if method not in allowed_methods:
method_map[method] = na_responder
method_map[method] = _wrap_with_hooks(before, after, na_responder)
return method_map

View File

@@ -25,6 +25,14 @@ def cuteness(req, resp):
resp.body += ' and cute'
def fluffiness_in_the_head(req, resp):
resp.set_header('X-Fluffiness', 'fluffy')
def cuteness_in_the_head(req, resp):
resp.set_header('X-Cuteness', 'cute')
class WrappedRespondersResource(object):
@falcon.after(serialize_body)
@@ -93,10 +101,6 @@ class TestHooks(testing.TestBase):
self.simulate_request(self.test_route)
self.assertEqual(b'fluffy', zoo_resource.resp.body_encoded)
# hook does not affect the default on_options
body = self.simulate_request(self.test_route, method='OPTIONS')
self.assertEqual(falcon.HTTP_204, self.srmock.status)
self.assertEqual([], body)
def test_multiple_global_hook(self):
self.api = falcon.API(after=[fluffiness, cuteness])
@@ -107,6 +111,55 @@ class TestHooks(testing.TestBase):
self.simulate_request(self.test_route)
self.assertEqual(b'fluffy and cute', zoo_resource.resp.body_encoded)
def test_global_hook_wrap_default_on_options(self):
self.api = falcon.API(after=fluffiness_in_the_head)
zoo_resource = ZooResource()
self.api.add_route(self.test_route, zoo_resource)
self.simulate_request(self.test_route, method='OPTIONS')
self.assertEqual(falcon.HTTP_204, self.srmock.status)
self.assertEqual('fluffy', self.srmock.headers_dict['X-Fluffiness'])
def test_global_hook_wrap_default_405(self):
self.api = falcon.API(after=fluffiness_in_the_head)
zoo_resource = ZooResource()
self.api.add_route(self.test_route, zoo_resource)
self.simulate_request(self.test_route, method='POST')
self.assertEqual(falcon.HTTP_405, self.srmock.status)
self.assertEqual('fluffy', self.srmock.headers_dict['X-Fluffiness'])
def test_multiple_global_hooks_wrap_default_on_options(self):
self.api = falcon.API(after=[fluffiness_in_the_head,
cuteness_in_the_head])
zoo_resource = ZooResource()
self.api.add_route(self.test_route, zoo_resource)
self.simulate_request(self.test_route, method='OPTIONS')
self.assertEqual(falcon.HTTP_204, self.srmock.status)
self.assertEqual('fluffy', self.srmock.headers_dict['X-Fluffiness'])
self.assertEqual('cute', self.srmock.headers_dict['X-Cuteness'])
def test_multiple_global_hooks_wrap_default_405(self):
self.api = falcon.API(after=[fluffiness_in_the_head,
cuteness_in_the_head])
zoo_resource = ZooResource()
self.api.add_route(self.test_route, zoo_resource)
self.simulate_request(self.test_route, method='POST')
self.assertEqual(falcon.HTTP_405, self.srmock.status)
self.assertEqual('fluffy', self.srmock.headers_dict['X-Fluffiness'])
self.assertEqual('cute', self.srmock.headers_dict['X-Cuteness'])
def test_output_validator(self):
self.simulate_request(self.test_route)
self.assertEqual(falcon.HTTP_723, self.srmock.status)

View File

@@ -40,6 +40,14 @@ def frogs(req, resp, params):
params['frogs'] = 'not fluffy'
def bunnies_in_the_head(req, resp, params):
resp.set_header('X-Bunnies', 'fluffy')
def frogs_in_the_head(req, resp, params):
resp.set_header('X-Frogs', 'not fluffy')
class WrappedRespondersResource(object):
@falcon.before(validate_param)
@@ -128,6 +136,48 @@ class TestHooks(testing.TestBase):
self.assertEqual('fluffy', zoo_resource.bunnies)
self.assertEqual('not fluffy', zoo_resource.frogs)
def test_global_hook_wrap_default_on_options(self):
self.api = falcon.API(before=frogs_in_the_head)
bunny_resource = BunnyResource()
self.api.add_route(self.test_route, bunny_resource)
self.simulate_request(self.test_route, method='OPTIONS')
self.assertEqual(falcon.HTTP_204, self.srmock.status)
self.assertEqual('not fluffy', self.srmock.headers_dict['X-Frogs'])
def test_global_hook_wrap_default_405(self):
self.api = falcon.API(before=[frogs_in_the_head])
bunny_resource = BunnyResource()
self.api.add_route(self.test_route, bunny_resource)
# on_post is not defined in ZooResource
self.simulate_request(self.test_route, method='POST')
self.assertEqual(falcon.HTTP_405, self.srmock.status)
self.assertEqual('not fluffy', self.srmock.headers_dict['X-Frogs'])
def test_multiple_global_hooks_wrap_default_on_options(self):
self.api = falcon.API(before=[frogs_in_the_head, bunnies_in_the_head])
bunny_resource = BunnyResource()
self.api.add_route(self.test_route, bunny_resource)
self.simulate_request(self.test_route, method='OPTIONS')
self.assertEqual('not fluffy', self.srmock.headers_dict['X-Frogs'])
self.assertEqual('fluffy', self.srmock.headers_dict['X-Bunnies'])
def test_multiple_global_hooks_wrap_default_405(self):
self.api = falcon.API(before=[frogs_in_the_head, bunnies_in_the_head])
bunny_resource = BunnyResource()
self.api.add_route(self.test_route, bunny_resource)
# on_post is not defined in ZooResource
self.simulate_request(self.test_route, method='POST')
self.assertEqual('not fluffy', self.srmock.headers_dict['X-Frogs'])
self.assertEqual('fluffy', self.srmock.headers_dict['X-Bunnies'])
def test_input_validator(self):
self.simulate_request(self.test_route, method='PUT')
self.assertEqual(falcon.HTTP_400, self.srmock.status)