diff --git a/.editorconfig b/.editorconfig
index 8bbdc9ca2..788ade7af 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -719,7 +719,7 @@ dotnet_code_quality.CA1828.api_surface = all
dotnet_diagnostic.CA1852.severity = none
# CA1848: don't enforce LoggerMessage pattern
-dotnet_diagnostic.CA1848.severity = suggestion
+dotnet_diagnostic.CA1848.severity = silent
# CA1859: Change return type for improved performance
#
diff --git a/src/Renci.SshNet/Common/Extensions.cs b/src/Renci.SshNet/Common/Extensions.cs
index 14b80ac74..c334d19a1 100644
--- a/src/Renci.SshNet/Common/Extensions.cs
+++ b/src/Renci.SshNet/Common/Extensions.cs
@@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Net.Sockets;
using System.Numerics;
using System.Runtime.CompilerServices;
-using System.Text;
using System.Threading;
using Renci.SshNet.Abstractions;
@@ -153,22 +151,6 @@ public static void SetIgnoringObjectDisposed(this EventWaitHandle waitHandle)
}
}
- ///
- /// Prints out the specified bytes.
- ///
- /// The bytes.
- internal static void DebugPrint(this IEnumerable bytes)
- {
- var sb = new StringBuilder();
-
- foreach (var b in bytes)
- {
- _ = sb.AppendFormat(CultureInfo.CurrentCulture, "0x{0:x2}, ", b);
- }
-
- Debug.WriteLine(sb.ToString());
- }
-
internal static void ValidatePort(this uint value, [CallerArgumentExpression(nameof(value))] string argument = null)
{
if (value > IPEndPoint.MaxPort)
diff --git a/src/Renci.SshNet/ISubsystemSession.cs b/src/Renci.SshNet/ISubsystemSession.cs
index c00b5ac7b..f15de0c4f 100644
--- a/src/Renci.SshNet/ISubsystemSession.cs
+++ b/src/Renci.SshNet/ISubsystemSession.cs
@@ -84,57 +84,11 @@ internal interface ISubsystemSession : IDisposable
/// A representing the wait.
Task WaitOnHandleAsync(TaskCompletionSource tcs, int millisecondsTimeout, CancellationToken cancellationToken);
- ///
- /// Blocks the current thread until the specified gets signaled, using a
- /// 32-bit signed integer to specify the time interval in milliseconds.
- ///
- /// The handle to wait for.
- /// To number of milliseconds to wait for to get signaled, or -1 to wait indefinitely.
- ///
- /// if received a signal within the specified timeout;
- /// otherwise, .
- ///
- /// The connection was closed by the server.
- /// The channel was closed.
- ///
- /// The blocking wait is also interrupted when either the established channel is closed, the current
- /// session is disconnected or an unexpected occurred while processing a channel
- /// or session event.
- ///
- bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout);
-
- ///
- /// Blocks the current thread until the specified gets signaled, using a
- /// 32-bit signed integer to specify the time interval in milliseconds.
- ///
- /// The first handle to wait for.
- /// The second handle to wait for.
- /// To number of milliseconds to wait for a to get signaled, or -1 to wait indefinitely.
- ///
- /// 0 if received a signal within the specified timeout and 1
- /// if received a signal within the specified timeout, or
- /// if no object satisfied the wait.
- ///
- /// The connection was closed by the server.
- /// The channel was closed.
- ///
- ///
- /// The blocking wait is also interrupted when either the established channel is closed, the current
- /// session is disconnected or an unexpected occurred while processing a channel
- /// or session event.
- ///
- ///
- /// When both and are signaled during the call,
- /// then 0 is returned.
- ///
- ///
- int WaitAny(WaitHandle waitHandleA, WaitHandle waitHandleB, int millisecondsTimeout);
-
///
/// Waits for any of the elements in the specified array to receive a signal, using a 32-bit signed
/// integer to specify the time interval.
///
- /// A array - constructed using - containing the objects to wait for.
+ /// A array - constructed using - containing the objects to wait for.
/// To number of milliseconds to wait for a to get signaled, or -1 to wait indefinitely.
///
/// The array index of the first non-system object that satisfied the wait.
@@ -147,16 +101,6 @@ internal interface ISubsystemSession : IDisposable
///
int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout);
- ///
- /// Creates a array that is composed of system objects and the specified
- /// elements.
- ///
- /// A array containing the objects to wait for.
- ///
- /// A array that is composed of system objects and the specified elements.
- ///
- WaitHandle[] CreateWaitHandleArray(params WaitHandle[] waitHandles);
-
///
/// Creates a array that is composed of system objects and the specified
/// elements.
diff --git a/src/Renci.SshNet/Sftp/ISftpSession.cs b/src/Renci.SshNet/Sftp/ISftpSession.cs
index 07cf1b8b9..b9baf43a5 100644
--- a/src/Renci.SshNet/Sftp/ISftpSession.cs
+++ b/src/Renci.SshNet/Sftp/ISftpSession.cs
@@ -40,7 +40,7 @@ internal interface ISftpSession : ISubsystemSession
/// The new working directory.
/// The token to monitor for cancellation requests.
/// A that tracks the asynchronous change working directory request.
- Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default);
+ Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken);
///
/// Resolves a given path into an absolute path on the server.
@@ -168,7 +168,7 @@ internal interface ISftpSession : ISubsystemSession
/// The path.
/// The to observe.
/// A that represents the asynchronous SSH_FXP_MKDIR operation.
- Task RequestMkDirAsync(string path, CancellationToken cancellationToken = default);
+ Task RequestMkDirAsync(string path, CancellationToken cancellationToken);
///
/// Performs a SSH_FXP_OPEN request.
@@ -389,7 +389,7 @@ internal interface ISftpSession : ISubsystemSession
///
/// A task that represents the asynchronous SSH_FXP_RMDIR request.
///
- Task RequestRmDirAsync(string path, CancellationToken cancellationToken = default);
+ Task RequestRmDirAsync(string path, CancellationToken cancellationToken);
///
/// Performs SSH_FXP_SETSTAT request.
diff --git a/src/Renci.SshNet/Sftp/SftpFileStream.cs b/src/Renci.SshNet/Sftp/SftpFileStream.cs
index 0b86595e2..c5e486a9a 100644
--- a/src/Renci.SshNet/Sftp/SftpFileStream.cs
+++ b/src/Renci.SshNet/Sftp/SftpFileStream.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
@@ -191,7 +192,7 @@ public TimeSpan Timeout
}
}
- private SftpFileStream(ISftpSession session, string path, FileAccess access, int bufferSize, byte[] handle, long position)
+ private SftpFileStream(ISftpSession session, string path, FileAccess access, int readBufferSize, int writeBufferSize, byte[] handle, long position)
{
Timeout = TimeSpan.FromSeconds(30);
Name = path;
@@ -202,25 +203,24 @@ private SftpFileStream(ISftpSession session, string path, FileAccess access, int
_canWrite = (access & FileAccess.Write) == FileAccess.Write;
_handle = handle;
+ _readBufferSize = readBufferSize;
+ _writeBufferSize = writeBufferSize;
+ _position = position;
+ }
- /*
- * Instead of using the specified buffer size as is, we use it to calculate a buffer size
- * that ensures we always receive or send the max. number of bytes in a single SSH_FXP_READ
- * or SSH_FXP_WRITE message.
- */
-
- _readBufferSize = (int)session.CalculateOptimalReadLength((uint)bufferSize);
- _writeBufferSize = (int)session.CalculateOptimalWriteLength((uint)bufferSize, _handle);
+ internal static SftpFileStream Open(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize)
+ {
+ return Open(session, path, mode, access, bufferSize, isAsync: false, CancellationToken.None).GetAwaiter().GetResult();
+ }
- _position = position;
+ internal static Task OpenAsync(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, CancellationToken cancellationToken)
+ {
+ return Open(session, path, mode, access, bufferSize, isAsync: true, cancellationToken);
}
- internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize)
+ private static async Task Open(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, bool isAsync, CancellationToken cancellationToken)
{
- if (session is null)
- {
- throw new SshConnectionException("Client not connected.");
- }
+ Debug.Assert(isAsync || cancellationToken == default);
ThrowHelper.ThrowIfNull(path);
@@ -229,14 +229,10 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc
throw new ArgumentOutOfRangeException(nameof(bufferSize), "Cannot be less than or equal to zero.");
}
- Timeout = TimeSpan.FromSeconds(30);
- Name = path;
-
- // Initialize the object state.
- _session = session;
- _canRead = (access & FileAccess.Read) == FileAccess.Read;
- _canSeek = true;
- _canWrite = (access & FileAccess.Write) == FileAccess.Write;
+ if (session is null)
+ {
+ throw new SshConnectionException("Client not connected.");
+ }
var flags = Flags.None;
@@ -284,16 +280,7 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc
flags |= Flags.Append | Flags.CreateNewOrOpen;
break;
case FileMode.Create:
- _handle = _session.RequestOpen(path, flags | Flags.Truncate, nullOnError: true);
- if (_handle is null)
- {
- flags |= Flags.CreateNew;
- }
- else
- {
- flags |= Flags.Truncate;
- }
-
+ flags |= Flags.CreateNewOrOpen | Flags.Truncate;
break;
case FileMode.CreateNew:
flags |= Flags.CreateNew;
@@ -310,7 +297,16 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc
throw new ArgumentOutOfRangeException(nameof(mode));
}
- _handle ??= _session.RequestOpen(path, flags);
+ byte[] handle;
+
+ if (isAsync)
+ {
+ handle = await session.RequestOpenAsync(path, flags, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ handle = session.RequestOpen(path, flags);
+ }
/*
* Instead of using the specified buffer size as is, we use it to calculate a buffer size
@@ -318,119 +314,27 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc
* or SSH_FXP_WRITE message.
*/
- _readBufferSize = (int)session.CalculateOptimalReadLength((uint)bufferSize);
- _writeBufferSize = (int)session.CalculateOptimalWriteLength((uint)bufferSize, _handle);
+ var readBufferSize = (int)session.CalculateOptimalReadLength((uint)bufferSize);
+ var writeBufferSize = (int)session.CalculateOptimalWriteLength((uint)bufferSize, handle);
+ long position = 0;
if (mode == FileMode.Append)
{
- var attributes = _session.RequestFStat(_handle, nullOnError: false);
- _position = attributes.Size;
- }
- }
-
- internal static async Task OpenAsync(ISftpSession session, string path, FileMode mode, FileAccess access, int bufferSize, CancellationToken cancellationToken)
- {
- if (session is null)
- {
- throw new SshConnectionException("Client not connected.");
- }
-
- ThrowHelper.ThrowIfNull(path);
-
- if (bufferSize <= 0)
- {
- throw new ArgumentOutOfRangeException(nameof(bufferSize), "Cannot be less than or equal to zero.");
- }
-
- var flags = Flags.None;
-
- switch (access)
- {
- case FileAccess.Read:
- flags |= Flags.Read;
- break;
- case FileAccess.Write:
- flags |= Flags.Write;
- break;
- case FileAccess.ReadWrite:
- flags |= Flags.Read;
- flags |= Flags.Write;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(access));
- }
-
- if ((access & FileAccess.Read) == FileAccess.Read && mode == FileMode.Append)
- {
- throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
- "{0} mode can be requested only when combined with write-only access.",
- mode.ToString("G")),
- nameof(mode));
- }
+ SftpFileAttributes attributes;
- if ((access & FileAccess.Write) != FileAccess.Write)
- {
- if (mode is FileMode.Create or FileMode.CreateNew or FileMode.Truncate or FileMode.Append)
+ if (isAsync)
{
- throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
- "Combining {0}: {1} with {2}: {3} is invalid.",
- nameof(FileMode),
- mode,
- nameof(FileAccess),
- access),
- nameof(mode));
+ attributes = await session.RequestFStatAsync(handle, cancellationToken).ConfigureAwait(false);
}
- }
-
- switch (mode)
- {
- case FileMode.Append:
- flags |= Flags.Append | Flags.CreateNewOrOpen;
- break;
- case FileMode.Create:
- flags |= Flags.CreateNewOrOpen | Flags.Truncate;
- break;
- case FileMode.CreateNew:
- flags |= Flags.CreateNew;
- break;
- case FileMode.Open:
- break;
- case FileMode.OpenOrCreate:
- flags |= Flags.CreateNewOrOpen;
- break;
- case FileMode.Truncate:
- flags |= Flags.Truncate;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(mode));
- }
-
- var handle = await session.RequestOpenAsync(path, flags, cancellationToken).ConfigureAwait(false);
-
- long position = 0;
- if (mode == FileMode.Append)
- {
- try
+ else
{
- var attributes = await session.RequestFStatAsync(handle, cancellationToken).ConfigureAwait(false);
- position = attributes.Size;
+ attributes = session.RequestFStat(handle, nullOnError: false);
}
- catch
- {
- try
- {
- await session.RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false);
- }
- catch
- {
- // The original exception is presumably more informative, so we just ignore this one.
- }
- throw;
- }
+ position = attributes.Size;
}
- return new SftpFileStream(session, path, access, bufferSize, handle, position);
+ return new SftpFileStream(session, path, access, readBufferSize, writeBufferSize, handle, position);
}
///
diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs
index b63ed01dd..1de63eaf2 100644
--- a/src/Renci.SshNet/Sftp/SftpSession.cs
+++ b/src/Renci.SshNet/Sftp/SftpSession.cs
@@ -90,7 +90,7 @@ public void ChangeDirectory(string path)
/// The new working directory.
/// The token to monitor for cancellation requests.
/// A that tracks the asynchronous change working directory request.
- public async Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken = default)
+ public async Task ChangeDirectoryAsync(string path, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -1469,7 +1469,7 @@ public void RequestMkDir(string path)
/// The path.
/// The to observe.
/// A that represents the asynchronous SSH_FXP_MKDIR operation.
- public Task RequestMkDirAsync(string path, CancellationToken cancellationToken = default)
+ public Task RequestMkDirAsync(string path, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
@@ -1529,7 +1529,7 @@ public void RequestRmDir(string path)
}
///
- public Task RequestRmDirAsync(string path, CancellationToken cancellationToken = default)
+ public Task RequestRmDirAsync(string path, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs
index f220bae24..a5e4fee9b 100644
--- a/src/Renci.SshNet/SftpClient.cs
+++ b/src/Renci.SshNet/SftpClient.cs
@@ -1423,7 +1423,7 @@ public StreamWriter AppendText(string path, Encoding encoding)
CheckDisposed();
ThrowHelper.ThrowIfNull(encoding);
- return new StreamWriter(new SftpFileStream(_sftpSession, path, FileMode.Append, FileAccess.Write, (int)_bufferSize), encoding);
+ return new StreamWriter(Open(path, FileMode.Append, FileAccess.Write), encoding);
}
///
@@ -1442,9 +1442,7 @@ public StreamWriter AppendText(string path, Encoding encoding)
///
public SftpFileStream Create(string path)
{
- CheckDisposed();
-
- return new SftpFileStream(_sftpSession, path, FileMode.Create, FileAccess.ReadWrite, (int)_bufferSize);
+ return Create(path, (int)_bufferSize);
}
///
@@ -1466,7 +1464,7 @@ public SftpFileStream Create(string path, int bufferSize)
{
CheckDisposed();
- return new SftpFileStream(_sftpSession, path, FileMode.Create, FileAccess.ReadWrite, bufferSize);
+ return SftpFileStream.Open(_sftpSession, path, FileMode.Create, FileAccess.ReadWrite, bufferSize);
}
///
@@ -1642,7 +1640,7 @@ public SftpFileStream Open(string path, FileMode mode, FileAccess access)
{
CheckDisposed();
- return new SftpFileStream(_sftpSession, path, mode, access, (int)_bufferSize);
+ return SftpFileStream.Open(_sftpSession, path, mode, access, (int)_bufferSize);
}
///
@@ -1662,14 +1660,6 @@ public SftpFileStream Open(string path, FileMode mode, FileAccess access)
public Task OpenAsync(string path, FileMode mode, FileAccess access, CancellationToken cancellationToken)
{
CheckDisposed();
- ThrowHelper.ThrowIfNull(path);
-
- if (_sftpSession is null)
- {
- throw new SshConnectionException("Client not connected.");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
return SftpFileStream.OpenAsync(_sftpSession, path, mode, access, (int)_bufferSize, cancellationToken);
}
@@ -1719,9 +1709,7 @@ public StreamReader OpenText(string path)
///
public SftpFileStream OpenWrite(string path)
{
- CheckDisposed();
-
- return new SftpFileStream(_sftpSession, path, FileMode.OpenOrCreate, FileAccess.Write, (int)_bufferSize);
+ return Open(path, FileMode.OpenOrCreate, FileAccess.Write);
}
///
diff --git a/src/Renci.SshNet/SubsystemSession.cs b/src/Renci.SshNet/SubsystemSession.cs
index c83cd6e02..a632f41e8 100644
--- a/src/Renci.SshNet/SubsystemSession.cs
+++ b/src/Renci.SshNet/SubsystemSession.cs
@@ -18,7 +18,7 @@ internal abstract class SubsystemSession : ISubsystemSession
{
///
/// Holds the number of system wait handles that are returned as the leading entries in the array returned
- /// in .
+ /// in .
///
private const int SystemWaitHandleCount = 3;
@@ -335,124 +335,7 @@ public void Dispose()
}
}
- ///
- /// Blocks the current thread until the specified gets signaled, using a
- /// 32-bit signed integer to specify the time interval in milliseconds.
- ///
- /// The handle to wait for.
- /// To number of milliseconds to wait for to get signaled, or -1 to wait indefinitely.
- ///
- /// if received a signal within the specified timeout;
- /// otherwise, .
- ///
- /// The connection was closed by the server.
- /// The channel was closed.
- ///
- /// The blocking wait is also interrupted when either the established channel is closed, the current
- /// session is disconnected or an unexpected occurred while processing a channel
- /// or session event.
- ///
- public bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout)
- {
- var waitHandles = new[]
- {
- _errorOccurredWaitHandle,
- _sessionDisconnectedWaitHandle,
- _channelClosedWaitHandle,
- waitHandle
- };
-
- var result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout);
- switch (result)
- {
- case 0:
- ExceptionDispatchInfo.Capture(_exception).Throw();
- return false; // unreached
- case 1:
- throw new SshException("Connection was closed by the server.");
- case 2:
- throw new SshException("Channel was closed.");
- case 3:
- return true;
- case WaitHandle.WaitTimeout:
- return false;
- default:
- throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "WaitAny return value '{0}' is not implemented.", result));
- }
- }
-
- ///
- /// Blocks the current thread until the specified gets signaled, using a
- /// 32-bit signed integer to specify the time interval in milliseconds.
- ///
- /// The first handle to wait for.
- /// The second handle to wait for.
- /// To number of milliseconds to wait for a to get signaled, or -1 to wait indefinitely.
- ///
- /// 0 if received a signal within the specified timeout, and 1
- /// if received a signal within the specified timeout.
- ///
- /// The connection was closed by the server.
- /// The channel was closed.
- /// The handle did not get signaled within the specified timeout.
- ///
- ///
- /// The blocking wait is also interrupted when either the established channel is closed, the current
- /// session is disconnected or an unexpected occurred while processing a channel
- /// or session event.
- ///
- ///
- /// When both and are signaled during the call,
- /// then 0 is returned.
- ///
- ///
- public int WaitAny(WaitHandle waitHandleA, WaitHandle waitHandleB, int millisecondsTimeout)
- {
- var waitHandles = new[]
- {
- _errorOccurredWaitHandle,
- _sessionDisconnectedWaitHandle,
- _channelClosedWaitHandle,
- waitHandleA,
- waitHandleB
- };
-
- var result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout);
- switch (result)
- {
- case 0:
- ExceptionDispatchInfo.Capture(_exception).Throw();
- return -1; // unreached
- case 1:
- throw new SshException("Connection was closed by the server.");
- case 2:
- throw new SshException("Channel was closed.");
- case 3:
- return 0;
- case 4:
- return 1;
- case WaitHandle.WaitTimeout:
- throw new SshOperationTimeoutException("Operation has timed out.");
- default:
- throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "WaitAny return value '{0}' is not implemented.", result));
- }
- }
-
- ///
- /// Waits for any of the elements in the specified array to receive a signal, using a 32-bit signed
- /// integer to specify the time interval.
- ///
- /// A array - constructed using - containing the objects to wait for.
- /// To number of milliseconds to wait for a to get signaled, or -1 to wait indefinitely.
- ///
- /// The array index of the first non-system object that satisfied the wait.
- ///
- /// The connection was closed by the server.
- /// The channel was closed.
- /// No object satisfied the wait and a time interval equivalent to has passed.
- ///
- /// For the return value, the index of the first non-system object is considered to be zero.
- ///
+ ///
public int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout)
{
var result = WaitHandle.WaitAny(waitHandles, millisecondsTimeout);
@@ -472,15 +355,7 @@ public int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout)
}
}
- ///
- /// Creates a array that is composed of system objects and the specified
- /// elements.
- ///
- /// The first to wait for.
- /// The second to wait for.
- ///
- /// A array that is composed of system objects and the specified elements.
- ///
+ ///
public WaitHandle[] CreateWaitHandleArray(WaitHandle waitHandle1, WaitHandle waitHandle2)
{
return new WaitHandle[]
@@ -493,29 +368,6 @@ public WaitHandle[] CreateWaitHandleArray(WaitHandle waitHandle1, WaitHandle wai
};
}
- ///
- /// Creates a array that is composed of system objects and the specified
- /// elements.
- ///
- /// A array containing the objects to wait for.
- ///
- /// A array that is composed of system objects and the specified elements.
- ///
- public WaitHandle[] CreateWaitHandleArray(params WaitHandle[] waitHandles)
- {
- var array = new WaitHandle[waitHandles.Length + SystemWaitHandleCount];
- array[0] = _errorOccurredWaitHandle;
- array[1] = _sessionDisconnectedWaitHandle;
- array[2] = _channelClosedWaitHandle;
-
- for (var i = 0; i < waitHandles.Length; i++)
- {
- array[i + SystemWaitHandleCount] = waitHandles[i];
- }
-
- return array;
- }
-
private void Session_Disconnected(object sender, EventArgs e)
{
_ = _sessionDisconnectedWaitHandle?.Set();
diff --git a/test/Renci.SshNet.IntegrationTests/SftpTests.cs b/test/Renci.SshNet.IntegrationTests/SftpTests.cs
index 64ca1521b..603c726a5 100644
--- a/test/Renci.SshNet.IntegrationTests/SftpTests.cs
+++ b/test/Renci.SshNet.IntegrationTests/SftpTests.cs
@@ -49,7 +49,8 @@ public void Sftp_UploadFile_FileStream(int size)
using (var memoryStream = new MemoryStream(size))
{
- client.DownloadFile(remoteFile, memoryStream);
+ DownloadFileRandomMethod(client, remoteFile, memoryStream);
+
memoryStream.Position = 0;
Assert.AreEqual(CreateFileHash(file), CreateHash(memoryStream));
}
@@ -164,7 +165,7 @@ public void Sftp_BeginUploadFile()
// check uploaded file
using (var memoryStream = new MemoryStream())
{
- client.DownloadFile(remoteFile, memoryStream);
+ DownloadFileRandomMethod(client, remoteFile, memoryStream);
memoryStream.Position = 0;
var remoteContent = Encoding.ASCII.GetString(memoryStream.ToArray());
Assert.AreEqual(content, remoteContent);
@@ -300,18 +301,12 @@ public void Sftp_Create_FileDoesNotExist()
{
using (var fs = client.Create(remoteFile))
{
- byte[] buffer = new byte[Math.Min(client.BufferSize, imageStream.Length)];
- int bytesRead;
-
- while ((bytesRead = imageStream.Read(buffer, offset: 0, buffer.Length)) > 0)
- {
- fs.Write(buffer, offset: 0, bytesRead);
- }
+ imageStream.CopyTo(fs);
}
using (var memoryStream = new MemoryStream())
{
- client.DownloadFile(remoteFile, memoryStream);
+ DownloadFileRandomMethod(client, remoteFile, memoryStream);
memoryStream.Position = 0;
imageStream.Position = 0;
@@ -3192,7 +3187,7 @@ public void Sftp_BeginUploadFile_InputAndPath_FileDoesNotExist()
using (var downloadMemoryStream = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloadMemoryStream);
+ DownloadFileRandomMethod(client, remoteFile, downloadMemoryStream);
downloadMemoryStream.Position = 0;
@@ -3242,7 +3237,7 @@ public void Sftp_BeginUploadFile_InputAndPath_ExistingFile()
using (var downloadMemoryStream = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloadMemoryStream);
+ DownloadFileRandomMethod(client, remoteFile, downloadMemoryStream);
downloadMemoryStream.Position = 0;
@@ -3328,7 +3323,7 @@ public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsFalse_F
using (var downloadMemoryStream = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloadMemoryStream);
+ DownloadFileRandomMethod(client, remoteFile, downloadMemoryStream);
downloadMemoryStream.Position = 0;
@@ -3463,7 +3458,7 @@ public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsTrue_Fi
using (var downloadMemoryStream = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloadMemoryStream);
+ DownloadFileRandomMethod(client, remoteFile, downloadMemoryStream);
downloadMemoryStream.Position = 0;
@@ -3515,7 +3510,7 @@ public void Sftp_BeginUploadFile_InputAndPathAndCanOverride_CanOverrideIsTrue_Ex
using (var downloadMemoryStream = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloadMemoryStream);
+ DownloadFileRandomMethod(client, remoteFile, downloadMemoryStream);
downloadMemoryStream.Position = 0;
@@ -3556,20 +3551,25 @@ public void Sftp_UploadAndDownloadBigFile()
{
var memoryStream = CreateMemoryStream(size);
memoryStream.Position = 0;
+ var expectedHash = CreateHash(memoryStream);
+ memoryStream.Position = 0;
client.UploadFile(memoryStream, remoteFile);
+ memoryStream.SetLength(0);
+
var stopwatch = new Stopwatch();
stopwatch.Start();
- // check uploaded file
- memoryStream = new MemoryStream();
client.DownloadFile(remoteFile, memoryStream);
- Assert.AreEqual(size, memoryStream.Length);
-
stopwatch.Stop();
+ Assert.AreEqual(size, memoryStream.Length);
+ memoryStream.Position = 0;
+ var actualHash = CreateHash(memoryStream);
+ Assert.AreEqual(expectedHash, actualHash);
+
Console.WriteLine(@"Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
Console.WriteLine(@"Transfer speed: {0:N2} KB/s",
CalculateTransferSpeed(memoryStream.Length, stopwatch.ElapsedMilliseconds));
@@ -4140,46 +4140,6 @@ public async Task Sftp_ChangeDirectory_DirectoryExistsAsync()
}
}
- [TestMethod]
- public void Sftp_DownloadFile_MemoryStream()
- {
- const int fileSize = 500 * 1024;
-
- using (var client = new SftpClient(_connectionInfoFactory.Create()))
- {
- client.Connect();
-
- var remoteFile = GenerateUniqueRemoteFileName();
-
- SftpCreateRemoteFile(client, remoteFile, fileSize);
-
- try
- {
- using (var memoryStream = new MemoryStream())
- {
- var stopwatch = new Stopwatch();
- stopwatch.Start();
-
- client.DownloadFile(remoteFile, memoryStream);
- stopwatch.Stop();
-
- var transferSpeed = CalculateTransferSpeed(memoryStream.Length, stopwatch.ElapsedMilliseconds);
- Console.WriteLine(@"Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
- Console.WriteLine(@"Transfer speed: {0:N2} KB/s", transferSpeed);
-
- Assert.AreEqual(fileSize, memoryStream.Length);
- }
- }
- finally
- {
- if (client.Exists(remoteFile))
- {
- client.DeleteFile(remoteFile);
- }
- }
- }
- }
-
[TestMethod]
public void Sftp_SubsystemExecution_Failed()
{
@@ -4229,6 +4189,75 @@ public void Sftp_SubsystemExecution_Failed()
[TestMethod]
public void Sftp_SftpFileStream_ReadAndWrite()
+ {
+ TestReadAndWrite(
+ (s, buffer, offset, count) => s.Read(buffer, offset, count),
+ (s, buffer, offset, count) => s.Write(buffer, offset, count));
+ }
+
+ [TestMethod]
+ public void Sftp_SftpFileStream_ReadAndWriteByte()
+ {
+ TestReadAndWrite(
+ read: (s, buffer, offset, count) =>
+ {
+ int bytesRead = 0;
+ int b;
+
+ while (bytesRead < count && (b = s.ReadByte()) != -1)
+ {
+ buffer[offset + bytesRead] = (byte)b;
+ bytesRead++;
+ }
+
+ return bytesRead;
+ },
+ write: (s, buffer, offset, count) =>
+ {
+ for (int i = 0; i < count; i++)
+ {
+ s.WriteByte(buffer[offset + i]);
+ }
+ });
+ }
+
+ [TestMethod]
+ public void Sftp_SftpFileStream_ReadAndWriteAsync()
+ {
+ TestReadAndWrite(
+ (s, buffer, offset, count) => s.ReadAsync(buffer, offset, count).GetAwaiter().GetResult(),
+ (s, buffer, offset, count) => s.WriteAsync(buffer, offset, count).GetAwaiter().GetResult());
+ }
+
+ [TestMethod]
+ public void Sftp_SftpFileStream_ReadAndWriteBeginEnd()
+ {
+ TestReadAndWrite(
+ (s, buffer, offset, count) => s.EndRead(s.BeginRead(buffer, offset, count, null, null)),
+ (s, buffer, offset, count) => s.EndWrite(s.BeginWrite(buffer, offset, count, null, null)));
+ }
+
+#if NET
+ [TestMethod]
+ public void Sftp_SftpFileStream_ReadAndWriteSpan()
+ {
+ TestReadAndWrite(
+ (s, buffer, offset, count) => s.Read(buffer.AsSpan(offset, count)),
+ (s, buffer, offset, count) => s.Write(buffer.AsSpan(offset, count)));
+ }
+
+ [TestMethod]
+ public void Sftp_SftpFileStream_ReadAndWriteAsyncMemory()
+ {
+ TestReadAndWrite(
+ (s, buffer, offset, count) => s.ReadAsync(buffer.AsMemory(offset, count)).AsTask().GetAwaiter().GetResult(),
+ (s, buffer, offset, count) => s.WriteAsync(buffer.AsMemory(offset, count)).AsTask().GetAwaiter().GetResult());
+ }
+#endif
+
+ private void TestReadAndWrite(
+ Func read,
+ Action write)
{
using (var client = new SftpClient(_connectionInfoFactory.Create()))
{
@@ -4245,60 +4274,69 @@ public void Sftp_SftpFileStream_ReadAndWrite()
{
using (var s = client.Open(remoteFile, FileMode.CreateNew, FileAccess.Write))
{
- s.Write(new byte[] { 5, 4, 3, 2, 1 }, 1, 3);
+ Assert.IsFalse(s.CanRead);
+ Assert.IsTrue(s.CanWrite);
+
+ write(s, new byte[] { 5, 4, 3, 2, 1 }, 1, 3);
}
// switch from read to write mode
using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite))
{
- Assert.AreEqual(4, s.ReadByte());
- Assert.AreEqual(3, s.ReadByte());
+ Assert.IsTrue(s.CanRead);
+ Assert.IsTrue(s.CanWrite);
+
+ Assert.AreEqual(4, ReadByte(s));
+ Assert.AreEqual(3, ReadByte(s));
Assert.AreEqual(2, s.Position);
- s.WriteByte(7);
- s.Write(new byte[] { 8, 9, 10, 11, 12 }, 1, 3);
+ WriteByte(s, 7);
+ write(s, new byte[] { 8, 9, 10, 11, 12 }, 1, 3);
Assert.AreEqual(6, s.Position);
}
using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.Read))
{
+ Assert.IsTrue(s.CanRead);
+ Assert.IsFalse(s.CanWrite);
+
Assert.AreEqual(6, s.Length);
var buffer = new byte[s.Length];
- Assert.AreEqual(6, s.Read(buffer, offset: 0, buffer.Length));
+ Assert.AreEqual(6, read(s, buffer, 0, buffer.Length));
CollectionAssert.AreEqual(new byte[] { 4, 3, 7, 9, 10, 11 }, buffer);
// Ensure we've reached end of the stream
- Assert.AreEqual(-1, s.ReadByte());
+ Assert.AreEqual(-1, ReadByte(s));
}
// switch from read to write mode, and back to read mode and finally
// append a byte
using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite))
{
- Assert.AreEqual(4, s.ReadByte());
- Assert.AreEqual(3, s.ReadByte());
- Assert.AreEqual(7, s.ReadByte());
+ Assert.AreEqual(4, ReadByte(s));
+ Assert.AreEqual(3, ReadByte(s));
+ Assert.AreEqual(7, ReadByte(s));
- s.Write(new byte[] { 0, 1, 6, 4 }, 1, 2);
+ write(s, new byte[] { 0, 1, 6, 4 }, 1, 2);
- Assert.AreEqual(11, s.ReadByte());
+ Assert.AreEqual(11, ReadByte(s));
// Ensure we've reached end of the stream
- Assert.AreEqual(-1, s.ReadByte());
+ Assert.AreEqual(-1, ReadByte(s));
- s.WriteByte(12);
+ WriteByte(s, 12);
}
// switch from write to read mode, and back to write mode
using (var s = client.Open(remoteFile, FileMode.Open, FileAccess.ReadWrite))
{
- s.WriteByte(5);
- Assert.AreEqual(3, s.ReadByte());
- s.WriteByte(13);
+ WriteByte(s, 5);
+ Assert.AreEqual(3, ReadByte(s));
+ WriteByte(s, 13);
Assert.AreEqual(3, s.Position);
}
@@ -4308,12 +4346,12 @@ public void Sftp_SftpFileStream_ReadAndWrite()
Assert.AreEqual(7, s.Length);
var buffer = new byte[s.Length];
- Assert.AreEqual(7, s.Read(buffer, offset: 0, buffer.Length));
+ Assert.AreEqual(7, read(s, buffer, 0, buffer.Length));
CollectionAssert.AreEqual(new byte[] { 5, 3, 13, 1, 6, 11, 12 }, buffer);
// Ensure we've reached end of the stream
- Assert.AreEqual(-1, s.ReadByte());
+ Assert.AreEqual(-1, ReadByte(s));
}
}
finally
@@ -4324,6 +4362,18 @@ public void Sftp_SftpFileStream_ReadAndWrite()
}
}
}
+
+ int ReadByte(SftpFileStream s)
+ {
+ var buffer = new byte[1];
+ var bytesRead = read(s, buffer, 0, 1);
+ return bytesRead == 0 ? -1 : buffer[0];
+ }
+
+ void WriteByte(SftpFileStream s, byte b)
+ {
+ write(s, [b], 0, 1);
+ }
}
[TestMethod]
@@ -4502,6 +4552,7 @@ public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginBegin()
Assert.AreEqual(3, newPosition);
Assert.AreEqual(3, fs.Position);
+ Assert.AreEqual(1, fs.Length);
}
using (var fs = client.OpenRead(remoteFile))
@@ -4529,6 +4580,7 @@ public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginBegin()
Assert.AreEqual(700, newPosition);
Assert.AreEqual(700, fs.Position);
+ Assert.AreEqual(1, fs.Length);
}
using (var fs = client.OpenRead(remoteFile))
@@ -4563,6 +4615,8 @@ public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginBegin()
Assert.AreEqual(seekOffset, fs.Position);
fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+
+ Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length);
}
using (var fs = client.OpenRead(remoteFile))
@@ -4605,6 +4659,8 @@ public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginBegin()
Assert.AreEqual(seekOffset, fs.Position);
fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+
+ Assert.AreEqual(seekOffset + writeBuffer.Length, fs.Length);
}
using (var fs = client.OpenRead(remoteFile))
@@ -4742,6 +4798,7 @@ public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginEnd()
Assert.AreEqual(4, newPosition);
Assert.AreEqual(4, fs.Position);
+ Assert.AreEqual(1, fs.Length);
}
using (var fs = client.OpenRead(remoteFile))
@@ -4769,6 +4826,7 @@ public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginEnd()
Assert.AreEqual(701, newPosition);
Assert.AreEqual(701, fs.Position);
+ Assert.AreEqual(1, fs.Length);
}
using (var fs = client.OpenRead(remoteFile))
@@ -4803,6 +4861,8 @@ public void Sftp_SftpFileStream_Seek_BeyondEndOfFile_SeekOriginEnd()
Assert.AreEqual(4, fs.Position);
fs.Write(writeBuffer, offset: 0, writeBuffer.Length);
+
+ Assert.AreEqual(1 + seekOffset + writeBuffer.Length, fs.Length);
}
using (var fs = client.OpenRead(remoteFile))
@@ -5455,7 +5515,7 @@ public void Sftp_SftpFileStream_SetLength_FileDoesNotExist()
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(new byte[size]), CreateHash(downloaded));
}
@@ -5495,6 +5555,9 @@ public void Sftp_Open_Append_Write_ExistingFile()
using (var s = client.Open(remoteFile, FileMode.Append, FileAccess.Write))
{
+ Assert.IsFalse(s.CanRead);
+ Assert.IsTrue(s.CanWrite);
+
var buffer = new byte[] { 0x05, 0x0f, 0x0d, 0x0a, 0x04 };
s.Write(buffer, offset: 0, buffer.Length);
input.Write(buffer, offset: 0, buffer.Length);
@@ -5502,7 +5565,7 @@ public void Sftp_Open_Append_Write_ExistingFile()
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
input.Position = 0;
downloaded.Position = 0;
@@ -5562,7 +5625,7 @@ public void Sftp_Open_Append_Write_FileDoesNotExist()
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
}
@@ -5579,8 +5642,6 @@ public void Sftp_Open_Append_Write_FileDoesNotExist()
}
}
-
-
[TestMethod]
public void Sftp_Open_PathAndMode_ModeIsCreate_FileDoesNotExist()
{
@@ -5619,12 +5680,15 @@ public void Sftp_Open_PathAndMode_ModeIsCreate_FileDoesNotExist()
using (var s = client.Open(remoteFile, FileMode.Create))
{
+ Assert.IsTrue(s.CanRead);
+ Assert.IsTrue(s.CanWrite);
+
s.Write(content, offset: 0, content.Length);
}
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
}
@@ -5730,7 +5794,7 @@ public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsReadWrite_FileDo
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
}
@@ -5823,7 +5887,7 @@ public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsWrite_ExistingFi
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(newContent), CreateHash(downloaded));
@@ -5882,7 +5946,7 @@ public void Sftp_Open_PathAndModeAndAccess_ModeIsCreate_AccessIsWrite_FileDoesNo
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
}
@@ -5940,7 +6004,7 @@ public void Sftp_Open_CreateNew_Write_ExistingFile()
// Verify that the file was not modified
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
input.Position = 0;
downloaded.Position = 0;
@@ -6000,7 +6064,7 @@ public void Sftp_Open_CreateNew_Write_FileDoesNotExist()
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
}
@@ -6048,7 +6112,7 @@ public void Sftp_Open_Open_Write_ExistingFile()
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(expectedContent), CreateHash(downloaded));
@@ -6138,7 +6202,7 @@ public void Sftp_Open_OpenOrCreate_Write_ExistingFile()
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(expectedContent), CreateHash(downloaded));
@@ -6197,7 +6261,7 @@ public void Sftp_Open_OpenOrCreate_Write_FileDoesNotExist()
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
downloaded.Position = 0;
Assert.AreEqual(CreateHash(content), CreateHash(downloaded));
}
@@ -6214,12 +6278,6 @@ public void Sftp_Open_OpenOrCreate_Write_FileDoesNotExist()
}
}
-
-
-
-
-
-
[TestMethod]
public void Sftp_Open_Truncate_Write_ExistingFile()
{
@@ -6255,7 +6313,7 @@ public void Sftp_Open_Truncate_Write_ExistingFile()
using (var downloaded = new MemoryStream())
{
- client.DownloadFile(remoteFile, downloaded);
+ DownloadFileRandomMethod(client, remoteFile, downloaded);
input.Position = 0;
downloaded.Position = 0;
@@ -6311,49 +6369,6 @@ public void Sftp_Open_Truncate_Write_FileDoesNotExist()
}
}
- [TestMethod]
- public void Sftp_OpenRead()
- {
- const int fileSize = 5 * 1024 * 1024;
-
- using (var client = new SftpClient(_connectionInfoFactory.Create()))
- {
- client.Connect();
-
- var remoteFile = GenerateUniqueRemoteFileName();
-
- SftpCreateRemoteFile(client, remoteFile, fileSize);
-
- try
- {
- using (var s = client.OpenRead(remoteFile))
- {
- var buffer = new byte[s.Length];
-
- var stopwatch = new Stopwatch();
- stopwatch.Start();
-
- var bytesRead = s.Read(buffer, offset: 0, buffer.Length);
-
- stopwatch.Stop();
-
- var transferSpeed = CalculateTransferSpeed(bytesRead, stopwatch.ElapsedMilliseconds);
- Console.WriteLine(@"Elapsed: {0} ms", stopwatch.ElapsedMilliseconds);
- Console.WriteLine(@"Transfer speed: {0:N2} KB/s", transferSpeed);
-
- Assert.AreEqual(fileSize, bytesRead);
- }
- }
- finally
- {
- if (client.Exists(remoteFile))
- {
- client.DeleteFile(remoteFile);
- }
- }
- }
- }
-
[TestMethod]
public void Sftp_SetLastAccessTime()
{
@@ -6468,17 +6483,63 @@ private static IEnumerable