Show More
@@ -156,13 +156,69 b' def _makeconnection(ui, sshcmd, args, re' | |||||
156 |
|
156 | |||
157 | return proc, stdin, stdout, stderr |
|
157 | return proc, stdin, stdout, stderr | |
158 |
|
158 | |||
|
159 | def _performhandshake(ui, stdin, stdout, stderr): | |||
|
160 | def badresponse(): | |||
|
161 | msg = _('no suitable response from remote hg') | |||
|
162 | hint = ui.config('ui', 'ssherrorhint') | |||
|
163 | raise error.RepoError(msg, hint=hint) | |||
|
164 | ||||
|
165 | requestlog = ui.configbool('devel', 'debug.peer-request') | |||
|
166 | ||||
|
167 | try: | |||
|
168 | pairsarg = '%s-%s' % ('0' * 40, '0' * 40) | |||
|
169 | handshake = [ | |||
|
170 | 'hello\n', | |||
|
171 | 'between\n', | |||
|
172 | 'pairs %d\n' % len(pairsarg), | |||
|
173 | pairsarg, | |||
|
174 | ] | |||
|
175 | ||||
|
176 | if requestlog: | |||
|
177 | ui.debug('devel-peer-request: hello\n') | |||
|
178 | ui.debug('sending hello command\n') | |||
|
179 | if requestlog: | |||
|
180 | ui.debug('devel-peer-request: between\n') | |||
|
181 | ui.debug('devel-peer-request: pairs: %d bytes\n' % len(pairsarg)) | |||
|
182 | ui.debug('sending between command\n') | |||
|
183 | ||||
|
184 | stdin.write(''.join(handshake)) | |||
|
185 | stdin.flush() | |||
|
186 | except IOError: | |||
|
187 | badresponse() | |||
|
188 | ||||
|
189 | lines = ['', 'dummy'] | |||
|
190 | max_noise = 500 | |||
|
191 | while lines[-1] and max_noise: | |||
|
192 | try: | |||
|
193 | l = stdout.readline() | |||
|
194 | _forwardoutput(ui, stderr) | |||
|
195 | if lines[-1] == '1\n' and l == '\n': | |||
|
196 | break | |||
|
197 | if l: | |||
|
198 | ui.debug('remote: ', l) | |||
|
199 | lines.append(l) | |||
|
200 | max_noise -= 1 | |||
|
201 | except IOError: | |||
|
202 | badresponse() | |||
|
203 | else: | |||
|
204 | badresponse() | |||
|
205 | ||||
|
206 | caps = set() | |||
|
207 | for l in reversed(lines): | |||
|
208 | if l.startswith('capabilities:'): | |||
|
209 | caps.update(l[:-1].split(':')[1].split()) | |||
|
210 | break | |||
|
211 | ||||
|
212 | return caps | |||
|
213 | ||||
159 | class sshpeer(wireproto.wirepeer): |
|
214 | class sshpeer(wireproto.wirepeer): | |
160 | def __init__(self, ui, url, proc, stdin, stdout, stderr): |
|
215 | def __init__(self, ui, url, proc, stdin, stdout, stderr, caps): | |
161 | """Create a peer from an existing SSH connection. |
|
216 | """Create a peer from an existing SSH connection. | |
162 |
|
217 | |||
163 | ``proc`` is a handle on the underlying SSH process. |
|
218 | ``proc`` is a handle on the underlying SSH process. | |
164 | ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio |
|
219 | ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio | |
165 | pipes for that process. |
|
220 | pipes for that process. | |
|
221 | ``caps`` is a set of capabilities supported by the remote. | |||
166 | """ |
|
222 | """ | |
167 | self._url = url |
|
223 | self._url = url | |
168 | self._ui = ui |
|
224 | self._ui = ui | |
@@ -172,8 +228,7 b' class sshpeer(wireproto.wirepeer):' | |||||
172 | self._pipeo = stdin |
|
228 | self._pipeo = stdin | |
173 | self._pipei = stdout |
|
229 | self._pipei = stdout | |
174 | self._pipee = stderr |
|
230 | self._pipee = stderr | |
175 |
|
231 | self._caps = caps | ||
176 | self._validaterepo() |
|
|||
177 |
|
232 | |||
178 | # Begin of _basepeer interface. |
|
233 | # Begin of _basepeer interface. | |
179 |
|
234 | |||
@@ -205,61 +260,6 b' class sshpeer(wireproto.wirepeer):' | |||||
205 |
|
260 | |||
206 | # End of _basewirecommands interface. |
|
261 | # End of _basewirecommands interface. | |
207 |
|
262 | |||
208 | def _validaterepo(self): |
|
|||
209 | def badresponse(): |
|
|||
210 | msg = _("no suitable response from remote hg") |
|
|||
211 | hint = self.ui.config("ui", "ssherrorhint") |
|
|||
212 | self._abort(error.RepoError(msg, hint=hint)) |
|
|||
213 |
|
||||
214 | try: |
|
|||
215 | pairsarg = '%s-%s' % ('0' * 40, '0' * 40) |
|
|||
216 |
|
||||
217 | handshake = [ |
|
|||
218 | 'hello\n', |
|
|||
219 | 'between\n', |
|
|||
220 | 'pairs %d\n' % len(pairsarg), |
|
|||
221 | pairsarg, |
|
|||
222 | ] |
|
|||
223 |
|
||||
224 | requestlog = self.ui.configbool('devel', 'debug.peer-request') |
|
|||
225 |
|
||||
226 | if requestlog: |
|
|||
227 | self.ui.debug('devel-peer-request: hello\n') |
|
|||
228 | self.ui.debug('sending hello command\n') |
|
|||
229 | if requestlog: |
|
|||
230 | self.ui.debug('devel-peer-request: between\n') |
|
|||
231 | self.ui.debug('devel-peer-request: pairs: %d bytes\n' % |
|
|||
232 | len(pairsarg)) |
|
|||
233 | self.ui.debug('sending between command\n') |
|
|||
234 |
|
||||
235 | self._pipeo.write(''.join(handshake)) |
|
|||
236 | self._pipeo.flush() |
|
|||
237 | except IOError: |
|
|||
238 | badresponse() |
|
|||
239 |
|
||||
240 | lines = ["", "dummy"] |
|
|||
241 | max_noise = 500 |
|
|||
242 | while lines[-1] and max_noise: |
|
|||
243 | try: |
|
|||
244 | l = self._pipei.readline() |
|
|||
245 | _forwardoutput(self.ui, self._pipee) |
|
|||
246 | if lines[-1] == "1\n" and l == "\n": |
|
|||
247 | break |
|
|||
248 | if l: |
|
|||
249 | self.ui.debug("remote: ", l) |
|
|||
250 | lines.append(l) |
|
|||
251 | max_noise -= 1 |
|
|||
252 | except IOError: |
|
|||
253 | badresponse() |
|
|||
254 | else: |
|
|||
255 | badresponse() |
|
|||
256 |
|
||||
257 | self._caps = set() |
|
|||
258 | for l in reversed(lines): |
|
|||
259 | if l.startswith("capabilities:"): |
|
|||
260 | self._caps.update(l[:-1].split(":")[1].split()) |
|
|||
261 | break |
|
|||
262 |
|
||||
263 | def _readerr(self): |
|
263 | def _readerr(self): | |
264 | _forwardoutput(self.ui, self._pipee) |
|
264 | _forwardoutput(self.ui, self._pipee) | |
265 |
|
265 | |||
@@ -414,4 +414,10 b' def instance(ui, path, create):' | |||||
414 | proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd, |
|
414 | proc, stdin, stdout, stderr = _makeconnection(ui, sshcmd, args, remotecmd, | |
415 | remotepath, sshenv) |
|
415 | remotepath, sshenv) | |
416 |
|
416 | |||
417 | return sshpeer(ui, path, proc, stdin, stdout, stderr) |
|
417 | try: | |
|
418 | caps = _performhandshake(ui, stdin, stdout, stderr) | |||
|
419 | except Exception: | |||
|
420 | _cleanuppipes(ui, stdout, stdin, stderr) | |||
|
421 | raise | |||
|
422 | ||||
|
423 | return sshpeer(ui, path, proc, stdin, stdout, stderr, caps) |
@@ -12,6 +12,7 b' from __future__ import absolute_import' | |||||
12 |
|
12 | |||
13 | from mercurial import ( |
|
13 | from mercurial import ( | |
14 | error, |
|
14 | error, | |
|
15 | extensions, | |||
15 | registrar, |
|
16 | registrar, | |
16 | sshpeer, |
|
17 | sshpeer, | |
17 | wireproto, |
|
18 | wireproto, | |
@@ -52,30 +53,26 b' class prehelloserver(wireprotoserver.ssh' | |||||
52 |
|
53 | |||
53 | super(prehelloserver, self).serve_forever() |
|
54 | super(prehelloserver, self).serve_forever() | |
54 |
|
55 | |||
55 | class extrahandshakecommandspeer(sshpeer.sshpeer): |
|
56 | def performhandshake(orig, ui, stdin, stdout, stderr): | |
56 | """An ssh peer that sends extra commands as part of initial handshake.""" |
|
57 | """Wrapped version of sshpeer._performhandshake to send extra commands.""" | |
57 | def _validaterepo(self): |
|
58 | mode = ui.config(b'sshpeer', b'handshake-mode') | |
58 | mode = self._ui.config(b'sshpeer', b'handshake-mode') |
|
59 | if mode == b'pre-no-args': | |
59 | if mode == b'pre-no-args': |
|
60 | ui.debug(b'sending no-args command\n') | |
60 |
|
|
61 | stdin.write(b'no-args\n') | |
61 | return super(extrahandshakecommandspeer, self)._validaterepo() |
|
62 | stdin.flush() | |
62 | elif mode == b'pre-multiple-no-args': |
|
63 | return orig(ui, stdin, stdout, stderr) | |
63 | self._callstream(b'unknown1') |
|
64 | elif mode == b'pre-multiple-no-args': | |
64 | self._callstream(b'unknown2') |
|
65 | ui.debug(b'sending unknown1 command\n') | |
65 | self._callstream(b'unknown3') |
|
66 | stdin.write(b'unknown1\n') | |
66 | return super(extrahandshakecommandspeer, self)._validaterepo() |
|
67 | ui.debug(b'sending unknown2 command\n') | |
67 | else: |
|
68 | stdin.write(b'unknown2\n') | |
68 | raise error.ProgrammingError(b'unknown HANDSHAKECOMMANDMODE: %s' % |
|
69 | ui.debug(b'sending unknown3 command\n') | |
69 | mode) |
|
70 | stdin.write(b'unknown3\n') | |
70 |
|
71 | stdin.flush() | ||
71 | def registercommands(): |
|
72 | return orig(ui, stdin, stdout, stderr) | |
72 | def dummycommand(repo, proto): |
|
73 | else: | |
73 | raise error.ProgrammingError('this should never be called') |
|
74 | raise error.ProgrammingError(b'unknown HANDSHAKECOMMANDMODE: %s' % | |
74 |
|
75 | mode) | ||
75 | wireproto.wireprotocommand(b'no-args', b'')(dummycommand) |
|
|||
76 | wireproto.wireprotocommand(b'unknown1', b'')(dummycommand) |
|
|||
77 | wireproto.wireprotocommand(b'unknown2', b'')(dummycommand) |
|
|||
78 | wireproto.wireprotocommand(b'unknown3', b'')(dummycommand) |
|
|||
79 |
|
76 | |||
80 | def extsetup(ui): |
|
77 | def extsetup(ui): | |
81 | # It's easier for tests to define the server behavior via environment |
|
78 | # It's easier for tests to define the server behavior via environment | |
@@ -94,7 +91,6 b' def extsetup(ui):' | |||||
94 | peermode = ui.config(b'sshpeer', b'mode') |
|
91 | peermode = ui.config(b'sshpeer', b'mode') | |
95 |
|
92 | |||
96 | if peermode == b'extra-handshake-commands': |
|
93 | if peermode == b'extra-handshake-commands': | |
97 | sshpeer.sshpeer = extrahandshakecommandspeer |
|
94 | extensions.wrapfunction(sshpeer, '_performhandshake', performhandshake) | |
98 | registercommands() |
|
|||
99 | elif peermode: |
|
95 | elif peermode: | |
100 | raise error.ProgrammingError(b'unknown peer mode: %s' % peermode) |
|
96 | raise error.ProgrammingError(b'unknown peer mode: %s' % peermode) |
@@ -51,10 +51,6 b' class dummyrepo(object):' | |||||
51 | pass |
|
51 | pass | |
52 |
|
52 | |||
53 | # Facilitates testing sshpeer without requiring an SSH server. |
|
53 | # Facilitates testing sshpeer without requiring an SSH server. | |
54 | class testingsshpeer(sshpeer.sshpeer): |
|
|||
55 | def _validaterepo(self, *args, **kwargs): |
|
|||
56 | pass |
|
|||
57 |
|
||||
58 | class badpeer(httppeer.httppeer): |
|
54 | class badpeer(httppeer.httppeer): | |
59 | def __init__(self): |
|
55 | def __init__(self): | |
60 | super(badpeer, self).__init__(uimod.ui(), 'http://localhost') |
|
56 | super(badpeer, self).__init__(uimod.ui(), 'http://localhost') | |
@@ -69,8 +65,8 b' def main():' | |||||
69 | checkobject(badpeer()) |
|
65 | checkobject(badpeer()) | |
70 | checkobject(httppeer.httppeer(ui, 'http://localhost')) |
|
66 | checkobject(httppeer.httppeer(ui, 'http://localhost')) | |
71 | checkobject(localrepo.localpeer(dummyrepo())) |
|
67 | checkobject(localrepo.localpeer(dummyrepo())) | |
72 |
checkobject( |
|
68 | checkobject(sshpeer.sshpeer(ui, 'ssh://localhost/foo', None, None, None, | |
73 | None)) |
|
69 | None, None)) | |
74 | checkobject(bundlerepo.bundlepeer(dummyrepo())) |
|
70 | checkobject(bundlerepo.bundlepeer(dummyrepo())) | |
75 | checkobject(statichttprepo.statichttppeer(dummyrepo())) |
|
71 | checkobject(statichttprepo.statichttppeer(dummyrepo())) | |
76 | checkobject(unionrepo.unionpeer(dummyrepo())) |
|
72 | checkobject(unionrepo.unionpeer(dummyrepo())) |
@@ -146,7 +146,6 b' Sending an unknown command to the server' | |||||
146 |
|
146 | |||
147 | $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-no-args --debug debugpeer ssh://user@dummy/server |
|
147 | $ 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) |
|
148 | running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) | |
149 | devel-peer-request: no-args |
|
|||
150 | sending no-args command |
|
149 | sending no-args command | |
151 | devel-peer-request: hello |
|
150 | devel-peer-request: hello | |
152 | sending hello command |
|
151 | sending hello command | |
@@ -182,11 +181,8 b' Send multiple unknown commands before he' | |||||
182 |
|
181 | |||
183 | $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-multiple-no-args --debug debugpeer ssh://user@dummy/server |
|
182 | $ hg --config sshpeer.mode=extra-handshake-commands --config sshpeer.handshake-mode=pre-multiple-no-args --debug debugpeer ssh://user@dummy/server | |
184 | running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) |
|
183 | running * "*/tests/dummyssh" 'user@dummy' 'hg -R server serve --stdio' (glob) | |
185 | devel-peer-request: unknown1 |
|
|||
186 | sending unknown1 command |
|
184 | sending unknown1 command | |
187 | devel-peer-request: unknown2 |
|
|||
188 | sending unknown2 command |
|
185 | sending unknown2 command | |
189 | devel-peer-request: unknown3 |
|
|||
190 | sending unknown3 command |
|
186 | sending unknown3 command | |
191 | devel-peer-request: hello |
|
187 | devel-peer-request: hello | |
192 | sending hello command |
|
188 | sending hello command |
General Comments 0
You need to be logged in to leave comments.
Login now