瀏覽代碼

Fix how exceptions are thrown
Fix exception being thrown for asynchronous multiple SFTP file download
Add additional helper BeginDownloadFile and BeginUploadFile methods

olegkap_cp 12 年之前
父節點
當前提交
d31b4c76e1

+ 5 - 1
Renci.SshClient/Renci.SshNet/Session.cs

@@ -637,7 +637,11 @@ namespace Renci.SshNet
             switch (EventWaitHandle.WaitAny(waitHandles, this.ConnectionInfo.Timeout))
             {
                 case 0:
-                    throw this._exception;
+                    {
+                        var exception = this._exception;
+                        this._exception = null;
+                        throw exception;
+                    }
                 case System.Threading.WaitHandle.WaitTimeout:
                     this.SendDisconnect(DisconnectReason.ByApplication, "Operation timeout");
                     throw new SshOperationTimeoutException("Session operation has timed out");

+ 212 - 166
Renci.SshClient/Renci.SshNet/Sftp/SftpSession.cs

@@ -272,6 +272,7 @@ namespace Renci.SshNet.Sftp
         internal byte[] RequestOpen(string path, Flags flags, bool nullOnError = false)
         {
             byte[] handle = null;
+            SshException exception = null;
 
             using (var wait = new AutoResetEvent(false))
             {
@@ -283,14 +284,8 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        if (nullOnError)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
@@ -298,6 +293,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (!nullOnError && exception != null)
+            {
+                throw exception;
+            }
+
             return handle;
         }
 
@@ -307,25 +307,26 @@ namespace Renci.SshNet.Sftp
         /// <param name="handle">The handle.</param>
         internal void RequestClose(byte[] handle)
         {
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpCloseRequest(this.ProtocolVersion, this.NextRequestId, handle,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         /// <summary>
@@ -337,6 +338,8 @@ namespace Renci.SshNet.Sftp
         /// <returns>data array; null if EOF</returns>
         internal byte[] RequestRead(byte[] handle, UInt64 offset, UInt32 length)
         {
+            SshException exception = null;
+
             byte[] data = new byte[0];
 
             using (var wait = new AutoResetEvent(false))
@@ -349,14 +352,11 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Eof)
-                        {
-                            wait.Set();
-                        }
-                        else
+                        if (response.StatusCode != StatusCodes.Eof)
                         {
-                            this.ThrowSftpException(response);
+                            exception = this.GetSftpException(response);
                         }
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
@@ -364,6 +364,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (exception != null)
+            {
+                throw exception;
+            }
+
             return data;
         }
 
@@ -376,6 +381,8 @@ namespace Renci.SshNet.Sftp
         /// <param name="wait">The wait event handle if needed.</param>
         internal void RequestWrite(byte[] handle, UInt64 offset, byte[] data, EventWaitHandle wait, Action<SftpStatusResponse> writeCompleted = null)
         {
+            SshException exception = null;
+
             var request = new SftpWriteRequest(this.ProtocolVersion, this.NextRequestId, handle, offset, data,
                 (response) =>
                 {
@@ -384,21 +391,20 @@ namespace Renci.SshNet.Sftp
                         writeCompleted(response);
                     }
 
-                    if (response.StatusCode == StatusCodes.Ok)
-                    {
-                        if (wait != null)
-                            wait.Set();
-                    }
-                    else
-                    {
-                        this.ThrowSftpException(response);
-                    }
+                    exception = this.GetSftpException(response);
+                    if (wait != null)
+                        wait.Set();
                 });
 
             this.SendRequest(request);
 
             if (wait != null)
                 this.WaitHandle(wait, this._operationTimeout);
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         /// <summary>
@@ -411,6 +417,8 @@ namespace Renci.SshNet.Sftp
         /// </returns>
         internal SftpFileAttributes RequestLStat(string path, bool nullOnError = false)
         {
+            SshException exception = null;
+
             SftpFileAttributes attributes = null;
             using (var wait = new AutoResetEvent(false))
             {
@@ -422,7 +430,8 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        this.ThrowSftpException(response);
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
@@ -430,6 +439,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (exception != null)
+            {
+                throw exception;
+            }
+
             return attributes;
         }
 
@@ -443,7 +457,9 @@ namespace Renci.SshNet.Sftp
         /// </returns>
         internal SftpFileAttributes RequestFStat(byte[] handle, bool nullOnError = false)
         {
+            SshException exception = null;
             SftpFileAttributes attributes = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpFStatRequest(this.ProtocolVersion, this.NextRequestId, handle,
@@ -454,7 +470,8 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        this.ThrowSftpException(response);
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
@@ -462,6 +479,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (exception != null)
+            {
+                throw exception;
+            }
+
             return attributes;
         }
 
@@ -472,25 +494,26 @@ namespace Renci.SshNet.Sftp
         /// <param name="attributes">The attributes.</param>
         internal void RequestSetStat(string path, SftpFileAttributes attributes)
         {
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpSetStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding, attributes,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         /// <summary>
@@ -500,25 +523,26 @@ namespace Renci.SshNet.Sftp
         /// <param name="attributes">The attributes.</param>
         internal void RequestFSetStat(byte[] handle, SftpFileAttributes attributes)
         {
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpFSetStatRequest(this.ProtocolVersion, this.NextRequestId, handle, attributes,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         /// <summary>
@@ -529,6 +553,8 @@ namespace Renci.SshNet.Sftp
         /// <returns>File handle.</returns>
         internal byte[] RequestOpenDir(string path, bool nullOnError = false)
         {
+            SshException exception = null;
+
             byte[] handle = null;
 
             using (var wait = new AutoResetEvent(false))
@@ -541,14 +567,8 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        if (nullOnError)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
@@ -556,6 +576,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (!nullOnError && exception != null)
+            {
+                throw exception;
+            }
+
             return handle;
         }
 
@@ -566,6 +591,8 @@ namespace Renci.SshNet.Sftp
         /// <returns></returns>
         internal KeyValuePair<string, SftpFileAttributes>[] RequestReadDir(byte[] handle)
         {
+            SshException exception = null;
+
             KeyValuePair<string, SftpFileAttributes>[] result = null;
 
             using (var wait = new AutoResetEvent(false))
@@ -578,14 +605,11 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Eof)
-                        {
-                            wait.Set();
-                        }
-                        else
+                        if (response.StatusCode != StatusCodes.Eof)
                         {
-                            this.ThrowSftpException(response);
+                            exception = this.GetSftpException(response);
                         }
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
@@ -593,6 +617,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (exception != null)
+            {
+                throw exception;
+            }
+
             return result;
         }
 
@@ -602,25 +631,26 @@ namespace Renci.SshNet.Sftp
         /// <param name="path">The path.</param>
         internal void RequestRemove(string path)
         {
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpRemoveRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         /// <summary>
@@ -629,25 +659,26 @@ namespace Renci.SshNet.Sftp
         /// <param name="path">The path.</param>
         internal void RequestMkDir(string path)
         {
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpMkDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         /// <summary>
@@ -656,25 +687,26 @@ namespace Renci.SshNet.Sftp
         /// <param name="path">The path.</param>
         internal void RequestRmDir(string path)
         {
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpRmDirRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         /// <summary>
@@ -685,6 +717,8 @@ namespace Renci.SshNet.Sftp
         /// <returns></returns>
         internal KeyValuePair<string, SftpFileAttributes>[] RequestRealPath(string path, bool nullOnError = false)
         {
+            SshException exception = null;
+
             KeyValuePair<string, SftpFileAttributes>[] result = null;
 
             using (var wait = new AutoResetEvent(false))
@@ -693,19 +727,12 @@ namespace Renci.SshNet.Sftp
                     (response) =>
                     {
                         result = response.Files;
-
                         wait.Set();
                     },
                     (response) =>
                     {
-                        if (nullOnError)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
@@ -713,6 +740,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (!nullOnError && exception != null)
+            {
+                throw exception;
+            }
+            
             return result;
         }
 
@@ -726,7 +758,10 @@ namespace Renci.SshNet.Sftp
         /// </returns>
         internal SftpFileAttributes RequestStat(string path, bool nullOnError = false)
         {
+            SshException exception = null;
+
             SftpFileAttributes attributes = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpStatRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,
@@ -737,14 +772,8 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        if (nullOnError)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
@@ -752,6 +781,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (!nullOnError && exception != null)
+            {
+                throw exception;
+            }
+
             return attributes;
         }
 
@@ -766,25 +800,27 @@ namespace Renci.SshNet.Sftp
             {
                 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_RENAME operation is not supported in {0} version that server operates in.", this.ProtocolVersion));
             }
+
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         /// <summary>
@@ -800,6 +836,8 @@ namespace Renci.SshNet.Sftp
                 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_READLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion));
             }
 
+            SshException exception = null;
+
             KeyValuePair<string, SftpFileAttributes>[] result = null;
 
             using (var wait = new AutoResetEvent(false))
@@ -813,14 +851,8 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        if (nullOnError)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
@@ -828,6 +860,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (!nullOnError && exception != null)
+            {
+                throw exception;
+            }
+
             return result;
         }
 
@@ -843,25 +880,26 @@ namespace Renci.SshNet.Sftp
                 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_SYMLINK operation is not supported in {0} version that server operates in.", this.ProtocolVersion));
             }
 
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new SftpSymLinkRequest(this.ProtocolVersion, this.NextRequestId, linkpath, targetpath, this.Encoding,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            this.ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 this.SendRequest(request);
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         #endregion
@@ -880,19 +918,15 @@ namespace Renci.SshNet.Sftp
                 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion));
             }
 
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new PosixRenameRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath, this.Encoding,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 if (!this._supportedExtensions.ContainsKey(request.Name))
@@ -902,6 +936,11 @@ namespace Renci.SshNet.Sftp
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         /// <summary>
@@ -917,7 +956,10 @@ namespace Renci.SshNet.Sftp
                 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion));
             }
 
+            SshException exception = null;
+
             SftpFileSytemInformation information = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new StatVfsRequest(this.ProtocolVersion, this.NextRequestId, path, this.Encoding,
@@ -929,15 +971,8 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        if (nullOnError)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            ThrowSftpException(response);
-                        }
-
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 if (!this._supportedExtensions.ContainsKey(request.Name))
@@ -948,6 +983,11 @@ namespace Renci.SshNet.Sftp
                 this.WaitHandle(wait, this._operationTimeout);
             }
 
