From 8942b5bb8781ae73221f80d027034590813adfca Mon Sep 17 00:00:00 2001 From: Stefan Tjarks Date: Mon, 28 Sep 2015 14:58:15 -0700 Subject: [PATCH] Perform sanity checks on request.body only if allow_nonstandard_methods is True. This makes CurlAsyncHTTPClient handle allow_nonstandard_methods the same as AsyncHTTPClient. --- tornado/curl_httpclient.py | 23 +++++++++++++++-------- tornado/test/curl_httpclient_test.py | 1 + tornado/test/httpclient_test.py | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/tornado/curl_httpclient.py b/tornado/curl_httpclient.py index 9c9481688..baa616ba0 100644 --- a/tornado/curl_httpclient.py +++ b/tornado/curl_httpclient.py @@ -389,15 +389,22 @@ class CurlAsyncHTTPClient(AsyncHTTPClient): # Handle curl's cryptic options for every individual HTTP method if request.method == "GET": + # Even with `allow_nonstandard_methods` we disallow GET with a + # body. While the spec doesn't forbid clients from sending a body, + # it arguably disallows the server from doing anything with them. if request.body is not None: raise ValueError('Body must be None for GET request') - elif request.method in ("POST", "PUT") or request.body: - if request.body is None: + if request.method in ("POST", "PUT") or request.body: + # Fail in case POST or PUT method has no body, unless the user has + # opted out of sanity checks with allow_nonstandard_methods. + if request.body is not None: + request_buffer = BytesIO(utf8(request.body)) + elif not request.allow_nonstandard_methods: raise ValueError( - 'Body must not be None for "%s" request' - % request.method) - - request_buffer = BytesIO(utf8(request.body)) + 'Body must not be None for method %s (unless ' + 'allow_nonstandard_methods is true)' % request.method) + else: + request_buffer = BytesIO() def ioctl(cmd): if cmd == curl.IOCMD_RESTARTREAD: @@ -405,10 +412,10 @@ class CurlAsyncHTTPClient(AsyncHTTPClient): curl.setopt(pycurl.READFUNCTION, request_buffer.read) curl.setopt(pycurl.IOCTLFUNCTION, ioctl) if request.method == "POST": - curl.setopt(pycurl.POSTFIELDSIZE, len(request.body)) + curl.setopt(pycurl.POSTFIELDSIZE, len(request.body or '')) else: curl.setopt(pycurl.UPLOAD, True) - curl.setopt(pycurl.INFILESIZE, len(request.body)) + curl.setopt(pycurl.INFILESIZE, len(request.body or '')) if request.auth_username is not None: userpwd = "%s:%s" % (request.auth_username, request.auth_password or '') diff --git a/tornado/test/curl_httpclient_test.py b/tornado/test/curl_httpclient_test.py index 3ac21f4d7..d06a7bd2a 100644 --- a/tornado/test/curl_httpclient_test.py +++ b/tornado/test/curl_httpclient_test.py @@ -121,3 +121,4 @@ class CurlHTTPClientTestCase(AsyncHTTPTestCase): def test_fail_custom_reason(self): response = self.fetch('/custom_fail_reason') self.assertEqual(str(response.error), "HTTP 400: Custom reason") + diff --git a/tornado/test/httpclient_test.py b/tornado/test/httpclient_test.py index 3c6d4c6b9..dcf6decb1 100644 --- a/tornado/test/httpclient_test.py +++ b/tornado/test/httpclient_test.py @@ -483,6 +483,24 @@ X-XSS-Protection: 1; self.assertTrue('must not be None' in str(context.exception)) + @gen_test + def test_ignore_body_sanity_checks_when_allow_nonstandard_methods(self): + all_methods_url = self.get_url('/all_methods') + for method in ('POST', 'PUT'): + response = yield self.http_client.fetch( + all_methods_url, method=method, body=None, + allow_nonstandard_methods=True) + self.assertEqual(response.code, 200) + self.assertIsNone(response.request.body) + + # Don't test for GET with a body. Curl client does not allow it. + for method in ('PATCH', 'DELETE', 'OPTIONS'): + response = yield self.http_client.fetch( + all_methods_url, method=method, body=utf8(method), + allow_nonstandard_methods=True) + self.assertEqual(response.code, 200) + self.assertEqual(response.body, utf8(method)) + # This test causes odd failures with the combination of # curl_httpclient (at least with the version of libcurl available # on ubuntu 12.04), TwistedIOLoop, and epoll. For POST (but not PUT), -- 2.47.2