Putting Nevow Page under Apache Proxy

It is generally not recommended to put the staff that requires many simultaneous connections under apache, unless Event MPM is used. But sometimes that’s just fine, e.g for tests.

Imagine you want to hide http://localhost:8080/ behind http://go.site.com/

This is what you put on apache with mod_proxy enabled:

<VirtualHost *:80>  # of course, *:80 can be replaced by something

    # external site URL
    ServerName go.site.com

    # internal (proxied) URL, note vhost/<protocol>/<host>/ scheme (*)
    ProxyPass / http://localhost:8080/vhost/http/go.site.com/

</VirtualHost>

Usually people also add ‘ProxyRequests Off’ to their apache conf to ensure that apache won’t be used as free proxy.

But it’s clearly not enough to setup apache, because it will send requests to Twisted asking http://localhost:8080/.. , while Nevow application should process request as if it were http://go.site.com.

There is a special module nevow.vhost.VHostMonsterResource() (please note nevow.vhost is used, not twisted.web.vhost), which takes url in the form http://localhost:8080/vhost/http/go.site.com/* and fixes request to make Resource think that it were direct go.site.com/* .

Example

Let’s hide a resource MyResource, which works perfectly as http://localhost:8080, under http://go.site.com.

Here is a brief ‘proxy.tac’ example, which can be run by ‘twistd -noy proxy.tac’

from twisted.application import service, strports
from nevow import appserver, inevow, rend, loaders, vhost
from zope.interface import implements

# This is the root resource we are hiding behind proxy
# It *was* working as http://localhost:8080, but now it should *become* http://go.site.com
class MyResource(rend.Page):

    def child_(self, ctx):
        return HelloPage()

# A simple root page, *was* http://localhost:8080/,
# will *become* http://go.site.com/
# (or http://localhost:8080/vhost/http/go.site.com/ from proxied url)
class HelloPage(rend.Page):

    docFactory = loaders.xmlstr('''\
<html><body>Hello</body></html>
''')

# thanks Damascene for this wrapper
# it delegates /vhost/* requests to VHostMonsterResource, which fixes request
class VhostFakeRoot:
    '''
    I am a wrapper to be used at site root when you want to combine
    vhost.VHostMonsterResource with nevow.guard. If you are using guard, you
    will pass me a guard.SessionWrapper resource.
    Also can hide generic resources
    '''
    implements(inevow.IResource)
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def renderHTTP(self, ctx):
        return self.wrapped.renderHTTP(ctx)

    def locateChild(self, ctx, segments):
        '''Returns a VHostMonster if the first segment is 'vhost'. Otherwise
        delegates to the wrapped resource.'''
        if segments[0] == 'vhost':
            return vhost.VHostMonsterResource(), segments[1:]
        else:
            return self.wrapped.locateChild(ctx, segments)

# setup/run site
site = appserver.NevowSite(VhostFakeRoot(MyResource()))
application = service.Application('go')

strports.service('8080', site).setServiceParent(application)

How that works ?

  • Request comes as http://localhost:8080/vhost/http/go.site.com/<**>
  • VhostFakeRoot.locateChild is called
  • ‘vhost’ is stripped from path and http/go.site.com/* comes to VHostMonsterResource
  • http is stripped by VHostMonsterResource, request is fixed
  • go.site.com is stripped by VHostMonsterResource, request is fixed
  • MyResource comes into play and does its job with <**>

Notes

  1. Headers from Twisted are consumed by apache.. That may be a problem
  2. Nevow.livePage stuff had problems working through proxy

Alternative Method

There’s an alternative (a lot easier method) to achieve the desired result. Just do it in apache config:

<VirtualHost [insert IP or * here]:80>
  ServerName mysub.mydomain.com

  ErrorLog /var/log/apache2/mysub.mydomain.com_error_log
  CustomLog /var/log/apache2/mysub.mydomain.com_access_log combined

  ProxyVia On
  ProxyPass / http://127.0.0.1:8080/
  ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>

Also see Reverse Proxy.