Skip to content Skip to sidebar Skip to footer

How Can CherryPy Alone Be Used To Serve Multiple Domains?

I'd like to use a standalone instance of CherryPy to serve several domains from a single server. I'd like each domain to be served from a completely separate CherryPy application,

Solution 1:

In the same repo, there's vhost recipe. However it uses a shared app. I don't see the way to get cherrypy.dispatch.VirtualHost working with separately mounted apps. This is because cherrypy.serving.request.app is set before invocation of the dispatcher. Say you have the following.

hostmap = {
  'api.domain.com' : '/app1',
  'www.domain.com' : '/app2'
}
cherrypy.tree.mount(App1(), '/app1', appConfig1)
cherrypy.tree.mount(App2(), '/app2', appConfig2)

All what cherrypy.dispatch.VirtualHost does is prepending domain prefix to current url, e.g. requesting http://www.domain.com/foo will result in /app2/foo/ as internal path that is sent to a next dispatcher which is usually cherrypy.dispatch.Dispatcher. However the latter will try to find a page handler using current cherrypy.serving.request.app which is set to empty app because there's nothing in CherryPy tree that corresonded to /foo path. So it will find nothing.

All you need here is to replace prefixing to changing the current app. That is to say changing that line to this.

cherrypy.serving.request.app = cherrypy.tree.apps[prefix]

But because cherrypy.dispatch.VirtualHost is pretty small, you can rewrite in your code easily.

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import cherrypy
from cherrypy._cpdispatch import Dispatcher


config = {
  'global' : {
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 80,
    'server.thread_pool' : 8
  }, 
  'hostmap' : {
    'api.domain.com' : '/app1',
    'www.domain.com' : '/app2'
  }
}

appConfig1 = {
  '/' : {
    'tools.json_out.on' : True
  }
}

appConfig2 = {
  '/' : {
    'tools.encode.encoding' : 'utf-8'
  }
}     

def VirtualHost(nextDispatcher = Dispatcher(), useXForwardedHost = True, **domains):

  def dispatch(pathInfo):
    request = cherrypy.serving.request
    domain  = request.headers.get('Host', '')
    if useXForwardedHost:
      domain = request.headers.get('X-Forwarded-Host', domain)

    prefix = domains.get(domain, '')
    if prefix:
      request.app = cherrypy.tree.apps[prefix]

    result = nextDispatcher(pathInfo)

    # Touch up staticdir config. See
    # https://bitbucket.org/cherrypy/cherrypy/issue/614.
    section = request.config.get('tools.staticdir.section')
    if section:
      section = section[len(prefix):]
      request.config['tools.staticdir.section'] = section

    return result

  return dispatch


class App1:

  @cherrypy.expose
  def index(self):
    return {'bar': 42}


class App2:

  @cherrypy.expose
  def index(self):
    return '<em>foo</em>'


if __name__ == '__main__':
  config['/'] = {'request.dispatch': VirtualHost(**config['hostmap'])}

  cherrypy.tree.mount(App1(), '/app1', appConfig1)
  cherrypy.tree.mount(App2(), '/app2', appConfig2)

  cherrypy.quickstart(config = config)

Post a Comment for "How Can CherryPy Alone Be Used To Serve Multiple Domains?"