Mercurial > public > mercurial-scm > hg-stable
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 |