using Renci.SshNet.Common; using Renci.SshNet.Sftp; namespace Renci.SshNet.IntegrationTests.OldIntegrationTests { /// /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. /// public partial class SftpClientTest : IntegrationTestBase { [TestMethod] [TestCategory("Sftp")] public void Test_Sftp_Upload_And_Download_1MB_File() { RemoveAllFiles(); using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); var uploadedFileName = Path.GetTempFileName(); var remoteFileName = Path.GetRandomFileName(); CreateTestFile(uploadedFileName, 1); // Calculate has value var uploadedHash = CalculateMD5(uploadedFileName); using (var file = File.OpenRead(uploadedFileName)) { sftp.UploadFile(file, remoteFileName); } var downloadedFileName = Path.GetTempFileName(); using (var file = File.OpenWrite(downloadedFileName)) { sftp.DownloadFile(remoteFileName, file); } var downloadedHash = CalculateMD5(downloadedFileName); sftp.DeleteFile(remoteFileName); File.Delete(uploadedFileName); File.Delete(downloadedFileName); sftp.Disconnect(); Assert.AreEqual(uploadedHash, downloadedHash); } } [TestMethod] [TestCategory("Sftp")] public async Task Test_Sftp_Upload_And_Download_Async_1MB_File() { RemoveAllFiles(); using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { await sftp.ConnectAsync(CancellationToken.None).ConfigureAwait(false); var uploadedFileName = Path.GetTempFileName(); var remoteFileName = Path.GetRandomFileName(); CreateTestFile(uploadedFileName, 1); // Calculate has value var uploadedHash = CalculateMD5(uploadedFileName); using (var file = File.OpenRead(uploadedFileName)) { await sftp.UploadFileAsync(file, remoteFileName).ConfigureAwait(false); } // uploading again should not throw because of the default canOverride = true using (var file = File.OpenRead(uploadedFileName)) { await sftp.UploadFileAsync(file, remoteFileName).ConfigureAwait(false); } // uploading with canOverride = false should throw because the file already exists using (var file = File.OpenRead(uploadedFileName)) { await Assert.ThrowsAsync(async () => await sftp.UploadFileAsync(file, remoteFileName, canOverride: false).ConfigureAwait(false)); } var downloadedFileName = Path.GetTempFileName(); using (var file = File.OpenWrite(downloadedFileName)) { await sftp.DownloadFileAsync(remoteFileName, file).ConfigureAwait(false); } var downloadedHash = CalculateMD5(downloadedFileName); await sftp.DeleteFileAsync(remoteFileName, CancellationToken.None).ConfigureAwait(false); File.Delete(uploadedFileName); File.Delete(downloadedFileName); sftp.Disconnect(); Assert.AreEqual(uploadedHash, downloadedHash); } } [TestMethod] [TestCategory("Sftp")] public void Test_Sftp_Upload_Forbidden() { using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); var uploadedFileName = Path.GetTempFileName(); var remoteFileName = "/root/1"; CreateTestFile(uploadedFileName, 1); using (var file = File.OpenRead(uploadedFileName)) { Assert.ThrowsExactly(() => sftp.UploadFile(file, remoteFileName)); } sftp.Disconnect(); } } [TestMethod] [TestCategory("Sftp")] public async Task Test_Sftp_UploadAsync_Cancellation_Requested() { using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { await sftp.ConnectAsync(CancellationToken.None); var uploadedFileName = Path.GetTempFileName(); var remoteFileName = "/root/1"; CreateTestFile(uploadedFileName, 1); var cancelledToken = new CancellationToken(true); using (var file = File.OpenRead(uploadedFileName)) { await Assert.ThrowsAsync(() => sftp.UploadFileAsync(file, remoteFileName, cancelledToken)); } } } [TestMethod] [TestCategory("Sftp")] public void Test_Sftp_Multiple_Async_Upload_And_Download_10Files_5MB_Each() { var maxFiles = 10; var maxSize = 5; RemoveAllFiles(); using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.OperationTimeout = TimeSpan.FromMinutes(1); sftp.Connect(); var testInfoList = new Dictionary(); for (var i = 0; i < maxFiles; i++) { var testInfo = new TestInfo { UploadedFileName = Path.GetTempFileName(), DownloadedFileName = Path.GetTempFileName(), RemoteFileName = Path.GetRandomFileName() }; CreateTestFile(testInfo.UploadedFileName, maxSize); // Calculate hash value testInfo.UploadedHash = CalculateMD5(testInfo.UploadedFileName); testInfoList.Add(testInfo.RemoteFileName, testInfo); } var uploadWaitHandles = new List(); // Start file uploads foreach (var remoteFile in testInfoList.Keys) { var testInfo = testInfoList[remoteFile]; testInfo.UploadedFile = File.OpenRead(testInfo.UploadedFileName); testInfo.UploadResult = sftp.BeginUploadFile(testInfo.UploadedFile, remoteFile, null, null) as SftpUploadAsyncResult; uploadWaitHandles.Add(testInfo.UploadResult.AsyncWaitHandle); } // Wait for upload to finish var uploadCompleted = false; while (!uploadCompleted) { // Assume upload completed uploadCompleted = true; foreach (var testInfo in testInfoList.Values) { var sftpResult = testInfo.UploadResult; if (!testInfo.UploadResult.IsCompleted) { uploadCompleted = false; } } Thread.Sleep(500); } // End file uploads foreach (var remoteFile in testInfoList.Keys) { var testInfo = testInfoList[remoteFile]; sftp.EndUploadFile(testInfo.UploadResult); testInfo.UploadedFile.Dispose(); } // Start file downloads var downloadWaitHandles = new List(); foreach (var remoteFile in testInfoList.Keys) { var testInfo = testInfoList[remoteFile]; testInfo.DownloadedFile = File.OpenWrite(testInfo.DownloadedFileName); testInfo.DownloadResult = sftp.BeginDownloadFile(remoteFile, testInfo.DownloadedFile, null, null) as SftpDownloadAsyncResult; downloadWaitHandles.Add(testInfo.DownloadResult.AsyncWaitHandle); } // Wait for download to finish var downloadCompleted = false; while (!downloadCompleted) { // Assume download completed downloadCompleted = true; foreach (var testInfo in testInfoList.Values) { var sftpResult = testInfo.DownloadResult; if (!testInfo.DownloadResult.IsCompleted) { downloadCompleted = false; } } Thread.Sleep(500); } var hashMatches = true; var uploadDownloadSizeOk = true; // End file downloads foreach (var remoteFile in testInfoList.Keys) { var testInfo = testInfoList[remoteFile]; sftp.EndDownloadFile(testInfo.DownloadResult); testInfo.DownloadedFile.Dispose(); testInfo.DownloadedHash = CalculateMD5(testInfo.DownloadedFileName); Console.WriteLine(remoteFile); Console.WriteLine("UploadedBytes: " + testInfo.UploadResult.UploadedBytes); Console.WriteLine("DownloadedBytes: " + testInfo.DownloadResult.DownloadedBytes); Console.WriteLine("UploadedHash: " + testInfo.UploadedHash); Console.WriteLine("DownloadedHash: " + testInfo.DownloadedHash); if (!(testInfo.UploadResult.UploadedBytes > 0 && testInfo.DownloadResult.DownloadedBytes > 0 && testInfo.DownloadResult.DownloadedBytes == testInfo.UploadResult.UploadedBytes)) { uploadDownloadSizeOk = false; } if (!testInfo.DownloadedHash.Equals(testInfo.UploadedHash)) { hashMatches = false; } } // Clean up after test foreach (var remoteFile in testInfoList.Keys) { var testInfo = testInfoList[remoteFile]; sftp.DeleteFile(remoteFile); File.Delete(testInfo.UploadedFileName); File.Delete(testInfo.DownloadedFileName); } sftp.Disconnect(); Assert.IsTrue(hashMatches, "Hash does not match"); Assert.IsTrue(uploadDownloadSizeOk, "Uploaded and downloaded bytes does not match"); } } // TODO: Split this test into multiple tests [TestMethod] [TestCategory("Sftp")] [Description("Test that delegates passed to BeginUploadFile, BeginDownloadFile and BeginListDirectory are actually called.")] public void Test_Sftp_Ensure_Async_Delegates_Called_For_BeginFileUpload_BeginFileDownload_BeginListDirectory() { RemoveAllFiles(); using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); var remoteFileName = Path.GetRandomFileName(); var localFileName = Path.GetRandomFileName(); using var uploadDelegateEvent = new ManualResetEventSlim(); using var downloadDelegateEvent = new ManualResetEventSlim(); using var listDirectoryDelegateEvent = new ManualResetEventSlim(); using var uploadCallbackEvent = new ManualResetEventSlim(); using var downloadCallbackEvent = new ManualResetEventSlim(); using var listDirectoryCallbackEvent = new ManualResetEventSlim(); IAsyncResult asyncResult; // Test for BeginUploadFile. CreateTestFile(localFileName, 1); var originalContext = SynchronizationContext.Current; try { // Set a throwing context to verify it's not captured by the callback SynchronizationContext.SetSynchronizationContext(new ThrowingSynchronizationContext()); using (var fileStream = File.OpenRead(localFileName)) { asyncResult = sftp.BeginUploadFile(fileStream, remoteFileName, delegate (IAsyncResult ar) { uploadDelegateEvent.Set(); }, state: null, uploadCallback: _ => uploadCallbackEvent.Set()); sftp.EndUploadFile(asyncResult); } } finally { SynchronizationContext.SetSynchronizationContext(originalContext); } File.Delete(localFileName); Assert.IsTrue(uploadDelegateEvent.Wait(1000)); Assert.IsTrue(uploadCallbackEvent.Wait(1000)); // Test for BeginDownloadFile. asyncResult = null; try { // Set a throwing context to verify it's not captured by the callback SynchronizationContext.SetSynchronizationContext(new ThrowingSynchronizationContext()); using (var fileStream = File.OpenWrite(localFileName)) { asyncResult = sftp.BeginDownloadFile(remoteFileName, fileStream, delegate (IAsyncResult ar) { downloadDelegateEvent.Set(); }, state: null, downloadCallback: _ => downloadCallbackEvent.Set()); sftp.EndDownloadFile(asyncResult); } } finally { SynchronizationContext.SetSynchronizationContext(originalContext); } File.Delete(localFileName); Assert.IsTrue(downloadDelegateEvent.Wait(1000)); Assert.IsTrue(downloadCallbackEvent.Wait(1000)); // Test for BeginListDirectory. try { // Set a throwing context to verify it's not captured by the callback SynchronizationContext.SetSynchronizationContext(new ThrowingSynchronizationContext()); asyncResult = sftp.BeginListDirectory(sftp.WorkingDirectory, delegate (IAsyncResult ar) { listDirectoryDelegateEvent.Set(); }, state: null, listCallback: _ => listDirectoryCallbackEvent.Set()); _ = sftp.EndListDirectory(asyncResult); } finally { SynchronizationContext.SetSynchronizationContext(originalContext); } Assert.IsTrue(listDirectoryDelegateEvent.Wait(1000)); Assert.IsTrue(listDirectoryCallbackEvent.Wait(1000)); } } [TestMethod] [TestCategory("Sftp")] [Description("Test passing null to BeginUploadFile")] public void Test_Sftp_BeginUploadFile_StreamIsNull() { using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); Assert.ThrowsExactly(() => sftp.BeginUploadFile(null, "aaaaa", null, null)); } } [TestMethod] [TestCategory("Sftp")] [Description("Test passing null to BeginUploadFile")] public void Test_Sftp_BeginUploadFile_FileNameIsWhiteSpace() { using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); Assert.ThrowsExactly(() => sftp.BeginUploadFile(new MemoryStream(), " ", null, null)); } } [TestMethod] [TestCategory("Sftp")] [Description("Test passing null to BeginUploadFile")] public void Test_Sftp_BeginUploadFile_FileNameIsNull() { using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); Assert.ThrowsExactly(() => sftp.BeginUploadFile(new MemoryStream(), null, null, null)); } } [TestMethod] [TestCategory("Sftp")] public void Test_Sftp_EndUploadFile_Invalid_Async_Handle() { using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { sftp.Connect(); var async1 = sftp.BeginListDirectory("/", null, null); var filename = Path.GetTempFileName(); CreateTestFile(filename, 100); using var fileStream = File.OpenRead(filename); var async2 = sftp.BeginUploadFile(fileStream, "test", null, null); Assert.ThrowsExactly(() => sftp.EndUploadFile(async1)); } } [TestMethod] [TestCategory("Sftp")] public async Task Test_Sftp_UploadFileAsync_UploadProgress() { using (var sftp = new SftpClient(SshServerHostName, SshServerPort, User.UserName, User.Password)) { await sftp.ConnectAsync(CancellationToken.None); var filename = Path.GetTempFileName(); int testFileSizeMB = 1; CreateTestFile(filename, testFileSizeMB); using var fileStream = File.OpenRead(filename); using ManualResetEventSlim finalCallbackCalledEvent = new(); IProgress progress = new Progress(r => { if ((int)r.TotalBytesUploaded == testFileSizeMB * 1024 * 1024) { finalCallbackCalledEvent.Set(); } }); await sftp.UploadFileAsync(fileStream, "test", progress); // since the callback is queued to the thread pool, wait for the event. bool callbackCalled = finalCallbackCalledEvent.Wait(5000); Assert.IsTrue(callbackCalled); } } private sealed class ThrowingSynchronizationContext : SynchronizationContext { public override void Post(SendOrPostCallback d, object state) { throw new InvalidOperationException(); } public override void Send(SendOrPostCallback d, object state) { throw new InvalidOperationException(); } } } }