Skip to content

Commit

Permalink
Update the request header compatibility code for Python 2.7 to proper…
Browse files Browse the repository at this point in the history
…ly fold headers persuant to RFC 2616.

Resolves pallets#1080
  • Loading branch information
aenglander committed May 6, 2019
1 parent 627a90e commit 14afe29
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CHANGES.rst
@@ -1,5 +1,12 @@
.. currentmodule:: werkzeug

Version 0.15.3
--------------

- Properly handle multi-line header folding in development server in
Python 2.7. (:issue:`1080`)


Version 0.15.2
--------------

Expand Down
25 changes: 22 additions & 3 deletions src/werkzeug/serving.py
Expand Up @@ -218,6 +218,7 @@ def shutdown_server():

for key, value in self.get_header_items():
key = key.upper().replace("-", "_")
value = value.replace("\r\n", "")
if key not in ("CONTENT_TYPE", "CONTENT_LENGTH"):
key = "HTTP_" + key
if key in environ:
Expand Down Expand Up @@ -434,7 +435,8 @@ def get_header_items(self):
This function provides Python 2/3 compatibility as related to the
parsing of request headers. Python 2.7 is not compliant with
RFC 3875 Section 4.1.18 which requires multiple values for headers
to be provided. This function will return a matching list regardless
to be provided or RFC 2616 which allows for folding of multi-line
headers. This function will return a matching list regardless
of Python version. It can be removed once Python 2.7 support
is dropped.
Expand All @@ -445,9 +447,26 @@ def get_header_items(self):
# W3C RFC 2616 Section 4.2.
items = []
for header in self.headers.headers:
# Remove "\n\r" from the header and split on ":" to get
# Remove "\r\n" from the header and split on ":" to get
# the field name and value.
key, value = header[0:-2].split(":", 1)
try:
key, value = header[0:-2].split(":", 1)
except ValueError as e:
# If header could not be slit with : but starts with white
# space and it follows an existing header, it's a folded
# header.
if header[0] in ("\t", " ") and len(items) > 0:
# Pop off the last header
key, value = items.pop()
# Append the current header to the value of the last
# header which will be placed back on the end of the
# list
value = value + header
# Otherwise it's just a bad header and should error
else:
# Re-raise the value error
raise e

# Add the key and the value once stripped of leading
# white space. The specification allows for stripping
# trailing white space but the Python 3 code does not
Expand Down
30 changes: 30 additions & 0 deletions tests/test_serving.py
Expand Up @@ -510,6 +510,36 @@ def app(environ, start_response):
conn.close()


def test_multiline_header_folding_for_http_1_1(dev_server):
"""
This is testing the provision of multi-line header folding per:
* RFC 2616 Section 2.2
* RFC 3875 Section 4.1.18
"""
server = dev_server(
r"""
from werkzeug.wrappers import Response
def app(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return [environ['HTTP_XYZ'].encode()]
"""
)

conn = httplib.HTTPConnection("127.0.0.1", server.port)
conn.connect()
conn.putrequest("GET", "/")
conn.putheader("Accept", "text/plain")
conn.putheader("XYZ", "first-line", "second-line", "third-line")
conn.endheaders()
conn.send(b"")
res = conn.getresponse()

assert res.status == 200
assert res.read() == b"first-line\tsecond-line\tthird-line"

conn.close()


def can_test_unix_socket():
if not hasattr(socket, "AF_UNIX"):
return False
Expand Down

0 comments on commit 14afe29

Please sign in to comment.