From 40118e785f0ec69bb519c7f34dcceb092006aa02 Mon Sep 17 00:00:00 2001 From: garenchan <1412950785@qq.com> Date: Thu, 4 Oct 2018 20:42:24 +0800 Subject: [PATCH 1/3] Make sure the attachment filename is text type. If attachment filename is bytes type and contains non-ascii coded bytes, then the following ASCII encoding process will trigger UnicodeDecodeError exception. Fix issue #2933. --- CHANGES.rst | 6 +++++- flask/helpers.py | 3 +++ tests/test_helpers.py | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index b7d5b6a6..cb1e6239 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,13 +13,17 @@ Unreleased (ISO-8859-1). This fixes compatibility with Gunicorn, which is stricter about header encodings than PEP 3333. (`#2766`_) - Allow custom CLIs using ``FlaskGroup`` to set the debug flag without - it always being overwritten based on environment variables. (`#2765`_) + it always being overwritten based on environment variables. + (`#2765`_) - ``flask --version`` outputs Werkzeug's version and simplifies the Python version. (`#2825`_) +- :func:`send_file` handles an ``attachment_filename`` that is a + native Python 2 string (bytes) with UTF-8 coded bytes. (`#2933`_) .. _#2766: https://github.com/pallets/flask/issues/2766 .. _#2765: https://github.com/pallets/flask/pull/2765 .. _#2825: https://github.com/pallets/flask/pull/2825 +.. _#2933: https://github.com/pallets/flask/issues/2933 Version 1.0.2 diff --git a/flask/helpers.py b/flask/helpers.py index 7679a496..24c8a5da 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -567,6 +567,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, raise TypeError('filename unavailable, required for ' 'sending as attachment') + if not isinstance(attachment_filename, text_type): + attachment_filename = attachment_filename.decode('utf-8') + try: attachment_filename = attachment_filename.encode('ascii') except UnicodeEncodeError: diff --git a/tests/test_helpers.py b/tests/test_helpers.py index ae1c0805..e3751899 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -644,6 +644,8 @@ class TestSendfile(object): (u'Ñandú/pingüino.txt', '"Nandu/pinguino.txt"', '%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt'), (u'Vögel.txt', 'Vogel.txt', 'V%C3%B6gel.txt'), + # Native string not marked as Unicode on Python 2 + ('tést.txt', 'test.txt', 't%C3%A9st.txt'), )) def test_attachment_filename_encoding(self, filename, ascii, utf8): rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=filename) From b92b2e6c743c8b09744c9c9788046c719cc38eab Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Thu, 1 Nov 2018 16:41:53 -0400 Subject: [PATCH 2/3] Do not handle RoutingExceptions with app error handlers --- flask/app.py | 9 ++++++++- tests/test_user_error_handler.py | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index 49a025cd..8d193656 100644 --- a/flask/app.py +++ b/flask/app.py @@ -20,7 +20,8 @@ from threading import Lock from werkzeug.datastructures import Headers, ImmutableDict from werkzeug.exceptions import BadRequest, BadRequestKeyError, HTTPException, \ InternalServerError, MethodNotAllowed, default_exceptions -from werkzeug.routing import BuildError, Map, RequestRedirect, Rule +from werkzeug.routing import BuildError, Map, RequestRedirect, \ + RoutingException, Rule from . import cli, json from ._compat import integer_types, reraise, string_types, text_type @@ -1638,6 +1639,12 @@ class Flask(_PackageBoundObject): if e.code is None: return e + # RoutingExceptions such as RequestRedirects are special exceptions + # used to trigger routing actions such as redirects, and also behave + # like proxy exceptions. We return these unchanged as well. + if isinstance(e, RoutingException): + return e + handler = self._find_error_handler(e) if handler is None: return e diff --git a/tests/test_user_error_handler.py b/tests/test_user_error_handler.py index 18d5f277..4e52485c 100644 --- a/tests/test_user_error_handler.py +++ b/tests/test_user_error_handler.py @@ -184,6 +184,10 @@ def test_default_error_handler(): def forbidden(): raise Forbidden() + @app.route('/slash/') + def slash(): + return '' + app.register_blueprint(bp, url_prefix='/bp') c = app.test_client() @@ -191,5 +195,6 @@ def test_default_error_handler(): assert c.get('/bp/forbidden').data == b'bp-forbidden' assert c.get('/undefined').data == b'default' assert c.get('/forbidden').data == b'forbidden' + assert c.get('/slash').location.endswith('/slash/') From 662ce2151d0f17d592133eeecdc8d7e6676db811 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 7 Jan 2019 09:52:54 -0800 Subject: [PATCH 3/3] add changelog for GH-2986 --- CHANGES.rst | 5 +++++ flask/app.py | 16 +++++++++++++--- tests/test_user_error_handler.py | 9 ++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index cb1e6239..bdf0c896 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,11 +19,16 @@ Unreleased Python version. (`#2825`_) - :func:`send_file` handles an ``attachment_filename`` that is a native Python 2 string (bytes) with UTF-8 coded bytes. (`#2933`_) +- A catch-all error handler registered for ``HTTPException`` will not + handle ``RoutingExcpetion``, which is used internally during + routing. This fixes the unexpected behavior that had been introduced + in 1.0. (`#2986`_) .. _#2766: https://github.com/pallets/flask/issues/2766 .. _#2765: https://github.com/pallets/flask/pull/2765 .. _#2825: https://github.com/pallets/flask/pull/2825 .. _#2933: https://github.com/pallets/flask/issues/2933 +.. _#2986: https://github.com/pallets/flask/pull/2986 Version 1.0.2 diff --git a/flask/app.py b/flask/app.py index 8d193656..c570a95b 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1632,6 +1632,16 @@ class Flask(_PackageBoundObject): registered error handlers and fall back to returning the exception as response. + .. versionchanged:: 1.0.3 + ``RoutingException``, used internally for actions such as + slash redirects during routing, is not passed to error + handlers. + + .. versionchanged:: 1.0 + Exceptions are looked up by code *and* by MRO, so + ``HTTPExcpetion`` subclasses can be handled with a catch-all + handler for the base ``HTTPException``. + .. versionadded:: 0.3 """ # Proxy exceptions don't have error codes. We want to always return @@ -1639,9 +1649,9 @@ class Flask(_PackageBoundObject): if e.code is None: return e - # RoutingExceptions such as RequestRedirects are special exceptions - # used to trigger routing actions such as redirects, and also behave - # like proxy exceptions. We return these unchanged as well. + # RoutingExceptions are used internally to trigger routing + # actions, such as slash redirects raising RequestRedirect. They + # are not raised or handled in user code. if isinstance(e, RoutingException): return e diff --git a/tests/test_user_error_handler.py b/tests/test_user_error_handler.py index 4e52485c..f4a08f58 100644 --- a/tests/test_user_error_handler.py +++ b/tests/test_user_error_handler.py @@ -184,9 +184,9 @@ def test_default_error_handler(): def forbidden(): raise Forbidden() - @app.route('/slash/') + @app.route("/slash/") def slash(): - return '' + return "slash" app.register_blueprint(bp, url_prefix='/bp') @@ -195,6 +195,5 @@ def test_default_error_handler(): assert c.get('/bp/forbidden').data == b'bp-forbidden' assert c.get('/undefined').data == b'default' assert c.get('/forbidden').data == b'forbidden' - assert c.get('/slash').location.endswith('/slash/') - - + # Don't handle RequestRedirect raised when adding slash. + assert c.get("/slash", follow_redirects=True).data == b"slash"