You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2016/02/21 12:39:52 UTC

[5/5] mina-sshd git commit: Expose IoWriteFuture as return value wherever applicable

Expose IoWriteFuture as return value wherever applicable


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/751ea6a7
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/751ea6a7
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/751ea6a7

Branch: refs/heads/master
Commit: 751ea6a718d383042e15b013bc8ad08f139d93d2
Parents: 51a767b
Author: Lyor Goldstein <ly...@gmail.com>
Authored: Sun Feb 21 13:40:27 2016 +0200
Committer: Lyor Goldstein <ly...@gmail.com>
Committed: Sun Feb 21 13:40:27 2016 +0200

----------------------------------------------------------------------
 .../client/auth/password/UserAuthPassword.java  |   7 +-
 .../client/session/ClientConnectionService.java |  18 +++-
 .../org/apache/sshd/common/FactoryManager.java  |  11 ++
 .../sshd/common/channel/AbstractChannel.java    |  25 +++--
 .../common/forward/DefaultTcpipForwarder.java   |   2 +-
 .../session/AbstractConnectionService.java      |  16 ++-
 .../sshd/common/session/AbstractSession.java    | 103 ++++++++++++-------
 .../org/apache/sshd/common/session/Session.java |   3 +-
 .../sshd/server/auth/gss/UserAuthGSS.java       |   1 -
 .../sshd/server/channel/ChannelSession.java     |  15 +--
 .../sshd/common/auth/AuthenticationTest.java    |   7 +-
 11 files changed, 144 insertions(+), 64 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
index 5ab7ebf..37f3ba5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/password/UserAuthPassword.java
@@ -27,6 +27,7 @@ import org.apache.sshd.client.auth.keyboard.UserInteraction;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.RuntimeSshException;
 import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
@@ -125,9 +126,11 @@ public class UserAuthPassword extends AbstractUserAuth {
      * @param session The target {@link ClientSession}
      * @param oldPassword The previous password
      * @param newPassword The new password
+     * @return An {@link IoWriteFuture} that can be used to wait and check
+     * on the success/failure of the request packet being sent
      * @throws IOException If failed to send the message.
      */
-    protected void sendPassword(Buffer buffer, ClientSession session, String oldPassword, String newPassword) throws IOException {
+    protected IoWriteFuture sendPassword(Buffer buffer, ClientSession session, String oldPassword, String newPassword) throws IOException {
         String username = session.getUsername();
         String service = getService();
         String name = getName();
@@ -150,6 +153,6 @@ public class UserAuthPassword extends AbstractUserAuth {
         if (modified) {
             buffer.putString(newPassword);
         }
-        session.writePacket(buffer);
+        return session.writePacket(buffer);
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/client/session/ClientConnectionService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientConnectionService.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientConnectionService.java
index fff23fd..5e2677e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientConnectionService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientConnectionService.java
@@ -28,6 +28,8 @@ import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.common.PropertyResolverUtils;
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.io.AbstractIoWriteFuture;
+import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.session.AbstractConnectionService;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.server.x11.X11ForwardSupport;
@@ -74,18 +76,30 @@ public class ClientConnectionService extends AbstractConnectionService<AbstractC
         }
     }
 
-    protected void sendHeartBeat() {
+    /**
+     * Sends a heartbeat message
+     * @return The {@link IoWriteFuture} that can be used to wait for the
+     * message write completion
+     */
+    protected IoWriteFuture sendHeartBeat() {
         ClientSession session = getClientSession();
         String request = PropertyResolverUtils.getStringProperty(session, ClientFactoryManager.HEARTBEAT_REQUEST, ClientFactoryManager.DEFAULT_KEEP_ALIVE_HEARTBEAT_STRING);
         try {
             Buffer buf = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST, request.length() + Byte.SIZE);
             buf.putString(request);
             buf.putBoolean(false);
-            session.writePacket(buf);
+            return session.writePacket(buf);
         } catch (IOException e) {
             if (log.isDebugEnabled()) {
                 log.debug("Error (" + e.getClass().getSimpleName() + ") sending keepalive message=" + request + ": " + e.getMessage());
             }
+
+            final Throwable t = e;
+            return new AbstractIoWriteFuture(null) {
+                {
+                    setValue(t);
+                }
+            };
         }
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
index bc9dd14..9ab1f7e 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/FactoryManager.java
@@ -216,6 +216,17 @@ public interface FactoryManager
     String DEFAULT_VERSION = "SSHD-UNKNOWN";
 
     /**
+     * Maximum allowed size of the initial identification text sent during
+     * the handshake
+     */
+    String MAX_IDENTIFICATION_SIZE = "max-identification-size";
+
+    /**
+     * Default value for {@link #MAX_IDENTIFICATION_SIZE} if none set
+     */
+    int DEFAULT_MAX_IDENTIFICATION_SIZE = 16 * 1024;
+
+    /**
      * Key re-exchange will be automatically performed after the session
      * has sent or received the given amount of bytes. If non-positive,
      * then disabled. The default value is {@link #DEFAULT_REKEY_BYTES_LIMIT}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
index 8a78d6e..afced68 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/AbstractChannel.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sshd.common.channel;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -41,6 +42,7 @@ import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.future.CloseFuture;
 import org.apache.sshd.common.future.DefaultCloseFuture;
 import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.AbstractIoWriteFuture;
 import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.session.ConnectionService;
 import org.apache.sshd.common.session.Session;
@@ -313,13 +315,17 @@ public abstract class AbstractChannel
         return RequestHandler.Result.Unsupported;
     }
 
-    protected void sendResponse(Buffer buffer, String req, RequestHandler.Result result, boolean wantReply) throws IOException {
+    protected IoWriteFuture sendResponse(Buffer buffer, String req, RequestHandler.Result result, boolean wantReply) throws IOException {
         if (log.isDebugEnabled()) {
             log.debug("sendResponse({}) request={} result={}, want-reply={}", this, req, result, wantReply);
         }
 
         if (RequestHandler.Result.Replied.equals(result) || (!wantReply)) {
-            return;
+            return new AbstractIoWriteFuture(null) {
+                {
+                    setValue(Boolean.TRUE);
+                }
+            };
         }
 
         byte cmd = RequestHandler.Result.ReplySuccess.equals(result)
@@ -328,7 +334,7 @@ public abstract class AbstractChannel
         Session session = getSession();
         Buffer rsp = session.createBuffer(cmd, Integer.SIZE / Byte.SIZE);
         rsp.putInt(recipient);
-        session.writePacket(rsp);
+        return session.writePacket(rsp);
     }
 
     @Override
@@ -616,12 +622,19 @@ public abstract class AbstractChannel
         super.doCloseImmediately();
     }
 
-    protected void writePacket(Buffer buffer) throws IOException {
+    protected IoWriteFuture writePacket(Buffer buffer) throws IOException {
         if (!isClosing()) {
             Session s = getSession();
-            s.writePacket(buffer);
+            return s.writePacket(buffer);
         } else {
-            log.debug("writePacket({}) Discarding output packet because channel is being closed", this);
+            if (log.isDebugEnabled()) {
+                log.debug("writePacket({}) Discarding output packet because channel is being closed", this);
+            }
+            return new AbstractIoWriteFuture(null) {
+                {
+                    setValue(new EOFException("Channel is being closed"));
+                }
+            };
         }
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
index 905a16d..b8aa438 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/forward/DefaultTcpipForwarder.java
@@ -188,7 +188,7 @@ public class DefaultTcpipForwarder
         buffer.putInt(remotePort);
 
         long timeout = PropertyResolverUtils.getLongProperty(session, FORWARD_REQUEST_TIMEOUT, DEFAULT_FORWARD_REQUEST_TIMEOUT);
-        Buffer result = session.request(buffer, timeout, TimeUnit.MILLISECONDS);
+        Buffer result = session.request("tcpip-forward", buffer, timeout, TimeUnit.MILLISECONDS);
         if (result == null) {
             throw new SshException("Tcpip forwarding request denied by server");
         }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java
index b3696fc..7f26424 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractConnectionService.java
@@ -45,6 +45,8 @@ import org.apache.sshd.common.channel.Window;
 import org.apache.sshd.common.forward.TcpipForwarder;
 import org.apache.sshd.common.forward.TcpipForwarderFactory;
 import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.AbstractIoWriteFuture;
+import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.Int2IntFunction;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -555,7 +557,7 @@ public abstract class AbstractConnectionService<S extends AbstractSession> exten
         });
     }
 
-    protected void sendChannelOpenFailure(Buffer buffer, int sender, int reasonCode, String message, String lang) throws IOException {
+    protected IoWriteFuture sendChannelOpenFailure(Buffer buffer, int sender, int reasonCode, String message, String lang) throws IOException {
         if (log.isDebugEnabled()) {
             log.debug("sendChannelOpenFailure({}) sender={}, reason={}, lang={}, message='{}'",
                       this, sender, SshConstants.getOpenErrorCodeName(reasonCode), lang, message);
@@ -568,7 +570,7 @@ public abstract class AbstractConnectionService<S extends AbstractSession> exten
         buf.putInt(reasonCode);
         buf.putString(message);
         buf.putString(lang);
-        session.writePacket(buf);
+        return session.writePacket(buf);
     }
 
     /**
@@ -624,13 +626,17 @@ public abstract class AbstractConnectionService<S extends AbstractSession> exten
         sendGlobalResponse(buffer, req, RequestHandler.Result.Unsupported, wantReply);
     }
 
-    protected void sendGlobalResponse(Buffer buffer, String req, RequestHandler.Result result, boolean wantReply) throws IOException {
+    protected IoWriteFuture sendGlobalResponse(Buffer buffer, String req, RequestHandler.Result result, boolean wantReply) throws IOException {
         if (log.isDebugEnabled()) {
             log.debug("sendGlobalResponse({})[{}] result={}, want-reply={}", this, req, result, wantReply);
         }
 
         if (RequestHandler.Result.Replied.equals(result) || (!wantReply)) {
-            return;
+            return new AbstractIoWriteFuture(null) {
+                {
+                    setValue(Boolean.TRUE);
+                }
+            };
         }
 
         byte cmd = RequestHandler.Result.ReplySuccess.equals(result)
@@ -638,7 +644,7 @@ public abstract class AbstractConnectionService<S extends AbstractSession> exten
                  : SshConstants.SSH_MSG_REQUEST_FAILURE;
         Session session = getSession();
         Buffer rsp = session.createBuffer(cmd, 2);
-        session.writePacket(rsp);
+        return session.writePacket(rsp);
     }
 
     protected void requestSuccess(Buffer buffer) throws Exception {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index 967650f..a7c7242 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -517,7 +517,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
             Pair<String, String> result = comparePreferredKexProposalOption(option);
             if (result != null) {
                 if (log.isDebugEnabled()) {
-                    log.debug("handleKexMessage({})[{}] 1st follow KEX packet {} option mismatch: client={}, server={}",
+                    log.debug("handleFirstKexPacketFollows({})[{}] 1st follow KEX packet {} option mismatch: client={}, server={}",
                               this, SshConstants.getCommandMessageName(cmd), option, result.getFirst(), result.getSecond());
                 }
                 return false;
@@ -574,6 +574,10 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         if (log.isDebugEnabled()) {
             log.debug("handleUnimplented({}) SSH_MSG_UNIMPLEMENTED #{}", this, seqNo);
         }
+
+        if (log.isTraceEnabled()) {
+            log.trace("handleUnimplemented({}) data: {}", this, buffer.toHex());
+        }
     }
 
     protected void handleDebug(Buffer buffer) throws Exception {
@@ -611,7 +615,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         validateKexState(SshConstants.SSH_MSG_SERVICE_REQUEST, KexState.DONE);
         try {
             startService(serviceName);
-        } catch (Exception e) {
+        } catch (Throwable e) {
             if (log.isDebugEnabled()) {
                 log.debug("handleServiceRequest({}) Service {} rejected: {} = {}",
                           this, serviceName, e.getClass().getSimpleName(), e.getMessage());
@@ -847,15 +851,6 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         throw new IllegalStateException("Attempted to access unknown service " + clazz.getSimpleName());
     }
 
-    /**
-     * Encode and send the given buffer.
-     * The buffer has to have 5 bytes free at the beginning to allow the encoding to take place.
-     * Also, the write position of the buffer has to be set to the position of the last byte to write.
-     *
-     * @param buffer the buffer to encode and send
-     * @return a future that can be used to check when the packet has actually been sent
-     * @throws IOException if an error occurred when encoding sending the packet
-     */
     @Override
     public IoWriteFuture writePacket(Buffer buffer) throws IOException {
         // While exchanging key, queue high level packets
@@ -981,12 +976,16 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
     }
 
     @Override
-    public Buffer request(Buffer buffer, long timeout, TimeUnit unit) throws IOException {
+    public Buffer request(String request, Buffer buffer, long timeout, TimeUnit unit) throws IOException {
         ValidateUtils.checkTrue(timeout > 0L, "Non-positive timeout requested: %d", timeout);
 
         long maxWaitMillis = TimeUnit.MILLISECONDS.convert(timeout, unit);
         if (maxWaitMillis <= 0L) {
-            throw new IllegalArgumentException("Requested timeout below 1 msec: " + timeout + " " + unit);
+            throw new IllegalArgumentException("Requested timeout for " + request + " below 1 msec: " + timeout + " " + unit);
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("request({}) request={}, timeout={} {}", this, request, timeout, unit);
         }
 
         Object result;
@@ -1011,16 +1010,21 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
                     result = requestResult.getAndSet(null);
                 }
             } catch (InterruptedException e) {
-                throw (InterruptedIOException) new InterruptedIOException("Interrupted while waiting for request result").initCause(e);
+                throw (InterruptedIOException) new InterruptedIOException("Interrupted while waiting for request=" + request + " result").initCause(e);
             }
         }
 
         if (!isOpen()) {
-            throw new IOException("Session is closed or closing");
+            throw new IOException("Session is closed or closing while awaiting reply for request=" + request);
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("request({}) request={}, timeout={} {}, result received={}",
+                      this, request, timeout, unit, result != null);
         }
 
         if (result == null) {
-            throw new SocketTimeoutException("No response received after " + timeout + " " + unit);
+            throw new SocketTimeoutException("No response received after " + timeout + " " + unit + " for request=" + request);
         }
 
         if (result instanceof Buffer) {
@@ -1286,10 +1290,10 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
      * @param ident our identification to send
      */
     protected void sendIdentification(String ident) {
+        byte[] data = (ident + "\r\n").getBytes(StandardCharsets.UTF_8);
         if (log.isDebugEnabled()) {
             log.debug("sendIdentification({}): {}", this, ident);
         }
-        byte[] data = (ident + "\r\n").getBytes(StandardCharsets.UTF_8);
         ioSession.write(new ByteArrayBuffer(data));
     }
 
@@ -1318,8 +1322,15 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
      * @return the remote identification or {@code null} if more data is needed
      */
     protected String doReadIdentification(Buffer buffer, boolean server) {
-        byte[] data = new byte[256];
-        for (;;) {
+        int maxIdentSize = PropertyResolverUtils.getIntProperty(this,
+                FactoryManager.MAX_IDENTIFICATION_SIZE, FactoryManager.DEFAULT_MAX_IDENTIFICATION_SIZE);
+        /*
+         * see https://tools.ietf.org/html/rfc4253 section 4.2
+         *
+         *      The maximum length of the string is 255 characters,
+         *      including the Carriage Return and Line Feed.
+         */
+        for (byte[] data = new byte[256];;) {
             int rpos = buffer.rpos();
             int pos = 0;
             boolean needLf = false;
@@ -1345,12 +1356,13 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
                 }
                 data[pos++] = b;
             }
-            String str = new String(data, 0, pos);
+
+            String str = new String(data, 0, pos, StandardCharsets.UTF_8);
             if (server || str.startsWith("SSH-")) {
                 return str;
             }
-            if (buffer.rpos() > (16 * 1024)) {
-                throw new IllegalStateException("Incorrect identification: too many header lines");
+            if (buffer.rpos() > maxIdentSize) {
+                throw new IllegalStateException("Incorrect identification: too many header lines - size > " + maxIdentSize);
             }
         }
     }
@@ -1407,13 +1419,14 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
             random.fill(buffer.array(), p, SshConstants.MSG_KEX_COOKIE_SIZE);
         }
         if (log.isTraceEnabled()) {
-            log.trace("sendKexInit(" + toString() + ") cookie=" + BufferUtils.toHex(buffer.array(), p, SshConstants.MSG_KEX_COOKIE_SIZE, ':'));
+            log.trace("sendKexInit({}) cookie={}",
+                      this, BufferUtils.toHex(buffer.array(), p, SshConstants.MSG_KEX_COOKIE_SIZE, ':'));
         }
 
         for (KexProposalOption paramType : KexProposalOption.VALUES) {
             String s = proposal.get(paramType);
             if (log.isTraceEnabled()) {
-                log.trace("sendKexInit(" + toString() + ")[" + paramType.getDescription() + "] " + s);
+                log.trace("sendKexInit(}|)[{}] {}", this, paramType.getDescription(), s);
             }
             buffer.putString(GenericUtils.trimToEmpty(s));
         }
@@ -1446,7 +1459,8 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         buffer.rpos(cookieStartPos + SshConstants.MSG_KEX_COOKIE_SIZE);
         size += SshConstants.MSG_KEX_COOKIE_SIZE;
         if (log.isTraceEnabled()) {
-            log.trace("receiveKexInit(" + toString() + ") cookie=" + BufferUtils.toHex(d, cookieStartPos, SshConstants.MSG_KEX_COOKIE_SIZE, ':'));
+            log.trace("receiveKexInit({}) cookie={}",
+                      this, BufferUtils.toHex(d, cookieStartPos, SshConstants.MSG_KEX_COOKIE_SIZE, ':'));
         }
 
         // Read proposal
@@ -1454,7 +1468,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
             int lastPos = buffer.rpos();
             String value = buffer.getString();
             if (log.isTraceEnabled()) {
-                log.trace("receiveKexInit(" + toString() + ")[" + paramType.getDescription() + "] " + value);
+                log.trace("receiveKexInit({})[{}] {}", this, paramType.getDescription(), value);
             }
             int curPos = buffer.rpos();
             int readLen = curPos - lastPos;
@@ -1464,13 +1478,13 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
 
         firstKexPacketFollows = buffer.getBoolean();
         if (log.isTraceEnabled()) {
-            log.trace("receiveKexInit(" + toString() + ") first kex packet follows: " + firstKexPacketFollows);
+            log.trace("receiveKexInit({}) first kex packet follows: {}", this, firstKexPacketFollows);
         }
 
         long reserved = buffer.getUInt();
         if (reserved != 0) {
             if (log.isTraceEnabled()) {
-                log.trace("receiveKexInit(" + toString() + ") non-zero reserved value: " + reserved);
+                log.trace("receiveKexInit({}) non-zero reserved value: {}", this, reserved);
             }
         }
 
@@ -1483,14 +1497,16 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
     /**
      * Send a message to put new keys into use.
      *
+     * @return An {@link IoWriteFuture} that can be used to wait and
+     * check the result of sending the packet
      * @throws IOException if an error occurs sending the message
      */
-    protected void sendNewKeys() throws IOException {
+    protected IoWriteFuture sendNewKeys() throws IOException {
         if (log.isDebugEnabled()) {
             log.debug("sendNewKeys({}) Send SSH_MSG_NEWKEYS", this);
         }
         Buffer buffer = createBuffer(SshConstants.SSH_MSG_NEWKEYS, Byte.SIZE);
-        writePacket(buffer);
+        return writePacket(buffer);
     }
 
     /**
@@ -1517,11 +1533,12 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         buffer.putRawBytes(h);
         buffer.putByte((byte) 0x41);
         buffer.putRawBytes(sessionId);
+
         int pos = buffer.available();
         byte[] buf = buffer.array();
         hash.update(buf, 0, pos);
-        byte[] iv_c2s = hash.digest();
 
+        byte[] iv_c2s = hash.digest();
         int j = pos - sessionId.length - 1;
 
         buf[j]++;
@@ -1664,6 +1681,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         buffer.putInt(reason);
         buffer.putString(msg);
         buffer.putString("");   // TODO configure language...
+
         // Write the packet with a timeout to ensure a timely close of the session
         // in case the consumer does not read packets anymore.
         long disconnectTimeoutMs = PropertyResolverUtils.getLongProperty(this, FactoryManager.DISCONNECT_TIMEOUT, FactoryManager.DEFAULT_DISCONNECT_TIMEOUT);
@@ -1699,12 +1717,25 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
      * sequence id of the unsupported packet: this number is assumed to
      * be the last packet received.
      *
+     * @return An {@link IoWriteFuture} that can be used to wait for packet write completion
+     * @throws IOException if an error occurred sending the packet
+     * @see #sendNotImplemented(long)
+     */
+    protected IoWriteFuture notImplemented() throws IOException {
+        return sendNotImplemented(seqi - 1);
+    }
+
+    /**
+     * Sends a {@code SSH_MSG_UNIMPLEMENTED} message
+     *
+     * @param seqNoValue The referenced sequence number
+     * @return An {@link IoWriteFuture} that can be used to wait for packet write completion
      * @throws IOException if an error occurred sending the packet
      */
-    protected void notImplemented() throws IOException {
+    protected IoWriteFuture sendNotImplemented(long seqNoValue) throws IOException {
         Buffer buffer = createBuffer(SshConstants.SSH_MSG_UNIMPLEMENTED, Byte.SIZE);
-        buffer.putInt(seqi - 1);
-        writePacket(buffer);
+        buffer.putInt(seqNoValue);
+        return writePacket(buffer);
     }
 
     /**
@@ -1743,14 +1774,14 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
                 // OK if could not negotiate languages
                 if (KexProposalOption.S2CLANG.equals(paramType) || KexProposalOption.C2SLANG.equals(paramType)) {
                     if (log.isTraceEnabled()) {
-                        log.trace(message);
+                        log.trace("negotiate({}) {}", this, message);
                     }
                 } else {
                     throw new IllegalStateException(message);
                 }
             } else {
                 if (log.isTraceEnabled()) {
-                    log.trace("Kex: negotiate(" + paramType.getDescription() + ") guess=" + value
+                    log.trace("negotiate(" + this + ")[" + paramType.getDescription() + "] guess=" + value
                             + " (client: " + clientParamValue + " / server: " + serverParamValue + ")");
                 }
             }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index 43d0f7c..94f08b4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -182,13 +182,14 @@ public interface Session
      * Send a global request and wait for the response. This must only be used when sending
      * a {@code SSH_MSG_GLOBAL_REQUEST} with a result expected, else it will time out
      *
+     * @param request the request name - used mainly for logging and debugging
      * @param buffer the buffer containing the global request
      * @param timeout The number of time units to wait - must be <U>positive</U>
      * @param unit The {@link TimeUnit} to wait for the response
      * @return the return buffer if the request was successful, {@code null} otherwise.
      * @throws IOException if an error occurred when encoding sending the packet
      */
-    Buffer request(Buffer buffer, long timeout, TimeUnit unit) throws IOException;
+    Buffer request(String request, Buffer buffer, long timeout, TimeUnit unit) throws IOException;
 
     /**
      * Handle any exceptions that occurred on this session.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
index 68116ec..1687cd1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/gss/UserAuthGSS.java
@@ -174,7 +174,6 @@ public class UserAuthGSS extends AbstractUserAuth {
 
                 if (NumberUtils.length(out) > 0) {
                     Buffer b = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE, out.length + Integer.SIZE);
-
                     b.putBytes(out);
                     session.writePacket(b);
                     return null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
index 45a8f4f..61241e1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
@@ -54,6 +54,7 @@ import org.apache.sshd.common.file.FileSystemFactory;
 import org.apache.sshd.common.future.CloseFuture;
 import org.apache.sshd.common.future.DefaultCloseFuture;
 import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.ValidateUtils;
@@ -332,32 +333,31 @@ public class ChannelSession extends AbstractServerChannel {
     }
 
     @Override
-    protected void sendResponse(Buffer buffer, String req, Result result, boolean wantReply) throws IOException {
-        super.sendResponse(buffer, req, result, wantReply);
-
+    protected IoWriteFuture sendResponse(Buffer buffer, String req, Result result, boolean wantReply) throws IOException {
+        IoWriteFuture future = super.sendResponse(buffer, req, result, wantReply);
         if (!RequestHandler.Result.ReplySuccess.equals(result)) {
-            return;
+            return future;
         }
 
         if (commandInstance == null) {
             if (log.isDebugEnabled()) {
                 log.debug("sendResponse({}) request={} no pending command", this, req);
             }
-            return; // no pending command to activate
+            return future; // no pending command to activate
         }
 
         if (!Objects.equals(this.type, req)) {
             if (log.isDebugEnabled()) {
                 log.debug("sendResponse({}) request={} mismatched channel type: {}", this, req, this.type);
             }
-            return; // request does not match the current channel type
+            return future; // request does not match the current channel type
         }
 
         if (commandStarted.getAndSet(true)) {
             if (log.isDebugEnabled()) {
                 log.debug("sendResponse({}) request={} pending command already started", this, req);
             }
-            return;
+            return future;
         }
 
         // TODO - consider if (Channel.CHANNEL_SHELL.equals(req) || Channel.CHANNEL_EXEC.equals(req) || Channel.CHANNEL_SUBSYSTEM.equals(req)) {
@@ -365,6 +365,7 @@ public class ChannelSession extends AbstractServerChannel {
             log.debug("sendResponse({}) request={} activate command", this, req);
         }
         commandInstance.start(getEnvironment());
+        return future;
     }
 
     protected RequestHandler.Result handleEnv(Buffer buffer, boolean wantReply) throws IOException {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/751ea6a7/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
index a3de28f..09de71d 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/auth/AuthenticationTest.java
@@ -49,6 +49,7 @@ import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.config.keys.KeyUtils;
 import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.io.IoWriteFuture;
 import org.apache.sshd.common.keyprovider.KeyPairProvider;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionListener;
@@ -226,14 +227,14 @@ public class AuthenticationTest extends BaseTestSupport {
                     public org.apache.sshd.client.auth.password.UserAuthPassword create() {
                         return new org.apache.sshd.client.auth.password.UserAuthPassword() {
                             @Override
-                            protected void sendPassword(Buffer buffer, ClientSession session, String oldPassword, String newPassword) throws IOException {
+                            protected IoWriteFuture sendPassword(Buffer buffer, ClientSession session, String oldPassword, String newPassword) throws IOException {
                                 int count = sentCount.incrementAndGet();
                                 // 1st one is the original one (which is denied by the server)
                                 // 2nd one is the updated one retrieved from the user interaction
                                 if (count == 2) {
-                                    super.sendPassword(buffer, session, getClass().getName(), newPassword);
+                                    return super.sendPassword(buffer, session, getClass().getName(), newPassword);
                                 } else {
-                                    super.sendPassword(buffer, session, oldPassword, newPassword);
+                                    return super.sendPassword(buffer, session, oldPassword, newPassword);
                                 }
                             }
                         };