Hi all,
I found the Jsch framework will not grab the first remote message when the timeout is set for a channel.
This happened when the timeout in channel is set.
This will cause a while-notify in the Request class: (all code in 0.1.53, and I added some logs to track this problem):
void write(Packet packet) throws Exception {
if (reply) {
channel.reply = -1;
}
session.write(packet);
if (reply) {
long start = System.currentTimeMillis();
long timeout = channel.connectTimeout;
JSch.getLogger().log(Logger.DEBUG, "Waiting for response for request " + timeout);
while (channel.isConnected() && channel.reply == -1) {
try {
Thread.sleep(10);
}
catch (Exception ee) {
}
if (timeout > 0L &&
(System.currentTimeMillis() - start) > timeout) {
channel.reply = 0;
JSch.getLogger().log(Logger.DEBUG, "Waiting for response for request timeout .... " + timeout);
throw new JSchException("channel request: timeout");
}
}
JSch.getLogger().log(Logger.DEBUG, "Replay for response for request " + timeout + " Received");
if (channel.reply == 0) {
JSch.getLogger().log(Logger.DEBUG, "Fail to send for response for request timeout .... " + timeout);
throw new JSchException("failed to send channel request");
}
}
else {
JSch.getLogger().log(Logger.DEBUG, "no need Waiting for response for request timeout .... ");
}
}
This is exeucted in the channel.connect thread, let's name to main thread.
but in previous Session.connect it will start a new thread named connect thread of HostXXX:
synchronized (lock) {
if (isConnected) {
connectThread = new Thread(this);
connectThread.setName("Connect thread " + host + " session");
if (daemon_thread) {
connectThread.setDaemon(daemon_thread);
}
connectThread.start();
requestPortForwarding();
}
else {
// The session has been already down and
// we don't have to start new thread.
}
}
This connect thread will read and process data.
When it received data, it will try to call channel.write:
case SSH_MSG_CHANNEL_DATA:
buf.getInt();
buf.getByte();
buf.getByte();
i = buf.getInt();
channel = Channel.getChannel(i, this);
foo = buf.getString(start, length);
if (channel == null) {
break;
}
if (length[0] == 0) {
break;
}
JSch.getLogger().log(Logger.DEBUG, "Receive data from remote, data is:" + new String(foo, start[0], length[0]) + " channel claz:" + channel.getClass().getCanonicalName());
try {
channel.write(foo, start[0], length[0]);
}
In this method it will fail because of the io.output stream is still null.
It will be set latter in the main thread when someone calling the channel.getInputStream
So when two threads is executing, it may lose the first message because of the NPE catched in the channel.write method:
void write(byte[] foo, int s, int l) throws IOException {
try {
io.put(foo, s, l);
}
catch (NullPointerException e) {
JSch.getLogger().log(Logger.DEBUG, "Got exception " + Print.printException(e));
}
}