Skip to content

Commit

Permalink
Merge pull request #378 from Pylons/bugfix/expose_tracebacks-encode-e…
Browse files Browse the repository at this point in the history
…rror

Bugfix: expose_tracebacks encode error
  • Loading branch information
mmerickel committed Apr 27, 2022
2 parents 603d2c1 + 4467d76 commit 3066fdd
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/waitress/task.py
Expand Up @@ -355,7 +355,7 @@ def execute(self):
self.response_headers.append(("Connection", "close"))
self.close_on_finish = True
self.content_length = len(body)
self.write(body.encode("latin-1"))
self.write(body)


class WSGITask(Task):
Expand Down
4 changes: 2 additions & 2 deletions src/waitress/utilities.py
Expand Up @@ -262,8 +262,8 @@ def to_response(self):
status = "%s %s" % (self.code, self.reason)
body = "%s\r\n\r\n%s" % (self.reason, self.body)
tag = "\r\n\r\n(generated by waitress)"
body = body + tag
headers = [("Content-Type", "text/plain")]
body = (body + tag).encode("utf-8")
headers = [("Content-Type", "text/plain; charset=utf-8")]

return status, headers, body

Expand Down
2 changes: 2 additions & 0 deletions tests/fixtureapps/error_traceback.py
@@ -0,0 +1,2 @@
def app(environ, start_response): # pragma: no cover
raise ValueError("Invalid application: " + chr(8364))
64 changes: 59 additions & 5 deletions tests/test_functional.py
Expand Up @@ -359,7 +359,7 @@ def test_broken_chunked_encoding(self):
sorted(headers.keys()),
["connection", "content-length", "content-type", "date", "server"],
)
self.assertEqual(headers["content-type"], "text/plain")
self.assertEqual(headers["content-type"], "text/plain; charset=utf-8")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
Expand All @@ -381,7 +381,7 @@ def test_broken_chunked_encoding_invalid_hex(self):
sorted(headers.keys()),
["connection", "content-length", "content-type", "date", "server"],
)
self.assertEqual(headers["content-type"], "text/plain")
self.assertEqual(headers["content-type"], "text/plain; charset=utf-8")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
Expand All @@ -403,7 +403,7 @@ def test_broken_chunked_encoding_invalid_extension(self):
sorted(headers.keys()),
["connection", "content-length", "content-type", "date", "server"],
)
self.assertEqual(headers["content-type"], "text/plain")
self.assertEqual(headers["content-type"], "text/plain; charset=utf-8")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
Expand All @@ -428,7 +428,7 @@ def test_broken_chunked_encoding_missing_chunk_end(self):
sorted(headers.keys()),
["connection", "content-length", "content-type", "date", "server"],
)
self.assertEqual(headers["content-type"], "text/plain")
self.assertEqual(headers["content-type"], "text/plain; charset=utf-8")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
Expand Down Expand Up @@ -1121,7 +1121,7 @@ def test_request_body_too_large_chunked_encoding(self):
self.assertline(line, "413", "Request Entity Too Large", "HTTP/1.1")
cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
self.assertEqual(headers["content-type"], "text/plain")
self.assertEqual(headers["content-type"], "text/plain; charset=utf-8")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)
Expand Down Expand Up @@ -1269,6 +1269,49 @@ def test_in_generator(self):
self.assertRaises(ConnectionClosed, read_http, fp)


class InternalServerErrorTestsWithTraceback:
def setUp(self):
from tests.fixtureapps import error_traceback

self.start_subprocess(error_traceback.app, expose_tracebacks=True)

def tearDown(self):
self.stop_subprocess()

def test_expose_tracebacks_http_10(self):
to_send = b"GET / HTTP/1.0\r\n\r\n"
self.connect()
self.sock.send(to_send)
with self.sock.makefile("rb", 0) as fp:
line, headers, response_body = read_http(fp)
self.assertline(line, "500", "Internal Server Error", "HTTP/1.0")
cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
self.assertTrue(response_body.startswith(b"Internal Server Error"))
self.assertEqual(headers["connection"], "close")
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)

def test_expose_tracebacks_http_11(self):
to_send = b"GET / HTTP/1.1\r\n\r\n"
self.connect()
self.sock.send(to_send)
with self.sock.makefile("rb", 0) as fp:
line, headers, response_body = read_http(fp)
self.assertline(line, "500", "Internal Server Error", "HTTP/1.1")
cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
self.assertTrue(response_body.startswith(b"Internal Server Error"))
self.assertEqual(
sorted(headers.keys()),
["connection", "content-length", "content-type", "date", "server"],
)
# connection has been closed
self.send_check_error(to_send)
self.assertRaises(ConnectionClosed, read_http, fp)


