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)