##// END OF EJS Templates
share: make it possible to control the working copy format variant...
share: make it possible to control the working copy format variant A share will use the same format as its source for the store, but there are no reason to not lets it control the working copy variant at creation time. So we make it so. Differential Revision: https://phab.mercurial-scm.org/D11892

File last commit:

r48581:de2e04fe default
r49297:bf2738e0 default
Show More
hgwebdir_mod.py
597 lines | 20.1 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 from __future__ import absolute_import
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
Eric Hopper
Final stage of the hgweb split up....
r2356 class hgwebdir(object):
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.
Augie Fackler
hgwebdir: un-bytes the env dict before re-parsing env...
r37730 uenv = req.rawenv
if pycompat.ispy3:
Augie Fackler
formatting: blacken the codebase...
r43346 uenv = {
Gregory Szorc
py3: finish porting iteritems() to pycompat and remove source transformer...
r43376 k.decode('latin1'): v
for k, v in pycompat.iteritems(uenv)
Augie Fackler
formatting: blacken the codebase...
r43346 }
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
)