From dbf55de7e8bfcf7cde967aa72ea006b764d484ac Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 28 Jul 2010 01:25:08 +0200 Subject: [PATCH] Fixed an issue where the default `OPTIONS` response was not exposing all valid methods in the `Allow` header. This fixes #97 Signed-off-by: Armin Ronacher --- CHANGES | 3 +++ flask/app.py | 23 +++++++++++++++++++---- tests/flask_tests.py | 11 +++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 0bd84823..b9aa3739 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,9 @@ Version 0.6.1 Bugfix release, release date to be announced. +- Fixed an issue where the default `OPTIONS` response was + not exposing all valid methods in the `Allow` header. + Version 0.6 ----------- diff --git a/flask/app.py b/flask/app.py index 9b6b7836..72ed6292 100644 --- a/flask/app.py +++ b/flask/app.py @@ -19,7 +19,8 @@ from jinja2 import Environment from werkzeug import ImmutableDict from werkzeug.routing import Map, Rule -from werkzeug.exceptions import HTTPException, InternalServerError +from werkzeug.exceptions import HTTPException, InternalServerError, \ + MethodNotAllowed from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \ _tojson_filter, _endpoint_from_view_func @@ -689,14 +690,28 @@ class Flask(_PackageBoundObject): # if we provide automatic options for this URL and the # request came with the OPTIONS method, reply automatically if rule.provide_automatic_options and req.method == 'OPTIONS': - rv = self.response_class() - rv.allow.update(rule.methods) - return rv + return self._make_default_options_response() # otherwise dispatch to the handler for that endpoint return self.view_functions[rule.endpoint](**req.view_args) except HTTPException, e: return self.handle_http_exception(e) + def _make_default_options_response(self): + # This would be nicer in Werkzeug 0.7, which however currently + # is not released. Werkzeug 0.7 provides a method called + # allowed_methods() that returns all methods that are valid for + # a given path. + methods = [] + try: + _request_ctx_stack.top.url_adapter.match(method='--') + except MethodNotAllowed, e: + methods = e.valid_methods + except HTTPException, e: + pass + rv = self.response_class() + rv.allow.update(methods) + return rv + def make_response(self, rv): """Converts the return value from a view function to a real response object that is an instance of :attr:`response_class`. diff --git a/tests/flask_tests.py b/tests/flask_tests.py index 3000e41c..93bdfef7 100644 --- a/tests/flask_tests.py +++ b/tests/flask_tests.py @@ -120,6 +120,17 @@ class BasicFunctionalityTestCase(unittest.TestCase): assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] assert rv.data == '' + def test_options_on_multiple_rules(self): + app = flask.Flask(__name__) + @app.route('/', methods=['GET', 'POST']) + def index(): + return 'Hello World' + @app.route('/', methods=['PUT']) + def index_put(): + return 'Aha!' + rv = app.test_client().open('/', method='OPTIONS') + assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] + def test_request_dispatching(self): app = flask.Flask(__name__) @app.route('/')