From 5b5ab90299a0f4ba21aacf4db83dff508390e72d Mon Sep 17 00:00:00 2001 From: Artem Azaraev Date: Thu, 23 Apr 2020 01:01:10 +0300 Subject: [PATCH] Fix #153 support of Task-based Asynchronous Pattern (TAP) over existing async API for SftpClient. (Not supported by .net 3.5) --- src/Renci.SshNet/Renci.SshNet.csproj | 2 +- src/Renci.SshNet/SftpClientExtensions.cs | 181 +++++++++++++++++++++++ 2 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 src/Renci.SshNet/SftpClientExtensions.cs diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 2af20911c..8d1ccc18e 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -33,7 +33,7 @@ - FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA + NET35;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_POLL;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA FEATURE_STRINGBUILDER_CLEAR;FEATURE_HASHALGORITHM_DISPOSE;FEATURE_REGEX_COMPILE;FEATURE_BINARY_SERIALIZATION;FEATURE_RNG_CREATE;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_SOCKET_DISPOSE;FEATURE_STREAM_APM;FEATURE_DNS_SYNC;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_WAITHANDLE_DISPOSE;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512;FEATURE_HMAC_RIPEMD160;FEATURE_MEMORYSTREAM_GETBUFFER;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_ENCODING_ASCII;FEATURE_ECDSA diff --git a/src/Renci.SshNet/SftpClientExtensions.cs b/src/Renci.SshNet/SftpClientExtensions.cs new file mode 100644 index 000000000..fb98a7d7d --- /dev/null +++ b/src/Renci.SshNet/SftpClientExtensions.cs @@ -0,0 +1,181 @@ +#if !NET35 +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Renci.SshNet.Sftp; +using Renci.SshNet.Common; + +namespace Renci.SshNet +{ + /// + /// The SftpClient extensions to implement the Task-based Asynchronous Pattern (TAP) over the originally implemented Asynchronous Programming Model (APM). + /// + public static class SftpClientExtensions + { + /// + /// Asynchronously downloads a remote file specified by the path into the stream. + /// + /// + /// File to download. + /// Stream to write the file into. + /// The download callback. + /// is null. + /// is null or contains only whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// was not found on the remote host./// + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public static Task DownloadFileAsync(this SftpClient sftpClient, string path, Stream output, Action downloadCallback = null) + { + if (sftpClient == null) + throw new ArgumentNullException("sftpClient"); + + var tcs = new TaskCompletionSource(); + sftpClient.BeginDownloadFile(path, output, iar => + { + try + { + sftpClient.EndDownloadFile(iar); + tcs.TrySetResult(true); + } + catch (OperationCanceledException) + { + tcs.TrySetCanceled(); + } + catch (Exception exc) + { + tcs.TrySetException(exc); + } + }, null, downloadCallback); + return tcs.Task; + } + + /// + /// Asynchronously retrieves list of files in remote directory. + /// + /// + /// The path. + /// The list callback. + /// + /// A list of files. + /// + /// The method was called after the client was disposed. + public static Task> ListDirectoryAsync(this SftpClient sftpClient, string path, Action listCallback = null) + { + if (sftpClient == null) + throw new ArgumentNullException("sftpClient"); + + var tcs = new TaskCompletionSource>(); + sftpClient.BeginListDirectory(path, iar => + { + try + { + tcs.TrySetResult(sftpClient.EndListDirectory(iar)); + } + catch (OperationCanceledException) + { + tcs.TrySetCanceled(); + } + catch (Exception exc) + { + tcs.TrySetException(exc); + } + }, null, listCallback); + return tcs.Task; + } + + + /// + /// Asynchronously synchronizes the directories. + /// + /// + /// The source path. + /// The destination path. + /// The search pattern. + /// + /// A list of uploaded files. + /// + /// is null. + /// is null or contains only whitespace. + /// was not found on the remote host. + public static Task> SynchronizeDirectoriesAsync(this SftpClient sftpClient + , string sourcePath, string destinationPath, string searchPattern) + { + if (sftpClient == null) + throw new ArgumentNullException("sftpClient"); + + return Task>.Factory.FromAsync(sftpClient.BeginSynchronizeDirectories, + sftpClient.EndSynchronizeDirectories, sourcePath, destinationPath, searchPattern, null); + } + + /// + /// Asynchronously uploads stream into remote file. If the file exists it will be overwritten. + /// + /// + /// Data input stream. + /// Remote file path. + /// The upload callback. + /// is null. + /// is null or contains only whitespace characters. + /// Client is not connected. + /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public static Task UploadFileAsync(this SftpClient sftpClient, Stream input, string path, Action uploadCallback = null) + { + return UploadFileAsync(sftpClient, input, path, true, uploadCallback); + } + + /// + /// Asynchronously uploads stream into remote file. + /// + /// + /// Data input stream. + /// Remote file path. + /// if set to true then existing file will be overwritten. + /// The upload callback. + /// is null. + /// is null or contains only whitespace characters. + /// Client is not connected. + /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// The method was called after the client was disposed. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public static Task UploadFileAsync(this SftpClient sftpClient, Stream input, string path, bool canOverride, Action uploadCallback = null) + { + if (sftpClient == null) + throw new ArgumentNullException("sftpClient"); + + var tcs = new TaskCompletionSource(); + sftpClient.BeginUploadFile(input, path, canOverride, iar => + { + try + { + sftpClient.EndUploadFile(iar); + tcs.TrySetResult(true); + } + catch (OperationCanceledException) + { + tcs.TrySetCanceled(); + } + catch (Exception exc) + { + tcs.TrySetException(exc); + } + }, null, uploadCallback); + return tcs.Task; + } + + } +} +#endif \ No newline at end of file