Skip to content

Added ChangeDirectoryAsync to SftpClient #1504

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/Renci.SshNet/ISftpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,20 @@ public interface ISftpClient : IBaseClient
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
void ChangeDirectory(string path);

/// <summary>
/// Asynchronously requests to change the current working directory to the specified path.
/// </summary>
/// <param name="path">The new working directory.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
/// <exception cref="SftpPermissionDeniedException">Permission to change directory denied by remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</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 ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default);

/// <summary>
/// Changes permissions of file(s) to specified mode.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions src/Renci.SshNet/Sftp/ISftpSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ internal interface ISftpSession : ISubsystemSession
/// <param name="path">The new working directory.</param>
void ChangeDirectory(string path);

/// <summary>
/// Asynchronously requests to change the current working directory to the specified path.
/// </summary>
/// <param name="path">The new working directory.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default);

/// <summary>
/// Resolves a given path into an absolute path on the server.
/// </summary>
Expand Down
18 changes: 18 additions & 0 deletions src/Renci.SshNet/Sftp/SftpSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@ public void ChangeDirectory(string path)
WorkingDirectory = fullPath;
}

/// <summary>
/// Asynchronously requests to change the current working directory to the specified path.
/// </summary>
/// <param name="path">The new working directory.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
public async Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

var fullPath = await GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
var handle = await RequestOpenDirAsync(fullPath, cancellationToken).ConfigureAwait(false);

await RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false);

WorkingDirectory = fullPath;
}

internal void SendMessage(SftpMessage sftpMessage)
{
var data = sftpMessage.GetBytes();
Expand Down
27 changes: 27 additions & 0 deletions src/Renci.SshNet/SftpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,33 @@ public void ChangeDirectory(string path)
_sftpSession.ChangeDirectory(path);
}

/// <summary>
/// Asynchronously requests to change the current working directory to the specified path.
/// </summary>
/// <param name="path">The new working directory.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
/// <exception cref="SftpPermissionDeniedException">Permission to change directory denied by remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</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 Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default)
{
CheckDisposed();
ThrowHelper.ThrowIfNull(path);

if (_sftpSession is null)
{
throw new SshConnectionException("Client not connected.");
}

cancellationToken.ThrowIfCancellationRequested();

return _sftpSession.ChangeDirectoryAsync(path, cancellationToken);
}

/// <summary>
/// Changes permissions of file(s) to specified mode.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ public void Test_Sftp_ChangeDirectory_Root_Dont_Exists()
}
}

[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SftpPathNotFoundException))]
public async Task Test_Sftp_ChangeDirectory_Root_Dont_ExistsAsync()
{
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
{
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
await sftp.ChangeDirectoryAsync("/asdasd", CancellationToken.None).ConfigureAwait(false);
}
}

[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SftpPathNotFoundException))]
Expand All @@ -31,6 +43,18 @@ public void Test_Sftp_ChangeDirectory_Root_With_Slash_Dont_Exists()
}
}

[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SftpPathNotFoundException))]
public async Task Test_Sftp_ChangeDirectory_Root_With_Slash_Dont_ExistsAsync()
{
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
{
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
await sftp.ChangeDirectoryAsync("/asdasd/", CancellationToken.None).ConfigureAwait(false);
}
}

[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SftpPathNotFoundException))]
Expand All @@ -43,6 +67,18 @@ public void Test_Sftp_ChangeDirectory_Subfolder_Dont_Exists()
}
}

[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SftpPathNotFoundException))]
public async Task Test_Sftp_ChangeDirectory_Subfolder_Dont_ExistsAsync()
{
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
{
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
await sftp.ChangeDirectoryAsync("/asdasd/sssddds", CancellationToken.None).ConfigureAwait(false);
}
}

[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SftpPathNotFoundException))]
Expand All @@ -55,6 +91,18 @@ public void Test_Sftp_ChangeDirectory_Subfolder_With_Slash_Dont_Exists()
}
}

