[Repoze-checkins] r793 - in repoze.vhm/trunk: . repoze/vhm repoze/vhm/tests

Chris McDonough chrism at agendaless.com
Mon Mar 10 04:41:49 UTC 2008


Author: Chris McDonough <chrism at agendaless.com>
Date: Sun Mar  9 23:41:49 2008
New Revision: 793

Log:
  Kill off path-segment-based filter (repoze.vhm.zope2).  Only the
  xheaders filter remains.

  Add license headers.

  The middleware now sets a 'repoze.vhm.virtual_host_base' which is
  preferred by setServerUrl over 'HTTP_HOST' when present.

  Add a getVirtualRoot API.



Added:
   repoze.vhm/trunk/repoze/vhm/middleware.py   (contents, props changed)
   repoze.vhm/trunk/repoze/vhm/tests/test_middleware.py   (contents, props changed)
   repoze.vhm/trunk/repoze/vhm/tests/test_utils.py   (contents, props changed)
   repoze.vhm/trunk/repoze/vhm/utils.py   (contents, props changed)
Removed:
   repoze.vhm/trunk/repoze/vhm/tests/test_xheaders.py
   repoze.vhm/trunk/repoze/vhm/tests/test_zope2.py
   repoze.vhm/trunk/repoze/vhm/xheaders.py
   repoze.vhm/trunk/repoze/vhm/zope2.py
Modified:
   repoze.vhm/trunk/CHANGES.txt
   repoze.vhm/trunk/README.txt
   repoze.vhm/trunk/repoze/vhm/__init__.py
   repoze.vhm/trunk/repoze/vhm/constants.py
   repoze.vhm/trunk/setup.py

Modified: repoze.vhm/trunk/CHANGES.txt
==============================================================================
--- repoze.vhm/trunk/CHANGES.txt	(original)
+++ repoze.vhm/trunk/CHANGES.txt	Sun Mar  9 23:41:49 2008
@@ -1,3 +1,15 @@
+0.4 (2008-03-09)
+
+  Kill off path-segment-based filter (repoze.vhm.zope2).  Only the
+  xheaders filter remains.
+
+  Add license headers.
+
+  The middleware now sets a 'repoze.vhm.virtual_host_base' which is
+  preferred by setServerUrl over 'HTTP_HOST' when present.
+
+  Add a getVirtualRoot API.
+
 0.3
 
   Fix setServerURL method to take into account HTTP_HOST passed by

Modified: repoze.vhm/trunk/README.txt
==============================================================================
--- repoze.vhm/trunk/README.txt	(original)
+++ repoze.vhm/trunk/README.txt	Sun Mar  9 23:41:49 2008
@@ -2,13 +2,12 @@
 
   Overview
 
-    This package provides some glue for doing Zope2-style virtual hosting
-    within the 'repoze.zope2' environment, where the classic Zope2
+    This package provides middleware and utilities for doing virtual
+    hosting within a WSGI/Repoze environment.  It is particularly
+    useful within a 'repoze.zope2' environment, where it may be used
+    as an alternative to the classic
     "VirtualHostMonster":http://www.zope.org/Members/4am/SiteAccess2/info
-    won't work.
-
-    It also provides CGI-environment-munging middleware that is
-    potentially useful within a non-Zope WSGI application.
+    method of doing virtual hosting.
 
   Virtual Hosting in a Nutshell
 
@@ -32,7 +31,9 @@
     'mod_python'), there follwing environment variables are of interest
     when doing virtual hosting:
 
-      'SERVER_NAME' -- the apparent hostname of the server (i.e., as
+      'SERVER_NAME' -- the name which the server believes it has.
+
+      'HTTP_HOST' -- the apparent hostname of the server (i.e., as
         passed in the 'Host:' header)
 
       'SERVER_PORT' -- the apparent port of the server
@@ -43,37 +44,29 @@
       'PATH_INFO' -- the remainder of the path, after removing any parts
         used in dispatch.
 
-  Zope2 Virtual Hosting Model
-
-    In scenarios which use Apache rewrite + proxy to host a Zope application
-    "behind" Apache, the classic Zope recipe is to rewrite the URL, adding
-    virtual hosting information as extra path elements, which are then
-    consumed during traversal of the Zope root by the VHM.  E.g., the
-    Apache server might see a request like:
-
-      http://www.example.com/news/politics/local/mayor_impeached.html
-
-    And rewrite it onto the Zope backend as something like:
-
-      http://localhost:8080/VirtualHostBase/http/www.example.com:80/cms/VirtualHostRoot/news/politics/local/mayor_impeached.html
-
-    The VHM would then transform the request, re-converting the path into:
+  'repoze.vhm#xheaders' WSGI Filter
 
-      /cms/news/politics/local/mayor_impeached.html
+    When configured as WSGI middleware, theis filter will convert the
+    path information in the environment from the "X-Vhm" headers added
+    to the request into the "standard" CGI environment variables
+    outlined above.  It will also place repoze.vhm-specific
+    environment variables into the WSGI environment for consumption by
+    repoze.zope2 (or another application which chooses to use its
+    services).
+
+    If this filter is placed into the pipeline in front of a Zope 2
+    application, the standard Virtual Host Monster object
+    ('/virtual_hosting') may be deleted, as it is no longer necessary.
+    However, it does not need to be deleted; repoze.vhm will work if
+    it is present.
 
-    setting the "virtual root" of the request to the '/cms' object,
-    and also setting the 'SERVER_URL' in the request to:
+    The filter requires no configuration; it can be added to any
+    pipeline via its egg name: "egg:repoze.vhm#vhm_xheaders".
 
-      http://www.example.com/
+  repoze.vhm Virtual Hosting Model
 
-  Zope3 Virtual Hosting Model
-
-    TODO:  show the example using Z3's syntax.
-
-  Proxy Headers Virtual Hosting Model
-
-    This model, based on a "suggestion of Ian Bicking's",
-    http://blog.ianbicking.org/2007/08/10/defaults-inheritance/ ,
+    This model (based on a "suggestion of Ian Bicking's",
+    http://blog.ianbicking.org/2007/08/10/defaults-inheritance/),
     passes virtual hosting information from the proxy / web server to
     the application by adding extra headers to the proxied request:
 
@@ -85,44 +78,64 @@
      'HTTP_X_VHM_ROOT' -- path of the object within the application
         which is supposed to function as the "virtual root".
 
-    When serving an application from "within" Apache, we can just set
-    the environment directly::
+    When serving an application from "within" Apache via mod_wsgi, we
+    can just set the environment directly::
 
       <Directory /path/to/wsgiapp>
-       SetEnv HTTP_X_VHM_HOST http://www.example.com/
-       SetEnv HTTP_X_VHM_ROOT /cms
+        SetEnv HTTP_X_VHM_HOST http://www.example.com/
+        SetEnv HTTP_X_VHM_ROOT /cms
       </Directory>
 
-    Proxies pass this information by adding additional headers.  E.g.,
-    a sample Apache configuration for the example above might be::
+    If you are serving repoze.zope2 via a proxy rewrite rule, you may
+    pass this information by adding additional headers.  E.g., a
+    sample Apache configuration for the example above might be::
 
       <VirtualHost *:80>
         ServerName www.example.com
         RewriteEngine on
-        RewriteRule ^/(.*) http://localhost:8080/$1
+        RewriteRule ^/(.*) http://localhost:8080/$1 [P,L]
         Header add X-VHm-Host http://www.example.com/
         Header add X-VHm-Root /cms
       </VirtualHost>
 
-  'repoze.vhm' WSGI Filters
+    In either of the above example cases, the effect on repoze.zope2
+    when repoze.vhm's filter is in the WSGI pipeline is the same: the
+    apparent root of "http://www.example.com" will be the default view
+    of the object that has a physical path of "/cms".  Additionally,
+    paths in URLs generated by Zope will not start with '/cms', and
+    the scheme and hostname in URLs will be "http://www.example.com"
+    as opposed to "http://localhost:8080".  
+ 
+    The "vhm host" header may contain further path information as
+    necessary; further path information can (and will, in the case of
+    repoze.zope2) be respected by downstream applications to root an
+    application at a non-server-root path ::
+
+      <Directory /path/to/wsgiapp>
+        SetEnv HTTP_X_VHM_HOST http://www.example.com/further/path
+        SetEnv HTTP_X_VHM_ROOT /cms
+      </Directory>
 
-    This package provides two filters for use in the "behind" (proxied)
-    scenario described above, one for each model.  When configured as
-    WSGI middleware, these filters convert the path information in the
-    environment from the Zope-specific syntax into the "standard" CGI
-    environment variables outlined above.
+    In this case, URLs generated by Zope will begin with
+    "http://www.example.com/further/path".  This syntax replaces the
+    "inside out" virtual hosting syntax ('_vh_' segment markers in the
+    URL) as described in the "Virtual Host Monster" documentation.
+
+    The "vhm host" and "vhm root" headers can be used independently
+    (the system will operate as you would expect in the absence of one
+    or the other).
 
   'repoze.vhm' Library API
 
-    Because the existing Zope virtual hosting solutions do not rely
-    on the "standard" CGI variables, the application dispatcher needs to
-    "fix up" the environment to match Zope's expectations.  'repoze.vhm'
-    offers the following functions to aid in this fixup:
+    Because the existing Zope 2 virtual hosting machinery does not
+    rely on the "standard" CGI variables, the application dispatcher
+    needs to "fix up" the environment to match Zope's expectations.
+    'repoze.vhm' offers the following functions to aid in this fixup:
 
-      'repoze.vhm.zope2.setServerURL' -- convert the standard CGI
+      'repoze.vhm.utils.setServerURL' -- convert the standard CGI
         virtual hosting environment into the form expected by Zope2
         (adding the 'SERVER_URL' key).
 
-      'repoze.vhm.zope2.setVirtualRoot' -- mark the object serving
-        as the virtual root for the current Zope2 request. (TODO)
+      'repoze.vhm.utils.getVirtualRoot' -- return the virtual root
+        path ('repoze.vhm.virtual_root') as set by the middleware.
 

Modified: repoze.vhm/trunk/repoze/vhm/__init__.py
==============================================================================
--- repoze.vhm/trunk/repoze/vhm/__init__.py	(original)
+++ repoze.vhm/trunk/repoze/vhm/__init__.py	Sun Mar  9 23:41:49 2008
@@ -1,15 +1,2 @@
 # repoze virtual hosting WSGI middleware
 
-class VHM_Z3:
-    def __init__(self, application):
-        self.application = application
-        
-    def __call__(self, environ, start_response):
-        # TODO: Consume VHM-style tokens from environ['PATH_INFO'] and stash
-        # them away in the format expected by Zope3's vhosting.
-        result = self.application(environ, start_response)
-        return result
- 
-def make_vhm_z3(app, global_conf):
-    return VHM_Z3(app)
-

Modified: repoze.vhm/trunk/repoze/vhm/constants.py
==============================================================================
--- repoze.vhm/trunk/repoze/vhm/constants.py	(original)
+++ repoze.vhm/trunk/repoze/vhm/constants.py	Sun Mar  9 23:41:49 2008
@@ -1,3 +1,17 @@
+##############################################################################
+#
+# Copyright (c) 2008 Agendaless Consulting and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the BSD-like license at
+# http://www.repoze.org/LICENSE.txt.  A copy of the license should accompany
+# this distribution.  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
+# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
+# FITNESS FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
 
 
 DEFAULT_PORTS = {'http': '80',

Added: repoze.vhm/trunk/repoze/vhm/middleware.py
==============================================================================
--- (empty file)
+++ repoze.vhm/trunk/repoze/vhm/middleware.py	Sun Mar  9 23:41:49 2008
@@ -0,0 +1,56 @@
+##############################################################################
+#
+# Copyright (c) 2008 Agendaless Consulting and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the BSD-like license at
+# http://www.repoze.org/LICENSE.txt.  A copy of the license should accompany
+# this distribution.  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
+# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
+# FITNESS FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+from urlparse import urlsplit
+
+from repoze.vhm.constants import DEFAULT_PORTS
+
+class VHMFilter:
+    """ WSGI ingress filter:
+
+    o Converts HTTP header-based vhost info  into "stock" CGI
+      equivalents, with extra keys in the 'repoze.vhm' namespace.
+
+    o After conversion, the environment should be suitable for munging
+      via 'utils.setServerURL' (for compatibility with OFS.Traversable).
+    """
+    def __init__(self, application):
+        self.application = application
+        
+    def __call__(self, environ, start_response):
+
+        host_header = environ.get('HTTP_X_VHM_HOST')
+
+        if host_header is not None:
+            (scheme, netloc, path, query, fragment) = urlsplit(host_header)
+            if ':' in netloc:
+                host, port = netloc.split(':')
+            else:
+                host = netloc
+                port = DEFAULT_PORTS[scheme]
+            environ['wsgi.url_scheme'] = scheme
+            environ['SERVER_NAME'] = host
+            environ['SERVER_PORT'] = port
+            environ['SCRIPT_NAME'] = path
+            environ['repoze.vhm.virtual_host_base'] = '%s:%s' % (host, port)
+
+        root_header = environ.get('HTTP_X_VHM_ROOT')
+
+        if root_header is not None:
+            environ['repoze.vhm.virtual_root'] = root_header
+ 
+        return self.application(environ, start_response)
+ 
+def make_filter(app, global_conf):
+    return VHMFilter(app)

Added: repoze.vhm/trunk/repoze/vhm/tests/test_middleware.py
==============================================================================
--- (empty file)
+++ repoze.vhm/trunk/repoze/vhm/tests/test_middleware.py	Sun Mar  9 23:41:49 2008
@@ -0,0 +1,130 @@
+##############################################################################
+#
+# Copyright (c) 2008 Agendaless Consulting and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the BSD-like license at
+# http://www.repoze.org/LICENSE.txt.  A copy of the license should accompany
+# this distribution.  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
+# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
+# FITNESS FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+import unittest
+
+class TestXHeaders(unittest.TestCase):
+    def _getTargetClass(self):
+        from repoze.vhm.middleware import VHMFilter
+        return VHMFilter
+
+    def _makeOne(self, app):
+        return self._getTargetClass()(app)
+
+    def test___call___no_markers_unchanged(self):
+        # Environments which do not have markers don't get munged.
+        expected = {}
+        app = VHMTestApp(expected)
+        filter = self._makeOne(app)
+        REAL_PATH = '/a/b/c/'
+        environ = {'wsgi.url_scheme': 'http',
+                   'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '8888',
+                   'SCRIPT_NAME': '/',
+                   'PATH_INFO': REAL_PATH,
+                  }
+
+        filter(environ, noopStartResponse)
+
+        self.assertEqual(expected.get('wsgi.url_scheme'), 'http')
+        self.assertEqual(expected['SERVER_NAME'], 'example.com')
+        self.assertEqual(expected['SERVER_PORT'], '8888')
+        self.assertEqual(expected['SCRIPT_NAME'], '/')
+        self.assertEqual(expected['PATH_INFO'], REAL_PATH)
+        self.assertEqual(expected.get('repoze.vhm.virtual_root'), None)
+        self.assertEqual(expected.get('repoze.vhm.virtual_host_base'), None)
+
+    def test___call___X_VHM_HOST_only_explicit_port(self):
+        expected = {}
+        app = VHMTestApp(expected)
+        filter = self._makeOne(app)
+        REAL_PATH = '/a/b/c/'
+        X_VHM_HOST = 'http://example.com:80/script'
+        environ = {'wsgi.url_scheme': 'http',
+                   'SERVER_NAME': 'localhost',
+                   'SERVER_PORT': '8080',
+                   'SCRIPT_NAME': '/',
+                   'PATH_INFO': REAL_PATH,
+                   'HTTP_X_VHM_HOST': X_VHM_HOST,
+                  }
+
+        filter(environ, noopStartResponse)
+
+        self.assertEqual(expected['wsgi.url_scheme'], 'http')
+        self.assertEqual(expected['SERVER_NAME'], 'example.com')
+        self.assertEqual(expected['SERVER_PORT'], '80')
+        self.assertEqual(expected['SCRIPT_NAME'], '/script')
+        self.assertEqual(expected['PATH_INFO'], REAL_PATH)
+        self.assertEqual(expected['repoze.vhm.virtual_host_base'],
+                         'example.com:80')
+
+    def test___call___X_VHM_HOST_only_default_port(self):
+        expected = {}
+        app = VHMTestApp(expected)
+        filter = self._makeOne(app)
+        REAL_PATH = '/a/b/c/'
+        X_VHM_HOST = 'http://example.com:80/script'
+        environ = {'wsgi.url_scheme': 'http',
+                   'SERVER_NAME': 'localhost',
+                   'SERVER_PORT': '8080',
+                   'SCRIPT_NAME': '/',
+                   'PATH_INFO': REAL_PATH,
+                   'HTTP_X_VHM_HOST': X_VHM_HOST,
+                  }
+
+        filter(environ, noopStartResponse)
+
+        self.assertEqual(expected['wsgi.url_scheme'], 'http')
+        self.assertEqual(expected['SERVER_NAME'], 'example.com')
+        self.assertEqual(expected['SERVER_PORT'], '80')
+        self.assertEqual(expected['SCRIPT_NAME'], '/script')
+        self.assertEqual(expected['PATH_INFO'], REAL_PATH)
+        self.assertEqual(expected.get('repoze.vhm.virtual_root'), None)
+        self.assertEqual(expected['repoze.vhm.virtual_host_base'],
+                         'example.com:80')
+
+    def test___call___X_VHM_ROOT(self):
+        expected = {}
+        app = VHMTestApp(expected)
+        filter = self._makeOne(app)
+        REAL_PATH = '/a/b/c/'
+        X_VHM_ROOT = '/a/b'
+        environ = {'wsgi.url_scheme': 'http',
+                   'SERVER_NAME': 'localhost',
+                   'SERVER_PORT': '8080',
+                   'SCRIPT_NAME': '/',
+                   'PATH_INFO': REAL_PATH,
+                   'HTTP_X_VHM_ROOT': X_VHM_ROOT,
+                  }
+
+        filter(environ, noopStartResponse)
+
+        self.assertEqual(expected.get('wsgi.url_scheme'), 'http')
+        self.assertEqual(expected['SERVER_NAME'], 'localhost')
+        self.assertEqual(expected['SERVER_PORT'], '8080')
+        self.assertEqual(expected['SCRIPT_NAME'], '/')
+        self.assertEqual(expected['PATH_INFO'], REAL_PATH)
+        self.assertEqual(expected.get('repoze.vhm.virtual_root'), '/a/b')
+
+def noopStartResponse(status, headers):
+    pass
+
+class VHMTestApp:
+    def __init__(self, _called_environ):
+        self._called_environ = _called_environ
+
+    def __call__(self, environ, start_response):
+        self._called_environ.clear()
+        self._called_environ.update(environ)
+        return self.__class__.__name__

Added: repoze.vhm/trunk/repoze/vhm/tests/test_utils.py
==============================================================================
--- (empty file)
+++ repoze.vhm/trunk/repoze/vhm/tests/test_utils.py	Sun Mar  9 23:41:49 2008
@@ -0,0 +1,145 @@
+##############################################################################
+#
+# Copyright (c) 2008 Agendaless Consulting and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the BSD-like license at
+# http://www.repoze.org/LICENSE.txt.  A copy of the license should accompany
+# this distribution.  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
+# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
+# FITNESS FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+import unittest
+
+class Test_setServerURL(unittest.TestCase):
+
+    def _getFUT(self):
+        from repoze.vhm.utils import setServerURL
+        return setServerURL
+
+    def test_empty(self):
+        setServerURL = self._getFUT()
+        environ = {}
+        setServerURL(environ)
+
+        self.assertEqual(environ,
+                         {'SERVER_URL': 'http://localhost:8080'})
+
+    def test_without_url_scheme(self):
+        setServerURL = self._getFUT()
+        environ = {'wsgi.url_scheme': 'http',
+                   'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '8000',
+                   'SCRIPT_NAME': '/script',
+                  }
+        setServerURL(environ)
+
+        self.assertEqual(environ['SERVER_URL'],
+                         'http://example.com:8000')
+
+    def test_with_default_port(self):
+        setServerURL = self._getFUT()
+        environ = {'wsgi.url_scheme': 'http',
+                   'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '80',
+                   'SCRIPT_NAME': '/script',
+                  }
+        setServerURL(environ)
+
+        self.assertEqual(environ['SERVER_URL'],
+                         'http://example.com')
+
+    def test_with_alternate_port(self):
+        setServerURL = self._getFUT()
+        environ = {'wsgi.url_scheme': 'https',
+                   'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '4433',
+                   'SCRIPT_NAME': '/script',
+                  }
+        setServerURL(environ)
+
+        self.assertEqual(environ['SERVER_URL'],
+                         'https://example.com:4433')
+
+    def test_https_without_url_scheme_with_default_port(self):
+        # In case of a PEP 333 violation.
+        setServerURL = self._getFUT()
+        environ = {'HTTPS': 'on',
+                   'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '443',
+                   'SCRIPT_NAME': '/script',
+                  }
+        setServerURL(environ)
+
+        self.assertEqual(environ['SERVER_URL'],
+                         'https://example.com')
+
+    def test_with_http_host_has_port(self):
+        setServerURL = self._getFUT()
+        environ = {'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '8081',
+                   'SCRIPT_NAME': '/script',
+                   'HTTP_HOST':'localhost:8080',
+                  }
+        setServerURL(environ)
+        self.assertEqual(environ['SERVER_URL'], 'http://localhost:8080')
+        
+    def test_with_http_host_has_default_port(self):
+        setServerURL = self._getFUT()
+        environ = {'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '80',
+                   'SCRIPT_NAME': '/script',
+                   'HTTP_HOST':'localhost:80',
+                  }
+        setServerURL(environ)
+        self.assertEqual(environ['SERVER_URL'], 'http://localhost')
+
+    def test_with_http_host_no_port(self):
+        setServerURL = self._getFUT()
+        environ = {'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '8081',
+                   'SCRIPT_NAME': '/script',
+                   'HTTP_HOST':'localhost',
+                  }
+        setServerURL(environ)
+        self.assertEqual(environ['SERVER_URL'], 'http://localhost')
+
+    def test_vhm_host_base_trumps_http_host(self):
+        setServerURL = self._getFUT()
+        environ = {'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '8081',
+                   'SCRIPT_NAME': '/script',
+                   'HTTP_HOST':'localhost:8080',
+                   'repoze.vhm.virtual_host_base':'www.example.com:80',
+                  }
+        setServerURL(environ)
+        self.assertEqual(environ['SERVER_URL'], 'http://www.example.com')
+
+    def test_vhm_host_base_no_port(self):
+        setServerURL = self._getFUT()
+        environ = {'SERVER_NAME': 'example.com',
+                   'SERVER_PORT': '8081',
+                   'SCRIPT_NAME': '/script',
+                   'HTTP_HOST':'localhost:8080',
+                   'repoze.vhm.virtual_host_base':'www.example.com',
+                  }
+        setServerURL(environ)
+        self.assertEqual(environ['SERVER_URL'], 'http://www.example.com')
+
+class Test_getVirtualRoot(unittest.TestCase):
+    def _getFUT(self):
+        from repoze.vhm.utils import getVirtualRoot
+        return getVirtualRoot
+
+    def test_without_virtual_root(self):
+        environ = {}
+        f = self._getFUT()
+        self.assertEqual(f(environ), None)
+
+    def test_with_virtual_root(self):
+        environ = {'repoze.vhm.virtual_root':'/abc'}
+        f = self._getFUT()
+        self.assertEqual(f(environ), '/abc')

Added: repoze.vhm/trunk/repoze/vhm/utils.py
==============================================================================
--- (empty file)
+++ repoze.vhm/trunk/repoze/vhm/utils.py	Sun Mar  9 23:41:49 2008
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# Copyright (c) 2008 Agendaless Consulting and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the BSD-like license at
+# http://www.repoze.org/LICENSE.txt.  A copy of the license should accompany
+# this distribution.  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
+# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
+# FITNESS FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+
+from urlparse import urlunsplit
+
+from repoze.vhm.constants import DEFAULT_PORTS
+
+def setServerURL(environ):
+    """ Compute Zope2 'SERVER_URL' using WSGI environment.
+
+    o Write the key into the environment.
+    """
+    scheme = environ.get('wsgi.url_scheme')
+    if scheme is None:
+        scheme = 'HTTPS' in environ and 'https' or 'http'
+
+    http_host = environ.get('HTTP_HOST')
+
+    # if vhm specifies a virtual host base, prefer it over the http
+    # host
+    vhm_host_base = environ.get('repoze.vhm.virtual_host_base')
+
+    http_host = vhm_host_base or http_host
+
+    if http_host:
+        if ':' in http_host:
+            host, port = http_host.split(':', 1)
+        else:
+            host = http_host
+            port = None
+    else:
+        host = environ.get('SERVER_NAME', 'localhost')
+        port = environ.get('SERVER_PORT', '8080')
+
+    script_name = environ.get('SCRIPT_NAME', '/')
+
+    if port is not None and port != DEFAULT_PORTS.get(scheme):
+        netloc = '%s:%s' % (host, port)
+    else:
+        netloc = host
+
+    url = urlunsplit((scheme, netloc, '', '', ''))
+    environ['SERVER_URL'] = url
+ 
+def getVirtualRoot(environ):
+    return environ.get('repoze.vhm.virtual_root')

Modified: repoze.vhm/trunk/setup.py
==============================================================================
--- repoze.vhm/trunk/setup.py	(original)
+++ repoze.vhm/trunk/setup.py	Sun Mar  9 23:41:49 2008
@@ -1,4 +1,4 @@
-__version__ = '0.3'
+__version__ = '0.4'
 
 import os
 from setuptools import setup, find_packages
@@ -34,7 +34,6 @@
       test_suite = "repoze.vhm.tests",
       entry_points="""
       [paste.filter_app_factory]
-      vhm_zope2 = repoze.vhm.zope2:make_filter
       vhm_xheaders = repoze.vhm.xheaders:make_filter
       """,
       )


More information about the Repoze-checkins mailing list