r47304 - Revert r47104: "Merge hide-request-transport-8191-5: Hide the transport backing the HTTPChannel object from twisted.web Resource objects."

hawkowl-TA+aISz0psMTMxyoc4vAAJOcrHinNvQL0E9HWUfgJXw@public.gmane.org Sat, 23 Apr 2016 01:51:48 -0600 (MDT)
Newsgroups gmane.comp.python.twisted.commits
Message-ID <[email protected]>
Author: hawkowl
Date: Sat Apr 23 01:51:33 2016
New Revision: 47304

Removed:
   trunk/twisted/web/topfiles/8191.misc
Modified:
   trunk/twisted/web/http.py
   trunk/twisted/web/server.py
   trunk/twisted/web/test/requesthelper.py
   trunk/twisted/web/test/test_http.py
   trunk/twisted/web/test/test_proxy.py
   trunk/twisted/web/test/test_web.py
   trunk/twisted/web/wsgi.py

Log:
Revert r47104: "Merge hide-request-transport-8191-5: Hide the transport backing the HTTPChannel object from twisted.web Resource objects."

Reopens: #8191

Modified: trunk/twisted/web/http.py
==============================================================================
--- trunk/twisted/web/http.py	(original)
+++ trunk/twisted/web/http.py	Sat Apr 23 01:51:33 2016
@@ -91,7 +91,6 @@
 # twisted imports
 from twisted.python.compat import (
     _PY3, unicode, intToBytes, networkString, nativeString)
-from twisted.python.constants import NamedConstant, Names
 from twisted.python.deprecate import deprecated
 from twisted.python import log
 from twisted.python.versions import Version
@@ -569,16 +568,6 @@
         which this request was received is closed and which is C{True} after
         that.
     @type _disconnected: C{bool}
