[tor-commits] [bridgedb/master] Revise HTTPS DummyRequest test utility for changes in Twisted>14.0.2.

isis at torproject.org isis at torproject.org
Thu Jul 28 16:41:11 UTC 2016


commit 22817ee11fd7d21cf38ee6050a9bcd3dd2b7849a
Author: Isis Lovecruft <isis at torproject.org>
Date:   Mon May 2 16:16:40 2016 +0000

    Revise HTTPS DummyRequest test utility for changes in Twisted>14.0.2.
---
 test/https_helpers.py | 228 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 226 insertions(+), 2 deletions(-)

diff --git a/test/https_helpers.py b/test/https_helpers.py
index cc1ddfa..14755ec 100644
--- a/test/https_helpers.py
+++ b/test/https_helpers.py
@@ -13,6 +13,10 @@
 
 import io
 
+from twisted.internet.defer import Deferred
+from twisted.internet.address import IPv4Address
+from twisted.web.http_headers import Headers
+from twisted.web.server import NOT_DONE_YET, Session
 from twisted.web.test import requesthelper
 
 from bridgedb.persistent import Conf
@@ -121,15 +125,232 @@ class DummyHTTPSDistributor(object):
         return [self._bridge_class() for _ in range(self._bridgesPerResponseMin)]
 
 
