##// END OF EJS Templates
ui: refactor `readconfig()` into a form that can consume resources...
ui: refactor `readconfig()` into a form that can consume resources The old form can't completely go away, because files outside of packages still need to be read. The name passed in here is a tuple of `package name, resource` as needed by the resource API. I like the idea of stating the config file is embedded in the executable by listing is as `exe!package.resource`. This would be consistent with how `debuginstall` points to the executable for the python executable, lib, and installed modules. While in practice the filesystem path is available from the backing ResourceReader when the resource is opened, it is a relative path on py2 and absolute on py3. Further, while this would show in the `hg config` output for each option if set as such here, it doesn't show in the `reading from...` line when `--debug` is used. The file isn't actually open where that prints, so there's no way I see to get that info there. So I opted for the simple prefix to distinguish resources from files. Differential Revision: https://phab.mercurial-scm.org/D7775

File last commit:

r43376:d783f945 default
r44482:5ac0e6f1 default
Show More
hgweb_mod.py
499 lines | 17.6 KiB | text/x-python | PythonLexer
Eric Hopper
Fixing up comment headers for split up code.
r2391 # hgweb/hgweb_mod.py - Web interface for a repository.
Eric Hopper
Final stage of the hgweb split up....
r2356 #
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
Thomas Arendsen Hein
Updated copyright notices and add "and others" to "hg version"
r4635 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
Eric Hopper
Final stage of the hgweb split up....
r2356 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Eric Hopper
Final stage of the hgweb split up....
r2356
Yuya Nishihara
hgweb: use absolute_import
r27046 from __future__ import absolute_import
Gregory Szorc
hgweb: use separate repo instances per thread...
r26220 import contextlib
Gregory Szorc
hgweb: extract web substitutions table generation to own function...
r26162 import os
Yuya Nishihara
hgweb: use absolute_import
r27046
from .common import (
ErrorResponse,
HTTP_BAD_REQUEST,
Gregory Szorc
hgweb: support Content Security Policy...
r30766 cspvalues,
Yuya Nishihara
hgweb: use absolute_import
r27046 permhooks,
Gregory Szorc
hgweb: send errors using new response API...
r36895 statusmessage,
Yuya Nishihara
hgweb: use absolute_import
r27046 )
Gregory Szorc
py3: manually import getattr where it is needed...
r43359 from ..pycompat import getattr
Yuya Nishihara
hgweb: use absolute_import
r27046
from .. import (
encoding,
error,
Yuya Nishihara
hgweb: load globally-enabled extensions explicitly...
r40759 extensions,
Yuya Nishihara
hgweb: make templater mostly compatible with log templates...
r36535 formatter,
Yuya Nishihara
hgweb: use absolute_import
r27046 hg,
hook,
Gregory Szorc
hgweb: profile HTTP requests...
r29787 profiling,
Augie Fackler
hgweb: extract function for loading style from request context...
r34516 pycompat,
Yuya Nishihara
hgweb: use registrar to declare "websub" template filter...
r37238 registrar,
Yuya Nishihara
hgweb: use absolute_import
r27046 repoview,
templatefilters,
templater,
Yuya Nishihara
hgweb: wrap {pathdef} with mappinglist...
r37527 templateutil,
Yuya Nishihara
hgweb: use absolute_import
r27046 ui as uimod,
util,
Gregory Szorc
wireprotoserver: rename hgweb.protocol to wireprotoserver (API)...
r35874 wireprotoserver,
Yuya Nishihara
hgweb: use absolute_import
r27046 )
from . import (
Gregory Szorc
hgweb: rename req to wsgireq...
r36822 request as requestmod,
Yuya Nishihara
hgweb: use absolute_import
r27046 webcommands,
webutil,
wsgicgi,
)
Eric Hopper
Final stage of the hgweb split up....
r2356
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
hgweb: extract function for loading style from request context...
r34516 def getstyle(req, configfn, templatepath):
styles = (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 req.qsparams.get(b'style', None),
configfn(b'web', b'style'),
b'paper',
Augie Fackler
hgweb: extract function for loading style from request context...
r34516 )
return styles, templater.stylemap(styles, templatepath)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def makebreadcrumb(url, prefix=b''):
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258 '''Return a 'URL breadcrumb' list
A 'URL breadcrumb' is a list of URL-name pairs,
corresponding to each of the path items on a URL.
This can be used to create path navigation entries.
'''
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if url.endswith(b'/'):
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258 url = url[:-1]
Angel Ezquerra
hgwebdir: use web.prefix when creating url breadcrumbs (issue3790)...
r18515 if prefix:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 url = b'/' + prefix + url
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258 relpath = url
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if relpath.startswith(b'/'):
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258 relpath = relpath[1:]
breadcrumb = []
urlel = url
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 pathitems = [b''] + relpath.split(b'/')
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258 for pathel in reversed(pathitems):
if not pathel or not urlel:
break
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 breadcrumb.append({b'url': urlel, b'name': pathel})
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258 urlel = os.path.dirname(urlel)
Yuya Nishihara
hgweb: wrap {pathdef} with mappinglist...
r37527 return templateutil.mappinglist(reversed(breadcrumb))
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
hgweb: establish class for holding per request context...
r26134 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.
"""
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
hgweb: support using new response object for web commands...
r36886 def __init__(self, app, repo, req, res):
Gregory Szorc
hg: establish a cache for localrepository instances...
r26219 self.repo = repo
Gregory Szorc
hgweb: remove proxy to hgweb instance...
r26217 self.reponame = app.reponame
Gregory Szorc
hgweb: support using new response object for web commands...
r36886 self.req = req
self.res = res
Gregory Szorc
hgweb: establish class for holding per request context...
r26134
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.maxchanges = self.configint(b'web', b'maxchanges')
self.stripecount = self.configint(b'web', b'stripes')
self.maxshortchanges = self.configint(b'web', b'maxshortchanges')
self.maxfiles = self.configint(b'web', b'maxfiles')
self.allowpull = self.configbool(b'web', b'allow-pull')
Gregory Szorc
hgweb: move some config options to requestcontext...
r26135
Gregory Szorc
hgweb: move templatepath to requestcontext...
r26163 # we use untrusted=False to prevent a repo owner from using
# web.templates in .hg/hgrc to get access to any file readable
# by the user running the CGI script
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.templatepath = self.config(b'web', b'templates', untrusted=False)
Gregory Szorc
hgweb: move templatepath to requestcontext...
r26163
Gregory Szorc
hgweb: create websubtable on requestcontext
r26164 # This object is more expensive to build than simple config values.
# It is shared across requests. The app will replace the object
# if it is updated. Since this is a reference and nothing should
# modify the underlying object, it should be constant for the lifetime
# of the request.
Gregory Szorc
hgweb: remove proxy to hgweb instance...
r26217 self.websubtable = app.websubtable
Gregory Szorc
hgweb: create websubtable on requestcontext
r26164
Gregory Szorc
hgweb: support Content Security Policy...
r30766 self.csp, self.nonce = cspvalues(self.repo.ui)
Gregory Szorc
hgweb: move some config options to requestcontext...
r26135 # Trust the settings from the .hg/hgrc files by default.
David Demelier
hgweb: use ui._unset to prevent a warning in configitems
r33328 def config(self, section, name, default=uimod._unset, untrusted=True):
Augie Fackler
formatting: blacken the codebase...
r43346 return self.repo.ui.config(section, name, default, untrusted=untrusted)
Gregory Szorc
hgweb: move some config options to requestcontext...
r26135
David Demelier
hgweb: use ui._unset to prevent a warning in configitems
r33328 def configbool(self, section, name, default=uimod._unset, untrusted=True):
Augie Fackler
formatting: blacken the codebase...
r43346 return self.repo.ui.configbool(
section, name, default, untrusted=untrusted
)
Gregory Szorc
hgweb: move some config options to requestcontext...
r26135
David Demelier
hgweb: use ui._unset to prevent a warning in configitems
r33328 def configint(self, section, name, default=uimod._unset, untrusted=True):
Augie Fackler
formatting: blacken the codebase...
r43346 return self.repo.ui.configint(
section, name, default, untrusted=untrusted
)
Gregory Szorc
hgweb: move some config options to requestcontext...
r26135
David Demelier
hgweb: use ui._unset to prevent a warning in configitems
r33328 def configlist(self, section, name, default=uimod._unset, untrusted=True):
Augie Fackler
formatting: blacken the codebase...
r43346 return self.repo.ui.configlist(
section, name, default, untrusted=untrusted
)
Gregory Szorc
hgweb: move some config options to requestcontext...
r26135
Gregory Szorc
hgweb: move archive related attributes to requestcontext...
r26136 def archivelist(self, nodeid):
Yuya Nishihara
hgweb: forward archivelist() of hgweb to webutil...
r37532 return webutil.archivelist(self.repo.ui, nodeid)
Angel Ezquerra <angel.ezquerra at gmail.com>
hgweb: add a "URL breadcrumb" to the index and repository pages...
r18258
Gregory Szorc
hgweb: expose repo name on parsedrequest...
r36884 def templater(self, req):
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183 # determine scheme, port and server name
# this is needed to create absolute urls
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 logourl = self.config(b'web', b'logourl')
logoimg = self.config(b'web', b'logoimg')
Augie Fackler
formatting: blacken the codebase...
r43346 staticurl = (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.config(b'web', b'staticurl')
or req.apppath.rstrip(b'/') + b'/static/'
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not staticurl.endswith(b'/'):
staticurl += b'/'
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183
# figure out which style to use
vars = {}
Augie Fackler
formatting: blacken the codebase...
r43346 styles, (style, mapfile) = getstyle(req, self.config, self.templatepath)
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183 if style == styles[0]:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 vars[b'style'] = style
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 sessionvars = webutil.sessionvars(vars, b'?')
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183
if not self.reponame:
Augie Fackler
formatting: blacken the codebase...
r43346 self.reponame = (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.config(b'web', b'name', b'')
Augie Fackler
formatting: blacken the codebase...
r43346 or req.reponame
or req.apppath
or self.repo.root
)
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183
Yuya Nishihara
hgweb: use registrar to declare "websub" template filter...
r37238 filters = {}
templatefilter = registrar.templatefilter(filters)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 @templatefilter(b'websub', intype=bytes)
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183 def websubfilter(text):
Yuya Nishihara
hgweb: do not import templatefilters.revescape and websub as symbol...
r27008 return templatefilters.websub(text, self.websubtable)
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183
# create the templater
Yuya Nishihara
hgweb: make templater mostly compatible with log templates...
r36535 # TODO: export all keywords: defaults = templatekw.keywords.copy()
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 defaults = {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'url': req.apppath + b'/',
b'logourl': logourl,
b'logoimg': logoimg,
b'staticurl': staticurl,
b'urlbase': req.advertisedbaseurl,
b'repo': self.reponame,
b'encoding': encoding.encoding,
b'sessionvars': sessionvars,
b'pathdef': makebreadcrumb(req.apppath),
b'style': style,
b'nonce': self.nonce,
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 }
Yuya Nishihara
hgweb: use registrar to add "motd" template keyword...
r38964 templatekeyword = registrar.templatekeyword(defaults)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 @templatekeyword(b'motd', requires=())
Yuya Nishihara
hgweb: use registrar to add "motd" template keyword...
r38964 def motd(context, mapping):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield self.config(b'web', b'motd')
Yuya Nishihara
hgweb: use registrar to add "motd" template keyword...
r38964
Yuya Nishihara
hgweb: make templater mostly compatible with log templates...
r36535 tres = formatter.templateresources(self.repo.ui, self.repo)
Augie Fackler
formatting: blacken the codebase...
r43346 tmpl = templater.templater.frommapfile(
mapfile, filters=filters, defaults=defaults, resources=tres
)
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183 return tmpl
Gregory Szorc
hgweb: add a sendtemplate() helper function...
r36899 def sendtemplate(self, name, **kwargs):
"""Helper function to send a response generated from a template."""
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 kwargs = pycompat.byteskwargs(kwargs)
self.res.setbodygen(self.tmpl.generate(name, kwargs))
Gregory Szorc
hgweb: add a sendtemplate() helper function...
r36899 return self.res.sendresponse()
Gregory Szorc
hgweb: move templater instantiation to requestcontext...
r26183
Augie Fackler
formatting: blacken the codebase...
r43346
Eric Hopper
Final stage of the hgweb split up....
r2356 class hgweb(object):
Gregory Szorc
hgweb: add some documentation...
r26132 """HTTP server for individual repositories.
Instances of this class serve HTTP responses for a particular
repository.
Instances are typically used as WSGI applications.
Some servers are multi-threaded. On these servers, there may
be multiple active threads inside __call__.
"""
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
hgweb: add baseui to hgweb entrypoint
r10994 def __init__(self, repo, name=None, baseui=None):
Augie Fackler
hgweb_mod: inform hgweb class about paths actually being bytes...
r37764 if isinstance(repo, bytes):
Matt Mackall
hgweb: add baseui to hgweb entrypoint
r10994 if baseui:
u = baseui.copy()
else:
Yuya Nishihara
ui: factor out ui.load() to create a ui without loading configs (API)...
r30559 u = uimod.ui.load()
Yuya Nishihara
hgweb: load globally-enabled extensions explicitly...
r40759 extensions.loadall(u)
Yuya Nishihara
extensions: add "uipopulate" hook, called per instance, not per process...
r40760 extensions.populateui(u)
Matt Mackall
hgweb: avoid initialization race (issue3953)
r20168 r = hg.repository(u, repo)
Eric Hopper
Final stage of the hgweb split up....
r2356 else:
Matt Mackall
hgweb: avoid config object race with hgwebdir (issue4326)...
r22087 # we trust caller to give us a private copy
Matt Mackall
hgweb: avoid initialization race (issue3953)
r20168 r = repo
Eric Hopper
Final stage of the hgweb split up....
r2356
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 r.ui.setconfig(b'ui', b'report_untrusted', b'off', b'hgweb')
r.baseui.setconfig(b'ui', b'report_untrusted', b'off', b'hgweb')
r.ui.setconfig(b'ui', b'nontty', b'true', b'hgweb')
r.baseui.setconfig(b'ui', b'nontty', b'true', b'hgweb')
Yuya Nishihara
hgweb: overwrite cwd to resolve file patterns relative to repo (issue4568)...
r26294 # resolve file patterns relative to repo root
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 r.ui.setconfig(b'ui', b'forcecwd', r.root, b'hgweb')
r.baseui.setconfig(b'ui', b'forcecwd', r.root, b'hgweb')
Yuya Nishihara
hgweb: do not try to replace signal handlers while locking...
r38158 # it's unlikely that we can replace signal handlers in WSGI server,
# and mod_wsgi issues a big warning. a plain hgweb process (with no
# threading) could replace signal handlers, but we don't bother
# conditionally enabling it.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 r.ui.setconfig(b'ui', b'signal-safe-lock', b'false', b'hgweb')
r.baseui.setconfig(b'ui', b'signal-safe-lock', b'false', b'hgweb')
Pierre-Yves David
hgewb: disable progress when serving (issue4582)...
r25488 # displaying bundling progress bar while serving feel wrong and may
# break some wsgi implementation.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 r.ui.setconfig(b'progress', b'disable', b'true', b'hgweb')
r.baseui.setconfig(b'progress', b'disable', b'true', b'hgweb')
Gregory Szorc
hgweb: use separate repo instances per thread...
r26220 self._repos = [hg.cachedlocalrepo(self._webifyrepo(r))]
self._lastrepo = self._repos[0]
Matt Mackall
hook: redirect stdout to stderr for ssh and http servers
r5833 hook.redirect(True)
Eric Hopper
Final stage of the hgweb split up....
r2356 self.reponame = name
Alexis S. L. Carvalho
use untrusted settings in hgweb...
r3555
Gregory Szorc
hgweb: create function to perform actions on new repo...
r26218 def _webifyrepo(self, repo):
repo = getwebview(repo)
self.websubtable = webutil.getwebsubs(repo)
return repo
Gregory Szorc
hgweb: use separate repo instances per thread...
r26220 @contextlib.contextmanager
def _obtainrepo(self):
"""Obtain a repo unique to the caller.
Internally we maintain a stack of cachedlocalrepo instances
to be handed out. If one is available, we pop it and return it,
ensuring it is up to date in the process. If one is not available,
we clone the most recently used repo instance and return it.
Gregory Szorc
hg: establish a cache for localrepository instances...
r26219
Gregory Szorc
hgweb: use separate repo instances per thread...
r26220 It is currently possible for the stack to grow without bounds
if the server allows infinite threads. However, servers should
have a thread limit, thus establishing our limit.
"""
if self._repos:
cached = self._repos.pop()
r, created = cached.fetch()
else:
cached = self._lastrepo.copy()
r, created = cached.fetch()
Gregory Szorc
hg: always create new localrepository instance...
r26240 if created:
r = self._webifyrepo(r)
Gregory Szorc
hgweb: use separate repo instances per thread...
r26220
self._lastrepo = cached
self.mtime = cached.mtime
try:
yield r
finally:
self._repos.append(cached)
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 def run(self):
Gregory Szorc
hgweb: add some documentation...
r26132 """Start a server from CGI environment.
Modern servers should be using WSGI and should avoid this
method, if possible.
"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not encoding.environ.get(b'GATEWAY_INTERFACE', b'').startswith(
b"CGI/1."
Augie Fackler
formatting: blacken the codebase...
r43346 ):
raise RuntimeError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"This function is only intended to be "
b"called while running as a CGI script."
Augie Fackler
formatting: blacken the codebase...
r43346 )
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 wsgicgi.launch(self)
def __call__(self, env, respond):
Gregory Szorc
hgweb: add some documentation...
r26132 """Run the WSGI application.
This may be called by multiple threads.
"""
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 req = requestmod.parserequestfromenv(env)
res = requestmod.wsgiresponse(req, respond)
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 return self.run_wsgi(req, res)
def run_wsgi(self, req, res):
Gregory Szorc
hgweb: add some documentation...
r26132 """Internal method to run the WSGI application.
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Gregory Szorc
hgweb: add some documentation...
r26132 This is typically only called by Mercurial. External consumers
should be using instances of this class as the WSGI application.
"""
Gregory Szorc
hgweb: use separate repo instances per thread...
r26220 with self._obtainrepo() as repo:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 profile = repo.ui.configbool(b'profiling', b'enabled')
profile: drop maybeprofile...
r32788 with profiling.profile(repo.ui, enabled=profile):
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 for r in self._runwsgi(req, res, repo):
Gregory Szorc
hgweb: profile HTTP requests...
r29787 yield r
Gregory Szorc
hgweb: use separate repo instances per thread...
r26220
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 def _runwsgi(self, req, res, repo):
Gregory Szorc
hgweb: support using new response object for web commands...
r36886 rctx = requestcontext(self, repo, req, res)
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Gregory Szorc
hgweb: move additional state setting outside of refresh...
r26160 # This state is global across all threads.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 encoding.encoding = rctx.config(b'web', b'encoding')
Gregory Szorc
hgweb: store the raw WSGI environment dict...
r36925 rctx.repo.ui.environ = req.rawenv
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Gregory Szorc
hgweb: support Content Security Policy...
r30766 if rctx.csp:
# hgwebdir may have added CSP header. Since we generate our own,
# replace it.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.headers[b'Content-Security-Policy'] = rctx.csp
Gregory Szorc
hgweb: support Content Security Policy...
r30766
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 # /api/* is reserved for various API implementations. Dispatch
Gregory Szorc
hgweb: don't responsd to api requests unless feature is enabled...
r37111 # accordingly. But URL paths can conflict with subrepos and virtual
# repos in hgwebdir. So until we have a workaround for this, only
# expose the URLs if the feature is enabled.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 apienabled = rctx.repo.ui.configbool(b'experimental', b'web.apiserver')
Gregory Szorc
hgweb: don't responsd to api requests unless feature is enabled...
r37111 if apienabled and req.dispatchparts and req.dispatchparts[0] == b'api':
Augie Fackler
formatting: blacken the codebase...
r43346 wireprotoserver.handlewsgiapirequest(
rctx, req, res, self.check_perm
)
Gregory Szorc
wireproto: support /api/* URL space for exposing APIs...
r37064 return res.sendresponse()
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 handled = wireprotoserver.handlewsgirequest(
Augie Fackler
formatting: blacken the codebase...
r43346 rctx, req, res, self.check_perm
)
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830 if handled:
Gregory Szorc
hgweb: create dedicated type for WSGI responses...
r36877 return res.sendresponse()
Gregory Szorc
wireprotoserver: move all wire protocol handling logic out of hgweb...
r36830
Gregory Szorc
hgweb: change how dispatch path is reported...
r36914 # Old implementations of hgweb supported dispatching the request via
# the initial query string parameter instead of using PATH_INFO.
# If PATH_INFO is present (signaled by ``req.dispatchpath`` having
# a value), we use it. Otherwise fall back to the query string.
if req.dispatchpath is not None:
Gregory Szorc
hgweb: use parsed request to construct query parameters...
r36829 query = req.dispatchpath
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 query = req.querystring.partition(b'&')[0].partition(b';')[0]
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596 # translate user-visible url structure to internal structure
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 args = query.split(b'/', 2)
if b'cmd' not in req.qsparams and args and args[0]:
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 cmd = args.pop(0)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 style = cmd.rfind(b'-')
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 if style != -1:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 req.qsparams[b'style'] = cmd[:style]
Augie Fackler
formatting: blacken the codebase...
r43346 cmd = cmd[style + 1 :]
Dirkjan Ochtman
hgweb: get rid of some nested functions
r5596
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 # avoid accepting e.g. style parameter as command
Augie Fackler
hgweb_mod: use safehasattr instead of hasattr
r14953 if util.safehasattr(webcommands, cmd):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 req.qsparams[b'cmd'] = cmd
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if cmd == b'static':
req.qsparams[b'file'] = b'/'.join(args)
Brendan Cully
hgweb: handle subdirectories within static directory
r7287 else:
if args and args[0]:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 node = args.pop(0).replace(b'%2F', b'/')
req.qsparams[b'node'] = node
Brendan Cully
hgweb: handle subdirectories within static directory
r7287 if args:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'file' in req.qsparams:
del req.qsparams[b'file']
Gregory Szorc
hgweb: set variables in qsparams...
r36880 for a in args:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 req.qsparams.add(b'file', a)
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ua = req.headers.get(b'User-Agent', b'')
if cmd == b'rev' and b'mercurial' in ua:
req.qsparams[b'style'] = b'raw'
Dirkjan Ochtman
hgweb: treat rev as raw-rev if user agent is hg
r9731
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if cmd == b'archive':
fn = req.qsparams[b'node']
Gregory Szorc
py3: finish porting iteritems() to pycompat and remove source transformer...
r43376 for type_, spec in pycompat.iteritems(webutil.archivespecs):
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591 ext = spec[2]
if fn.endswith(ext):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 req.qsparams[b'node'] = fn[: -len(ext)]
req.qsparams[b'type'] = type_
Gregory Szorc
wireprotoserver: move protocol parsing and dispatch out of hgweb...
r36002 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 cmd = req.qsparams.get(b'cmd', b'')
Dirkjan Ochtman
split out hgweb commands into a separate file, move some code around
r5591
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 # process the web interface request
Dirkjan Ochtman
hgweb: split out templater definition
r5599
try:
Gregory Szorc
hgweb: add a sendtemplate() helper function...
r36899 rctx.tmpl = rctx.templater(req)
Augie Fackler
formatting: blacken the codebase...
r43346 ctype = rctx.tmpl.render(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'mimetype', {b'encoding': encoding.encoding}
Augie Fackler
formatting: blacken the codebase...
r43346 )
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149
Mark Edgington
hgweb: allow static content when deny_read denies access
r7562 # check read permissions non-static content
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if cmd != b'static':
Gregory Szorc
hgweb: transition permissions hooks to modern request type (API)...
r36893 self.check_perm(rctx, req, None)
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if cmd == b'':
req.qsparams[b'cmd'] = rctx.tmpl.render(b'default', {})
cmd = req.qsparams[b'cmd']
Dirkjan Ochtman
hgweb: fast path for sending raw files
r5890
Gregory Szorc
hgweb: support Content Security Policy...
r30766 # Don't enable caching if using a CSP nonce because then it wouldn't
# be a nonce.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if rctx.configbool(b'web', b'cache') and not rctx.nonce:
tag = b'W/"%d"' % self.mtime
if req.headers.get(b'If-None-Match') == tag:
res.status = b'304 Not Modified'
Gregory Szorc
hgweb: discard Content-Type header for 304 responses (issue5844)...
r37846 # Content-Type may be defined globally. It isn't valid on a
# 304, so discard it.
try:
del res.headers[b'Content-Type']
except KeyError:
pass
Gregory Szorc
hgweb: refactor 304 handling code...
r36894 # Response body not allowed on 304.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.setbodybytes(b'')
Gregory Szorc
hgweb: refactor 304 handling code...
r36894 return res.sendresponse()
Gregory Szorc
hgweb: inline caching() and port to modern mechanisms...
r36885
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.headers[b'ETag'] = tag
Gregory Szorc
hgweb: inline caching() and port to modern mechanisms...
r36885
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 if cmd not in webcommands.__all__:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 msg = b'no such method: %s' % cmd
Dirkjan Ochtman
hgweb: separate protocol calls from interface calls (issue996)...
r6149 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
else:
Gregory Szorc
hgweb: support using new response object for web commands...
r36886 # Set some globals appropriate for web handlers. Commands can
# override easily enough.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.status = b'200 Script output follows'
res.headers[b'Content-Type'] = ctype
Gregory Szorc
hgweb: stop passing req and tmpl into @webcommand functions (API)...
r36903 return getattr(webcommands, cmd)(rctx)
Dirkjan Ochtman
hgweb: explicitly pass around the templater
r5600
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except (error.LookupError, error.RepoLookupError) as err:
Augie Fackler
py3: get bytes-repr of network errors portably...
r36272 msg = pycompat.bytestr(err)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if util.safehasattr(err, b'name') and not isinstance(
Augie Fackler
formatting: blacken the codebase...
r43346 err, error.ManifestLookupError
):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 msg = b'revision not found: %s' % err.name
Gregory Szorc
hgweb: send errors using new response API...
r36895
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.status = b'404 Not Found'
res.headers[b'Content-Type'] = ctype
return rctx.sendtemplate(b'error', error=msg)
Gregory Szorc
global: replace most uses of RevlogError with StorageError (API)...
r39813 except (error.RepoError, error.StorageError) as e:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.status = b'500 Internal Server Error'
res.headers[b'Content-Type'] = ctype
return rctx.sendtemplate(b'error', error=pycompat.bytestr(e))
Yuya Nishihara
hgweb: map Abort to 403 error to report inaccessible path for example...
r39506 except error.Abort as e:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.status = b'403 Forbidden'
res.headers[b'Content-Type'] = ctype
return rctx.sendtemplate(b'error', error=pycompat.bytestr(e))
Gregory Szorc
hgweb: send errors using new response API...
r36895 except ErrorResponse as e:
Sune Foldager
hgweb: propagate http headers from ErrorResponse for web interface commands...
r38326 for k, v in e.headers:
res.headers[k] = v
Gregory Szorc
hgweb: send errors using new response API...
r36895 res.status = statusmessage(e.code, pycompat.bytestr(e))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.headers[b'Content-Type'] = ctype
return rctx.sendtemplate(b'error', error=pycompat.bytestr(e))
Dirkjan Ochtman
hgweb: split out templater definition
r5599
Gregory Szorc
hgweb: establish class for holding per request context...
r26134 def check_perm(self, rctx, req, op):
Mads Kiilerich
cleanup: avoid local vars shadowing imports...
r22200 for permhook in permhooks:
Gregory Szorc
hgweb: establish class for holding per request context...
r26134 permhook(rctx, req, op)
Gregory Szorc
hgweb: extract _getview to own function...
r26208
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
hgweb: extract _getview to own function...
r26208 def getwebview(repo):
"""The 'web.view' config controls changeset filter to hgweb. Possible
values are ``served``, ``visible`` and ``all``. Default is ``served``.
The ``served`` filter only shows changesets that can be pulled from the
hgweb instance. The``visible`` filter includes secret changesets but
still excludes "hidden" one.
See the repoview module for details.
The option has been around undocumented since Mercurial 2.5, but no
user ever asked about it. So we better keep it undocumented for now."""
Boris Feld
configitems: register the 'web.view' config
r34585 # experimental config: web.view
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 viewconfig = repo.ui.config(b'web', b'view', untrusted=True)
if viewconfig == b'all':
Gregory Szorc
hgweb: extract _getview to own function...
r26208 return repo.unfiltered()
elif viewconfig in repoview.filtertable:
return repo.filtered(viewconfig)
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return repo.filtered(b'served')