--- 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)