##// END OF EJS Templates
sshpeer: remove support for connecting to <0.9.1 servers (BC)...
Gregory Szorc -
r35958:556218e0 default
parent child Browse files
Show More
@@ -1,456 +1,466 b''
1 # sshpeer.py - ssh repository proxy class for mercurial
1 # sshpeer.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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 error,
14 error,
15 pycompat,
15 pycompat,
16 util,
16 util,
17 wireproto,
17 wireproto,
18 )
18 )
19
19
20 def _serverquote(s):
20 def _serverquote(s):
21 """quote a string for the remote shell ... which we assume is sh"""
21 """quote a string for the remote shell ... which we assume is sh"""
22 if not s:
22 if not s:
23 return s
23 return s
24 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s):
24 if re.match('[a-zA-Z0-9@%_+=:,./-]*$', s):
25 return s
25 return s
26 return "'%s'" % s.replace("'", "'\\''")
26 return "'%s'" % s.replace("'", "'\\''")
27
27
28 def _forwardoutput(ui, pipe):
28 def _forwardoutput(ui, pipe):
29 """display all data currently available on pipe as remote output.
29 """display all data currently available on pipe as remote output.
30
30
31 This is non blocking."""
31 This is non blocking."""
32 s = util.readpipe(pipe)
32 s = util.readpipe(pipe)
33 if s:
33 if s:
34 for l in s.splitlines():
34 for l in s.splitlines():
35 ui.status(_("remote: "), l, '\n')
35 ui.status(_("remote: "), l, '\n')
36
36
37 class doublepipe(object):
37 class doublepipe(object):
38 """Operate a side-channel pipe in addition of a main one
38 """Operate a side-channel pipe in addition of a main one
39
39
40 The side-channel pipe contains server output to be forwarded to the user
40 The side-channel pipe contains server output to be forwarded to the user
41 input. The double pipe will behave as the "main" pipe, but will ensure the
41 input. The double pipe will behave as the "main" pipe, but will ensure the
42 content of the "side" pipe is properly processed while we wait for blocking
42 content of the "side" pipe is properly processed while we wait for blocking
43 call on the "main" pipe.
43 call on the "main" pipe.
44
44
45 If large amounts of data are read from "main", the forward will cease after
45 If large amounts of data are read from "main", the forward will cease after
46 the first bytes start to appear. This simplifies the implementation
46 the first bytes start to appear. This simplifies the implementation
47 without affecting actual output of sshpeer too much as we rarely issue
47 without affecting actual output of sshpeer too much as we rarely issue
48 large read for data not yet emitted by the server.
48 large read for data not yet emitted by the server.
49
49
50 The main pipe is expected to be a 'bufferedinputpipe' from the util module
50 The main pipe is expected to be a 'bufferedinputpipe' from the util module
51 that handle all the os specific bits. This class lives in this module
51 that handle all the os specific bits. This class lives in this module
52 because it focus on behavior specific to the ssh protocol."""
52 because it focus on behavior specific to the ssh protocol."""
53
53
54 def __init__(self, ui, main, side):
54 def __init__(self, ui, main, side):
55 self._ui = ui
55 self._ui = ui
56 self._main = main
56 self._main = main
57 self._side = side
57 self._side = side
58
58
59 def _wait(self):
59 def _wait(self):
60 """wait until some data are available on main or side
60 """wait until some data are available on main or side
61
61
62 return a pair of boolean (ismainready, issideready)
62 return a pair of boolean (ismainready, issideready)
63
63
64 (This will only wait for data if the setup is supported by `util.poll`)
64 (This will only wait for data if the setup is supported by `util.poll`)
65 """
65 """
66 if getattr(self._main, 'hasbuffer', False): # getattr for classic pipe
66 if getattr(self._main, 'hasbuffer', False): # getattr for classic pipe
67 return (True, True) # main has data, assume side is worth poking at.
67 return (True, True) # main has data, assume side is worth poking at.
68 fds = [self._main.fileno(), self._side.fileno()]
68 fds = [self._main.fileno(), self._side.fileno()]
69 try:
69 try:
70 act = util.poll(fds)
70 act = util.poll(fds)
71 except NotImplementedError:
71 except NotImplementedError:
72 # non supported yet case, assume all have data.
72 # non supported yet case, assume all have data.
73 act = fds
73 act = fds
74 return (self._main.fileno() in act, self._side.fileno() in act)
74 return (self._main.fileno() in act, self._side.fileno() in act)
75
75
76 def write(self, data):
76 def write(self, data):
77 return self._call('write', data)
77 return self._call('write', data)
78
78
79 def read(self, size):
79 def read(self, size):
80 r = self._call('read', size)
80 r = self._call('read', size)
81 if size != 0 and not r:
81 if size != 0 and not r:
82 # We've observed a condition that indicates the
82 # We've observed a condition that indicates the
83 # stdout closed unexpectedly. Check stderr one
83 # stdout closed unexpectedly. Check stderr one
84 # more time and snag anything that's there before
84 # more time and snag anything that's there before
85 # letting anyone know the main part of the pipe
85 # letting anyone know the main part of the pipe
86 # closed prematurely.
86 # closed prematurely.
87 _forwardoutput(self._ui, self._side)
87 _forwardoutput(self._ui, self._side)
88 return r
88 return r
89
89
90 def readline(self):
90 def readline(self):
91 return self._call('readline')
91 return self._call('readline')
92
92
93 def _call(self, methname, data=None):
93 def _call(self, methname, data=None):
94 """call <methname> on "main", forward output of "side" while blocking
94 """call <methname> on "main", forward output of "side" while blocking
95 """
95 """
96 # data can be '' or 0
96 # data can be '' or 0
97 if (data is not None and not data) or self._main.closed:
97 if (data is not None and not data) or self._main.closed:
98 _forwardoutput(self._ui, self._side)
98 _forwardoutput(self._ui, self._side)
99 return ''
99 return ''
100 while True:
100 while True:
101 mainready, sideready = self._wait()
101 mainready, sideready = self._wait()
102 if sideready:
102 if sideready:
103 _forwardoutput(self._ui, self._side)
103 _forwardoutput(self._ui, self._side)
104 if mainready:
104 if mainready:
105 meth = getattr(self._main, methname)
105 meth = getattr(self._main, methname)
106 if data is None:
106 if data is None:
107 return meth()
107 return meth()
108 else:
108 else:
109 return meth(data)
109 return meth(data)
110
110
111 def close(self):
111 def close(self):
112 return self._main.close()
112 return self._main.close()
113
113
114 def flush(self):
114 def flush(self):
115 return self._main.flush()
115 return self._main.flush()
116
116
117 def _cleanuppipes(ui, pipei, pipeo, pipee):
117 def _cleanuppipes(ui, pipei, pipeo, pipee):
118 """Clean up pipes used by an SSH connection."""
118 """Clean up pipes used by an SSH connection."""
119 if pipeo:
119 if pipeo:
120 pipeo.close()
120 pipeo.close()
121 if pipei:
121 if pipei:
122 pipei.close()
122 pipei.close()
123
123
124 if pipee:
124 if pipee:
125 # Try to read from the err descriptor until EOF.
125 # Try to read from the err descriptor until EOF.
126 try:
126 try:
127 for l in pipee:
127 for l in pipee:
128 ui.status(_('remote: '), l)
128 ui.status(_('remote: '), l)
129 except (IOError, ValueError):
129 except (IOError, ValueError):
130 pass
130 pass
131
131
132 pipee.close()
132 pipee.close()
133
133
134 def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None):
134 def _makeconnection(ui, sshcmd, args, remotecmd, path, sshenv=None):
135 """Create an SSH connection to a server.
135 """Create an SSH connection to a server.
136
136
137 Returns a tuple of (process, stdin, stdout, stderr) for the
137 Returns a tuple of (process, stdin, stdout, stderr) for the
138 spawned process.
138 spawned process.
139 """
139 """
140 cmd = '%s %s %s' % (
140 cmd = '%s %s %s' % (
141 sshcmd,
141 sshcmd,
142 args,
142 args,
143 util.shellquote('%s -R %s serve --stdio' % (
143 util.shellquote('%s -R %s serve --stdio' % (
144 _serverquote(remotecmd), _serverquote(path))))
144 _serverquote(remotecmd), _serverquote(path))))
145
145
146 ui.debug('running %s\n' % cmd)
146 ui.debug('running %s\n' % cmd)
147 cmd = util.quotecommand(cmd)
147 cmd = util.quotecommand(cmd)
148
148
149 # no buffer allow the use of 'select'
149 # no buffer allow the use of 'select'
150 # feel free to remove buffering and select usage when we ultimately
150 # feel free to remove buffering and select usage when we ultimately
151 # move to threading.
151 # move to threading.
152 stdin, stdout, stderr, proc = util.popen4(cmd, bufsize=0, env=sshenv)
152 stdin, stdout, stderr, proc = util.popen4(cmd, bufsize=0, env=sshenv)
153
153
154 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
154 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
155 stdin = doublepipe(ui, stdin, stderr)
155 stdin = doublepipe(ui, stdin, stderr)
156
156
157 return proc, stdin, stdout, stderr
157 return proc, stdin, stdout, stderr
158
158
159 def _performhandshake(ui, stdin, stdout, stderr):
159 def _performhandshake(ui, stdin, stdout, stderr):
160 def badresponse():
160 def badresponse():
161 msg = _('no suitable response from remote hg')
161 msg = _('no suitable response from remote hg')
162 hint = ui.config('ui', 'ssherrorhint')
162 hint = ui.config('ui', 'ssherrorhint')
163 raise error.RepoError(msg, hint=hint)
163 raise error.RepoError(msg, hint=hint)
164
164
165 # The handshake consists of sending 2 wire protocol commands:
165 # The handshake consists of sending 2 wire protocol commands:
166 # ``hello`` and ``between``.
166 # ``hello`` and ``between``.
167 #
167 #
168 # The ``hello`` command (which was introduced in Mercurial 0.9.1)
168 # The ``hello`` command (which was introduced in Mercurial 0.9.1)
169 # instructs the server to advertise its capabilities.
169 # instructs the server to advertise its capabilities.
170 #
170 #
171 # The ``between`` command (which has existed in all Mercurial servers
171 # The ``between`` command (which has existed in all Mercurial servers
172 # for as long as SSH support has existed), asks for the set of revisions
172 # for as long as SSH support has existed), asks for the set of revisions
173 # between a pair of revisions.
173 # between a pair of revisions.
174 #
174 #
175 # The ``between`` command is issued with a request for the null
175 # The ``between`` command is issued with a request for the null
176 # range. If the remote is a Mercurial server, this request will
176 # range. If the remote is a Mercurial server, this request will
177 # generate a specific response: ``1\n\n``. This represents the
177 # generate a specific response: ``1\n\n``. This represents the
178 # wire protocol encoded value for ``\n``. We look for ``1\n\n``
178 # wire protocol encoded value for ``\n``. We look for ``1\n\n``
179 # in the output stream and know this is the response to ``between``
179 # in the output stream and know this is the response to ``between``
180 # and we're at the end of our handshake reply.
180 # and we're at the end of our handshake reply.
181 #
181 #
182 # The response to the ``hello`` command will be a line with the
182 # The response to the ``hello`` command will be a line with the
183 # length of the value returned by that command followed by that
183 # length of the value returned by that command followed by that
184 # value. If the server doesn't support ``hello`` (which should be
184 # value. If the server doesn't support ``hello`` (which should be
185 # rare), that line will be ``0\n``. Otherwise, the value will contain
185 # rare), that line will be ``0\n``. Otherwise, the value will contain
186 # RFC 822 like lines. Of these, the ``capabilities:`` line contains
186 # RFC 822 like lines. Of these, the ``capabilities:`` line contains
187 # the capabilities of the server.
187 # the capabilities of the server.
188 #
188 #
189 # In addition to the responses to our command requests, the server
189 # In addition to the responses to our command requests, the server
190 # may emit "banner" output on stdout. SSH servers are allowed to
190 # may emit "banner" output on stdout. SSH servers are allowed to
191 # print messages to stdout on login. Issuing commands on connection
191 # print messages to stdout on login. Issuing commands on connection
192 # allows us to flush this banner output from the server by scanning
192 # allows us to flush this banner output from the server by scanning
193 # for output to our well-known ``between`` command. Of course, if
193 # for output to our well-known ``between`` command. Of course, if
194 # the banner contains ``1\n\n``, this will throw off our detection.
194 # the banner contains ``1\n\n``, this will throw off our detection.
195
195
196 requestlog = ui.configbool('devel', 'debug.peer-request')
196 requestlog = ui.configbool('devel', 'debug.peer-request')
197
197
198 try:
198 try:
199 pairsarg = '%s-%s' % ('0' * 40, '0' * 40)
199 pairsarg = '%s-%s' % ('0' * 40, '0' * 40)
200 handshake = [
200 handshake = [
201 'hello\n',
201 'hello\n',
202 'between\n',
202 'between\n',
203 'pairs %d\n' % len(pairsarg),
203 'pairs %d\n' % len(pairsarg),
204 pairsarg,
204 pairsarg,
205 ]
205 ]
206
206
207 if requestlog:
207 if requestlog:
208 ui.debug('devel-peer-request: hello\n')
208 ui.debug('devel-peer-request: hello\n')
209 ui.debug('sending hello command\n')
209 ui.debug('sending hello command\n')
210 if requestlog:
210 if requestlog:
211 ui.debug('devel-peer-request: between\n')
211 ui.debug('devel-peer-request: between\n')
212 ui.debug('devel-peer-request: pairs: %d bytes\n' % len(pairsarg))
212 ui.debug('devel-peer-request: pairs: %d bytes\n' % len(pairsarg))
213 ui.debug('sending between command\n')
213 ui.debug('sending between command\n')
214
214
215 stdin.write(''.join(handshake))
215 stdin.write(''.join(handshake))
216 stdin.flush()
216 stdin.flush()
217 except IOError:
217 except IOError:
218 badresponse()
218 badresponse()
219
219
220 lines = ['', 'dummy']
220 lines = ['', 'dummy']
221 max_noise = 500
221 max_noise = 500
222 while lines[-1] and max_noise:
222 while lines[-1] and max_noise:
223 try:
223 try:
224 l = stdout.readline()
224 l = stdout.readline()
225 _forwardoutput(ui, stderr)
225 _forwardoutput(ui, stderr)
226 if lines[-1] == '1\n' and l == '\n':
226 if lines[-1] == '1\n' and l == '\n':
227 break
227 break
228 if l:
228 if l:
229 ui.debug('remote: ', l)
229 ui.debug('remote: ', l)
230 lines.append(l)
230 lines.append(l)
231 max_noise -= 1
231 max_noise -= 1
232 except IOError:
232 except IOError:
233 badresponse()
233 badresponse()
234 else:
234 else:
235 badresponse()
235 badresponse()
236
236
237 caps = set()
237 caps = set()
238 for l in reversed(lines):
238 for l in reversed(lines):
239 # Look for response to ``hello`` command. Scan from the back so
239 # Look for response to ``hello`` command. Scan from the back so
240 # we don't misinterpret banner output as the command reply.
240 # we don't misinterpret banner output as the command reply.
241 if l.startswith('capabilities:'):
241 if l.startswith('capabilities:'):
242 caps.update(l[:-1].split(':')[1].split())
242 caps.update(l[:-1].split(':')[1].split())
243 break
243 break
244
244
245 # Error if we couldn't find a response to ``hello``. This could
246 # mean:
247 #
248 # 1. Remote isn't a Mercurial server
249 # 2. Remote is a <0.9.1 Mercurial server
250 # 3. Remote is a future Mercurial server that dropped ``hello``
251 # support.
252 if not caps:
253 badresponse()
254
245 return caps
255 return caps
246
256
247 class sshpeer(wireproto.wirepeer):
257 class sshpeer(wireproto.wirepeer):
248 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps):
258 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps):
249 """Create a peer from an existing SSH connection.
259 """Create a peer from an existing SSH connection.
250
260
251 ``proc`` is a handle on the underlying SSH process.
261 ``proc`` is a handle on the underlying SSH process.
252 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio
262 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio
253 pipes for that process.
263 pipes for that process.
254 ``caps`` is a set of capabilities supported by the remote.
264 ``caps`` is a set of capabilities supported by the remote.
255 """
265 """
256 self._url = url
266 self._url = url
257 self._ui = ui
267 self._ui = ui
258 # self._subprocess is unused. Keeping a handle on the process
268 # self._subprocess is unused. Keeping a handle on the process
259 # holds a reference and prevents it from being garbage collected.
269 # holds a reference and prevents it from being garbage collected.
260 self._subprocess = proc
270 self._subprocess = proc
261 self._pipeo = stdin
271 self._pipeo = stdin
262 self._pipei = stdout
272 self._pipei = stdout
263 self._pipee = stderr
273 self._pipee = stderr
264 self._caps = caps
274 self._caps = caps
265
275
266 # Begin of _basepeer interface.
276 # Begin of _basepeer interface.
267
277
268 @util.propertycache
278 @util.propertycache
269 def ui(self):
279 def ui(self):
270 return self._ui
280 return self._ui
271
281
272 def url(self):
282 def url(self):
273 return self._url
283 return self._url
274
284
275 def local(self):
285 def local(self):
276 return None
286 return None
277
287
278 def peer(self):
288 def peer(self):
279 return self
289 return self
280
290
281 def canpush(self):
291 def canpush(self):
282 return True
292 return True
283
293
284 def close(self):
294 def close(self):
285 pass
295 pass
286
296
287 # End of _basepeer interface.
297 # End of _basepeer interface.
288
298
289 # Begin of _basewirecommands interface.
299 # Begin of _basewirecommands interface.
290
300
291 def capabilities(self):
301 def capabilities(self):
292 return self._caps
302 return self._caps
293
303
294 # End of _basewirecommands interface.
304 # End of _basewirecommands interface.
295
305
296 def _readerr(self):
306 def _readerr(self):
297 _forwardoutput(self.ui, self._pipee)
307 _forwardoutput(self.ui, self._pipee)
298
308
299 def _abort(self, exception):
309 def _abort(self, exception):
300 self._cleanup()
310 self._cleanup()
301 raise exception
311 raise exception
302
312
303 def _cleanup(self):
313 def _cleanup(self):
304 _cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee)
314 _cleanuppipes(self.ui, self._pipei, self._pipeo, self._pipee)
305
315
306 __del__ = _cleanup
316 __del__ = _cleanup
307
317
308 def _submitbatch(self, req):
318 def _submitbatch(self, req):
309 rsp = self._callstream("batch", cmds=wireproto.encodebatchcmds(req))
319 rsp = self._callstream("batch", cmds=wireproto.encodebatchcmds(req))
310 available = self._getamount()
320 available = self._getamount()
311 # TODO this response parsing is probably suboptimal for large
321 # TODO this response parsing is probably suboptimal for large
312 # batches with large responses.
322 # batches with large responses.
313 toread = min(available, 1024)
323 toread = min(available, 1024)
314 work = rsp.read(toread)
324 work = rsp.read(toread)
315 available -= toread
325 available -= toread
316 chunk = work
326 chunk = work
317 while chunk:
327 while chunk:
318 while ';' in work:
328 while ';' in work:
319 one, work = work.split(';', 1)
329 one, work = work.split(';', 1)
320 yield wireproto.unescapearg(one)
330 yield wireproto.unescapearg(one)
321 toread = min(available, 1024)
331 toread = min(available, 1024)
322 chunk = rsp.read(toread)
332 chunk = rsp.read(toread)
323 available -= toread
333 available -= toread
324 work += chunk
334 work += chunk
325 yield wireproto.unescapearg(work)
335 yield wireproto.unescapearg(work)
326
336
327 def _callstream(self, cmd, **args):
337 def _callstream(self, cmd, **args):
328 args = pycompat.byteskwargs(args)
338 args = pycompat.byteskwargs(args)
329 if (self.ui.debugflag
339 if (self.ui.debugflag
330 and self.ui.configbool('devel', 'debug.peer-request')):
340 and self.ui.configbool('devel', 'debug.peer-request')):
331 dbg = self.ui.debug
341 dbg = self.ui.debug
332 line = 'devel-peer-request: %s\n'
342 line = 'devel-peer-request: %s\n'
333 dbg(line % cmd)
343 dbg(line % cmd)
334 for key, value in sorted(args.items()):
344 for key, value in sorted(args.items()):
335 if not isinstance(value, dict):
345 if not isinstance(value, dict):
336 dbg(line % ' %s: %d bytes' % (key, len(value)))
346 dbg(line % ' %s: %d bytes' % (key, len(value)))
337 else:
347 else:
338 for dk, dv in sorted(value.items()):
348 for dk, dv in sorted(value.items()):
339 dbg(line % ' %s-%s: %d' % (key, dk, len(dv)))
349 dbg(line % ' %s-%s: %d' % (key, dk, len(dv)))
340 self.ui.debug("sending %s command\n" % cmd)
350 self.ui.debug("sending %s command\n" % cmd)
341 self._pipeo.write("%s\n" % cmd)
351 self._pipeo.write("%s\n" % cmd)
342 _func, names = wireproto.commands[cmd]
352 _func, names = wireproto.commands[cmd]
343 keys = names.split()
353 keys = names.split()
344 wireargs = {}
354 wireargs = {}
345 for k in keys:
355 for k in keys:
346 if k == '*':
356 if k == '*':
347 wireargs['*'] = args
357 wireargs['*'] = args
348 break
358 break
349 else:
359 else:
350 wireargs[k] = args[k]
360 wireargs[k] = args[k]
351 del args[k]
361 del args[k]
352 for k, v in sorted(wireargs.iteritems()):
362 for k, v in sorted(wireargs.iteritems()):
353 self._pipeo.write("%s %d\n" % (k, len(v)))
363 self._pipeo.write("%s %d\n" % (k, len(v)))
354 if isinstance(v, dict):
364 if isinstance(v, dict):
355 for dk, dv in v.iteritems():
365 for dk, dv in v.iteritems():
356 self._pipeo.write("%s %d\n" % (dk, len(dv)))
366 self._pipeo.write("%s %d\n" % (dk, len(dv)))
357 self._pipeo.write(dv)
367 self._pipeo.write(dv)
358 else:
368 else:
359 self._pipeo.write(v)
369 self._pipeo.write(v)
360 self._pipeo.flush()
370 self._pipeo.flush()
361
371
362 return self._pipei
372 return self._pipei
363
373
364 def _callcompressable(self, cmd, **args):
374 def _callcompressable(self, cmd, **args):
365 return self._callstream(cmd, **args)
375 return self._callstream(cmd, **args)
366
376
367 def _call(self, cmd, **args):
377 def _call(self, cmd, **args):
368 self._callstream(cmd, **args)
378 self._callstream(cmd, **args)
369 return self._recv()
379 return self._recv()
370
380
371 def _callpush(self, cmd, fp, **args):
381 def _callpush(self, cmd, fp, **args):
372 r = self._call(cmd, **args)
382 r = self._call(cmd, **args)
373 if r:
383 if r:
374 return '', r
384 return '', r
375 for d in iter(lambda: fp.read(4096), ''):
385 for d in iter(lambda: fp.read(4096), ''):
376 self._send(d)
386 self._send(d)
377 self._send("", flush=True)
387 self._send("", flush=True)
378 r = self._recv()
388 r = self._recv()
379 if r:
389 if r:
380 return '', r
390 return '', r
381 return self._recv(), ''
391 return self._recv(), ''
382
392
383 def _calltwowaystream(self, cmd, fp, **args):
393 def _calltwowaystream(self, cmd, fp, **args):
384 r = self._call(cmd, **args)
394 r = self._call(cmd, **args)
385 if r:
395 if r:
386 # XXX needs to be made better
396 # XXX needs to be made better
387 raise error.Abort(_('unexpected remote reply: %s') % r)
397 raise error.Abort(_('unexpected remote reply: %s') % r)
388 for d in iter(lambda: fp.read(4096), ''):
398 for d in iter(lambda: fp.read(4096), ''):
389 self._send(d)
399 self._send(d)
390 self._send("", flush=True)
400 self._send("", flush=True)
391 return self._pipei
401 return self._pipei
392
402
393 def _getamount(self):
403 def _getamount(self):
394 l = self._pipei.readline()
404 l = self._pipei.readline()
395 if l == '\n':
405 if l == '\n':
396 self._readerr()
406 self._readerr()
397 msg = _('check previous remote output')
407 msg = _('check previous remote output')
398 self._abort(error.OutOfBandError(hint=msg))
408 self._abort(error.OutOfBandError(hint=msg))
399 self._readerr()
409 self._readerr()
400 try:
410 try:
401 return int(l)
411 return int(l)
402 except ValueError:
412 except ValueError:
403 self._abort(error.ResponseError(_("unexpected response:"), l))
413 self._abort(error.ResponseError(_("unexpected response:"), l))
404
414
405 def _recv(self):
415 def _recv(self):
406 return self._pipei.read(self._getamount())
416 return self._pipei.read(self._getamount())
407
417
408 def _send(self, data, flush=False):
418 def _send(self, data, flush=False):
409 self._pipeo.write("%d\n" % len(data))
419 self._pipeo.write("%d\n" % len(data))
410 if data:
420 if data:
411 self._pipeo.write(data)
421 self._pipeo.write(data)
412 if flush:
422 if flush:
413 self._pipeo.flush()
423 self._pipeo.flush()
414 self._readerr()
424 self._readerr()
415
425
416 def instance(ui, path, create):
426 def instance(ui, path, create):
417 """Create an SSH peer.
427 """Create an SSH peer.
418
428
419 The returned object conforms to the ``wireproto.wirepeer`` interface.
429 The returned object conforms to the ``wireproto.wirepeer`` interface.
420 """
430 """
421 u = util.url(path, parsequery=False, parsefragment=False)
431 u = util.url(path, parsequery=False, parsefragment=False)
422 if u.scheme != 'ssh' or not u.host or u.path is None:
432 if u.scheme != 'ssh' or not u.host or u.path is None:
423 raise error.RepoError(_("couldn't parse location %s") % path)
433 raise error.RepoError(_("couldn't parse location %s") % path)
424
434
425 util.checksafessh(path)
435 util.checksafessh(path)
426
436
427 if u.passwd is not None:
437 if u.passwd is not None:
428 raise error.RepoError(_('password in URL not supported'))
438 raise error.RepoError(_('password in URL not supported'))
429
439
430 sshcmd = ui.config('ui', 'ssh')
440 sshcmd = ui.config('ui', 'ssh')
431 remotecmd = ui.config('ui', 'remotecmd')
441 remotecmd = ui.config('ui', 'remotecmd')
432 sshaddenv = dict(ui.configitems('sshenv'))
442 sshaddenv = dict(ui.configitems('sshenv'))
433 sshenv = util.shellenviron(sshaddenv)
443 sshenv = util.shellenviron(sshaddenv)
434 remotepath = u.path or '.'
444 remotepath = u.path or '.'
435
445
436 args = util.sshargs(sshcmd, u.host, u.user, u.port)
446 args = util.sshargs(sshcmd, u.host, u.user, u.port)
437
447
438 if create:
448 if create:
439 cmd = '%s %s %s' % (sshcmd, args,
449 cmd = '%s %s %s' % (sshcmd, args,
440 util.shellquote('%s init %s' %
450 util.shellquote('%s init %s' %
441 (_serverquote(remotecmd), _serverquote(remotepath))))
451 (_serverquote(remotecmd), _serverquote(remotepath))))
442 ui.debug('running %s\n' % cmd)
452 ui.debug('running %s\n' % cmd)
443 res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv)
453 res = ui.system(cmd, blockedtag='sshpeer', environ=sshenv)
444 if res != 0:
454 if res != 0:
445 raise error.RepoError(_('could not create remote repo'))
455 raise error.RepoError(_('could not create remote repo'))
446
456
447 proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd,
457 proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd,
448 remotepath, sshenv)
458 remotepath, sshenv)
449
459
450 try:
460 try:
451 caps = _performhandshake(ui, stdin, stdout, stderr)
461 caps = _performhandshake(ui, stdin, stdout, stderr)
452 except Exception:
462 except Exception:
453 _cleanuppipes(ui, stdout, stdin, stderr)
463 _cleanuppipes(ui, stdout, stdin, stderr)
454 raise
464 raise
455
465
456 return sshpeer(ui, path, proc, stdin, stdout, stderr, caps)
466 return sshpeer(ui, path, proc, stdin, stdout, stderr, caps)
@@ -1,394 +1,390 b''
1 $ cat >> $HGRCPATH << EOF
1 $ cat >> $HGRCPATH << EOF
2 > [ui]
2 > [ui]
3 > ssh = $PYTHON "$TESTDIR/dummyssh"
3 > ssh = $PYTHON "$TESTDIR/dummyssh"
4 > [devel]
4 > [devel]
5 > debug.peer-request = true
5 > debug.peer-request = true
6 > [extensions]
6 > [extensions]
7 > sshprotoext = $TESTDIR/sshprotoext.py
7 > sshprotoext = $TESTDIR/sshprotoext.py
8 > EOF
8 > EOF
9
9
10 $ hg init server
10 $ hg init server
11 $ cd server
11 $ cd server
12 $ echo 0 > foo
12 $ echo 0 > foo
13 $ hg -q add foo
13 $ hg -q add foo
14 $ hg commit -m initial
14 $ hg commit -m initial
15 $ cd ..
15 $ cd ..
16
16
17 Test a normal behaving server, for sanity
17 Test a normal behaving server, for sanity
18
18
19 $ hg --debug debugpeer ssh://user@dummy/server
19 $ hg --debug debugpeer ssh://user@dummy/server
20 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
20 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
21 devel-peer-request: hello
21 devel-peer-request: hello
22 sending hello command
22 sending hello command
23 devel-peer-request: between
23 devel-peer-request: between
24 devel-peer-request: pairs: 81 bytes
24 devel-peer-request: pairs: 81 bytes
25 sending between command
25 sending between command
26 remote: 384
26 remote: 384
27 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
27 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
28 remote: 1
28 remote: 1
29 url: ssh://user@dummy/server
29 url: ssh://user@dummy/server
30 local: no
30 local: no
31 pushable: yes
31 pushable: yes
32
32
33 Server should answer the "hello" command in isolation
33 Server should answer the "hello" command in isolation
34
34
35 $ hg -R server serve --stdio << EOF
35 $ hg -R server serve --stdio << EOF
36 > hello
36 > hello
37 > EOF
37 > EOF
38 384
38 384
39 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
39 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
40
40
41 >=0.9.1 clients send a "hello" + "between" for the null range as part of handshake.
41 >=0.9.1 clients send a "hello" + "between" for the null range as part of handshake.
42 Server should reply with capabilities and should send "1\n\n" as a successful
42 Server should reply with capabilities and should send "1\n\n" as a successful
43 reply with empty response to the "between".
43 reply with empty response to the "between".
44
44
45 $ hg -R server serve --stdio << EOF
45 $ hg -R server serve --stdio << EOF
46 > hello
46 > hello
47 > between
47 > between
48 > pairs 81
48 > pairs 81
49 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
49 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
50 > EOF
50 > EOF
51 384
51 384
52 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
52 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
53 1
53 1
54
54
55
55
56 SSH banner is not printed by default, ignored by clients
56 SSH banner is not printed by default, ignored by clients
57
57
58 $ SSHSERVERMODE=banner hg debugpeer ssh://user@dummy/server
58 $ SSHSERVERMODE=banner hg debugpeer ssh://user@dummy/server
59 url: ssh://user@dummy/server
59 url: ssh://user@dummy/server
60 local: no
60 local: no
61 pushable: yes
61 pushable: yes
62
62
63 --debug will print the banner
63 --debug will print the banner
64
64
65 $ SSHSERVERMODE=banner hg --debug debugpeer ssh://user@dummy/server
65 $ SSHSERVERMODE=banner hg --debug debugpeer ssh://user@dummy/server
66 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
66 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
67 devel-peer-request: hello
67 devel-peer-request: hello
68 sending hello command
68 sending hello command
69 devel-peer-request: between
69 devel-peer-request: between
70 devel-peer-request: pairs: 81 bytes
70 devel-peer-request: pairs: 81 bytes
71 sending between command
71 sending between command
72 remote: banner: line 0
72 remote: banner: line 0
73 remote: banner: line 1
73 remote: banner: line 1
74 remote: banner: line 2
74 remote: banner: line 2
75 remote: banner: line 3
75 remote: banner: line 3
76 remote: banner: line 4
76 remote: banner: line 4
77 remote: banner: line 5
77 remote: banner: line 5
78 remote: banner: line 6
78 remote: banner: line 6
79 remote: banner: line 7
79 remote: banner: line 7
80 remote: banner: line 8
80 remote: banner: line 8
81 remote: banner: line 9
81 remote: banner: line 9
82 remote: 384
82 remote: 384
83 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
83 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
84 remote: 1
84 remote: 1
85 url: ssh://user@dummy/server
85 url: ssh://user@dummy/server
86 local: no
86 local: no
87 pushable: yes
87 pushable: yes
88
88
89 And test the banner with the raw protocol
89 And test the banner with the raw protocol
90
90
91 $ SSHSERVERMODE=banner hg -R server serve --stdio << EOF
91 $ SSHSERVERMODE=banner hg -R server serve --stdio << EOF
92 > hello
92 > hello
93 > between
93 > between
94 > pairs 81
94 > pairs 81
95 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
95 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
96 > EOF
96 > EOF
97 banner: line 0
97 banner: line 0
98 banner: line 1
98 banner: line 1
99 banner: line 2
99 banner: line 2
100 banner: line 3
100 banner: line 3
101 banner: line 4
101 banner: line 4
102 banner: line 5
102 banner: line 5
103 banner: line 6
103 banner: line 6
104 banner: line 7
104 banner: line 7
105 banner: line 8
105 banner: line 8
106 banner: line 9
106 banner: line 9
107 384
107 384
108 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
108 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
109 1
109 1
110
110
111
111
112 Connecting to a <0.9.1 server that doesn't support the hello command
112 Connecting to a <0.9.1 server that doesn't support the hello command.
113 The client should refuse, as we dropped support for connecting to such
114 servers.
113
115
114 $ SSHSERVERMODE=no-hello hg --debug debugpeer ssh://user@dummy/server
116 $ SSHSERVERMODE=no-hello hg --debug debugpeer ssh://user@dummy/server
115 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
117 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
116 devel-peer-request: hello
118 devel-peer-request: hello
117 sending hello command
119 sending hello command
118 devel-peer-request: between
120 devel-peer-request: between
119 devel-peer-request: pairs: 81 bytes
121 devel-peer-request: pairs: 81 bytes
120 sending between command
122 sending between command
121 remote: 0
123 remote: 0
122 remote: 1
124 remote: 1
123 url: ssh://user@dummy/server
125 abort: no suitable response from remote hg!
124 local: no
126 [255]
125 pushable: yes
126
127 The client should interpret this as no capabilities
128
129 $ SSHSERVERMODE=no-hello hg debugcapabilities ssh://user@dummy/server
130 Main capabilities:
131
127
132 Sending an unknown command to the server results in an empty response to that command
128 Sending an unknown command to the server results in an empty response to that command
133
129
134 $ hg -R server serve --stdio << EOF
130 $ hg -R server serve --stdio << EOF
135 > pre-hello
131 > pre-hello
136 > hello
132 > hello
137 > between
133 > between
138 > pairs 81
134 > pairs 81
139 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
135 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
140 > EOF
136 > EOF
141 0
137 0
142 384
138 384
143 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
139 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
144 1
140 1
145
141
146
142
147 $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-no-args --debug debugpeer ssh://user@dummy/server
143 $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-no-args --debug debugpeer ssh://user@dummy/server
148 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
144 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
149 sending no-args command
145 sending no-args command
150 devel-peer-request: hello
146 devel-peer-request: hello
151 sending hello command
147 sending hello command
152 devel-peer-request: between
148 devel-peer-request: between
153 devel-peer-request: pairs: 81 bytes
149 devel-peer-request: pairs: 81 bytes
154 sending between command
150 sending between command
155 remote: 0
151 remote: 0
156 remote: 384
152 remote: 384
157 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
153 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
158 remote: 1
154 remote: 1
159 url: ssh://user@dummy/server
155 url: ssh://user@dummy/server
160 local: no
156 local: no
161 pushable: yes
157 pushable: yes
162
158
163 Send multiple unknown commands before hello
159 Send multiple unknown commands before hello
164
160
165 $ hg -R server serve --stdio << EOF
161 $ hg -R server serve --stdio << EOF
166 > unknown1
162 > unknown1
167 > unknown2
163 > unknown2
168 > unknown3
164 > unknown3
169 > hello
165 > hello
170 > between
166 > between
171 > pairs 81
167 > pairs 81
172 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
168 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
173 > EOF
169 > EOF
174 0
170 0
175 0
171 0
176 0
172 0
177 384
173 384
178 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
174 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
179 1
175 1
180
176
181
177
182 $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-multiple-no-args --debug debugpeer ssh://user@dummy/server
178 $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-multiple-no-args --debug debugpeer ssh://user@dummy/server
183 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
179 running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob)
184 sending unknown1 command
180 sending unknown1 command
185 sending unknown2 command
181 sending unknown2 command
186 sending unknown3 command
182 sending unknown3 command
187 devel-peer-request: hello
183 devel-peer-request: hello
188 sending hello command
184 sending hello command
189 devel-peer-request: between
185 devel-peer-request: between
190 devel-peer-request: pairs: 81 bytes
186 devel-peer-request: pairs: 81 bytes
191 sending between command
187 sending between command
192 remote: 0
188 remote: 0
193 remote: 0
189 remote: 0
194 remote: 0
190 remote: 0
195 remote: 384
191 remote: 384
196 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
192 remote: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
197 remote: 1
193 remote: 1
198 url: ssh://user@dummy/server
194 url: ssh://user@dummy/server
199 local: no
195 local: no
200 pushable: yes
196 pushable: yes
201
197
202 Send an unknown command before hello that has arguments
198 Send an unknown command before hello that has arguments
203
199
204 $ hg -R server serve --stdio << EOF
200 $ hg -R server serve --stdio << EOF
205 > with-args
201 > with-args
206 > foo 13
202 > foo 13
207 > value for foo
203 > value for foo
208 > bar 13
204 > bar 13
209 > value for bar
205 > value for bar
210 > hello
206 > hello
211 > between
207 > between
212 > pairs 81
208 > pairs 81
213 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
209 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
214 > EOF
210 > EOF
215 0
211 0
216 0
212 0
217 0
213 0
218 0
214 0
219 0
215 0
220 384
216 384
221 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
217 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
222 1
218 1
223
219
224
220
225 Send an unknown command having an argument that looks numeric
221 Send an unknown command having an argument that looks numeric
226
222
227 $ hg -R server serve --stdio << EOF
223 $ hg -R server serve --stdio << EOF
228 > unknown
224 > unknown
229 > foo 1
225 > foo 1
230 > 0
226 > 0
231 > hello
227 > hello
232 > between
228 > between
233 > pairs 81
229 > pairs 81
234 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
230 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
235 > EOF
231 > EOF
236 0
232 0
237 0
233 0
238 0
234 0
239 384
235 384
240 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
236 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
241 1
237 1
242
238
243
239
244 $ hg -R server serve --stdio << EOF
240 $ hg -R server serve --stdio << EOF
245 > unknown
241 > unknown
246 > foo 1
242 > foo 1
247 > 1
243 > 1
248 > hello
244 > hello
249 > between
245 > between
250 > pairs 81
246 > pairs 81
251 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
247 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
252 > EOF
248 > EOF
253 0
249 0
254 0
250 0
255 0
251 0
256 384
252 384
257 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
253 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
258 1
254 1
259
255
260
256
261 When sending a dict argument value, it is serialized to
257 When sending a dict argument value, it is serialized to
262 "<arg> <item count>" followed by "<key> <len>\n<value>" for each item
258 "<arg> <item count>" followed by "<key> <len>\n<value>" for each item
263 in the dict.
259 in the dict.
264
260
265 Dictionary value for unknown command
261 Dictionary value for unknown command
266
262
267 $ hg -R server serve --stdio << EOF
263 $ hg -R server serve --stdio << EOF
268 > unknown
264 > unknown
269 > dict 3
265 > dict 3
270 > key1 3
266 > key1 3
271 > foo
267 > foo
272 > key2 3
268 > key2 3
273 > bar
269 > bar
274 > key3 3
270 > key3 3
275 > baz
271 > baz
276 > hello
272 > hello
277 > EOF
273 > EOF
278 0
274 0
279 0
275 0
280 0
276 0
281 0
277 0
282 0
278 0
283 0
279 0
284 0
280 0
285 0
281 0
286 384
282 384
287 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
283 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
288
284
289 Incomplete dictionary send
285 Incomplete dictionary send
290
286
291 $ hg -R server serve --stdio << EOF
287 $ hg -R server serve --stdio << EOF
292 > unknown
288 > unknown
293 > dict 3
289 > dict 3
294 > key1 3
290 > key1 3
295 > foo
291 > foo
296 > EOF
292 > EOF
297 0
293 0
298 0
294 0
299 0
295 0
300 0
296 0
301
297
302 Incomplete value send
298 Incomplete value send
303
299
304 $ hg -R server serve --stdio << EOF
300 $ hg -R server serve --stdio << EOF
305 > unknown
301 > unknown
306 > dict 3
302 > dict 3
307 > key1 3
303 > key1 3
308 > fo
304 > fo
309 > EOF
305 > EOF
310 0
306 0
311 0
307 0
312 0
308 0
313 0
309 0
314
310
315 Send a command line with spaces
311 Send a command line with spaces
316
312
317 $ hg -R server serve --stdio << EOF
313 $ hg -R server serve --stdio << EOF
318 > unknown withspace
314 > unknown withspace
319 > hello
315 > hello
320 > between
316 > between
321 > pairs 81
317 > pairs 81
322 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
318 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
323 > EOF
319 > EOF
324 0
320 0
325 384
321 384
326 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
322 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
327 1
323 1
328
324
329
325
330 $ hg -R server serve --stdio << EOF
326 $ hg -R server serve --stdio << EOF
331 > unknown with multiple spaces
327 > unknown with multiple spaces
332 > hello
328 > hello
333 > between
329 > between
334 > pairs 81
330 > pairs 81
335 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
331 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
336 > EOF
332 > EOF
337 0
333 0
338 384
334 384
339 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
335 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
340 1
336 1
341
337
342
338
343 $ hg -R server serve --stdio << EOF
339 $ hg -R server serve --stdio << EOF
344 > unknown with spaces
340 > unknown with spaces
345 > key 10
341 > key 10
346 > some value
342 > some value
347 > hello
343 > hello
348 > between
344 > between
349 > pairs 81
345 > pairs 81
350 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
346 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000
351 > EOF
347 > EOF
352 0
348 0
353 0
349 0
354 0
350 0
355 384
351 384
356 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
352 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
357 1
353 1
358
354
359
355
360 Send an unknown command after the "between"
356 Send an unknown command after the "between"
361
357
362 $ hg -R server serve --stdio << EOF
358 $ hg -R server serve --stdio << EOF
363 > hello
359 > hello
364 > between
360 > between
365 > pairs 81
361 > pairs 81
366 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000unknown
362 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000unknown
367 > EOF
363 > EOF
368 384
364 384
369 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
365 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
370 1
366 1
371
367
372 0
368 0
373
369
374 And one with arguments
370 And one with arguments
375
371
376 $ hg -R server serve --stdio << EOF
372 $ hg -R server serve --stdio << EOF
377 > hello
373 > hello
378 > between
374 > between
379 > pairs 81
375 > pairs 81
380 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000unknown
376 > 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000unknown
381 > foo 5
377 > foo 5
382 > value
378 > value
383 > bar 3
379 > bar 3
384 > baz
380 > baz
385 > EOF
381 > EOF
386 384
382 384
387 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
383 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN
388 1
384 1
389
385
390 0
386 0
391 0
387 0
392 0
388 0
393 0
389 0
394 0
390 0
General Comments 0
You need to be logged in to leave comments. Login now