From: Ben Darnell Date: Sun, 10 Jul 2011 23:13:45 +0000 (-0700) Subject: Support multi-line headers X-Git-Tag: v2.1.0~94 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=67ff311a3c0a70bc49250a903630ea50e66fca32;p=thirdparty%2Ftornado.git Support multi-line headers Closes #9. --- diff --git a/tornado/httputil.py b/tornado/httputil.py index 6ba34f7a0..3b78dc640 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -54,6 +54,7 @@ class HTTPHeaders(dict): # our __setitem__ dict.__init__(self) self._as_list = {} + self._last_key = None self.update(*args, **kwargs) # new public methods @@ -61,6 +62,7 @@ class HTTPHeaders(dict): def add(self, name, value): """Adds a new value for the given key.""" norm_name = HTTPHeaders._normalize_name(name) + self._last_key = norm_name if norm_name in self: # bypass our override of __setitem__ since it modifies _as_list dict.__setitem__(self, norm_name, self[norm_name] + ',' + value) @@ -91,8 +93,15 @@ class HTTPHeaders(dict): >>> h.get('content-type') 'text/html' """ - name, value = line.split(":", 1) - self.add(name, value.strip()) + if line[0].isspace(): + # continuation of a multi-line header + new_part = ' ' + line.lstrip() + self._as_list[self._last_key][-1] += new_part + dict.__setitem__(self, self._last_key, + self[self._last_key] + new_part) + else: + name, value = line.split(":", 1) + self.add(name, value.strip()) @classmethod def parse(cls, headers): diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py index a1fcf61cb..0566b6e0e 100644 --- a/tornado/test/httputil_test.py +++ b/tornado/test/httputil_test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from tornado.httputil import url_concat, parse_multipart_form_data +from tornado.httputil import url_concat, parse_multipart_form_data, HTTPHeaders from tornado.escape import utf8 from tornado.testing import LogTrapTestCase from tornado.util import b @@ -113,3 +113,28 @@ Foo file = files["files"][0] self.assertEqual(file["filename"], filename) self.assertEqual(file["body"], b("Foo")) + +class HTTPHeadersTest(unittest.TestCase): + def test_multi_line(self): + # Lines beginning with whitespace are appended to the previous line + # with any leading whitespace replaced by a single space. + # Note that while multi-line headers are a part of the HTTP spec, + # their use is strongly discouraged. + data = """\ +Foo: bar + baz +Asdf: qwer +\tzxcv +Foo: even + more + lines +""".replace("\n", "\r\n") + headers = HTTPHeaders.parse(data) + self.assertEqual(headers["asdf"], "qwer zxcv") + self.assertEqual(headers.get_list("asdf"), ["qwer zxcv"]) + self.assertEqual(headers["Foo"], "bar baz,even more lines") + self.assertEqual(headers.get_list("foo"), ["bar baz", "even more lines"]) + self.assertEqual(sorted(list(headers.get_all())), + [("Asdf", "qwer zxcv"), + ("Foo", "bar baz"), + ("Foo", "even more lines")])