Browse Source

encode filenames as ascii instead of latin-1

latin-1 is allowed by pep3333, but that breaks gunicorn
pull/2804/head
David Lord 6 years ago
parent
commit
b51ab3ff2c
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
  1. 6
      CHANGES.rst
  2. 8
      flask/helpers.py
  3. 21
      tests/test_helpers.py

6
CHANGES.rst

@ -9,6 +9,12 @@ Version 1.0.3
Unreleased Unreleased
- :func:`send_file` encodes filenames as ASCII instead of Latin-1
(ISO-8859-1). This fixes compatibility with Gunicorn, which is
stricter about header encodings than PEP 3333. (`#2766`_)
.. _#2766: https://github.com/pallets/flask/issues/2766
Version 1.0.2 Version 1.0.2
------------- -------------

8
flask/helpers.py

@ -506,6 +506,10 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
.. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4 .. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4
.. versionchanged:: 1.0.3
Filenames are encoded with ASCII instead of Latin-1 for broader
compatibility with WSGI servers.
:param filename_or_fp: the filename of the file to send. :param filename_or_fp: the filename of the file to send.
This is relative to the :attr:`~Flask.root_path` This is relative to the :attr:`~Flask.root_path`
if a relative path is specified. if a relative path is specified.
@ -564,11 +568,11 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
'sending as attachment') 'sending as attachment')
try: try:
attachment_filename = attachment_filename.encode('latin-1') attachment_filename = attachment_filename.encode('ascii')
except UnicodeEncodeError: except UnicodeEncodeError:
filenames = { filenames = {
'filename': unicodedata.normalize( 'filename': unicodedata.normalize(
'NFKD', attachment_filename).encode('latin-1', 'ignore'), 'NFKD', attachment_filename).encode('ascii', 'ignore'),
'filename*': "UTF-8''%s" % url_quote(attachment_filename), 'filename*': "UTF-8''%s" % url_quote(attachment_filename),
} }
else: else:

21
tests/test_helpers.py

@ -638,15 +638,22 @@ class TestSendfile(object):
assert options['filename'] == 'index.txt' assert options['filename'] == 'index.txt'
rv.close() rv.close()
def test_attachment_with_utf8_filename(self, app, req_ctx): @pytest.mark.usefixtures('req_ctx')
rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=u'Ñandú/pingüino.txt') @pytest.mark.parametrize(('filename', 'ascii', 'utf8'), (
content_disposition = set(rv.headers['Content-Disposition'].split('; ')) ('index.html', 'index.html', False),
assert content_disposition == set(( (u'Ñandú/pingüino.txt', '"Nandu/pinguino.txt"',
'attachment', '%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt'),
'filename="Nandu/pinguino.txt"', (u'Vögel.txt', 'Vogel.txt', 'V%C3%B6gel.txt'),
"filename*=UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt"
)) ))
def test_attachment_filename_encoding(self, filename, ascii, utf8):
rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=filename)
rv.close() rv.close()
content_disposition = rv.headers['Content-Disposition']
assert 'filename=%s' % ascii in content_disposition
if utf8:
assert "filename*=UTF-8''" + utf8 in content_disposition
else:
assert "filename*=UTF-8''" not in content_disposition
def test_static_file(self, app, req_ctx): def test_static_file(self, app, req_ctx):
# default cache timeout is 12 hours # default cache timeout is 12 hours

Loading…
Cancel
Save