Skip to content

Commit

Permalink
Merge pull request #1208 from davidism/header-option-quotes
Browse files Browse the repository at this point in the history
match header option value with single quotes
  • Loading branch information
davidism committed Dec 5, 2017
2 parents fa16549 + 3737044 commit a1759a8
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Expand Up @@ -31,6 +31,7 @@ unreleased
- Secure cookie contrib works with string secret key on Python 3. (`#1205`_)
- Shared data middleware accepts a list instead of a dict of static locations
to preserve lookup order. (`#1197`_)
- HTTP header values without encoding can contain single quotes. (`#1208`_)
- The built-in dev server supports receiving requests with chunked transfer
encoding. (`#1198`_)

Expand All @@ -47,6 +48,7 @@ unreleased
.. _#1197: https://github.com/pallets/werkzeug/pull/1197
.. _#1198: https://github.com/pallets/werkzeug/pull/1198
.. _#1205: https://github.com/pallets/werkzeug/pull/1205
.. _#1208: https://github.com/pallets/werkzeug/pull/1208


Version 0.12.2
Expand Down
8 changes: 8 additions & 0 deletions tests/test_http.py
Expand Up @@ -280,6 +280,14 @@ def test_parse_options_header(self):
('form-data', {'name': u'\u016an\u012dc\u014dde\u033d',
'filename': 'some_file.txt'})

def test_parse_options_header_value_with_quotes(self):
assert http.parse_options_header(
'form-data; name="file"; filename="t\'es\'t.txt"'
) == ('form-data', {'name': 'file', 'filename': "t'es't.txt"})
assert http.parse_options_header(
'form-data; name="file"; filename*=UTF-8\'\'"\'🐍\'.txt"'
) == ('form-data', {'name': 'file', 'filename': u"'🐍'.txt"})

def test_parse_options_header_broken_values(self):
# Issue #995
assert http.parse_options_header(' ') == ('', {})
Expand Down
30 changes: 24 additions & 6 deletions werkzeug/http.py
Expand Up @@ -62,12 +62,30 @@
'^_`abcdefghijklmnopqrstuvwxyz|~')
_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')
_unsafe_header_chars = set('()<>@,;:\"/[]?={} \t')
_quoted_string_re = r'"[^"\\]*(?:\\.[^"\\]*)*"'
_option_header_piece_re = re.compile(
r';\s*(%s|[^\s;,=\*]+)\s*'
r'(?:\*?=\s*(?:([^\s]+?)\'([^\s]*?)\')?(%s|[^;,]+)?)?\s*' %
(_quoted_string_re, _quoted_string_re)
)
_option_header_piece_re = re.compile(r'''
;\s*
(?P<key>
"[^"\\]*(?:\\.[^"\\]*)*" # quoted string
|
[^\s;,=*]+ # token
)
\s*
(?: # optionally followed by =value
(?: # equals sign, possibly with encoding
\*\s*=\s* # * indicates extended notation
(?P<encoding>[^\s]+?)
'(?P<language>[^\s]*?)'
|
=\s* # basic notation
)
(?P<value>
"[^"\\]*(?:\\.[^"\\]*)*" # quoted string
|
[^;,]+ # token
)?
)?
\s*
''', flags=re.VERBOSE)
_option_header_start_mime_type = re.compile(r',\s*([^;,\s]+)([;,]\s*.+)?')

_entity_headers = frozenset([
Expand Down

0 comments on commit a1759a8

Please sign in to comment.