class FileWrapperTests:
def setUp(self):
from tests.fixtureapps import filewrapper
Expand Down Expand Up @@ -1538,6 +1581,12 @@ class TcpInternalServerErrorTests(
pass


class TcpInternalServerErrorTestsWithTraceback(
InternalServerErrorTestsWithTraceback, TcpTests, unittest.TestCase
):
pass


class TcpFileWrapperTests(FileWrapperTests, TcpTests, unittest.TestCase):
pass

Expand Down Expand Up @@ -1604,6 +1653,11 @@ class UnixInternalServerErrorTests(
):
pass

class UnixInternalServerErrorTestsWithTraceback(
InternalServerErrorTestsWithTraceback, UnixTests, unittest.TestCase
):
pass

class UnixFileWrapperTests(FileWrapperTests, UnixTests, unittest.TestCase):
pass

Expand Down
4 changes: 2 additions & 2 deletions tests/test_proxy_headers.py
Expand Up @@ -16,7 +16,7 @@ def start_response(status, response_headers):
response.headers = response_headers

response.steps = list(app(environ, start_response))
response.body = b"".join(s.encode("latin-1") for s in response.steps)
response.body = b"".join(s for s in response.steps)
return response

def test_get_environment_values_w_scheme_override_untrusted(self):
Expand Down Expand Up @@ -727,7 +727,7 @@ class DummyApp:
def __call__(self, environ, start_response):
self.environ = environ
start_response("200 OK", [("Content-Type", "text/plain")])
yield "hello"
yield b"hello"


class DummyResponse:
Expand Down
8 changes: 4 additions & 4 deletions tests/test_task.py
Expand Up @@ -869,7 +869,7 @@ def test_execute_http_10(self):
self.assertEqual(lines[0], b"HTTP/1.0 432 Too Ugly")
self.assertEqual(lines[1], b"Connection: close")
self.assertEqual(lines[2], b"Content-Length: 43")
self.assertEqual(lines[3], b"Content-Type: text/plain")
self.assertEqual(lines[3], b"Content-Type: text/plain; charset=utf-8")
self.assertTrue(lines[4])
self.assertEqual(lines[5], b"Server: waitress")
self.assertEqual(lines[6], b"Too Ugly")
Expand All @@ -885,7 +885,7 @@ def test_execute_http_11(self):
self.assertEqual(lines[0], b"HTTP/1.1 432 Too Ugly")
self.assertEqual(lines[1], b"Connection: close")
self.assertEqual(lines[2], b"Content-Length: 43")
self.assertEqual(lines[3], b"Content-Type: text/plain")
self.assertEqual(lines[3], b"Content-Type: text/plain; charset=utf-8")
self.assertTrue(lines[4])
self.assertEqual(lines[5], b"Server: waitress")
self.assertEqual(lines[6], b"Too Ugly")
Expand All @@ -902,7 +902,7 @@ def test_execute_http_11_close(self):
self.assertEqual(lines[0], b"HTTP/1.1 432 Too Ugly")
self.assertEqual(lines[1], b"Connection: close")
self.assertEqual(lines[2], b"Content-Length: 43")
self.assertEqual(lines[3], b"Content-Type: text/plain")
self.assertEqual(lines[3], b"Content-Type: text/plain; charset=utf-8")
self.assertTrue(lines[4])
self.assertEqual(lines[5], b"Server: waitress")
self.assertEqual(lines[6], b"Too Ugly")
Expand All @@ -919,7 +919,7 @@ def test_execute_http_11_keep_forces_close(self):
self.assertEqual(lines[0], b"HTTP/1.1 432 Too Ugly")
self.assertEqual(lines[1], b"Connection: close")
self.assertEqual(lines[2], b"Content-Length: 43")
self.assertEqual(lines[3], b"Content-Type: text/plain")
self.assertEqual(lines[3], b"Content-Type: text/plain; charset=utf-8")
self.assertTrue(lines[4])
self.assertEqual(lines[5], b"Server: waitress")
self.assertEqual(lines[6], b"Too Ugly")
Expand Down

0 comments on commit 3066fdd

Please sign in to comment.