comparison mercurial/hgweb/hgweb_mod.py @ 36870:1f42d621f090

hgweb: support using new response object for web commands We have a "requestcontext" type for holding state for the current request. Why we pass in the wsgirequest and templater instance to @webcommand functions, I don't know. I like the idea of standardizing on using "requestcontext" for passing all state to @webcommand functions because that scales well without API changes every time you want to pass a new piece of data. So, we add our new request and response instances to "requestcontext" so @webcommand functions can access them. We also teach our command dispatcher to recognize a new calling convention. Instead of returning content from the @webcommand function, we return our response object. This signals that this response object is to be used for sending output. The keyword extension was wrapping various @webcommand and assuming the output was iterable, so we had to teach it about the new calling convention. To prove everything works, we convert the "filelog" @webcommand to use the new convention. The new calling convention is a bit wonky. I intend to improve this once all commands are ported to use the new response object. Differential Revision: https://phab.mercurial-scm.org/D2786
author Gregory Szorc <gregory.szorc@gmail.com>
date Sat, 10 Mar 2018 17:02:57 -0800
parents 7ad6a275316f
children 9fc3d814646e
comparison
equal deleted inserted replaced
36869:7ad6a275316f 36870:1f42d621f090
89 89
90 Servers can be multi-threaded. Holding state on the WSGI application 90 Servers can be multi-threaded. Holding state on the WSGI application
91 is prone to race conditions. Instances of this class exist to hold 91 is prone to race conditions. Instances of this class exist to hold
92 mutable and race-free state for requests. 92 mutable and race-free state for requests.
93 """ 93 """
94 def __init__(self, app, repo): 94 def __init__(self, app, repo, req, res):
95 self.repo = repo 95 self.repo = repo
96 self.reponame = app.reponame 96 self.reponame = app.reponame
97 self.req = req
98 self.res = res
97 99
98 self.archivespecs = archivespecs 100 self.archivespecs = archivespecs
99 101
100 self.maxchanges = self.configint('web', 'maxchanges') 102 self.maxchanges = self.configint('web', 'maxchanges')
101 self.stripecount = self.configint('web', 'stripes') 103 self.stripecount = self.configint('web', 'stripes')
303 yield r 305 yield r
304 306
305 def _runwsgi(self, wsgireq, repo): 307 def _runwsgi(self, wsgireq, repo):
306 req = wsgireq.req 308 req = wsgireq.req
307 res = wsgireq.res 309 res = wsgireq.res
308 rctx = requestcontext(self, repo) 310 rctx = requestcontext(self, repo, req, res)
309 311
310 # This state is global across all threads. 312 # This state is global across all threads.
311 encoding.encoding = rctx.config('web', 'encoding') 313 encoding.encoding = rctx.config('web', 'encoding')
312 rctx.repo.ui.environ = wsgireq.env 314 rctx.repo.ui.environ = wsgireq.env
313 315
399 raise ErrorResponse(HTTP_BAD_REQUEST, msg) 401 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
400 elif cmd == 'file' and req.qsparams.get('style') == 'raw': 402 elif cmd == 'file' and req.qsparams.get('style') == 'raw':
401 rctx.ctype = ctype 403 rctx.ctype = ctype
402 content = webcommands.rawfile(rctx, wsgireq, tmpl) 404 content = webcommands.rawfile(rctx, wsgireq, tmpl)
403 else: 405 else:
406 # Set some globals appropriate for web handlers. Commands can
407 # override easily enough.
408 res.status = '200 Script output follows'
409 res.headers['Content-Type'] = ctype
404 content = getattr(webcommands, cmd)(rctx, wsgireq, tmpl) 410 content = getattr(webcommands, cmd)(rctx, wsgireq, tmpl)
411
412 if content is res:
413 return res.sendresponse()
414
405 wsgireq.respond(HTTP_OK, ctype) 415 wsgireq.respond(HTTP_OK, ctype)
406 416
407 return content 417 return content
408 418
409 except (error.LookupError, error.RepoLookupError) as err: 419 except (error.LookupError, error.RepoLookupError) as err: