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:
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user