mercurial/hgweb/hgweb_mod.py
changeset 26134 e0a6908f066f
parent 26133 44ed220ef26f
child 26135 edfb4d3b9672
--- a/mercurial/hgweb/hgweb_mod.py	Sat Aug 22 14:22:40 2015 -0700
+++ b/mercurial/hgweb/hgweb_mod.py	Sat Aug 22 14:59:36 2015 -0700
@@ -61,6 +61,25 @@
     return reversed(breadcrumb)
 
 
+class requestcontext(object):
+    """Holds state/context for an individual request.
+
+    Servers can be multi-threaded. Holding state on the WSGI application
+    is prone to race conditions. Instances of this class exist to hold
+    mutable and race-free state for requests.
+    """
+    def __init__(self, app):
+        object.__setattr__(self, 'app', app)
+        object.__setattr__(self, 'repo', app.repo)
+
+    # Proxy unknown reads and writes to the application instance
+    # until everything is moved to us.
+    def __getattr__(self, name):
+        return getattr(self.app, name)
+
+    def __setattr__(self, name, value):
+        return setattr(self.app, name, value)
+
 class hgweb(object):
     """HTTP server for individual repositories.
 
@@ -193,6 +212,7 @@
         should be using instances of this class as the WSGI application.
         """
         self.refresh(req)
+        rctx = requestcontext(self)
 
         # work with CGI variables to create coherent structure
         # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
@@ -223,7 +243,7 @@
                 if query:
                     raise ErrorResponse(HTTP_NOT_FOUND)
                 if cmd in perms:
-                    self.check_perm(req, perms[cmd])
+                    self.check_perm(rctx, req, perms[cmd])
                 return protocol.call(self.repo, req, cmd)
             except ErrorResponse as inst:
                 # A client that sends unbundle without 100-continue will
@@ -284,7 +304,7 @@
 
             # check read permissions non-static content
             if cmd != 'static':
-                self.check_perm(req, None)
+                self.check_perm(rctx, req, None)
 
             if cmd == '':
                 req.form['cmd'] = [tmpl.cache['default']]
@@ -297,9 +317,9 @@
                 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
             elif cmd == 'file' and 'raw' in req.form.get('style', []):
                 self.ctype = ctype
-                content = webcommands.rawfile(self, req, tmpl)
+                content = webcommands.rawfile(rctx, req, tmpl)
             else:
-                content = getattr(webcommands, cmd)(self, req, tmpl)
+                content = getattr(webcommands, cmd)(rctx, req, tmpl)
                 req.respond(HTTP_OK, ctype)
 
             return content
@@ -442,6 +462,6 @@
         'zip': ('application/zip', 'zip', '.zip', None),
         }
 
-    def check_perm(self, req, op):
+    def check_perm(self, rctx, req, op):
         for permhook in permhooks:
-            permhook(self, req, op)
+            permhook(rctx, req, op)