##// END OF EJS Templates
pycompat: drop usage of hasattr/getattr/setattr/delatt proxy...
pycompat: drop usage of hasattr/getattr/setattr/delatt proxy The function remains to ease extensions transition, but we no longer use them in core.

File last commit:

r49801:642e31cb default
r51822:18c8c189 default
Show More
hgwebdir_mod.py
593 lines | 19.9 KiB | text/x-python | PythonLexer
Eric Hopper
Fixing up comment headers for split up code.
r2391 # hgweb/hgwebdir_mod.py - Web interface for a directory of repositories.
Eric Hopper
Final stage of the hgweb split up....
r2356 #
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 # Copyright 2005, 2006 Olivia Mackall <olivia@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
Gregory Szorc
hgweb: garbage collect on every request...
r36929 import gc
Yuya Nishihara
hgweb: use absolute_import
r27046 import os
import time
from ..i18n import _
from .common import (
ErrorResponse,
HTTP_SERVER_ERROR,
Gregory Szorc
hgweb: support Content Security Policy...
r30766 cspvalues,
Yuya Nishihara
hgweb: use absolute_import
r27046 get_contact,
get_mtime,
ismember,
paritygen,
staticfile,
Gregory Szorc
hgweb: port to new response API...
r36923 statusmessage,
Yuya Nishihara
hgweb: use absolute_import
r27046 )
from .. import (
Boris Feld
configitems: register the 'web.refreshinterval' config
r34241 configitems,
Yuya Nishihara
hgweb: use absolute_import
r27046 encoding,
error,
Yuya Nishihara
hgweb: load globally-enabled extensions explicitly...
r40759 extensions,
Yuya Nishihara
hgweb: use absolute_import
r27046 hg,
Martin von Zweigbergk
utils: move finddirs() to pathutil...
r44032 pathutil,
Gregory Szorc
hgweb: profile HTTP requests...
r29787 profiling,
Yuya Nishihara
py3: remove use of str() in hgwebdir...
r34354 pycompat,
config: also respect HGRCSKIPREPO in hgwebdir_mod...
r44729 rcutil,
Yuya Nishihara
hgweb: use registrar to add "motd" template keyword...
r38964 registrar,
Yuya Nishihara
hgweb: use absolute_import
r27046 scmutil,
templater,
Yuya Nishihara
hgwebdir: wrap {entries} with mappinggenerator...
r37526 templateutil,
Yuya Nishihara
hgweb: use absolute_import
r27046 ui as uimod,
util,
)
from . import (
hgweb_mod,
Gregory Szorc
hgweb: rename req to wsgireq...
r36822 request as requestmod,
Yuya Nishihara
hgweb: use absolute_import
r27046 webutil,
wsgicgi,
)
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 from ..utils import dateutil
Eric Hopper
Final stage of the hgweb split up....
r2356
Augie Fackler
formatting: blacken the codebase...
r43346
Dirkjan Ochtman
hgweb: some cleanups in hgwebdir, remove double defaults...
r8215 def cleannames(items):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return [(util.pconvert(name).strip(b'/'), path) for name, path in items]
Eric Hopper
Final stage of the hgweb split up....
r2356
Augie Fackler
formatting: blacken the codebase...
r43346
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 def findrepos(paths):
Dirkjan Ochtman
hgweb: use a tuple-list instead of dictionary for append-only store
r9723 repos = []
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 for prefix, root in cleannames(paths):
roothead, roottail = os.path.split(root)
Mads Kiilerich
help: improve hgweb help...
r17104 # "foo = /bar/*" or "foo = /bar/**" lets every repo /bar/N in or below
# /bar/ be served as as foo/N .
# '*' will not search inside dirs with .hg (except .hg/patches),
# '**' will search inside dirs with .hg (and thus also find subrepos).
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 recurse = {b'*': False, b'**': True}[roottail]
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 except KeyError:
Dirkjan Ochtman
hgweb: use a tuple-list instead of dictionary for append-only store
r9723 repos.append((prefix, root))
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 continue
windows: use abspath in hgwebdir...
r48427 roothead = os.path.normpath(util.abspath(roothead))
Adrian Buehlmann
move walkrepos from util to scmutil
r13975 paths = scmutil.walkrepos(roothead, followsym=True, recurse=recurse)
Mads Kiilerich
hgweb: doctest of url creation from wildcard expansion
r13402 repos.extend(urlrepos(prefix, roothead, paths))
Dirkjan Ochtman
hgweb: use a tuple-list instead of dictionary for append-only store
r9723 return repos
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529
Augie Fackler
formatting: blacken the codebase...
r43346
Mads Kiilerich
hgweb: doctest of url creation from wildcard expansion
r13402 def urlrepos(prefix, roothead, paths):
"""yield url paths and filesystem paths from a list of repo paths
Patrick Mezard
test-doctest: handle unix/windows path discrepancies
r13538 >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> conv(urlrepos(b'hg', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt']))
Mads Kiilerich
hgweb: make paths wildcards expanding in a repo root match repo correctly...
r13403 [('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> conv(urlrepos(b'', b'/opt', [b'/opt/r', b'/opt/r/r', b'/opt']))
Mads Kiilerich
hgweb: doctest of url creation from wildcard expansion
r13402 [('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
"""
for path in paths:
path = os.path.normpath(path)
Augie Fackler
formatting: blacken the codebase...
r43346 yield (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 prefix + b'/' + util.pconvert(path[len(roothead) :]).lstrip(b'/')
).strip(b'/'), path
Augie Fackler
formatting: blacken the codebase...
r43346
Mads Kiilerich
hgweb: doctest of url creation from wildcard expansion
r13402
Gregory Szorc
hgweb: move readallowed to a standalone function...
r36906 def readallowed(ui, req):
"""Check allow_read and deny_read config options of a repo's ui object
to determine user permissions. By default, with neither option set (or
both empty), allow all users to read the repo. There are two ways a
user can be denied read access: (1) deny_read is not empty, and the
user is unauthenticated or deny_read contains user (or *), and (2)
allow_read is not empty and the user is not in allow_read. Return True
if user is allowed to read the repo, else return False."""
user = req.remoteuser
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 deny_read = ui.configlist(b'web', b'deny_read', untrusted=True)
Gregory Szorc
hgweb: move readallowed to a standalone function...
r36906 if deny_read and (not user or ismember(ui, user, deny_read)):
return False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 allow_read = ui.configlist(b'web', b'allow_read', untrusted=True)
Gregory Szorc
hgweb: move readallowed to a standalone function...
r36906 # by default, allow reading if no allow_read option has been set
if not allow_read or ismember(ui, user, allow_read):
return True
return False
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def rawindexentries(ui, repos, req, subdir=b''):
descend = ui.configbool(b'web', b'descend')
collapse = ui.configbool(b'web', b'collapse')
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 seenrepos = set()
seendirs = set()
for name, path in repos:
if not name.startswith(subdir):
continue
Augie Fackler
formatting: blacken the codebase...
r43346 name = name[len(subdir) :]
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 directory = False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'/' in name:
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 if not descend:
continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 nameparts = name.split(b'/')
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 rootname = nameparts[0]
if not collapse:
pass
elif rootname in seendirs:
continue
elif rootname in seenrepos:
pass
else:
directory = True
name = rootname
# redefine the path to refer to the directory
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 discarded = b'/'.join(nameparts[1:])
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908
# remove name parts plus accompanying slash
Augie Fackler
formatting: blacken the codebase...
r43346 path = path[: -len(discarded) - 1]
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908
try:
Martin von Zweigbergk
cleanup: delete lots of unused local variables...
r41401 hg.repository(ui, path)
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 directory = False
except (IOError, error.RepoError):
pass
Gregory Szorc
hgweb: rewrite path generation for index entries...
r36918 parts = [
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 req.apppath.strip(b'/'),
subdir.strip(b'/'),
name.strip(b'/'),
Gregory Szorc
hgweb: rewrite path generation for index entries...
r36918 ]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 url = b'/' + b'/'.join(p for p in parts if p) + b'/'
Matt Mackall
hgweb: extract the path logic from updatereqenv and add doctests
r15003
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 # show either a directory entry or a repository
if directory:
# get the directory's time information
try:
d = (get_mtime(path), dateutil.makedate()[1])
except OSError:
continue
# add '/' to the name to make it obvious that
# the entry is a directory, not a regular repository
Augie Fackler
formatting: blacken the codebase...
r43346 row = {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'contact': b"",
b'contact_sort': b"",
b'name': name + b'/',
b'name_sort': name,
b'url': url,
b'description': b"",
b'description_sort': b"",
b'lastchange': d,
b'lastchange_sort': d[1] - d[0],
b'archives': templateutil.mappinglist([]),
b'isdirectory': True,
b'labels': templateutil.hybridlist([], name=b'label'),
Augie Fackler
formatting: blacken the codebase...
r43346 }
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908
seendirs.add(name)
yield row
continue
u = ui.copy()
config: also respect HGRCSKIPREPO in hgwebdir_mod...
r44729 if rcutil.use_repo_hgrc():
try:
u.readconfig(os.path.join(path, b'.hg', b'hgrc'))
except Exception as e:
u.warn(_(b'error reading %s/.hg/hgrc: %s\n') % (path, e))
continue
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908
def get(section, name, default=uimod._unset):
return u.config(section, name, default, untrusted=True)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if u.configbool(b"web", b"hidden", untrusted=True):
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 continue
if not readallowed(u, req):
continue
Matt Mackall
hgweb: extract the path logic from updatereqenv and add doctests
r15003
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 # update time with local timezone
try:
r = hg.repository(ui, path)
except IOError:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 u.warn(_(b'error accessing repository at %s\n') % path)
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 continue
except error.RepoError:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 u.warn(_(b'error accessing repository at %s\n') % path)
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 continue
try:
d = (get_mtime(r.spath), dateutil.makedate()[1])
except OSError:
continue
contact = get_contact(get)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 description = get(b"web", b"description")
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908 seenrepos.add(name)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 name = get(b"web", b"name", name)
labels = u.configlist(b'web', b'labels', untrusted=True)
Augie Fackler
formatting: blacken the codebase...
r43346 row = {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'contact': contact or b"unknown",
b'contact_sort': contact.upper() or b"unknown",
b'name': name,
b'name_sort': name,
b'url': url,
b'description': description or b"unknown",
b'description_sort': description.upper() or b"unknown",
b'lastchange': d,
b'lastchange_sort': d[1] - d[0],
b'archives': webutil.archivelist(u, b"tip", url),
b'isdirectory': None,
b'labels': templateutil.hybridlist(labels, name=b'label'),
Augie Fackler
formatting: blacken the codebase...
r43346 }
Gregory Szorc
hgweb: move rawentries() to a standalone function...
r36908
yield row
Augie Fackler
formatting: blacken the codebase...
r43346
def _indexentriesgen(
context, ui, repos, req, stripecount, sortcolumn, descending, subdir
):
Gregory Szorc
hgweb: don't pass wsgireq to makeindex and other functions...
r36920 rows = rawindexentries(ui, repos, req, subdir=subdir)
Gregory Szorc
hgweb: extract entries() to standalone function...
r36909
sortdefault = None, False
if sortcolumn and sortdefault != (sortcolumn, descending):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 sortkey = b'%s_sort' % sortcolumn
Augie Fackler
formatting: blacken the codebase...
r43346 rows = sorted(rows, key=lambda x: x[sortkey], reverse=descending)
Gregory Szorc
hgweb: extract entries() to standalone function...
r36909
for row, parity in zip(rows, paritygen(stripecount)):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 row[b'parity'] = parity
Gregory Szorc
hgweb: extract entries() to standalone function...
r36909 yield row
Matt Mackall
hgweb: extract the path logic from updatereqenv and add doctests
r15003
Augie Fackler
formatting: blacken the codebase...
r43346
def indexentries(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ui, repos, req, stripecount, sortcolumn=b'', descending=False, subdir=b''
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Yuya Nishihara
hgwebdir: wrap {entries} with mappinggenerator...
r37526 args = (ui, repos, req, stripecount, sortcolumn, descending, subdir)
return templateutil.mappinggenerator(_indexentriesgen, args=args)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class hgwebdir:
Gregory Szorc
hgweb: add some documentation...
r26132 """HTTP server for multiple repositories.
Given a configuration, different repositories will be served depending
on the request path.
Instances are typically used as WSGI applications.
"""
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
hgweb: kill parentui references
r8191 def __init__(self, conf, baseui=None):
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.conf = conf
self.baseui = baseui
Gregory Szorc
hgweb: make refresh interval configurable...
r26072 self.ui = None
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.lastrefresh = 0
Thomas Arendsen Hein
Do not overwrite motd attribute of hgwebdir instances on refresh....
r9903 self.motd = None
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.refresh()
Georges Racinet
hgwebdir: avoid systematic full garbage collection...
r48581 self.requests_count = 0
Yuya Nishihara
hgweb: load globally-enabled extensions explicitly...
r40759 if not baseui:
# set up environment for new ui
extensions.loadall(self.ui)
Yuya Nishihara
extensions: add "uipopulate" hook, called per instance, not per process...
r40760 extensions.populateui(self.ui)
Eric Hopper
Final stage of the hgweb split up....
r2356
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 def refresh(self):
Gregory Szorc
hgweb: make refresh interval configurable...
r26072 if self.ui:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 refreshinterval = self.ui.configint(b'web', b'refreshinterval')
Boris Feld
configitems: register the 'web.refreshinterval' config
r34241 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 item = configitems.coreitems[b'web'][b'refreshinterval']
Boris Feld
configitems: register the 'web.refreshinterval' config
r34241 refreshinterval = item.default
Gregory Szorc
hgweb: make refresh interval configurable...
r26072
# refreshinterval <= 0 means to always refresh.
Augie Fackler
formatting: blacken the codebase...
r43346 if (
refreshinterval > 0
and self.lastrefresh + refreshinterval > time.time()
):
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 return
if self.baseui:
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239 u = self.baseui.copy()
Eric Hopper
Final stage of the hgweb split up....
r2356 else:
Yuya Nishihara
ui: factor out ui.load() to create a ui without loading configs (API)...
r30559 u = uimod.ui.load()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 u.setconfig(b'ui', b'report_untrusted', b'off', b'hgwebdir')
u.setconfig(b'ui', b'nontty', b'true', b'hgwebdir')
Pierre-Yves David
hgewb: disable progress when serving (issue4582)...
r25488 # displaying bundling progress bar while serving feels wrong and may
# break some wsgi implementations.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 u.setconfig(b'progress', b'disable', b'true', b'hgweb')
Matt Mackall
ui: refactor option setting...
r8136
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 if not isinstance(self.conf, (dict, list, tuple)):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 map = {b'paths': b'hgweb-paths'}
Matt Mackall
hgweb: abort if config file isn't found
r13214 if not os.path.exists(self.conf):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'config file %s not found!') % self.conf)
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239 u.readconfig(self.conf, remap=map, trust=True)
timeless
hgweb: support multiple directories for the same path...
r13667 paths = []
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for name, ignored in u.configitems(b'hgweb-paths'):
for path in u.configlist(b'hgweb-paths', name):
timeless
hgweb: support multiple directories for the same path...
r13667 paths.append((name, path))
Jeremy Whitlock
hgweb: make hgwebdir handle dict/list paths the same as config paths...
r8529 elif isinstance(self.conf, (list, tuple)):
paths = self.conf
elif isinstance(self.conf, dict):
paths = self.conf.items()
Yuya Nishihara
extensions: add "uipopulate" hook, called per instance, not per process...
r40760 extensions.populateui(u)
Alexander Solovyov
hgwebdir: read --webdir-conf as actual configuration to ui (issue1586)...
r8345
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239 repos = findrepos(paths)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for prefix, root in u.configitems(b'collections'):
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239 prefix = util.pconvert(prefix)
Adrian Buehlmann
move walkrepos from util to scmutil
r13975 for path in scmutil.walkrepos(root, followsym=True):
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239 repo = os.path.normpath(path)
name = util.pconvert(repo)
if name.startswith(prefix):
Augie Fackler
formatting: blacken the codebase...
r43346 name = name[len(prefix) :]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 repos.append((name.lstrip(b'/'), repo))
Matt Mackall
hgweb: fix race in refreshing repo list (issue2188)
r11239
self.repos = repos
self.ui = u
Georges Racinet
hgwebdir: avoid systematic full garbage collection...
r48581 self.gc_full_collect_rate = self.ui.configint(
b'experimental', b'web.full-garbage-collection-rate'
)
self.gc_full_collections_done = 0
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 encoding.encoding = self.ui.config(b'web', b'encoding')
self.style = self.ui.config(b'web', b'style')
self.templatepath = self.ui.config(
b'web', b'templates', untrusted=False
)
self.stripecount = self.ui.config(b'web', b'stripes')
Dirkjan Ochtman
hgweb: extract config values after reading webdir-config
r8621 if self.stripecount:
self.stripecount = int(self.stripecount)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 prefix = self.ui.config(b'web', b'prefix')
if prefix.startswith(b'/'):
Angel Ezquerra
hgwebdir: use web.prefix when creating url breadcrumbs (issue3790)...
r18515 prefix = prefix[1:]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if prefix.endswith(b'/'):
Angel Ezquerra
hgwebdir: use web.prefix when creating url breadcrumbs (issue3790)...
r18515 prefix = prefix[:-1]
self.prefix = prefix
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.lastrefresh = time.time()
Eric Hopper
Final stage of the hgweb split up....
r2356
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535 def run(self):
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
Less indirection in the WSGI web interface. This simplifies some code, and makes it more compliant with WSGI.
r5566 wsgicgi.launch(self)
def __call__(self, env, respond):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 baseurl = self.ui.config(b'web', b'baseurl')
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 req = requestmod.parserequestfromenv(env, altbaseurl=baseurl)
res = requestmod.wsgiresponse(req, respond)
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 return self.run_wsgi(req, res)
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 def run_wsgi(self, req, res):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 profile = self.ui.configbool(b'profiling', b'enabled')
profile: drop maybeprofile...
r32788 with profiling.profile(self.ui, enabled=profile):
Gregory Szorc
hgweb: garbage collect on every request...
r36929 try:
Augie Fackler
merge with stable
r36996 for r in self._runwsgi(req, res):
Gregory Szorc
hgweb: garbage collect on every request...
r36929 yield r
finally:
# There are known cycles in localrepository that prevent
# those objects (and tons of held references) from being
Georges Racinet
hgwebdir: avoid systematic full garbage collection...
r48581 # collected through normal refcounting.
# In some cases, the resulting memory consumption can
# be tamed by performing explicit garbage collections.
# In presence of actual leaks or big long-lived caches, the
# impact on performance of such collections can become a
# problem, hence the rate shouldn't be set too low.
# See "Collecting the oldest generation" in
# https://devguide.python.org/garbage_collector
# for more about such trade-offs.
rate = self.gc_full_collect_rate
# this is not thread safe, but the consequence (skipping
# a garbage collection) is arguably better than risking
# to have several threads perform a collection in parallel
# (long useless wait on all threads).
self.requests_count += 1
if rate > 0 and self.requests_count % rate == 0:
gc.collect()
self.gc_full_collections_done += 1
else:
gc.collect(generation=1)
Gregory Szorc
hgweb: abstract call to hgwebdir wsgi function...
r29786
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 def _runwsgi(self, req, res):
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 try:
Matt Mackall
hgweb: use try/except/finally
r25083 self.refresh()
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603
Gregory Szorc
hgweb: support Content Security Policy...
r30766 csp, nonce = cspvalues(self.ui)
if csp:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.headers[b'Content-Security-Policy'] = csp
Gregory Szorc
hgweb: support Content Security Policy...
r30766
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 virtual = req.dispatchpath.strip(b'/')
Gregory Szorc
hgweb: support Content Security Policy...
r30766 tmpl = self.templater(req, nonce)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ctype = tmpl.render(b'mimetype', {b'encoding': encoding.encoding})
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Gregory Szorc
hgweb: port static file handling to new response API...
r36889 # Global defaults. These can be overridden by any handler.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.status = b'200 Script output follows'
res.headers[b'Content-Type'] = ctype
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Matt Mackall
hgweb: use try/except/finally
r25083 # a static file
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if virtual.startswith(b'static/') or b'static' in req.qsparams:
if virtual.startswith(b'static/'):
Matt Mackall
hgweb: use try/except/finally
r25083 fname = virtual[7:]
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fname = req.qsparams[b'static']
static = self.ui.config(b"web", b"static", untrusted=False)
Martin von Zweigbergk
hgweb: let staticfile() look up path from default location unless provided...
r45938 staticfile(self.templatepath, static, fname, res)
Gregory Szorc
hgweb: port static file handling to new response API...
r36889 return res.sendresponse()
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603
Matt Mackall
hgweb: use try/except/finally
r25083 # top-level index
Matt Harbison
hgwebdir: add support for explicit index files...
r31482
repos = dict(self.repos)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if (not virtual or virtual == b'index') and virtual not in repos:
Gregory Szorc
hgweb: use modern response type for index generation...
r36921 return self.makeindex(req, res, tmpl)
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601
Matt Mackall
hgweb: use try/except/finally
r25083 # nested indexes and hgwebs
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if virtual.endswith(b'/index') and virtual not in repos:
subdir = virtual[: -len(b'index')]
Matt Harbison
hgwebdir: add support for explicit index files...
r31482 if any(r.startswith(subdir) for r in repos):
Gregory Szorc
hgweb: use modern response type for index generation...
r36921 return self.makeindex(req, res, tmpl, subdir)
Matt Harbison
hgwebdir: add support for explicit index files...
r31482
Matt Harbison
hgwebdir: allow a repository to be hosted at "/"...
r32004 def _virtualdirs():
Martin von Zweigbergk
util: make util.dirs() and util.finddirs() include root directory (API)...
r42530 # Check the full virtual path, and each parent
yield virtual
Martin von Zweigbergk
utils: move finddirs() to pathutil...
r44032 for p in pathutil.finddirs(virtual):
Martin von Zweigbergk
util: make util.dirs() and util.finddirs() include root directory (API)...
r42530 yield p
Matt Harbison
hgwebdir: allow a repository to be hosted at "/"...
r32004
for virtualrepo in _virtualdirs():
Matt Mackall
hgweb: use try/except/finally
r25083 real = repos.get(virtualrepo)
if real:
Gregory Szorc
hgweb: refactor repository name URL parsing...
r36913 # Re-parse the WSGI environment to take into account our
# repository path component.
Gregory Szorc
hgweb: simplify uenv assignment...
r49759 uenv = {
k.decode('latin1'): v for k, v in req.rawenv.items()
}
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 req = requestmod.parserequestfromenv(
Augie Fackler
formatting: blacken the codebase...
r43346 uenv,
reponame=virtualrepo,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 altbaseurl=self.ui.config(b'web', b'baseurl'),
Gregory Szorc
hgweb: reuse body file object when hgwebdir calls hgweb (issue5851)...
r37836 # Reuse wrapped body file object otherwise state
# tracking can get confused.
Augie Fackler
formatting: blacken the codebase...
r43346 bodyfh=req.bodyfh,
)
Matt Mackall
hgweb: use try/except/finally
r25083 try:
# ensure caller gets private copy of ui
repo = hg.repository(self.ui.copy(), real)
Gregory Szorc
hgweb: remove wsgirequest (API)...
r36928 return hgweb_mod.hgweb(repo).run_wsgi(req, res)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as inst:
Augie Fackler
python3: wrap all uses of <exception>.strerror with strtolocal...
r34024 msg = encoding.strtolocal(inst.strerror)
Matt Mackall
hgweb: use try/except/finally
r25083 raise ErrorResponse(HTTP_SERVER_ERROR, msg)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.RepoError as inst:
Yuya Nishihara
py3: remove use of str() in hgwebdir...
r34354 raise ErrorResponse(HTTP_SERVER_ERROR, bytes(inst))
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601
Matt Mackall
hgweb: use try/except/finally
r25083 # browse subdirectories
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 subdir = virtual + b'/'
Matt Mackall
hgweb: use try/except/finally
r25083 if [r for r in repos if r.startswith(subdir)]:
Gregory Szorc
hgweb: use modern response type for index generation...
r36921 return self.makeindex(req, res, tmpl, subdir)
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603
Matt Mackall
hgweb: use try/except/finally
r25083 # prefixes not found
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.status = b'404 Not Found'
res.setbodygen(tmpl.generate(b'notfound', {b'repo': virtual}))
Gregory Szorc
hgweb: port to new response API...
r36923 return res.sendresponse()
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Gregory Szorc
hgweb: port to new response API...
r36923 except ErrorResponse as e:
res.status = statusmessage(e.code, pycompat.bytestr(e))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.setbodygen(
tmpl.generate(b'error', {b'error': e.message or b''})
)
Gregory Szorc
hgweb: port to new response API...
r36923 return res.sendresponse()
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 finally:
Matt Harbison
hgweb: delete local variable instead of setting it to `None`...
r44438 del tmpl
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def makeindex(self, req, res, tmpl, subdir=b""):
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.refresh()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 sortable = [b"name", b"description", b"contact", b"lastchange"]
Gregory Szorc
hgweb: extract entries() to standalone function...
r36909 sortcolumn, descending = None, False
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'sort' in req.qsparams:
sortcolumn = req.qsparams[b'sort']
descending = sortcolumn.startswith(b'-')
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 if descending:
sortcolumn = sortcolumn[1:]
if sortcolumn not in sortable:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 sortcolumn = b""
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841
Augie Fackler
formatting: blacken the codebase...
r43346 sort = [
(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"sort_%s" % column,
b"%s%s"
Augie Fackler
formatting: blacken the codebase...
r43346 % (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (not descending and column == sortcolumn) and b"-" or b"",
Augie Fackler
formatting: blacken the codebase...
r43346 column,
),
)
for column in sortable
]
Dirkjan Ochtman
hgweb: move HTTP content types out of header templates...
r5928
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.refresh()
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221
Augie Fackler
formatting: blacken the codebase...
r43346 entries = indexentries(
self.ui,
self.repos,
req,
self.stripecount,
sortcolumn=sortcolumn,
descending=descending,
subdir=subdir,
)
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 mapping = {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'entries': entries,
b'subdir': subdir,
b'pathdef': hgweb_mod.makebreadcrumb(b'/' + subdir, self.prefix),
b'sortcolumn': sortcolumn,
b'descending': descending,
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 }
mapping.update(sort)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 res.setbodygen(tmpl.generate(b'index', mapping))
Gregory Szorc
hgweb: use modern response type for index generation...
r36921 return res.sendresponse()
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
Gregory Szorc
hgweb: support Content Security Policy...
r30766 def templater(self, req, nonce):
Martin von Zweigbergk
hgweb: remove some accesses to private member uimod._unset...
r45874 def config(*args, **kwargs):
kwargs.setdefault('untrusted', True)
return self.ui.config(*args, **kwargs)
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
Dirkjan Ochtman
hgweb: use new sessionvars code in hgwebdir, too
r8216 vars = {}
Martin von Zweigbergk
hgweb: enable reading styles from resources in frozen binaries...
r45878 styles, (style, mapfile, fp) = hgweb_mod.getstyle(
Augie Fackler
formatting: blacken the codebase...
r43346 req, config, self.templatepath
)
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 if style == styles[0]:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 vars[b'style'] = style
Matt Mackall
many, many trivial check-code fixups
r10282
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 sessionvars = webutil.sessionvars(vars, b'?')
logourl = config(b'web', b'logourl')
logoimg = config(b'web', b'logoimg')
Augie Fackler
formatting: blacken the codebase...
r43346 staticurl = (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 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'/'
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
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"encoding": encoding.encoding,
b"url": req.apppath + b'/',
b"logourl": logourl,
b"logoimg": logoimg,
b"staticurl": staticurl,
b"sessionvars": sessionvars,
b"style": style,
b"nonce": 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):
if self.motd is not None:
yield self.motd
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield config(b'web', b'motd')
Yuya Nishihara
hgweb: use registrar to add "motd" template keyword...
r38964
Martin von Zweigbergk
hgweb: enable reading styles from resources in frozen binaries...
r45878 return templater.templater.frommapfile(
mapfile, fp=fp, defaults=defaults
)