##// END OF EJS Templates
wireproto: fix handling of '*' args for HTTP and SSH
Peter Arrenbrecht -
r13721:3458c15a default
parent child Browse files
Show More
@@ -1,75 +1,75 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 != 'cmd' and 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 _client(self):
46 def _client(self):
47 return 'remote:%s:%s:%s' % (
47 return 'remote:%s:%s:%s' % (
48 self.req.env.get('wsgi.url_scheme') or 'http',
48 self.req.env.get('wsgi.url_scheme') or 'http',
49 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
49 urllib.quote(self.req.env.get('REMOTE_HOST', '')),
50 urllib.quote(self.req.env.get('REMOTE_USER', '')))
50 urllib.quote(self.req.env.get('REMOTE_USER', '')))
51
51
52 def iscmd(cmd):
52 def iscmd(cmd):
53 return cmd in wireproto.commands
53 return cmd in wireproto.commands
54
54
55 def call(repo, req, cmd):
55 def call(repo, req, cmd):
56 p = webproto(req)
56 p = webproto(req)
57 rsp = wireproto.dispatch(repo, p, cmd)
57 rsp = wireproto.dispatch(repo, p, cmd)
58 if isinstance(rsp, str):
58 if isinstance(rsp, str):
59 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
59 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
60 return [rsp]
60 return [rsp]
61 elif isinstance(rsp, wireproto.streamres):
61 elif isinstance(rsp, wireproto.streamres):
62 req.respond(HTTP_OK, HGTYPE)
62 req.respond(HTTP_OK, HGTYPE)
63 return rsp.gen
63 return rsp.gen
64 elif isinstance(rsp, wireproto.pushres):
64 elif isinstance(rsp, wireproto.pushres):
65 val = sys.stdout.getvalue()
65 val = sys.stdout.getvalue()
66 sys.stdout, sys.stderr = p.oldio
66 sys.stdout, sys.stderr = p.oldio
67 req.respond(HTTP_OK, HGTYPE)
67 req.respond(HTTP_OK, HGTYPE)
68 return ['%d\n%s' % (rsp.res, val)]
68 return ['%d\n%s' % (rsp.res, val)]
69 elif isinstance(rsp, wireproto.pusherr):
69 elif isinstance(rsp, wireproto.pusherr):
70 # drain the incoming bundle
70 # drain the incoming bundle
71 req.drain()
71 req.drain()
72 sys.stdout, sys.stderr = p.oldio
72 sys.stdout, sys.stderr = p.oldio
73 rsp = '0\n%s\n' % rsp.res
73 rsp = '0\n%s\n' % rsp.res
74 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
74 req.respond(HTTP_OK, HGTYPE, length=len(rsp))
75 return [rsp]
75 return [rsp]
@@ -1,200 +1,215 b''
1 # sshrepo.py - ssh repository proxy class for mercurial
1 # sshrepo.py - ssh repository proxy class for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 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 from i18n import _
8 from i18n import _
9 import util, error, wireproto
9 import util, error, wireproto
10 import re
10 import re
11
11
12 class remotelock(object):
12 class remotelock(object):
13 def __init__(self, repo):
13 def __init__(self, repo):
14 self.repo = repo
14 self.repo = repo
15 def release(self):
15 def release(self):
16 self.repo.unlock()
16 self.repo.unlock()
17 self.repo = None
17 self.repo = None
18 def __del__(self):
18 def __del__(self):
19 if self.repo:
19 if self.repo:
20 self.release()
20 self.release()
21
21
22 class sshrepository(wireproto.wirerepository):
22 class sshrepository(wireproto.wirerepository):
23 def __init__(self, ui, path, create=0):
23 def __init__(self, ui, path, create=0):
24 self._url = path
24 self._url = path
25 self.ui = ui
25 self.ui = ui
26
26
27 m = re.match(r'^ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?$', path)
27 m = re.match(r'^ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?$', path)
28 if not m:
28 if not m:
29 self._abort(error.RepoError(_("couldn't parse location %s") % path))
29 self._abort(error.RepoError(_("couldn't parse location %s") % path))
30
30
31 self.user = m.group(2)
31 self.user = m.group(2)
32 if self.user and ':' in self.user:
32 if self.user and ':' in self.user:
33 self._abort(error.RepoError(_("password in URL not supported")))
33 self._abort(error.RepoError(_("password in URL not supported")))
34 self.host = m.group(3)
34 self.host = m.group(3)
35 self.port = m.group(5)
35 self.port = m.group(5)
36 self.path = m.group(7) or "."
36 self.path = m.group(7) or "."
37
37
38 sshcmd = self.ui.config("ui", "ssh", "ssh")
38 sshcmd = self.ui.config("ui", "ssh", "ssh")
39 remotecmd = self.ui.config("ui", "remotecmd", "hg")
39 remotecmd = self.ui.config("ui", "remotecmd", "hg")
40
40
41 args = util.sshargs(sshcmd, self.host, self.user, self.port)
41 args = util.sshargs(sshcmd, self.host, self.user, self.port)
42
42
43 if create:
43 if create:
44 cmd = '%s %s "%s init %s"'
44 cmd = '%s %s "%s init %s"'
45 cmd = cmd % (sshcmd, args, remotecmd, self.path)
45 cmd = cmd % (sshcmd, args, remotecmd, self.path)
46
46
47 ui.note(_('running %s\n') % cmd)
47 ui.note(_('running %s\n') % cmd)
48 res = util.system(cmd)
48 res = util.system(cmd)
49 if res != 0:
49 if res != 0:
50 self._abort(error.RepoError(_("could not create remote repo")))
50 self._abort(error.RepoError(_("could not create remote repo")))
51
51
52 self.validate_repo(ui, sshcmd, args, remotecmd)
52 self.validate_repo(ui, sshcmd, args, remotecmd)
53
53
54 def url(self):
54 def url(self):
55 return self._url
55 return self._url
56
56
57 def validate_repo(self, ui, sshcmd, args, remotecmd):
57 def validate_repo(self, ui, sshcmd, args, remotecmd):
58 # cleanup up previous run
58 # cleanup up previous run
59 self.cleanup()
59 self.cleanup()
60
60
61 cmd = '%s %s "%s -R %s serve --stdio"'
61 cmd = '%s %s "%s -R %s serve --stdio"'
62 cmd = cmd % (sshcmd, args, remotecmd, self.path)
62 cmd = cmd % (sshcmd, args, remotecmd, self.path)
63
63
64 cmd = util.quotecommand(cmd)
64 cmd = util.quotecommand(cmd)
65 ui.note(_('running %s\n') % cmd)
65 ui.note(_('running %s\n') % cmd)
66 self.pipeo, self.pipei, self.pipee = util.popen3(cmd)
66 self.pipeo, self.pipei, self.pipee = util.popen3(cmd)
67
67
68 # skip any noise generated by remote shell
68 # skip any noise generated by remote shell
69 self._callstream("hello")
69 self._callstream("hello")
70 r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
70 r = self._callstream("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
71 lines = ["", "dummy"]
71 lines = ["", "dummy"]
72 max_noise = 500
72 max_noise = 500
73 while lines[-1] and max_noise:
73 while lines[-1] and max_noise:
74 l = r.readline()
74 l = r.readline()
75 self.readerr()
75 self.readerr()
76 if lines[-1] == "1\n" and l == "\n":
76 if lines[-1] == "1\n" and l == "\n":
77 break
77 break
78 if l:
78 if l:
79 ui.debug("remote: ", l)
79 ui.debug("remote: ", l)
80 lines.append(l)
80 lines.append(l)
81 max_noise -= 1
81 max_noise -= 1
82 else:
82 else:
83 self._abort(error.RepoError(_("no suitable response from remote hg")))
83 self._abort(error.RepoError(_("no suitable response from remote hg")))
84
84
85 self.capabilities = set()
85 self.capabilities = set()
86 for l in reversed(lines):
86 for l in reversed(lines):
87 if l.startswith("capabilities:"):
87 if l.startswith("capabilities:"):
88 self.capabilities.update(l[:-1].split(":")[1].split())
88 self.capabilities.update(l[:-1].split(":")[1].split())
89 break
89 break
90
90
91 def readerr(self):
91 def readerr(self):
92 while 1:
92 while 1:
93 size = util.fstat(self.pipee).st_size
93 size = util.fstat(self.pipee).st_size
94 if size == 0:
94 if size == 0:
95 break
95 break
96 s = self.pipee.read(size)
96 s = self.pipee.read(size)
97 if not s:
97 if not s:
98 break
98 break
99 for l in s.splitlines():
99 for l in s.splitlines():
100 self.ui.status(_("remote: "), l, '\n')
100 self.ui.status(_("remote: "), l, '\n')
101
101
102 def _abort(self, exception):
102 def _abort(self, exception):
103 self.cleanup()
103 self.cleanup()
104 raise exception
104 raise exception
105
105
106 def cleanup(self):
106 def cleanup(self):
107 try:
107 try:
108 self.pipeo.close()
108 self.pipeo.close()
109 self.pipei.close()
109 self.pipei.close()
110 # read the error descriptor until EOF
110 # read the error descriptor until EOF
111 for l in self.pipee:
111 for l in self.pipee:
112 self.ui.status(_("remote: "), l)
112 self.ui.status(_("remote: "), l)
113 self.pipee.close()
113 self.pipee.close()
114 except:
114 except:
115 pass
115 pass
116
116
117 __del__ = cleanup
117 __del__ = cleanup
118
118
119 def _callstream(self, cmd, **args):
119 def _callstream(self, cmd, **args):
120 self.ui.debug("sending %s command\n" % cmd)
120 self.ui.debug("sending %s command\n" % cmd)
121 self.pipeo.write("%s\n" % cmd)
121 self.pipeo.write("%s\n" % cmd)
122 for k, v in sorted(args.iteritems()):
122 _func, names = wireproto.commands[cmd]
123 keys = names.split()
124 wireargs = {}
125 for k in keys:
126 if k == '*':
127 wireargs['*'] = args
128 break
129 else:
130 wireargs[k] = args[k]
131 del args[k]
132 for k, v in sorted(wireargs.iteritems()):
123 self.pipeo.write("%s %d\n" % (k, len(v)))
133 self.pipeo.write("%s %d\n" % (k, len(v)))
124 self.pipeo.write(v)
134 if isinstance(v, dict):
135 for dk, dv in v.iteritems():
136 self.pipeo.write("%s %d\n" % (dk, len(dv)))
137 self.pipeo.write(dv)
138 else:
139 self.pipeo.write(v)
125 self.pipeo.flush()
140 self.pipeo.flush()
126
141
127 return self.pipei
142 return self.pipei
128
143
129 def _call(self, cmd, **args):
144 def _call(self, cmd, **args):
130 self._callstream(cmd, **args)
145 self._callstream(cmd, **args)
131 return self._recv()
146 return self._recv()
132
147
133 def _callpush(self, cmd, fp, **args):
148 def _callpush(self, cmd, fp, **args):
134 r = self._call(cmd, **args)
149 r = self._call(cmd, **args)
135 if r:
150 if r:
136 return '', r
151 return '', r
137 while 1:
152 while 1:
138 d = fp.read(4096)
153 d = fp.read(4096)
139 if not d:
154 if not d:
140 break
155 break
141 self._send(d)
156 self._send(d)
142 self._send("", flush=True)
157 self._send("", flush=True)
143 r = self._recv()
158 r = self._recv()
144 if r:
159 if r:
145 return '', r
160 return '', r
146 return self._recv(), ''
161 return self._recv(), ''
147
162
148 def _decompress(self, stream):
163 def _decompress(self, stream):
149 return stream
164 return stream
150
165
151 def _recv(self):
166 def _recv(self):
152 l = self.pipei.readline()
167 l = self.pipei.readline()
153 self.readerr()
168 self.readerr()
154 try:
169 try:
155 l = int(l)
170 l = int(l)
156 except:
171 except:
157 self._abort(error.ResponseError(_("unexpected response:"), l))
172 self._abort(error.ResponseError(_("unexpected response:"), l))
158 return self.pipei.read(l)
173 return self.pipei.read(l)
159
174
160 def _send(self, data, flush=False):
175 def _send(self, data, flush=False):
161 self.pipeo.write("%d\n" % len(data))
176 self.pipeo.write("%d\n" % len(data))
162 if data:
177 if data:
163 self.pipeo.write(data)
178 self.pipeo.write(data)
164 if flush:
179 if flush:
165 self.pipeo.flush()
180 self.pipeo.flush()
166 self.readerr()
181 self.readerr()
167
182
168 def lock(self):
183 def lock(self):
169 self._call("lock")
184 self._call("lock")
170 return remotelock(self)
185 return remotelock(self)
171
186
172 def unlock(self):
187 def unlock(self):
173 self._call("unlock")
188 self._call("unlock")
174
189
175 def addchangegroup(self, cg, source, url):
190 def addchangegroup(self, cg, source, url):
176 '''Send a changegroup to the remote server. Return an integer
191 '''Send a changegroup to the remote server. Return an integer
177 similar to unbundle(). DEPRECATED, since it requires locking the
192 similar to unbundle(). DEPRECATED, since it requires locking the
178 remote.'''
193 remote.'''
179 d = self._call("addchangegroup")
194 d = self._call("addchangegroup")
180 if d:
195 if d:
181 self._abort(error.RepoError(_("push refused: %s") % d))
196 self._abort(error.RepoError(_("push refused: %s") % d))
182 while 1:
197 while 1:
183 d = cg.read(4096)
198 d = cg.read(4096)
184 if not d:
199 if not d:
185 break
200 break
186 self.pipeo.write(d)
201 self.pipeo.write(d)
187 self.readerr()
202 self.readerr()
188
203
189 self.pipeo.flush()
204 self.pipeo.flush()
190
205
191 self.readerr()
206 self.readerr()
192 r = self._recv()
207 r = self._recv()
193 if not r:
208 if not r:
194 return 1
209 return 1
195 try:
210 try:
196 return int(r)
211 return int(r)
197 except:
212 except:
198 self._abort(error.ResponseError(_("unexpected response:"), r))
213 self._abort(error.ResponseError(_("unexpected response:"), r))
199
214
200 instance = sshrepository
215 instance = sshrepository
@@ -1,143 +1,144 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 import util, hook, wireproto, changegroup
9 import util, hook, wireproto, changegroup
10 import os, sys
10 import os, sys
11
11
12 class sshserver(object):
12 class sshserver(object):
13 def __init__(self, ui, repo):
13 def __init__(self, ui, repo):
14 self.ui = ui
14 self.ui = ui
15 self.repo = repo
15 self.repo = repo
16 self.lock = None
16 self.lock = None
17 self.fin = sys.stdin
17 self.fin = sys.stdin
18 self.fout = sys.stdout
18 self.fout = sys.stdout
19
19
20 hook.redirect(True)
20 hook.redirect(True)
21 sys.stdout = sys.stderr
21 sys.stdout = sys.stderr
22
22
23 # Prevent insertion/deletion of CRs
23 # Prevent insertion/deletion of CRs
24 util.set_binary(self.fin)
24 util.set_binary(self.fin)
25 util.set_binary(self.fout)
25 util.set_binary(self.fout)
26
26
27 def getargs(self, args):
27 def getargs(self, args):
28 data = {}
28 data = {}
29 keys = args.split()
29 keys = args.split()
30 for n in xrange(len(keys)):
30 for n in xrange(len(keys)):
31 argline = self.fin.readline()[:-1]
31 argline = self.fin.readline()[:-1]
32 arg, l = argline.split()
32 arg, l = argline.split()
33 val = self.fin.read(int(l))
34 if arg not in keys:
33 if arg not in keys:
35 raise util.Abort("unexpected parameter %r" % arg)
34 raise util.Abort("unexpected parameter %r" % arg)
36 if arg == '*':
35 if arg == '*':
37 star = {}
36 star = {}
38 for n in xrange(int(l)):
37 for k in xrange(int(l)):
38 argline = self.fin.readline()[:-1]
39 arg, l = argline.split()
39 arg, l = argline.split()
40 val = self.fin.read(int(l))
40 val = self.fin.read(int(l))
41 star[arg] = val
41 star[arg] = val
42 data['*'] = star
42 data['*'] = star
43 else:
43 else:
44 val = self.fin.read(int(l))
44 data[arg] = val
45 data[arg] = val
45 return [data[k] for k in keys]
46 return [data[k] for k in keys]
46
47
47 def getarg(self, name):
48 def getarg(self, name):
48 return self.getargs(name)[0]
49 return self.getargs(name)[0]
49
50
50 def getfile(self, fpout):
51 def getfile(self, fpout):
51 self.sendresponse('')
52 self.sendresponse('')
52 count = int(self.fin.readline())
53 count = int(self.fin.readline())
53 while count:
54 while count:
54 fpout.write(self.fin.read(count))
55 fpout.write(self.fin.read(count))
55 count = int(self.fin.readline())
56 count = int(self.fin.readline())
56
57
57 def redirect(self):
58 def redirect(self):
58 pass
59 pass
59
60
60 def groupchunks(self, changegroup):
61 def groupchunks(self, changegroup):
61 while True:
62 while True:
62 d = changegroup.read(4096)
63 d = changegroup.read(4096)
63 if not d:
64 if not d:
64 break
65 break
65 yield d
66 yield d
66
67
67 def sendresponse(self, v):
68 def sendresponse(self, v):
68 self.fout.write("%d\n" % len(v))
69 self.fout.write("%d\n" % len(v))
69 self.fout.write(v)
70 self.fout.write(v)
70 self.fout.flush()
71 self.fout.flush()
71
72
72 def sendstream(self, source):
73 def sendstream(self, source):
73 for chunk in source.gen:
74 for chunk in source.gen:
74 self.fout.write(chunk)
75 self.fout.write(chunk)
75 self.fout.flush()
76 self.fout.flush()
76
77
77 def sendpushresponse(self, rsp):
78 def sendpushresponse(self, rsp):
78 self.sendresponse('')
79 self.sendresponse('')
79 self.sendresponse(str(rsp.res))
80 self.sendresponse(str(rsp.res))
80
81
81 def sendpusherror(self, rsp):
82 def sendpusherror(self, rsp):
82 self.sendresponse(rsp.res)
83 self.sendresponse(rsp.res)
83
84
84 def serve_forever(self):
85 def serve_forever(self):
85 try:
86 try:
86 while self.serve_one():
87 while self.serve_one():
87 pass
88 pass
88 finally:
89 finally:
89 if self.lock is not None:
90 if self.lock is not None:
90 self.lock.release()
91 self.lock.release()
91 sys.exit(0)
92 sys.exit(0)
92
93
93 handlers = {
94 handlers = {
94 str: sendresponse,
95 str: sendresponse,
95 wireproto.streamres: sendstream,
96 wireproto.streamres: sendstream,
96 wireproto.pushres: sendpushresponse,
97 wireproto.pushres: sendpushresponse,
97 wireproto.pusherr: sendpusherror,
98 wireproto.pusherr: sendpusherror,
98 }
99 }
99
100
100 def serve_one(self):
101 def serve_one(self):
101 cmd = self.fin.readline()[:-1]
102 cmd = self.fin.readline()[:-1]
102 if cmd and cmd in wireproto.commands:
103 if cmd and cmd in wireproto.commands:
103 rsp = wireproto.dispatch(self.repo, self, cmd)
104 rsp = wireproto.dispatch(self.repo, self, cmd)
104 self.handlers[rsp.__class__](self, rsp)
105 self.handlers[rsp.__class__](self, rsp)
105 elif cmd:
106 elif cmd:
106 impl = getattr(self, 'do_' + cmd, None)
107 impl = getattr(self, 'do_' + cmd, None)
107 if impl:
108 if impl:
108 r = impl()
109 r = impl()
109 if r is not None:
110 if r is not None:
110 self.sendresponse(r)
111 self.sendresponse(r)
111 else: self.sendresponse("")
112 else: self.sendresponse("")
112 return cmd != ''
113 return cmd != ''
113
114
114 def do_lock(self):
115 def do_lock(self):
115 '''DEPRECATED - allowing remote client to lock repo is not safe'''
116 '''DEPRECATED - allowing remote client to lock repo is not safe'''
116
117
117 self.lock = self.repo.lock()
118 self.lock = self.repo.lock()
118 return ""
119 return ""
119
120
120 def do_unlock(self):
121 def do_unlock(self):
121 '''DEPRECATED'''
122 '''DEPRECATED'''
122
123
123 if self.lock:
124 if self.lock:
124 self.lock.release()
125 self.lock.release()
125 self.lock = None
126 self.lock = None
126 return ""
127 return ""
127
128
128 def do_addchangegroup(self):
129 def do_addchangegroup(self):
129 '''DEPRECATED'''
130 '''DEPRECATED'''
130
131
131 if not self.lock:
132 if not self.lock:
132 self.sendresponse("not locked")
133 self.sendresponse("not locked")
133 return
134 return
134
135
135 self.sendresponse("")
136 self.sendresponse("")
136 cg = changegroup.unbundle10(self.fin, "UN")
137 cg = changegroup.unbundle10(self.fin, "UN")
137 r = self.repo.addchangegroup(cg, 'serve', self._client(),
138 r = self.repo.addchangegroup(cg, 'serve', self._client(),
138 lock=self.lock)
139 lock=self.lock)
139 return str(r)
140 return str(r)
140
141
141 def _client(self):
142 def _client(self):
142 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
143 client = os.environ.get('SSH_CLIENT', '').split(' ', 1)[0]
143 return 'remote:ssh:' + client
144 return 'remote:ssh:' + client
@@ -1,366 +1,379 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, sys
8 import urllib, tempfile, os, sys
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 repo, error, encoding, util, store
12 import repo, error, encoding, util, store
13 import pushkey as pushkeymod
13 import pushkey as pushkeymod
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=encoding.fromlocal(key))
28 d = self._call("lookup", key=encoding.fromlocal(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 = encoding.tolocal(urllib.unquote(branchname))
47 branchname = encoding.tolocal(urllib.unquote(branchname))
48 branchheads = decodelist(branchheads)
48 branchheads = decodelist(branchheads)
49 branchmap[branchname] = branchheads
49 branchmap[branchname] = branchheads
50 return branchmap
50 return branchmap
51 except TypeError:
51 except TypeError:
52 self._abort(error.ResponseError(_("unexpected response:"), d))
52 self._abort(error.ResponseError(_("unexpected response:"), d))
53
53
54 def branches(self, nodes):
54 def branches(self, nodes):
55 n = encodelist(nodes)
55 n = encodelist(nodes)
56 d = self._call("branches", nodes=n)
56 d = self._call("branches", nodes=n)
57 try:
57 try:
58 br = [tuple(decodelist(b)) for b in d.splitlines()]
58 br = [tuple(decodelist(b)) for b in d.splitlines()]
59 return br
59 return br
60 except:
60 except:
61 self._abort(error.ResponseError(_("unexpected response:"), d))
61 self._abort(error.ResponseError(_("unexpected response:"), d))
62
62
63 def between(self, pairs):
63 def between(self, pairs):
64 batch = 8 # avoid giant requests
64 batch = 8 # avoid giant requests
65 r = []
65 r = []
66 for i in xrange(0, len(pairs), batch):
66 for i in xrange(0, len(pairs), batch):
67 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
67 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
68 d = self._call("between", pairs=n)
68 d = self._call("between", pairs=n)
69 try:
69 try:
70 r.extend(l and decodelist(l) or [] for l in d.splitlines())
70 r.extend(l and decodelist(l) or [] for l in d.splitlines())
71 except:
71 except:
72 self._abort(error.ResponseError(_("unexpected response:"), d))
72 self._abort(error.ResponseError(_("unexpected response:"), d))
73 return r
73 return r
74
74
75 def pushkey(self, namespace, key, old, new):
75 def pushkey(self, namespace, key, old, new):
76 if not self.capable('pushkey'):
76 if not self.capable('pushkey'):
77 return False
77 return False
78 d = self._call("pushkey",
78 d = self._call("pushkey",
79 namespace=encoding.fromlocal(namespace),
79 namespace=encoding.fromlocal(namespace),
80 key=encoding.fromlocal(key),
80 key=encoding.fromlocal(key),
81 old=encoding.fromlocal(old),
81 old=encoding.fromlocal(old),
82 new=encoding.fromlocal(new))
82 new=encoding.fromlocal(new))
83 try:
83 try:
84 d = bool(int(d))
84 d = bool(int(d))
85 except ValueError:
85 except ValueError:
86 raise error.ResponseError(
86 raise error.ResponseError(
87 _('push failed (unexpected response):'), d)
87 _('push failed (unexpected response):'), d)
88 return d
88 return d
89
89
90 def listkeys(self, namespace):
90 def listkeys(self, namespace):
91 if not self.capable('pushkey'):
91 if not self.capable('pushkey'):
92 return {}
92 return {}
93 d = self._call("listkeys", namespace=encoding.fromlocal(namespace))
93 d = self._call("listkeys", namespace=encoding.fromlocal(namespace))
94 r = {}
94 r = {}
95 for l in d.splitlines():
95 for l in d.splitlines():
96 k, v = l.split('\t')
96 k, v = l.split('\t')
97 r[encoding.tolocal(k)] = encoding.tolocal(v)
97 r[encoding.tolocal(k)] = encoding.tolocal(v)
98 return r
98 return r
99
99
100 def stream_out(self):
100 def stream_out(self):
101 return self._callstream('stream_out')
101 return self._callstream('stream_out')
102
102
103 def changegroup(self, nodes, kind):
103 def changegroup(self, nodes, kind):
104 n = encodelist(nodes)
104 n = encodelist(nodes)
105 f = self._callstream("changegroup", roots=n)
105 f = self._callstream("changegroup", roots=n)
106 return changegroupmod.unbundle10(self._decompress(f), 'UN')
106 return changegroupmod.unbundle10(self._decompress(f), 'UN')
107
107
108 def changegroupsubset(self, bases, heads, kind):
108 def changegroupsubset(self, bases, heads, kind):
109 self.requirecap('changegroupsubset', _('look up remote changes'))
109 self.requirecap('changegroupsubset', _('look up remote changes'))
110 bases = encodelist(bases)
110 bases = encodelist(bases)
111 heads = encodelist(heads)
111 heads = encodelist(heads)
112 f = self._callstream("changegroupsubset",
112 f = self._callstream("changegroupsubset",
113 bases=bases, heads=heads)
113 bases=bases, heads=heads)
114 return changegroupmod.unbundle10(self._decompress(f), 'UN')
114 return changegroupmod.unbundle10(self._decompress(f), 'UN')
115
115
116 def unbundle(self, cg, heads, source):
116 def unbundle(self, cg, heads, source):
117 '''Send cg (a readable file-like object representing the
117 '''Send cg (a readable file-like object representing the
118 changegroup to push, typically a chunkbuffer object) to the
118 changegroup to push, typically a chunkbuffer object) to the
119 remote server as a bundle. Return an integer indicating the
119 remote server as a bundle. Return an integer indicating the
120 result of the push (see localrepository.addchangegroup()).'''
120 result of the push (see localrepository.addchangegroup()).'''
121
121
122 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
122 ret, output = self._callpush("unbundle", cg, heads=encodelist(heads))
123 if ret == "":
123 if ret == "":
124 raise error.ResponseError(
124 raise error.ResponseError(
125 _('push failed:'), output)
125 _('push failed:'), output)
126 try:
126 try:
127 ret = int(ret)
127 ret = int(ret)
128 except ValueError:
128 except ValueError:
129 raise error.ResponseError(
129 raise error.ResponseError(
130 _('push failed (unexpected response):'), ret)
130 _('push failed (unexpected response):'), ret)
131
131
132 for l in output.splitlines(True):
132 for l in output.splitlines(True):
133 self.ui.status(_('remote: '), l)
133 self.ui.status(_('remote: '), l)
134 return ret
134 return ret
135
135
136 def debugwireargs(self, one, two, three=None, four=None):
136 def debugwireargs(self, one, two, three=None, four=None):
137 # don't pass optional arguments left at their default value
137 # don't pass optional arguments left at their default value
138 opts = {}
138 opts = {}
139 if three is not None:
139 if three is not None:
140 opts['three'] = three
140 opts['three'] = three
141 if four is not None:
141 if four is not None:
142 opts['four'] = four
142 opts['four'] = four
143 return self._call('debugwireargs', one=one, two=two, **opts)
143 return self._call('debugwireargs', one=one, two=two, **opts)
144
144
145 # server side
145 # server side
146
146
147 class streamres(object):
147 class streamres(object):
148 def __init__(self, gen):
148 def __init__(self, gen):
149 self.gen = gen
149 self.gen = gen
150
150
151 class pushres(object):
151 class pushres(object):
152 def __init__(self, res):
152 def __init__(self, res):
153 self.res = res
153 self.res = res
154
154
155 class pusherr(object):
155 class pusherr(object):
156 def __init__(self, res):
156 def __init__(self, res):
157 self.res = res
157 self.res = res
158
158
159 def dispatch(repo, proto, command):
159 def dispatch(repo, proto, command):
160 func, spec = commands[command]
160 func, spec = commands[command]
161 args = proto.getargs(spec)
161 args = proto.getargs(spec)
162 return func(repo, proto, *args)
162 return func(repo, proto, *args)
163
163
164 def options(cmd, keys, others):
165 opts = {}
166 for k in keys:
167 if k in others:
168 opts[k] = others[k]
169 del others[k]
170 if others:
171 sys.stderr.write("abort: %s got unexpected arguments %s\n"
172 % (cmd, ",".join(others)))
173 return opts
174
164 def between(repo, proto, pairs):
175 def between(repo, proto, pairs):
165 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
176 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
166 r = []
177 r = []
167 for b in repo.between(pairs):
178 for b in repo.between(pairs):
168 r.append(encodelist(b) + "\n")
179 r.append(encodelist(b) + "\n")
169 return "".join(r)
180 return "".join(r)
170
181
171 def branchmap(repo, proto):
182 def branchmap(repo, proto):
172 branchmap = repo.branchmap()
183 branchmap = repo.branchmap()
173 heads = []
184 heads = []
174 for branch, nodes in branchmap.iteritems():
185 for branch, nodes in branchmap.iteritems():
175 branchname = urllib.quote(encoding.fromlocal(branch))
186 branchname = urllib.quote(encoding.fromlocal(branch))
176 branchnodes = encodelist(nodes)
187 branchnodes = encodelist(nodes)
177 heads.append('%s %s' % (branchname, branchnodes))
188 heads.append('%s %s' % (branchname, branchnodes))
178 return '\n'.join(heads)
189 return '\n'.join(heads)
179
190
180 def branches(repo, proto, nodes):
191 def branches(repo, proto, nodes):
181 nodes = decodelist(nodes)
192 nodes = decodelist(nodes)
182 r = []
193 r = []
183 for b in repo.branches(nodes):
194 for b in repo.branches(nodes):
184 r.append(encodelist(b) + "\n")
195 r.append(encodelist(b) + "\n")
185 return "".join(r)
196 return "".join(r)
186
197
187 def capabilities(repo, proto):
198 def capabilities(repo, proto):
188 caps = 'lookup changegroupsubset branchmap pushkey'.split()
199 caps = 'lookup changegroupsubset branchmap pushkey'.split()
189 if _allowstream(repo.ui):
200 if _allowstream(repo.ui):
190 requiredformats = repo.requirements & repo.supportedformats
201 requiredformats = repo.requirements & repo.supportedformats
191 # if our local revlogs are just revlogv1, add 'stream' cap
202 # if our local revlogs are just revlogv1, add 'stream' cap
192 if not requiredformats - set(('revlogv1',)):
203 if not requiredformats - set(('revlogv1',)):
193 caps.append('stream')
204 caps.append('stream')
194 # otherwise, add 'streamreqs' detailing our local revlog format
205 # otherwise, add 'streamreqs' detailing our local revlog format
195 else:
206 else:
196 caps.append('streamreqs=%s' % ','.join(requiredformats))
207 caps.append('streamreqs=%s' % ','.join(requiredformats))
197 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
208 caps.append('unbundle=%s' % ','.join(changegroupmod.bundlepriority))
198 return ' '.join(caps)
209 return ' '.join(caps)
199
210
200 def changegroup(repo, proto, roots):
211 def changegroup(repo, proto, roots):
201 nodes = decodelist(roots)
212 nodes = decodelist(roots)
202 cg = repo.changegroup(nodes, 'serve')
213 cg = repo.changegroup(nodes, 'serve')
203 return streamres(proto.groupchunks(cg))
214 return streamres(proto.groupchunks(cg))
204
215
205 def changegroupsubset(repo, proto, bases, heads):
216 def changegroupsubset(repo, proto, bases, heads):
206 bases = decodelist(bases)
217 bases = decodelist(bases)
207 heads = decodelist(heads)
218 heads = decodelist(heads)
208 cg = repo.changegroupsubset(bases, heads, 'serve')
219 cg = repo.changegroupsubset(bases, heads, 'serve')
209 return streamres(proto.groupchunks(cg))
220 return streamres(proto.groupchunks(cg))
210
221
211 def debugwireargs(repo, proto, one, two):
222 def debugwireargs(repo, proto, one, two, others):
212 return repo.debugwireargs(one, two)
223 # only accept optional args from the known set
224 opts = options('debugwireargs', ['three', 'four'], others)
225 return repo.debugwireargs(one, two, **opts)
213
226
214 def heads(repo, proto):
227 def heads(repo, proto):
215 h = repo.heads()
228 h = repo.heads()
216 return encodelist(h) + "\n"
229 return encodelist(h) + "\n"
217
230
218 def hello(repo, proto):
231 def hello(repo, proto):
219 '''the hello command returns a set of lines describing various
232 '''the hello command returns a set of lines describing various
220 interesting things about the server, in an RFC822-like format.
233 interesting things about the server, in an RFC822-like format.
221 Currently the only one defined is "capabilities", which
234 Currently the only one defined is "capabilities", which
222 consists of a line in the form:
235 consists of a line in the form:
223
236
224 capabilities: space separated list of tokens
237 capabilities: space separated list of tokens
225 '''
238 '''
226 return "capabilities: %s\n" % (capabilities(repo, proto))
239 return "capabilities: %s\n" % (capabilities(repo, proto))
227
240
228 def listkeys(repo, proto, namespace):
241 def listkeys(repo, proto, namespace):
229 d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
242 d = pushkeymod.list(repo, encoding.tolocal(namespace)).items()
230 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
243 t = '\n'.join(['%s\t%s' % (encoding.fromlocal(k), encoding.fromlocal(v))
231 for k, v in d])
244 for k, v in d])
232 return t
245 return t
233
246
234 def lookup(repo, proto, key):
247 def lookup(repo, proto, key):
235 try:
248 try:
236 r = hex(repo.lookup(encoding.tolocal(key)))
249 r = hex(repo.lookup(encoding.tolocal(key)))
237 success = 1
250 success = 1
238 except Exception, inst:
251 except Exception, inst:
239 r = str(inst)
252 r = str(inst)
240 success = 0
253 success = 0
241 return "%s %s\n" % (success, r)
254 return "%s %s\n" % (success, r)
242
255
243 def pushkey(repo, proto, namespace, key, old, new):
256 def pushkey(repo, proto, namespace, key, old, new):
244 # compatibility with pre-1.8 clients which were accidentally
257 # compatibility with pre-1.8 clients which were accidentally
245 # sending raw binary nodes rather than utf-8-encoded hex
258 # sending raw binary nodes rather than utf-8-encoded hex
246 if len(new) == 20 and new.encode('string-escape') != new:
259 if len(new) == 20 and new.encode('string-escape') != new:
247 # looks like it could be a binary node
260 # looks like it could be a binary node
248 try:
261 try:
249 u = new.decode('utf-8')
262 u = new.decode('utf-8')
250 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
263 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
251 except UnicodeDecodeError:
264 except UnicodeDecodeError:
252 pass # binary, leave unmodified
265 pass # binary, leave unmodified
253 else:
266 else:
254 new = encoding.tolocal(new) # normal path
267 new = encoding.tolocal(new) # normal path
255
268
256 r = pushkeymod.push(repo,
269 r = pushkeymod.push(repo,
257 encoding.tolocal(namespace), encoding.tolocal(key),
270 encoding.tolocal(namespace), encoding.tolocal(key),
258 encoding.tolocal(old), new)
271 encoding.tolocal(old), new)
259 return '%s\n' % int(r)
272 return '%s\n' % int(r)
260
273
261 def _allowstream(ui):
274 def _allowstream(ui):
262 return ui.configbool('server', 'uncompressed', True, untrusted=True)
275 return ui.configbool('server', 'uncompressed', True, untrusted=True)
263
276
264 def stream(repo, proto):
277 def stream(repo, proto):
265 '''If the server supports streaming clone, it advertises the "stream"
278 '''If the server supports streaming clone, it advertises the "stream"
266 capability with a value representing the version and flags of the repo
279 capability with a value representing the version and flags of the repo
267 it is serving. Client checks to see if it understands the format.
280 it is serving. Client checks to see if it understands the format.
268
281
269 The format is simple: the server writes out a line with the amount
282 The format is simple: the server writes out a line with the amount
270 of files, then the total amount of bytes to be transfered (separated
283 of files, then the total amount of bytes to be transfered (separated
271 by a space). Then, for each file, the server first writes the filename
284 by a space). Then, for each file, the server first writes the filename
272 and filesize (separated by the null character), then the file contents.
285 and filesize (separated by the null character), then the file contents.
273 '''
286 '''
274
287
275 if not _allowstream(repo.ui):
288 if not _allowstream(repo.ui):
276 return '1\n'
289 return '1\n'
277
290
278 entries = []
291 entries = []
279 total_bytes = 0
292 total_bytes = 0
280 try:
293 try:
281 # get consistent snapshot of repo, lock during scan
294 # get consistent snapshot of repo, lock during scan
282 lock = repo.lock()
295 lock = repo.lock()
283 try:
296 try:
284 repo.ui.debug('scanning\n')
297 repo.ui.debug('scanning\n')
285 for name, ename, size in repo.store.walk():
298 for name, ename, size in repo.store.walk():
286 entries.append((name, size))
299 entries.append((name, size))
287 total_bytes += size
300 total_bytes += size
288 finally:
301 finally:
289 lock.release()
302 lock.release()
290 except error.LockError:
303 except error.LockError:
291 return '2\n' # error: 2
304 return '2\n' # error: 2
292
305
293 def streamer(repo, entries, total):
306 def streamer(repo, entries, total):
294 '''stream out all metadata files in repository.'''
307 '''stream out all metadata files in repository.'''
295 yield '0\n' # success
308 yield '0\n' # success
296 repo.ui.debug('%d files, %d bytes to transfer\n' %
309 repo.ui.debug('%d files, %d bytes to transfer\n' %
297 (len(entries), total_bytes))
310 (len(entries), total_bytes))
298 yield '%d %d\n' % (len(entries), total_bytes)
311 yield '%d %d\n' % (len(entries), total_bytes)
299 for name, size in entries:
312 for name, size in entries:
300 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
313 repo.ui.debug('sending %s (%d bytes)\n' % (name, size))
301 # partially encode name over the wire for backwards compat
314 # partially encode name over the wire for backwards compat
302 yield '%s\0%d\n' % (store.encodedir(name), size)
315 yield '%s\0%d\n' % (store.encodedir(name), size)
303 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
316 for chunk in util.filechunkiter(repo.sopener(name), limit=size):
304 yield chunk
317 yield chunk
305
318
306 return streamres(streamer(repo, entries, total_bytes))
319 return streamres(streamer(repo, entries, total_bytes))
307
320
308 def unbundle(repo, proto, heads):
321 def unbundle(repo, proto, heads):
309 their_heads = decodelist(heads)
322 their_heads = decodelist(heads)
310
323
311 def check_heads():
324 def check_heads():
312 heads = repo.heads()
325 heads = repo.heads()
313 return their_heads == ['force'] or their_heads == heads
326 return their_heads == ['force'] or their_heads == heads
314
327
315 proto.redirect()
328 proto.redirect()
316
329
317 # fail early if possible
330 # fail early if possible
318 if not check_heads():
331 if not check_heads():
319 return pusherr('unsynced changes')
332 return pusherr('unsynced changes')
320
333
321 # write bundle data to temporary file because it can be big
334 # write bundle data to temporary file because it can be big
322 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
335 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
323 fp = os.fdopen(fd, 'wb+')
336 fp = os.fdopen(fd, 'wb+')
324 r = 0
337 r = 0
325 try:
338 try:
326 proto.getfile(fp)
339 proto.getfile(fp)
327 lock = repo.lock()
340 lock = repo.lock()
328 try:
341 try:
329 if not check_heads():
342 if not check_heads():
330 # someone else committed/pushed/unbundled while we
343 # someone else committed/pushed/unbundled while we
331 # were transferring data
344 # were transferring data
332 return pusherr('unsynced changes')
345 return pusherr('unsynced changes')
333
346
334 # push can proceed
347 # push can proceed
335 fp.seek(0)
348 fp.seek(0)
336 gen = changegroupmod.readbundle(fp, None)
349 gen = changegroupmod.readbundle(fp, None)
337
350
338 try:
351 try:
339 r = repo.addchangegroup(gen, 'serve', proto._client(),
352 r = repo.addchangegroup(gen, 'serve', proto._client(),
340 lock=lock)
353 lock=lock)
341 except util.Abort, inst:
354 except util.Abort, inst:
342 sys.stderr.write("abort: %s\n" % inst)
355 sys.stderr.write("abort: %s\n" % inst)
343 finally:
356 finally:
344 lock.release()
357 lock.release()
345 return pushres(r)
358 return pushres(r)
346
359
347 finally:
360 finally:
348 fp.close()
361 fp.close()
349 os.unlink(tempname)
362 os.unlink(tempname)
350
363
351 commands = {
364 commands = {
352 'between': (between, 'pairs'),
365 'between': (between, 'pairs'),
353 'branchmap': (branchmap, ''),
366 'branchmap': (branchmap, ''),
354 'branches': (branches, 'nodes'),
367 'branches': (branches, 'nodes'),
355 'capabilities': (capabilities, ''),
368 'capabilities': (capabilities, ''),
356 'changegroup': (changegroup, 'roots'),
369 'changegroup': (changegroup, 'roots'),
357 'changegroupsubset': (changegroupsubset, 'bases heads'),
370 'changegroupsubset': (changegroupsubset, 'bases heads'),
358 'debugwireargs': (debugwireargs, 'one two'),
371 'debugwireargs': (debugwireargs, 'one two *'),
359 'heads': (heads, ''),
372 'heads': (heads, ''),
360 'hello': (hello, ''),
373 'hello': (hello, ''),
361 'listkeys': (listkeys, 'namespace'),
374 'listkeys': (listkeys, 'namespace'),
362 'lookup': (lookup, 'key'),
375 'lookup': (lookup, 'key'),
363 'pushkey': (pushkey, 'namespace key old new'),
376 'pushkey': (pushkey, 'namespace key old new'),
364 'stream_out': (stream, ''),
377 'stream_out': (stream, ''),
365 'unbundle': (unbundle, 'heads'),
378 'unbundle': (unbundle, 'heads'),
366 }
379 }
@@ -1,42 +1,60 b''
1
1
2 Test wire protocol argument passing
2 Test wire protocol argument passing
3
3
4 Setup repo:
4 Setup repo:
5
5
6 $ hg init repo
6 $ hg init repo
7
7
8 Local:
8 Local:
9
9
10 $ hg debugwireargs repo eins zwei --three drei --four vier
11 eins zwei drei vier
12 $ hg debugwireargs repo eins zwei --four vier
13 eins zwei None vier
10 $ hg debugwireargs repo eins zwei
14 $ hg debugwireargs repo eins zwei
11 eins zwei None None
15 eins zwei None None
12
16
13 HTTP:
17 HTTP:
14
18
15 $ hg serve -R repo -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
19 $ hg serve -R repo -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
16 $ cat hg1.pid >> $DAEMON_PIDS
20 $ cat hg1.pid >> $DAEMON_PIDS
17
21
22 $ hg debugwireargs http://localhost:$HGPORT/ un deux trois quatre
23 un deux trois quatre
24 $ hg debugwireargs http://localhost:$HGPORT/ eins zwei --four vier
25 eins zwei None vier
18 $ hg debugwireargs http://localhost:$HGPORT/ eins zwei
26 $ hg debugwireargs http://localhost:$HGPORT/ eins zwei
19 eins zwei None None
27 eins zwei None None
20 $ cat access.log
28 $ cat access.log
21 * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
29 * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
30 * - - [*] "GET /?cmd=debugwireargs&four=quatre&one=un&three=trois&two=deux HTTP/1.1" 200 - (glob)
31 * - - [*] "GET /?cmd=debugwireargs&four=quatre&one=un&three=trois&two=deux HTTP/1.1" 200 - (glob)
32 * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
33 * - - [*] "GET /?cmd=debugwireargs&four=vier&one=eins&two=zwei HTTP/1.1" 200 - (glob)
34 * - - [*] "GET /?cmd=debugwireargs&four=vier&one=eins&two=zwei HTTP/1.1" 200 - (glob)
35 * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
22 * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
36 * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
23 * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
37 * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
24
38
25 SSH (try to exercise the ssh functionality with a dummy script):
39 SSH (try to exercise the ssh functionality with a dummy script):
26
40
27 $ cat <<EOF > dummyssh
41 $ cat <<EOF > dummyssh
28 > import sys
42 > import sys
29 > import os
43 > import os
30 > os.chdir(os.path.dirname(sys.argv[0]))
44 > os.chdir(os.path.dirname(sys.argv[0]))
31 > if sys.argv[1] != "user@dummy":
45 > if sys.argv[1] != "user@dummy":
32 > sys.exit(-1)
46 > sys.exit(-1)
33 > if not os.path.exists("dummyssh"):
47 > if not os.path.exists("dummyssh"):
34 > sys.exit(-1)
48 > sys.exit(-1)
35 > os.environ["SSH_CLIENT"] = "127.0.0.1 1 2"
49 > os.environ["SSH_CLIENT"] = "127.0.0.1 1 2"
36 > r = os.system(sys.argv[2])
50 > r = os.system(sys.argv[2])
37 > sys.exit(bool(r))
51 > sys.exit(bool(r))
38 > EOF
52 > EOF
39
53
54 $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo uno due tre quattro
55 uno due tre quattro
56 $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo eins zwei --four vier
57 eins zwei None vier
40 $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo eins zwei
58 $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo eins zwei
41 eins zwei None None
59 eins zwei None None
42
60
General Comments 0
You need to be logged in to leave comments. Login now