diff --git a/tornado/curl_httpclient.py b/tornado/curl_httpclient.py index d51ae350..68047cc9 100644 --- a/tornado/curl_httpclient.py +++ b/tornado/curl_httpclient.py @@ -280,13 +280,9 @@ class CurlAsyncHTTPClient(AsyncHTTPClient): if "Pragma" not in request.headers: request.headers["Pragma"] = "" - # Request headers may be either a regular dict or HTTPHeaders object - if isinstance(request.headers, httputil.HTTPHeaders): - curl.setopt(pycurl.HTTPHEADER, - [native_str("%s: %s" % i) for i in request.headers.get_all()]) - else: - curl.setopt(pycurl.HTTPHEADER, - [native_str("%s: %s" % i) for i in request.headers.items()]) + curl.setopt(pycurl.HTTPHEADER, + ["%s: %s" % (native_str(k), native_str(v)) + for k, v in request.headers.get_all()]) curl.setopt(pycurl.HEADERFUNCTION, functools.partial(self._curl_header_callback, diff --git a/tornado/test/httpclient_test.py b/tornado/test/httpclient_test.py index 36fdeb0c..bfb50d87 100644 --- a/tornado/test/httpclient_test.py +++ b/tornado/test/httpclient_test.py @@ -23,7 +23,7 @@ from tornado.testing import AsyncHTTPTestCase, bind_unused_port, gen_test, Expec from tornado.test.util import unittest, skipOnTravis from tornado.util import u from tornado.web import Application, RequestHandler, url -from tornado.httputil import format_timestamp +from tornado.httputil import format_timestamp, HTTPHeaders class HelloWorldHandler(RequestHandler): @@ -347,6 +347,21 @@ Transfer-Encoding: chunked finally: client.close() + def test_header_types(self): + # Header values may be passed as character or utf8 byte strings, + # in a plain dictionary or an HTTPHeaders object. + # Keys must always be the native str type. + # All combinations should have the same results on the wire. + for value in [u("MyUserAgent"), b"MyUserAgent"]: + for container in [dict, HTTPHeaders]: + headers = container() + headers['User-Agent'] = value + resp = self.fetch('/user_agent', headers=headers) + self.assertEqual( + resp.body, b"MyUserAgent", + "response=%r, value=%r, container=%r" % + (resp.body, value, container)) + def test_304_with_content_length(self): # According to the spec 304 responses SHOULD NOT include # Content-Length or other entity headers, but some servers do it diff --git a/tox.ini b/tox.ini index 7d9197c9..443448ca 100644 --- a/tox.ini +++ b/tox.ini @@ -103,7 +103,7 @@ commands = python \ # py3*: -b turns on an extra warning when calling # str(bytes), and -bb makes it an error. - {py32,py33,py34,pypy3}: -bb \ + {py3,py32,py33,py34,pypy3}: -bb \ # Python's optimized mode disables the assert statement, so # run the tests in this mode to ensure we haven't fallen into # the trap of relying on an assertion's side effects or using