Skip to content

Commit 99c56ea

Browse files
authored
Merge pull request pallets#2804 from pallets/ascii-header
encode filenames as ascii instead of latin-1
2 parents 401d7f9 + b51ab3f commit 99c56ea

File tree

3 files changed

+27
-10
lines changed

3 files changed

+27
-10
lines changed

CHANGES.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ Version 1.0.3
99

1010
Unreleased
1111

12+
- :func:`send_file` encodes filenames as ASCII instead of Latin-1
13+
(ISO-8859-1). This fixes compatibility with Gunicorn, which is
14+
stricter about header encodings than PEP 3333. (`#2766`_)
15+
16+
.. _#2766: https://github.com/pallets/flask/issues/2766
17+
1218

1319
Version 1.0.2
1420
-------------

flask/helpers.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
506506
507507
.. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4
508508
509+
.. versionchanged:: 1.0.3
510+
Filenames are encoded with ASCII instead of Latin-1 for broader
511+
compatibility with WSGI servers.
512+
509513
:param filename_or_fp: the filename of the file to send.
510514
This is relative to the :attr:`~Flask.root_path`
511515
if a relative path is specified.
@@ -564,11 +568,11 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
564568
'sending as attachment')
565569

566570
try:
567-
attachment_filename = attachment_filename.encode('latin-1')
571+
attachment_filename = attachment_filename.encode('ascii')
568572
except UnicodeEncodeError:
569573
filenames = {
570574
'filename': unicodedata.normalize(
571-
'NFKD', attachment_filename).encode('latin-1', 'ignore'),
575+
'NFKD', attachment_filename).encode('ascii', 'ignore'),
572576
'filename*': "UTF-8''%s" % url_quote(attachment_filename),
573577
}
574578
else:

tests/test_helpers.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -638,15 +638,22 @@ def test_attachment(self, app, req_ctx):
638638
assert options['filename'] == 'index.txt'
639639
rv.close()
640640

641-
def test_attachment_with_utf8_filename(self, app, req_ctx):
642-
rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=u'Ñandú/pingüino.txt')
643-
content_disposition = set(rv.headers['Content-Disposition'].split('; '))
644-
assert content_disposition == set((
645-
'attachment',
646-
'filename="Nandu/pinguino.txt"',
647-
"filename*=UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt"
648-
))
641+
@pytest.mark.usefixtures('req_ctx')
642+
@pytest.mark.parametrize(('filename', 'ascii', 'utf8'), (
643+
('index.html', 'index.html', False),
644+
(u'Ñandú/pingüino.txt', '"Nandu/pinguino.txt"',
645+
'%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt'),
646+
(u'Vögel.txt', 'Vogel.txt', 'V%C3%B6gel.txt'),
647+
))
648+
def test_attachment_filename_encoding(self, filename, ascii, utf8):
649+
rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=filename)
649650
rv.close()
651+
content_disposition = rv.headers['Content-Disposition']
652+
assert 'filename=%s' % ascii in content_disposition
653+
if utf8:
654+
assert "filename*=UTF-8''" + utf8 in content_disposition
655+
else:
656+
assert "filename*=UTF-8''" not in content_disposition
650657

651658
def test_static_file(self, app, req_ctx):
652659
# default cache timeout is 12 hours

0 commit comments

Comments
 (0)