diff --git a/tornado/httputil.py b/tornado/httputil.py index a1068224..7d884e28 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -215,11 +215,11 @@ def parse_multipart_form_data(boundary, data, arguments, files): # in the wild. if boundary.startswith(b('"')) and boundary.endswith(b('"')): boundary = boundary[1:-1] - if data.endswith(b("\r\n")): - footer_length = len(boundary) + 6 - else: - footer_length = len(boundary) + 4 - parts = data[:-footer_length].split(b("--") + boundary + b("\r\n")) + final_boundary_index = data.rfind(b("--") + boundary + b("--")) + if final_boundary_index == -1: + logging.warning("Invalid multipart/form-data: no final boundary") + return + parts = data[:final_boundary_index].split(b("--") + boundary + b("\r\n")) for part in parts: if not part: continue diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py index d0e58e91..736ed6d6 100644 --- a/tornado/test/httputil_test.py +++ b/tornado/test/httputil_test.py @@ -177,6 +177,24 @@ Foo parse_multipart_form_data(b("1234"), data, args, files) self.assertEqual(files, {}) + def test_data_after_final_boundary(self): + # The spec requires that data after the final boundary be ignored. + # http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html + # In practice, some libraries include an extra CRLF after the boundary. + data = b("""\ +--1234 +Content-Disposition: form-data; name="files"; filename="ab.txt" + +Foo +--1234-- +""").replace(b("\n"), b("\r\n")) + args = {} + files = {} + parse_multipart_form_data(b("1234"), data, args, files) + file = files["files"][0] + self.assertEqual(file["filename"], "ab.txt") + self.assertEqual(file["body"], b("Foo")) + class HTTPHeadersTest(unittest.TestCase): def test_multi_line(self):