##// END OF EJS Templates
hgweb: clarify which address and port can/cannot be bound at startup (bug 769)...
hgweb: clarify which address and port can/cannot be bound at startup (bug 769) The error message at startup when the address/port could not be bound was confusing: hg serve abort: cannot start server: Address already in use Be more explicit: $ hg serve -a localhost abort: cannot start server at 'localhost:8000': Address already in use Also be more explicit on success, showing hostname and ip address/port: $ hg -v serve -a localhost -p 80 listening at http://localhost/ (127.0.0.1:80) We are careful to handle a missconfigured machine whose hostname does not resolve, falling back to the address given at the command line. Remove a dead-code error message.

File last commit:

r6221:2eb18c78 default
r6262:de7256c8 default
Show More
hgwebdir_mod.py
288 lines | 11.3 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 #
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
Dirkjan Ochtman
hgweb: move HTTP content types out of header templates...
r5928 import os
Eric Hopper
Final stage of the hgweb split up....
r2356 from mercurial.i18n import gettext as _
Joel Rosdahl
Avoid importing mercurial.node/mercurial.repo stuff from mercurial.hg
r6217 from mercurial.repo import RepoError
Matt Mackall
templates: move filters to their own module...
r5976 from mercurial import ui, hg, util, templater, templatefilters
from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen,\
Dirkjan Ochtman
hgweb: explicit response status
r5993 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
Matt Mackall
Replace demandload with new demandimport
r3877 from hgweb_mod import hgweb
Dirkjan Ochtman
Less indirection in the WSGI web interface. This simplifies some code, and makes it more compliant with WSGI.
r5566 from request import wsgirequest
Eric Hopper
Final stage of the hgweb split up....
r2356
# This is a stopgap
class hgwebdir(object):
Alexis S. L. Carvalho
Pass a ui from create_server to hgwebdir and a repo from hgwebdir to hgweb...
r4079 def __init__(self, config, parentui=None):
Eric Hopper
Final stage of the hgweb split up....
r2356 def cleannames(items):
Patrick Mezard
hgwebdir: normalize virtual paths before stripping the separator...
r5584 return [(util.pconvert(name).strip('/'), path)
Alexis S. L. Carvalho
hgwebdir: change os.sep in the name of repos to "/"
r5063 for name, path in items]
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 self.parentui = parentui or ui.ui(report_untrusted=False,
interactive = False)
Alexis S. L. Carvalho
hgwebdir: try to get web.style and web.motd from the ui.config system...
r4080 self.motd = None
self.style = None
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 self.stripecount = None
Eric Hopper
Final stage of the hgweb split up....
r2356 self.repos_sorted = ('name', False)
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221 self._baseurl = None
Eric Hopper
Final stage of the hgweb split up....
r2356 if isinstance(config, (list, tuple)):
self.repos = cleannames(config)
self.repos_sorted = ('', False)
elif isinstance(config, dict):
self.repos = cleannames(config.items())
self.repos.sort()
else:
Michael Gebetsroither
hgwebdir: class hgwebdir should also accept a configparser instance
r4051 if isinstance(config, util.configparser):
cp = config
else:
cp = util.configparser()
cp.read(config)
Eric Hopper
Final stage of the hgweb split up....
r2356 self.repos = []
Edouard Gomez
Add style support to hgwebdir
r3221 if cp.has_section('web'):
if cp.has_option('web', 'motd'):
self.motd = cp.get('web', 'motd')
if cp.has_option('web', 'style'):
Thomas Arendsen Hein
Whitespace/Tab cleanup
r3223 self.style = cp.get('web', 'style')
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 if cp.has_option('web', 'stripes'):
self.stripecount = int(cp.get('web', 'stripes'))
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221 if cp.has_option('web', 'baseurl'):
self._baseurl = cp.get('web', 'baseurl')
Eric Hopper
Final stage of the hgweb split up....
r2356 if cp.has_section('paths'):
self.repos.extend(cleannames(cp.items('paths')))
if cp.has_section('collections'):
for prefix, root in cp.items('collections'):
for path in util.walkrepos(root):
repo = os.path.normpath(path)
name = repo
if name.startswith(prefix):
name = name[len(prefix):]
self.repos.append((name.lstrip(os.sep), repo))
self.repos.sort()
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535 def run(self):
Eric Hopper
Cleanup hgweb and hgwebdir's run method a bit.
r2538 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
import mercurial.hgweb.wsgicgi as wsgicgi
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)
self.run_wsgi(req)
return req
Eric Hopper
Arrange for old copies of CGI scripts to still work.
r2535
def run_wsgi(self, req):
Eric Hopper
Final stage of the hgweb split up....
r2356
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 try:
try:
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 virtual = req.env.get("PATH_INFO", "").strip('/')
Dirkjan Ochtman
hgweb: explicit response status
r5993 tmpl = self.templater(req)
ctype = tmpl('mimetype', encoding=util._encoding)
ctype = templater.stringify(ctype)
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603 # a static file
if virtual.startswith('static/') or 'static' in req.form:
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 static = os.path.join(templater.templatepath(), 'static')
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603 if virtual.startswith('static/'):
fname = virtual[7:]
else:
fname = req.form['static'][0]
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 req.write(staticfile(static, fname, req))
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603 return
# top-level index
elif not virtual:
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, ctype)
Dirkjan Ochtman
hgweb: forgot to centralize the req.write() calls in hgwebdir
r5965 req.write(self.makeindex(req, tmpl))
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603 return
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603 # nested indexes and hgwebs
Thomas Arendsen Hein
Removed trailing spaces from everything except test output
r6210
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603 repos = dict(self.repos)
while virtual:
real = repos.get(virtual)
if real:
req.env['REPO_NAME'] = virtual
try:
repo = hg.repository(self.parentui, real)
hgweb(repo).run_wsgi(req)
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 return
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603 except IOError, inst:
Dirkjan Ochtman
hgweb: explicit response status
r5993 msg = inst.strerror
raise ErrorResponse(HTTP_SERVER_ERROR, msg)
Joel Rosdahl
Avoid importing mercurial.node/mercurial.repo stuff from mercurial.hg
r6217 except RepoError, inst:
Dirkjan Ochtman
hgweb: explicit response status
r5993 raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603 # browse subdirectories
subdir = virtual + '/'
if [r for r in repos if r.startswith(subdir)]:
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, ctype)
Dirkjan Ochtman
hgweb: forgot to centralize the req.write() calls in hgwebdir
r5965 req.write(self.makeindex(req, tmpl, subdir))
Dirkjan Ochtman
hgwebdir: refactor inner loop
r5603 return
up = virtual.rfind('/')
if up < 0:
break
virtual = virtual[:up]
# prefixes not found
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_NOT_FOUND, ctype)
req.write(tmpl("notfound", repo=virtual))
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 except ErrorResponse, err:
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(err.code, ctype)
req.write(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)
Brendan Cully
Teach hgwebdir about new interface
r3262 for i in [('zip', '.zip'), ('gz', '.tar.gz'), ('bz2', '.tar.bz2')]:
Alexis S. L. Carvalho
use untrusted settings in hgwebdir
r3556 if i[0] in allowed or ui.configbool("web", "allow" + i[0],
untrusted=True):
Brendan Cully
Teach hgwebdir about new interface
r3262 yield {"type" : i[0], "extension": i[1],
"node": nodeid, "url": url}
Eric Hopper
Final stage of the hgweb split up....
r2356
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841 def entries(sortcolumn="", descending=False, subdir="", **map):
Thomas Arendsen Hein
Keep session variables when linking from hgwebdir's index to repositories.
r3365 def sessionvars(**map):
fields = []
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'style' in req.form:
Thomas Arendsen Hein
Keep session variables when linking from hgwebdir's index to repositories.
r3365 style = req.form['style'][0]
if style != get('web', 'style', ''):
fields.append(('style', style))
separator = url[-1] == '?' and ';' or '?'
for name, value in fields:
yield dict(name=name, value=value, separator=separator)
separator = ';'
Eric Hopper
Final stage of the hgweb split up....
r2356 rows = []
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 parity = paritygen(self.stripecount)
Eric Hopper
Final stage of the hgweb split up....
r2356 for name, path in self.repos:
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):]
Brendan Cully
hgweb: let hgwebdir browse subdirectories
r4841
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 u = ui.ui(parentui=self.parentui)
Eric Hopper
Final stage of the hgweb split up....
r2356 try:
u.readconfig(os.path.join(path, '.hg', 'hgrc'))
Alexis S. L. Carvalho
hgwebdir: ignore hgrc parse errors while building the index page...
r5332 except Exception, e:
u.warn(_('error reading %s/.hg/hgrc: %s\n' % (path, e)))
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
Michele Cella
fix to let test-hgwebdir pass...
r6046 parts = [req.env['PATH_INFO'].rstrip('/'), name]
Dirkjan Ochtman
Use SCRIPT_NAME and PATH_INFO instead of REQUEST_URI. This is required by WSGI (fixes issue846).
r5579 if req.env['SCRIPT_NAME']:
Thomas Arendsen Hein
Removed tabs and trailing whitespace in python files
r5760 parts.insert(0, req.env['SCRIPT_NAME'])
Dirkjan Ochtman
Use SCRIPT_NAME and PATH_INFO instead of REQUEST_URI. This is required by WSGI (fixes issue846).
r5579 url = ('/'.join(parts).replace("//", "/")) + '/'
Eric Hopper
Final stage of the hgweb split up....
r2356
# update time with local timezone
try:
d = (get_mtime(path), util.makedate()[1])
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", "")
name = get("web", "name", name)
row = dict(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],
Thomas Arendsen Hein
Keep session variables when linking from hgwebdir's index to repositories.
r3365 sessionvars=sessionvars,
Eric Hopper
Final stage of the hgweb split up....
r2356 archives=archivelist(u, "tip", url))
if (not sortcolumn
or (sortcolumn, descending) == self.repos_sorted):
# fast path for unsorted output
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 row['parity'] = parity.next()
Eric Hopper
Final stage of the hgweb split up....
r2356 yield row
else:
rows.append((row["%s_sort" % sortcolumn], row))
if rows:
rows.sort()
if descending:
rows.reverse()
for key, row in rows:
Thomas Arendsen Hein
hgweb: use generator to count parity of horizontal stripes for easier reading....
r4462 row['parity'] = parity.next()
Eric Hopper
Final stage of the hgweb split up....
r2356 yield row
Dirkjan Ochtman
hgwebdir: split out makeindex function, facilitate test failure diagnosis
r5601 sortable = ["name", "description", "contact", "lastchange"]
sortcolumn, descending = self.repos_sorted
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
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221 if self._baseurl is not None:
req.env['SCRIPT_NAME'] = self._baseurl
Dirkjan Ochtman
hgweb: forgot to centralize the req.write() calls in hgwebdir
r5965 return tmpl("index", entries=entries, subdir=subdir,
sortcolumn=sortcolumn, descending=descending,
**dict(sort))
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
def templater(self, req):
def header(**map):
Dirkjan Ochtman
hgweb: move HTTP content types out of header templates...
r5928 yield tmpl('header', encoding=util._encoding, **map)
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602
def footer(**map):
yield tmpl("footer", **map)
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):
return self.parentui.config(section, name, default, untrusted)
Brendan Cully
Support web.baseurl in hgwebdir, overriding SCRIPT_NAME
r6221 if self._baseurl is not None:
req.env['SCRIPT_NAME'] = self._baseurl
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602 url = req.env.get('SCRIPT_NAME', '')
if not url.endswith('/'):
url += '/'
staticurl = config('web', 'staticurl') or url + 'static/'
if not staticurl.endswith('/'):
staticurl += '/'
style = self.style
if style is None:
style = config('web', 'style', '')
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'style' in req.form:
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602 style = req.form['style'][0]
if self.stripecount is None:
self.stripecount = int(config('web', 'stripes', 1))
mapfile = style_map(templater.templatepath(), style)
Matt Mackall
templates: move filters to their own module...
r5976 tmpl = templater.templater(mapfile, templatefilters.filters,
Dirkjan Ochtman
hgwebdir: split out templater creation
r5602 defaults={"header": header,
"footer": footer,
"motd": motd,
"url": url,
"staticurl": staticurl})
return tmpl