diff --git a/src/Renci.SshNet/Sftp/ISftpSession.cs b/src/Renci.SshNet/Sftp/ISftpSession.cs index 4a804a35e..0349bcd8c 100644 --- a/src/Renci.SshNet/Sftp/ISftpSession.cs +++ b/src/Renci.SshNet/Sftp/ISftpSession.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Renci.SshNet.Common; +using Renci.SshNet.Sftp.Requests; using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp @@ -325,14 +326,19 @@ internal interface ISftpSession : ISubsystemSession /// the zero-based offset in at which to begin taking bytes to write. /// The length (in bytes) of the data to write. /// The wait event handle if needed. - /// The callback to invoke when the write has completed. void RequestWrite(byte[] handle, ulong serverOffset, byte[] data, int offset, int length, - AutoResetEvent wait, - Action writeCompleted = null); + AutoResetEvent wait); + + /// + /// Performs SSH_FXP_WRITE request. + /// + /// The buffer. + /// The callback to invoke when the write has completed. + void RequestWrite(SftpWriteRequestBuffer buffer, Action writeCompleted); /// /// Asynchronouly performs a SSH_FXP_WRITE request. diff --git a/src/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs b/src/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs index 6627513ae..0725d42dc 100644 --- a/src/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs +++ b/src/Renci.SshNet/Sftp/Requests/SftpWriteRequest.cs @@ -1,17 +1,27 @@ using System; +using System.Buffers.Binary; +using Renci.SshNet.Common; using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Sftp.Requests { internal sealed class SftpWriteRequest : SftpRequest { + private readonly SftpWriteRequestBuffer _buffer; + public override SftpMessageTypes SftpMessageType { get { return SftpMessageTypes.Write; } } - public byte[] Handle { get; private set; } + public ReadOnlySpan Handle + { + get + { + return _buffer.Handle; + } + } /// /// Gets the zero-based offset (in bytes) relative to the beginning of the file that the write @@ -21,7 +31,13 @@ public override SftpMessageTypes SftpMessageType /// The zero-based offset (in bytes) relative to the beginning of the file that the write must /// start at. /// - public ulong ServerFileOffset { get; private set; } + public ulong ServerFileOffset + { + get + { + return _buffer.ServerFileOffset; + } + } /// /// Gets the buffer holding the data to write. @@ -29,74 +45,55 @@ public override SftpMessageTypes SftpMessageType /// /// The buffer holding the data to write. /// - public byte[] Data { get; private set; } - - /// - /// Gets the zero-based offset in at which to begin taking bytes to - /// write. - /// - /// - /// The zero-based offset in at which to begin taking bytes to write. - /// - public int Offset { get; private set; } - - /// - /// Gets the length (in bytes) of the data to write. - /// - /// - /// The length (in bytes) of the data to write. - /// - public int Length { get; private set; } + public ReadOnlySpan Data + { + get + { + return _buffer.Data.AsSpan(0, _buffer.DataLength); + } + } protected override int BufferCapacity { get { - var capacity = base.BufferCapacity; - capacity += 4; // Handle length - capacity += Handle.Length; // Handle - capacity += 8; // ServerFileOffset length - capacity += 4; // Data length - capacity += Length; // Data - return capacity; + return _buffer.ActiveBytes.Count; } } public SftpWriteRequest(uint protocolVersion, - uint requestId, - byte[] handle, - ulong serverFileOffset, - byte[] data, - int offset, - int length, + SftpWriteRequestBuffer buffer, Action statusAction) - : base(protocolVersion, requestId, statusAction) + : base(protocolVersion, buffer.RequestId, statusAction) { - Handle = handle; - ServerFileOffset = serverFileOffset; - Data = data; - Offset = offset; - Length = length; + _buffer = buffer; } protected override void LoadData() { - base.LoadData(); - - Handle = ReadBinary(); - ServerFileOffset = ReadUInt64(); - Data = ReadBinary(); - Offset = 0; - Length = Data.Length; + throw new NotImplementedException(); } protected override void SaveData() { - base.SaveData(); + throw new NotImplementedException(); + } + + protected override void WriteBytes(SshDataStream stream) + { + var activeBuffer = GetBytes(); + + stream.Write(activeBuffer.Array, activeBuffer.Offset, activeBuffer.Count); + } + + public new ArraySegment GetBytes() + { + var activeBuffer = _buffer.ActiveBytes; + + // Write SFTP packet length. + BinaryPrimitives.WriteInt32BigEndian(activeBuffer.AsSpan(), activeBuffer.Count - 4); - WriteBinaryString(Handle); - Write(ServerFileOffset); - WriteBinary(Data, Offset, Length); + return activeBuffer; } } } diff --git a/src/Renci.SshNet/Sftp/Requests/SftpWriteRequestBuffer.cs b/src/Renci.SshNet/Sftp/Requests/SftpWriteRequestBuffer.cs new file mode 100644 index 000000000..9f7582e94 --- /dev/null +++ b/src/Renci.SshNet/Sftp/Requests/SftpWriteRequestBuffer.cs @@ -0,0 +1,149 @@ +#nullable enable +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Diagnostics; + +namespace Renci.SshNet.Sftp.Requests +{ + /// + /// A helper type that wraps a buffer for SFTP write requests. + /// + /// + /// [Sftp packet length, SftpMessageType, RequestId, Handle length, Handle, Server offset, data length, data]. + /// [ 4, 1, 4, 4, ?, 8, 4, ?]. + /// + internal sealed class SftpWriteRequestBuffer : IDisposable + { + private const int MessageTypeOffset = 4; + private const int RequestIdOffset = MessageTypeOffset + 1; + private const int HandleLengthOffset = RequestIdOffset + 4; + private const int HandleOffset = HandleLengthOffset + 4; + + private readonly bool _usePool; + private byte[] _buffer; + + public ArraySegment ActiveBytes + { + get + { + return new(_buffer, 0, HandleOffset + HandleLength + 8 + 4 + DataLength); + } + } + + public SftpWriteRequestBuffer(ReadOnlySpan handle, int dataCapacity, bool usePool = false) + { + Debug.Assert(dataCapacity >= 0); + + var totalCapacity = HandleOffset + handle.Length + 8 + 4 + dataCapacity; + + _usePool = usePool; + + _buffer = usePool + ? ArrayPool.Shared.Rent(totalCapacity) + : new byte[totalCapacity]; + + _buffer[MessageTypeOffset] = (byte)SftpMessageTypes.Write; + + HandleLength = handle.Length; + + handle.CopyTo(_buffer.AsSpan(HandleOffset)); + } + + public SftpWriteRequestBuffer(ReadOnlySpan handle, ulong serverFileOffset, ReadOnlySpan data, bool usePool = false) + : this(handle, data.Length, usePool) + { + ServerFileOffset = serverFileOffset; + + DataLength = data.Length; + + data.CopyTo(Data); + } + + public uint RequestId + { + get + { + return BinaryPrimitives.ReadUInt32BigEndian(_buffer.AsSpan(RequestIdOffset)); + } + set + { + BinaryPrimitives.WriteUInt32BigEndian(_buffer.AsSpan(RequestIdOffset), value); + } + } + + public int HandleLength + { + get + { + return BinaryPrimitives.ReadInt32BigEndian(_buffer.AsSpan(HandleLengthOffset)); + } + private init + { + Debug.Assert(value >= 0); + BinaryPrimitives.WriteInt32BigEndian(_buffer.AsSpan(HandleLengthOffset), value); + } + } + + public ReadOnlySpan Handle + { + get + { + return _buffer.AsSpan(HandleOffset, HandleLength); + } + } + + public ulong ServerFileOffset + { + get + { + return BinaryPrimitives.ReadUInt64BigEndian(_buffer.AsSpan(HandleOffset + HandleLength)); + } + set + { + BinaryPrimitives.WriteUInt64BigEndian(_buffer.AsSpan(HandleOffset + HandleLength), value); + } + } + + public int DataLength + { + get + { + return BinaryPrimitives.ReadInt32BigEndian(_buffer.AsSpan(HandleOffset + HandleLength + 8)); + } + set + { + Debug.Assert(value >= 0); + Debug.Assert(value <= _buffer.Length - (HandleOffset + HandleLength + 8 + 4)); + + BinaryPrimitives.WriteInt32BigEndian(_buffer.AsSpan(HandleOffset + HandleLength + 8), value); + } + } + + /// + /// Gets the space available to write as file data. Does not consider . + /// + public ArraySegment Data + { + get + { + var offset = HandleOffset + HandleLength + 8 + 4; + return new ArraySegment(_buffer, offset, _buffer.Length - offset); + } + } + + public void Dispose() + { + if (_usePool) + { + var buffer = _buffer; + _buffer = null!; + + if (buffer is not null) + { + ArrayPool.Shared.Return(buffer); + } + } + } + } +} diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs index d5648bda4..6b2198053 100644 --- a/src/Renci.SshNet/Sftp/SftpSession.cs +++ b/src/Renci.SshNet/Sftp/SftpSession.cs @@ -87,8 +87,16 @@ public async Task ChangeDirectoryAsync(string path, CancellationToken cancellati internal void SendMessage(SftpMessage sftpMessage) { - var data = sftpMessage.GetBytes(); - SendData(data); + if (sftpMessage is SftpWriteRequest writeRequest) + { + var data = writeRequest.GetBytes(); + SendData(data.Array, data.Offset, data.Count); + } + else + { + var data = sftpMessage.GetBytes(); + SendData(data); + } } /// @@ -579,20 +587,31 @@ public void RequestWrite(byte[] handle, byte[] data, int offset, int length, + AutoResetEvent wait) + { + using var buffer = new SftpWriteRequestBuffer(handle, serverOffset, data.AsSpan(offset, length), usePool: true); + + RequestWrite(buffer, wait, writeCompleted: null); + } + + /// + public void RequestWrite(SftpWriteRequestBuffer buffer, Action writeCompleted) + { + RequestWrite(buffer, wait: null, writeCompleted); + } + + private void RequestWrite(SftpWriteRequestBuffer buffer, AutoResetEvent wait, - Action writeCompleted = null) + Action writeCompleted) { Debug.Assert((wait is null) != (writeCompleted is null), "Should have one parameter or the other."); SftpException exception = null; + buffer.RequestId = NextRequestId; + var request = new SftpWriteRequest(ProtocolVersion, - NextRequestId, - handle, - serverOffset, - data, - offset, - length, + buffer, response => { if (writeCompleted is not null) @@ -629,24 +648,24 @@ public Task RequestWriteAsync(byte[] handle, ulong serverOffset, byte[] data, in var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - SendRequest(new SftpWriteRequest(ProtocolVersion, - NextRequestId, - handle, - serverOffset, - data, - offset, - length, - response => - { - if (response.StatusCode == StatusCode.Ok) - { - _ = tcs.TrySetResult(true); - } - else - { - _ = tcs.TrySetException(GetSftpException(response)); - } - })); + using (var buffer = new SftpWriteRequestBuffer(handle, serverOffset, data.AsSpan(offset, length), usePool: true)) + { + buffer.RequestId = NextRequestId; + + SendRequest(new SftpWriteRequest(ProtocolVersion, + buffer, + response => + { + if (response.StatusCode == StatusCode.Ok) + { + _ = tcs.TrySetResult(true); + } + else + { + _ = tcs.TrySetException(GetSftpException(response)); + } + })); + } return WaitOnHandleAsync(tcs, OperationTimeout, cancellationToken); } diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index 960c6261f..f8d483d2a 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -16,6 +16,7 @@ using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Requests; namespace Renci.SshNet { @@ -2477,7 +2478,13 @@ private async Task InternalUploadFile( ulong offset = 0; // create buffer of optimal length - var buffer = new byte[_sftpSession.CalculateOptimalWriteLength(_bufferSize, handle)]; + var dataCapacity = (int)_sftpSession.CalculateOptimalWriteLength(_bufferSize, handle); + + using var buffer = new SftpWriteRequestBuffer(handle, dataCapacity, usePool: true); + + var dataBuffer = buffer.Data; + + Debug.Assert(dataBuffer.Count >= dataCapacity); var expectedResponses = 0; @@ -2492,11 +2499,11 @@ private async Task InternalUploadFile( { var bytesRead = isAsync #if NET - ? await input.ReadAsync(buffer, cancellationToken).ConfigureAwait(false) + ? await input.ReadAsync(dataBuffer.AsMemory(0, dataCapacity), cancellationToken).ConfigureAwait(false) #else - ? await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false) + ? await input.ReadAsync(dataBuffer.Array, dataBuffer.Offset, dataCapacity, cancellationToken).ConfigureAwait(false) #endif - : input.Read(buffer, 0, buffer.Length); + : input.Read(dataBuffer.Array!, dataBuffer.Offset, dataCapacity); if (bytesRead == 0) { @@ -2510,12 +2517,15 @@ private async Task InternalUploadFile( exception?.Throw(); + buffer.ServerFileOffset = offset; + buffer.DataLength = bytesRead; + var writtenBytes = offset + (ulong)bytesRead; _ = Interlocked.Increment(ref expectedResponses); mres.Reset(); - _sftpSession.RequestWrite(handle, offset, buffer, offset: 0, bytesRead, wait: null, s => + _sftpSession.RequestWrite(buffer, s => { var setHandle = false; diff --git a/src/Renci.SshNet/SubsystemSession.cs b/src/Renci.SshNet/SubsystemSession.cs index 47e37161c..3185cf52c 100644 --- a/src/Renci.SshNet/SubsystemSession.cs +++ b/src/Renci.SshNet/SubsystemSession.cs @@ -165,11 +165,22 @@ public void Disconnect() /// /// The data to be sent. public void SendData(byte[] data) + { + SendData(data, 0, data.Length); + } + + /// + /// Sends data to the subsystem. + /// + /// The data to be sent. + /// The zero-based byte offset in at which to begin sending bytes. + /// The number of bytes to send. + public void SendData(byte[] data, int offset, int count) { ObjectDisposedException.ThrowIf(_isDisposed, this); EnsureSessionIsOpen(); - _channel.SendData(data); + _channel.SendData(data, offset, count); } /// diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpWriteRequestTest.cs b/test/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpWriteRequestTest.cs index 6083455fa..3ff972826 100644 --- a/test/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpWriteRequestTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Sftp/Requests/SftpWriteRequestTest.cs @@ -41,12 +41,16 @@ public void Init() [TestMethod] public void Constructor() { - var request = new SftpWriteRequest(_protocolVersion, _requestId, _handle, _serverFileOffset, _data, _offset, _length, null); - - Assert.AreSame(_data, request.Data); - Assert.AreSame(_handle, request.Handle); - Assert.AreEqual(_length, request.Length); - Assert.AreEqual(_offset, request.Offset); + var request = new SftpWriteRequest( + _protocolVersion, + new SftpWriteRequestBuffer(_handle, _serverFileOffset, _data.AsSpan(_offset, _length)) + { + RequestId = _requestId + }, + statusAction: null); + + CollectionAssert.AreEqual(_data.Take(_offset, _length), request.Data.ToArray()); + CollectionAssert.AreEqual(_handle, request.Handle.ToArray()); Assert.AreEqual(_protocolVersion, request.ProtocolVersion); Assert.AreEqual(_requestId, request.RequestId); Assert.AreEqual(_serverFileOffset, request.ServerFileOffset); @@ -62,12 +66,10 @@ public void Complete_SftpStatusResponse() var request = new SftpWriteRequest( _protocolVersion, - _requestId, - _handle, - _serverFileOffset, - _data, - _offset, - _length, + new SftpWriteRequestBuffer(_handle, _serverFileOffset, _data.AsSpan(_offset, _length)) + { + RequestId = _requestId + }, statusAction); request.Complete(statusResponse); @@ -79,9 +81,15 @@ public void Complete_SftpStatusResponse() [TestMethod] public void GetBytes() { - var request = new SftpWriteRequest(_protocolVersion, _requestId, _handle, _serverFileOffset, _data, _offset, _length, null); + var request = new SftpWriteRequest( + _protocolVersion, + new SftpWriteRequestBuffer(_handle, _serverFileOffset, _data.AsSpan(_offset, _length)) + { + RequestId = _requestId + }, + statusAction: null); - var bytes = request.GetBytes(); + var bytes = ((SftpRequest)request).GetBytes(); var expectedBytesLength = 0; expectedBytesLength += 4; // Length @@ -114,6 +122,8 @@ public void GetBytes() Assert.IsTrue(_data.Take(_offset, _length).SequenceEqual(actualData)); Assert.IsTrue(sshDataStream.IsEndOfData); + + CollectionAssert.AreEqual(bytes, request.GetBytes().ToArray()); } } } diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest.cs index 2d497f245..ebc87ca68 100644 --- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest.cs +++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest.cs @@ -11,7 +11,6 @@ using Renci.SshNet.Common; using Renci.SshNet.Sftp; -using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Tests.Classes.Sftp { @@ -240,8 +239,7 @@ private void TestSendsBufferedWrites(Action flushAction) It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), - It.IsAny>()), + It.IsAny()), Times.Never); // Whatever is called here should trigger the bytes to be sent @@ -336,8 +334,7 @@ private static void VerifyRequestWrite(Mock sessionMock, ReadOnlyM /* data: */ It.Is(x => IndexOf(x, newData) >= 0), /* offset: */ It.IsAny(), /* length: */ newData.Length, - /* wait: */ It.IsAny(), - /* writeCompleted: */ It.IsAny>()), + /* wait: */ It.IsAny()), Times.Once); } diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestRead.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestRead.cs index b0bc6157a..48031454d 100644 --- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestRead.cs +++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestRead.cs @@ -114,14 +114,14 @@ private void SetupMocks() _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); _channelSessionMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest("sftp")).Returns(true); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes, 0, _sftpInitRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, new ChannelDataEventArgs(0, _sftpVersionResponse.GetBytes())); }); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes, 0, _sftpRealPathRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, @@ -131,7 +131,7 @@ private void SetupMocks() #endregion SftpSession.Connect() _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes, 0, _sftpReadRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise( diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs index a5086d3d2..684e7b522 100644 --- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs +++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs @@ -115,7 +115,7 @@ private void SetupMocks() .Setup(p => p.IsOpen) .Returns(true); _ = _channelSessionMock.InSequence(sequence) - .Setup(p => p.SendData(_sftpInitRequestBytes)) + .Setup(p => p.SendData(_sftpInitRequestBytes, 0, _sftpInitRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, @@ -125,7 +125,7 @@ private void SetupMocks() .Setup(p => p.IsOpen) .Returns(true); _ = _channelSessionMock.InSequence(sequence) - .Setup(p => p.SendData(_sftpRealPathRequestBytes)) + .Setup(p => p.SendData(_sftpRealPathRequestBytes, 0, _sftpRealPathRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, @@ -138,7 +138,7 @@ private void SetupMocks() .Setup(p => p.IsOpen) .Returns(true); _ = _channelSessionMock.InSequence(sequence) - .Setup(p => p.SendData(_sftpStatVfsRequestBytes)) + .Setup(p => p.SendData(_sftpStatVfsRequestBytes, 0, _sftpStatVfsRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesInSingleSshDataMessage.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesInSingleSshDataMessage.cs index feada4d09..8bcd6f636 100644 --- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesInSingleSshDataMessage.cs +++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesInSingleSshDataMessage.cs @@ -132,7 +132,7 @@ private void SetupMocks() _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); _channelSessionMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest("sftp")).Returns(true); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes, 0, _sftpInitRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, @@ -142,7 +142,7 @@ private void SetupMocks() .Setup(p => p.Create(0U, (byte)SftpMessageTypes.Version, _encoding)) .Returns(_sftpVersionResponse); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes, 0, _sftpRealPathRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, @@ -155,9 +155,9 @@ private void SetupMocks() #endregion SftpSession.Connect() _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpOpenRequestBytes)); + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpOpenRequestBytes, 0, _sftpOpenRequestBytes.Length)); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes)).Callback(() => + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes, 0, _sftpReadRequestBytes.Length)).Callback(() => { var sshMessagePayload = new byte[_sftpHandleResponseBytes.Length + _sftpDataResponseBytes.Length]; Buffer.BlockCopy(_sftpHandleResponseBytes, 0, sshMessagePayload, 0, _sftpHandleResponseBytes.Length); diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesSplitOverMultipleSshDataMessages.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesSplitOverMultipleSshDataMessages.cs index fa8728e6e..0937686ef 100644 --- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesSplitOverMultipleSshDataMessages.cs +++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesSplitOverMultipleSshDataMessages.cs @@ -130,7 +130,7 @@ private void SetupMocks() _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); _channelSessionMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest("sftp")).Returns(true); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes, 0, _sftpInitRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, @@ -140,7 +140,7 @@ private void SetupMocks() .Setup(p => p.Create(0U, (byte)SftpMessageTypes.Version, _encoding)) .Returns(_sftpVersionResponse); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes, 0, _sftpRealPathRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, @@ -153,7 +153,7 @@ private void SetupMocks() #endregion SftpSession.Connect() _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpOpenRequestBytes)).Callback(() => + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpOpenRequestBytes, 0, _sftpOpenRequestBytes.Length)).Callback(() => { var sshMessagePayload = new byte[_sftpHandleResponseBytes.Length + 40]; Buffer.BlockCopy(_sftpHandleResponseBytes, 0, sshMessagePayload, 0, _sftpHandleResponseBytes.Length); @@ -166,7 +166,7 @@ private void SetupMocks() .Setup(p => p.Create(_protocolVersion, (byte)SftpMessageTypes.Handle, _encoding)) .Returns(new SftpHandleResponse(_protocolVersion)); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes)).Callback(() => + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes, 0, _sftpReadRequestBytes.Length)).Callback(() => { var sshMessagePayload = new byte[_sftpDataResponseBytes.Length - 40]; Buffer.BlockCopy(_sftpDataResponseBytes, 40, sshMessagePayload, 0, _sftpDataResponseBytes.Length - 40); diff --git a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_SingleSftpMessageInSshDataMessage.cs b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_SingleSftpMessageInSshDataMessage.cs index f5f570e60..ae5f466b2 100644 --- a/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_SingleSftpMessageInSshDataMessage.cs +++ b/test/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_SingleSftpMessageInSshDataMessage.cs @@ -113,7 +113,7 @@ private void SetupMocks() _channelSessionMock.InSequence(sequence).Setup(p => p.Open()); _channelSessionMock.InSequence(sequence).Setup(p => p.SendSubsystemRequest("sftp")).Returns(true); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes, 0, _sftpInitRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, @@ -123,7 +123,7 @@ private void SetupMocks() .Setup(p => p.Create(0U, (byte)SftpMessageTypes.Version, _encoding)) .Returns(_sftpVersionResponse); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes, 0, _sftpRealPathRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, @@ -136,7 +136,7 @@ private void SetupMocks() #endregion SftpSession.Connect() _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes)) + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes, 0, _sftpReadRequestBytes.Length)) .Callback(() => { _channelSessionMock.Raise(c => c.DataReceived += null, diff --git a/test/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Connected.cs b/test/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Connected.cs index eb19eac14..4cb1c9565 100644 --- a/test/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Connected.cs +++ b/test/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Connected.cs @@ -50,7 +50,7 @@ protected void Arrange() _channelMock.InSequence(_sequence).Setup(p => p.Open()); _channelMock.InSequence(_sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(true); _channelMock.InSequence(_sequence).Setup(p => p.IsOpen).Returns(true); - _channelMock.InSequence(_sequence).Setup(p => p.SendData(_data)); + _channelMock.InSequence(_sequence).Setup(p => p.SendData(_data, 0, _data.Length)); _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, @@ -81,7 +81,7 @@ public void ErrorOccurredHasNeverFired() [TestMethod] public void SendDataOnChannelShouldBeInvokedOnce() { - _channelMock.Verify(p => p.SendData(_data), Times.Once); + _channelMock.Verify(p => p.SendData(_data, 0, _data.Length), Times.Once); } [TestMethod]