+            if (!nullOnError && exception != null)
+            {
+                throw exception;
+            }
+
             return information;
         }
 
@@ -965,7 +1005,10 @@ namespace Renci.SshNet.Sftp
                 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion));
             }
 
+            SshException exception = null;
+
             SftpFileSytemInformation information = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new FStatVfsRequest(this.ProtocolVersion, this.NextRequestId, handle,
@@ -977,15 +1020,8 @@ namespace Renci.SshNet.Sftp
                     },
                     (response) =>
                     {
-                        if (nullOnError)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            ThrowSftpException(response);
-                        }
-
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 if (!this._supportedExtensions.ContainsKey(request.Name))
@@ -995,6 +1031,11 @@ namespace Renci.SshNet.Sftp
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+            
+            if (!nullOnError && exception != null)
+            {
+                throw exception;
+            }
 
             return information;
         }
@@ -1011,19 +1052,15 @@ namespace Renci.SshNet.Sftp
                 throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "SSH_FXP_EXTENDED operation is not supported in {0} version that server operates in.", this.ProtocolVersion));
             }
 
+            SshException exception = null;
+
             using (var wait = new AutoResetEvent(false))
             {
                 var request = new HardLinkRequest(this.ProtocolVersion, this.NextRequestId, oldPath, newPath,
                     (response) =>
                     {
-                        if (response.StatusCode == StatusCodes.Ok)
-                        {
-                            wait.Set();
-                        }
-                        else
-                        {
-                            ThrowSftpException(response);
-                        }
+                        exception = this.GetSftpException(response);
+                        wait.Set();
                     });
 
                 if (!this._supportedExtensions.ContainsKey(request.Name))
@@ -1033,23 +1070,32 @@ namespace Renci.SshNet.Sftp
 
                 this.WaitHandle(wait, this._operationTimeout);
             }