-
-    @ivar _queuedHeaders: A L{tuple} of items that would normally be passed to
-        L{HTTPChannel.writeHeaders}. Used when the response is queued to store
-        the eventual headers.
-    @type _queuedHeaders: A L{tuple} of HTTP version (:{bytes}), status code
-        (L{int}), reason phrase (L{bytes}), and headers (L{list} of L{tuple}s
-        of C{(bytes, bytes)}.
-
-    @ivar _send100: Whether to send a 100-Continue response when unqueued.
-    @type _send100: L{bool}
     """
     producer = None
     finished = 0
@@ -597,8 +586,6 @@
     content = None
     _forceSSL = 0
     _disconnected = False
-    _queuedHeaders = None
-    _send100 = False
 
     def __init__(self, channel, queued):
         """
@@ -607,7 +594,7 @@
             the transport?
         """
         self.notifications = []
-        self._channel = channel
+        self.channel = channel
         self.queued = queued
         self.requestHeaders = Headers()
         self.received_cookies = {}
@@ -615,59 +602,9 @@
         self.cookies = [] # outgoing cookies
 
         if queued:
-            self._transport = StringTransport()
+            self.transport = StringTransport()
         else:
-            self._transport = self._channel
-
-
-    def __getattr__(self, attr):
-        # This is implemented to deprecate some properties of this object.
-        if attr == 'transport':
-            warnings.warn(
-                "twisted.web.http.Request.transport was deprecated in Twisted "
-                "16.2.0. Call directly into the Request object instead.",
-                category=DeprecationWarning,
-                stacklevel=2
-            )
-            attr = '_transport'
-        elif attr == 'channel':
-            warnings.warn(
-                "twisted.web.http.Request.channel was deprecated in Twisted "
-                "16.2.0. Call directly into the Request object instead.",
-                category=DeprecationWarning,
-                stacklevel=2
-            )
-            attr = '_channel'
-
-        try:
-            return self.__dict__[attr]
-        except KeyError:
-            raise AttributeError(
-                "'%s' object has no attribute '%s'" %
-                (self.__class__.__name__, attr)
-            )
-
-
-    def __setattr__(self, attr, value):
-        # This is implemented to deprecate some properties of this object.
-        if attr == 'transport':
-            warnings.warn(
-                "twisted.web.http.Request.transport was deprecated in Twisted "
-                "16.2.0. Call directly into the Request object instead.",
-                category=DeprecationWarning,
-                stacklevel=2
-            )
-            attr = '_transport'
-        elif attr == 'channel':
-            warnings.warn(
-                "twisted.web.http.Request.channel was deprecated in Twisted "
-                "16.2.0. Call directly into the Request object instead.",
-                category=DeprecationWarning,
-                stacklevel=2
-            )
-            attr = '_channel'
-
-        self.__dict__[attr] = value
+            self.transport = self.channel.transport
 
 
     def _cleanup(self):
@@ -677,8 +614,8 @@
         if self.producer:
             log.err(RuntimeError("Producer was not unregistered for %s" % self.uri))
             self.unregisterProducer()
-        self._channel.requestDone(self)
-        del self._channel
+        self.channel.requestDone(self)
+        del self.channel
         try:
             self.content.close()
         except OSError:
@@ -705,19 +642,14 @@
         self.queued = 0
 
         # set transport to real one and send any buffer data
-        data = self._transport.getvalue()
-        self._transport = self._channel
-        if self._send100:
-            self.channel._send100Continue()
-        if self._queuedHeaders:
-            self._transport.writeHeaders(*self._queuedHeaders)
-            self._queuedHeaders = None
+        data = self.transport.getvalue()
+        self.transport = self.channel.transport
         if data:
-            self._transport.write(data)
+            self.transport.write(data)
 
         # if we have producer, register it with transport
         if (self.producer is not None) and not self.finished:
-            self._transport.registerProducer(self.producer, self.streamingProducer)
+            self.transport.registerProducer(self.producer, self.streamingProducer)
 
         # if we're finished, clean up
         if self.finished:
@@ -801,8 +733,8 @@
 
         # cache the client and server information, we'll need this later to be
         # serialized and sent with the request so CGIs will work remotely
-        self.client = self._channel.getPeer()
-        self.host = self._channel.getHost()
+        self.client = self.channel.transport.getPeer()
+        self.host = self.channel.transport.getHost()
 
         # Argument processing
         args = self.args
@@ -829,7 +761,7 @@
                         self.args.update(cgiArgs)
                 except:
                     # It was a bad request.
-                    self._channel._respondToBadRequestAndDisconnect()
+                    _respondToBadRequestAndDisconnect(self.channel.transport)
                     return
             self.content.seek(0, 0)
 
@@ -879,14 +811,14 @@
             if streaming:
                 producer.pauseProducing()
         else:
-            self._channel.registerProducer(producer, streaming)
+            self.transport.registerProducer(producer, streaming)
 
     def unregisterProducer(self):
         """
         Unregister the producer.
         """
         if not self.queued:
-            self._channel.unregisterProducer()
+            self.transport.unregisterProducer()
         self.producer = None
 
 
@@ -948,11 +880,11 @@
 
         if self.chunked:
             # write last chunk and closing CRLF
-            self._transport.write(b"0\r\n\r\n")
+            self.transport.write(b"0\r\n\r\n")
 
         # log request
-        if hasattr(self._channel, "factory"):
-            self._channel.factory.log(self)
+        if hasattr(self.channel, "factory"):
+            self.channel.factory.log(self)
 
         self.finished = 1
         if not self.queued:
@@ -973,10 +905,11 @@
         if not self.startedWriting:
             self.startedWriting = 1
             version = self.clientproto
-            code = intToBytes(self.code)
-            reason = self.code_message
-
-            headers = []
+            l = []
+            l.append(
+                version + b" " +
+                intToBytes(self.code) + b" " +
+                self.code_message + b"\r\n")
 
             # if we don't have a content length, we send data in
             # chunked mode, so that we can support pipelining in
@@ -984,7 +917,7 @@
             if ((version == b"HTTP/1.1") and
                 (self.responseHeaders.getRawHeaders(b'content-length') is None) and
                 self.method != b"HEAD" and self.code not in NO_BODY_CODES):
-                headers.append((b'Transfer-Encoding', b'chunked'))
+                l.append(b'Transfer-Encoding: chunked\r\n')
                 self.chunked = 1
 
             if self.lastModified is not None:
@@ -1008,15 +941,14 @@
                             category=DeprecationWarning, stacklevel=2)
                         # Backward compatible cast for non-bytes values
                         value = networkString('%s' % (value,))
-                    headers.append((name, value))
+                    l.extend([name, b": ", value, b"\r\n"])
 
             for cookie in self.cookies:
-                headers.append((b'Set-Cookie', cookie))
+                l.append(b'Set-Cookie: ' + cookie + b'\r\n')
 
-            if self.queued:
-                self._queuedHeaders = (version, code, reason, headers)
-            else:
-                self._channel.writeHeaders(version, code, reason, headers)
+            l.append(b"\r\n")
+
+            self.transport.writeSequence(l)
 
             # if this is a "HEAD" request, we shouldn't return any data
             if self.method == b"HEAD":
@@ -1031,9 +963,9 @@
         self.sentLength = self.sentLength + len(data)
         if data:
             if self.chunked:
-                self._transport.writeSequence(toChunk(data))
+                self.transport.writeSequence(toChunk(data))
             else:
-                self._transport.write(data)
+                self.transport.write(data)
 
     def addCookie(self, k, v, expires=None, domain=None, path=None,
                   max_age=None, comment=None, secure=None, httpOnly=False):
@@ -1332,9 +1264,7 @@
         """
         if self._forceSSL:
             return True
-        transport = getattr(
-            getattr(self, '_channel', None), 'transport', None
-        )
+        transport = getattr(getattr(self, 'channel', None), 'transport', None)
         if interfaces.ISSLTransport(transport, None) is not None:
             return True
         return False
@@ -1409,31 +1339,13 @@
         Clean up anything which can't be useful anymore.
         """
         self._disconnected = True
-        self._channel = None
+        self.channel = None
         if self.content is not None:
             self.content.close()
         for d in self.notifications:
             d.errback(reason)
         self.notifications = []
 
-
-    def loseConnection(self):
-        """
-        Pass the loseConnection through to the underlying channel.
-        """
-        self._channel.loseConnection()
-
-
-    def _send100Continue(self):
-        """
-        Sends a "100 Continue" status code, where 100 Continue is supported.
-        """
-        if not self.queued:
-            self._channel._send100Continue()
-        else:
-            self._send100 = True
-
-
 Request.getClient = deprecated(
     Version("Twisted", 15, 0, 0),
     "Twisted Names to resolve hostnames")(Request.getClient)
@@ -1673,25 +1585,6 @@
 
 
 
-class _ChannelSendState(Names):
-    """
-    Defines a collection of states that indicate what portion of a HTTP
-    response has already been sent on L{HTTPChannel}. Used to enforce that
-    callers of methods on the L{HTTPChannel} are calling them at the
-    appropriate times and to prevent callers from sending invalid HTTP
-    responses.
-
-    The state of the channel send state goes from C{IDLE} (the previous
-    response is complete), optionally to C{SENT_100_CONTINUE}, and then to
-    C{SENT_HEADERS} When the response is complete, the state is reset to
-    C{IDLE}.
-    """
-    IDLE = NamedConstant()
-    SENT_100_CONTINUE = NamedConstant()
-    SENT_HEADERS = NamedConstant()
-
-
-
 class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin):
     """
     A receiver for HTTP requests.
@@ -1734,7 +1627,6 @@
         # the request queue
         self.requests = []
         self._transferDecoder = None
-        self._sendState = _ChannelSendState.IDLE
 
 
     def connectionMade(self):
@@ -1750,7 +1642,7 @@
 
         self._receivedHeaderSize += len(line)
         if (self._receivedHeaderSize > self.totalHeadersSize):
-            self._respondToBadRequestAndDisconnect()
+            _respondToBadRequestAndDisconnect(self.transport)
             return
 
         if self.__first_line:
@@ -1774,13 +1666,13 @@
 
             parts = line.split()
             if len(parts) != 3:
-                self._respondToBadRequestAndDisconnect()
+                _respondToBadRequestAndDisconnect(self.transport)
                 return
             command, request, version = parts
             try:
                 command.decode("ascii")
             except UnicodeDecodeError:
-                self._respondToBadRequestAndDisconnect()
+                _respondToBadRequestAndDisconnect(self.transport)
                 return
 
             self._command = command
@@ -1825,7 +1717,7 @@
         try:
             header, data = line.split(b':', 1)
         except ValueError:
-            self._respondToBadRequestAndDisconnect()
+            _respondToBadRequestAndDisconnect(self.transport)
             return
 
         header = header.lower()
@@ -1834,7 +1726,7 @@
             try:
                 self.length = int(data)
             except ValueError:
-                self._respondToBadRequestAndDisconnect()
+                _respondToBadRequestAndDisconnect(self.transport)
                 self.length = None
                 return
             self._transferDecoder = _IdentityTransferDecoder(
@@ -1854,7 +1746,7 @@
 
         self._receivedHeaderCount += 1
         if self._receivedHeaderCount > self.maxHeaders:
-            self._respondToBadRequestAndDisconnect()
+            _respondToBadRequestAndDisconnect(self.transport)
             return
 
 
@@ -1885,7 +1777,7 @@
         try:
             self._transferDecoder.dataReceived(data)
         except _MalformedChunkedDataError:
-            self._respondToBadRequestAndDisconnect()
+            _respondToBadRequestAndDisconnect(self.transport)
 
 
     def allHeadersReceived(self):
@@ -1898,7 +1790,7 @@
         expectContinue = req.requestHeaders.getRawHeaders(b'expect')
         if (expectContinue and expectContinue[0].lower() == b'100-continue' and
             self._version == b'HTTP/1.1'):
-            req._send100Continue()
+            req.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")
 
 
     def checkPersistence(self, request, version):
@@ -1953,8 +1845,6 @@
         del self.requests[0]
 
         if self.persistent:
-            self._sendState = _ChannelSendState.IDLE
-
             # notify next request it can start writing
             if self.requests:
                 self.requests[0].noLongerQueued()
@@ -1974,157 +1864,20 @@
             request.connectionLost(reason)
 
 
-    def writeHeaders(self, version, code, reason, headers):
-        """
-        Called by L{Request} objects to write a complete set of HTTP headers to
-        a transport.
-
-        @param version: The HTTP version in use.
-        @type version: L{bytes}
-
-        @param code: The HTTP status code to write.
-        @type code: L{bytes}
-
-        @param reason: The HTTP reason phrase to write.
-        @type reason: L{bytes}
-
-        @param headers: The headers to write to the transport.
-        @type headers: L{twisted.web.http_headers.Headers}
-        """
-        assert self._sendState in (
-            _ChannelSendState.IDLE, _ChannelSendState.SENT_100_CONTINUE
-        )
-        responseLine = version + b" " + code + b" " + reason + b"\r\n"
-        headerSequence = [responseLine]
-        headerSequence.extend(
-            name + b': ' + value + b"\r\n" for name, value in headers
-        )
-        headerSequence.append(b"\r\n")
-        self.transport.writeSequence(headerSequence)
-        self._sendState = _ChannelSendState.SENT_HEADERS
-
-
-    def registerProducer(self, producer, streaming):
-        """
-        Register to receive data from a producer.
-
-        This sets self to be a consumer for a producer.  When this object runs
-        out of data (as when a send(2) call on a socket succeeds in moving the
-        last data from a userspace buffer into a kernelspace buffer), it will
-        ask the producer to resumeProducing().
-
-        For L{IPullProducer} providers, C{resumeProducing} will be called once
-        each time data is required.
-
-        For L{IPushProducer} providers, C{pauseProducing} will be called
-        whenever the write buffer fills up and C{resumeProducing} will only be
-        called when it empties.
-
-        @type producer: L{IProducer} provider
-        @param producer: The L{IProducer} that will be producing data.
-
-        @type streaming: L{bool}
-        @param streaming: C{True} if C{producer} provides L{IPushProducer},
-        C{False} if C{producer} provides L{IPullProducer}.
-
-        @raise RuntimeError: If a producer is already registered.
-
-        @return: C{None}
-        """
-        return self.transport.registerProducer(producer, streaming)
-
-
-    def unregisterProducer(self):
-        """
-        Stop consuming data from a producer, without disconnecting.
-
-        @return: C{None}
-        """
-        return self.transport.unregisterProducer()
-
-
-    def write(self, data):
-        """
-        Called by L{Request} objects to write response data.
-
-        @param data: The data chunk to write to the stream.
-        @type data: L{bytes}
-
-        @return: C{None}
-        """
-        assert self._sendState == _ChannelSendState.SENT_HEADERS
-        self.transport.write(data)
-
-
-    def writeSequence(self, iovec):
-        """
-        Write a list of strings to the HTTP response.
-
-        @param iovec: A list of byte strings to write to the stream.
-        @type data: L{list} of L{bytes}
-
-        @return: C{None}
-        """
-        assert self._sendState == _ChannelSendState.SENT_HEADERS
-        self.transport.writeSequence(iovec)
-
-
-    def getPeer(self):
-        """
-        Get the remote address of this connection.
-
-        @return: An L{IAddress} provider.
-        """
-        return self.transport.getPeer()
-
-
-    def getHost(self):
-        """
-        Get the local address of this connection.
-
-        @return: An L{IAddress} provider.
-        """
-        return self.transport.getHost()
-
-
-    def loseConnection(self):
-        """
-        Closes the connection. Will write any data that is pending to be sent
-        on the network, but if this response has not yet been written to the
-        network will not write anything.
-
-        @return: C{None}
-        """
-        # TODO: Does this need to be smarter, particularly about queued
-        # responses?
-        return self.transport.loseConnection()
-
-
-    def _send100Continue(self):
-        """
-        Sends a 100 Continue response, used to signal to clients that further
-        processing will be performed.
-        """
-        assert self._sendState == _ChannelSendState.IDLE
-        self.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")
-        self._sendState = _ChannelSendState.SENT_100_CONTINUE
 
+def _respondToBadRequestAndDisconnect(transport):
+    """
+    This is a quick and dirty way of responding to bad requests.
 
-    def _respondToBadRequestAndDisconnect(self):
-        """
-        This is a quick and dirty way of responding to bad requests.
+    As described by HTTP standard we should be patient and accept the
+    whole request from the client before sending a polite bad request
+    response, even in the case when clients send tons of data.
 
-        As described by HTTP standard we should be patient and accept the
-        whole request from the client before sending a polite bad request
-        response, even in the case when clients send tons of data.
-        """
-        # FIXME: There is nothing in this method to ensure that the response it
-        # sends is not intermingled with some other response body being written
-        # at the same time. Clearly this isn't the end of the world: the method
-        # has been arund a little while. Still, this should probably be
-        # refactored.
-        self.transport.write(b"HTTP/1.1 400 Bad Request\r\n\r\n")
-        self.transport.loseConnection()
+    @param transport: Transport handling connection to the client.
+    @type transport: L{interfaces.ITransport}
+    """
+    transport.write(b"HTTP/1.1 400 Bad Request\r\n\r\n")
+    transport.loseConnection()
 
 
 

Modified: trunk/twisted/web/server.py
==============================================================================
--- trunk/twisted/web/server.py	(original)
+++ trunk/twisted/web/server.py	Sat Apr 23 01:51:33 2016
@@ -110,10 +110,10 @@
 
     def getStateToCopyFor(self, issuer):
         x = self.__dict__.copy()
-        del x['_transport']
+        del x['transport']
         # XXX refactor this attribute out; it's from protocol
         # del x['server']
-        del x['_channel']
+        del x['channel']
         del x['content']
         del x['site']
         self.content.seek(0, 0)
@@ -164,7 +164,7 @@
         """
 
         # get site from channel
-        self.site = self._channel.site
+        self.site = self.channel.site
 
         # set various default headers
         self.setHeader(b'server', version)

Modified: trunk/twisted/web/test/requesthelper.py
==============================================================================
--- trunk/twisted/web/test/requesthelper.py	(original)
+++ trunk/twisted/web/test/requesthelper.py	Sat Apr 23 01:51:33 2016
@@ -53,9 +53,6 @@
         def registerProducer(self, producer, streaming):
             self.producers.append((producer, streaming))
 
-        def unregisterProducer(self):
-            pass
-
         def loseConnection(self):
             self.disconnected = True
 
@@ -74,56 +71,6 @@
         pass
 
 
-    def writeHeaders(self, version, code, reason, headers):
-        response_line = version + b" " + code + b" " + reason + b"\r\n"
-        headerSequence = [response_line]
-        headerSequence.extend(
-            name + b': ' + value + b"\r\n" for name, value in headers
-        )
-        headerSequence.append(b"\r\n")
-        self.transport.writeSequence(headerSequence)
-
-
-    def getPeer(self):
-        return self.transport.getPeer()
-
-
-    def getHost(self):
-        return self.transport.getHost()
-
-
-    def registerProducer(self, producer, streaming):
-        self.transport.registerProducer(producer, streaming)
-
-
-    def unregisterProducer(self):
-        self.transport.unregisterProducer()
-
-
-    def write(self, data):
-        self.transport.write(data)
-
-
-    def writeSequence(self, iovec):
-        self.transport.writeSequence(iovec)
-
-
-    def loseConnection(self):
-        self.transport.loseConnection()
-
-
-    def endRequest(self):
-        pass
-
-
-    @property
-    def producers(self):
-        return self.transport.producers
-
-    def _send100Continue(self):
-        self.transport.write(b"HTTP/1.1 100 Continue\r\n\r\n")
-
-
 
 class DummyRequest(object):
     """

Modified: trunk/twisted/web/test/test_http.py
==============================================================================
--- trunk/twisted/web/test/test_http.py	(original)
+++ trunk/twisted/web/test/test_http.py	Sat Apr 23 01:51:33 2016
@@ -338,105 +338,6 @@
 
 
 
-class ResponseWriteOrderingTests(unittest.TestCase):
-    requests = (
-        b"GET / HTTP/1.1\r\n"
-        b"Accept: text/html\r\n"
-        b"\r\n"
-        b"GET / HTTP/1.1\r\n"
-        b"\r\n")
-
-
-    def setUp(self):
-        self.transport = StringTransport()
-        self.channel = http.HTTPChannel()
-        self.channel.requestFactory = http.Request
-        self.channel.makeConnection(self.transport)
-        self.channel.dataReceived(self.requests)
-
-
-    def test_cannotWriteBeforeHeaders(self):
-        """
-        The L{HTTPChannel} forbids writes before headers are sent.
-        """
-        self.assertRaises(
-            AssertionError,
-            self.channel.write,
-            b"some response data",
-        )
-
-
-    def test_cannot100ContinueAfterHeaders(self):
-        """
-        The L{HTTPChannel} forbids sending 100 Continue after headers were
-        sent.
-        """
-        self.channel.writeHeaders(b"HTTP/1.1", b"200", b"OK", [])
-        self.assertRaises(
-            AssertionError,
-            self.channel._send100Continue,
-        )
-
-
-    def test_cannotSendHeadersTwice(self):
-        """
-        The L{HTTPChannel} forbids sending a header block twice.
-        """
-        self.channel.writeHeaders(b"HTTP/1.1", b"200", b"OK", [])
-        self.assertRaises(
-            AssertionError,
-            self.channel.writeHeaders,
-            b"HTTP/1.1",
-            b"200",
-            b"OK",
-            [],
-        )
-
-
-    def test_completingRequestAllows100ContinueAfterHeaders(self):
-        """
-        The L{HTTPChannel} allows a 100 Continue once a request is complete.
-        """
-        self.channel.writeHeaders(b"HTTP/1.1", b"200", b"OK", [])
-        self.channel.requestDone(self.channel.requests[0])
-        self.channel._send100Continue()
-
-        self.assertEqual(
-            self.transport.value(),
-            b"HTTP/1.1 200 OK\r\n\r\nHTTP/1.1 100 Continue\r\n\r\n",
-        )
-
-
-    def test_completingRequestAllowsNewHeaders(self):
-        """
-        The L{HTTPChannel} allows sending a new header block after headers are
-        complete.
-        """
-        self.channel.writeHeaders(b"HTTP/1.1", b"200", b"OK", [])
-        self.channel.requestDone(self.channel.requests[0])
-        self.channel.writeHeaders(b"HTTP/1.1", b"204", b"No Content", [])
-
-        self.assertEqual(
-            self.transport.value(),
-            b"HTTP/1.1 200 OK\r\n\r\nHTTP/1.1 204 No Content\r\n\r\n",
-        )
-
-
-    def test_canWriteAfterHeaders(self):
-        """
-        The L{HTTPChannel} allows writing data after headers.
-        """
-        headers = [(b'Content-Length', b'5')]
-        self.channel.writeHeaders(b"HTTP/1.1", b"200", b"OK", headers)
-        self.channel.write(b'hello')
-
-        self.assertEqual(
-            self.transport.value(),
-            b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello",
-        )
-
-
-
 class HTTPLoopbackTests(unittest.TestCase):
 
     expectedHeaders = {b'request': b'/foo/bar',
@@ -1361,7 +1262,7 @@
                 content.append(self.content.read())
                 method.append(self.method)
                 path.append(self.path)
-                decoder.append(self._channel._transferDecoder)
+                decoder.append(self.channel._transferDecoder)
                 testcase.didRequest = True
                 self.finish()
 
@@ -1841,12 +1742,10 @@
         For an HTTP 1.0 request, L{http.Request.write} sends an HTTP 1.0
         Response-Line and whatever response headers are set.
         """
-        channel = DummyChannel()
+        req = http.Request(DummyChannel(), False)
         trans = StringTransport()
-        channel.transport = trans
-        req = http.Request(channel, False)
 
-        req._transport = trans
+        req.transport = trans
 
         req.setResponseCode(200)
         req.clientproto = b"HTTP/1.0"
@@ -1865,12 +1764,10 @@
         L{http.Request.write} casts non-bytes header value to bytes
         transparently.
         """
-        channel = DummyChannel()
+        req = http.Request(DummyChannel(), False)
         trans = StringTransport()
-        channel.transport = trans
-        req = http.Request(channel, False)
 
-        req._transport = trans
+        req.transport = trans
 
         req.setResponseCode(200)
         req.clientproto = b"HTTP/1.0"
@@ -1899,12 +1796,10 @@
         Response-Line, whatever response headers are set, and uses chunked
         encoding for the response body.
         """
-        channel = DummyChannel()
+        req = http.Request(DummyChannel(), False)
         trans = StringTransport()
-        channel.transport = trans
-        req = http.Request(channel, False)
 
-        req._transport = trans
+        req.transport = trans
 
         req.setResponseCode(200)
         req.clientproto = b"HTTP/1.1"
@@ -1926,12 +1821,10 @@
         L{http.Request.write} sends an HTTP Response-Line, whatever response
         headers are set, and a last-modified header with that time.
         """
-        channel = DummyChannel()
+        req = http.Request(DummyChannel(), False)
         trans = StringTransport()
-        channel.transport = trans
-        req = http.Request(channel, False)
 
-        req._transport = trans
+        req.transport = trans
 
         req.setResponseCode(200)
         req.clientproto = b"HTTP/1.0"
@@ -2086,7 +1979,7 @@
         # Then something goes wrong and content should get closed.
         req.connectionLost(Failure(ConnectionLost("Finished")))
         self.assertTrue(content.closed)
-        self.assertIdentical(req._channel, None)
+        self.assertIdentical(req.channel, None)
 
 
     def test_registerProducerTwiceFails(self):
@@ -2139,7 +2032,7 @@
         # This is a roundabout assertion: http.StringTransport doesn't
         # implement registerProducer, so Request.registerProducer can't have
         # tried to call registerProducer on the transport.
-        self.assertIsInstance(req._transport, http.StringTransport)
+        self.assertIsInstance(req.transport, http.StringTransport)
 
 
     def test_registerProducerWhenQueuedDoesntRegisterPullProducer(self):
@@ -2158,7 +2051,7 @@
         # This is a roundabout assertion: http.StringTransport doesn't
         # implement registerProducer, so Request.registerProducer can't have
         # tried to call registerProducer on the transport.
-        self.assertIsInstance(req._transport, http.StringTransport)
+        self.assertIsInstance(req.transport, http.StringTransport)
 
 
     def test_registerProducerWhenNotQueuedRegistersPushProducer(self):
@@ -2170,7 +2063,7 @@
         req = http.Request(DummyChannel(), False)
         producer = DummyProducer()
         req.registerProducer(producer, True)
-        self.assertEqual([(producer, True)], req._transport.producers)
+        self.assertEqual([(producer, True)], req.transport.producers)
 
 
     def test_registerProducerWhenNotQueuedRegistersPullProducer(self):
@@ -2182,7 +2075,7 @@
         req = http.Request(DummyChannel(), False)
         producer = DummyProducer()
         req.registerProducer(producer, False)
-        self.assertEqual([(producer, False)], req._transport.producers)
+        self.assertEqual([(producer, False)], req.transport.producers)
 
 
     def test_connectionLostNotification(self):
@@ -2194,7 +2087,7 @@
         request = http.Request(d, True)
         finished = request.notifyFinish()
         request.connectionLost(Failure(ConnectionLost("Connection done")))
-        self.assertIdentical(request._channel, None)
+        self.assertIdentical(request.channel, None)
         return self.assertFailure(finished, ConnectionLost)
 
 
@@ -2290,10 +2183,10 @@
         producer from the request and the request's transport.
         """
         req = http.Request(DummyChannel(), False)
-        req._transport = StringTransport()
+        req.transport = StringTransport()
         req.registerProducer(DummyProducer(), False)
         req.unregisterProducer()
-        self.assertEqual((None, None), (req.producer, req._transport.producer))
+        self.assertEqual((None, None), (req.producer, req.transport.producer))
 
 
     def test_unregisterNonQueuedStreamingProducer(self):
@@ -2302,10 +2195,10 @@
         producer from the request and the request's transport.
         """
         req = http.Request(DummyChannel(), False)
-        req._transport = StringTransport()
+        req.transport = StringTransport()
         req.registerProducer(DummyProducer(), True)
         req.unregisterProducer()
-        self.assertEqual((None, None), (req.producer, req._transport.producer))
+        self.assertEqual((None, None), (req.producer, req.transport.producer))
 
 
     def test_unregisterQueuedNonStreamingProducer(self):
@@ -2370,141 +2263,6 @@
                       factory.logFile.getvalue())
 
 
-    def test_transportDeprecated(self):
-        """
-        L{http.Request.transport} will raise a L{DeprecationWarning} if
-        accessed.
-        """
-        channel = DummyChannel()
-        trans = StringTransport()
-        channel.transport = trans
-        req = http.Request(channel, False)
-
-        # This should fire a warning.
-        req.transport
-
-        warnings = self.flushWarnings([self.test_transportDeprecated])
-        self.assertEqual(1, len(warnings))
-        self.assertEqual(warnings[0]['category'], DeprecationWarning)
-        self.assertEqual(
-            warnings[0]['message'],
-            "twisted.web.http.Request.transport was deprecated in "
-            "Twisted 16.2.0. Call directly into the Request object instead."
-        )
-
-
-    def test_transportSetDeprecated(self):
-        """
-        L{http.Request.transport} will raise a L{DeprecationWarning} if set.
-        """
-        channel = DummyChannel()
-        trans = StringTransport()
-        channel.transport = trans
-        req = http.Request(channel, False)
-
-        # This should fire a warning.
-        req.transport = object()
-
-        warnings = self.flushWarnings([self.test_transportSetDeprecated])
-        self.assertEqual(1, len(warnings))
-        self.assertEqual(warnings[0]['category'], DeprecationWarning)
-        self.assertEqual(
-            warnings[0]['message'],
-            "twisted.web.http.Request.transport was deprecated in "
-            "Twisted 16.2.0. Call directly into the Request object instead."
-        )
-
-
-    def test_channelDeprecated(self):
-        """
-        L{http.Request.channel} will raise a L{DeprecationWarning} if
-        accessed.
-        """
-        channel = DummyChannel()
-        trans = StringTransport()
-        channel.transport = trans
-        req = http.Request(channel, False)
-
-        # This should fire a warning.
-        req.channel
-
-        warnings = self.flushWarnings([self.test_channelDeprecated])
-        self.assertEqual(1, len(warnings))
-        self.assertEqual(warnings[0]['category'], DeprecationWarning)
-        self.assertEqual(
-            warnings[0]['message'],
-            "twisted.web.http.Request.channel was deprecated in Twisted "
-            "16.2.0. Call directly into the Request object instead."
-        )
-
-
-    def test_channelSetDeprecated(self):
-        """
-        L{http.Request.channel} will raise a L{DeprecationWarning} if set.
-        """
-        channel = DummyChannel()
-        trans = StringTransport()
-        channel.transport = trans
-        req = http.Request(channel, False)
-
-        # This should fire a warning.
-        req.channel = object()
-
-        warnings = self.flushWarnings([self.test_channelSetDeprecated])
-        self.assertEqual(1, len(warnings))
-        self.assertEqual(warnings[0]['category'], DeprecationWarning)
-        self.assertEqual(
-            warnings[0]['message'],
-            "twisted.web.http.Request.channel was deprecated in Twisted "
-            "16.2.0. Call directly into the Request object instead."
-        )
-
-
-    def test_sendHeadersWhenUnqueued(self):
-        """
-        Unqueueing a L{Request} sends headers properly.
-        """
-        transport = StringTransport()
-        channel = DummyChannel()
-        channel.transport = transport
-        request = http.Request(channel, True)
-        request.gotLength(123)
-        request.requestReceived(b"GET", b"/", b"HTTP/1.1")
-        request.write(b'test data')
-
-        self.assertEqual(transport.value(), b'')
-
-        request.noLongerQueued()
-
-        self.assertEqual(
-            transport.value(),
-            b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n9\r\n"
-            b"test data\r\n"
-        )
-
-
-    def test_send100ContinueWhenUnqueued(self):
-        """
-        Unqueueing a L{Request} sends any 100-Continue repsonse properly.
-        """
-        transport = StringTransport()
-        channel = DummyChannel()
-        channel.transport = transport
-        request = http.Request(channel, True)
-        request.gotLength(123)
-        request.requestReceived(b"GET", b"/", b"HTTP/1.1")
-        request._send100Continue()
-
-        self.assertEqual(transport.value(), b'')
-
-        request.noLongerQueued()
-
-        self.assertEqual(
-            transport.value(),
-            b"HTTP/1.1 100 Continue\r\n\r\n"
-        )
-
-
 
 class MultilineHeadersTests(unittest.TestCase):
     """

Modified: trunk/twisted/web/test/test_proxy.py
==============================================================================
--- trunk/twisted/web/test/test_proxy.py	(original)
+++ trunk/twisted/web/test/test_proxy.py	Sat Apr 23 01:51:33 2016
@@ -123,14 +123,6 @@
         self.lostReason = reason
 
 
-    def getPeer(self):
-        return self.transport.getPeer()
-
-
-    def getHost(self):
-        return self.transport.getHost()
-
-
 
 class ProxyClientTests(TestCase):
     """

Modified: trunk/twisted/web/test/test_web.py
==============================================================================
--- trunk/twisted/web/test/test_web.py	(original)
+++ trunk/twisted/web/test/test_web.py	Sat Apr 23 01:51:33 2016
@@ -554,8 +554,8 @@
         fail = failure.Failure(Exception("Oh no!"))
         request.processingFailed(fail)
 
-        self.assertNotIn(b"Oh no!", request._transport.getvalue())
-        self.assertIn(b"Processing Failed", request._transport.getvalue())
+        self.assertNotIn(b"Oh no!", request.transport.getvalue())
+        self.assertIn(b"Processing Failed", request.transport.getvalue())
 
         # Since we didn't "handle" the exception, flush it to prevent a test
         # failure
@@ -574,7 +574,7 @@
         fail = failure.Failure(Exception("Oh no!"))
         request.processingFailed(fail)
 
-        self.assertIn(b"Oh no!", request._transport.getvalue())
+        self.assertIn(b"Oh no!", request.transport.getvalue())
 
         # Since we didn't "handle" the exception, flush it to prevent a test
         # failure
@@ -594,7 +594,7 @@
         fail = failure.Failure(Exception(u"\u2603"))
         request.processingFailed(fail)
 
-        self.assertIn(b"&#9731;", request._transport.getvalue())
+        self.assertIn(b"&#9731;", request.transport.getvalue())
 
         # Since we didn't "handle" the exception, flush it to prevent a test
         # failure
@@ -823,11 +823,11 @@
     def testGoodMethods(self):
         req = self._getReq()
         req.requestReceived(b'GET', b'/newrender', b'HTTP/1.0')
-        self.assertEqual(req._transport.getvalue().splitlines()[-1], b'hi hi')
+        self.assertEqual(req.transport.getvalue().splitlines()[-1], b'hi hi')
 
         req = self._getReq()
         req.requestReceived(b'HEH', b'/newrender', b'HTTP/1.0')
-        self.assertEqual(req._transport.getvalue().splitlines()[-1], b'ho ho')
+        self.assertEqual(req.transport.getvalue().splitlines()[-1], b'ho ho')
 
     def testBadMethods(self):
         req = self._getReq()
@@ -855,7 +855,7 @@
         req = self._getReq()
         req.requestReceived(b'HEAD', b'/newrender', b'HTTP/1.0')
         self.assertEqual(req.code, 200)
-        self.assertEqual(-1, req._transport.getvalue().find(b'hi hi'))
+        self.assertEqual(-1, req.transport.getvalue().find(b'hi hi'))
 
 
     def test_unsupportedHead(self):
@@ -866,7 +866,7 @@
         resource = HeadlessResource()
         req = self._getReq(resource)
         req.requestReceived(b"HEAD", b"/newrender", b"HTTP/1.0")
-        body = req._transport.getvalue()
+        headers, body = req.transport.getvalue().split(b'\r\n\r\n')
         self.assertEqual(req.code, 200)
         self.assertEqual(body, b'')
 
@@ -887,7 +887,7 @@
 
         request.requestReceived(b"GET", b"/newrender", b"HTTP/1.0")
 
-        body = request._transport.getvalue()
+        headers, body = request.transport.getvalue().split(b'\r\n\r\n')
         self.assertEqual(request.code, 500)
         expected = [
             '',
@@ -988,7 +988,7 @@
         req.requestReceived(b'POST', b'/gettableresource?'
                             b'value=<script>bad', b'HTTP/1.0')
         self.assertEqual(req.code, 405)
-        renderedPage = req._transport.getvalue()
+        renderedPage = req.transport.getvalue()
         self.assertNotIn(b"<script>bad", renderedPage)
         self.assertIn(b'&lt;script&gt;bad', renderedPage)
 
@@ -1003,7 +1003,7 @@
         req = self._getReq()
         req.requestReceived(b'<style>bad', b'/gettableresource', b'HTTP/1.0')
         self.assertEqual(req.code, 501)
-        renderedPage = req._transport.getvalue()
+        renderedPage = req.transport.getvalue()
         self.assertNotIn(b"<style>bad", renderedPage)
         self.assertIn(b'&lt;style&gt;bad', renderedPage)
 

Modified: trunk/twisted/web/wsgi.py
==============================================================================
--- trunk/twisted/web/wsgi.py	(original)
+++ trunk/twisted/web/wsgi.py	Sat Apr 23 01:51:33 2016
@@ -506,7 +506,7 @@
             def wsgiError(started, type, value, traceback):
                 err(Failure(value, type, traceback), "WSGI application error")
                 if started:
-                    self.request.loseConnection()
+                    self.request.transport.loseConnection()
                 else:
                     self.request.setResponseCode(INTERNAL_SERVER_ERROR)
                     self.request.finish()