Ver Fonte

Added CreateDirectoryAsync to SftpClient (#1505)

* Added CreateDirectoryAsync to SftpClient

* Use CreateDirectoryAsync for async test

---------

Co-authored-by: Rob Hague <rob.hague00@gmail.com>
Ryan Esteves há 1 ano atrás
pai
commit
ce867d6640

+ 13 - 0
src/Renci.SshNet/ISftpClient.cs

@@ -429,6 +429,19 @@ namespace Renci.SshNet
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
         void CreateDirectory(string path);
 
+        /// <summary>
+        /// Asynchronously requests to create a remote directory specified by path.
+        /// </summary>
+        /// <param name="path">Directory path to create.</param>
+        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
+        /// <returns>A <see cref="Task"/> that represents the asynchronous create directory operation.</returns>
+        /// <exception cref="ArgumentException"><paramref name="path"/> is <see langword="null"/> or contains only whitespace characters.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="SftpPermissionDeniedException">Permission to create the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
+        /// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        Task CreateDirectoryAsync(string path, CancellationToken cancellationToken = default);
+
         /// <summary>
         /// Creates or opens a file for writing UTF-8 encoded text.
         /// </summary>

+ 8 - 0
src/Renci.SshNet/Sftp/ISftpSession.cs

@@ -162,6 +162,14 @@ namespace Renci.SshNet.Sftp
         /// <param name="path">The path.</param>
         void RequestMkDir(string path);
 
+        /// <summary>
+        /// Asynchronously performs SSH_FXP_MKDIR request.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
+        /// <returns>A <see cref="Task"/> that represents the asynchronous <c>SSH_FXP_MKDIR</c> operation.</returns>
+        Task RequestMkDirAsync(string path, CancellationToken cancellationToken = default);
+
         /// <summary>
         /// Performs a <c>SSH_FXP_OPEN</c> request.
         /// </summary>

+ 38 - 0
src/Renci.SshNet/Sftp/SftpSession.cs

@@ -1544,6 +1544,44 @@ namespace Renci.SshNet.Sftp
             }
         }
 
+        /// <summary>
+        /// Asynchronously performs SSH_FXP_MKDIR request.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
+        /// <returns>A <see cref="Task"/> that represents the asynchronous <c>SSH_FXP_MKDIR</c> operation.</returns>
+        public async Task RequestMkDirAsync(string path, CancellationToken cancellationToken = default)
+        {
+            cancellationToken.ThrowIfCancellationRequested();
+
+            var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
+
+#if NET || NETSTANDARD2_1_OR_GREATER
+            await using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
+#else
+            using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
+#endif // NET || NETSTANDARD2_1_OR_GREATER
+            {
+                SendRequest(new SftpMkDirRequest(ProtocolVersion,
+                                                 NextRequestId,
+                                                 path,
+                                                 _encoding,
+                                                 response =>
+                                                     {
+                                                         if (response.StatusCode == StatusCodes.Ok)
+                                                         {
+                                                             _ = tcs.TrySetResult(true);
+                                                         }
+                                                         else
+                                                         {
+                                                             tcs.TrySetException(GetSftpException(response));
+                                                         }
+                                                     }));
+
+                _ = await tcs.Task.ConfigureAwait(false);
+            }
+        }
+
         /// <summary>
         /// Performs SSH_FXP_RMDIR request.
         /// </summary>

+ 26 - 0
src/Renci.SshNet/SftpClient.cs

@@ -373,6 +373,32 @@ namespace Renci.SshNet
             _sftpSession.RequestMkDir(fullPath);
         }
 
+        /// <summary>
+        /// Asynchronously requests to create a remote directory specified by path.
+        /// </summary>
+        /// <param name="path">Directory path to create.</param>
+        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
+        /// <returns>A <see cref="Task"/> that represents the asynchronous create directory operation.</returns>
+        /// <exception cref="ArgumentException"><paramref name="path"/> is <see langword="null"/> or contains only whitespace characters.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="SftpPermissionDeniedException">Permission to create the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
+        /// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
+        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
+        public async Task CreateDirectoryAsync(string path, CancellationToken cancellationToken = default)
+        {
+            CheckDisposed();
+            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
+
+            if (_sftpSession is null)
+            {
+                throw new SshConnectionException("Client not connected.");
+            }
+
+            var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
+
+            await _sftpSession.RequestMkDirAsync(fullPath, cancellationToken).ConfigureAwait(false);
+        }
+
         /// <summary>
         /// Deletes remote directory specified by path.
         /// </summary>

+ 4 - 4
test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs

@@ -239,15 +239,15 @@ namespace Renci.SshNet.IntegrationTests.OldIntegrationTests
 
                 Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet");
 
-                sftp.CreateDirectory("test1");
+                await sftp.CreateDirectoryAsync("test1", CancellationToken.None).ConfigureAwait(false);
 
                 await sftp.ChangeDirectoryAsync("test1", CancellationToken.None).ConfigureAwait(false);
 
                 Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1");
 
-                sftp.CreateDirectory("test1_1");
-                sftp.CreateDirectory("test1_2");
-                sftp.CreateDirectory("test1_3");
+                await sftp.CreateDirectoryAsync("test1_1", CancellationToken.None).ConfigureAwait(false);
+                await sftp.CreateDirectoryAsync("test1_2", CancellationToken.None).ConfigureAwait(false);
+                await sftp.CreateDirectoryAsync("test1_3", CancellationToken.None).ConfigureAwait(false);
 
                 var files = sftp.ListDirectory(".");
 

+ 1 - 1
test/Renci.SshNet.IntegrationTests/SftpClientTests.cs

@@ -60,7 +60,7 @@ namespace Renci.SshNet.IntegrationTests
             var testContent = "file content";
 
             // Create new directory and check if it exists
-            _sftpClient.CreateDirectory(testDirectory);
+            await _sftpClient.CreateDirectoryAsync(testDirectory, CancellationToken.None).ConfigureAwait(false);
             Assert.IsTrue(await _sftpClient.ExistsAsync(testDirectory));
 
             // Upload file and check if it exists