From: Tatiana Al-Chueyr Date: Tue, 1 Jul 2014 17:39:14 +0000 (-0300) Subject: Improve curl_httpclient so it supports custom methods with payload. X-Git-Tag: v4.1.0b1~123^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c957a5d453594155f8ad4fde35a8093619424304;p=thirdparty%2Ftornado.git Improve curl_httpclient so it supports custom methods with payload. Before this, no custom method (e.g. PATCH) added body to the CURL request. --- diff --git a/tornado/curl_httpclient.py b/tornado/curl_httpclient.py index ae4471fdb..294027784 100644 --- a/tornado/curl_httpclient.py +++ b/tornado/curl_httpclient.py @@ -390,7 +390,10 @@ def _curl_setup_request(curl, request, buffer, headers): raise KeyError('unknown method ' + request.method) # Handle curl's cryptic options for every individual HTTP method - if request.method in ("POST", "PUT"): + if request.method == "GET": + if request.body is not None: + raise AssertionError('Body must be empty for GET request') + elif request.method in ("POST", "PUT") or request.body: if request.body is None: raise AssertionError( 'Body must not be empty for "%s" request' @@ -405,10 +408,8 @@ def _curl_setup_request(curl, request, buffer, headers): curl.setopt(pycurl.IOCTLFUNCTION, ioctl) curl.setopt(pycurl.POSTFIELDSIZE, len(request.body)) else: + curl.setopt(pycurl.UPLOAD, True) curl.setopt(pycurl.INFILESIZE, len(request.body)) - elif request.method == "GET": - if request.body is not None: - raise AssertionError('Body must be empty for GET request') 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 3873cf1e3..5330ca387 100644 --- a/tornado/test/curl_httpclient_test.py +++ b/tornado/test/curl_httpclient_test.py @@ -1,5 +1,6 @@ from __future__ import absolute_import, division, print_function, with_statement +import json from hashlib import md5 from tornado.escape import utf8 @@ -8,7 +9,8 @@ from tornado.stack_context import ExceptionStackContext from tornado.testing import AsyncHTTPTestCase from tornado.test import httpclient_test from tornado.test.util import unittest -from tornado.web import Application, RequestHandler +from tornado.web import Application, RequestHandler, URLSpec + try: import pycurl @@ -120,3 +122,86 @@ class CurlHTTPClientTestCase(AsyncHTTPTestCase): def test_fail_custom_reason(self): response = self.fetch('/custom_fail_reason') self.assertEqual(str(response.error), "HTTP 400: Custom reason") + + +class ContactListHandler(RequestHandler): + + contact_list = { + "someone": { + "telephone": "1-800-247-9792", + "email": "some@email.com" + }, + "another": { + "telephone": "1-104-132-7713", + "email": "other@email.com" + } + } + + def get(self, contact_name): + data = self.contact_list.get(contact_name) + self.write(data) + + def patch(self, contact_name): + """ + Patch implementation according to RFC-6902 + http://tools.ietf.org/html/rfc6902 + """ + patch_data = json.loads(self.request.body) + for patch_item in patch_data: + if patch_item["op"] == "replace": + attribute = patch_item["path"] + value = patch_item["value"] + self.contact_list[contact_name][attribute] = value + + +@unittest.skipIf(pycurl is None, "pycurl module not present") +class NonStandardMethodCurlHTTPClientTestCase(AsyncHTTPTestCase): + + def setUp(self): + super(NonStandardMethodCurlHTTPClientTestCase, self).setUp() + self.http_client = CurlAsyncHTTPClient(self.io_loop, + defaults=dict(allow_ipv6=False)) + + def get_app(self): + return Application([ + ('/(?P[\w\-]+)', ContactListHandler), + ]) + + def fetch(self, path, body=None, **kwargs): + kwargs['url'] = self.get_url(path) + request = HTTPRequest(**kwargs) + if body is not None: + request.body = body + request.allow_nonstandard_methods = True + self.http_client.fetch(request, self.stop, method=None) + return self.wait() + + def test_get(self): + response = self.fetch("/someone", method='GET') + self.assertEqual(response.code, 200) + computed_body = json.loads(response.body) + expected_body = { + "telephone": "1-800-247-9792", + "email": "some@email.com" + } + self.assertEqual(computed_body, expected_body) + + def test_patch_with_payload(self): + patch_list = [ + { + "op": "replace", + "path": "telephone", + "value": "55-21-99756-1934" + } + ] + body = json.dumps(patch_list) + response_patch = self.fetch("/someone", method='PATCH', body=body) + self.assertEqual(response_patch.code, 200) + + response_get = self.fetch("/someone", method="GET") + computed_body = json.loads(response_get.body) + expected_body = { + "telephone": "55-21-99756-1934", + "email": "some@email.com" + } + self.assertEqual(computed_body, expected_body)