##// END OF EJS Templates
hgweb: pre-init mimetypes module (fixes ugly bug in python-2.6.2 mimetypes)...
hgweb: pre-init mimetypes module (fixes ugly bug in python-2.6.2 mimetypes) The ugly initialization hack in mimetypes' initialization procedure can make hg serve hit the maximum recursion depth when in threaded mode.

File last commit:

r8109:496ae1ea default
r8224:1075f5c1 default
Show More
protocol.py
192 lines | 6.1 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>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
Joel Rosdahl
Remove unused imports
r6212 import cStringIO, zlib, tempfile, errno, os, sys
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',
]
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
except Exception,inst:
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
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):
Dirkjan Ochtman
hgweb: move capabilities calculation back into hgweb.protocol
r6780 caps = ['lookup', 'changegroupsubset']
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:
url = 'remote:%s:%s' % (proto,
req.env.get('REMOTE_HOST', ''))
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:
filename = getattr(inst, 'filename', '')
# Don't send our filesystem layout to the client
Dirkjan Ochtman
hgweb: protocol functions take repo instead of web...
r6781 if filename.startswith(repo.root):
filename = filename[len(repo.root)+1:]
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 else:
filename = ''
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
Dirkjan Ochtman
hgweb: raise ErrorResponses to communicate protocol errors
r6926 raise ErrorResponse(code, '%s: %s' % (error, filename))
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)