comparison mercurial/hgweb/hgwebdir_mod.py @ 36804:b9b968e21f78

hgweb: rename req to wsgireq We will soon introduce a parsed WSGI request object so we don't have to concern ourselves with low-level WSGI matters. Prepare for multiple request objects by renaming the existing one so it is clear it deals with WSGI. We also remove a symbol import to avoid even more naming confusion. # no-check-commit because of some new foo_bar naming that's required Differential Revision: https://phab.mercurial-scm.org/D2732
author Gregory Szorc <gregory.szorc@gmail.com>
date Thu, 08 Mar 2018 15:15:59 -0800
parents c6061cadb400
children ec46415ed826
comparison
equal deleted inserted replaced
36803:8e1556ac01bb 36804:b9b968e21f78
24 get_mtime, 24 get_mtime,
25 ismember, 25 ismember,
26 paritygen, 26 paritygen,
27 staticfile, 27 staticfile,
28 ) 28 )
29 from .request import wsgirequest
30 29
31 from .. import ( 30 from .. import (
32 configitems, 31 configitems,
33 encoding, 32 encoding,
34 error, 33 error,
41 util, 40 util,
42 ) 41 )
43 42
44 from . import ( 43 from . import (
45 hgweb_mod, 44 hgweb_mod,
45 request as requestmod,
46 webutil, 46 webutil,
47 wsgicgi, 47 wsgicgi,
48 ) 48 )
49 from ..utils import dateutil 49 from ..utils import dateutil
50 50
195 raise RuntimeError("This function is only intended to be " 195 raise RuntimeError("This function is only intended to be "
196 "called while running as a CGI script.") 196 "called while running as a CGI script.")
197 wsgicgi.launch(self) 197 wsgicgi.launch(self)
198 198
199 def __call__(self, env, respond): 199 def __call__(self, env, respond):
200 req = wsgirequest(env, respond) 200 wsgireq = requestmod.wsgirequest(env, respond)
201 return self.run_wsgi(req) 201 return self.run_wsgi(wsgireq)
202 202
203 def read_allowed(self, ui, req): 203 def read_allowed(self, ui, wsgireq):
204 """Check allow_read and deny_read config options of a repo's ui object 204 """Check allow_read and deny_read config options of a repo's ui object
205 to determine user permissions. By default, with neither option set (or 205 to determine user permissions. By default, with neither option set (or
206 both empty), allow all users to read the repo. There are two ways a 206 both empty), allow all users to read the repo. There are two ways a
207 user can be denied read access: (1) deny_read is not empty, and the 207 user can be denied read access: (1) deny_read is not empty, and the
208 user is unauthenticated or deny_read contains user (or *), and (2) 208 user is unauthenticated or deny_read contains user (or *), and (2)
209 allow_read is not empty and the user is not in allow_read. Return True 209 allow_read is not empty and the user is not in allow_read. Return True
210 if user is allowed to read the repo, else return False.""" 210 if user is allowed to read the repo, else return False."""
211 211
212 user = req.env.get('REMOTE_USER') 212 user = wsgireq.env.get('REMOTE_USER')
213 213
214 deny_read = ui.configlist('web', 'deny_read', untrusted=True) 214 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
215 if deny_read and (not user or ismember(ui, user, deny_read)): 215 if deny_read and (not user or ismember(ui, user, deny_read)):
216 return False 216 return False
217 217
220 if (not allow_read) or ismember(ui, user, allow_read): 220 if (not allow_read) or ismember(ui, user, allow_read):
221 return True 221 return True
222 222
223 return False 223 return False
224 224
225 def run_wsgi(self, req): 225 def run_wsgi(self, wsgireq):
226 profile = self.ui.configbool('profiling', 'enabled') 226 profile = self.ui.configbool('profiling', 'enabled')
227 with profiling.profile(self.ui, enabled=profile): 227 with profiling.profile(self.ui, enabled=profile):
228 for r in self._runwsgi(req): 228 for r in self._runwsgi(wsgireq):
229 yield r 229 yield r
230 230
231 def _runwsgi(self, req): 231 def _runwsgi(self, wsgireq):
232 try: 232 try:
233 self.refresh() 233 self.refresh()
234 234
235 csp, nonce = cspvalues(self.ui) 235 csp, nonce = cspvalues(self.ui)
236 if csp: 236 if csp:
237 req.headers.append(('Content-Security-Policy', csp)) 237 wsgireq.headers.append(('Content-Security-Policy', csp))
238 238
239 virtual = req.env.get("PATH_INFO", "").strip('/') 239 virtual = wsgireq.env.get("PATH_INFO", "").strip('/')
240 tmpl = self.templater(req, nonce) 240 tmpl = self.templater(wsgireq, nonce)
241 ctype = tmpl('mimetype', encoding=encoding.encoding) 241 ctype = tmpl('mimetype', encoding=encoding.encoding)
242 ctype = templater.stringify(ctype) 242 ctype = templater.stringify(ctype)
243 243
244 # a static file 244 # a static file
245 if virtual.startswith('static/') or 'static' in req.form: 245 if virtual.startswith('static/') or 'static' in wsgireq.form:
246 if virtual.startswith('static/'): 246 if virtual.startswith('static/'):
247 fname = virtual[7:] 247 fname = virtual[7:]
248 else: 248 else:
249 fname = req.form['static'][0] 249 fname = wsgireq.form['static'][0]
250 static = self.ui.config("web", "static", None, 250 static = self.ui.config("web", "static", None,
251 untrusted=False) 251 untrusted=False)
252 if not static: 252 if not static:
253 tp = self.templatepath or templater.templatepaths() 253 tp = self.templatepath or templater.templatepaths()
254 if isinstance(tp, str): 254 if isinstance(tp, str):
255 tp = [tp] 255 tp = [tp]
256 static = [os.path.join(p, 'static') for p in tp] 256 static = [os.path.join(p, 'static') for p in tp]
257 staticfile(static, fname, req) 257 staticfile(static, fname, wsgireq)
258 return [] 258 return []
259 259
260 # top-level index 260 # top-level index
261 261
262 repos = dict(self.repos) 262 repos = dict(self.repos)
263 263
264 if (not virtual or virtual == 'index') and virtual not in repos: 264 if (not virtual or virtual == 'index') and virtual not in repos:
265 req.respond(HTTP_OK, ctype) 265 wsgireq.respond(HTTP_OK, ctype)
266 return self.makeindex(req, tmpl) 266 return self.makeindex(wsgireq, tmpl)
267 267
268 # nested indexes and hgwebs 268 # nested indexes and hgwebs
269 269
270 if virtual.endswith('/index') and virtual not in repos: 270 if virtual.endswith('/index') and virtual not in repos:
271 subdir = virtual[:-len('index')] 271 subdir = virtual[:-len('index')]
272 if any(r.startswith(subdir) for r in repos): 272 if any(r.startswith(subdir) for r in repos):
273 req.respond(HTTP_OK, ctype) 273 wsgireq.respond(HTTP_OK, ctype)
274 return self.makeindex(req, tmpl, subdir) 274 return self.makeindex(wsgireq, tmpl, subdir)
275 275
276 def _virtualdirs(): 276 def _virtualdirs():
277 # Check the full virtual path, each parent, and the root ('') 277 # Check the full virtual path, each parent, and the root ('')
278 if virtual != '': 278 if virtual != '':
279 yield virtual 279 yield virtual
284 yield '' 284 yield ''
285 285
286 for virtualrepo in _virtualdirs(): 286 for virtualrepo in _virtualdirs():
287 real = repos.get(virtualrepo) 287 real = repos.get(virtualrepo)
288 if real: 288 if real:
289 req.env['REPO_NAME'] = virtualrepo 289 wsgireq.env['REPO_NAME'] = virtualrepo
290 try: 290 try:
291 # ensure caller gets private copy of ui 291 # ensure caller gets private copy of ui
292 repo = hg.repository(self.ui.copy(), real) 292 repo = hg.repository(self.ui.copy(), real)
293 return hgweb_mod.hgweb(repo).run_wsgi(req) 293 return hgweb_mod.hgweb(repo).run_wsgi(wsgireq)
294 except IOError as inst: 294 except IOError as inst:
295 msg = encoding.strtolocal(inst.strerror) 295 msg = encoding.strtolocal(inst.strerror)
296 raise ErrorResponse(HTTP_SERVER_ERROR, msg) 296 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
297 except error.RepoError as inst: 297 except error.RepoError as inst:
298 raise ErrorResponse(HTTP_SERVER_ERROR, bytes(inst)) 298 raise ErrorResponse(HTTP_SERVER_ERROR, bytes(inst))
299 299
300 # browse subdirectories 300 # browse subdirectories
301 subdir = virtual + '/' 301 subdir = virtual + '/'
302 if [r for r in repos if r.startswith(subdir)]: 302 if [r for r in repos if r.startswith(subdir)]:
303 req.respond(HTTP_OK, ctype) 303 wsgireq.respond(HTTP_OK, ctype)
304 return self.makeindex(req, tmpl, subdir) 304 return self.makeindex(wsgireq, tmpl, subdir)
305 305
306 # prefixes not found 306 # prefixes not found
307 req.respond(HTTP_NOT_FOUND, ctype) 307 wsgireq.respond(HTTP_NOT_FOUND, ctype)
308 return tmpl("notfound", repo=virtual) 308 return tmpl("notfound", repo=virtual)
309 309
310 except ErrorResponse as err: 310 except ErrorResponse as err:
311 req.respond(err, ctype) 311 wsgireq.respond(err, ctype)
312 return tmpl('error', error=err.message or '') 312 return tmpl('error', error=err.message or '')
313 finally: 313 finally:
314 tmpl = None 314 tmpl = None
315 315
316 def makeindex(self, req, tmpl, subdir=""): 316 def makeindex(self, wsgireq, tmpl, subdir=""):
317 317
318 def archivelist(ui, nodeid, url): 318 def archivelist(ui, nodeid, url):
319 allowed = ui.configlist("web", "allow_archive", untrusted=True) 319 allowed = ui.configlist("web", "allow_archive", untrusted=True)
320 archives = [] 320 archives = []
321 for typ, spec in hgweb_mod.archivespecs.iteritems(): 321 for typ, spec in hgweb_mod.archivespecs.iteritems():
367 except (IOError, error.RepoError): 367 except (IOError, error.RepoError):
368 pass 368 pass
369 369
370 parts = [name] 370 parts = [name]
371 parts.insert(0, '/' + subdir.rstrip('/')) 371 parts.insert(0, '/' + subdir.rstrip('/'))
372 if req.env['SCRIPT_NAME']: 372 if wsgireq.env['SCRIPT_NAME']:
373 parts.insert(0, req.env['SCRIPT_NAME']) 373 parts.insert(0, wsgireq.env['SCRIPT_NAME'])
374 url = re.sub(r'/+', '/', '/'.join(parts) + '/') 374 url = re.sub(r'/+', '/', '/'.join(parts) + '/')
375 375
376 # show either a directory entry or a repository 376 # show either a directory entry or a repository
377 if directory: 377 if directory:
378 # get the directory's time information 378 # get the directory's time information
411 return u.config(section, name, default, untrusted=True) 411 return u.config(section, name, default, untrusted=True)
412 412
413 if u.configbool("web", "hidden", untrusted=True): 413 if u.configbool("web", "hidden", untrusted=True):
414 continue 414 continue
415 415
416 if not self.read_allowed(u, req): 416 if not self.read_allowed(u, wsgireq):
417 continue 417 continue
418 418
419 # update time with local timezone 419 # update time with local timezone
420 try: 420 try:
421 r = hg.repository(self.ui, path) 421 r = hg.repository(self.ui, path)
463 yield row 463 yield row
464 464
465 self.refresh() 465 self.refresh()
466 sortable = ["name", "description", "contact", "lastchange"] 466 sortable = ["name", "description", "contact", "lastchange"]
467 sortcolumn, descending = sortdefault 467 sortcolumn, descending = sortdefault
468 if 'sort' in req.form: 468 if 'sort' in wsgireq.form:
469 sortcolumn = req.form['sort'][0] 469 sortcolumn = wsgireq.form['sort'][0]
470 descending = sortcolumn.startswith('-') 470 descending = sortcolumn.startswith('-')
471 if descending: 471 if descending:
472 sortcolumn = sortcolumn[1:] 472 sortcolumn = sortcolumn[1:]
473 if sortcolumn not in sortable: 473 if sortcolumn not in sortable:
474 sortcolumn = "" 474 sortcolumn = ""
477 "%s%s" % ((not descending and column == sortcolumn) 477 "%s%s" % ((not descending and column == sortcolumn)
478 and "-" or "", column)) 478 and "-" or "", column))
479 for column in sortable] 479 for column in sortable]
480 480
481 self.refresh() 481 self.refresh()
482 self.updatereqenv(req.env) 482 self.updatereqenv(wsgireq.env)
483 483
484 return tmpl("index", entries=entries, subdir=subdir, 484 return tmpl("index", entries=entries, subdir=subdir,
485 pathdef=hgweb_mod.makebreadcrumb('/' + subdir, self.prefix), 485 pathdef=hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
486 sortcolumn=sortcolumn, descending=descending, 486 sortcolumn=sortcolumn, descending=descending,
487 **dict(sort)) 487 **dict(sort))
488 488
489 def templater(self, req, nonce): 489 def templater(self, wsgireq, nonce):
490 490
491 def motd(**map): 491 def motd(**map):
492 if self.motd is not None: 492 if self.motd is not None:
493 yield self.motd 493 yield self.motd
494 else: 494 else:
495 yield config('web', 'motd') 495 yield config('web', 'motd')
496 496
497 def config(section, name, default=uimod._unset, untrusted=True): 497 def config(section, name, default=uimod._unset, untrusted=True):
498 return self.ui.config(section, name, default, untrusted) 498 return self.ui.config(section, name, default, untrusted)
499 499
500 self.updatereqenv(req.env) 500 self.updatereqenv(wsgireq.env)
501 501
502 url = req.env.get('SCRIPT_NAME', '') 502 url = wsgireq.env.get('SCRIPT_NAME', '')
503 if not url.endswith('/'): 503 if not url.endswith('/'):
504 url += '/' 504 url += '/'
505 505
506 vars = {} 506 vars = {}
507 styles, (style, mapfile) = hgweb_mod.getstyle(req, config, 507 styles, (style, mapfile) = hgweb_mod.getstyle(wsgireq, config,
508 self.templatepath) 508 self.templatepath)
509 if style == styles[0]: 509 if style == styles[0]:
510 vars['style'] = style 510 vars['style'] = style
511 511
512 start = r'&' if url[-1] == r'?' else r'?' 512 start = r'&' if url[-1] == r'?' else r'?'