Commits

Robert Brewer committed 6cf069a

Fix for #600 (InternalRedirect does double duty). This dispwrappers.patch changes vhost and xmlrpc from using InternalRedirect (which rewrites request.path_info) to dispatch wrapper functions (which do not).

  • Participants
  • Parent commits 15f965a

Comments (0)

Files changed (5)

File cherrypy/_cpdispatch.py

                 merge(app.config[curpath])
         
         return handler
+
+
+def XMLRPCDispatcher(next_dispatcher=Dispatcher()):
+    from cherrypy.lib import xmlrpc
+    def xmlrpc_dispatch(path_info):
+        path_info = xmlrpc.patched_path(path_info)
+        return next_dispatcher(path_info)
+    return xmlrpc_dispatch
+
+
+def VirtualHost(next_dispatcher=Dispatcher(), use_x_forwarded_host=True, **domains):
+    """Select a different handler based on the Host header.
+    
+    Useful when running multiple sites within one CP server.
+    
+    From http://groups.google.com/group/cherrypy-users/browse_thread/thread/f393540fe278e54d:
+    
+    For various reasons I need several domains to point to different parts of a
+    single website structure as well as to their own "homepage"   EG
+    
+    http://www.mydom1.com  ->  root
+    http://www.mydom2.com  ->  root/mydom2/
+    http://www.mydom3.com  ->  root/mydom3/
+    http://www.mydom4.com  ->  under construction page
+    
+    but also to have  http://www.mydom1.com/mydom2/  etc to be valid pages in
+    their own right.
+    """
+    from cherrypy.lib import http
+    def vhost_dispatch(path_info):
+        header = cherrypy.request.headers.get
+        
+        domain = header('Host', '')
+        if use_x_forwarded_host:
+            domain = header("X-Forwarded-Host", domain)
+        
+        prefix = domains.get(domain, "")
+        if prefix:
+            path_info = http.urljoin(prefix, path_info)
+        
+        return next_dispatcher(path_info)
+    return vhost_dispatch
+

File cherrypy/_cptools.py

     index = __call__
 
 
-class XMLRPCTool(object):
-    """Tool for using XMLRPC over HTTP.
-    
-    Python's None value cannot be used in standard XML-RPC; to allow
-    using it via an extension, provide a true value for allow_none.
-    """
-    
-    def _setup(self):
-        """Hook this tool into cherrypy.request."""
-        request = cherrypy.request
-        
-        # Guard against running this method twice.
-        if hasattr(request, 'xmlrpc'):
-            return
-        request.xmlrpc = True
-        
-        request.error_response = _xmlrpc.on_error
-        path_info = request.path_info
-        ppath = _xmlrpc.patched_path(path_info)
-        if ppath != path_info:
-            raise cherrypy.InternalRedirect(ppath)
-
-
 class WSGIAppTool(HandlerTool):
     """A tool for running any WSGI middleware/application within CP.
     
 _d.session_auth = SessionAuthTool(cptools.session_auth)
 _d.proxy = Tool('before_request_body', cptools.proxy, priority=30)
 _d.response_headers = Tool('on_start_resource', cptools.response_headers)
-_d.virtual_host = Tool('on_start_resource', cptools.virtual_host, priority=40)
 _d.log_tracebacks = Tool('before_error_response', cptools.log_traceback)
 _d.log_headers = Tool('before_error_response', cptools.log_request_headers)
 _d.err_redirect = ErrorTool(cptools.redirect)
 _d.staticfile = HandlerTool(static.staticfile)
 # _sessions.init must be bound after headers are read
 _d.sessions = SessionTool('before_request_body', _sessions.init)
-_d.xmlrpc = XMLRPCTool()
+_d.xmlrpc = ErrorTool(_xmlrpc.on_error)
 _d.wsgiapp = WSGIAppTool(_wsgiapp.run)
 _d.caching = CachingTool('before_handler', _caching.get, 'caching')
 _d.expires = Tool('before_finalize', _caching.expires)

File cherrypy/lib/cptools.py

                  for k in dir(SessionAuth) if not k.startswith("__")])
 
 
-def virtual_host(use_x_forwarded_host=True, **domains):
-    """Redirect internally based on the Host header.
-    
-    Useful when running multiple sites within one CP server.
-    
-    From http://groups.google.com/group/cherrypy-users/browse_thread/thread/f393540fe278e54d:
-    
-    For various reasons I need several domains to point to different parts of a
-    single website structure as well as to their own "homepage"   EG
-    
-    http://www.mydom1.com  ->  root
-    http://www.mydom2.com  ->  root/mydom2/
-    http://www.mydom3.com  ->  root/mydom3/
-    http://www.mydom4.com  ->  under construction page
-    
-    but also to have  http://www.mydom1.com/mydom2/  etc to be valid pages in
-    their own right.
-    """
-    request = cherrypy.request
-    
-    # Guard against running twice.
-    if hasattr(request, "virtual_prefix"):
-        return
-    
-    domain = request.headers.get('Host', '')
-    if use_x_forwarded_host:
-        domain = request.headers.get("X-Forwarded-Host", domain)
-    
-    request.virtual_prefix = prefix = domains.get(domain, "")
-    if prefix:
-        raise cherrypy.InternalRedirect(_http.urljoin(prefix, request.path_info))
-
 def log_traceback():
     """Write the last error's traceback to the cherrypy error log."""
     from cherrypy import _cperror

File cherrypy/test/test_virtualhost.py

 test.prefer_parent_path()
 
 import cherrypy
+from cherrypy import _cpdispatch
 
 def setup_server():
     class Root:
     root = Root()
     root.mydom2 = VHost("Domain 2")
     root.mydom3 = VHost("Domain 3")
-    cherrypy.tree.mount(root)
+    cherrypy.tree.mount(root, config={'/': {
+        'request.dispatch': _cpdispatch.VirtualHost(
+            **{'www.mydom2.com': '/mydom2',
+               'www.mydom3.com': '/mydom3',
+               'www.mydom4.com': '/dom4',
+               }),
+        }})
     
-    cherrypy.config.update({
-        'environment': 'test_suite',
-        'tools.virtual_host.on': True,
-        'tools.virtual_host.www.mydom2.com': '/mydom2',
-        'tools.virtual_host.www.mydom3.com': '/mydom3',
-        'tools.virtual_host.www.mydom4.com': '/dom4',
-        })
+    cherrypy.config.update({'environment': 'test_suite'})
 
 from cherrypy.test import helper
 
         self.getPage("/vmethod/pos", [('Host', 'www.mydom3.com')])
         self.assertBody("You sent 'pos'")
         
+        # Test that cherrypy.url uses the browser url, not the virtual url
         self.getPage("/url", [('Host', 'www.mydom2.com')])
         self.assertBody("http://www.mydom2.com/nextpage")
 

File cherrypy/test/test_xmlrpc.py

 test.prefer_parent_path()
 import xmlrpclib
 
+from cherrypy import _cpdispatch
+
 
 def setup_server():
     import cherrypy
 
     root = Root()
     root.xmlrpc = XmlRpc()
-    cherrypy.tree.mount(root)
+    cherrypy.tree.mount(root, config={'/': {
+        'request.dispatch': _cpdispatch.XMLRPCDispatcher(),
+        }})
     cherrypy.config.update({'environment': 'test_suite'})