From: Ben Darnell Date: Wed, 6 Nov 2013 19:16:01 +0000 (-0500) Subject: Catch exceptions from parse_qs_bytes in POST bodies and log them. X-Git-Tag: v3.2.0b1~40 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=463d9ecda872f9df9644920103fcbfa4b6aef06f;p=thirdparty%2Ftornado.git Catch exceptions from parse_qs_bytes in POST bodies and log them. This is consistent with error handling for multipart/form-data. parse_qs is very liberal, but the conversion to unicode on python 3 may fail. This is most common for binary data sent from clients where x-www-form-urlencoded is the default, which is not actually intended to be interpreted as arguments. Closes #921. --- diff --git a/tornado/httputil.py b/tornado/httputil.py index 3e7337d99..2575bc568 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -320,7 +320,11 @@ def parse_body_arguments(content_type, body, arguments, files): with the parsed contents. """ if content_type.startswith("application/x-www-form-urlencoded"): - uri_arguments = parse_qs_bytes(native_str(body), keep_blank_values=True) + try: + uri_arguments = parse_qs_bytes(native_str(body), keep_blank_values=True) + except Exception as e: + gen_log.warning('Invalid x-www-form-urlencoded body: %s', e) + uri_arguments = {} for name, values in uri_arguments.items(): if values: arguments.setdefault(name, []).extend(values) diff --git a/tornado/test/httpserver_test.py b/tornado/test/httpserver_test.py index 1d45ca604..47607d4bc 100644 --- a/tornado/test/httpserver_test.py +++ b/tornado/test/httpserver_test.py @@ -344,6 +344,21 @@ class HTTPServerTest(AsyncHTTPTestCase): self.assertEqual(200, response.code) self.assertEqual(json_decode(response.body), {}) + def test_malformed_body(self): + # parse_qs is pretty forgiving, but it will fail on python 3 + # if the data is not utf8. On python 2 parse_qs will work, + # but then the recursive_unicode call in EchoHandler will + # fail. + if str is bytes_type: + return + with ExpectLog(gen_log, 'Invalid x-www-form-urlencoded body'): + response = self.fetch( + '/echo', method="POST", + headers={'Content-Type': 'application/x-www-form-urlencoded'}, + body=b'\xe9') + self.assertEqual(200, response.code) + self.assertEqual(b'{}', response.body) + class HTTPServerRawTest(AsyncHTTPTestCase): def get_app(self):