Skip to content

Commit 737c3e5

Browse files
authored
Added ChangeDirectoryAsync to SftpClient (#1504)
* Add ChangeDirectoryAsync * Added async tests for ChangeDirectoryAsync
1 parent 28e6742 commit 737c3e5

File tree

7 files changed

+313
-1
lines changed

7 files changed

+313
-1
lines changed

src/Renci.SshNet/ISftpClient.cs

+14
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,20 @@ public interface ISftpClient : IBaseClient
358358
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
359359
void ChangeDirectory(string path);
360360

361+
/// <summary>
362+
/// Asynchronously requests to change the current working directory to the specified path.
363+
/// </summary>
364+
/// <param name="path">The new working directory.</param>
365+
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
366+
/// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
367+
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
368+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
369+
/// <exception cref="SftpPermissionDeniedException">Permission to change directory denied by remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
370+
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
371+
/// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
372+
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
373+
Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default);
374+
361375
/// <summary>
362376
/// Changes permissions of file(s) to specified mode.
363377
/// </summary>

src/Renci.SshNet/Sftp/ISftpSession.cs

+8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ internal interface ISftpSession : ISubsystemSession
3434
/// <param name="path">The new working directory.</param>
3535
void ChangeDirectory(string path);
3636

37+
/// <summary>
38+
/// Asynchronously requests to change the current working directory to the specified path.
39+
/// </summary>
40+
/// <param name="path">The new working directory.</param>
41+
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
42+
/// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
43+
Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default);
44+
3745
/// <summary>
3846
/// Resolves a given path into an absolute path on the server.
3947
/// </summary>

src/Renci.SshNet/Sftp/SftpSession.cs

+18
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,24 @@ public void ChangeDirectory(string path)
8282
WorkingDirectory = fullPath;
8383
}
8484

85+
/// <summary>
86+
/// Asynchronously requests to change the current working directory to the specified path.
87+
/// </summary>
88+
/// <param name="path">The new working directory.</param>
89+
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
90+
/// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
91+
public async Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default)
92+
{
93+
cancellationToken.ThrowIfCancellationRequested();
94+
95+
var fullPath = await GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
96+
var handle = await RequestOpenDirAsync(fullPath, cancellationToken).ConfigureAwait(false);
97+
98+
await RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false);
99+
100+
WorkingDirectory = fullPath;
101+
}
102+
85103
internal void SendMessage(SftpMessage sftpMessage)
86104
{
87105
var data = sftpMessage.GetBytes();

src/Renci.SshNet/SftpClient.cs

+27
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,33 @@ public void ChangeDirectory(string path)
305305
_sftpSession.ChangeDirectory(path);
306306
}
307307

308+
/// <summary>
309+
/// Asynchronously requests to change the current working directory to the specified path.
310+
/// </summary>
311+
/// <param name="path">The new working directory.</param>
312+
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
313+
/// <returns>A <see cref="Task"/> that tracks the asynchronous change working directory request.</returns>
314+
/// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
315+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
316+
/// <exception cref="SftpPermissionDeniedException">Permission to change directory denied by remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
317+
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>
318+
/// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
319+
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
320+
public Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default)
321+
{
322+
CheckDisposed();
323+
ThrowHelper.ThrowIfNull(path);
324+
325+
if (_sftpSession is null)
326+
{
327+
throw new SshConnectionException("Client not connected.");
328+
}
329+
330+
cancellationToken.ThrowIfCancellationRequested();
331+
332+
return _sftpSession.ChangeDirectoryAsync(path, cancellationToken);
333+
}
334+
308335
/// <summary>
309336
/// Changes permissions of file(s) to specified mode.
310337
/// </summary>

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

+72
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ public void Test_Sftp_ChangeDirectory_Root_Dont_Exists()
1919
}
2020
}
2121

