--- a/mercurial/hgweb/hgweb_mod.py Mon Dec 03 12:06:21 2007 +0100
+++ b/mercurial/hgweb/hgweb_mod.py Mon Dec 03 12:27:11 2007 +0100
@@ -12,7 +12,7 @@
from mercurial import revlog, templater
from common import ErrorResponse, get_mtime, style_map, paritygen
from request import wsgirequest
-import webcommands
+import webcommands, protocol
shortcuts = {
'cl': [('cmd', ['changelog']), ('rev', None)],
@@ -177,7 +177,7 @@
cmd = cmd[style+1:]
# avoid accepting e.g. style parameter as command
- if hasattr(webcommands, cmd):
+ if hasattr(webcommands, cmd) or hasattr(protocol, cmd):
req.form['cmd'] = [cmd]
if args and args[0]:
@@ -276,7 +276,10 @@
cmd = req.form['cmd'][0]
try:
- method = getattr(webcommands, cmd)
+ if hasattr(protocol, cmd):
+ method = getattr(protocol, cmd)
+ else:
+ method = getattr(webcommands, cmd)
method(self, req)
except revlog.LookupError, err:
req.respond(404, self.t(
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/hgweb/protocol.py Mon Dec 03 12:27:11 2007 +0100
@@ -0,0 +1,237 @@
+#
+# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
+# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+import cStringIO, zlib, bz2, tempfile, errno, os, sys
+from mercurial import util, streamclone
+from mercurial.i18n import gettext as _
+from mercurial.node import *
+
+def lookup(web, req):
+ try:
+ r = hex(web.repo.lookup(req.form['key'][0]))
+ success = 1
+ except Exception,inst:
+ r = str(inst)
+ success = 0
+ resp = "%s %s\n" % (success, r)
+ req.httphdr("application/mercurial-0.1", length=len(resp))
+ req.write(resp)
+
+def heads(web, req):
+ resp = " ".join(map(hex, web.repo.heads())) + "\n"
+ req.httphdr("application/mercurial-0.1", length=len(resp))
+ req.write(resp)
+
+def branches(web, req):
+ nodes = []
+ if req.form.has_key('nodes'):
+ nodes = map(bin, req.form['nodes'][0].split(" "))
+ resp = cStringIO.StringIO()
+ for b in web.repo.branches(nodes):
+ resp.write(" ".join(map(hex, b)) + "\n")
+ resp = resp.getvalue()
+ req.httphdr("application/mercurial-0.1", length=len(resp))
+ req.write(resp)
+
+def between(web, req):
+ if req.form.has_key('pairs'):
+ pairs = [map(bin, p.split("-"))
+ for p in req.form['pairs'][0].split(" ")]
+ resp = cStringIO.StringIO()
+ for b in web.repo.between(pairs):
+ resp.write(" ".join(map(hex, b)) + "\n")
+ resp = resp.getvalue()
+ req.httphdr("application/mercurial-0.1", length=len(resp))
+ req.write(resp)
+
+def changegroup(web, req):
+ req.httphdr("application/mercurial-0.1")
+ nodes = []
+ if not web.allowpull:
+ return
+
+ if req.form.has_key('roots'):
+ nodes = map(bin, req.form['roots'][0].split(" "))
+
+ z = zlib.compressobj()
+ f = web.repo.changegroup(nodes, 'serve')
+ while 1:
+ chunk = f.read(4096)
+ if not chunk:
+ break
+ req.write(z.compress(chunk))
+
+ req.write(z.flush())
+
+def changegroupsubset(web, req):
+ req.httphdr("application/mercurial-0.1")
+ bases = []
+ heads = []
+ if not web.allowpull:
+ return
+
+ if req.form.has_key('bases'):
+ bases = [bin(x) for x in req.form['bases'][0].split(' ')]
+ if req.form.has_key('heads'):
+ heads = [bin(x) for x in req.form['heads'][0].split(' ')]
+
+ z = zlib.compressobj()
+ f = web.repo.changegroupsubset(bases, heads, 'serve')
+ while 1:
+ chunk = f.read(4096)
+ if not chunk:
+ break
+ req.write(z.compress(chunk))
+
+ req.write(z.flush())
+
+def capabilities(web, req):
+ caps = ['lookup', 'changegroupsubset']
+ if web.configbool('server', 'uncompressed'):
+ caps.append('stream=%d' % web.repo.changelog.version)
+ # XXX: make configurable and/or share code with do_unbundle:
+ unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
+ if unbundleversions:
+ caps.append('unbundle=%s' % ','.join(unbundleversions))
+ resp = ' '.join(caps)
+ req.httphdr("application/mercurial-0.1", length=len(resp))
+ req.write(resp)
+
+def unbundle(web, req):
+ def bail(response, headers={}):
+ length = int(req.env['CONTENT_LENGTH'])
+ for s in util.filechunkiter(req, limit=length):
+ # drain incoming bundle, else client will not see
+ # response when run outside cgi script
+ pass
+ req.httphdr("application/mercurial-0.1", headers=headers)
+ req.write('0\n')
+ req.write(response)
+
+ # require ssl by default, auth info cannot be sniffed and
+ # replayed
+ ssl_req = web.configbool('web', 'push_ssl', True)
+ if ssl_req:
+ if req.env.get('wsgi.url_scheme') != 'https':
+ bail(_('ssl required\n'))
+ return
+ proto = 'https'
+ else:
+ proto = 'http'
+
+ # do not allow push unless explicitly allowed
+ if not web.check_perm(req, 'push', False):
+ bail(_('push not authorized\n'),
+ headers={'status': '401 Unauthorized'})
+ return
+
+ their_heads = req.form['heads'][0].split(' ')
+
+ def check_heads():
+ heads = map(hex, web.repo.heads())
+ return their_heads == [hex('force')] or their_heads == heads
+
+ # fail early if possible
+ if not check_heads():
+ bail(_('unsynced changes\n'))
+ return
+
+ req.httphdr("application/mercurial-0.1")
+
+ # do not lock repo until all changegroup data is
+ # streamed. save to temporary file.
+
+ fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
+ fp = os.fdopen(fd, 'wb+')
+ try:
+ length = int(req.env['CONTENT_LENGTH'])
+ for s in util.filechunkiter(req, limit=length):
+ fp.write(s)
+
+ try:
+ lock = web.repo.lock()
+ try:
+ if not check_heads():
+ req.write('0\n')
+ req.write(_('unsynced changes\n'))
+ return
+
+ fp.seek(0)
+ header = fp.read(6)
+ if not header.startswith("HG"):
+ # old client with uncompressed bundle
+ def generator(f):
+ yield header
+ for chunk in f:
+ yield chunk
+ elif not header.startswith("HG10"):
+ req.write("0\n")
+ req.write(_("unknown bundle version\n"))
+ return
+ elif header == "HG10GZ":
+ def generator(f):
+ zd = zlib.decompressobj()
+ for chunk in f:
+ yield zd.decompress(chunk)
+ elif header == "HG10BZ":
+ def generator(f):
+ zd = bz2.BZ2Decompressor()
+ zd.decompress("BZ")
+ for chunk in f:
+ yield zd.decompress(chunk)
+ elif header == "HG10UN":
+ def generator(f):
+ for chunk in f:
+ yield chunk
+ else:
+ req.write("0\n")
+ req.write(_("unknown bundle compression type\n"))
+ return
+ gen = generator(util.filechunkiter(fp, 4096))
+
+ # send addchangegroup output to client
+
+ old_stdout = sys.stdout
+ sys.stdout = cStringIO.StringIO()
+
+ try:
+ url = 'remote:%s:%s' % (proto,
+ req.env.get('REMOTE_HOST', ''))
+ try:
+ ret = web.repo.addchangegroup(
+ util.chunkbuffer(gen), 'serve', url)
+ except util.Abort, inst:
+ sys.stdout.write("abort: %s\n" % inst)
+ ret = 0
+ finally:
+ val = sys.stdout.getvalue()
+ sys.stdout = old_stdout
+ req.write('%d\n' % ret)
+ req.write(val)
+ finally:
+ del lock
+ except (OSError, IOError), inst:
+ req.write('0\n')
+ filename = getattr(inst, 'filename', '')
+ # Don't send our filesystem layout to the client
+ if filename.startswith(web.repo.root):
+ filename = filename[len(web.repo.root)+1:]
+ else:
+ filename = ''
+ error = getattr(inst, 'strerror', 'Unknown error')
+ if inst.errno == errno.ENOENT:
+ code = 404
+ else:
+ code = 500
+ req.respond(code, '%s: %s\n' % (error, filename))
+ finally:
+ fp.close()
+ os.unlink(tempname)
+
+def stream_out(web, req):
+ req.httphdr("application/mercurial-0.1")
+ streamclone.stream_out(web.repo, req, untrusted=True)
--- a/mercurial/hgweb/webcommands.py Mon Dec 03 12:06:21 2007 +0100
+++ b/mercurial/hgweb/webcommands.py Mon Dec 03 12:27:11 2007 +0100
@@ -5,10 +5,8 @@
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
-import cStringIO, zlib, bz2, tempfile, errno, os, sys
-from mercurial import revlog, util, streamclone
-from mercurial.i18n import gettext as _
-from mercurial.node import *
+import os
+from mercurial import revlog
from common import staticfile
def log(web, req):
@@ -73,85 +71,6 @@
def filelog(web, req):
req.write(web.filelog(web.filectx(req)))
-def lookup(web, req):
- try:
- r = hex(web.repo.lookup(req.form['key'][0]))
- success = 1
- except Exception,inst:
- r = str(inst)
- success = 0
- resp = "%s %s\n" % (success, r)
- req.httphdr("application/mercurial-0.1", length=len(resp))
- req.write(resp)
-
-def heads(web, req):
- resp = " ".join(map(hex, web.repo.heads())) + "\n"
- req.httphdr("application/mercurial-0.1", length=len(resp))
- req.write(resp)
-
-def branches(web, req):
- nodes = []
- if req.form.has_key('nodes'):
- nodes = map(bin, req.form['nodes'][0].split(" "))
- resp = cStringIO.StringIO()
- for b in web.repo.branches(nodes):
- resp.write(" ".join(map(hex, b)) + "\n")
- resp = resp.getvalue()
- req.httphdr("application/mercurial-0.1", length=len(resp))
- req.write(resp)
-
-def between(web, req):
- if req.form.has_key('pairs'):
- pairs = [map(bin, p.split("-"))
- for p in req.form['pairs'][0].split(" ")]
- resp = cStringIO.StringIO()
- for b in web.repo.between(pairs):
- resp.write(" ".join(map(hex, b)) + "\n")
- resp = resp.getvalue()
- req.httphdr("application/mercurial-0.1", length=len(resp))
- req.write(resp)
-
-def changegroup(web, req):
- req.httphdr("application/mercurial-0.1")
- nodes = []
- if not web.allowpull:
- return
-
- if req.form.has_key('roots'):
- nodes = map(bin, req.form['roots'][0].split(" "))
-
- z = zlib.compressobj()
- f = web.repo.changegroup(nodes, 'serve')
- while 1:
- chunk = f.read(4096)
- if not chunk:
- break
- req.write(z.compress(chunk))
-
- req.write(z.flush())
-
-def changegroupsubset(web, req):
- req.httphdr("application/mercurial-0.1")
- bases = []
- heads = []
- if not web.allowpull:
- return
-
- if req.form.has_key('bases'):
- bases = [bin(x) for x in req.form['bases'][0].split(' ')]
- if req.form.has_key('heads'):
- heads = [bin(x) for x in req.form['heads'][0].split(' ')]
-
- z = zlib.compressobj()
- f = web.repo.changegroupsubset(bases, heads, 'serve')
- while 1:
- chunk = f.read(4096)
- if not chunk:
- break
- req.write(z.compress(chunk))
-
- req.write(z.flush())
-
def archive(web, req):
type_ = req.form['type'][0]
allowed = web.configlist("web", "allow_archive")
@@ -171,150 +90,3 @@
os.path.join(web.templatepath, "static"),
untrusted=False)
req.write(staticfile(static, fname, req))
-
-def capabilities(web, req):
- caps = ['lookup', 'changegroupsubset']
- if web.configbool('server', 'uncompressed'):
- caps.append('stream=%d' % web.repo.changelog.version)
- # XXX: make configurable and/or share code with do_unbundle:
- unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
- if unbundleversions:
- caps.append('unbundle=%s' % ','.join(unbundleversions))
- resp = ' '.join(caps)
- req.httphdr("application/mercurial-0.1", length=len(resp))
- req.write(resp)
-
-def unbundle(web, req):
- def bail(response, headers={}):
- length = int(req.env['CONTENT_LENGTH'])
- for s in util.filechunkiter(req, limit=length):
- # drain incoming bundle, else client will not see
- # response when run outside cgi script
- pass
- req.httphdr("application/mercurial-0.1", headers=headers)
- req.write('0\n')
- req.write(response)
-
- # require ssl by default, auth info cannot be sniffed and
- # replayed
- ssl_req = web.configbool('web', 'push_ssl', True)
- if ssl_req:
- if req.env.get('wsgi.url_scheme') != 'https':
- bail(_('ssl required\n'))
- return
- proto = 'https'
- else:
- proto = 'http'
-
- # do not allow push unless explicitly allowed
- if not web.check_perm(req, 'push', False):
- bail(_('push not authorized\n'),
- headers={'status': '401 Unauthorized'})
- return
-
- their_heads = req.form['heads'][0].split(' ')
-
- def check_heads():
- heads = map(hex, web.repo.heads())
- return their_heads == [hex('force')] or their_heads == heads
-
- # fail early if possible
- if not check_heads():
- bail(_('unsynced changes\n'))
- return
-
- req.httphdr("application/mercurial-0.1")
-
- # do not lock repo until all changegroup data is
- # streamed. save to temporary file.
-
- fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
- fp = os.fdopen(fd, 'wb+')
- try:
- length = int(req.env['CONTENT_LENGTH'])
- for s in util.filechunkiter(req, limit=length):
- fp.write(s)
-
- try:
- lock = web.repo.lock()
- try:
- if not check_heads():
- req.write('0\n')
- req.write(_('unsynced changes\n'))
- return
-
- fp.seek(0)
- header = fp.read(6)
- if not header.startswith("HG"):
- # old client with uncompressed bundle
- def generator(f):
- yield header
- for chunk in f:
- yield chunk
- elif not header.startswith("HG10"):
- req.write("0\n")
- req.write(_("unknown bundle version\n"))
- return
- elif header == "HG10GZ":
- def generator(f):
- zd = zlib.decompressobj()
- for chunk in f:
- yield zd.decompress(chunk)
- elif header == "HG10BZ":
- def generator(f):
- zd = bz2.BZ2Decompressor()
- zd.decompress("BZ")
- for chunk in f:
- yield zd.decompress(chunk)
- elif header == "HG10UN":
- def generator(f):
- for chunk in f:
- yield chunk
- else:
- req.write("0\n")
- req.write(_("unknown bundle compression type\n"))
- return
- gen = generator(util.filechunkiter(fp, 4096))
-
- # send addchangegroup output to client
-
- old_stdout = sys.stdout
- sys.stdout = cStringIO.StringIO()
-
- try:
- url = 'remote:%s:%s' % (proto,
- req.env.get('REMOTE_HOST', ''))
- try:
- ret = web.repo.addchangegroup(
- util.chunkbuffer(gen), 'serve', url)
- except util.Abort, inst:
- sys.stdout.write("abort: %s\n" % inst)
- ret = 0
- finally:
- val = sys.stdout.getvalue()
- sys.stdout = old_stdout
- req.write('%d\n' % ret)
- req.write(val)
- finally:
- del lock
- except (OSError, IOError), inst:
- req.write('0\n')
- filename = getattr(inst, 'filename', '')
- # Don't send our filesystem layout to the client
- if filename.startswith(web.repo.root):
- filename = filename[len(web.repo.root)+1:]
- else:
- filename = ''
- error = getattr(inst, 'strerror', 'Unknown error')
- if inst.errno == errno.ENOENT:
- code = 404
- else:
- code = 500
- req.respond(code, '%s: %s\n' % (error, filename))
- finally:
- fp.close()
- os.unlink(tempname)
-
-def stream_out(web, req):
- req.httphdr("application/mercurial-0.1")
- streamclone.stream_out(web.repo, req, untrusted=True)