-class DummyRequest(requesthelper.DummyRequest):
+class RequestHelperDummyRequest(object):
+    """
+    Represents a dummy or fake request.
+
+    Taken from Twisted-14.0.2 because this helper class changed in 16.0.0.
+
+    @ivar _finishedDeferreds: C{None} or a C{list} of L{Deferreds} which will
+        be called back with C{None} when C{finish} is called or which will be
+        errbacked if C{processingFailed} is called.
+
+    @type headers: C{dict}
+    @ivar headers: A mapping of header name to header value for all request
+        headers.
+
+    @type outgoingHeaders: C{dict}
+    @ivar outgoingHeaders: A mapping of header name to header value for all
+        response headers.
+
+    @type responseCode: C{int}
+    @ivar responseCode: The response code which was passed to
+        C{setResponseCode}.
+
+    @type written: C{list} of C{bytes}
+    @ivar written: The bytes which have been written to the request.
+    """
+    uri = b'http://dummy/'
+    method = b'GET'
+    client = None
+
+    def registerProducer(self, prod,s):
+        self.go = 1
+        while self.go:
+            prod.resumeProducing()
+
+    def unregisterProducer(self):
+        self.go = 0
+
+
+    def __init__(self, postpath, session=None):
+        self.sitepath = []
+        self.written = []
+        self.finished = 0
+        self.postpath = postpath
+        self.prepath = []
+        self.session = None
+        self.protoSession = session or Session(0, self)
+        self.args = {}
+        self.outgoingHeaders = {}
+        self.requestHeaders = Headers()
+        self.responseHeaders = Headers()
+        self.responseCode = None
+        self.headers = {}
+        self._finishedDeferreds = []
+        self._serverName = b"dummy"
+        self.clientproto = b"HTTP/1.0"
+
+    def getHeader(self, name):
+        """
+        Retrieve the value of a request header.
+
+        @type name: C{bytes}
+        @param name: The name of the request header for which to retrieve the
+            value.  Header names are compared case-insensitively.
+
+        @rtype: C{bytes} or L{NoneType}
+        @return: The value of the specified request header.
+        """
+        return self.headers.get(name.lower(), None)
+
+
+    def getAllHeaders(self):
+        """
+        Retrieve all the values of the request headers as a dictionary.
+
+        @return: The entire C{headers} L{dict}.
+        """
+        return self.headers
+
+
+    def setHeader(self, name, value):
+        """TODO: make this assert on write() if the header is content-length
+        """
+        self.outgoingHeaders[name.lower()] = value
+
+    def getSession(self):
+        if self.session:
+            return self.session
+        assert not self.written, "Session cannot be requested after data has been written."
+        self.session = self.protoSession
+        return self.session
+
+
+    def render(self, resource):
+        """
+        Render the given resource as a response to this request.
+
+        This implementation only handles a few of the most common behaviors of
+        resources.  It can handle a render method that returns a string or
+        C{NOT_DONE_YET}.  It doesn't know anything about the semantics of
+        request methods (eg HEAD) nor how to set any particular headers.
+        Basically, it's largely broken, but sufficient for some tests at least.
+        It should B{not} be expanded to do all the same stuff L{Request} does.
+        Instead, L{DummyRequest} should be phased out and L{Request} (or some
+        other real code factored in a different way) used.
+        """
+        result = resource.render(self)
+        if result is NOT_DONE_YET:
+            return
+        self.write(result)
+        self.finish()
+
+
+    def write(self, data):
+        if not isinstance(data, bytes):
+            raise TypeError("write() only accepts bytes")
+        self.written.append(data)
+
+    def notifyFinish(self):
+        """
+        Return a L{Deferred} which is called back with C{None} when the request
+        is finished.  This will probably only work if you haven't called
+        C{finish} yet.
+        """
+        finished = Deferred()
+        self._finishedDeferreds.append(finished)
+        return finished
+
+
+    def finish(self):
+        """
+        Record that the request is finished and callback and L{Deferred}s
+        waiting for notification of this.
+        """
+        self.finished = self.finished + 1
+        if self._finishedDeferreds is not None:
+            observers = self._finishedDeferreds
+            self._finishedDeferreds = None
+            for obs in observers:
+                obs.callback(None)
+
+
+    def processingFailed(self, reason):
+        """
+        Errback and L{Deferreds} waiting for finish notification.
+        """
+        if self._finishedDeferreds is not None:
+            observers = self._finishedDeferreds
+            self._finishedDeferreds = None
+            for obs in observers:
+                obs.errback(reason)
+
+
+    def addArg(self, name, value):
+        self.args[name] = [value]
+
+
+    def setResponseCode(self, code, message=None):
+        """
+        Set the HTTP status response code, but takes care that this is called
+        before any data is written.
+        """
+        assert not self.written, "Response code cannot be set after data has been written: %s." % "@@@@".join(self.written)
+        self.responseCode = code
+        self.responseMessage = message
+
+
+    def setLastModified(self, when):
+        assert not self.written, "Last-Modified cannot be set after data has been written: %s." % "@@@@".join(self.written)
+
+
+    def setETag(self, tag):
+        assert not self.written, "ETag cannot be set after data has been written: %s." % "@@@@".join(self.written)
+
+
+    def getClientIP(self):
+        """
+        Return the IPv4 address of the client which made this request, if there
+        is one, otherwise C{None}.
+        """
+        if isinstance(self.client, IPv4Address):
+            return self.client.host
+        return None
+
+
+    def getRequestHostname(self):
+        """
+        Get a dummy hostname associated to the HTTP request.
+
+        @rtype: C{bytes}
+        @returns: a dummy hostname
+        """
+        return self._serverName
+
+
+    def getHost(self):
+        """
+        Get a dummy transport's host.
+
+        @rtype: C{IPv4Address}
+        @returns: a dummy transport's host
+        """
+        return IPv4Address('TCP', '127.0.0.1', 80)
+
+
+    def getClient(self):
+        """
+        Stub to get the client doing the HTTP request.
+        This merely just ensures that this method exists here. Feel free to
+        extend it.
+        """
+
+
+class DummyRequest(RequestHelperDummyRequest):
     """Wrapper for :api:`twisted.test.requesthelper.DummyRequest` to add
     redirect support.
     """
     def __init__(self, *args, **kwargs):
-        requesthelper.DummyRequest.__init__(self, *args, **kwargs)
+        RequestHelperDummyRequest.__init__(self, *args, **kwargs)
         self.redirect = self._redirect(self)
         self.content = io.StringIO()
 
+        self.headers = {}  # Needed for Twisted>14.0.2
+        #self.outgoingHeaders = {}
+        #self.responseHeaders = Headers()
+        #self.requestHeaders = Headers()
+
     def writeContent(self, data):
         """Add some **data** to the faked body of this request.
 
@@ -159,3 +380,6 @@ class DummyRequest(requesthelper.DummyRequest):
         newRequest = type(request)
         newRequest.uri = request.uri
         return newRequest
+
+
+#DummyRequest = RequestHelperDummyRequest





More information about the tor-commits mailing list