[Twisted-Python] passClient for ReverseProxyResource/VHostMonsterResource

Andrea Arcangeli andrea at cpushare.com
Sun Oct 2 11:25:05 EDT 2005


Hello,

I need a new (optional) feature in the
ReverseProxyResource/VHostMonsterResource protocol. It's optional
because I doubt the reverse proxy of other packages could support it
without upgrades. I only had to add "passClient = True" as parameter to
ReverseProxyResource and VHostMonsterResource to enable it (two liner
patch to my code).

In short this passes the client ip and port from the reverse proxy
server to the vmonster resource, this way the nevow server that receives
the proxy connection knows who has connected to the proxy and it can
react accordingly. This is needed to really allow putting more nevow
servers transparently behind the same port, one of my nevow apps really
needed to know the ip of the connecting client.

Cross posting to both lists because it has two different parts, the
first is the Nevow part and the second is the Twisted part.  This code
is online right now and no problems so far (if you see any problem let
me know ;).

Here the Nevow part:

Index: Nevow/nevow/vhost.py
===================================================================
--- Nevow/nevow/vhost.py	(revision 1788)
+++ Nevow/nevow/vhost.py	(working copy)
@@ -134,13 +134,15 @@
     *after* it returns the (resource,segments) tuple.
     """
     implements(inevow.IResource)
+
+    def __init__(self, vhost_segments):
+        self.prepath_segments = vhost_segments+1 # +1 is for /vhost
+
     def locateChild(self, ctx, segments):
         request = inevow.IRequest(ctx)
-        request.prepath = request.prepath[3:]
+        request.prepath = request.prepath[self.prepath_segments:]
         return request.site.resource, segments
 
-_prepathCleaner = _VHostMonsterResourcePrepathCleaner()
-
 class VHostMonsterResource:
     """VHostMonster resource that helps to deploy a Nevow site behind a proxy.
     
@@ -173,14 +175,27 @@
 
     This also means your private server should serve the real content at
     /foo/bar, and not at the root of the tree.
+
+    If passClient is set to True it expects the client to be passed by the
+    proxy (see ReverseProxyResource passClient parameter for details). When
+    passClient is True this page should not be reacheable directly from the
+    internet if logging the IP address and port securely is needed.
     """
     implements(inevow.IResource)
 
+    vhost_segments = 2
+
+    def __init__(self, passClient = False):
+        self.passClient = passClient
+        if passClient:
+            self.vhost_segments = 3
+        self._prepathCleaner = _VHostMonsterResourcePrepathCleaner(self.vhost_segments)
+
     def locateChild(self, ctx, segments):
 
         request = inevow.IRequest(ctx)
 
-        if len(segments) < 2:
+        if len(segments) < self.vhost_segments:
             return rend.NotFound
         else:
             if segments[0] == 'http':
@@ -193,13 +208,18 @@
                 port = int(port)
             else:
                 host, port = segments[1], 80
-           
             request.setHost(host, port)
 
-            prefixLen = len('/'+'/'.join(request.prepath)+'/'+'/'.join(segments[:2]))
-            request.path = '/'+'/'.join(segments[2:])
+            if self.passClient:
+                if ':' not in segments[2]:
+                    return rend.NotFound
+                host, port = segments[2].split(':')
+                request.setClient(host, port)
+
+            prefixLen = len('/'+'/'.join(request.prepath)+'/'+'/'.join(segments[:self.vhost_segments]))
+            request.path = '/'+'/'.join(segments[self.vhost_segments:])
             request.uri = request.uri[prefixLen:]
 
-            return _prepathCleaner, segments[2:]
+            return self._prepathCleaner, segments[self.vhost_segments:]
         
 compy.backwardsCompatImplements(VHostMonsterResource)



Here the twisted part:

Index: Twisted/twisted/web/http.py
===================================================================
--- Twisted/twisted/web/http.py	(revision 14534)
+++ Twisted/twisted/web/http.py	(working copy)
@@ -876,6 +876,10 @@
         self.received_headers["host"] = host
         self.host = address.IPv4Address("TCP", host, port)
 
+    def setClient(self, host, port):
+        """Same as setHost but for the client address"""
+        self.client = address.IPv4Address("TCP", host, port)
+
     def getClientIP(self):
         if isinstance(self.client, address.IPv4Address):
             return self.client.host
Index: Twisted/twisted/web/proxy.py
===================================================================
--- Twisted/twisted/web/proxy.py	(revision 14534)
+++ Twisted/twisted/web/proxy.py	(working copy)
@@ -160,23 +160,33 @@
     to a different server.
     """
 
-    def __init__(self, host, port, path):
+    def __init__(self, host, port, path, passClient = False):
         resource.Resource.__init__(self)
         self.host = host
         self.port = port
         self.path = path
+        self.passClient = passClient
 
+    def getPath(self, request):
+        path = self.path
+        if self.passClient:
+            path += '/%s:%d' % (request.client.host, request.client.port)
+        return path
+
     def getChild(self, path, request):
-        return ReverseProxyResource(self.host, self.port, self.path+'/'+path)
+        return ReverseProxyResource(self.host, self.port, self.getPath(request)+'/'+path, False)
 
     def render(self, request):
         request.received_headers['host'] = self.host
         request.content.seek(0, 0)
+
+        path = self.getPath(request)
+
         qs = urlparse.urlparse(request.uri)[4]
         if qs:
-            rest = self.path + '?' + qs
+            rest = path + '?' + qs
         else:
-            rest = self.path
+            rest = path
         clientFactory = ProxyClientFactory(request.method, rest, 
                                      request.clientproto, 
                                      request.getAllHeaders(),




More information about the Twisted-Python mailing list