##// END OF EJS Templates
tests: add tests for poorly behaving HTTP server...
tests: add tests for poorly behaving HTTP server I've spent several hours over the past few weeks investigating networking failures involving hg.mozilla.org. As part of this, it has become clear that the Mercurial client's error handling when it encounters network failures is far from robust. To prove this is true, I've devised a battery of tests simulating various network failures, notably premature connection closes. To achieve this, I've implemented an extension that monkeypatches the built-in HTTP server and hooks in at the socket level and allows various events to occur based on config options. For example, you can refuse to accept() a client socket or you can close() the socket after N bytes have been sent or received. The latter effectively simulates an unexpected connection drop (and these occur all the time in the real world). The new test file launches servers exhibiting various "bad" behaviors and points a client at them. As the many TODO comments in the test call attention to, Mercurial often displays unhelpful errors when network-related failures occur. This makes it difficult for users to understand what's going on and difficult for server administrators to pinpoint root causes without packet tracing. Upcoming patches will attempt to fix these error handling deficiencies.

File last commit:

r31482:da7d1932 default
r32001:c85f19c6 default
Show More
hgwebdir_mod.py
535 lines | 19.0 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>
Vadim Gelfer
update copyrights.
r2859 # Copyright 2005, 2006 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
import os
import re
import time
from ..i18n import _
from .common import (
ErrorResponse,
HTTP_NOT_FOUND,
HTTP_OK,
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,
)
from .request import wsgirequest
from .. import (
encoding,
error,
hg,
Gregory Szorc
hgweb: profile HTTP requests...
r29787 profiling,
Yuya Nishihara
hgweb: use absolute_import
r27046 scmutil,
templater,
ui as uimod,
util,
)
from . import (
hgweb_mod,
webutil,
wsgicgi,
)
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
hgweb: some cleanups in hgwebdir, remove double defaults...
r8215 def cleannames(items):
return [(util.pconvert(name).strip('/'), path) for name, path in items]
Eric Hopper
Final stage of the hgweb split up....
r2356
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:
recurse = {'*': False, '**': True}[roottail]
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
Mads Kiilerich
hgwebdir: allow pure relative globs in paths...
r11677 roothead = os.path.normpath(os.path.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
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]
>>> conv(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/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')]
Patrick Mezard
test-doctest: handle unix/windows path discrepancies
r13538 >>> conv(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/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)
Mads Kiilerich
hgweb: make paths wildcards expanding in a repo root match repo correctly...
r13403 yield (prefix + '/' +
util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
Mads Kiilerich
hgweb: doctest of url creation from wildcard expansion
r13402
Matt Mackall
hgweb: extract the path logic from updatereqenv and add doctests
r15003 def geturlcgivars(baseurl, port):
"""
Extract CGI variables from baseurl
>>> geturlcgivars("http://host.org/base", "80")
('host.org', '80', '/base')
>>> geturlcgivars("http://host.org:8000/base", "80")
('host.org', '8000', '/base')
>>> geturlcgivars('/base', 8000)
('', '8000', '/base')
>>> geturlcgivars("base", '8000')
('', '8000', '/base')
>>> geturlcgivars("http://host", '8000')
('host', '8000', '/')
>>> geturlcgivars("http://host/", '8000')
('host', '8000', '/')
"""
u = util.url(baseurl)
name = u.host or ''
if u.port:
port = u.port
path = u.path or ""
if not path.startswith('/'):
path = '/' + path
return name, str(port), path
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.
"""
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()
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 refreshinterval = 20
if self.ui:
refreshinterval = self.ui.configint('web', 'refreshinterval',
refreshinterval)
# refreshinterval <= 0 means to always refresh.
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()
Mads Kiilerich
config: set a 'source' in most cases where config don't come from file but code...
r20790 u.setconfig('ui', 'report_untrusted', 'off', 'hgwebdir')
u.setconfig('ui', 'nontty', 'true', '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.
u.setconfig('progress', 'disable', 'true', '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)):
map = {'paths': 'hgweb-paths'}
Matt Mackall
hgweb: abort if config file isn't found
r13214 if not os.path.exists(self.conf):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_('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 = []
for name, ignored in u.configitems('hgweb-paths'):
for path in u.configlist('hgweb-paths', name):
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()
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)
for prefix, root in u.configitems('collections'):
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):
name = name[len(prefix):]
repos.append((name.lstrip('/'), repo))
self.repos = repos
self.ui = u
Matt Mackall
hgweb: web.encoding should override encoding.encoding (issue1183)
r8859 encoding.encoding = self.ui.config('web', 'encoding',
encoding.encoding)
Dirkjan Ochtman
hgweb: extract config values after reading webdir-config
r8621 self.style = self.ui.config('web', 'style', 'paper')
Christian Fischer
hgwebdir: use template paths configured in the hgrc (issue2281)
r11649 self.templatepath = self.ui.config('web', 'templates', None)
Dirkjan Ochtman
hgweb: extract config values after reading webdir-config
r8621 self.stripecount = self.ui.config('web', 'stripes', 1)
if self.stripecount:
self.stripecount = int(self.stripecount)
self._baseurl = self.ui.config('web', 'baseurl')
Angel Ezquerra
hgwebdir: use web.prefix when creating url breadcrumbs (issue3790)...
r18515 prefix = self.ui.config('web', 'prefix', '')
if prefix.startswith('/'):
prefix = prefix[1:]
if prefix.endswith('/'):
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):
Pulkit Goyal
py3: replace os.environ with encoding.environ (part 3 of 5)
r30636 if not encoding.environ.get('GATEWAY_INTERFACE',
'').startswith("CGI/1."):
Martin Geisler
wrap string literals in error messages
r8663 raise RuntimeError("This function is only intended to be "
"called while running as a CGI script.")
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):
req = wsgirequest(env, respond)
Dirkjan Ochtman
merge another backout
r6797 return self.run_wsgi(req)
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336 def read_allowed(self, 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.env.get('REMOTE_USER')
Dirkjan Ochtman
hgweb: fix long line lengths introduced in 2dc868712dcc
r7575 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
Wagner Bruna
hgweb: refactor checks for granting and revoking user permissions...
r19032 if deny_read and (not user or ismember(ui, user, deny_read)):
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336 return False
Dirkjan Ochtman
hgweb: fix long line lengths introduced in 2dc868712dcc
r7575 allow_read = ui.configlist('web', 'allow_read', untrusted=True)
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336 # by default, allow reading if no allow_read option has been set
Wagner Bruna
hgweb: refactor checks for granting and revoking user permissions...
r19032 if (not allow_read) or ismember(ui, user, allow_read):
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336 return True
return False
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535 def run_wsgi(self, req):
Gregory Szorc
hgweb: profile HTTP requests...
r29787 with profiling.maybeprofile(self.ui):
for r in self._runwsgi(req):
yield r
Gregory Szorc
hgweb: abstract call to hgwebdir wsgi function...
r29786
def _runwsgi(self, req):
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:
req.headers.append(('Content-Security-Policy', csp))
Matt Mackall
hgweb: use try/except/finally
r25083 virtual = req.env.get("PATH_INFO", "").strip('/')
Gregory Szorc
hgweb: support Content Security Policy...
r30766 tmpl = self.templater(req, nonce)
Matt Mackall
hgweb: use try/except/finally
r25083 ctype = tmpl('mimetype', encoding=encoding.encoding)
ctype = templater.stringify(ctype)
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Matt Mackall
hgweb: use try/except/finally
r25083 # a static file
if virtual.startswith('static/') or 'static' in req.form:
if virtual.startswith('static/'):
fname = virtual[7:]
else:
fname = req.form['static'][0]
static = self.ui.config("web", "static", None,
untrusted=False)
if not static:
tp = self.templatepath or templater.templatepaths()
if isinstance(tp, str):
tp = [tp]
static = [os.path.join(p, 'static') for p in tp]
staticfile(static, fname, req)
return []
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)
if not virtual or (virtual == 'index' and virtual not in repos):
Matt Mackall
hgweb: use try/except/finally
r25083 req.respond(HTTP_OK, ctype)
return self.makeindex(req, 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
Matt Harbison
hgwebdir: add support for explicit index files...
r31482 if virtual.endswith('/index') and virtual not in repos:
subdir = virtual[:-len('index')]
if any(r.startswith(subdir) for r in repos):
req.respond(HTTP_OK, ctype)
return self.makeindex(req, tmpl, subdir)
Matt Mackall
hgweb: use try/except/finally
r25083 virtualrepo = virtual
while virtualrepo:
real = repos.get(virtualrepo)
if real:
req.env['REPO_NAME'] = virtualrepo
try:
# ensure caller gets private copy of ui
repo = hg.repository(self.ui.copy(), real)
Yuya Nishihara
hgweb: do not import hgweb_mod.hgweb and .makebreadcrumb as symbol...
r27043 return hgweb_mod.hgweb(repo).run_wsgi(req)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as inst:
Matt Mackall
hgweb: use try/except/finally
r25083 msg = inst.strerror
raise ErrorResponse(HTTP_SERVER_ERROR, msg)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except error.RepoError as inst:
Matt Mackall
hgweb: use try/except/finally
r25083 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601
Matt Mackall
hgweb: use try/except/finally
r25083 up = virtualrepo.rfind('/')
if up < 0:
break
virtualrepo = virtualrepo[:up]
Wagner Bruna
hgwebdir: fix incorrect index generation for invalid paths (issue2023)...
r13066
Matt Mackall
hgweb: use try/except/finally
r25083 # browse subdirectories
subdir = virtual + '/'
if [r for r in repos if r.startswith(subdir)]:
req.respond(HTTP_OK, ctype)
return self.makeindex(req, tmpl, subdir)
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603
Matt Mackall
hgweb: use try/except/finally
r25083 # prefixes not found
req.respond(HTTP_NOT_FOUND, ctype)
return tmpl("notfound", repo=virtual)
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except ErrorResponse as err:
Matt Mackall
hgweb: use try/except/finally
r25083 req.respond(err, ctype)
return tmpl('error', error=err.message or '')
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 finally:
tmpl = None
def makeindex(self, req, tmpl, subdir=""):
Eric Hopper
Final stage of the hgweb split up....
r2356 def archivelist(ui, nodeid, url):
Alexis S. L. Carvalho
use untrusted settings in hgwebdir
r3556 allowed = ui.configlist("web", "allow_archive", untrusted=True)
Wagner Bruna
hgwebdir: reduce memory usage for index generation...
r13436 archives = []
av6
hgweb: use archivespecs for links on repo index page too...
r30749 for typ, spec in hgweb_mod.archivespecs.iteritems():
if typ in allowed or ui.configbool("web", "allow" + typ,
Alexis S. L. Carvalho
use untrusted settings in hgwebdir
r3556 untrusted=True):
av6
hgweb: use archivespecs for links on repo index page too...
r30749 archives.append({"type" : typ, "extension": spec[2],
Wagner Bruna
hgwebdir: reduce memory usage for index generation...
r13436 "node": nodeid, "url": url})
return archives
Eric Hopper
Final stage of the hgweb split up....
r2356
Benoit Boissinot
hgweb: separate generation of entries and sorting (cleanup)
r10600 def rawentries(subdir="", **map):
Dirkjan Ochtman
hgweb: add web.descend configuration variable
r9363
descend = self.ui.configbool('web', 'descend', True)
Paul Boddie
hgweb: support multi-level repository indexes by enabling descend and collapse...
r16239 collapse = self.ui.configbool('web', 'collapse', False)
seenrepos = set()
seendirs = set()
Eric Hopper
Final stage of the hgweb split up....
r2356 for name, path in self.repos:
Dirkjan Ochtman
hgweb: add web.descend configuration variable
r9363
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841 if not name.startswith(subdir):
continue
Brendan Cully
hgwebdir: show only trailing part of path when browsing subdirectories
r4843 name = name[len(subdir):]
Paul Boddie
hgweb: support multi-level repository indexes by enabling descend and collapse...
r16239 directory = False
if '/' in name:
if not descend:
continue
nameparts = name.split('/')
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
discarded = '/'.join(nameparts[1:])
# remove name parts plus accompanying slash
path = path[:-len(discarded) - 1]
Matt Harbison
hgwebdir: don't allow the hidden parent of a subrepo to show as a directory...
r25426 try:
r = hg.repository(self.ui, path)
directory = False
except (IOError, error.RepoError):
pass
Paul Boddie
hgweb: support multi-level repository indexes by enabling descend and collapse...
r16239 parts = [name]
Matt Harbison
hgwebdir: add support for explicit index files...
r31482 parts.insert(0, '/' + subdir.rstrip('/'))
Paul Boddie
hgweb: support multi-level repository indexes by enabling descend and collapse...
r16239 if req.env['SCRIPT_NAME']:
parts.insert(0, req.env['SCRIPT_NAME'])
url = re.sub(r'/+', '/', '/'.join(parts) + '/')
# show either a directory entry or a repository
if directory:
# get the directory's time information
try:
d = (get_mtime(path), util.makedate()[1])
except OSError:
continue
Angel Ezquerra
hgwebdir: make collapsed folders easier to distinguish from repositories...
r17838 # add '/' to the name to make it obvious that
# the entry is a directory, not a regular repository
Augie Fackler
hgwebdir_mod: move from dict() construction to {} literals...
r20677 row = {'contact': "",
'contact_sort': "",
'name': name + '/',
'name_sort': name,
'url': url,
'description': "",
'description_sort': "",
'lastchange': d,
'lastchange_sort': d[1]-d[0],
'archives': [],
Gregory Szorc
hgweb: expose list of per-repo labels to templates...
r29471 'isdirectory': True,
'labels': [],
}
Paul Boddie
hgweb: support multi-level repository indexes by enabling descend and collapse...
r16239
seendirs.add(name)
yield row
Dirkjan Ochtman
hgweb: add web.descend configuration variable
r9363 continue
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841
Matt Mackall
hgweb: kill parentui references
r8191 u = self.ui.copy()
Eric Hopper
Final stage of the hgweb split up....
r2356 try:
u.readconfig(os.path.join(path, '.hg', 'hgrc'))
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except Exception as e:
Martin Geisler
move % out of translatable strings...
r6913 u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
Alexis S. L. Carvalho
hgwebdir: ignore hgrc parse errors while building the index page...
r5332 continue
Alexis S. L. Carvalho
use untrusted settings in hgwebdir
r3556 def get(section, name, default=None):
return u.config(section, name, default, untrusted=True)
Eric Hopper
Final stage of the hgweb split up....
r2356
Markus F.X.J. Oberhumer
Add option "hidden" to hgwebdir....
r4709 if u.configbool("web", "hidden", untrusted=True):
continue
Mark Edgington
hgweb: support for deny_read/allow_read options...
r7336 if not self.read_allowed(u, req):
continue
Eric Hopper
Final stage of the hgweb split up....
r2356 # update time with local timezone
try:
Brendan Cully
hgweb: Make get_mtime use repository to find store path....
r10078 r = hg.repository(self.ui, path)
timeless@gmail.com
hgwebdir: handle IOErrors from localrepo while enumerating...
r13796 except IOError:
u.warn(_('error accessing repository at %s\n') % path)
continue
Yuya Nishihara
hgweb: handle exception of misconfigured path on index page...
r12038 except error.RepoError:
u.warn(_('error accessing repository at %s\n') % path)
continue
try:
Brendan Cully
hgweb: Make get_mtime use repository to find store path....
r10078 d = (get_mtime(r.spath), util.makedate()[1])
Eric Hopper
Final stage of the hgweb split up....
r2356 except OSError:
continue
Thomas Arendsen Hein
Don't let ui.username override web.contact (issue900)...
r5779 contact = get_contact(get)
Eric Hopper
Final stage of the hgweb split up....
r2356 description = get("web", "description", "")
Matt Harbison
hgwebdir: avoid redundant repo and directory entries when 'web.name' is set...
r25396 seenrepos.add(name)
Eric Hopper
Final stage of the hgweb split up....
r2356 name = get("web", "name", name)
Augie Fackler
hgwebdir_mod: move from dict() construction to {} literals...
r20677 row = {'contact': contact or "unknown",
'contact_sort': contact.upper() or "unknown",
'name': name,
'name_sort': name,
'url': url,
'description': description or "unknown",
'description_sort': description.upper() or "unknown",
'lastchange': d,
'lastchange_sort': d[1]-d[0],
'archives': archivelist(u, "tip", url),
'isdirectory': None,
Gregory Szorc
hgweb: expose list of per-repo labels to templates...
r29471 'labels': u.configlist('web', 'labels', untrusted=True),
Augie Fackler
hgwebdir_mod: move from dict() construction to {} literals...
r20677 }
Paul Boddie
hgweb: support multi-level repository indexes by enabling descend and collapse...
r16239
Benoit Boissinot
hgweb: separate generation of entries and sorting (cleanup)
r10600 yield row
sortdefault = None, False
def entries(sortcolumn="", descending=False, subdir="", **map):
rows = rawentries(subdir=subdir, **map)
if sortcolumn and sortdefault != (sortcolumn, descending):
sortkey = '%s_sort' % sortcolumn
rows = sorted(rows, key=lambda x: x[sortkey],
reverse=descending)
for row, parity in zip(rows, paritygen(self.stripecount)):
row['parity'] = parity
yield row
Eric Hopper
Final stage of the hgweb split up....
r2356
Bryan O'Sullivan
hgwebdir: refresh configuration periodically...
r8371 self.refresh()
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 sortable = ["name", "description", "contact", "lastchange"]
Dirkjan Ochtman
hgweb: get rid of inaccurate hgwebdir.repos_sorted, localize machinery
r8346 sortcolumn, descending = sortdefault
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'sort' in req.form:
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 sortcolumn = req.form['sort'][0]
descending = sortcolumn.startswith('-')
if descending:
sortcolumn = sortcolumn[1:]
if sortcolumn not in sortable:
sortcolumn = ""
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 sort = [("sort_%s" % column,
"%s%s" % ((not descending and column == sortcolumn)
and "-" or "", 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()
Yuya Nishihara
hgweb: introduce helper method to update req.env
r10673 self.updatereqenv(req.env)
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221
Dirkjan Ochtman
hgweb: forgot to centralize the req.write() calls in hgwebdir
r5965 return tmpl("index", entries=entries, subdir=subdir,
Yuya Nishihara
hgweb: do not import hgweb_mod.hgweb and .makebreadcrumb as symbol...
r27043 pathdef=hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
Dirkjan Ochtman
hgweb: forgot to centralize the req.write() calls in hgwebdir
r5965 sortcolumn=sortcolumn, descending=descending,
**dict(sort))
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
Gregory Szorc
hgweb: support Content Security Policy...
r30766 def templater(self, req, nonce):
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
def motd(**map):
if self.motd is not None:
yield self.motd
else:
yield config('web', 'motd', '')
def config(section, name, default=None, untrusted=True):
Matt Mackall
hgweb: kill parentui references
r8191 return self.ui.config(section, name, default, untrusted)
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
Yuya Nishihara
hgweb: introduce helper method to update req.env
r10673 self.updatereqenv(req.env)
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602 url = req.env.get('SCRIPT_NAME', '')
if not url.endswith('/'):
url += '/'
Dirkjan Ochtman
hgweb: use new sessionvars code in hgwebdir, too
r8216 vars = {}
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 styles = (
req.form.get('style', [None])[0],
config('web', 'style'),
'paper'
)
Christian Fischer
hgwebdir: use template paths configured in the hgrc (issue2281)
r11649 style, mapfile = templater.stylemap(styles, self.templatepath)
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 if style == styles[0]:
vars['style'] = style
Matt Mackall
many, many trivial check-code fixups
r10282
Dirkjan Ochtman
hgweb: use new sessionvars code in hgwebdir, too
r8216 start = url[-1] == '?' and '&' or '?'
sessionvars = webutil.sessionvars(vars, start)
Matt Mackall
urls: bulk-change primary website URLs
r26421 logourl = config('web', 'logourl', 'https://mercurial-scm.org/')
Angel Ezquerra
hgweb: add a "web/logoimg" setting to customize the web logo image...
r14913 logoimg = config('web', 'logoimg', 'hglogo.png')
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602 staticurl = config('web', 'staticurl') or url + 'static/'
if not staticurl.endswith('/'):
staticurl += '/'
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 defaults = {
"encoding": encoding.encoding,
"motd": motd,
"url": url,
"logourl": logourl,
"logoimg": logoimg,
"staticurl": staticurl,
"sessionvars": sessionvars,
"style": style,
Gregory Szorc
hgweb: support Content Security Policy...
r30766 "nonce": nonce,
Yuya Nishihara
templater: separate function to create templater from map file (API)...
r28954 }
tmpl = templater.templater.frommapfile(mapfile, defaults=defaults)
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602 return tmpl
Yuya Nishihara
hgweb: introduce helper method to update req.env
r10673
def updatereqenv(self, env):
if self._baseurl is not None:
Matt Mackall
hgweb: extract the path logic from updatereqenv and add doctests
r15003 name, port, path = geturlcgivars(self._baseurl, env['SERVER_PORT'])
env['SERVER_NAME'] = name
env['SERVER_PORT'] = port
wujek
hgweb: handle 'baseurl' configurations with leading slash (issue2934)
r15001 env['SCRIPT_NAME'] = path