comparison mercurial/sshpeer.py @ 36533:1a36ef7df70a

sshpeer: support not reading and forwarding stderr The "doublepipe" primitive as used by sshpeer will automatically read from stderr and forward output to the local ui. This poses problems for deterministic testing because reads may not be consistent. For example, the server may not be done sending all output to stderr and the client will perform different numbers of read operations or will read from stderr and stdout at different times. To make tests deterministic, we'll need to disable the "doublepipe" primitive and perform stderr I/O explicitly. We add an argument to the sshpeer constructor to disable the use of the doublepipe. Differential Revision: https://phab.mercurial-scm.org/D2467
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 26 Feb 2018 13:12:03 -0800
parents dabf86721200
children 3cd245945ef3
comparison
equal deleted inserted replaced
36532:1138e5c0fbc9 36533:1a36ef7df70a
335 _forwardoutput(ui, stderr) 335 _forwardoutput(ui, stderr)
336 336
337 return protoname, caps 337 return protoname, caps
338 338
339 class sshv1peer(wireproto.wirepeer): 339 class sshv1peer(wireproto.wirepeer):
340 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps): 340 def __init__(self, ui, url, proc, stdin, stdout, stderr, caps,
341 autoreadstderr=True):
341 """Create a peer from an existing SSH connection. 342 """Create a peer from an existing SSH connection.
342 343
343 ``proc`` is a handle on the underlying SSH process. 344 ``proc`` is a handle on the underlying SSH process.
344 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio 345 ``stdin``, ``stdout``, and ``stderr`` are handles on the stdio
345 pipes for that process. 346 pipes for that process.
346 ``caps`` is a set of capabilities supported by the remote. 347 ``caps`` is a set of capabilities supported by the remote.
348 ``autoreadstderr`` denotes whether to automatically read from
349 stderr and to forward its output.
347 """ 350 """
348 self._url = url 351 self._url = url
349 self._ui = ui 352 self._ui = ui
350 # self._subprocess is unused. Keeping a handle on the process 353 # self._subprocess is unused. Keeping a handle on the process
351 # holds a reference and prevents it from being garbage collected. 354 # holds a reference and prevents it from being garbage collected.
352 self._subprocess = proc 355 self._subprocess = proc
353 356
354 # And we hook up our "doublepipe" wrapper to allow querying 357 # And we hook up our "doublepipe" wrapper to allow querying
355 # stderr any time we perform I/O. 358 # stderr any time we perform I/O.
356 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr) 359 if autoreadstderr:
357 stdin = doublepipe(ui, stdin, stderr) 360 stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
361 stdin = doublepipe(ui, stdin, stderr)
358 362
359 self._pipeo = stdin 363 self._pipeo = stdin
360 self._pipei = stdout 364 self._pipei = stdout
361 self._pipee = stderr 365 self._pipee = stderr
362 self._caps = caps 366 self._caps = caps
529 """A peer that speakers version 2 of the transport protocol.""" 533 """A peer that speakers version 2 of the transport protocol."""
530 # Currently version 2 is identical to version 1 post handshake. 534 # Currently version 2 is identical to version 1 post handshake.
531 # And handshake is performed before the peer is instantiated. So 535 # And handshake is performed before the peer is instantiated. So
532 # we need no custom code. 536 # we need no custom code.
533 537
534 def makepeer(ui, path, proc, stdin, stdout, stderr): 538 def makepeer(ui, path, proc, stdin, stdout, stderr, autoreadstderr=True):
535 """Make a peer instance from existing pipes. 539 """Make a peer instance from existing pipes.
536 540
537 ``path`` and ``proc`` are stored on the eventual peer instance and may 541 ``path`` and ``proc`` are stored on the eventual peer instance and may
538 not be used for anything meaningful. 542 not be used for anything meaningful.
539 543
550 except Exception: 554 except Exception:
551 _cleanuppipes(ui, stdout, stdin, stderr) 555 _cleanuppipes(ui, stdout, stdin, stderr)
552 raise 556 raise
553 557
554 if protoname == wireprotoserver.SSHV1: 558 if protoname == wireprotoserver.SSHV1:
555 return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps) 559 return sshv1peer(ui, path, proc, stdin, stdout, stderr, caps,
560 autoreadstderr=autoreadstderr)
556 elif protoname == wireprotoserver.SSHV2: 561 elif protoname == wireprotoserver.SSHV2:
557 return sshv2peer(ui, path, proc, stdin, stdout, stderr, caps) 562 return sshv2peer(ui, path, proc, stdin, stdout, stderr, caps,
563 autoreadstderr=autoreadstderr)
558 else: 564 else:
559 _cleanuppipes(ui, stdout, stdin, stderr) 565 _cleanuppipes(ui, stdout, stdin, stderr)
560 raise error.RepoError(_('unknown version of SSH protocol: %s') % 566 raise error.RepoError(_('unknown version of SSH protocol: %s') %
561 protoname) 567 protoname)
562 568