Issue #1020 new

Wildcard virtual domains

Anonymous created an issue

The current virtual host dispatcher doesn't allow wildcard virtual hosts like '*.example.com', requiring that all possible subdomains of a virtual host be enumerated explicitely in the virtualhosts dict. This makes dynamically assigned and/or interpreted subdomains cumbersome, for example in a situation where people can sign up for a web service and should get their own subdomain on the site (like 'frederick.blogplaza.com' and 'littlegreenalien.blogplaza.com').

I made a little addition to _cpdispatch.py/vhost_dispatch() which allows one to use wildcard specification like '*.example.com' in the vhosts dict. The patch (and an example which shows its usage) is below, against trunk #2689.

{{{ --- _cpdispatch.py.2689 2010-08-21 11:28:27.000000000 +0200 +++ _cpdispatch.py 2010-08-21 11:59:31.000000000 +0200 @@ -515,6 +515,21 @@ return xmlrpc_dispatch

+ +def strip_subdomain(hostname): + """ + Return a domain name stripped from its subdomain-prefix(es). + For example:: + + www.search.nl -> search.nl + sales.corporation.com -> corporation.com + articles.international.newspaper.net -> newspaper.net + + Caveat: Doesn't know about ".co.uk" and other multipart TLDs + """ + return ".".join(hostname.split(".")[-2:]) + + def VirtualHost(next_dispatcher=Dispatcher(), use_x_forwarded_host=True, *domains): """ Select a different handler based on the Host header. @@ -553,6 +568,7 @@ next dispatcher. Note that you often need separate entries for "example.com" and "www.example.com". In addition, "Host" headers may contain the port number. + Use ".example.com" to catch wildcard virtual domains. """ from cherrypy.lib import httputil def vhost_dispatch(path_info): @@ -564,6 +580,8 @@ domain = header("X-Forwarded-Host", domain)

     prefix = domains.get(domain, "")
  • if not prefix:
  • prefix = domains.get("*." + strip_subdomain(domain)) if prefix: path_info = httputil.urljoin(prefix, path_info) }}}

And a little demonstration program:

{{{

import cherrypy from cherrypy import expose import socket

class Divulge(object): def init(self, title): self.title = title

@expose
def index(self, *args, **kwargs):
    host = cherrypy.request.headers["host"]
    return "On host %s, application %s's index() got called with %r and %r" % (host, self.title, args, kwargs)

@expose
def default(self, *args, **kwargs):
    host = cherrypy.request.headers["host"]
    return "On host %s, application %s's default() got called with %r and %r" % (host, self.title, args, kwargs)

class Webapp(Divulge): pass

class Lemonapp(Divulge): pass

class Mangoapp(Divulge): pass

''' Add this to your /etc/hosts to demonstrate

127.0.0.1 virtualdomaintest.org

127.0.0.1 virtualdomaintest.com 127.0.0.1 www.virtualdomaintest.com 127.0.0.1 articles.virtualdomaintest.com

127.0.0.1 virtualdomaintest.nl 127.0.0.1 www.virtualdomaintest.nl 127.0.0.1 sales.virtualdomaintest.nl 127.0.0.1 national.marketing.virtualdomaintest.nl 127.0.0.1 international.marketing.virtualdomaintest.nl '''

Note the :8080's below. If the app serves

on port 80, the port suffix should be left off.

vh = { "virtualdomaintest.com:8080": "/lemon", # enumerating all the existing virtual hosts "www.virtualdomaintest.com:8080": "/lemon", "articles.virtualdomaintest.com:8080": "/lemon", "*.virtualdomaintest.nl:8080": "/mango", # but note the wildcard here }

app = Webapp("Main") app.lemon = Lemonapp("Lemon") app.mango = Mangoapp("Mango")

cherrypy.quickstart(app, "/", {"/": {"request.dispatch": cherrypy.dispatch.VirtualHost(**vh)}})

}}}

Reported by motoom@xs4all.nl

Comments (1)

  1. guest

    Oh, I made a small error, I forgot a default value.

    The line

    prefix = domains.get("*." + strip_subdomain(domain))

    should be

    prefix = domains.get("*." + strip_subdomain(domain), "")

  2. Log in to comment