diff --git a/tornado/curl_httpclient.py b/tornado/curl_httpclient.py index 6dadedd9fe..664cb492c0 100644 --- a/tornado/curl_httpclient.py +++ b/tornado/curl_httpclient.py @@ -462,7 +462,7 @@ def ioctl(cmd): request.prepare_curl_callback(curl) def _curl_header_callback(self, headers, header_callback, header_line): - header_line = native_str(header_line) + header_line = native_str(header_line.decode('latin1')) if header_callback is not None: self.io_loop.add_callback(header_callback, header_line) # header_line as returned by curl includes the end-of-line characters. diff --git a/tornado/test/httpclient_test.py b/tornado/test/httpclient_test.py index 9e8e62c6f9..8c9a99d9f5 100644 --- a/tornado/test/httpclient_test.py +++ b/tornado/test/httpclient_test.py @@ -12,7 +12,7 @@ import datetime from io import BytesIO -from tornado.escape import utf8 +from tornado.escape import utf8, native_str from tornado import gen from tornado.httpclient import HTTPRequest, HTTPResponse, _RequestProxy, HTTPError, HTTPClient from tornado.httpserver import HTTPServer @@ -113,6 +113,15 @@ def method(self): get = post = put = delete = options = patch = other = method + +class SetHeaderHandler(RequestHandler): + def get(self): + # Use get_arguments for keys to get strings, but + # request.arguments for values to get bytes. + for k, v in zip(self.get_arguments('k'), + self.request.arguments['v']): + self.set_header(k, v) + # These tests end up getting run redundantly: once here with the default # HTTPClient implementation, and then again in each implementation's own # test suite. @@ -133,6 +142,7 @@ def get_app(self): url("/304_with_content_length", ContentLength304Handler), url("/all_methods", AllMethodsHandler), url('/patch', PatchHandler), + url('/set_header', SetHeaderHandler), ], gzip=True) def test_patch_receives_payload(self): @@ -520,6 +530,12 @@ def test_put_307(self): response.rethrow() self.assertEqual(response.body, b"Put body: hello") + def test_non_ascii_header(self): + # Non-ascii headers are sent as latin1. + response = self.fetch("/set_header?k=foo&v=%E9") + response.rethrow() + self.assertEqual(response.headers["Foo"], native_str(u"\u00e9")) + class RequestProxyTest(unittest.TestCase): def test_request_set(self):