diff --git a/tornado/httputil.py b/tornado/httputil.py index b9cd1065..f68c7414 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -236,6 +236,8 @@ def parse_body_arguments(content_type, body, arguments, files): uri_arguments = parse_qs_bytes(native_str(body)) for name, values in uri_arguments.items(): values = [v for v in values if v] + uri_arguments = parse_qs_bytes(native_str(body), keep_blank_values=True) + for name, values in uri_arguments.items(): if values: arguments.setdefault(name, []).extend(values) elif content_type.startswith("multipart/form-data"): diff --git a/tornado/test/httpserver_test.py b/tornado/test/httpserver_test.py index 123ae2e7..54b38b46 100644 --- a/tornado/test/httpserver_test.py +++ b/tornado/test/httpserver_test.py @@ -258,6 +258,8 @@ class EchoHandler(RequestHandler): def get(self): self.write(recursive_unicode(self.request.arguments)) + def post(self): + self.write(recursive_unicode(self.request.arguments)) class TypeCheckHandler(RequestHandler): def prepare(self): @@ -316,6 +318,11 @@ class HTTPServerTest(AsyncHTTPTestCase): data = json_decode(response.body) self.assertEqual(data, {u("foo"): [u(""), u("")]}) + def test_empty_post_parameters(self): + response = self.fetch("/echo", method="POST", body="foo=&bar=") + data = json_decode(response.body) + self.assertEqual(data, {u("foo"): [u("")], u("bar"): [u("")]}) + def test_types(self): headers = {"Cookie": "foo=bar"} response = self.fetch("/typecheck?foo=bar", headers=headers)