mercurial/hgweb/request.py
changeset 36897 d7fd203e36cc
parent 36896 b2a3308d6a21
child 36898 d0b0fedbfb53
--- a/mercurial/hgweb/request.py	Sun Mar 11 12:53:47 2018 -0700
+++ b/mercurial/hgweb/request.py	Sun Mar 11 13:11:13 2018 -0700
@@ -155,11 +155,16 @@
     # Request body input stream.
     bodyfh = attr.ib()
 
-def parserequestfromenv(env, bodyfh):
+def parserequestfromenv(env, bodyfh, reponame=None):
     """Parse URL components from environment variables.
 
     WSGI defines request attributes via environment variables. This function
     parses the environment variables into a data structure.
+
+    If ``reponame`` is defined, the leading path components matching that
+    string are effectively shifted from ``PATH_INFO`` to ``SCRIPT_NAME``.
+    This simulates the world view of a WSGI application that processes
+    requests from the base URL of a repo.
     """
     # PEP-0333 defines the WSGI spec and is a useful reference for this code.
 
@@ -215,28 +220,34 @@
         fullurl += '?' + env['QUERY_STRING']
         advertisedfullurl += '?' + env['QUERY_STRING']
 
-    # When dispatching requests, we look at the URL components (PATH_INFO
-    # and QUERY_STRING) after the application root (SCRIPT_NAME). But hgwebdir
-    # has the concept of "virtual" repositories. This is defined via REPO_NAME.
-    # If REPO_NAME is defined, we append it to SCRIPT_NAME to form a new app
-    # root. We also exclude its path components from PATH_INFO when resolving
-    # the dispatch path.
+    # If ``reponame`` is defined, that must be a prefix on PATH_INFO
+    # that represents the repository being dispatched to. When computing
+    # the dispatch info, we ignore these leading path components.
 
     apppath = env.get('SCRIPT_NAME', '')
 
-    if env.get('REPO_NAME'):
-        if not apppath.endswith('/'):
-            apppath += '/'
+    if reponame:
+        repoprefix = '/' + reponame.strip('/')
 
-        apppath += env.get('REPO_NAME')
+        if not env.get('PATH_INFO'):
+            raise error.ProgrammingError('reponame requires PATH_INFO')
+
+        if not env['PATH_INFO'].startswith(repoprefix):
+            raise error.ProgrammingError('PATH_INFO does not begin with repo '
+                                         'name: %s (%s)' % (env['PATH_INFO'],
+                                                            reponame))
 
-    if 'PATH_INFO' in env:
-        dispatchparts = env['PATH_INFO'].strip('/').split('/')
+        dispatchpath = env['PATH_INFO'][len(repoprefix):]
 
-        # Strip out repo parts.
-        repoparts = env.get('REPO_NAME', '').split('/')
-        if dispatchparts[:len(repoparts)] == repoparts:
-            dispatchparts = dispatchparts[len(repoparts):]
+        if dispatchpath and not dispatchpath.startswith('/'):
+            raise error.ProgrammingError('reponame prefix of PATH_INFO does '
+                                         'not end at path delimiter: %s (%s)' %
+                                         (env['PATH_INFO'], reponame))
+
+        apppath = apppath.rstrip('/') + repoprefix
+        dispatchparts = dispatchpath.strip('/').split('/')
+    elif env.get('PATH_INFO', '').strip('/'):
+        dispatchparts = env['PATH_INFO'].strip('/').split('/')
     else:
         dispatchparts = []
 
@@ -283,7 +294,7 @@
                          apppath=apppath,
                          dispatchparts=dispatchparts, dispatchpath=dispatchpath,
                          havepathinfo='PATH_INFO' in env,
-                         reponame=env.get('REPO_NAME'),
+                         reponame=reponame,
                          querystring=querystring,
                          qsparams=qsparams,
                          headers=headers,