22+
[TestMethod]
23+
[TestCategory("Sftp")]
24+
[ExpectedException(typeof(SftpPathNotFoundException))]
25+
public async Task Test_Sftp_ChangeDirectory_Root_Dont_ExistsAsync()
26+
{
27+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
28+
{
29+
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
30+
await sftp.ChangeDirectoryAsync("/asdasd", CancellationToken.None).ConfigureAwait(false);
31+
}
32+
}
33+
2234
[TestMethod]
2335
[TestCategory("Sftp")]
2436
[ExpectedException(typeof(SftpPathNotFoundException))]
@@ -31,6 +43,18 @@ public void Test_Sftp_ChangeDirectory_Root_With_Slash_Dont_Exists()
3143
}
3244
}
3345

46+
[TestMethod]
47+
[TestCategory("Sftp")]
48+
[ExpectedException(typeof(SftpPathNotFoundException))]
49+
public async Task Test_Sftp_ChangeDirectory_Root_With_Slash_Dont_ExistsAsync()
50+
{
51+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
52+
{
53+
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
54+
await sftp.ChangeDirectoryAsync("/asdasd/", CancellationToken.None).ConfigureAwait(false);
55+
}
56+
}
57+
3458
[TestMethod]
3559
[TestCategory("Sftp")]
3660
[ExpectedException(typeof(SftpPathNotFoundException))]
@@ -43,6 +67,18 @@ public void Test_Sftp_ChangeDirectory_Subfolder_Dont_Exists()
4367
}
4468
}
4569

70+
[TestMethod]
71+
[TestCategory("Sftp")]
72+
[ExpectedException(typeof(SftpPathNotFoundException))]
73+
public async Task Test_Sftp_ChangeDirectory_Subfolder_Dont_ExistsAsync()
74+
{
75+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
76+
{
77+
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
78+
await sftp.ChangeDirectoryAsync("/asdasd/sssddds", CancellationToken.None).ConfigureAwait(false);
79+
}
80+
}
81+
4682
[TestMethod]
4783
[TestCategory("Sftp")]
4884
[ExpectedException(typeof(SftpPathNotFoundException))]
@@ -55,6 +91,18 @@ public void Test_Sftp_ChangeDirectory_Subfolder_With_Slash_Dont_Exists()
5591
}
5692
}
5793

94+
[TestMethod]
95+
[TestCategory("Sftp")]
96+
[ExpectedException(typeof(SftpPathNotFoundException))]
97+
public async Task Test_Sftp_ChangeDirectory_Subfolder_With_Slash_Dont_ExistsAsync()
98+
{
99+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
100+
{
101+
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
102+
await sftp.ChangeDirectoryAsync("/asdasd/sssddds/", CancellationToken.None).ConfigureAwait(false);
103+
}
104+
}
105+
58106
[TestMethod]
59107
[TestCategory("Sftp")]
60108
public void Test_Sftp_ChangeDirectory_Which_Exists()
@@ -67,6 +115,18 @@ public void Test_Sftp_ChangeDirectory_Which_Exists()
67115
}
68116
}
69117

118+
[TestMethod]
119+
[TestCategory("Sftp")]
120+
public async Task Test_Sftp_ChangeDirectory_Which_ExistsAsync()
121+
{
122+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
123+
{
124+
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
125+
await sftp.ChangeDirectoryAsync("/usr", CancellationToken.None).ConfigureAwait(false);
126+
Assert.AreEqual("/usr", sftp.WorkingDirectory);
127+
}
128+
}
129+
70130
[TestMethod]
71131
[TestCategory("Sftp")]
72132
public void Test_Sftp_ChangeDirectory_Which_Exists_With_Slash()
@@ -78,5 +138,17 @@ public void Test_Sftp_ChangeDirectory_Which_Exists_With_Slash()
78138
Assert.AreEqual("/usr", sftp.WorkingDirectory);
79139
}
80140
}
141+
142+
[TestMethod]
143+
[TestCategory("Sftp")]
144+
public async Task Test_Sftp_ChangeDirectory_Which_Exists_With_SlashAsync()
145+
{
146+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
147+
{
148+
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
149+
await sftp.ChangeDirectoryAsync("/usr/", CancellationToken.None).ConfigureAwait(false);
150+
Assert.AreEqual("/usr", sftp.WorkingDirectory);
151+
}
152+
}
81153
}
82154
}

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

