Commits

Robert Brewer  committed 33e4361

Fix for #725 (cherrypy.url should default to emitting server-relative URL's). It doesn't default (that would break backward compatibility), but at least there's an option for it now.

  • Participants
  • Parent commits eb0095d

Comments (0)

Files changed (3)

File cherrypy/__init__.py

             parents = sys._getframe(1).f_locals
             return expose_
 
-def url(path="", qs="", script_name=None, base=None, relative=False):
+def url(path="", qs="", script_name=None, base=None, relative=None):
     """Create an absolute URL for the given path.
     
     If 'path' starts with a slash ('/'), this will return
     If you call url(qs=cherrypy.request.query_string), you should get the
     original browser URL (assuming no internal redirections).
     
-    If relative is False (the default), the output will be an absolute URL
-    (usually including the scheme, host, vhost, and script_name).
-    If relative is True, the output will instead be a URL that is relative
-    to the current request path, perhaps including '..' atoms.
+    If relative is None or not provided, request.app.relative_urls will
+    be used (if available, else False). If False, the output will be an
+    absolute URL (including the scheme, host, vhost, and script_name).
+    If True, the output will instead be a URL that is relative to the
+    current request path, perhaps including '..' atoms. If relative is
+    the string 'server', the output will instead be a URL that is
+    relative to the server root; i.e., it will start with a slash.
     """
     if qs:
         qs = '?' + qs
     
     # At this point, we should have a fully-qualified absolute URL.
     
-    if relative:
+    if relative is None:
+        relative = getattr(request.app, "relative_urls", False)
+    
+    # See http://www.ietf.org/rfc/rfc2396.txt
+    if relative == 'server':
+        # "A relative reference beginning with a single slash character is
+        # termed an absolute-path reference, as defined by <abs_path>..."
+        # This is also sometimes called "server-relative".
+        newurl = '/' + '/'.join(newurl.split('/', 3)[3:])
+    elif relative:
+        # "A relative reference that does not begin with a scheme name
+        # or a slash character is termed a relative-path reference."
         old = url().split('/')[:-1]
         new = newurl.split('/')
         while old and new:

File cherrypy/_cptree.py

     request_class = _cprequest.Request
     response_class = _cprequest.Response
     
+    relative_urls = False
+    
     def __init__(self, root, script_name=""):
         self.log = _cplogging.LogManager(id(self), cherrypy.log.logger_root)
         self.root = root

File cherrypy/test/test_core.py

         _cp_config = {'tools.trailing_slash.on': False}
         
         def index(self, path_info, relative=None):
-            return cherrypy.url(path_info, relative=bool(relative))
+            if relative != 'server':
+                relative = bool(relative)
+            return cherrypy.url(path_info, relative=relative)
         
         def leaf(self, path_info, relative=None):
-            return cherrypy.url(path_info, relative=bool(relative))
+            if relative != 'server':
+                relative = bool(relative)
+            return cherrypy.url(path_info, relative=relative)
     
     
     class Params(Test):
         # Output relative to /
         self.getPage('/baseurl?path_info=/ab&relative=True')
         self.assertBody('ab')
+        
+        # absolute-path references ("server-relative")
+        # Input relative to current
+        self.getPage('/url/leaf?path_info=page1&relative=server')
+        self.assertBody('/url/page1')
+        self.getPage('/url/?path_info=page1&relative=server')
+        self.assertBody('/url/page1')
+        # Input is 'absolute'; that is, relative to script_name
+        self.getPage('/url/leaf?path_info=/page1&relative=server')
+        self.assertBody('/page1')
+        self.getPage('/url/?path_info=/page1&relative=server')
+        self.assertBody('/page1')
 
 
 if __name__ == '__main__':