Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/sshpeer.py @ 35978:80a2b8ae42a1
sshpeer: move handshake outside of sshpeer
With the handshake now performed before a peer class is instantiated,
we can now instantiate a different peer class depending on the results
of the handshake.
Our test extension had to change to cope with the new API. Because
we now issue the command via raw I/O calls and don't call
_callstream(), we no longer have to register the fake command.
(_callstream() uses the command registration to see what args to
send).
Differential Revision: https://phab.mercurial-scm.org/D2034
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Mon, 05 Feb 2018 09:14:32 -0800 |
parents | a9cffd14aa04 |
children | a622a927fe03 |
comparison
equal
deleted
inserted
replaced
35977:a9cffd14aa04 | 35978:80a2b8ae42a1 |
---|---|
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): | |
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 |
169 # self._subprocess is unused. Keeping a handle on the process | 225 # self._subprocess is unused. Keeping a handle on the process |
170 # holds a reference and prevents it from being garbage collected. | 226 # holds a reference and prevents it from being garbage collected. |
171 self._subprocess = proc | 227 self._subprocess = proc |
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 |
180 @util.propertycache | 235 @util.propertycache |
181 def ui(self): | 236 def ui(self): |
202 | 257 |
203 def capabilities(self): | 258 def capabilities(self): |
204 return self._caps | 259 return self._caps |
205 | 260 |
206 # End of _basewirecommands interface. | 261 # End of _basewirecommands interface. |
207 | |
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 | 262 |
263 def _readerr(self): | 263 def _readerr(self): |
264 _forwardoutput(self.ui, self._pipee) | 264 _forwardoutput(self.ui, self._pipee) |
265 | 265 |
266 def _abort(self, exception): | 266 def _abort(self, exception): |
412 raise error.RepoError(_('could not create remote repo')) | 412 raise error.RepoError(_('could not create remote repo')) |
413 | 413 |
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) |