##// END OF EJS Templates
protocol: wrap non-string protocol responses in classes
Dirkjan Ochtman -
r11625:cdeb8613 default
parent child Browse files
Show More
@@ -1,70 +1,78 b''
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import cStringIO, zlib, sys, urllib
8 import cStringIO, zlib, sys, urllib
9 from mercurial import util, wireproto
9 from mercurial import util, wireproto
10 from common import HTTP_OK
10 from common import HTTP_OK
11
11
12 HGTYPE = 'application/mercurial-0.1'
12 HGTYPE = 'application/mercurial-0.1'
13
13
14 class webproto(object):
14 class webproto(object):
15 def __init__(self, req):
15 def __init__(self, req):
16 self.req = req
16 self.req = req
17 self.response = ''
17 self.response = ''
18 def getargs(self, args):
18 def getargs(self, args):
19 data = {}
19 data = {}
20 keys = args.split()
20 keys = args.split()
21 for k in keys:
21 for k in keys:
22 if k == '*':
22 if k == '*':
23 star = {}
23 star = {}
24 for key in self.req.form.keys():
24 for key in self.req.form.keys():
25 if key not in keys:
25 if key not in keys:
26 star[key] = self.req.form[key][0]
26 star[key] = self.req.form[key][0]
27 data['*'] = star
27 data['*'] = star
28 else:
28 else:
29 data[k] = self.req.form[k][0]
29 data[k] = self.req.form[k][0]
30 return [data[k] for k in keys]
30 return [data[k] for k in keys]
31 def getfile(self, fp):
31 def getfile(self, fp):
32 length = int(self.req.env['CONTENT_LENGTH'])
32 length = int(self.req.env['CONTENT_LENGTH'])
33 for s in util.filechunkiter(self.req, limit=length):
33 for s in util.filechunkiter(self.req, limit=length):
34 fp.write(s)
34 fp.write(s)
35 def redirect(self):
35 def redirect(self):
36 self.oldio = sys.stdout, sys.stderr
36 self.oldio = sys.stdout, sys.stderr
37 sys.stderr = sys.stdout = cStringIO.StringIO()
37 sys.stderr = sys.stdout = cStringIO.StringIO()
38 def groupchunks(self, cg):
38 def groupchunks(self, cg):
39 z = zlib.compressobj()
39 z = zlib.compressobj()
40 while 1:
40 while 1:
41 chunk = cg.read(4096)
41 chunk = cg.read(4096)
42 if not chunk:
42 if not chunk:
43 break
43 break
44 yield z.compress(chunk)
44 yield z.compress(chunk)
45 yield z.flush()
45 yield z.flush()
46 def sendresponse(self, s):
46 def sendresponse(self, s):
47 self.req.respond(HTTP_OK, HGTYPE, length=len(s))
47 self.req.respond(HTTP_OK, HGTYPE, length=len(s))
48 self.response = s
48 self.response = s
49 def sendstream(self, source):
49 def sendstream(self, source):
50 self.req.respond(HTTP_OK, HGTYPE)
50 self.req.respond(HTTP_OK, HGTYPE)
51 for chunk in source:
51 for chunk in source.gen:
52 self.req.write(str(chunk))
52 self.req.write(chunk)
53 def sendpushresponse(self, ret):
53 def sendpushresponse(self, rsp):
54 val = sys.stdout.getvalue()
54 val = sys.stdout.getvalue()
55 sys.stdout, sys.stderr = self.oldio
55 sys.stdout, sys.stderr = self.oldio
56 self.req.respond(HTTP_OK, HGTYPE)
56 self.req.respond(HTTP_OK, HGTYPE)
57 self.response = '%d\n%s' % (ret, val)
57 self.response = '%d\n%s' % (rsp.res, val)
58
59 handlers = {
60 str: sendresponse,
61 wireproto.streamres: sendstream,
62 wireproto.pushres: sendpushresponse,
63 }
64
58 def _client(self):
65 def _client(self):
59 return 'remote:%s:%s:%s' % (
66 return 'remote:%s:%s:%s' % (
60 self.req.env.get('wsgi.url_scheme') or 'http',
67 self.req.env.get('wsgi.url_scheme') or 'http',
61 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
68 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
62 urllib.quote(self.req.env.get('REMOTE_USER', '')))
69 urllib.quote(self.req.env.get('REMOTE_USER', '')))
63
70
64 def iscmd(cmd):
71 def iscmd(cmd):
65 return cmd in wireproto.commands
72 return cmd in wireproto.commands
66
73
67 def call(repo, req, cmd):
74 def call(repo, req, cmd):
68 p = webproto(req)
75 p = webproto(req)
69 wireproto.dispatch(repo, p, cmd)
76 rsp = wireproto.dispatch(repo, p, cmd)
70 yield p.response
77 webproto.handlers[rsp.__class__](p, rsp)
78 return [p.response]
@@ -1,133 +1,140 b''
1 # sshserver.py - ssh protocol server support for mercurial
1 # sshserver.py - ssh protocol server support for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from i18n import _
9 from i18n import _
10 import util, hook, wireproto
10 import util, hook, wireproto
11 import os, sys
11 import os, sys
12
12
13 class sshserver(object):
13 class sshserver(object):
14 def __init__(self, ui, repo):
14 def __init__(self, ui, repo):
15 self.ui = ui
15 self.ui = ui
16 self.repo = repo
16 self.repo = repo
17 self.lock = None
17 self.lock = None
18 self.fin = sys.stdin
18 self.fin = sys.stdin
19 self.fout = sys.stdout
19 self.fout = sys.stdout
20
20
21 hook.redirect(True)
21 hook.redirect(True)
22 sys.stdout = sys.stderr
22 sys.stdout = sys.stderr
23
23
24 # Prevent insertion/deletion of CRs
24 # Prevent insertion/deletion of CRs
25 util.set_binary(self.fin)
25 util.set_binary(self.fin)
26 util.set_binary(self.fout)
26 util.set_binary(self.fout)
27
27
28 def getargs(self, args):
28 def getargs(self, args):
29 data = {}
29 data = {}
30 keys = args.split()
30 keys = args.split()
31 count = len(keys)
31 count = len(keys)
32 for n in xrange(len(keys)):
32 for n in xrange(len(keys)):
33 argline = self.fin.readline()[:-1]
33 argline = self.fin.readline()[:-1]
34 arg, l = argline.split()
34 arg, l = argline.split()
35 val = self.fin.read(int(l))
35 val = self.fin.read(int(l))
36 if arg not in keys:
36 if arg not in keys:
37 raise util.Abort("unexpected parameter %r" % arg)
37 raise util.Abort("unexpected parameter %r" % arg)
38 if arg == '*':
38 if arg == '*':
39 star = {}
39 star = {}
40 for n in xrange(int(l)):
40 for n in xrange(int(l)):
41 arg, l = argline.split()
41 arg, l = argline.split()
42 val = self.fin.read(int(l))
42 val = self.fin.read(int(l))
43 star[arg] = val
43 star[arg] = val
44 data['*'] = star
44 data['*'] = star
45 else:
45 else:
46 data[arg] = val
46 data[arg] = val
47 return [data[k] for k in keys]
47 return [data[k] for k in keys]
48
48
49 def getarg(self, name):
49 def getarg(self, name):
50 return self.getargs(name)[0]
50 return self.getargs(name)[0]
51
51
52 def getfile(self, fpout):
52 def getfile(self, fpout):
53 self.sendresponse('')
53 self.sendresponse('')
54 count = int(self.fin.readline())
54 count = int(self.fin.readline())
55 while count:
55 while count:
56 fpout.write(self.fin.read(count))
56 fpout.write(self.fin.read(count))
57 count = int(self.fin.readline())
57 count = int(self.fin.readline())
58
58
59 def redirect(self):
59 def redirect(self):
60 pass
60 pass
61
61
62 def groupchunks(self, changegroup):
62 def groupchunks(self, changegroup):
63 while True:
63 while True:
64 d = changegroup.read(4096)
64 d = changegroup.read(4096)
65 if not d:
65 if not d:
66 break
66 break
67 yield d
67 yield d
68
68
69 def sendresponse(self, v):
69 def sendresponse(self, v):
70 self.fout.write("%d\n" % len(v))
70 self.fout.write("%d\n" % len(v))
71 self.fout.write(v)
71 self.fout.write(v)
72 self.fout.flush()
72 self.fout.flush()
73
73
74 def sendstream(self, source):
74 def sendstream(self, source):
75 for chunk in source:
75 for chunk in source.gen:
76 self.fout.write(chunk)
76 self.fout.write(chunk)
77 self.fout.flush()
77 self.fout.flush()
78
78
79 def sendpushresponse(self, ret):
79 def sendpushresponse(self, rsp):
80 self.sendresponse('')
80 self.sendresponse('')
81 self.sendresponse(str(ret))
81 self.sendresponse(str(rsp.res))
82
82
83 def serve_forever(self):
83 def serve_forever(self):
84 try:
84 try:
85 while self.serve_one():
85 while self.serve_one():
86 pass
86 pass
87 finally:
87 finally:
88 if self.lock is not None:
88 if self.lock is not None:
89 self.lock.release()
89 self.lock.release()
90 sys.exit(0)
90 sys.exit(0)
91
91
92 handlers = {
93 str: sendresponse,
94 wireproto.streamres: sendstream,
95 wireproto.pushres: sendpushresponse,
96 }
97
92 def serve_one(self):
98 def serve_one(self):
93 cmd = self.fin.readline()[:-1]
99 cmd = self.fin.readline()[:-1]
94 if cmd and cmd in wireproto.commands:
100 if cmd and cmd in wireproto.commands:
95 wireproto.dispatch(self.repo, self, cmd)
101 rsp = wireproto.dispatch(self.repo, self, cmd)
102 self.handlers[rsp.__class__](self, rsp)
96 elif cmd:
103 elif cmd:
97 impl = getattr(self, 'do_' + cmd, None)
104 impl = getattr(self, 'do_' + cmd, None)
98 if impl:
105 if impl:
99 r = impl()
106 r = impl()
100 if r is not None:
107 if r is not None:
101 self.sendresponse(r)
108 self.sendresponse(r)
102 else: self.sendresponse("")
109 else: self.sendresponse("")
103 return cmd != ''
110 return cmd != ''
104
111
105 def do_lock(self):
112 def do_lock(self):
106 '''DEPRECATED - allowing remote client to lock repo is not safe'''
113 '''DEPRECATED - allowing remote client to lock repo is not safe'''
107
114
108 self.lock = self.repo.lock()
115 self.lock = self.repo.lock()
109 return ""
116 return ""
110
117
111 def do_unlock(self):
118 def do_unlock(self):
112 '''DEPRECATED'''
119 '''DEPRECATED'''
113
120
114 if self.lock:
121 if self.lock:
115 self.lock.release()
122 self.lock.release()
116 self.lock = None
123 self.lock = None
117 return ""
124 return ""
118
125
119 def do_addchangegroup(self):
126 def do_addchangegroup(self):
120 '''DEPRECATED'''
127 '''DEPRECATED'''
121
128
122 if not self.lock:
129 if not self.lock:
123 self.sendresponse("not locked")
130 self.sendresponse("not locked")
124 return
131 return
125
132
126 self.sendresponse("")
133 self.sendresponse("")
127 r = self.repo.addchangegroup(self.fin, 'serve', self._client(),
134 r = self.repo.addchangegroup(self.fin, 'serve', self._client(),
128 lock=self.lock)
135 lock=self.lock)
129 return str(r)
136 return str(r)
130
137
131 def _client(self):
138 def _client(self):
132 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
139 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
133 return 'remote:ssh:' + client
140 return 'remote:ssh:' + client
@@ -1,282 +1,288 b''
1 # wireproto.py - generic wire protocol support functions
1 # wireproto.py - generic wire protocol support functions
2 #
2 #
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import urllib, tempfile, os
8 import urllib, tempfile, os
9 from i18n import _
9 from i18n import _
10 from node import bin, hex
10 from node import bin, hex
11 import changegroup as changegroupmod
11 import changegroup as changegroupmod
12 import streamclone, repo, error, encoding, util
12 import streamclone, repo, error, encoding, util
13 import pushkey as pushkey_
13 import pushkey as pushkey_
14
14
15 # list of nodes encoding / decoding
15 # list of nodes encoding / decoding
16
16
17 def decodelist(l, sep=' '):
17 def decodelist(l, sep=' '):
18 return map(bin, l.split(sep))
18 return map(bin, l.split(sep))
19
19
20 def encodelist(l, sep=' '):
20 def encodelist(l, sep=' '):
21 return sep.join(map(hex, l))
21 return sep.join(map(hex, l))
22
22
23 # client side
23 # client side
24
24
25 class wirerepository(repo.repository):
25 class wirerepository(repo.repository):
26 def lookup(self, key):
26 def lookup(self, key):
27 self.requirecap('lookup', _('look up remote revision'))
27 self.requirecap('lookup', _('look up remote revision'))
28 d = self._call("lookup", key=key)
28 d = self._call("lookup", key=key)
29 success, data = d[:-1].split(" ", 1)
29 success, data = d[:-1].split(" ", 1)
30 if int(success):
30 if int(success):
31 return bin(data)
31 return bin(data)
32 self._abort(error.RepoError(data))
32 self._abort(error.RepoError(data))
33
33
34 def heads(self):
34 def heads(self):
35 d = self._call("heads")
35 d = self._call("heads")
36 try:
36 try:
37 return decodelist(d[:-1])
37 return decodelist(d[:-1])
38 except:
38 except:
39 self.abort(error.ResponseError(_("unexpected response:"), d))
39 self.abort(error.ResponseError(_("unexpected response:"), d))
40
40
41 def branchmap(self):
41 def branchmap(self):
42 d = self._call("branchmap")
42 d = self._call("branchmap")
43 try:
43 try:
44 branchmap = {}
44 branchmap = {}
45 for branchpart in d.splitlines():
45 for branchpart in d.splitlines():
46 branchname, branchheads = branchpart.split(' ', 1)
46 branchname, branchheads = branchpart.split(' ', 1)
47 branchname = urllib.unquote(branchname)
47 branchname = urllib.unquote(branchname)
48 # Earlier servers (1.3.x) send branch names in (their) local
48 # Earlier servers (1.3.x) send branch names in (their) local
49 # charset. The best we can do is assume it's identical to our
49 # charset. The best we can do is assume it's identical to our
50 # own local charset, in case it's not utf-8.
50 # own local charset, in case it's not utf-8.
51 try:
51 try:
52 branchname.decode('utf-8')
52 branchname.decode('utf-8')
53 except UnicodeDecodeError:
53 except UnicodeDecodeError:
54 branchname = encoding.fromlocal(branchname)
54 branchname = encoding.fromlocal(branchname)
55 branchheads = decodelist(branchheads)
55 branchheads = decodelist(branchheads)
56 branchmap[branchname] = branchheads
56 branchmap[branchname] = branchheads
57 return branchmap
57 return branchmap
58 except TypeError:
58 except TypeError:
59 self._abort(error.ResponseError(_("unexpected response:"), d))
59 self._abort(error.ResponseError(_("unexpected response:"), d))
60
60
61 def branches(self, nodes):
61 def branches(self, nodes):
62 n = encodelist(nodes)
62 n = encodelist(nodes)
63 d = self._call("branches", nodes=n)
63 d = self._call("branches", nodes=n)
64 try:
64 try:
65 br = [tuple(decodelist(b)) for b in d.splitlines()]
65 br = [tuple(decodelist(b)) for b in d.splitlines()]
66 return br
66 return br
67 except:
67 except:
68 self._abort(error.ResponseError(_("unexpected response:"), d))
68 self._abort(error.ResponseError(_("unexpected response:"), d))
69
69
70 def between(self, pairs):
70 def between(self, pairs):
71 batch = 8 # avoid giant requests
71 batch = 8 # avoid giant requests
72 r = []
72 r = []
73 for i in xrange(0, len(pairs), batch):
73 for i in xrange(0, len(pairs), batch):
74 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
74 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
75 d = self._call("between", pairs=n)
75 d = self._call("between", pairs=n)
76 try:
76 try:
77 r.extend(l and decodelist(l) or [] for l in d.splitlines())
77 r.extend(l and decodelist(l) or [] for l in d.splitlines())
78 except:
78 except:
79 self._abort(error.ResponseError(_("unexpected response:"), d))
79 self._abort(error.ResponseError(_("unexpected response:"), d))
80 return r
80 return r
81
81
82 def pushkey(self, namespace, key, old, new):
82 def pushkey(self, namespace, key, old, new):
83 if not self.capable('pushkey'):
83 if not self.capable('pushkey'):
84 return False
84 return False
85 d = self._call("pushkey",
85 d = self._call("pushkey",
86 namespace=namespace, key=key, old=old, new=new)
86 namespace=namespace, key=key, old=old, new=new)
87 return bool(int(d))
87 return bool(int(d))
88
88
89 def listkeys(self, namespace):
89 def listkeys(self, namespace):
90 if not self.capable('pushkey'):
90 if not self.capable('pushkey'):
91 return {}
91 return {}
92 d = self._call("listkeys", namespace=namespace)
92 d = self._call("listkeys", namespace=namespace)
93 r = {}
93 r = {}
94 for l in d.splitlines():
94 for l in d.splitlines():
95 k, v = l.split('\t')
95 k, v = l.split('\t')
96 r[k.decode('string-escape')] = v.decode('string-escape')
96 r[k.decode('string-escape')] = v.decode('string-escape')
97 return r
97 return r
98
98
99 def stream_out(self):
99 def stream_out(self):
100 return self._callstream('stream_out')
100 return self._callstream('stream_out')
101
101
102 def changegroup(self, nodes, kind):
102 def changegroup(self, nodes, kind):
103 n = encodelist(nodes)
103 n = encodelist(nodes)
104 f = self._callstream("changegroup", roots=n)
104 f = self._callstream("changegroup", roots=n)
105 return self._decompress(f)
105 return self._decompress(f)
106
106
107 def changegroupsubset(self, bases, heads, kind):
107 def changegroupsubset(self, bases, heads, kind):
108 self.requirecap('changegroupsubset', _('look up remote changes'))
108 self.requirecap('changegroupsubset', _('look up remote changes'))
109 bases = encodelist(bases)
109 bases = encodelist(bases)
110 heads = encodelist(heads)
110 heads = encodelist(heads)
111 return self._decompress(self._callstream("changegroupsubset",
111 return self._decompress(self._callstream("changegroupsubset",
112 bases=bases, heads=heads))
112 bases=bases, heads=heads))
113
113
114 def unbundle(self, cg, heads, source):
114 def unbundle(self, cg, heads, source):
115 '''Send cg (a readable file-like object representing the
115 '''Send cg (a readable file-like object representing the
116 changegroup to push, typically a chunkbuffer object) to the
116 changegroup to push, typically a chunkbuffer object) to the
117 remote server as a bundle. Return an integer indicating the
117 remote server as a bundle. Return an integer indicating the
118 result of the push (see localrepository.addchangegroup()).'''
118 result of the push (see localrepository.addchangegroup()).'''
119
119
120 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
120 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
121 if ret == "":
121 if ret == "":
122 raise error.ResponseError(
122 raise error.ResponseError(
123 _('push failed:'), output)
123 _('push failed:'), output)
124 try:
124 try:
125 ret = int(ret)
125 ret = int(ret)
126 except ValueError, err:
126 except ValueError, err:
127 raise error.ResponseError(
127 raise error.ResponseError(
128 _('push failed (unexpected response):'), ret)
128 _('push failed (unexpected response):'), ret)
129
129
130 for l in output.splitlines(True):
130 for l in output.splitlines(True):
131 self.ui.status(_('remote: '), l)
131 self.ui.status(_('remote: '), l)
132 return ret
132 return ret
133
133
134 # server side
134 # server side
135
135
136 class streamres(object):
137 def __init__(self, gen):
138 self.gen = gen
139
140 class pushres(object):
141 def __init__(self, res):
142 self.res = res
143
136 def dispatch(repo, proto, command):
144 def dispatch(repo, proto, command):
137 func, spec = commands[command]
145 func, spec = commands[command]
138 args = proto.getargs(spec)
146 args = proto.getargs(spec)
139 r = func(repo, proto, *args)
147 return func(repo, proto, *args)
140 if r != None:
141 proto.sendresponse(r)
142
148
143 def between(repo, proto, pairs):
149 def between(repo, proto, pairs):
144 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
150 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
145 r = []
151 r = []
146 for b in repo.between(pairs):
152 for b in repo.between(pairs):
147 r.append(encodelist(b) + "\n")
153 r.append(encodelist(b) + "\n")
148 return "".join(r)
154 return "".join(r)
149
155
150 def branchmap(repo, proto):
156 def branchmap(repo, proto):
151 branchmap = repo.branchmap()
157 branchmap = repo.branchmap()
152 heads = []
158 heads = []
153 for branch, nodes in branchmap.iteritems():
159 for branch, nodes in branchmap.iteritems():
154 branchname = urllib.quote(branch)
160 branchname = urllib.quote(branch)
155 branchnodes = encodelist(nodes)
161 branchnodes = encodelist(nodes)
156 heads.append('%s %s' % (branchname, branchnodes))
162 heads.append('%s %s' % (branchname, branchnodes))
157 return '\n'.join(heads)
163 return '\n'.join(heads)
158
164
159 def branches(repo, proto, nodes):
165 def branches(repo, proto, nodes):
160 nodes = decodelist(nodes)
166 nodes = decodelist(nodes)
161 r = []
167 r = []
162 for b in repo.branches(nodes):
168 for b in repo.branches(nodes):
163 r.append(encodelist(b) + "\n")
169 r.append(encodelist(b) + "\n")
164 return "".join(r)
170 return "".join(r)
165
171
166 def capabilities(repo, proto):
172 def capabilities(repo, proto):
167 caps = 'lookup changegroupsubset branchmap pushkey'.split()
173 caps = 'lookup changegroupsubset branchmap pushkey'.split()
168 if streamclone.allowed(repo.ui):
174 if streamclone.allowed(repo.ui):
169 caps.append('stream=%d' % repo.changelog.version)
175 caps.append('stream=%d' % repo.changelog.version)
170 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
176 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
171 return ' '.join(caps)
177 return ' '.join(caps)
172
178
173 def changegroup(repo, proto, roots):
179 def changegroup(repo, proto, roots):
174 nodes = decodelist(roots)
180 nodes = decodelist(roots)
175 cg = repo.changegroup(nodes, 'serve')
181 cg = repo.changegroup(nodes, 'serve')
176 proto.sendstream(proto.groupchunks(cg))
182 return streamres(proto.groupchunks(cg))
177
183
178 def changegroupsubset(repo, proto, bases, heads):
184 def changegroupsubset(repo, proto, bases, heads):
179 bases = decodelist(bases)
185 bases = decodelist(bases)
180 heads = decodelist(heads)
186 heads = decodelist(heads)
181 cg = repo.changegroupsubset(bases, heads, 'serve')
187 cg = repo.changegroupsubset(bases, heads, 'serve')
182 proto.sendstream(proto.groupchunks(cg))
188 return streamres(proto.groupchunks(cg))
183
189
184 def heads(repo, proto):
190 def heads(repo, proto):
185 h = repo.heads()
191 h = repo.heads()
186 return encodelist(h) + "\n"
192 return encodelist(h) + "\n"
187
193
188 def hello(repo, proto):
194 def hello(repo, proto):
189 '''the hello command returns a set of lines describing various
195 '''the hello command returns a set of lines describing various
190 interesting things about the server, in an RFC822-like format.
196 interesting things about the server, in an RFC822-like format.
191 Currently the only one defined is "capabilities", which
197 Currently the only one defined is "capabilities", which
192 consists of a line in the form:
198 consists of a line in the form:
193
199
194 capabilities: space separated list of tokens
200 capabilities: space separated list of tokens
195 '''
201 '''
196 return "capabilities: %s\n" % (capabilities(repo, proto))
202 return "capabilities: %s\n" % (capabilities(repo, proto))
197
203
198 def listkeys(repo, proto, namespace):
204 def listkeys(repo, proto, namespace):
199 d = pushkey_.list(repo, namespace).items()
205 d = pushkey_.list(repo, namespace).items()
200 t = '\n'.join(['%s\t%s' % (k.encode('string-escape'),
206 t = '\n'.join(['%s\t%s' % (k.encode('string-escape'),
201 v.encode('string-escape')) for k, v in d])
207 v.encode('string-escape')) for k, v in d])
202 return t
208 return t
203
209
204 def lookup(repo, proto, key):
210 def lookup(repo, proto, key):
205 try:
211 try:
206 r = hex(repo.lookup(key))
212 r = hex(repo.lookup(key))
207 success = 1
213 success = 1
208 except Exception, inst:
214 except Exception, inst:
209 r = str(inst)
215 r = str(inst)
210 success = 0
216 success = 0
211 return "%s %s\n" % (success, r)
217 return "%s %s\n" % (success, r)
212
218
213 def pushkey(repo, proto, namespace, key, old, new):
219 def pushkey(repo, proto, namespace, key, old, new):
214 r = pushkey_.push(repo, namespace, key, old, new)
220 r = pushkey_.push(repo, namespace, key, old, new)
215 return '%s\n' % int(r)
221 return '%s\n' % int(r)
216
222
217 def stream(repo, proto):
223 def stream(repo, proto):
218 proto.sendstream(streamclone.stream_out(repo))
224 return streamres(streamclone.stream_out(repo))
219
225
220 def unbundle(repo, proto, heads):
226 def unbundle(repo, proto, heads):
221 their_heads = decodelist(heads)
227 their_heads = decodelist(heads)
222
228
223 def check_heads():
229 def check_heads():
224 heads = repo.heads()
230 heads = repo.heads()
225 return their_heads == ['force'] or their_heads == heads
231 return their_heads == ['force'] or their_heads == heads
226
232
227 # fail early if possible
233 # fail early if possible
228 if not check_heads():
234 if not check_heads():
229 return 'unsynced changes'
235 return 'unsynced changes'
230
236
231 # write bundle data to temporary file because it can be big
237 # write bundle data to temporary file because it can be big
232 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
238 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
233 fp = os.fdopen(fd, 'wb+')
239 fp = os.fdopen(fd, 'wb+')
234 r = 0
240 r = 0
235 proto.redirect()
241 proto.redirect()
236 try:
242 try:
237 proto.getfile(fp)
243 proto.getfile(fp)
238 lock = repo.lock()
244 lock = repo.lock()
239 try:
245 try:
240 if not check_heads():
246 if not check_heads():
241 # someone else committed/pushed/unbundled while we
247 # someone else committed/pushed/unbundled while we
242 # were transferring data
248 # were transferring data
243 return 'unsynced changes'
249 return 'unsynced changes'
244
250
245 # push can proceed
251 # push can proceed
246 fp.seek(0)
252 fp.seek(0)
247 header = fp.read(6)
253 header = fp.read(6)
248 if header.startswith('HG'):
254 if header.startswith('HG'):
249 if not header.startswith('HG10'):
255 if not header.startswith('HG10'):
250 raise ValueError('unknown bundle version')
256 raise ValueError('unknown bundle version')
251 elif header not in changegroupmod.bundletypes:
257 elif header not in changegroupmod.bundletypes:
252 raise ValueError('unknown bundle compression type')
258 raise ValueError('unknown bundle compression type')
253 gen = changegroupmod.unbundle(header, fp)
259 gen = changegroupmod.unbundle(header, fp)
254
260
255 try:
261 try:
256 r = repo.addchangegroup(gen, 'serve', proto._client(),
262 r = repo.addchangegroup(gen, 'serve', proto._client(),
257 lock=lock)
263 lock=lock)
258 except util.Abort, inst:
264 except util.Abort, inst:
259 sys.stderr.write("abort: %s\n" % inst)
265 sys.stderr.write("abort: %s\n" % inst)
260 finally:
266 finally:
261 lock.release()
267 lock.release()
262 proto.sendpushresponse(r)
268 return pushres(r)
263
269
264 finally:
270 finally:
265 fp.close()
271 fp.close()
266 os.unlink(tempname)
272 os.unlink(tempname)
267
273
268 commands = {
274 commands = {
269 'between': (between, 'pairs'),
275 'between': (between, 'pairs'),
270 'branchmap': (branchmap, ''),
276 'branchmap': (branchmap, ''),
271 'branches': (branches, 'nodes'),
277 'branches': (branches, 'nodes'),
272 'capabilities': (capabilities, ''),
278 'capabilities': (capabilities, ''),
273 'changegroup': (changegroup, 'roots'),
279 'changegroup': (changegroup, 'roots'),
274 'changegroupsubset': (changegroupsubset, 'bases heads'),
280 'changegroupsubset': (changegroupsubset, 'bases heads'),
275 'heads': (heads, ''),
281 'heads': (heads, ''),
276 'hello': (hello, ''),
282 'hello': (hello, ''),
277 'listkeys': (listkeys, 'namespace'),
283 'listkeys': (listkeys, 'namespace'),
278 'lookup': (lookup, 'key'),
284 'lookup': (lookup, 'key'),
279 'pushkey': (pushkey, 'namespace key old new'),
285 'pushkey': (pushkey, 'namespace key old new'),
280 'stream_out': (stream, ''),
286 'stream_out': (stream, ''),
281 'unbundle': (unbundle, 'heads'),
287 'unbundle': (unbundle, 'heads'),
282 }
288 }
General Comments 0
You need to be logged in to leave comments. Login now