r47316 - 100% coverage

hawkowl-TA+aISz0psMTMxyoc4vAAJOcrHinNvQL0E9HWUfgJXw@public.gmane.org Mon, 25 Apr 2016 08:10:08 -0600 (MDT)
Newsgroups gmane.comp.python.twisted.commits
Message-ID <[email protected]>
Author: hawkowl
Date: Mon Apr 25 08:09:57 2016
New Revision: 47316

Modified:
   branches/proper-upgrade-8301-2/twisted/web/http.py
   branches/proper-upgrade-8301-2/twisted/web/server.py
   branches/proper-upgrade-8301-2/twisted/web/test/test_http.py
   branches/proper-upgrade-8301-2/twisted/web/test/test_web.py

Log:
100% coverage

Modified: branches/proper-upgrade-8301-2/twisted/web/http.py
==============================================================================
--- branches/proper-upgrade-8301-2/twisted/web/http.py	(original)
+++ branches/proper-upgrade-8301-2/twisted/web/http.py	Mon Apr 25 08:09:57 2016
@@ -91,7 +91,7 @@
 # twisted imports
 from twisted import copyright
 from twisted.python.compat import (
-    _PY3, unicode, intToBytes, networkString, nativeString, iterbytes)
+    _PY3, unicode, intToBytes, networkString, nativeString)
 from twisted.python.deprecate import deprecated
 from twisted.python import log
 from twisted.python.versions import Version
@@ -2104,6 +2104,7 @@
 
         if requestLine[2].lower() == b"http/1.0":
             # It's HTTP/1.0, return that.
+            self._replay = True
             return b"http/1.0"
 
         if not requestLine[2].lower() == b"http/1.1":
@@ -2123,6 +2124,7 @@
 
         if b"upgrade" not in headers:
             # Regular HTTP/1.1, no "Upgrade"
+            self._replay = True
             return b"http/1.1"
 
         for upgrade in headers[b"upgrade"].split(b","):
@@ -2148,6 +2150,7 @@
                     pass
 
         # Negotiation failed!
+        self._replay = True
         return b"http/1.1"
 
 
@@ -2166,8 +2169,8 @@
             self._negotiatedProtocol = self._upgrade()
             self._buffering = False
 
-            if self._negotiatedProtocol in [b"http/1.1", b"http/1.0"]:
-                res = self.dataReceived(self._buffer)
+            if self._replay:
+                self.dataReceived(self._buffer)
 
             del self._buffer
 
@@ -2181,7 +2184,6 @@
             return self._bufferData(data)
 
         elif self._negotiatedProtocol is None:
-
             try:
                 # Does ALPN/NPN/some other transport negotiation have the
                 # protocol the client desires negotiated?
@@ -2195,7 +2197,6 @@
                     self._channel.transport)
 
             elif negotiatedProtocol in [b"http/1.1", None]:
-
                 if getattr(self.factory, "_upgradeables"):
                     # We can upgrade to different protocols through HTTP/1.1
                     # Upgrade, so we need to check the request to see if it

Modified: branches/proper-upgrade-8301-2/twisted/web/server.py
==============================================================================
--- branches/proper-upgrade-8301-2/twisted/web/server.py	(original)
+++ branches/proper-upgrade-8301-2/twisted/web/server.py	Mon Apr 25 08:09:57 2016
@@ -37,7 +37,7 @@
 from twisted.web.http import unquote, _version as version
 from twisted.python import log, reflect, failure, components
 from twisted.web import resource
-from twisted.web.error import UnsupportedMethod, CannotUpgrade
+from twisted.web.error import UnsupportedMethod
 
 from twisted.python.versions import Version
 from twisted.python.deprecate import deprecatedModuleAttribute

Modified: branches/proper-upgrade-8301-2/twisted/web/test/test_http.py
==============================================================================
--- branches/proper-upgrade-8301-2/twisted/web/test/test_http.py	(original)
+++ branches/proper-upgrade-8301-2/twisted/web/test/test_http.py	Mon Apr 25 08:09:57 2016
@@ -8,7 +8,6 @@
 from __future__ import absolute_import, division
 
 import random, cgi, base64
-import math
 
 try:
     from urlparse import urlparse, urlunsplit, clear_cache
