##// END OF EJS Templates
hgweb: send proper error messages to the client...
hgweb: send proper error messages to the client Fixes a bug in protocol which caused an exception during exception handling in some cases on Windows. Also makes sure the server error message is correctly propagated to the client, instead of being thrown away.

File last commit:

r9694:8269fe2d default
r9694:8269fe2d default
Show More
protocol.py
207 lines | 6.7 KiB | text/x-python | PythonLexer
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 #
# Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
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
# GNU General Public License version 2, incorporated herein by reference.
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Henrik Stuart
named branches: server branchmap wire protocol support (issue736)...
r8562 import cStringIO, zlib, tempfile, errno, os, sys, urllib
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 from mercurial import util, streamclone
Joel Rosdahl
Expand import * to allow Pyflakes to find problems
r6211 from mercurial.node import bin, hex
Dirkjan Ochtman
improve changegroup.readbundle(), use it in hgweb
r6154 from mercurial import changegroup as changegroupmod
Benoit Boissinot
fix missing import, spotted by pychecker
r7281 from common import ErrorResponse, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Dirkjan Ochtman
hgweb: explicitly check if requested command exists
r5963 # __all__ is populated with the allowed commands. Be sure to add to it if
# you're adding a new command, or the new command won't work.
__all__ = [
'lookup', 'heads', 'branches', 'between', 'changegroup',
'changegroupsubset', 'capabilities', 'unbundle', 'stream_out',
Henrik Stuart
named branches: server branchmap wire protocol support (issue736)...
r8562 'branchmap',
Dirkjan Ochtman
hgweb: explicitly check if requested command exists
r5963 ]
Dirkjan Ochtman
hgweb: explicit response status
r5993 HGTYPE = 'application/mercurial-0.1'
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 def lookup(repo, req):
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 try:
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 r = hex(repo.lookup(req.form['key'][0]))
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 success = 1
Martin Geisler
coding style: use a space after comma...
r9198 except Exception, inst:
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 r = str(inst)
success = 0
resp = "%s %s\n" % (success, r)
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE, length=len(resp))
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 yield resp
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 def heads(repo, req):
resp = " ".join(map(hex, repo.heads())) + "\n"
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE, length=len(resp))
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 yield resp
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Henrik Stuart
named branches: server branchmap wire protocol support (issue736)...
r8562 def branchmap(repo, req):
branches = repo.branchmap()
heads = []
for branch, nodes in branches.iteritems():
branchname = urllib.quote(branch)
branchnodes = [hex(node) for node in nodes]
heads.append('%s %s' % (branchname, ' '.join(branchnodes)))
resp = '\n'.join(heads)
req.respond(HTTP_OK, HGTYPE, length=len(resp))
yield resp
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 def branches(repo, req):
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 nodes = []
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'nodes' in req.form:
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 nodes = map(bin, req.form['nodes'][0].split(" "))
resp = cStringIO.StringIO()
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 for b in repo.branches(nodes):
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 resp.write(" ".join(map(hex, b)) + "\n")
resp = resp.getvalue()
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE, length=len(resp))
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 yield resp
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 def between(repo, req):
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'pairs' in req.form:
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 pairs = [map(bin, p.split("-"))
for p in req.form['pairs'][0].split(" ")]
resp = cStringIO.StringIO()
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 for b in repo.between(pairs):
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 resp.write(" ".join(map(hex, b)) + "\n")
resp = resp.getvalue()
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE, length=len(resp))
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 yield resp
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 def changegroup(repo, req):
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE)
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 nodes = []
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'roots' in req.form:
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 nodes = map(bin, req.form['roots'][0].split(" "))
z = zlib.compressobj()
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 f = repo.changegroup(nodes, 'serve')
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 while 1:
chunk = f.read(4096)
if not chunk:
break
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 yield z.compress(chunk)
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 yield z.flush()
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 def changegroupsubset(repo, req):
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE)
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 bases = []
heads = []
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'bases' in req.form:
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if 'heads' in req.form:
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
z = zlib.compressobj()
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 f = repo.changegroupsubset(bases, heads, 'serve')
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 while 1:
chunk = f.read(4096)
if not chunk:
break
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 yield z.compress(chunk)
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 yield z.flush()
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 def capabilities(repo, req):
Henrik Stuart
named branches: server branchmap wire protocol support (issue736)...
r8562 caps = ['lookup', 'changegroupsubset', 'branchmap']
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 if repo.ui.configbool('server', 'uncompressed', untrusted=True):
caps.append('stream=%d' % repo.changelog.version)
Dirkjan Ochtman
hgweb: move capabilities calculation back into hgweb.protocol
r6780 if changegroupmod.bundlepriority:
caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
rsp = ' '.join(caps)
req.respond(HTTP_OK, HGTYPE, length=len(rsp))
Dirkjan Ochtman
hgweb: all protocol functions have become generators...
r6784 yield rsp
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 def unbundle(repo, req):
Dirkjan Ochtman
hgweb: only accept POST requests for unbundle
r6335
Dirkjan Ochtman
hgweb: centralize permission checks for protocol commands...
r6779 proto = req.env.get('wsgi.url_scheme') or 'http'
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 their_heads = req.form['heads'][0].split(' ')
def check_heads():
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 heads = map(hex, repo.heads())
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 return their_heads == [hex('force')] or their_heads == heads
# fail early if possible
if not check_heads():
Dirkjan Ochtman
hgweb: be sure to drain request data even in early error conditions...
r7180 req.drain()
Dirkjan Ochtman
hgweb: raise ErrorResponses to communicate protocol errors
r6926 raise ErrorResponse(HTTP_OK, 'unsynced changes')
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
# 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:
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 lock = repo.lock()
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 try:
if not check_heads():
Dirkjan Ochtman
hgweb: raise ErrorResponses to communicate protocol errors
r6926 raise ErrorResponse(HTTP_OK, 'unsynced changes')
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
fp.seek(0)
header = fp.read(6)
Dirkjan Ochtman
improve changegroup.readbundle(), use it in hgweb
r6154 if header.startswith('HG') and not header.startswith('HG10'):
raise ValueError('unknown bundle version')
elif header not in changegroupmod.bundletypes:
raise ValueError('unknown bundle compression type')
gen = changegroupmod.unbundle(header, fp)
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
# send addchangegroup output to client
Jesse Glick
Issue937: error messages from hooks not sent over HTTP....
r6265 oldio = sys.stdout, sys.stderr
sys.stderr = sys.stdout = cStringIO.StringIO()
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598
try:
Henrik Stuart
acl: support for getting authenticated user from web server (issue298)...
r8846 url = 'remote:%s:%s:%s' % (
proto,
urllib.quote(req.env.get('REMOTE_HOST', '')),
urllib.quote(req.env.get('REMOTE_USER', '')))
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 try:
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 ret = repo.addchangegroup(gen, 'serve', url)
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 except util.Abort, inst:
sys.stdout.write("abort: %s\n" % inst)
ret = 0
finally:
val = sys.stdout.getvalue()
Jesse Glick
Issue937: error messages from hooks not sent over HTTP....
r6265 sys.stdout, sys.stderr = oldio
Dirkjan Ochtman
hgweb: raise ErrorResponses to communicate protocol errors
r6926 req.respond(HTTP_OK, HGTYPE)
Dirkjan Ochtman
hgweb: use a single-element tuple to return from protocol.unbundle()...
r6788 return '%d\n%s' % (ret, val),
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 finally:
Ronny Pfannschmidt
switch lock releasing in the core from gc to explicit
r8109 lock.release()
Dirkjan Ochtman
improve changegroup.readbundle(), use it in hgweb
r6154 except ValueError, inst:
Dirkjan Ochtman
hgweb: raise ErrorResponses to communicate protocol errors
r6926 raise ErrorResponse(HTTP_OK, inst)
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 except (OSError, IOError), inst:
error = getattr(inst, 'strerror', 'Unknown error')
if inst.errno == errno.ENOENT:
Dirkjan Ochtman
hgweb: explicit response status
r5993 code = HTTP_NOT_FOUND
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 else:
Dirkjan Ochtman
hgweb: explicit response status
r5993 code = HTTP_SERVER_ERROR
Sune Foldager
hgweb: send proper error messages to the client...
r9694 filename = getattr(inst, 'filename', '')
# Don't send our filesystem layout to the client
if filename and filename.startswith(repo.root):
filename = filename[len(repo.root)+1:]
text = '%s: %s' % (error, filename)
else:
text = error.replace(repo.root + os.path.sep, '')
raise ErrorResponse(code, text)
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 finally:
fp.close()
os.unlink(tempname)
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 def stream_out(repo, req):
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE)
Dirkjan Ochtman
make streamclone.stream_out() a generator
r6925 try:
for chunk in streamclone.stream_out(repo, untrusted=True):
yield chunk
except streamclone.StreamException, inst:
yield str(inst)