diff options
-rw-r--r-- | CHANGES.txt | 1 | ||||
-rw-r--r-- | waitress/channel.py | 5 | ||||
-rw-r--r-- | waitress/tests/test_channel.py | 27 |
3 files changed, 32 insertions, 1 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 1291f41..ef08b18 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,7 @@ Bugfixes socket due to a client disconnect. This should notify a long-lived streaming response when a client hangs up. See https://github.com/Pylons/waitress/pull/238 + and https://github.com/Pylons/waitress/pull/240 - When a client closes a socket unexpectedly there was potential for memory leaks in which data was written to the buffers after they were closed, diff --git a/waitress/channel.py b/waitress/channel.py index 6b80919..a377a54 100644 --- a/waitress/channel.py +++ b/waitress/channel.py @@ -374,7 +374,10 @@ class HTTPChannel(wasyncore.dispatcher, object): except KeyError: pass task = self.error_task_class(self, request) - task.service() # must not fail + try: + task.service() # must not fail + except ClientDisconnected: + task.close_on_finish = True else: task.close_on_finish = True # we cannot allow self.requests to drop to empty til diff --git a/waitress/tests/test_channel.py b/waitress/tests/test_channel.py index d550f87..7efd3b3 100644 --- a/waitress/tests/test_channel.py +++ b/waitress/tests/test_channel.py @@ -585,6 +585,33 @@ class TestHTTPChannel(unittest.TestCase): self.assertEqual(inst.error_task_class.serviced, False) self.assertTrue(request.closed) + def test_service_with_request_error_raises_disconnect(self): + from waitress.channel import ClientDisconnected + + inst, sock, map = self._makeOneWithMap() + inst.adj.expose_tracebacks = False + inst.server = DummyServer() + request = DummyRequest() + err_request = DummyRequest() + inst.requests = [request] + inst.parser_class = lambda x: err_request + inst.task_class = DummyTaskClass(RuntimeError) + inst.task_class.wrote_header = False + inst.error_task_class = DummyTaskClass(ClientDisconnected) + inst.logger = DummyLogger() + inst.service() + self.assertTrue(request.serviced) + self.assertTrue(err_request.serviced) + self.assertEqual(inst.requests, []) + self.assertEqual(len(inst.logger.exceptions), 1) + self.assertEqual(len(inst.logger.warnings), 0) + self.assertTrue(inst.force_flush) + self.assertTrue(inst.last_activity) + self.assertFalse(inst.will_close) + self.assertEqual(inst.task_class.serviced, True) + self.assertEqual(inst.error_task_class.serviced, True) + self.assertTrue(request.closed) + def test_cancel_no_requests(self): inst, sock, map = self._makeOneWithMap() inst.requests = () |