##// END OF EJS Templates
Merge with crew-stable
Merge with crew-stable

File last commit:

r6335:e29557d6 default
r6475:93e4bb8c merge default
Show More
protocol.py
228 lines | 7.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
Dirkjan Ochtman
hgweb: explicit response status
r5993 from common import 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
separate the wire protocol commands from the user interface commands
r5598 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)
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE, length=len(resp))
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 req.write(resp)
def heads(web, req):
resp = " ".join(map(hex, web.repo.heads())) + "\n"
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE, length=len(resp))
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 req.write(resp)
def branches(web, req):
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()
for b in web.repo.branches(nodes):
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
separate the wire protocol commands from the user interface commands
r5598 req.write(resp)
def between(web, 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()
for b in web.repo.between(pairs):
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
separate the wire protocol commands from the user interface commands
r5598 req.write(resp)
def changegroup(web, 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 = []
if not web.allowpull:
return
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()
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):
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 = []
if not web.allowpull:
return
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()
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):
Dirkjan Ochtman
hgweb: use bundletypes from mercurial.changegroup
r6152 resp = ' '.join(web.capabilities())
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.respond(HTTP_OK, HGTYPE, length=len(resp))
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 req.write(resp)
def unbundle(web, req):
Dirkjan Ochtman
hgweb: only accept POST requests for unbundle
r6335
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 def bail(response, headers={}):
Dirkjan Ochtman
hgweb: only accept POST requests for unbundle
r6335 length = int(req.env.get('CONTENT_LENGTH', 0))
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 for s in util.filechunkiter(req, limit=length):
# drain incoming bundle, else client will not see
# response when run outside cgi script
pass
Dirkjan Ochtman
hgweb: only accept POST requests for unbundle
r6335
status = headers.pop('status', HTTP_OK)
Dirkjan Ochtman
hgweb: explicit response status
r5993 req.header(headers.items())
Dirkjan Ochtman
hgweb: only accept POST requests for unbundle
r6335 req.respond(status, HGTYPE)
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 req.write('0\n')
req.write(response)
Dirkjan Ochtman
hgweb: only accept POST requests for unbundle
r6335 # enforce that you can only unbundle with POST requests
if req.env['REQUEST_METHOD'] != 'POST':
headers = {'status': '405 Method Not Allowed'}
bail('unbundle requires POST request\n', headers)
return
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 # 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':
Dirkjan Ochtman
hgweb: no i18n in protocol responses
r6155 bail('ssl required\n')
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 return
proto = 'https'
else:
proto = 'http'
# do not allow push unless explicitly allowed
if not web.check_perm(req, 'push', False):
Dirkjan Ochtman
hgweb: only accept POST requests for unbundle
r6335 bail('push not authorized\n', headers={'status': '401 Unauthorized'})
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 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():
Dirkjan Ochtman
hgweb: no i18n in protocol responses
r6155 bail('unsynced changes\n')
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 return
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
# 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')
Dirkjan Ochtman
hgweb: no i18n in protocol responses
r6155 req.write('unsynced changes\n')
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 return
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
improve changegroup.readbundle(), use it in hgweb
r6154 ret = web.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
separate the wire protocol commands from the user interface commands
r5598 req.write('%d\n' % ret)
req.write(val)
finally:
del lock
Dirkjan Ochtman
improve changegroup.readbundle(), use it in hgweb
r6154 except ValueError, inst:
req.write('0\n')
req.write(str(inst) + '\n')
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 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:
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
req.respond(code)
req.write('%s: %s\n' % (error, filename))
Dirkjan Ochtman
separate the wire protocol commands from the user interface commands
r5598 finally:
fp.close()
os.unlink(tempname)
def stream_out(web, 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 streamclone.stream_out(web.repo, req, untrusted=True)