@@ -22,8 +21,9 @@
 from twisted.trial.unittest import TestCase
 from twisted.web import http, http_headers
 from twisted.web.http import (
-    _respondToUpgrade, HTTPFactory, PotentialDataLoss, _DataLoss, _version,
+    HTTPFactory, PotentialDataLoss, _DataLoss, _version,
     _IdentityTransferDecoder)
+from twisted.web.error import CannotUpgrade
 from twisted.internet.protocol import Protocol, Factory
 from twisted.internet.task import Clock
 from twisted.internet.error import ConnectionLost
@@ -2537,6 +2537,7 @@
                          sub(["category", "message"], warnings[0]))
 
 
+
 class Pitocol(Protocol):
     """
     A protocol that writes pi to the transport.
@@ -2546,8 +2547,6 @@
         A C{dataReceived} that expects "GO" and will then write out
         "3.14" * C{piTimes}. If there's any other data, that won't
         """
-        if not protoself.connected:
-            self.fail("dataReceived called when disconnected!")
         if data == b"GO":
             for i in range(piTimes):
                 protoself.transport.write(b"3.14")
@@ -2571,6 +2570,8 @@
         """
         Make a testing suitable L{HTTPFactory} which doesn't rely on a running
         reactor.
+
+        @return: A L{HTTPFactory}.
         """
         factory = HTTPFactory()
         factory._logDateTime = "sometime"
@@ -2585,7 +2586,6 @@
         and an "Upgrade" header that lists a protocol we support will be
         upgraded to that protocol.
         """
-
         class PiUpgrader(object):
 
             def upgrade(self, verb, path, headers):
@@ -2623,6 +2623,52 @@
         self.assertTrue(trans.disconnecting)
 
 
+    def test_upgradeWithReplay(self):
+        """
+        A HTTP/1.1 request with a "Connection" header that contains "Upgrade"
+        and an "Upgrade" header that lists a protocol we support will be
+        upgraded to that protocol. If the upgrader says it wants a replay of
+        the request, it will be replayed.
+        """
+        val = [
+            b"GET / HTTP/1.1\r\n"
+            b"Connection: keep-alive, Upgrade\r\n",
+            b"Upgrade: replay\r\n\r\n",
+        ]
+
+        class Echo(Protocol):
+            """
+            A protocol that echos back to the transport.
+            """
+            def dataReceived(protoself, data):
+                """
+                Echo out the data.
+                """
+                protoself.transport.write(data)
+
+        echoFactory = Factory()
+        echoFactory.protocol = Echo
+        echoFactory.startFactory()
+
+        class ReplayUpgrader(object):
+
+            def upgrade(self, verb, path, headers):
+                echo = echoFactory.buildProtocol(None)
+                return echo, True, {}
+
+        factory = self._makeFactory()
+        factory._addUpgrader(b"replay", ReplayUpgrader())
+        protocol = factory.buildProtocol(None)
+
+        trans = StringTransport()
+        protocol.makeConnection(trans)
+
+        for x in iterbytes(b"".join(val)):
+            protocol.dataReceived(x)
+
+        self.assertEqual(trans.value(), b"".join(val))
+
+
     def test_upgradeNegotiatedHTTP11(self):
         """
         A ALPN-negotiated HTTP/1.1 protocol needs to be read and checked for
@@ -2716,8 +2762,8 @@
 
     def test_mangledStatusLine(self):
         """
-        A non-HTTP first-line (or a mangled one) will return with a
-        "bad request" error.
+        A non-HTTP first-line (or a mangled one) will return with a "bad
+        request" error.
         """
         factory = self._makeFactory()
         factory._addUpgrader(b"unused", None)
@@ -2737,9 +2783,10 @@
         self.assertEqual(trans.value(), expectedValue)
 
 
-    def test_regularRequest(self):
+    def test_regularRequestHTTP11(self):
         """
-        A regular HTTP/1.1 request (that does not want to upgrade) will be passed right through.
+        A regular HTTP/1.1 request (that does not want to upgrade) will be
+        passed right through.
         """
         factory = self._makeFactory()
         factory._addUpgrader(b"unused", None)
@@ -2749,11 +2796,93 @@
         protocol.makeConnection(trans)
 
         val = [
-            b"GET/ HTTP/1.1\r\n\r\n",
+            b"GET / HTTP/1.1\r\n"
+            b"Expect: 100-continue\r\n\r\n",
         ]
 
         for x in iterbytes(b"".join(val)):
             protocol.dataReceived(x)
 
