Virtual Site Hosting

#### Getting Started

Before you can set up virtual hosting in Zope, you need to answer two questions:

- What is the URI of each site's logical root?
- What is the corresponding path to the physical root?

Suppose, for example, that you want to use Zope to host the domain www.hotsite.com, and you want 'http://www.hotsite.com' to publish the Zope object '/hotsite/index_html'. Then 'http://www.hotsite.com' is the URI of your logical root, and '/hotsite' is the path to your physical root.

#### Example 1: One Site

Put a SiteRoot in '/hotsite', your site's physical root, and accept the default Path. Create a DTML Method in the Zope root folder containing

    Is the first-level domain 'hotsite.com'? Ignore sub-domains and port number.
    <dtml-if "_.string.split(_.string.split(HTTP_HOST, ':')[0], '.')[-2:]==['hotsite', 'com']">
      Add physical root:
      <dtml-call "REQUEST['TraversalRequestNameStack'].append('hotsite')">
    </dtml-if >

Use "Set Access Rule" with the DTML Method's Id. Want to understand this? Read on.

#### Getting Physical

The first half of virtual hosting is rewriting incoming URIs into physical paths. Many people run ZServer behind Apache, or another HTTP server with rewriting capabilities, or a proxy. In these cases, you can tell the front-end HTTP server to rewrite 'http://www.hotsite.com/(.*)' to '/blah/cgi/Zope.cgi/hotsite/$1', for example.
This works perfectly well, but if your clients are connecting directly to ZServer, or if you would like to keep all of the virtual hosting logic in Zope, you will need to do your rewriting in an Access Rule.
An Access Rule is just a regular method (DTML Method or Document, External Method, Python Method, etc.) on which you have used SiteAccess' "Set Access Rule" method. In this case, the method lives in the root, so it will examine every incoming request and decide how to deal with it.
The example DTML Method above is the simplest kind of rewrite rule, forcing all requests to traverse the 'hotsite' object before any others in the URI.

#### Getting Logical

The second, and more difficult, half of virtual hosting is getting your Zope objects to generate correct logical URIs for links and images. For example, if you are rewriting hotsite as described above, then a standard DTML snippet such as

    <a href="&dtml-URL;/hottopics">

in object '/hotsite/forum' will generate

    <a href="http://www.hotsite.com/hotsite/forum/hottopics">
    rather than
    <a href="http://www.hotsite.com/forum/hottopics">

To prevent this, all of the URLn and BASEn request variables and the absolute_url() method need to be told to strip off '/hotsite'. That's what SiteRoot objects do.
A SiteRoot object should be placed in the physical root folder ('/hotsite', in this case) and told the logical URL at which to base all requests passing through this folder. You tell it by setting its Path property, which in this case should have the value '/'.
For flexibility's sake, Path can also be set as a property 'SiteRootPATH' of the '/hotsite' folder or of the root folder, or it can be set in the rewriting Access Rule with a call to "REQUEST.set('SiteRootPATH', '/')", or it can be passed in from the mediating web server as an environment variable. You can also provide a Base ('SiteRootBASE') value, which will then replace the host:port/script portion of URIs.

#### Example 2: Multiple Sites

Suppose we are hosting 'hotsite.net', 'fooflowers.com', and 'openmouths.org' from '/hotsite', '/foof', and '/openm' respectively. We are distinguishing requests via HTTP_HOST, and we don't care what subdomain or port was specified.
Put a SiteRoot in each of '/hotsite', '/foof', and '/openm'. In each one, erase the default Path and leave Base blank. Make a DTML Method in the root folder containing

    Extract the part of HTTP_HOST we care about, and declare our rewrite dictionary.
    <dtml-let hostname="_.string.join(_.string.split(_.string.split(HTTP_HOST, ':')[0], '.')[-2:], '.')"
              sitemap="{'hotsite.net': 'hotsite',
                        'fooflowers.com': 'foof',
                        'openmouths.org': 'openm'}">
        Do we have a match?
        <dtml-if expr="sitemap.has_key(hostname)">
    Set the logical root: <dtml-call "REQUEST.set('SiteRootPATH', '/')">
    Add physical root: <dtml-call "REQUEST['TraversalRequestNameStack'].append(sitemap[hostname])">
        </dtml-if>
    </dtml-let>

Use "Set Access Rule" with the DTML Method's Id. An almost identical method can be used to distinguish sites by SERVER_ADDRESS and SERVER_PORT instead of HTTP_HOST. In that case, though, you would probably add a line to set the appropriate SiteRootBASE.
If you wanted all of these virtual hosts' root folders to live in the folder 'vhosts', you could add the line:

    Add vhost root: <dtml-call "REQUEST['TraversalRequestNameStack'].append('vhosts')">

after the 'Add physical root' line. If you wanted to add multiple path elements for each site, you could use path.extend instead of path.append and map 'fooflowers.org', for example, to ['foof', 'f', 'comsites']. This would place the root of fooflowers in folder '/comsites/f/foof/'.

#### Minor Notes

- The return value of an Access Rule is ignored and discarded. This allows embedded string comments such as in the examples above, and the use of <dtml-return " 'ignored' "> to exit the Rule. It also means that if you want to redirect within an Access Rule, you must use <dtml-raise type="Redirect"> instead of "RESPONSE.redirect()"
- A SiteRoot object is essentially an Access Rule which calls
  REQUEST.setServerURL(SiteRootBASE) and REQUEST.setVirtualRoot(SiteRootPATH).
- You might want to exempt management access from being affected by the virtual hosting. One way to do this is to have a 'magic folder' start all management interactions. I use 'Z', and wrap the rest of the Access Rule code in something like:

      Is there a path, and does it start with 'Z'?
      <dtml-let stack="REQUEST['TraversalRequestNameStack']">
      <dtml-if "stack and stack[-1]=='Z'">
      Get rid of 'Z':     <dtml-call "stack.pop()">
      Put it back logically: <dtml-call "REQUEST.setVirtualRoot('Z')">
      <dtml-else>
      ...
      </dtml-if>
      </dtml-let>

- If a SiteRooted folder is ever accessed through URLs with a base or path that does not get rewritten to match the Base and Path of the SiteRoot, you should make the SiteRoot's Base and Path blank and dynamically create SiteRootPATH/SiteRootBASE variables. For example, if you made a 'Zope' global-access prefix as described above, then the 'else' part should contain something like <dtml-call "REQUEST.set('SiteRootPATH', '/')">.