+85
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,75 @@ public void Test_Sftp_Change_Directory()
229229
RemoveAllFiles();
230230
}
231231

232+
[TestMethod]
233+
[TestCategory("Sftp")]
234+
public async Task Test_Sftp_Change_DirectoryAsync()
235+
{
236+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
237+
{
238+
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
239+
240+
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet");
241+
242+
sftp.CreateDirectory("test1");
243+
244+
await sftp.ChangeDirectoryAsync("test1", CancellationToken.None).ConfigureAwait(false);
245+
246+
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1");
247+
248+
sftp.CreateDirectory("test1_1");
249+
sftp.CreateDirectory("test1_2");
250+
sftp.CreateDirectory("test1_3");
251+
252+
var files = sftp.ListDirectory(".");
253+
254+
Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}", sftp.WorkingDirectory)));
255+
256+
await sftp.ChangeDirectoryAsync("test1_1", CancellationToken.None).ConfigureAwait(false);
257+
258+
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1");
259+
260+
await sftp.ChangeDirectoryAsync("../test1_2", CancellationToken.None).ConfigureAwait(false);
261+
262+
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_2");
263+
264+
await sftp.ChangeDirectoryAsync("..", CancellationToken.None).ConfigureAwait(false);
265+
266+
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1");
267+
268+
await sftp.ChangeDirectoryAsync("..", CancellationToken.None).ConfigureAwait(false);
269+
270+
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet");
271+
272+
files = sftp.ListDirectory("test1/test1_1");
273+
274+
Assert.IsTrue(files.First().FullName.StartsWith(string.Format("{0}/test1/test1_1", sftp.WorkingDirectory)));
275+
276+
await sftp.ChangeDirectoryAsync("test1/test1_1", CancellationToken.None).ConfigureAwait(false);
277+
278+
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1");
279+
280+
await sftp.ChangeDirectoryAsync("/home/sshnet/test1/test1_1", CancellationToken.None).ConfigureAwait(false);
281+
282+
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_1");
283+
284+
await sftp.ChangeDirectoryAsync("/home/sshnet/test1/test1_1/../test1_2", CancellationToken.None).ConfigureAwait(false);
285+
286+
Assert.AreEqual(sftp.WorkingDirectory, "/home/sshnet/test1/test1_2");
287+
288+
await sftp.ChangeDirectoryAsync("../../", CancellationToken.None).ConfigureAwait(false);
289+
290+
sftp.DeleteDirectory("test1/test1_1");
291+
sftp.DeleteDirectory("test1/test1_2");
292+
sftp.DeleteDirectory("test1/test1_3");
293+
sftp.DeleteDirectory("test1");
294+
295+
sftp.Disconnect();
296+
}
297+
298+
RemoveAllFiles();
299+
}
300+
232301
[TestMethod]
233302
[TestCategory("Sftp")]
234303
[Description("Test passing null to ChangeDirectory.")]
@@ -245,6 +314,22 @@ public void Test_Sftp_ChangeDirectory_Null()
245314
}
246315
}
247316

317+
[TestMethod]
318+
[TestCategory("Sftp")]
319+
[Description("Test passing null to ChangeDirectory.")]
320+
[ExpectedException(typeof(ArgumentNullException))]
321+
public async Task Test_Sftp_ChangeDirectory_NullAsync()
322+
{
323+
using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password))
324+
{
325+
await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false);
326+
327+
await sftp.ChangeDirectoryAsync(null, CancellationToken.None).ConfigureAwait(false);
328+
329+
sftp.Disconnect();
330+
}
331+
}
332+
248333
[TestMethod]
249334
[TestCategory("Sftp")]
250335
[Description("Test calling EndListDirectory method more then once.")]

0 commit comments

Comments
 (0)