+
+            if (exception != null)
+            {
+                throw exception;
+            }
         }
 
         #endregion
 
-        private void ThrowSftpException(SftpStatusResponse response)
+        private SshException GetSftpException(SftpStatusResponse response)
         {
+            if (response.StatusCode == StatusCodes.Ok)
+            {
+                return null;
+            }
             if (response.StatusCode == StatusCodes.PermissionDenied)
             {
-                throw new SftpPermissionDeniedException(response.ErrorMessage);
+                return new SftpPermissionDeniedException(response.ErrorMessage);
             }
             else if (response.StatusCode == StatusCodes.NoSuchFile)
             {
-                throw new SftpPathNotFoundException(response.ErrorMessage);
+                return new SftpPathNotFoundException(response.ErrorMessage);
             }
             else
             {
-                throw new SshException(response.ErrorMessage);
+                return new SshException(response.ErrorMessage);
             }
         }
 

+ 90 - 0
Renci.SshClient/Renci.SshNet/SftpClient.cs

@@ -437,6 +437,53 @@ namespace Renci.SshNet
             this.InternalDownloadFile(path, output, null, downloadCallback);
         }
 
+        /// <summary>
+        /// Begins an asynchronous file downloading into the stream.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        /// <param name="output">The output.</param>
+        /// <returns>
+        /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
+        /// </returns>
+        /// <exception cref="System.ArgumentException">path</exception>
+        /// <exception cref="System.ArgumentNullException">output</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="output" /> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentException"><paramref name="path" /> is <b>null</b> or contains whitespace characters.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to perform the operation was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
+        /// <exception cref="T:Renci.SshNet.Common.SshException">A SSH error where <see cref="P:System.Exception.Message" /> is the message from the remote host.</exception>
+        /// <remarks>
+        /// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
+        /// </remarks>
+        public IAsyncResult BeginDownloadFile(string path, Stream output)
+        {
+            return this.BeginDownloadFile(path, output, null, null, null);
+        }
+
+        /// <summary>
+        /// Begins an asynchronous file downloading into the stream.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        /// <param name="output">The output.</param>
+        /// <param name="asyncCallback">The method to be called when the asynchronous write operation is completed.</param>
+        /// <returns>
+        /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
+        /// </returns>
+        /// <exception cref="System.ArgumentException">path</exception>
+        /// <exception cref="System.ArgumentNullException">output</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="output" /> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentException"><paramref name="path" /> is <b>null</b> or contains whitespace characters.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to perform the operation was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
+        /// <exception cref="T:Renci.SshNet.Common.SshException">A SSH error where <see cref="P:System.Exception.Message" /> is the message from the remote host.</exception>
+        /// <remarks>
+        /// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
+        /// </remarks>
+        public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback)
+        {
+            return this.BeginDownloadFile(path, output, asyncCallback, null, null);
+        }
+
         /// <summary>
         /// Begins an asynchronous file downloading into the stream.
         /// </summary>
