[Twisted-web] VHostMonsterResource for url-context instead of subdomain

Steven Armstrong sa at c-area.ch
Wed Jan 19 04:57:56 MST 2005

Hello twisted hackers

I've been playing arround with nevow and twisted for a while now.
While looking for a way to use twisted with apache I came across the 
following problem and hacked together a possible solution.

In the twisted docs 
it sais to use VHostMonsterResource and proxy the documentroot to 
twisted. In my setup I can't do this. What I need is a way to proxy only 
a specific url-context to twisted while the rest of the domain remains 
under apache's control.

This is what I came up with. It seems to work well, the nevow examples 
are all working with this setup. All except those that don't work in 
native twisted/nevow eather that is.

Code is attached as vhostlocation.txt to prevent linebreaks from beeing 

Do you guys see any problems with this approach?
Are there any implications through changing request.uri and 
request.prepath that I didn't think/know of?


-------------- next part --------------
from twisted.python import log
from nevow import vhost
import re

_debug = False
_vhost_location_delim = "@"
_vhost_re = re.compile('(.*)(/'+ _vhost_location_delim +'.*'+ _vhost_location_delim +'/)(.*)')

class VHostLocation(vhost.VHostMonsterResource):
    Represents a single url-context of a Apache VirtualHost.
    Using this you do not need to proxy an entire virtual host to twisted.
    Instead you can proxy a specific location like:
    <VirtualHost *>
        ServerName www.example.com
        RewriteRule ^/nevow-app$ /nevow-app/ [R=301,L]
        ProxyPass /nevow-app/ http://localhost:8080/vhost/http/www.example.com:80/@nevow-app@/
        # this also works
        #ProxyPass /nevow/app/ http://localhost:8080/vhost/http/www.example.com:80/@nevow/app@/

    In this example only the http://www.example.com/nevow-app location is handled by twisted.
    The rest of the VirtualHost remains under Apache's control.

    The location used by Apache is passed to twisted through the trailing @location-name at .
    This is then used to fix request.uri and the request.postpath list to hold the segments 
    needed to create a valid absolute url.
    So the ProxyPass directive in the above example is seen/used as:
    Apache internal:
    Twisted internal:
    Twisted absolute:

    def _fix_segments(self, segments):
        Removes the vhost_location from the given segments if necessary.
        Returns the modified segments and the vhost_location.
        vhost_location would be "nevow-app" in the example mentioned above.
        segments before: ('http', 'www.example.com:80', '@nevow-app@', 'somepage')
        segments after: ('http', 'www.example.com:80', 'somepage')
        segments_string = "/".join(segments)
        vhost_location = _vhost_re.sub(r'\2',segments_string)
        # remove leading and trailing slash and delimiter
        vhost_location = vhost_location[(len(_vhost_location_delim)+1):-(len(_vhost_location_delim)+1)]
        new_segments = tuple(_vhost_re.sub(r'\1/\3',segments_string).split("/"))
        return new_segments, vhost_location

    def _fix_path(self, request, vhost_location):
        Fixes the requests uri.
        Fixes the requests postpath list.
        Given the above example:
        request.uri before: /@nevow-app@/somepage
        request.uri after: /somepage
        request.postpath before: ['http', 'www.example.com:80', '@nevow-app@', 'somepage']
        request.postpath after: ['http', 'www.example.com:80', 'nevow-app', 'somepage']
        request.uri = request.uri.replace("/"+ _vhost_location_delim + vhost_location + _vhost_location_delim, "")
        #request.path = "/"+ vhost_location + request.path
        postpath = "/".join(request.postpath)
        postpath = postpath.replace(_vhost_location_delim + vhost_location + _vhost_location_delim, vhost_location)
        request.postpath = postpath.split("/")

    def locateChild(self, ctx, segments):
        First fixes the given segments. see _fix_segments
        Then passes the method call to the superclass.
        Then fixes the request.postpath. see _fix_path
        returns what the superclass returned without changing it.
        request = inevow.IRequest(ctx)
        if _debug:
            log.msg("request.uri: %s" % request.uri)
            log.msg("request.path: %s" % request.path)
            log.msg("request.prepath: %s" % request.prepath)
            log.msg("request.postpath: %s" % request.postpath)
            log.msg("segments: %s" % str(segments))

        segments, vhost_location = self._fix_segments(segments)
        if _debug: 
            log.msg("segments after: %s" % str(segments))
            log.msg("vhost_location: %s" % vhost_location)

        res, segs = vhost.VHostMonsterResource.locateChild(self, ctx, segments)
        if _debug:
            log.msg("segs: %s" % str(segs))

        if vhost_location:
            self._fix_path(request, vhost_location)

        if _debug:
            log.msg("request.uri after: %s" % request.uri)
            log.msg("request.postpath after: %s" % request.postpath)
        return res, segs

application = service.Application("examples-vhost")
page = Examples()
#vResource = vhost.VHostMonsterResource()
vResource = VHostLocation()
page.putChild('vhost', vResource)

More information about the Twisted-web mailing list