summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert JW Regeer <bertjw@regeer.org>2019-12-12 13:33:41 -0800
committerBert JW Regeer <bertjw@regeer.org>2019-12-19 15:59:57 +0100
commit7009d1bd03866ad7ffce6a9c64182ff8b1906b32 (patch)
tree9e383a30d611723b227c6357de2dc5e1774a5859
parent1809765a65076844b67a122b4a573bcba36e2dcd (diff)
downloadwaitress-7009d1bd03866ad7ffce6a9c64182ff8b1906b32.tar.gz
Upon processing errors, set Connection: close header
When we start sending out an exception that has happened we want to drop any further pipelined requests that may be waiting. The way we do this is by slamming the connection shut after we send the response to the received request that has errored. This avoids potential issues with attempting to parse the rest of the data that is in the TCP/IP connection that may contain invalid data.
-rw-r--r--waitress/task.py12
-rw-r--r--waitress/tests/test_functional.py6
-rw-r--r--waitress/tests/test_task.py36
3 files changed, 26 insertions, 28 deletions
diff --git a/waitress/task.py b/waitress/task.py
index c12b0f9..8e7ab18 100644
--- a/waitress/task.py
+++ b/waitress/task.py
@@ -353,14 +353,10 @@ class ErrorTask(Task):
status, headers, body = e.to_response()
self.status = status
self.response_headers.extend(headers)
- if self.version == "1.1":
- connection = self.request.headers.get("CONNECTION", "").lower()
- if connection == "close":
- self.response_headers.append(("Connection", "close"))
- # under HTTP 1.1 keep-alive is default, no need to set the header
- else:
- # HTTP 1.0
- self.response_headers.append(("Connection", "close"))
+ # We need to explicitly tell the remote client we are closing the
+ # connection, because self.close_on_finish is set, and we are going to
+ # slam the door in the clients face.
+ self.response_headers.append(("Connection", "close"))
self.close_on_finish = True
self.content_length = len(body)
self.write(tobytes(body))
diff --git a/waitress/tests/test_functional.py b/waitress/tests/test_functional.py
index bfe5072..a278d54 100644
--- a/waitress/tests/test_functional.py
+++ b/waitress/tests/test_functional.py
@@ -336,7 +336,7 @@ class EchoTests(object):
cl = int(headers["content-length"])
self.assertEqual(cl, len(response_body))
self.assertEqual(
- sorted(headers.keys()), ["content-length", "content-type", "date", "server"]
+ sorted(headers.keys()), ["connection", "content-length", "content-type", "date", "server"]
)
self.assertEqual(headers["content-type"], "text/plain")
# connection has been closed
@@ -1069,7 +1069,7 @@ class InternalServerErrorTests(object):
self.assertEqual(cl, len(response_body))
self.assertTrue(response_body.startswith(b"Internal Server Error"))
self.assertEqual(
- sorted(headers.keys()), ["content-length", "content-type", "date", "server"]
+ sorted(headers.keys()), ["connection", "content-length", "content-type", "date", "server"]
)
# connection has been closed
self.send_check_error(to_send)
@@ -1128,7 +1128,7 @@ class InternalServerErrorTests(object):
self.assertEqual(cl, len(response_body))
self.assertTrue(response_body.startswith(b"Internal Server Error"))
self.assertEqual(
- sorted(headers.keys()), ["content-length", "content-type", "date", "server"]
+ sorted(headers.keys()), ["connection", "content-length", "content-type", "date", "server"]
)
# connection has been closed
self.send_check_error(to_send)
diff --git a/waitress/tests/test_task.py b/waitress/tests/test_task.py
index 584add1..1a86245 100644
--- a/waitress/tests/test_task.py
+++ b/waitress/tests/test_task.py
@@ -876,15 +876,16 @@ class TestErrorTask(unittest.TestCase):
inst.version = "1.1"
inst.execute()
lines = filter_lines(inst.channel.written)
- self.assertEqual(len(lines), 8)
+ self.assertEqual(len(lines), 9)
self.assertEqual(lines[0], b"HTTP/1.1 432 Too Ugly")
- self.assertEqual(lines[1], b"Content-Length: 43")
- self.assertEqual(lines[2], b"Content-Type: text/plain")
- self.assertTrue(lines[3])
- self.assertEqual(lines[4], b"Server: waitress")
- self.assertEqual(lines[5], b"Too Ugly")
- self.assertEqual(lines[6], b"body")
- self.assertEqual(lines[7], b"(generated by waitress)")
+ 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.assertTrue(lines[4])
+ self.assertEqual(lines[5], b"Server: waitress")
+ self.assertEqual(lines[6], b"Too Ugly")
+ self.assertEqual(lines[7], b"body")
+ self.assertEqual(lines[8], b"(generated by waitress)")
def test_execute_http_11_close(self):
inst = self._makeOne()
@@ -903,21 +904,22 @@ class TestErrorTask(unittest.TestCase):
self.assertEqual(lines[7], b"body")
self.assertEqual(lines[8], b"(generated by waitress)")
- def test_execute_http_11_keep(self):
+ def test_execute_http_11_keep_forces_close(self):
inst = self._makeOne()
inst.version = "1.1"
inst.request.headers["CONNECTION"] = "keep-alive"
inst.execute()
lines = filter_lines(inst.channel.written)
- self.assertEqual(len(lines), 8)
+ self.assertEqual(len(lines), 9)
self.assertEqual(lines[0], b"HTTP/1.1 432 Too Ugly")
- self.assertEqual(lines[1], b"Content-Length: 43")
- self.assertEqual(lines[2], b"Content-Type: text/plain")
- self.assertTrue(lines[3])
- self.assertEqual(lines[4], b"Server: waitress")
- self.assertEqual(lines[5], b"Too Ugly")
- self.assertEqual(lines[6], b"body")
- self.assertEqual(lines[7], b"(generated by waitress)")
+ 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.assertTrue(lines[4])
+ self.assertEqual(lines[5], b"Server: waitress")
+ self.assertEqual(lines[6], b"Too Ugly")
+ self.assertEqual(lines[7], b"body")
+ self.assertEqual(lines[8], b"(generated by waitress)")
class DummyTask(object):