@@ -555,6 +602,49 @@ namespace Renci.SshNet
             this.InternalUploadFile(input, path, flags, null, uploadCallback);
         }
 
+        /// <summary>
+        /// Begins an asynchronous uploading the steam into remote file.
+        /// </summary>
+        /// <param name="input">Data input stream.</param>
+        /// <param name="path">Remote file path.</param>
+        /// <returns>
+        /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
+        /// </returns>
+        /// <exception cref="ArgumentNullException"><paramref name="input" /> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentException"><paramref name="path" /> is <b>null</b> or contains whitespace characters.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
+        /// <exception cref="T:Renci.SshNet.Common.SshException">A SSH error where <see cref="P:System.Exception.Message" /> is the message from the remote host.</exception>
+        /// <remarks>
+        /// Method calls made by this method to <paramref name="input" />, may under certain conditions result in exceptions thrown by the stream.
+        /// </remarks>
+        public IAsyncResult BeginUploadFile(Stream input, string path)
+        {
+            return this.BeginUploadFile(input, path, true, null, null, null);
+        }
+
+        /// <summary>
+        /// Begins an asynchronous uploading the steam into remote file.
+        /// </summary>
+        /// <param name="input">Data input stream.</param>
+        /// <param name="path">Remote file path.</param>
+        /// <param name="asyncCallback">The method to be called when the asynchronous write operation is completed.</param>
+        /// <returns>
+        /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
+        /// </returns>
+        /// <exception cref="ArgumentNullException"><paramref name="input" /> is <b>null</b>.</exception>
+        /// <exception cref="ArgumentException"><paramref name="path" /> is <b>null</b> or contains whitespace characters.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
+        /// <exception cref="T:Renci.SshNet.Common.SshException">A SSH error where <see cref="P:System.Exception.Message" /> is the message from the remote host.</exception>
+        /// <remarks>
+        /// Method calls made by this method to <paramref name="input" />, may under certain conditions result in exceptions thrown by the stream.
+        /// </remarks>
+        public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback)
+        {
+            return this.BeginUploadFile(input, path, true, asyncCallback, null, null);
+        }
+
         /// <summary>
         /// Begins an asynchronous uploading the steam into remote file.
         /// </summary>

+ 5 - 1
Renci.SshClient/Renci.SshNet/SshCommand.cs

@@ -484,7 +484,11 @@ namespace Renci.SshNet
             switch (EventWaitHandle.WaitAny(waitHandles, this.CommandTimeout))
             {
                 case 0:
-                    throw this._exception;
+                    {
+                        var exception = this._exception;
+                        this._exception = null;
+                        throw exception;
+                    }
                 case System.Threading.WaitHandle.WaitTimeout:
                     throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Command '{0}' has timed out.", this.CommandText));
                 default:

+ 5 - 1
Renci.SshClient/Renci.SshNet/SubsystemSession.cs

@@ -165,7 +165,11 @@ namespace Renci.SshNet.Sftp
             switch (EventWaitHandle.WaitAny(waitHandles, operationTimeout))
             {
                 case 0:
-                    throw this._exception;
+                    {
+                        var exception = this._exception;
+                        this._exception = null;
+                        throw exception;
+                    }
                 case 1:
                     throw new SshException("Channel was closed.");
                 case System.Threading.WaitHandle.WaitTimeout: