D2383: sshpeer: defer pipe buffering and stderr sidechannel binding
indygreg (Gregory Szorc)
phabricator at mercurial-scm.org
Wed Feb 21 22:08:14 UTC 2018
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.
REVISION SUMMARY
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.
REPOSITORY
rHG Mercurial
REVISION DETAIL
https://phab.mercurial-scm.org/D2383
AFFECTED FILES
mercurial/sshpeer.py
tests/test-check-interfaces.py
CHANGE DETAILS
diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py
--- a/tests/test-check-interfaces.py
+++ b/tests/test-check-interfaces.py
@@ -59,16 +59,20 @@
def badmethod(self):
pass
+class dummypipe(object):
+ def close(self):
+ pass
+
def main():
ui = uimod.ui()
checkobject(badpeer())
checkobject(httppeer.httppeer(ui, 'http://localhost'))
checkobject(localrepo.localpeer(dummyrepo()))
- checkobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, None, None,
- None, None))
- checkobject(sshpeer.sshv2peer(ui, 'ssh://localhost/foo', None, None, None,
- None, None))
+ checkobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(),
+ dummypipe(), None, None))
+ checkobject(sshpeer.sshv2peer(ui, 'ssh://localhost/foo', None, dummypipe(),
+ dummypipe(), None, None))
checkobject(bundlerepo.bundlepeer(dummyrepo()))
checkobject(statichttprepo.statichttppeer(dummyrepo()))
checkobject(unionrepo.unionpeer(dummyrepo()))
diff --git a/mercurial/sshpeer.py b/mercurial/sshpeer.py
--- a/mercurial/sshpeer.py
+++ b/mercurial/sshpeer.py
@@ -156,13 +156,13 @@
# move to threading.
stdin, stdout, stderr, proc = util.popen4(cmd, bufsize=0, env=sshenv)
- stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
- stdin = doublepipe(ui, stdin, stderr)
-
return proc, stdin, stdout, stderr
def _performhandshake(ui, stdin, stdout, stderr):
def badresponse():
+ # Flush any output on stderr.
+ _forwardoutput(ui, stderr)
+
msg = _('no suitable response from remote hg')
hint = ui.config('ui', 'ssherrorhint')
raise error.RepoError(msg, hint=hint)
@@ -331,6 +331,9 @@
if not caps:
badresponse()
+ # Flush any output on stderr before proceeding.
+ _forwardoutput(ui, stderr)
+
return protoname, caps
class sshv1peer(wireproto.wirepeer):
@@ -347,6 +350,12 @@
# self._subprocess is unused. Keeping a handle on the process
# holds a reference and prevents it from being garbage collected.
self._subprocess = proc
+
+ # And we hook up our "doublepipe" wrapper to allow querying
+ # stderr any time we perform I/O.
+ stdout = doublepipe(ui, util.bufferedinputpipe(stdout), stderr)
+ stdin = doublepipe(ui, stdin, stderr)
+
self._pipeo = stdin
self._pipei = stdout
self._pipee = stderr
To: indygreg, #hg-reviewers
Cc: mercurial-devel
More information about the Mercurial-devel
mailing list