I have used Trilead and Jsch to use as a SSH client for Hg, and now I'm trying it with SSHJ because it seems to provide more modern key support. The sketched code reads as
SSHClient client = createClientAndAuthenticate();
Session session = client.startSession();
Command cmd = session.exec(command); // command usually is "hg -R /path/to/hg/repository serve --stdio"
startThreadToCopyInputStream(cmd.getOutputStream());
startThreadToCopyOutputStream(cmd.getInputStream());
startThreadToCopyOutputStream(cmd.getErrorStream());
cmd.join(); // here it hangs endlessly
The startThreadToCopyInputStream
method copies all bytes from the local Hg process to cmd.getOutputStream()
and then finishes the input stream. But in contrast to Trilead and JSch the cmd.getInputStream()
and cmd.getErrorStream()
remain open endlessly.
I've now changed the code to something like:
SSHClient client = createClientAndAuthenticate();
Session session = client.startSession();
Command cmd = session.exec(command); // command usually is "hg -R /path/to/hg/repository serve --stdio"
startThreadToCopyInputStream(cmd.getOutputStream());
startThreadToCopyOutputStream(cmd.getInputStream());
startThreadToCopyOutputStream(cmd.getErrorStream());
waitUntilInputStreamIsClosed();
cmd.close();
This works fine in 90% of the cases, but sometimes Hg complains that the server closed the connection to early. How should I know when to be able to call cmd.close()
because the server process has finished?
It looks like this is a bug in SSHJ (see ticket 143). The work-around is to not use cmd.getOutputStream()
directly, but instead:
OutputStream cmdOutputStream = new FilterOutputStream(cmd.getOutputStream()) {
@Override
public void close() throws IOException {
try {
super.close();
}
finally {
client.getTransport().write(new SSHPacket(Message.CHANNEL_EOF).putUInt32(session.getRecipient()));
}
}
};
before passing to startThreadToCopyInputStream(cmdOutputStream);
.