diff --git a/gunicorn/http/wsgi.py b/gunicorn/http/wsgi.py index 2ca242665..b8b56934b 100644 --- a/gunicorn/http/wsgi.py +++ b/gunicorn/http/wsgi.py @@ -317,7 +317,7 @@ def send_headers(self): tosend.extend(["%s: %s\r\n" % (k, v) for k, v in self.headers]) header_str = "%s\r\n" % "".join(tosend) - util.write(self.sock, util.to_bytestring(header_str)) + util.write(self.sock, util.to_latin1(header_str)) self.headers_sent = True def write(self, arg): diff --git a/gunicorn/util.py b/gunicorn/util.py index efe5f3d8d..ad5dd3435 100644 --- a/gunicorn/util.py +++ b/gunicorn/util.py @@ -508,6 +508,15 @@ def to_bytestring(value): return value.encode("utf-8") +def to_latin1(value): + """Converts a string argument to a byte string""" + if isinstance(value, bytes): + return value + if not isinstance(value, text_type): + raise TypeError('%r is not a string' % value) + return value.encode("latin-1") + + def is_fileobject(obj): if not hasattr(obj, "tell") or not hasattr(obj, "fileno"): return False diff --git a/tests/test_http_body.py b/tests/test_http.py similarity index 64% rename from tests/test_http_body.py rename to tests/test_http.py index 151df8dde..1d5e4f197 100644 --- a/tests/test_http_body.py +++ b/tests/test_http.py @@ -1,6 +1,14 @@ +# -*- encoding: utf-8 -*- + import t +from gunicorn import util from gunicorn.http.body import Body +from gunicorn.http.wsgi import Response from gunicorn.six import BytesIO +try: + import unittest.mock as mock +except ImportError: + import mock def assert_readline(payload, size, expected): @@ -57,3 +65,23 @@ def test_readline_buffer_loaded_with_size(): assert body.readline(2) == b"\n" assert body.readline(2) == b"de" assert body.readline(2) == b"f" + + +def test_http_header_encoding(): + """ tests whether http response headers are ISO-8859-1 encoded """ + + mocked_socket = mock.MagicMock() + mocked_socket.sendall = mock.MagicMock() + mocked_request = mock.MagicMock() + response = Response(mocked_request, mocked_socket, None) + + # set umlaut header + response.headers.append(('foo', 'häder')) + response.send_headers() + + # build our own header_str to compare against + tosend = response.default_headers() + tosend.extend(["%s: %s\r\n" % (k, v) for k, v in response.headers]) + header_str = "%s\r\n" % "".join(tosend) + + mocked_socket.sendall.assert_called_with(util.to_latin1(header_str))