comparison mercurial/sshpeer.py @ 36401:11ba1a96f946

sshpeer: defer pipe buffering and stderr sidechannel binding The doublepipe and bufferedinputpipe types facilitate polling multiple pipes without blocking and for automatically forwarding output from the SSH server's stderr pipe to the ui as "remote: " output. This all happens automatically and callers don't need to worry about reading from multiple pipes. An upcoming change to version 2 of the SSH wire protocol will eliminate the use of stderr and move side-channel output into the "main" pipe. The SSH wire protocol will use a pair of unidirectional pipes - just like the HTTP protocol. In this future world, the doublepipe primitive isn't necessary because the stderr pipe won't be used. To prepare for eventually not using doublepipe, we delay the construction of this primitive from immediately after connection establishment to inside construction of the peer instance. The handshake occurs between these two events. So we had to teach the handshake code to read from stderr so any stderr output from the server is still attended to early in the connection lifetime. Differential Revision: https://phab.mercurial-scm.org/D2383
author Gregory Szorc <gregory.szorc@gmail.com>
date Wed, 21 Feb 2018 14:02:23 -0800
parents 066e6a9d52bb
children b8d0761a85c7
comparison
equal deleted inserted replaced
36400:066e6a9d52bb 36401:11ba1a96f946
154 # no buffer allow the use of 'select' 154 # no buffer allow the use of 'select'
155 # feel free to remove buffering and select usage when we ultimately 155 # feel free to remove buffering and select usage when we ultimately
156 # move to threading. 156 # move to threading.
157 stdin, stdout, stderr, proc = util.popen4(cmd, bufsize=0, env=sshenv) 157 stdin, stdout, stderr, proc = util.popen4(cmd, bufsize=0, env=sshenv)
158 158
159 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
160 stdin = doublepipe(ui, stdin, stderr)
161
162 return proc, stdin, stdout, stderr 159 return proc, stdin, stdout, stderr
163 160
164 def _performhandshake(ui, stdin, stdout, stderr): 161 def _performhandshake(ui, stdin, stdout, stderr):
165 def badresponse(): 162 def badresponse():
163 # Flush any output on stderr.
164 _forwardoutput(ui, stderr)
165
166 msg = _('no suitable response from remote hg') 166 msg = _('no suitable response from remote hg')
167 hint = ui.config('ui', 'ssherrorhint') 167 hint = ui.config('ui', 'ssherrorhint')
168 raise error.RepoError(msg, hint=hint) 168 raise error.RepoError(msg, hint=hint)
169 169
170 # The handshake consists of sending wire protocol commands in reverse 170 # The handshake consists of sending wire protocol commands in reverse
329 # 3. Remote is a future Mercurial server that dropped ``hello`` 329 # 3. Remote is a future Mercurial server that dropped ``hello``
330 # and other attempted handshake mechanisms. 330 # and other attempted handshake mechanisms.
331 if not caps: 331 if not caps:
332 badresponse() 332 badresponse()
333 333
334 # Flush any output on stderr before proceeding.
335 _forwardoutput(ui, stderr)
336
334 return protoname, caps 337 return protoname, caps
335 338
336 class sshv1peer(wireproto.wirepeer): 339 class sshv1peer(wireproto.wirepeer):
337 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps): 340 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps):
338 """Create a peer from an existing SSH connection. 341 """Create a peer from an existing SSH connection.
345 self._url = url 348 self._url = url
346 self._ui = ui 349 self._ui = ui
347 # self._subprocess is unused. Keeping a handle on the process 350 # self._subprocess is unused. Keeping a handle on the process
348 # holds a reference and prevents it from being garbage collected. 351 # holds a reference and prevents it from being garbage collected.
349 self._subprocess = proc 352 self._subprocess = proc
353
354 # And we hook up our "doublepipe" wrapper to allow querying
355 # stderr any time we perform I/O.
356 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
357 stdin = doublepipe(ui, stdin, stderr)
358
350 self._pipeo = stdin 359 self._pipeo = stdin
351 self._pipei = stdout 360 self._pipei = stdout
352 self._pipee = stderr 361 self._pipee = stderr
353 self._caps = caps 362 self._caps = caps
354 363