r47342 - apply patch from lukasa, refs #8317

hawkowl-TA+aISz0psMTMxyoc4vAAJOcrHinNvQL0E9HWUfgJXw@public.gmane.org Wed, 4 May 2016 05:58:51 -0600 (MDT)
Newsgroups gmane.comp.python.twisted.commits
Message-ID <[email protected]>
Author: hawkowl
Date: Wed May  4 05:58:45 2016
New Revision: 47342

Added:
   branches/why-stop-a-request-when-you-can-keep-writing-8317/twisted/web/topfiles/8317.bugfix
Modified:
   branches/why-stop-a-request-when-you-can-keep-writing-8317/twisted/web/http.py
   branches/why-stop-a-request-when-you-can-keep-writing-8317/twisted/web/test/test_http.py

Log:
apply patch from lukasa, refs #8317

Modified: branches/why-stop-a-request-when-you-can-keep-writing-8317/twisted/web/http.py
==============================================================================
--- branches/why-stop-a-request-when-you-can-keep-writing-8317/twisted/web/http.py	(original)
+++ branches/why-stop-a-request-when-you-can-keep-writing-8317/twisted/web/http.py	Wed May  4 05:58:45 2016
@@ -1681,7 +1681,11 @@
         elif line == b'':
             # End of headers.
             if self.__header:
-                self.headerReceived(self.__header)
+                ok = self.headerReceived(self.__header)
+                # If the last header we got is invalid, we MUST NOT proceed
+                # with processing. We'll have sent a 400 anyway, so just stop.
+                if not ok:
+                    return
             self.__header = ''
             self.allHeadersReceived()
             if self.length == 0:
@@ -1713,12 +1717,15 @@
         @type line: C{bytes}
         @param line: A line from the header section of a request, excluding the
             line delimiter.
+
+        @return: A flag indicating whether the header was valid.
+        @rtype: L{bool}
         """
         try:
             header, data = line.split(b':', 1)
         except ValueError:
             _respondToBadRequestAndDisconnect(self.transport)
-            return
+            return False
 
         header = header.lower()
         data = data.strip()
@@ -1728,7 +1735,7 @@
             except ValueError:
                 _respondToBadRequestAndDisconnect(self.transport)
                 self.length = None
-                return
+                return False
             self._transferDecoder = _IdentityTransferDecoder(
                 self.length, self.requests[-1].handleContentChunk, self._finishRequestBody)
         elif header == b'transfer-encoding' and data.lower() == b'chunked':
@@ -1747,7 +1754,9 @@
         self._receivedHeaderCount += 1
         if self._receivedHeaderCount > self.maxHeaders:
             _respondToBadRequestAndDisconnect(self.transport)
-            return
+            return False
+
+        return True
 
 
     def allContentReceived(self):

Modified: branches/why-stop-a-request-when-you-can-keep-writing-8317/twisted/web/test/test_http.py
==============================================================================
--- branches/why-stop-a-request-when-you-can-keep-writing-8317/twisted/web/test/test_http.py	(original)
+++ branches/why-stop-a-request-when-you-can-keep-writing-8317/twisted/web/test/test_http.py	Wed May  4 05:58:45 2016
@@ -812,12 +812,19 @@
         When client sends invalid HTTP method containing
         non-ascii characters HTTP 400 'Bad Request' status will be returned.
         """
+        processed = []
+        class MyRequest(http.Request):
+            def process(self):
+                processed.append(self)
+                self.finish()
+
         badRequestLine = b"GE\xc2\xa9 / HTTP/1.1\r\n\r\n"
-        channel = self.runRequest(badRequestLine, http.Request, 0)
+        channel = self.runRequest(badRequestLine, MyRequest, 0)
         self.assertEqual(
             channel.transport.value(),
             b"HTTP/1.1 400 Bad Request\r\n\r\n")
         self.assertTrue(channel.transport.disconnecting)
+        self.assertEqual(processed, [])
 
 
     def test_basicAuth(self):
@@ -895,12 +902,19 @@
         (Bad Request) response is sent to the client and the connection is
         closed.
         """
+        processed = []
+        class MyRequest(http.Request):
+            def process(self):
+                processed.append(self)
+                self.finish()
+
         requestLines = [b"GET / HTTP/1.0", b"Content-Length: x", b"", b""]
-        channel = self.runRequest(b"\n".join(requestLines), http.Request, 0)
+        channel = self.runRequest(b"\n".join(requestLines), MyRequest, 0)
         self.assertEqual(
             channel.transport.value(),
             b"HTTP/1.1 400 Bad Request\r\n\r\n")
         self.assertTrue(channel.transport.disconnecting)
+        self.assertEqual(processed, [])
 
 
     def test_invalidHeaderNoColon(self):
@@ -908,12 +922,19 @@
         If a header without colon is received a 400 (Bad Request) response
         is sent to the client and the connection is closed.
         """
+        processed = []
+        class MyRequest(http.Request):
+            def process(self):
+                processed.append(self)
+                self.finish()
+
         requestLines = [b"GET / HTTP/1.0", b"HeaderName ", b"", b""]
-        channel = self.runRequest(b"\n".join(requestLines), http.Request, 0)
+        channel = self.runRequest(b"\n".join(requestLines), MyRequest, 0)
         self.assertEqual(
             channel.transport.value(),
             b"HTTP/1.1 400 Bad Request\r\n\r\n")
         self.assertTrue(channel.transport.disconnecting)
+        self.assertEqual(processed, [])
 
 
     def test_headerLimitPerRequest(self):
@@ -963,13 +984,24 @@
         on the size of headers received per request starting from initial
         command line.
         """
+        processed = []
+        class MyRequest(http.Request):
+            def process(self):
+                processed.append(self)
+                self.finish()
+
         channel = http.HTTPChannel()
         channel.totalHeadersSize = 10
         httpRequest = b'GET /path/longer/than/10 HTTP/1.1\n'
 
         channel = self.runRequest(
-            httpRequest=httpRequest, channel=channel, success=False)
+            httpRequest=httpRequest,
+            requestFactory=MyRequest,
+            channel=channel,
+            success=False
+        )
 
+        self.assertEqual(processed, [])
         self.assertEqual(
             channel.transport.value(),
             b"HTTP/1.1 400 Bad Request\r\n\r\n")
@@ -981,6 +1013,12 @@
         on the size of headers received per request counting first line
         and total headers.
         """
+        processed = []
+        class MyRequest(http.Request):
+            def process(self):
+                processed.append(self)
+                self.finish()
+
         channel = http.HTTPChannel()
         channel.totalHeadersSize = 40
         httpRequest = (
@@ -989,8 +1027,12 @@
             )
 
         channel = self.runRequest(
-            httpRequest=httpRequest, channel=channel, success=False)
+            httpRequest=httpRequest,
+            requestFactory=MyRequest,
+            channel=channel, success=False
+        )
 
+        self.assertEqual(processed, [])
         self.assertEqual(
             channel.transport.value(),
             b"HTTP/1.1 400 Bad Request\r\n\r\n")