-        expectedValue = b"HTTP/1.1 400 Bad Request\r\n\r\n"
+        expectedValue = b"HTTP/1.1 100 Continue\r\n\r\n"
+        self.assertEqual(trans.value(), expectedValue)
+
+
+    def test_regularRequestHTTP10(self):
+        """
+        A regular HTTP/1.0 request (that does not want to upgrade) will be
+        passed right through.
+        """
+        factory = self._makeFactory()
+        factory._addUpgrader(b"unused", None)
+        protocol = factory.buildProtocol(None)
+
+        trans = StringTransport()
+        protocol.makeConnection(trans)
+
+        val = [
+            b"GET / HTTP/1.0\r\n\r\n",
+        ]
+
+        for x in iterbytes(b"".join(val)):
+            protocol.dataReceived(x)
+
+        expectedValue = b""
+        self.assertEqual(trans.value(), expectedValue)
+
+
+    def test_noAvailableUpgrader(self):
+        """
+        An upgrade request that cannot be served with an upgrade (because we
+        don't have it) will continue on as normal without upgrading, per the
+        RFC.
+        """
+        factory = self._makeFactory()
+        factory._addUpgrader(b"someotherprotocol", None)
+        protocol = factory.buildProtocol(None)
+
+        trans = StringTransport()
+        protocol.makeConnection(trans)
+
+        val = [
+            b"GET / HTTP/1.1\r\n"
+            b"Connection: Upgrade\r\n",
+            b"Upgrade: beepboopprotocol\r\n",
+            b"Expect: 100-continue\r\n\r\n",
+        ]
+
+        for x in iterbytes(b"".join(val)):
+            protocol.dataReceived(x)
+
+        expectedValue = b"HTTP/1.1 100 Continue\r\n\r\n"
+        self.assertEqual(trans.value(), expectedValue)
+
+
+    def test_cannotUpgrade(self):
+        """
+        An upgrade request that cannot be served with an upgrade (because the
+        upgrader fails safely) will continue on as normal without upgrading,
+        per the RFC.
+        """
+        class FailingUpgrade(object):
+            def upgrade(self, *args, **kwargs):
+                raise CannotUpgrade()
+
+        factory = self._makeFactory()
+        factory._addUpgrader(b"failing", FailingUpgrade())
+        protocol = factory.buildProtocol(None)
+
+        trans = StringTransport()
+        protocol.makeConnection(trans)
+
+        val = [
+            b"GET / HTTP/1.1\r\n"
+            b"Connection: Upgrade\r\n",
+            b"Upgrade: failing\r\n",
+            b"Expect: 100-continue\r\n\r\n",
+        ]
+
+        for x in iterbytes(b"".join(val)):
+            protocol.dataReceived(x)
+
+        expectedValue = b"HTTP/1.1 100 Continue\r\n\r\n"
         self.assertEqual(trans.value(), expectedValue)

Modified: branches/proper-upgrade-8301-2/twisted/web/test/test_web.py
==============================================================================
--- branches/proper-upgrade-8301-2/twisted/web/test/test_web.py	(original)
+++ branches/proper-upgrade-8301-2/twisted/web/test/test_web.py	Mon Apr 25 08:09:57 2016
@@ -298,7 +298,7 @@
         self.channel = self.site.buildProtocol(None)
         self.transport = http.StringTransport()
         self.transport.close = lambda *a, **kw: None
-        self.transport.disconnecting = 0
+        self.transport.disconnecting = lambda *a, **kw: 0
         self.transport.getPeer = lambda *a, **kw: "peer"
         self.transport.getHost = lambda *a, **kw: "host"
         self.channel.makeConnection(self.transport)
@@ -319,7 +319,7 @@
             validator = b"If-Modified-Since: " + modifiedSince
         else:
             validator = b"If-Not-Match: " + etag
-        for line in [b"GET / HTTP/1.1", validator, b"\r\n"]:
+        for line in [b"GET / HTTP/1.1", validator, b""]:
             self.channel.dataReceived(line + b'\r\n')
         result = self.transport.getvalue()
         self.assertEqual(httpCode(result), http.OK)