[TestMethod]
[TestCategory("Sftp")]
[ExpectedException(typeof(SftpPathNotFoundException))]
public async Task Test_Sftp_ChangeDirectory_Subfolder_With_Slash_Dont_ExistsAsync()
{
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
{
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
await sftp.ChangeDirectoryAsync("/asdasd/sssddds/", CancellationToken.None).ConfigureAwait(false);
}
}

[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_ChangeDirectory_Which_Exists()
Expand All @@ -67,6 +115,18 @@ public void Test_Sftp_ChangeDirectory_Which_Exists()
}
}

[TestMethod]
[TestCategory("Sftp")]
public async Task Test_Sftp_ChangeDirectory_Which_ExistsAsync()
{
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
{
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
await sftp.ChangeDirectoryAsync("/usr", CancellationToken.None).ConfigureAwait(false);
Assert.AreEqual("/usr", sftp.WorkingDirectory);
}
}

[TestMethod]
[TestCategory("Sftp")]
public void Test_Sftp_ChangeDirectory_Which_Exists_With_Slash()
Expand All @@ -78,5 +138,17 @@ public void Test_Sftp_ChangeDirectory_Which_Exists_With_Slash()
Assert.AreEqual("/usr", sftp.WorkingDirectory);
}
}

[TestMethod]
[TestCategory("Sftp")]
public async Task Test_Sftp_ChangeDirectory_Which_Exists_With_SlashAsync()
{
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
{
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
await sftp.ChangeDirectoryAsync("/usr/", CancellationToken.None).ConfigureAwait(false);
Assert.AreEqual("/usr", sftp.WorkingDirectory);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,75 @@ public void Test_Sftp_Change_Directory()
RemoveAllFiles();
}

[TestMethod]
[TestCategory("Sftp")]
public async Task Test_Sftp_Change_DirectoryAsync()
{
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
{
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);

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

sftp.CreateDirectory("test1");

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");

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

Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}", sftp.WorkingDirectory)));

await sftp.ChangeDirectoryAsync("test1_1", CancellationToken.None).ConfigureAwait(false);

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

await sftp.ChangeDirectoryAsync("../test1_2", CancellationToken.None).ConfigureAwait(false);

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

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

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

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

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

files = sftp.ListDirectory("test1/test1_1");

Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}/test1/test1_1", sftp.WorkingDirectory)));

await sftp.ChangeDirectoryAsync("test1/test1_1", CancellationToken.None).ConfigureAwait(false);

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

await sftp.ChangeDirectoryAsync("/home/sshnet/test1/test1_1", CancellationToken.None).ConfigureAwait(false);

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

await sftp.ChangeDirectoryAsync("/home/sshnet/test1/test1_1/../test1_2", CancellationToken.None).ConfigureAwait(false);

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

await sftp.ChangeDirectoryAsync("../../", CancellationToken.None).ConfigureAwait(false);

sftp.DeleteDirectory("test1/test1_1");
sftp.DeleteDirectory("test1/test1_2");
sftp.DeleteDirectory("test1/test1_3");
sftp.DeleteDirectory("test1");
Comment on lines +290 to +293
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume these will become async with your other PR


sftp.Disconnect();
}

RemoveAllFiles();
}

[TestMethod]
[TestCategory("Sftp")]
[Description("Test passing null to ChangeDirectory.")]
Expand All @@ -245,6 +314,22 @@ public void Test_Sftp_ChangeDirectory_Null()
}
}

[TestMethod]
[TestCategory("Sftp")]
[Description("Test passing null to ChangeDirectory.")]
[ExpectedException(typeof(ArgumentNullException))]
public async Task Test_Sftp_ChangeDirectory_NullAsync()
{
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
{
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);

await sftp.ChangeDirectoryAsync(null, CancellationToken.None).ConfigureAwait(false);

sftp.Disconnect();
}
}

[TestMethod]
[TestCategory("Sftp")]
[Description("Test calling EndListDirectory method more then once.")]
Expand Down
Loading