Skip to content

Commit ce867d6

Browse files
Added CreateDirectoryAsync to SftpClient (#1505)
* Added CreateDirectoryAsync to SftpClient * Use CreateDirectoryAsync for async test --------- Co-authored-by: Rob Hague <[email protected]>
1 parent 737c3e5 commit ce867d6

File tree

6 files changed

+90
-5
lines changed

6 files changed

+90
-5
lines changed

src/Renci.SshNet/ISftpClient.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,19 @@ public interface ISftpClient : IBaseClient
429429
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
430430
void CreateDirectory(string path);
431431

432+
/// <summary>
433+
/// Asynchronously requests to create a remote directory specified by path.
434+
/// </summary>
435+
/// <param name="path">Directory path to create.</param>
436+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
437+
/// <returns>A <see cref="Task"/> that represents the asynchronous create directory operation.</returns>
438+
/// <exception cref="ArgumentException"><paramref name="path"/> is <see langword="null"/> or contains only whitespace characters.</exception>
439+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
440+
/// <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>
441+
/// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
442+
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
443+
Task CreateDirectoryAsync(string path, CancellationToken cancellationToken = default);
444+
432445
/// <summary>
433446
/// Creates or opens a file for writing UTF-8 encoded text.
434447
/// </summary>

src/Renci.SshNet/Sftp/ISftpSession.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ internal interface ISftpSession : ISubsystemSession
162162
/// <param name="path">The path.</param>
163163
void RequestMkDir(string path);
164164

165+
/// <summary>
166+
/// Asynchronously performs SSH_FXP_MKDIR request.
167+
/// </summary>
168+
/// <param name="path">The path.</param>
169+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
170+
/// <returns>A <see cref="Task"/> that represents the asynchronous <c>SSH_FXP_MKDIR</c> operation.</returns>
171+
Task RequestMkDirAsync(string path, CancellationToken cancellationToken = default);
172+
165173
/// <summary>
166174
/// Performs a <c>SSH_FXP_OPEN</c> request.
167175
/// </summary>

src/Renci.SshNet/Sftp/SftpSession.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,44 @@ public void RequestMkDir(string path)
15441544
}
15451545
}
15461546

1547+
/// <summary>
1548+
/// Asynchronously performs SSH_FXP_MKDIR request.
1549+
/// </summary>
1550+
/// <param name="path">The path.</param>
1551+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
1552+
/// <returns>A <see cref="Task"/> that represents the asynchronous <c>SSH_FXP_MKDIR</c> operation.</returns>
1553+
public async Task RequestMkDirAsync(string path, CancellationToken cancellationToken = default)
1554+
{
1555+
cancellationToken.ThrowIfCancellationRequested();
1556+
1557+
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
1558+
1559+
#if NET || NETSTANDARD2_1_OR_GREATER
1560+
await using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false).ConfigureAwait(continueOnCapturedContext: false))
1561+
#else
1562+
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetCanceled(cancellationToken), tcs, useSynchronizationContext: false))
1563+
#endif // NET || NETSTANDARD2_1_OR_GREATER
1564+
{
1565+
SendRequest(new SftpMkDirRequest(ProtocolVersion,
1566+
NextRequestId,
1567+
path,
1568+
_encoding,
1569+
response =>
1570+
{
1571+
if (response.StatusCode == StatusCodes.Ok)
1572+
{
1573+
_ = tcs.TrySetResult(true);
1574+
}
1575+
else
1576+
{
1577+
tcs.TrySetException(GetSftpException(response));
1578+
}
1579+
}));
1580+
1581+
_ = await tcs.Task.ConfigureAwait(false);
1582+
}
1583+
}
1584+
15471585
/// <summary>
15481586
/// Performs SSH_FXP_RMDIR request.
15491587
/// </summary>

src/Renci.SshNet/SftpClient.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,32 @@ public void CreateDirectory(string path)
373373
_sftpSession.RequestMkDir(fullPath);
374374
}
375375

376+
/// <summary>
377+
/// Asynchronously requests to create a remote directory specified by path.
378+
/// </summary>
379+
/// <param name="path">Directory path to create.</param>
380+
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
381+
/// <returns>A <see cref="Task"/> that represents the asynchronous create directory operation.</returns>
382+
/// <exception cref="ArgumentException"><paramref name="path"/> is <see langword="null"/> or contains only whitespace characters.</exception>
383+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
384+
/// <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>
385+
/// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
386+
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
387+
public async Task CreateDirectoryAsync(string path, CancellationToken cancellationToken = default)
388+
{
389+
CheckDisposed();
390+
ThrowHelper.ThrowIfNullOrWhiteSpace(path);
391+
392+
if (_sftpSession is null)
393+
{
394+
throw new SshConnectionException("Client not connected.");
395+
}
396+
397+
var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
398+
399+
await _sftpSession.RequestMkDirAsync(fullPath, cancellationToken).ConfigureAwait(false);
400+
}
401+
376402
/// <summary>
377403
/// Deletes remote directory specified by path.
378404
/// </summary>

test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.ListDirectory.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,15 +239,15 @@ public async Task Test_Sftp_Change_DirectoryAsync()
239239

240240
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet");
241241

242-
sftp.CreateDirectory("test1");
242+
await sftp.CreateDirectoryAsync("test1", CancellationToken.None).ConfigureAwait(false);
243243

244244
await sftp.ChangeDirectoryAsync("test1", CancellationToken.None).ConfigureAwait(false);
245245

246246
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1");
247247

248-
sftp.CreateDirectory("test1_1");
249-
sftp.CreateDirectory("test1_2");
250-
sftp.CreateDirectory("test1_3");
248+
await sftp.CreateDirectoryAsync("test1_1", CancellationToken.None).ConfigureAwait(false);
249+
await sftp.CreateDirectoryAsync("test1_2", CancellationToken.None).ConfigureAwait(false);
250+
await sftp.CreateDirectoryAsync("test1_3", CancellationToken.None).ConfigureAwait(false);
251251

252252
var files = sftp.ListDirectory(".");
253253

test/Renci.SshNet.IntegrationTests/SftpClientTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public async Task Create_directory_with_contents_and_list_it_async()
6060
var testContent = "file content";
6161

6262
// Create new directory and check if it exists
63-
_sftpClient.CreateDirectory(testDirectory);
63+
await _sftpClient.CreateDirectoryAsync(testDirectory, CancellationToken.None).ConfigureAwait(false);
6464
Assert.IsTrue(await _sftpClient.ExistsAsync(testDirectory));
6565

6666
// Upload file and check if it exists

0 commit comments

Comments
 (0)