From e2cf28a28d95ebb754ec60e0cbeaa6bd9594f4ac Mon Sep 17 00:00:00 2001 From: Gert Driesen Date: Sun, 6 Aug 2017 18:33:28 +0200 Subject: [PATCH] Remove Encoding from SubsystemSession and NetConfSession, and move it to SftpSession (as it is only used there). Refactor creation of SFTP response messages to a SftpResponseFactory. Implement SaveData for a few SFTP response messages. --- .../Renci.SshNet.NETCore.csproj | 4 + .../Classes/Sftp/SftpDataResponseBuilder.cs | 38 +++ .../Classes/Sftp/SftpHandleResponseBuilder.cs | 42 +++ .../Classes/Sftp/SftpInitRequestBuilder.cs | 20 ++ .../Classes/Sftp/SftpNameResponseBuilder.cs | 61 ++++ .../Classes/Sftp/SftpOpenRequestBuilder.cs | 71 +++++ .../Classes/Sftp/SftpReadRequestBuilder.cs | 67 +++++ .../Sftp/SftpRealPathRequestBuilder.cs | 61 ++++ .../SftpSessionTest_Connected_RequestRead.cs | 177 +++++++----- ...ftpSessionTest_Connected_RequestStatVfs.cs | 269 ++++++------------ ...tipleSftpMessagesInSingleSshDataMessage.cs | 200 +++++++++++++ ...essagesSplitOverMultipleSshDataMessages.cs | 207 ++++++++++++++ ...eived_SingleSftpMessageInSshDataMessage.cs | 165 +++++++++++ .../Classes/Sftp/SftpStatVfsRequestBuilder.cs | 61 ++++ .../Sftp/SftpStatVfsResponseBuilder.cs | 135 +++++++++ .../Sftp/SftpVersionResponseBuilder.cs | 47 ++- .../SftpClientTest_Dispose_Connected.cs | 19 +- .../SftpClientTest_Dispose_Disconnected.cs | 23 +- .../SftpClientTest_Dispose_Disposed.cs | 17 +- .../SftpClientTest_Finalize_Connected.cs | 15 +- .../Classes/SubsystemSessionStub.cs | 4 +- .../SubsystemSession_Connect_Connected.cs | 5 +- .../SubsystemSession_Connect_Disconnected.cs | 5 +- .../SubsystemSession_Connect_Disposed.cs | 5 +- .../SubsystemSession_Disconnect_Connected.cs | 5 +- .../SubsystemSession_Disconnect_Disposed.cs | 5 +- ...systemSession_Disconnect_NeverConnected.cs | 5 +- .../SubsystemSession_Dispose_Connected.cs | 5 +- .../SubsystemSession_Dispose_Disconnected.cs | 5 +- .../SubsystemSession_Dispose_Disposed.cs | 5 +- ...SubsystemSession_Dispose_NeverConnected.cs | 5 +- ...Session_OnChannelDataReceived_Connected.cs | 6 +- ...mSession_OnChannelDataReceived_Disposed.cs | 6 +- ...elDataReceived_OnDataReceived_Exception.cs | 6 +- ...temSession_OnChannelException_Connected.cs | 6 +- ...stemSession_OnChannelException_Disposed.cs | 5 +- ...Session_OnSessionDisconnected_Connected.cs | 5 +- ...mSession_OnSessionDisconnected_Disposed.cs | 5 +- ...ession_OnSessionErrorOccurred_Connected.cs | 6 +- ...Session_OnSessionErrorOccurred_Disposed.cs | 6 +- .../SubsystemSession_SendData_Connected.cs | 6 +- .../SubsystemSession_SendData_Disposed.cs | 6 +- ...ubsystemSession_SendData_NeverConnected.cs | 6 +- .../Renci.SshNet.Tests.csproj | 12 + src/Renci.SshNet/IServiceFactory.cs | 5 +- src/Renci.SshNet/Netconf/NetConfSession.cs | 2 +- src/Renci.SshNet/Renci.SshNet.csproj | 3 + src/Renci.SshNet/ServiceFactory.cs | 10 +- src/Renci.SshNet/Sftp/ISftpMessageFactory.cs | 9 + .../Sftp/Responses/SftpDataResponse.cs | 9 +- .../Sftp/Responses/SftpHandleResponse.cs | 9 +- .../Sftp/Responses/SftpNameResponse.cs | 43 ++- .../Sftp/Responses/SftpResponse.cs | 10 +- .../Sftp/Responses/SftpVersionResponse.cs | 4 +- .../Sftp/Responses/StatVfsResponse.cs | 31 +- .../Sftp/SftpFileSystemInformation.cs | 19 +- src/Renci.SshNet/Sftp/SftpMessage.cs | 48 +--- src/Renci.SshNet/Sftp/SftpMessageFactory.cs | 48 ++++ src/Renci.SshNet/Sftp/SftpSession.cs | 28 +- src/Renci.SshNet/SftpClient.cs | 7 +- src/Renci.SshNet/SubsystemSession.cs | 14 +- 61 files changed, 1620 insertions(+), 513 deletions(-) create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpDataResponseBuilder.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpHandleResponseBuilder.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpInitRequestBuilder.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpNameResponseBuilder.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpOpenRequestBuilder.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpReadRequestBuilder.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpRealPathRequestBuilder.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesInSingleSshDataMessage.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesSplitOverMultipleSshDataMessages.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_SingleSftpMessageInSshDataMessage.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsRequestBuilder.cs create mode 100644 src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsResponseBuilder.cs create mode 100644 src/Renci.SshNet/Sftp/ISftpMessageFactory.cs create mode 100644 src/Renci.SshNet/Sftp/SftpMessageFactory.cs diff --git a/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj b/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj index 6f217844..f68f35c6 100644 --- a/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj +++ b/src/Renci.SshNet.NETCore/Renci.SshNet.NETCore.csproj @@ -97,6 +97,10 @@ 4.0.1 + + + + TRACE;DEBUG;FEATURE_ENCODING_ASCII;FEATURE_DIAGNOSTICS_TRACESOURCE;FEATURE_DIRECTORYINFO_ENUMERATEFILES;FEATURE_MEMORYSTREAM_TRYGETBUFFER;FEATURE_REFLECTION_TYPEINFO;FEATURE_RNG_CREATE;FEATURE_SOCKET_TAP;FEATURE_SOCKET_EAP;FEATURE_SOCKET_SYNC;FEATURE_SOCKET_SETSOCKETOPTION;FEATURE_SOCKET_SELECT;FEATURE_SOCKET_POLL;FEATURE_DNS_TAP;FEATURE_STREAM_TAP;FEATURE_THREAD_COUNTDOWNEVENT;FEATURE_THREAD_THREADPOOL;FEATURE_THREAD_SLEEP;FEATURE_HASH_MD5;FEATURE_HASH_SHA1_CREATE;FEATURE_HASH_SHA256_CREATE;FEATURE_HASH_SHA384_CREATE;FEATURE_HASH_SHA512_CREATE;FEATURE_HMAC_MD5;FEATURE_HMAC_SHA1;FEATURE_HMAC_SHA256;FEATURE_HMAC_SHA384;FEATURE_HMAC_SHA512 diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpDataResponseBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpDataResponseBuilder.cs new file mode 100644 index 00000000..71cfbb67 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpDataResponseBuilder.cs @@ -0,0 +1,38 @@ +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + internal class SftpDataResponseBuilder + { + private uint _protocolVersion; + private uint _responseId; + private byte[] _data; + + public SftpDataResponseBuilder WithProtocolVersion(uint protocolVersion) + { + _protocolVersion = protocolVersion; + return this; + } + + public SftpDataResponseBuilder WithResponseId(uint responseId) + { + _responseId = responseId; + return this; + } + + public SftpDataResponseBuilder WithData(byte[] data) + { + _data = data; + return this; + } + + public SftpDataResponse Build() + { + return new SftpDataResponse(_protocolVersion) + { + ResponseId = _responseId, + Data = _data + }; + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpHandleResponseBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpHandleResponseBuilder.cs new file mode 100644 index 00000000..4477e2fb --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpHandleResponseBuilder.cs @@ -0,0 +1,42 @@ +using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Responses; +using System.Collections.Generic; +using System.Text; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + internal class SftpHandleResponseBuilder + { + private uint _protocolVersion; + private uint _responseId; + private byte[] _handle; + + public SftpHandleResponseBuilder WithProtocolVersion(uint protocolVersion) + { + _protocolVersion = protocolVersion; + return this; + } + + public SftpHandleResponseBuilder WithResponseId(uint responseId) + { + _responseId = responseId; + return this; + } + + public SftpHandleResponseBuilder WithHandle(byte[] handle) + { + _handle = handle; + return this; + } + + public SftpHandleResponse Build() + { + var sftpHandleResponse = new SftpHandleResponse(_protocolVersion) + { + ResponseId = _responseId, + Handle = _handle + }; + return sftpHandleResponse; + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpInitRequestBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpInitRequestBuilder.cs new file mode 100644 index 00000000..f0f5f6c6 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpInitRequestBuilder.cs @@ -0,0 +1,20 @@ +using Renci.SshNet.Sftp.Requests; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + internal class SftpInitRequestBuilder + { + private uint _version; + + public SftpInitRequestBuilder WithVersion(uint version) + { + _version = version; + return this; + } + + public SftpInitRequest Build() + { + return new SftpInitRequest(_version); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpNameResponseBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpNameResponseBuilder.cs new file mode 100644 index 00000000..e0dc9ae5 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpNameResponseBuilder.cs @@ -0,0 +1,61 @@ +using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Responses; +using System.Collections.Generic; +using System.Text; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + internal class SftpNameResponseBuilder + { + private uint _responseId; + private uint _protocolVersion; + private Encoding _encoding; + private List> _files; + + public SftpNameResponseBuilder() + { + _files = new List>(); + } + + public SftpNameResponseBuilder WithProtocolVersion(uint protocolVersion) + { + _protocolVersion = protocolVersion; + return this; + } + + public SftpNameResponseBuilder WithResponseId(uint responseId) + { + _responseId = responseId; + return this; + } + + public SftpNameResponseBuilder WithFiles(params KeyValuePair[] files) + { + for (var i = 0; i < files.Length; i++) + _files.Add(files[i]); + return this; + } + + public SftpNameResponseBuilder WithFile(string filename, SftpFileAttributes attributes) + { + _files.Add(new KeyValuePair(filename, attributes)); + return this; + } + + public SftpNameResponseBuilder WithEncoding(Encoding encoding) + { + _encoding = encoding; + return this; + } + + public SftpNameResponse Build() + { + var sftpNameResponse = new SftpNameResponse(_protocolVersion, _encoding) + { + ResponseId = _responseId, + Files = _files.ToArray() + }; + return sftpNameResponse; + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpOpenRequestBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpOpenRequestBuilder.cs new file mode 100644 index 00000000..00c0efa3 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpOpenRequestBuilder.cs @@ -0,0 +1,71 @@ +using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Requests; +using Renci.SshNet.Sftp.Responses; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + internal class SftpOpenRequestBuilder + { + private uint _protocolVersion; + private uint _requestId; + private string _fileName; + private Encoding _encoding; + private Flags _flags; + private Action _handleAction; + private Action _statusAction; + + public SftpOpenRequestBuilder WithProtocolVersion(uint protocolVersion) + { + _protocolVersion = protocolVersion; + return this; + } + + public SftpOpenRequestBuilder WithRequestId(uint requestId) + { + _requestId = requestId; + return this; + } + + public SftpOpenRequestBuilder WithFileName(string fileName) + { + _fileName = fileName; + return this; + } + + public SftpOpenRequestBuilder WithEncoding(Encoding encoding) + { + _encoding = encoding; + return this; + } + + public SftpOpenRequestBuilder WithFlags(Flags flags) + { + _flags = flags; + return this; + } + + public SftpOpenRequestBuilder WithDataAction(Action handleAction) + { + _handleAction = handleAction; + return this; + } + + public SftpOpenRequestBuilder WithStatusAction(Action statusAction) + { + _statusAction = statusAction; + return this; + } + + public SftpOpenRequest Build() + { + var handleAction = _handleAction ?? ((handleResponse) => { }); + var statusAction = _statusAction ?? ((statusResponse) => { }); + + return new SftpOpenRequest(_protocolVersion, _requestId, _fileName, _encoding, _flags, handleAction, statusAction); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpReadRequestBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpReadRequestBuilder.cs new file mode 100644 index 00000000..2dc995c0 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpReadRequestBuilder.cs @@ -0,0 +1,67 @@ +using Renci.SshNet.Sftp.Requests; +using Renci.SshNet.Sftp.Responses; +using System; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + internal class SftpReadRequestBuilder + { + private uint _protocolVersion; + private uint _requestId; + private byte[] _handle; + private uint _offset; + private uint _length; + private Action _dataAction; + private Action _statusAction; + + public SftpReadRequestBuilder WithProtocolVersion(uint protocolVersion) + { + _protocolVersion = protocolVersion; + return this; + } + + public SftpReadRequestBuilder WithRequestId(uint requestId) + { + _requestId = requestId; + return this; + } + + public SftpReadRequestBuilder WithHandle(byte[] handle) + { + _handle = handle; + return this; + } + + public SftpReadRequestBuilder WithOffset(uint offset) + { + _offset = offset; + return this; + } + + public SftpReadRequestBuilder WithLength(uint length) + { + _length = length; + return this; + } + + public SftpReadRequestBuilder WithDataAction(Action dataAction) + { + _dataAction = dataAction; + return this; + } + + public SftpReadRequestBuilder WithStatusAction(Action statusAction) + { + _statusAction = statusAction; + return this; + } + + public SftpReadRequest Build() + { + var dataAction = _dataAction ?? ((dataResponse) => { }); + var statusAction = _statusAction ?? ((statusResponse) => { }); + + return new SftpReadRequest(_protocolVersion, _requestId, _handle, _offset, _length, dataAction, statusAction); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpRealPathRequestBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpRealPathRequestBuilder.cs new file mode 100644 index 00000000..536e07e2 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpRealPathRequestBuilder.cs @@ -0,0 +1,61 @@ +using Renci.SshNet.Sftp.Requests; +using Renci.SshNet.Sftp.Responses; +using System; +using System.Text; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + internal class SftpRealPathRequestBuilder + { + private uint _protocolVersion; + private uint _requestId; + private string _path; + private Encoding _encoding; + private Action _nameAction; + private Action _statusAction; + + public SftpRealPathRequestBuilder WithProtocolVersion(uint protocolVersion) + { + _protocolVersion = protocolVersion; + return this; + } + + public SftpRealPathRequestBuilder WithRequestId(uint requestId) + { + _requestId = requestId; + return this; + } + + public SftpRealPathRequestBuilder WithPath(string path) + { + _path = path; + return this; + } + + public SftpRealPathRequestBuilder WithEncoding(Encoding encoding) + { + _encoding = encoding; + return this; + } + + public SftpRealPathRequestBuilder WithNameAction(Action nameAction) + { + _nameAction = nameAction; + return this; + } + + public SftpRealPathRequestBuilder WithStatusAction(Action statusAction) + { + _statusAction = statusAction; + return this; + } + + public SftpRealPathRequest Build() + { + var nameAction = _nameAction ?? ((nameResponse) => { }); + var statusAction = _statusAction ?? ((statusResponse) => { }); + + return new SftpRealPathRequest(_protocolVersion, _requestId, _path, _encoding, nameAction, statusAction); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestRead.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestRead.cs index ef2a428c..4aec5217 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestRead.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestRead.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -7,19 +6,37 @@ using Moq; using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Sftp; +using Renci.SshNet.Abstractions; +using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Tests.Classes.Sftp { [TestClass] public class SftpSessionTest_Connected_RequestRead { + #region SftpSession.Connect() + private Mock _sessionMock; private Mock _channelSessionMock; + private ISftpResponseFactory _sftpMessageFactory; private SftpSession _sftpSession; private int _operationTimeout; - private byte[] _actual; - private byte[] _expected; private Encoding _encoding; + private uint _protocolVersion; + private byte[] _sftpInitRequestBytes; + private SftpVersionResponse _sftpVersionResponse; + private byte[] _sftpRealPathRequestBytes; + private SftpNameResponse _sftpNameResponse; + + #endregion SftpSession.Connect() + + private byte[] _sftpReadRequestBytes; + private byte[] _sftpDataResponseBytes; + private byte[] _handle; + private uint _offset; + private uint _length; + private byte[] _data; + private byte[] _actual; [TestInitialize] public void Setup() @@ -28,111 +45,119 @@ namespace Renci.SshNet.Tests.Classes.Sftp Act(); } - protected void Arrange() + private void SetupData() { var random = new Random(); - _operationTimeout = random.Next(100, 500); - _expected = new byte[random.Next(30, 50)]; - _encoding = Encoding.UTF8; - random.NextBytes(_expected); + #region SftpSession.Connect() + _operationTimeout = random.Next(100, 500); + _protocolVersion = (uint) random.Next(0, 3); + _encoding = Encoding.UTF8; + _sftpMessageFactory = new SftpResponseFactory(); + _sftpInitRequestBytes = new SftpInitRequestBuilder().WithVersion(SftpSession.MaximumSupportedVersion) + .Build() + .GetBytes(); + _sftpVersionResponse = new SftpVersionResponseBuilder().WithVersion(_protocolVersion) + .Build(); + _sftpRealPathRequestBytes = new SftpRealPathRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(1) + .WithPath(".") + .WithEncoding(_encoding) + .Build() + .GetBytes(); + _sftpNameResponse = new SftpNameResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(1) + .WithEncoding(_encoding) + .WithFile("XYZ", SftpFileAttributes.Empty) + .Build(); + + #endregion SftpSession.Connect() + + _handle = CryptoAbstraction.GenerateRandom(random.Next(1, 10)); + _offset = (uint) random.Next(1, 5); + _length = (uint) random.Next(30, 50); + _data = CryptoAbstraction.GenerateRandom((int) _length); + _sftpReadRequestBytes = new SftpReadRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(2) + .WithHandle(_handle) + .WithOffset(_offset) + .WithLength(_length) + .Build() + .GetBytes(); + _sftpDataResponseBytes = new SftpDataResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(2) + .WithData(_data) + .Build() + .GetBytes(); + } + + private void CreateMocks() + { _sessionMock = new Mock(MockBehavior.Strict); _channelSessionMock = new Mock(MockBehavior.Strict); + } + private void SetupMocks() + { var sequence = new MockSequence(); + #region SftpSession.Connect() + _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); _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(It.IsAny())).Callback( - () => - { - // generate response for SftpInitRequest - var versionInfoResponse = SftpVersionResponseBuilder.Create(3) - .Build(); - _channelSessionMock.Raise( - c => c.DataReceived += null, - new ChannelDataEventArgs(0, versionInfoResponse)); - }); + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes)) + .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(It.IsAny())).Callback( - () => - { - var sftpNameResponse = CreateSftpNameResponse(1, _encoding, "ABC"); + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpNameResponse.GetBytes())); + }); + #endregion SftpSession.Connect() + + _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes)) + .Callback(() => + { _channelSessionMock.Raise( c => c.DataReceived += null, - new ChannelDataEventArgs(0, sftpNameResponse)); + new ChannelDataEventArgs(0, _sftpDataResponseBytes.Take(0, 20))); + _channelSessionMock.Raise( + c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpDataResponseBytes.Take(20, _sftpDataResponseBytes.Length - 20))); } ); - _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(It.IsAny())).Callback( - () => - { - var sftpDataResponse = CreateSftpDataResponse(2, _expected); + } - _channelSessionMock.Raise( - c => c.DataReceived += null, - new ChannelDataEventArgs(0, sftpDataResponse.Take(0, 20))); - _channelSessionMock.Raise( - c => c.DataReceived += null, - new ChannelDataEventArgs(0, sftpDataResponse.Take(20, sftpDataResponse.Length - 20))); - } - ); + private void Arrange() + { + SetupData(); + CreateMocks(); + SetupMocks(); - _sftpSession = new SftpSession(_sessionMock.Object, _operationTimeout, _encoding); + _sftpSession = new SftpSession(_sessionMock.Object, _operationTimeout, _encoding, _sftpMessageFactory); _sftpSession.Connect(); } protected void Act() { - _actual = _sftpSession.RequestRead(new byte[0], 0, 200); + _actual = _sftpSession.RequestRead(_handle, _offset, _length); } [TestMethod] public void ReturnedValueShouldBeDataOfSftpDataResponse() { Assert.IsNotNull(_actual); - Assert.IsTrue(_expected.SequenceEqual(_actual)); - } - - private static byte[] CreateSftpDataResponse(uint responseId, byte[] data) - { - var sshDataStream = new SshDataStream(4 + 1 + 4 + 4 + data.Length + 1); - sshDataStream.Write((uint) sshDataStream.Capacity - 4); - sshDataStream.WriteByte((byte) SftpMessageTypes.Data); - sshDataStream.Write(responseId); - sshDataStream.Write((uint) data.Length); - sshDataStream.Write(data, 0, data.Length); - sshDataStream.WriteByte(1); // EOF - return sshDataStream.ToArray(); - } - - private static byte[] CreateSftpNameResponse(uint responseId, Encoding encoding, params string[] names) - { - var namesAndAttributes = new List(); - foreach (var name in names) - { - var nameBytes = encoding.GetBytes(name); - var attributesBytes = SftpFileAttributes.Empty.GetBytes(); - - namesAndAttributes.AddRange(Pack.UInt32ToBigEndian((uint) nameBytes.Length)); // filename length - namesAndAttributes.AddRange(nameBytes); // filename - namesAndAttributes.AddRange(Pack.UInt32ToBigEndian(0U)); // longname length - namesAndAttributes.AddRange(attributesBytes); // attributes - } - - var namesAndAttributesBytes = namesAndAttributes.ToArray(); - - var sshDataStream = new SshDataStream(4 + 1 + 4 + 4 + namesAndAttributesBytes.Length); - sshDataStream.Write((uint) sshDataStream.Capacity - 4); - sshDataStream.WriteByte((byte) SftpMessageTypes.Name); - sshDataStream.Write(responseId); - sshDataStream.Write((uint) names.Length); - sshDataStream.Write(namesAndAttributesBytes, 0, namesAndAttributesBytes.Length); - return sshDataStream.ToArray(); + Assert.IsTrue(_data.SequenceEqual(_actual)); } } } \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs index a88fee21..ca6d225b 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_Connected_RequestStatVfs.cs @@ -1,25 +1,38 @@ using System; -using System.Collections.Generic; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; using Renci.SshNet.Common; using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Tests.Classes.Sftp { [TestClass] public class SftpSessionTest_Connected_RequestStatVfs { + #region SftpSession.Connect() + private Mock _sessionMock; private Mock _channelSessionMock; + private ISftpResponseFactory _sftpMessageFactory; private SftpSession _sftpSession; private int _operationTimeout; - private SftpFileSytemInformation _actual; private Encoding _encoding; + private uint _protocolVersion; + private SftpVersionResponse _sftpVersionResponse; + private SftpNameResponse _sftpNameResponse; + private byte[] _sftpInitRequestBytes; + private byte[] _sftpRealPathRequestBytes; + #endregion SftpSession.Connect() + + private byte[] _sftpStatVfsRequestBytes; + private StatVfsResponse _sftpStatVfsResponse; private ulong _bAvail; + private string _path; + private SftpFileSytemInformation _actual; [TestInitialize] public void Setup() @@ -28,67 +41,104 @@ namespace Renci.SshNet.Tests.Classes.Sftp Act(); } - protected void Arrange() + private void SetupData() { var random = new Random(); + #region SftpSession.Connect() + _operationTimeout = random.Next(100, 500); _encoding = Encoding.UTF8; + _protocolVersion = 3; + _sftpMessageFactory = new SftpResponseFactory(); + _sftpInitRequestBytes = new SftpInitRequestBuilder().WithVersion(SftpSession.MaximumSupportedVersion) + .Build() + .GetBytes(); + _sftpVersionResponse = new SftpVersionResponseBuilder().WithVersion(_protocolVersion) + .WithExtension("statvfs@openssh.com", "") + .Build(); + _sftpRealPathRequestBytes = new SftpRealPathRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(1) + .WithPath(".") + .WithEncoding(_encoding) + .Build() + .GetBytes(); + _sftpNameResponse = new SftpNameResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(1U) + .WithEncoding(_encoding) + .WithFile("ABC", SftpFileAttributes.Empty) + .Build(); + #endregion SftpSession.Connect() + + _path = random.Next().ToString(); _bAvail = (ulong) random.Next(0, int.MaxValue); + _sftpStatVfsRequestBytes = new SftpStatVfsRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(2) + .WithPath(_path) + .WithEncoding(_encoding) + .Build() + .GetBytes(); + _sftpStatVfsResponse = new SftpStatVfsResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(2U) + .WithBAvail(_bAvail) + .Build(); + } + private void CreateMocks() + { _sessionMock = new Mock(MockBehavior.Strict); _channelSessionMock = new Mock(MockBehavior.Strict); + } + private void SetupMocks() + { var sequence = new MockSequence(); + #region SftpSession.Connect() + _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); _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(It.IsAny())).Callback( + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpInitRequestBytes)).Callback( () => - { - // generate response for SftpInitRequest - var versionInfoResponse = SftpVersionResponseBuilder.Create(3) - .AddExtension("statvfs@openssh.com", "") - .Build(); - _channelSessionMock.Raise( - c => c.DataReceived += null, - new ChannelDataEventArgs(0, versionInfoResponse)); - }); + { + _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(It.IsAny())).Callback( + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpRealPathRequestBytes)).Callback( () => - { - var sftpNameResponse = CreateSftpNameResponse(1, _encoding, "ABC"); + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpNameResponse.GetBytes())); + }); + + #endregion SftpSession.Connect() - _channelSessionMock.Raise( - c => c.DataReceived += null, - new ChannelDataEventArgs(0, sftpNameResponse)); - } - ); _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); - _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(It.IsAny())).Callback( + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpStatVfsRequestBytes)).Callback( () => - { - var statVfsReplyBuilder = StatVfsReplyBuilder.Create(2); - statVfsReplyBuilder.WithBAvail(_bAvail); - var statVfsReply = statVfsReplyBuilder.Build(); + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpStatVfsResponse.GetBytes())); + }); + } - _channelSessionMock.Raise( - c => c.DataReceived += null, - new ChannelDataEventArgs(0, statVfsReply)); - } - ); + protected void Arrange() + { + SetupData(); + CreateMocks(); + SetupMocks(); - _sftpSession = new SftpSession(_sessionMock.Object, _operationTimeout, _encoding); + _sftpSession = new SftpSession(_sessionMock.Object, _operationTimeout, _encoding, _sftpMessageFactory); _sftpSession.Connect(); } protected void Act() { - _actual = _sftpSession.RequestStatVfs("path"); + _actual = _sftpSession.RequestStatVfs(_path); } [TestMethod] @@ -102,156 +152,5 @@ namespace Renci.SshNet.Tests.Classes.Sftp { Assert.AreEqual(_bAvail, _actual.AvailableBlocks); } - - private static byte[] CreateSftpNameResponse(uint responseId, Encoding encoding, params string[] names) - { - var namesAndAttributes = new List(); - foreach (var name in names) - { - var nameBytes = encoding.GetBytes(name); - var attributesBytes = SftpFileAttributes.Empty.GetBytes(); - - namesAndAttributes.AddRange(Pack.UInt32ToBigEndian((uint) nameBytes.Length)); // filename length - namesAndAttributes.AddRange(nameBytes); // filename - namesAndAttributes.AddRange(Pack.UInt32ToBigEndian(0U)); // longname length - namesAndAttributes.AddRange(attributesBytes); // attributes - } - - var namesAndAttributesBytes = namesAndAttributes.ToArray(); - - var sshDataStream = new SshDataStream(4 + 1 + 4 + 4 + namesAndAttributesBytes.Length); - sshDataStream.Write((uint) sshDataStream.Capacity - 4); - sshDataStream.WriteByte((byte) SftpMessageTypes.Name); - sshDataStream.Write(responseId); - sshDataStream.Write((uint) names.Length); - sshDataStream.Write(namesAndAttributesBytes, 0, namesAndAttributesBytes.Length); - return sshDataStream.ToArray(); - } - - public class StatVfsReplyBuilder - { - private readonly uint _responseId; - private ulong _bsize; - private ulong _frsize; - private ulong _blocks; - private ulong _bfree; - private ulong _bavail; - private ulong _files; - private ulong _ffree; - private ulong _favail; - private ulong _sid; - private ulong _flag; - private ulong _namemax; - - private StatVfsReplyBuilder(uint responseId) - { - _responseId = responseId; - } - - public static StatVfsReplyBuilder Create(uint responseId) - { - return new StatVfsReplyBuilder(responseId); - } - - public StatVfsReplyBuilder WithBSize(ulong bsize) - { - _bsize = bsize; - return this; - } - - public StatVfsReplyBuilder WithFrSize(ulong frsize) - { - _frsize = frsize; - return this; - } - - public StatVfsReplyBuilder WithBlocks(ulong blocks) - { - _blocks = blocks; - return this; - } - - public StatVfsReplyBuilder WithBFree(ulong bfree) - { - _bfree = bfree; - return this; - } - - public StatVfsReplyBuilder WithBAvail(ulong bavail) - { - _bavail = bavail; - return this; - } - - public StatVfsReplyBuilder WithFiles(ulong files) - { - _files = files; - return this; - } - - public StatVfsReplyBuilder WithFFree(ulong ffree) - { - _ffree = ffree; - return this; - } - - public StatVfsReplyBuilder WithFAvail(ulong favail) - { - _favail = favail; - return this; - } - - public StatVfsReplyBuilder WithSid(ulong sid) - { - _sid = sid; - return this; - } - - public StatVfsReplyBuilder WithIsReadOnly(bool isReadOnly) - { - if (isReadOnly) - _flag &= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_RDONLY; - else - _flag |= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_RDONLY; - - return this; - } - - public StatVfsReplyBuilder WithSupportsSetUid(bool supportsSetUid) - { - if (supportsSetUid) - _flag |= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_NOSUID; - else - _flag &= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_NOSUID; - - return this; - } - - public StatVfsReplyBuilder WithNameMax(ulong nameMax) - { - _namemax = nameMax; - return this; - } - - public byte[] Build() - { - var sshDataStream = new SshDataStream(4 + 1 + 4 + 88); - sshDataStream.Write((uint) sshDataStream.Capacity - 4); - sshDataStream.WriteByte((byte) SftpMessageTypes.ExtendedReply); - sshDataStream.Write(_responseId); - sshDataStream.Write(_bsize); - sshDataStream.Write(_frsize); - sshDataStream.Write(_blocks); - sshDataStream.Write(_bfree); - sshDataStream.Write(_bavail); - sshDataStream.Write(_files); - sshDataStream.Write(_ffree); - sshDataStream.Write(_favail); - sshDataStream.Write(_sid); - sshDataStream.Write(_flag); - sshDataStream.Write(_namemax); - return sshDataStream.ToArray(); - } - } - } + } } \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesInSingleSshDataMessage.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesInSingleSshDataMessage.cs new file mode 100644 index 00000000..4119fcf2 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesInSingleSshDataMessage.cs @@ -0,0 +1,200 @@ +using System; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Responses; +using Renci.SshNet.Abstractions; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpSessionTest_DataReceived_MultipleSftpMessagesInSingleSshDataMessage + { + #region SftpSession.Connect() + + private Mock _sessionMock; + private Mock _channelSessionMock; + private Mock _sftpMessageFactoryMock; + private SftpSession _sftpSession; + private int _operationTimeout; + private Encoding _encoding; + private uint _protocolVersion; + private byte[] _sftpInitRequestBytes; + private SftpVersionResponse _sftpVersionResponse; + private byte[] _sftpRealPathRequestBytes; + private SftpNameResponse _sftpNameResponse; + private byte[] _sftpOpenRequestBytes; + private byte[] _sftpHandleResponseBytes; + private byte[] _sftpReadRequestBytes; + private byte[] _sftpDataResponseBytes; + private byte[] _handle; + private uint _offset; + private uint _length; + private byte[] _data; + private string _path; + private byte[] _actualHandle; + private byte[] _actualData; + + #endregion SftpSession.Connect() + + [TestInitialize] + public void Setup() + { + Arrange(); + Act(); + } + + private void SetupData() + { + var random = new Random(); + + #region SftpSession.Connect() + + _operationTimeout = random.Next(100, 500); + _protocolVersion = (uint)random.Next(0, 3); + _encoding = Encoding.UTF8; + + _sftpInitRequestBytes = new SftpInitRequestBuilder().WithVersion(SftpSession.MaximumSupportedVersion) + .Build() + .GetBytes(); + _sftpVersionResponse = new SftpVersionResponseBuilder().WithVersion(_protocolVersion) + .Build(); + _sftpRealPathRequestBytes = new SftpRealPathRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(1) + .WithPath(".") + .WithEncoding(_encoding) + .Build() + .GetBytes(); + _sftpNameResponse = new SftpNameResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(1) + .WithEncoding(_encoding) + .WithFile("/ABC", SftpFileAttributes.Empty) + .Build(); + + #endregion SftpSession.Connect() + + _path = random.Next().ToString(); + _handle = CryptoAbstraction.GenerateRandom(4); + _offset = (uint) random.Next(1, 5); + _length = (uint) random.Next(30, 50); + _data = CryptoAbstraction.GenerateRandom(200); + _sftpOpenRequestBytes = new SftpOpenRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(2) + .WithFileName(_path) + .WithFlags(Flags.Read) + .WithEncoding(_encoding) + .Build() + .GetBytes(); + _sftpHandleResponseBytes = new SftpHandleResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(2) + .WithHandle(_handle) + .Build() + .GetBytes(); + _sftpReadRequestBytes = new SftpReadRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(3) + .WithHandle(_handle) + .WithOffset(_offset) + .WithLength(_length) + .Build() + .GetBytes(); + _sftpDataResponseBytes = new SftpDataResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(3) + .WithData(_data) + .Build() + .GetBytes(); + } + + private void CreateMocks() + { + _sessionMock = new Mock(MockBehavior.Strict); + _channelSessionMock = new Mock(MockBehavior.Strict); + _sftpMessageFactoryMock = new Mock(MockBehavior.Strict); + } + + private void SetupMocks() + { + var sequence = new MockSequence(); + + #region SftpSession.Connect() + + _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); + _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)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpVersionResponse.GetBytes())); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .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)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpNameResponse.GetBytes())); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .Setup(p => p.Create(_protocolVersion, (byte)SftpMessageTypes.Name, _encoding)) + .Returns(_sftpNameResponse); + + #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.IsOpen).Returns(true); + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes)).Callback(() => + { + var sshMessagePayload = new byte[_sftpHandleResponseBytes.Length + _sftpDataResponseBytes.Length]; + Buffer.BlockCopy(_sftpHandleResponseBytes, 0, sshMessagePayload, 0, _sftpHandleResponseBytes.Length); + Buffer.BlockCopy(_sftpDataResponseBytes, 0, sshMessagePayload, _sftpHandleResponseBytes.Length, _sftpDataResponseBytes.Length); + + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, sshMessagePayload)); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .Setup(p => p.Create(_protocolVersion, (byte) SftpMessageTypes.Handle, _encoding)) + .Returns(new SftpHandleResponse(_protocolVersion)); + _sftpMessageFactoryMock.InSequence(sequence) + .Setup(p => p.Create(_protocolVersion, (byte) SftpMessageTypes.Data, _encoding)) + .Returns(new SftpDataResponse(_protocolVersion)); + } + + protected void Arrange() + { + SetupData(); + CreateMocks(); + SetupMocks(); + + _sftpSession = new SftpSession(_sessionMock.Object, _operationTimeout, _encoding, _sftpMessageFactoryMock.Object); + _sftpSession.Connect(); + } + + protected void Act() + { + var openAsyncResult = _sftpSession.BeginOpen(_path, Flags.Read, null, null); + var readAsyncResult = _sftpSession.BeginRead(_handle, _offset, _length, null, null); + + _actualHandle = _sftpSession.EndOpen(openAsyncResult); + _actualData = _sftpSession.EndRead(readAsyncResult); + } + + [TestMethod] + public void ReturnedValueShouldBeDataOfSftpDataResponse() + { + Assert.IsTrue(_data.IsEqualTo(_actualData)); + } + + [TestMethod] + public void ReturnedHandleShouldBeHandleOfSftpHandleResponse() + { + Assert.IsTrue(_handle.IsEqualTo(_actualHandle)); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesSplitOverMultipleSshDataMessages.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesSplitOverMultipleSshDataMessages.cs new file mode 100644 index 00000000..3eb2c949 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_MultipleSftpMessagesSplitOverMultipleSshDataMessages.cs @@ -0,0 +1,207 @@ +using System; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Responses; +using Renci.SshNet.Abstractions; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpSessionTest_DataReceived_MultipleSftpMessagesSplitOverMultipleSshDataMessages + { + #region SftpSession.Connect() + + private Mock _sessionMock; + private Mock _channelSessionMock; + private Mock _sftpMessageFactoryMock; + private SftpSession _sftpSession; + private int _operationTimeout; + private Encoding _encoding; + private uint _protocolVersion; + private byte[] _sftpInitRequestBytes; + private SftpVersionResponse _sftpVersionResponse; + private byte[] _sftpRealPathRequestBytes; + private SftpNameResponse _sftpNameResponse; + private byte[] _sftpOpenRequestBytes; + private byte[] _sftpHandleResponseBytes; + private byte[] _sftpReadRequestBytes; + private byte[] _sftpDataResponseBytes; + private byte[] _handle; + private uint _offset; + private uint _length; + private byte[] _data; + private string _path; + private byte[] _actualHandle; + private byte[] _actualData; + + #endregion SftpSession.Connect() + + [TestInitialize] + public void Setup() + { + Arrange(); + Act(); + } + + private void SetupData() + { + var random = new Random(); + + #region SftpSession.Connect() + + _operationTimeout = random.Next(100, 500); + _protocolVersion = (uint)random.Next(0, 3); + _encoding = Encoding.UTF8; + + _sftpInitRequestBytes = new SftpInitRequestBuilder().WithVersion(SftpSession.MaximumSupportedVersion) + .Build() + .GetBytes(); + _sftpVersionResponse = new SftpVersionResponseBuilder().WithVersion(_protocolVersion) + .Build(); + _sftpRealPathRequestBytes = new SftpRealPathRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(1) + .WithPath(".") + .WithEncoding(_encoding) + .Build() + .GetBytes(); + _sftpNameResponse = new SftpNameResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(1) + .WithEncoding(_encoding) + .WithFile("/ABC", SftpFileAttributes.Empty) + .Build(); + + #endregion SftpSession.Connect() + + _path = random.Next().ToString(); + _handle = CryptoAbstraction.GenerateRandom(4); + _offset = (uint) random.Next(1, 5); + _length = (uint) random.Next(30, 50); + _data = CryptoAbstraction.GenerateRandom(200); + _sftpOpenRequestBytes = new SftpOpenRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(2) + .WithFileName(_path) + .WithFlags(Flags.Read) + .WithEncoding(_encoding) + .Build() + .GetBytes(); + _sftpHandleResponseBytes = new SftpHandleResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(2) + .WithHandle(_handle) + .Build() + .GetBytes(); + _sftpReadRequestBytes = new SftpReadRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(3) + .WithHandle(_handle) + .WithOffset(_offset) + .WithLength(_length) + .Build() + .GetBytes(); + _sftpDataResponseBytes = new SftpDataResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(3) + .WithData(_data) + .Build() + .GetBytes(); + } + + private void CreateMocks() + { + _sessionMock = new Mock(MockBehavior.Strict); + _channelSessionMock = new Mock(MockBehavior.Strict); + _sftpMessageFactoryMock = new Mock(MockBehavior.Strict); + } + + private void SetupMocks() + { + var sequence = new MockSequence(); + + #region SftpSession.Connect() + + _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); + _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)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpVersionResponse.GetBytes())); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .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)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpNameResponse.GetBytes())); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .Setup(p => p.Create(_protocolVersion, (byte)SftpMessageTypes.Name, _encoding)) + .Returns(_sftpNameResponse); + + #endregion SftpSession.Connect() + + _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpOpenRequestBytes)).Callback(() => + { + var sshMessagePayload = new byte[_sftpHandleResponseBytes.Length + 40]; + Buffer.BlockCopy(_sftpHandleResponseBytes, 0, sshMessagePayload, 0, _sftpHandleResponseBytes.Length); + Buffer.BlockCopy(_sftpDataResponseBytes, 0, sshMessagePayload, _sftpHandleResponseBytes.Length, 40); + + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, sshMessagePayload)); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .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(() => + { + var sshMessagePayload = new byte[_sftpDataResponseBytes.Length - 40]; + Buffer.BlockCopy(_sftpDataResponseBytes, 40, sshMessagePayload, 0, _sftpDataResponseBytes.Length - 40); + + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, sshMessagePayload)); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .Setup(p => p.Create(_protocolVersion, (byte) SftpMessageTypes.Data, _encoding)) + .Returns(new SftpDataResponse(_protocolVersion)); + } + + protected void Arrange() + { + SetupData(); + CreateMocks(); + SetupMocks(); + + _sftpSession = new SftpSession(_sessionMock.Object, _operationTimeout, _encoding, _sftpMessageFactoryMock.Object); + _sftpSession.Connect(); + } + + protected void Act() + { + var openAsyncResult = _sftpSession.BeginOpen(_path, Flags.Read, null, null); + var readAsyncResult = _sftpSession.BeginRead(_handle, _offset, _length, null, null); + + _actualHandle = _sftpSession.EndOpen(openAsyncResult); + _actualData = _sftpSession.EndRead(readAsyncResult); + } + + [TestMethod] + public void ReturnedValueShouldBeDataOfSftpDataResponse() + { + Assert.IsTrue(_data.IsEqualTo(_actualData)); + } + + [TestMethod] + public void ReturnedHandleShouldBeHandleOfSftpHandleResponse() + { + Assert.IsTrue(_handle.IsEqualTo(_actualHandle)); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_SingleSftpMessageInSshDataMessage.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_SingleSftpMessageInSshDataMessage.cs new file mode 100644 index 00000000..75f1d2b8 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpSessionTest_DataReceived_SingleSftpMessageInSshDataMessage.cs @@ -0,0 +1,165 @@ +using System; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Renci.SshNet.Channels; +using Renci.SshNet.Common; +using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Responses; +using Renci.SshNet.Abstractions; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + [TestClass] + public class SftpSessionTest_DataReceived_SingleSftpMessageInSshDataMessage + { + #region SftpSession.Connect() + + private Mock _sessionMock; + private Mock _channelSessionMock; + private Mock _sftpMessageFactoryMock; + private SftpSession _sftpSession; + private int _operationTimeout; + private Encoding _encoding; + private uint _protocolVersion; + private byte[] _sftpInitRequestBytes; + private SftpVersionResponse _sftpVersionResponse; + private byte[] _sftpRealPathRequestBytes; + private SftpNameResponse _sftpNameResponse; + private byte[] _sftpReadRequestBytes; + private byte[] _sftpDataResponseBytes; + private byte[] _handle; + private uint _offset; + private uint _length; + private byte[] _data; + private byte[] _actual; + + #endregion SftpSession.Connect() + + [TestInitialize] + public void Setup() + { + Arrange(); + Act(); + } + + private void SetupData() + { + var random = new Random(); + + #region SftpSession.Connect() + + _operationTimeout = random.Next(100, 500); + _protocolVersion = (uint) random.Next(0, 3); + _encoding = Encoding.UTF8; + + _sftpInitRequestBytes = new SftpInitRequestBuilder().WithVersion(SftpSession.MaximumSupportedVersion) + .Build() + .GetBytes(); + _sftpVersionResponse = new SftpVersionResponseBuilder().WithVersion(_protocolVersion) + .Build(); + _sftpRealPathRequestBytes = new SftpRealPathRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(1) + .WithPath(".") + .WithEncoding(_encoding) + .Build() + .GetBytes(); + _sftpNameResponse = new SftpNameResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(1) + .WithEncoding(_encoding) + .WithFile("/ABC", SftpFileAttributes.Empty) + .Build(); + + #endregion SftpSession.Connect() + + _handle = CryptoAbstraction.GenerateRandom(4); + _offset = (uint) random.Next(1, 5); + _length = (uint) random.Next(30, 50); + _data = CryptoAbstraction.GenerateRandom(200); + _sftpReadRequestBytes = new SftpReadRequestBuilder().WithProtocolVersion(_protocolVersion) + .WithRequestId(2) + .WithHandle(_handle) + .WithOffset(_offset) + .WithLength(_length) + .Build() + .GetBytes(); + _sftpDataResponseBytes = new SftpDataResponseBuilder().WithProtocolVersion(_protocolVersion) + .WithResponseId(2) + .WithData(_data) + .Build() + .GetBytes(); + } + + private void CreateMocks() + { + _sessionMock = new Mock(MockBehavior.Strict); + _channelSessionMock = new Mock(MockBehavior.Strict); + _sftpMessageFactoryMock = new Mock(MockBehavior.Strict); + } + + private void SetupMocks() + { + var sequence = new MockSequence(); + + #region SftpSession.Connect() + + _sessionMock.InSequence(sequence).Setup(p => p.CreateChannelSession()).Returns(_channelSessionMock.Object); + _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)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpVersionResponse.GetBytes())); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .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)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpNameResponse.GetBytes())); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .Setup(p => p.Create(_protocolVersion, (byte) SftpMessageTypes.Name, _encoding)) + .Returns(_sftpNameResponse); + + #endregion SftpSession.Connect() + + _channelSessionMock.InSequence(sequence).Setup(p => p.IsOpen).Returns(true); + _channelSessionMock.InSequence(sequence).Setup(p => p.SendData(_sftpReadRequestBytes)) + .Callback(() => + { + _channelSessionMock.Raise(c => c.DataReceived += null, + new ChannelDataEventArgs(0, _sftpDataResponseBytes)); + }); + _sftpMessageFactoryMock.InSequence(sequence) + .Setup(p => p.Create(_protocolVersion, (byte) SftpMessageTypes.Data, _encoding)) + .Returns(new SftpDataResponse(_protocolVersion)); + } + + protected void Arrange() + { + SetupData(); + CreateMocks(); + SetupMocks(); + + _sftpSession = new SftpSession(_sessionMock.Object, _operationTimeout, _encoding, _sftpMessageFactoryMock.Object); + _sftpSession.Connect(); + } + + protected void Act() + { + _actual = _sftpSession.RequestRead(_handle, _offset, _length); + } + + [TestMethod] + public void ReturnedValueShouldBeDataOfSftpDataResponse() + { + Assert.IsTrue(_data.IsEqualTo(_actual)); + } + } +} \ No newline at end of file diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsRequestBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsRequestBuilder.cs new file mode 100644 index 00000000..71e37bd1 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsRequestBuilder.cs @@ -0,0 +1,61 @@ +using Renci.SshNet.Sftp.Requests; +using Renci.SshNet.Sftp.Responses; +using System; +using System.Text; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + internal class SftpStatVfsRequestBuilder + { + private uint _protocolVersion; + private uint _requestId; + private string _path; + private Encoding _encoding; + private Action _extendedAction; + private Action _statusAction; + + public SftpStatVfsRequestBuilder WithProtocolVersion(uint protocolVersion) + { + _protocolVersion = protocolVersion; + return this; + } + + public SftpStatVfsRequestBuilder WithRequestId(uint requestId) + { + _requestId = requestId; + return this; + } + + public SftpStatVfsRequestBuilder WithPath(string path) + { + _path = path; + return this; + } + + public SftpStatVfsRequestBuilder WithEncoding(Encoding encoding) + { + _encoding = encoding; + return this; + } + + public SftpStatVfsRequestBuilder WithExtendedAction(Action extendedAction) + { + _extendedAction = extendedAction; + return this; + } + + public SftpStatVfsRequestBuilder WithStatusAction(Action statusAction) + { + _statusAction = statusAction; + return this; + } + + public StatVfsRequest Build() + { + var extendedAction = _extendedAction ?? ((extendedReplyResponse) => { }); + var statusAction = _statusAction ?? ((statusResponse) => { }); + + return new StatVfsRequest(_protocolVersion, _requestId, _path, _encoding, extendedAction, statusAction); + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsResponseBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsResponseBuilder.cs new file mode 100644 index 00000000..2d6be526 --- /dev/null +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpStatVfsResponseBuilder.cs @@ -0,0 +1,135 @@ +using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Responses; + +namespace Renci.SshNet.Tests.Classes.Sftp +{ + internal class SftpStatVfsResponseBuilder + { + private uint _protocolVersion; + private uint _responseId; + private ulong _bsize; + private ulong _frsize; + private ulong _blocks; + private ulong _bfree; + private ulong _bavail; + private ulong _files; + private ulong _ffree; + private ulong _favail; + private ulong _sid; + private ulong _flag; + private ulong _namemax; + + public SftpStatVfsResponseBuilder WithProtocolVersion(uint protocolVersion) + { + _protocolVersion = protocolVersion; + return this; + } + + public SftpStatVfsResponseBuilder WithResponseId(uint responseId) + { + _responseId = responseId; + return this; + } + + public SftpStatVfsResponseBuilder WithBSize(ulong bsize) + { + _bsize = bsize; + return this; + } + + public SftpStatVfsResponseBuilder WithFrSize(ulong frsize) + { + _frsize = frsize; + return this; + } + + public SftpStatVfsResponseBuilder WithBlocks(ulong blocks) + { + _blocks = blocks; + return this; + } + + public SftpStatVfsResponseBuilder WithBFree(ulong bfree) + { + _bfree = bfree; + return this; + } + + public SftpStatVfsResponseBuilder WithBAvail(ulong bavail) + { + _bavail = bavail; + return this; + } + + public SftpStatVfsResponseBuilder WithFiles(ulong files) + { + _files = files; + return this; + } + + public SftpStatVfsResponseBuilder WithFFree(ulong ffree) + { + _ffree = ffree; + return this; + } + + public SftpStatVfsResponseBuilder WithFAvail(ulong favail) + { + _favail = favail; + return this; + } + + public SftpStatVfsResponseBuilder WithSid(ulong sid) + { + _sid = sid; + return this; + } + + public SftpStatVfsResponseBuilder WithIsReadOnly(bool isReadOnly) + { + if (isReadOnly) + _flag &= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_RDONLY; + else + _flag |= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_RDONLY; + + return this; + } + + public SftpStatVfsResponseBuilder WithSupportsSetUid(bool supportsSetUid) + { + if (supportsSetUid) + _flag |= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_NOSUID; + else + _flag &= SftpFileSytemInformation.SSH_FXE_STATVFS_ST_NOSUID; + + return this; + } + + public SftpStatVfsResponseBuilder WithNameMax(ulong nameMax) + { + _namemax = nameMax; + return this; + } + + public StatVfsResponse Build() + { + var fileSystemInfo = new SftpFileSytemInformation(_bsize, + _frsize, + _blocks, + _bfree, + _bavail, + _files, + _ffree, + _favail, + _sid, + _flag, + _namemax); + + return new StatVfsResponse(_protocolVersion) + { + ResponseId = _responseId, + Information = fileSystemInfo + }; + } + } +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpVersionResponseBuilder.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpVersionResponseBuilder.cs index e5e1f670..85f33cc1 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpVersionResponseBuilder.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpVersionResponseBuilder.cs @@ -1,53 +1,38 @@ using System.Collections.Generic; -using System.Text; -using Renci.SshNet.Common; -using Renci.SshNet.Sftp; +using Renci.SshNet.Sftp.Responses; namespace Renci.SshNet.Tests.Classes.Sftp { - public class SftpVersionResponseBuilder + internal class SftpVersionResponseBuilder { - private readonly uint _version; - private readonly IDictionary _extensions; + private uint _version; + private IDictionary _extensions; - private SftpVersionResponseBuilder(uint version) + public SftpVersionResponseBuilder() { - _version = version; _extensions = new Dictionary(); } - public static SftpVersionResponseBuilder Create(uint version) + public SftpVersionResponseBuilder WithVersion(uint version) { - return new SftpVersionResponseBuilder(version); + _version = version; + return this; } - public SftpVersionResponseBuilder AddExtension(string name, string data) + public SftpVersionResponseBuilder WithExtension(string name, string data) { _extensions.Add(name, data); return this; } - public byte[] Build() + public SftpVersionResponse Build() { - var extensions = BuildExtensions(); - - var sshDataStream = new SshDataStream(4 + 1 + 4 + extensions.Length); - sshDataStream.Write((uint)sshDataStream.Capacity - 4); - sshDataStream.WriteByte((byte)SftpMessageTypes.Version); - sshDataStream.Write(_version); - sshDataStream.Write(extensions, 0, extensions.Length); - return sshDataStream.ToArray(); - } - - private byte[] BuildExtensions() - { - var sshDataStream = new SshDataStream(0); - foreach (var extensionPair in _extensions) - { - sshDataStream.Write(extensionPair.Key, Encoding.ASCII); - sshDataStream.Write(extensionPair.Value, Encoding.ASCII); - } - return sshDataStream.ToArray(); + var sftpVersionResponse = new SftpVersionResponse() + { + Version = _version, + Extentions = _extensions + }; + return sftpVersionResponse; } } } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs index f633a0b5..05f504f2 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Connected.cs @@ -10,9 +10,10 @@ namespace Renci.SshNet.Tests.Classes { private Mock _serviceFactoryMock; private Mock _sessionMock; + private Mock _sftpMessageFactoryMock; + private Mock _sftpSessionMock; private SftpClient _sftpClient; private ConnectionInfo _connectionInfo; - private Mock _sftpSessionMock; private int _operationTimeout; [TestInitialize] @@ -31,6 +32,7 @@ namespace Renci.SshNet.Tests.Classes { _serviceFactoryMock = new Mock(MockBehavior.Strict); _sessionMock = new Mock(MockBehavior.Strict); + _sftpMessageFactoryMock = new Mock(MockBehavior.Strict); _sftpSessionMock = new Mock(MockBehavior.Strict); _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); @@ -44,8 +46,11 @@ namespace Renci.SshNet.Tests.Classes .Returns(_sessionMock.Object); _sessionMock.InSequence(sequence).Setup(p => p.Connect()); _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding)) - .Returns(_sftpSessionMock.Object); + .Setup(p => p.CreateSftpMessageFactory()) + .Returns(_sftpMessageFactoryMock.Object); + _serviceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpMessageFactoryMock.Object)) + .Returns(_sftpSessionMock.Object); _sftpSessionMock.InSequence(sequence).Setup(p => p.Connect()); _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); _sftpSessionMock.InSequence(sequence).Setup(p => p.Dispose()); @@ -59,11 +64,17 @@ namespace Renci.SshNet.Tests.Classes _sftpClient.Dispose(); } + [TestMethod] + public void CreateSftpMessageFactoryOnServiceFactoryShouldBeInvokedOnce() + { + _serviceFactoryMock.Verify(p => p.CreateSftpMessageFactory(), Times.Once); + } + [TestMethod] public void CreateSftpSessionOnServiceFactoryShouldBeInvokedOnce() { _serviceFactoryMock.Verify( - p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding), + p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpMessageFactoryMock.Object), Times.Once); } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs index c29fdc16..cdf8bef6 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disconnected.cs @@ -10,9 +10,11 @@ namespace Renci.SshNet.Tests.Classes { private Mock _serviceFactoryMock; private Mock _sessionMock; + private Mock _sftpMessageFactoryMock; + private Mock _sftpSessionMock; private SftpClient _sftpClient; private ConnectionInfo _connectionInfo; - private Mock _sftpSessionMock; + private int _operationTimeout; [TestInitialize] @@ -22,15 +24,11 @@ namespace Renci.SshNet.Tests.Classes Act(); } - [TestCleanup] - public void Cleanup() - { - } - protected void Arrange() { _serviceFactoryMock = new Mock(MockBehavior.Strict); _sessionMock = new Mock(MockBehavior.Strict); + _sftpMessageFactoryMock = new Mock(MockBehavior.Strict); _sftpSessionMock = new Mock(MockBehavior.Strict); _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); @@ -44,7 +42,10 @@ namespace Renci.SshNet.Tests.Classes .Returns(_sessionMock.Object); _sessionMock.InSequence(sequence).Setup(p => p.Connect()); _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding)) + .Setup(p => p.CreateSftpMessageFactory()) + .Returns(_sftpMessageFactoryMock.Object); + _serviceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpMessageFactoryMock.Object)) .Returns(_sftpSessionMock.Object); _sftpSessionMock.InSequence(sequence).Setup(p => p.Connect()); _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); @@ -60,11 +61,17 @@ namespace Renci.SshNet.Tests.Classes _sftpClient.Dispose(); } + [TestMethod] + public void CreateSftpMessageFactoryOnServiceFactoryShouldBeInvokedOnce() + { + _serviceFactoryMock.Verify(p => p.CreateSftpMessageFactory(), Times.Once); + } + [TestMethod] public void CreateSftpSessionOnServiceFactoryShouldBeInvokedOnce() { _serviceFactoryMock.Verify( - p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding), + p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpMessageFactoryMock.Object), Times.Once); } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs index 1d9b8b4d..227e78f5 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Dispose_Disposed.cs @@ -10,9 +10,10 @@ namespace Renci.SshNet.Tests.Classes { private Mock _serviceFactoryMock; private Mock _sessionMock; + private Mock _sftpMessageFactoryMock; + private Mock _sftpSessionMock; private SftpClient _sftpClient; private ConnectionInfo _connectionInfo; - private Mock _sftpSessionMock; private int _operationTimeout; [TestInitialize] @@ -31,6 +32,7 @@ namespace Renci.SshNet.Tests.Classes { _serviceFactoryMock = new Mock(MockBehavior.Strict); _sessionMock = new Mock(MockBehavior.Strict); + _sftpMessageFactoryMock = new Mock(MockBehavior.Strict); _sftpSessionMock = new Mock(MockBehavior.Strict); _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); @@ -44,7 +46,10 @@ namespace Renci.SshNet.Tests.Classes .Returns(_sessionMock.Object); _sessionMock.InSequence(sequence).Setup(p => p.Connect()); _serviceFactoryMock.InSequence(sequence) - .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding)) + .Setup(p => p.CreateSftpMessageFactory()) + .Returns(_sftpMessageFactoryMock.Object); + _serviceFactoryMock.InSequence(sequence) + .Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpMessageFactoryMock.Object)) .Returns(_sftpSessionMock.Object); _sftpSessionMock.InSequence(sequence).Setup(p => p.Connect()); _sessionMock.InSequence(sequence).Setup(p => p.OnDisconnecting()); @@ -60,11 +65,17 @@ namespace Renci.SshNet.Tests.Classes _sftpClient.Dispose(); } + [TestMethod] + public void CreateSftpMessageFactoryOnServiceFactoryShouldBeInvokedOnce() + { + _serviceFactoryMock.Verify(p => p.CreateSftpMessageFactory(), Times.Once); + } + [TestMethod] public void CreateSftpSessionOnServiceFactoryShouldBeInvokedOnce() { _serviceFactoryMock.Verify( - p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding), + p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpMessageFactoryMock.Object), Times.Once); } diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs index 27b9b9ee..828de9e2 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest_Finalize_Connected.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Sftp; @@ -11,9 +10,10 @@ namespace Renci.SshNet.Tests.Classes { private Mock _serviceFactoryMock; private Mock _sessionMock; + private Mock _sftpMessageFactoryMock; + private Mock _sftpSessionMock; private SftpClient _sftpClient; private ConnectionInfo _connectionInfo; - private Mock _sftpSessionMock; private int _operationTimeout; [TestInitialize] @@ -27,6 +27,7 @@ namespace Renci.SshNet.Tests.Classes { _serviceFactoryMock = new Mock(MockBehavior.Loose); _sessionMock = new Mock(MockBehavior.Strict); + _sftpMessageFactoryMock = new Mock(MockBehavior.Strict); _sftpSessionMock = new Mock(MockBehavior.Strict); _connectionInfo = new ConnectionInfo("host", "user", new NoneAuthenticationMethod("userauth")); @@ -37,17 +38,15 @@ namespace Renci.SshNet.Tests.Classes _serviceFactoryMock.Setup(p => p.CreateSession(_connectionInfo)) .Returns(_sessionMock.Object); _sessionMock.Setup(p => p.Connect()); - _serviceFactoryMock.Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding)) - .Returns(_sftpSessionMock.Object); + _serviceFactoryMock.Setup(p => p.CreateSftpMessageFactory()) + .Returns(_sftpMessageFactoryMock.Object); + _serviceFactoryMock.Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding, _sftpMessageFactoryMock.Object)) + .Returns(_sftpSessionMock.Object); _sftpSessionMock.Setup(p => p.Connect()); _sftpClient.Connect(); _sftpClient = null; - _serviceFactoryMock.Setup(p => p.CreateSftpSession(_sessionMock.Object, _operationTimeout, _connectionInfo.Encoding)) - .Returns((ISftpSession) null); - _serviceFactoryMock.ResetCalls(); - // we need to dereference all other mocks as they might otherwise hold the target alive _sessionMock = null; _connectionInfo = null; diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSessionStub.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSessionStub.cs index ca47f09e..241b4910 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSessionStub.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSessionStub.cs @@ -10,8 +10,8 @@ namespace Renci.SshNet.Tests.Classes { private int _onChannelOpenInvocationCount; - public SubsystemSessionStub(ISession session, string subsystemName, int operationTimeout, Encoding encoding) - : base(session, subsystemName, operationTimeout, encoding) + public SubsystemSessionStub(ISession session, string subsystemName, int operationTimeout) + : base(session, subsystemName, operationTimeout) { OnDataReceivedInvocations = new List(); } diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Connected.cs index fa90ba56..24e868cb 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Connected.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private InvalidOperationException _actualException; @@ -35,7 +34,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -51,8 +49,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disconnected.cs index 065fed11..1d15dd6e 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disconnected.cs @@ -18,7 +18,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private MockSequence _sequence; @@ -35,7 +34,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -55,8 +53,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disposed.cs index b500dd35..11e6b0c6 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Connect_Disposed.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private ObjectDisposedException _actualException; @@ -34,7 +33,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -49,8 +47,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Dispose(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Connected.cs index 43b28985..f5d07813 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Connected.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private MockSequence _sequence; @@ -34,7 +33,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -50,8 +48,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Disposed.cs index f3e2312a..21116a78 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_Disposed.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; @@ -33,7 +32,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -49,8 +47,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_NeverConnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_NeverConnected.cs index 8804f6aa..71b161a5 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_NeverConnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Disconnect_NeverConnected.cs @@ -15,7 +15,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; @@ -31,7 +30,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -40,8 +38,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); } diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Connected.cs index dfe98c1c..f89da65c 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Connected.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; @@ -33,7 +32,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -49,8 +47,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disconnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disconnected.cs index 2111db3b..c304ed43 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disconnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disconnected.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; @@ -33,7 +32,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -49,8 +47,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disposed.cs index eb2e1d46..d2f93dec 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_Disposed.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; @@ -33,7 +32,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -49,8 +47,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_NeverConnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_NeverConnected.cs index 6c83b499..83fab899 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_NeverConnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_Dispose_NeverConnected.cs @@ -16,7 +16,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; @@ -32,7 +31,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -41,8 +39,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); } diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_Connected.cs index bdd03248..f8187a90 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_Connected.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; @@ -17,7 +16,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private ChannelDataEventArgs _channelDataEventArgs; @@ -35,7 +33,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); _channelDataEventArgs = new ChannelDataEventArgs( @@ -53,8 +50,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_Disposed.cs index ec925926..33dfac88 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_Disposed.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; @@ -17,7 +16,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private ChannelDataEventArgs _channelDataEventArgs; @@ -34,7 +32,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); _channelDataEventArgs = new ChannelDataEventArgs( @@ -53,8 +50,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_OnDataReceived_Exception.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_OnDataReceived_Exception.cs index a1e01413..43b9bbe3 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_OnDataReceived_Exception.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelDataReceived_OnDataReceived_Exception.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; @@ -18,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private ChannelDataEventArgs _channelDataEventArgs; @@ -36,7 +34,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); _channelDataEventArgs = new ChannelDataEventArgs( @@ -55,8 +52,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.OnDataReceivedException = _onDataReceivedException; diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Connected.cs index 774c2e91..e283c322 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Connected.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; @@ -18,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private ExceptionEventArgs _channelExceptionEventArgs; @@ -36,7 +34,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); _channelExceptionEventArgs = new ExceptionEventArgs(new SystemException()); @@ -52,8 +49,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Disposed.cs index 50a644e6..d9b3a162 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnChannelException_Disposed.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private ExceptionEventArgs _channelExceptionEventArgs; @@ -34,7 +33,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); _channelExceptionEventArgs = new ExceptionEventArgs(new SystemException()); @@ -51,8 +49,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Connected.cs index 49d915a4..b6851cfc 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Connected.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private MockSequence _sequence; @@ -34,7 +33,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -49,8 +47,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Disposed.cs index 094424d1..7a90eba4 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionDisconnected_Disposed.cs @@ -17,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; @@ -33,7 +32,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -49,8 +47,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionErrorOccurred_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionErrorOccurred_Connected.cs index 66cd4173..32151eb9 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionErrorOccurred_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionErrorOccurred_Connected.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; @@ -18,7 +17,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private ExceptionEventArgs _errorOccurredEventArgs; @@ -36,7 +34,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); _errorOccurredEventArgs = new ExceptionEventArgs(new SystemException()); @@ -52,8 +49,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionErrorOccurred_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionErrorOccurred_Disposed.cs index e3bd8bd9..df18c042 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionErrorOccurred_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_OnSessionErrorOccurred_Disposed.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; @@ -17,7 +16,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private ExceptionEventArgs _errorOccurredEventArgs; @@ -34,7 +32,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); _errorOccurredEventArgs = new ExceptionEventArgs(new SystemException()); @@ -51,8 +48,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Connected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Connected.cs index ce2c6850..11a455c7 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Connected.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; @@ -17,7 +16,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private byte[] _data; @@ -35,7 +33,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); _data = new[] {(byte) random.Next(byte.MinValue, byte.MaxValue)}; @@ -53,8 +50,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Disposed.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Disposed.cs index 8935fc8e..b8ebc7fa 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Disposed.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_Disposed.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; @@ -17,7 +16,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private ObjectDisposedException _actualException; @@ -34,7 +32,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); @@ -50,8 +47,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); _subsystemSession.Connect(); diff --git a/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_NeverConnected.cs b/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_NeverConnected.cs index 8d36e19c..3dd9f5cc 100644 --- a/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_NeverConnected.cs +++ b/src/Renci.SshNet.Tests/Classes/SubsystemSession_SendData_NeverConnected.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Renci.SshNet.Channels; @@ -17,7 +16,6 @@ namespace Renci.SshNet.Tests.Classes private string _subsystemName; private SubsystemSessionStub _subsystemSession; private int _operationTimeout; - private Encoding _encoding; private IList _disconnectedRegister; private IList _errorOccurredRegister; private InvalidOperationException _actualException; @@ -36,7 +34,6 @@ namespace Renci.SshNet.Tests.Classes var random = new Random(); _subsystemName = random.Next().ToString(CultureInfo.InvariantCulture); _operationTimeout = 30000; - _encoding = Encoding.UTF8; _disconnectedRegister = new List(); _errorOccurredRegister = new List(); _data = new[] {(byte) random.Next(byte.MinValue, byte.MaxValue)}; @@ -53,8 +50,7 @@ namespace Renci.SshNet.Tests.Classes _subsystemSession = new SubsystemSessionStub( _sessionMock.Object, _subsystemName, - _operationTimeout, - _encoding); + _operationTimeout); _subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args); _subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args); } diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index 9bf0c4cd..9fc8daef 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -343,6 +343,7 @@ + @@ -453,8 +454,19 @@ + + + + + + + + + + + diff --git a/src/Renci.SshNet/IServiceFactory.cs b/src/Renci.SshNet/IServiceFactory.cs index 7a2c35c0..dafc09c5 100644 --- a/src/Renci.SshNet/IServiceFactory.cs +++ b/src/Renci.SshNet/IServiceFactory.cs @@ -31,10 +31,11 @@ namespace Renci.SshNet /// The to create the in. /// The number of milliseconds to wait for an operation to complete, or -1 to wait indefinitely. /// The encoding. + /// The factory to use for creating SFTP messages. /// /// An . /// - ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding); + ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpMessageFactory); /// /// Create a new . @@ -59,5 +60,7 @@ namespace Renci.SshNet IKeyExchange CreateKeyExchange(IDictionary clientAlgorithms, string[] serverAlgorithms); ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize); + + ISftpResponseFactory CreateSftpMessageFactory(); } } diff --git a/src/Renci.SshNet/Netconf/NetConfSession.cs b/src/Renci.SshNet/Netconf/NetConfSession.cs index d2c7b35d..14ba00a6 100644 --- a/src/Renci.SshNet/Netconf/NetConfSession.cs +++ b/src/Renci.SshNet/Netconf/NetConfSession.cs @@ -35,7 +35,7 @@ namespace Renci.SshNet.NetConf /// The session. /// The number of milliseconds to wait for an operation to complete, or -1 to wait indefinitely. public NetConfSession(ISession session, int operationTimeout) - : base(session, "netconf", operationTimeout, Encoding.UTF8) + : base(session, "netconf", operationTimeout) { ClientCapabilities = new XmlDocument(); ClientCapabilities.LoadXml("" + diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj index 7b7a2631..28bca009 100644 --- a/src/Renci.SshNet/Renci.SshNet.csproj +++ b/src/Renci.SshNet/Renci.SshNet.csproj @@ -172,6 +172,7 @@ + @@ -416,6 +417,7 @@ + @@ -427,6 +429,7 @@ + Code diff --git a/src/Renci.SshNet/ServiceFactory.cs b/src/Renci.SshNet/ServiceFactory.cs index b9ad0e95..f9cd7fc6 100644 --- a/src/Renci.SshNet/ServiceFactory.cs +++ b/src/Renci.SshNet/ServiceFactory.cs @@ -46,12 +46,13 @@ namespace Renci.SshNet /// The to create the in. /// The number of milliseconds to wait for an operation to complete, or -1 to wait indefinitely. /// The encoding. + /// The factory to use for creating SFTP messages. /// /// An . /// - public ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding) + public ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpMessageFactory) { - return new SftpSession(session, operationTimeout, encoding); + return new SftpSession(session, operationTimeout, encoding, sftpMessageFactory); } /// @@ -130,5 +131,10 @@ namespace Renci.SshNet return sftpSession.CreateFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize); } + + public ISftpResponseFactory CreateSftpMessageFactory() + { + return new SftpResponseFactory(); + } } } diff --git a/src/Renci.SshNet/Sftp/ISftpMessageFactory.cs b/src/Renci.SshNet/Sftp/ISftpMessageFactory.cs new file mode 100644 index 00000000..afb440c1 --- /dev/null +++ b/src/Renci.SshNet/Sftp/ISftpMessageFactory.cs @@ -0,0 +1,9 @@ +using System.Text; + +namespace Renci.SshNet.Sftp +{ + internal interface ISftpResponseFactory + { + SftpMessage Create(uint protocolVersion, byte messageType, Encoding encoding); + } +} diff --git a/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs index 610f90b3..f15a8dcc 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpDataResponse.cs @@ -7,7 +7,7 @@ get { return SftpMessageTypes.Data; } } - public byte[] Data { get; private set; } + public byte[] Data { get; set; } public SftpDataResponse(uint protocolVersion) : base(protocolVersion) @@ -20,5 +20,12 @@ Data = ReadBinary(); } + + protected override void SaveData() + { + base.SaveData(); + + WriteBinary(Data, 0, Data.Length); + } } } diff --git a/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs index 2a856139..233e3e37 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpHandleResponse.cs @@ -7,7 +7,7 @@ get { return SftpMessageTypes.Handle; } } - public byte[] Handle { get; private set; } + public byte[] Handle { get; set; } public SftpHandleResponse(uint protocolVersion) : base(protocolVersion) @@ -20,5 +20,12 @@ Handle = ReadBinary(); } + + protected override void SaveData() + { + base.SaveData(); + + WriteBinary(Handle, 0, Handle.Length); + } } } diff --git a/src/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs index ca5166b3..3750119d 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpNameResponse.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using Renci.SshNet.Common; +using System.Collections.Generic; using System.Text; namespace Renci.SshNet.Sftp.Responses @@ -14,12 +15,12 @@ namespace Renci.SshNet.Sftp.Responses public Encoding Encoding { get; private set; } - public KeyValuePair[] Files { get; private set; } + public KeyValuePair[] Files { get; set; } public SftpNameResponse(uint protocolVersion, Encoding encoding) : base(protocolVersion) { - Files = new KeyValuePair[0]; + Files = Array>.Empty; Encoding = encoding; } @@ -29,14 +30,42 @@ namespace Renci.SshNet.Sftp.Responses Count = ReadUInt32(); Files = new KeyValuePair[Count]; - + for (var i = 0; i < Count; i++) { var fileName = ReadString(Encoding); - ReadString(Encoding); // skip longname - var attributes = ReadAttributes(); - Files[i] = new KeyValuePair(fileName, attributes); + if (SupportsLongName(ProtocolVersion)) + { + ReadString(Encoding); // skip longname + } + Files[i] = new KeyValuePair(fileName, ReadAttributes()); } } + + protected override void SaveData() + { + base.SaveData(); + + Write((uint) Files.Length); // count + + for (var i = 0; i < Files.Length; i++) + { + var file = Files[i]; + + Write(file.Key, Encoding); // filename + + if (SupportsLongName(ProtocolVersion)) + { + Write(0U); // longname + } + + Write(file.Value.GetBytes()); // attrs + } + } + + private static bool SupportsLongName(uint protocolVersion) + { + return protocolVersion <= 3U; + } } } diff --git a/src/Renci.SshNet/Sftp/Responses/SftpResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpResponse.cs index 44fafef1..3b784bf0 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpResponse.cs @@ -1,10 +1,8 @@ -using System; - -namespace Renci.SshNet.Sftp.Responses +namespace Renci.SshNet.Sftp.Responses { internal abstract class SftpResponse : SftpMessage { - public uint ResponseId { get; private set; } + public uint ResponseId { get; set; } public uint ProtocolVersion { get; private set; } @@ -22,7 +20,9 @@ namespace Renci.SshNet.Sftp.Responses protected override void SaveData() { - throw new InvalidOperationException("Response cannot be saved."); + base.SaveData(); + + Write(ResponseId); } } } diff --git a/src/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs b/src/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs index 642bd806..87c9f13f 100644 --- a/src/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/SftpVersionResponse.cs @@ -9,9 +9,9 @@ namespace Renci.SshNet.Sftp.Responses get { return SftpMessageTypes.Version; } } - public uint Version { get; private set; } + public uint Version { get; set; } - public IDictionary Extentions { get; private set; } + public IDictionary Extentions { get; set; } protected override void LoadData() { diff --git a/src/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs b/src/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs index 6a6d36a9..e7b6c2ed 100644 --- a/src/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs +++ b/src/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs @@ -2,10 +2,10 @@ { internal class StatVfsResponse : SftpExtendedReplyResponse { - public SftpFileSytemInformation Information { get; private set; } + public SftpFileSytemInformation Information { get; set; } - public StatVfsResponse() - : base(0) + public StatVfsResponse(uint protocolVersion) + : base(protocolVersion) { } @@ -13,12 +13,25 @@ { base.LoadData(); - this.Information = new SftpFileSytemInformation(this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64(), this.ReadUInt64(), - this.ReadUInt64()); + Information = new SftpFileSytemInformation(ReadUInt64(), // FileSystemBlockSize + ReadUInt64(), // BlockSize + ReadUInt64(), // TotalBlocks + ReadUInt64(), // FreeBlocks + ReadUInt64(), // AvailableBlocks + ReadUInt64(), // TotalNodes + ReadUInt64(), // FreeNodes + ReadUInt64(), // AvailableNodes + ReadUInt64(), // Sid + ReadUInt64(), // Flags + ReadUInt64() // MaxNameLenght + ); + } + + protected override void SaveData() + { + base.SaveData(); + + Information.SaveData(DataStream); } } } \ No newline at end of file diff --git a/src/Renci.SshNet/Sftp/SftpFileSystemInformation.cs b/src/Renci.SshNet/Sftp/SftpFileSystemInformation.cs index a8b73107..bff9295b 100644 --- a/src/Renci.SshNet/Sftp/SftpFileSystemInformation.cs +++ b/src/Renci.SshNet/Sftp/SftpFileSystemInformation.cs @@ -1,4 +1,6 @@ -namespace Renci.SshNet.Sftp +using Renci.SshNet.Common; + +namespace Renci.SshNet.Sftp { /// /// Contains File system information exposed by statvfs@openssh.com request. @@ -140,5 +142,20 @@ _flag = flag; MaxNameLenght = namemax; } + + internal void SaveData(SshDataStream stream) + { + stream.Write(FileSystemBlockSize); + stream.Write(BlockSize); + stream.Write(TotalBlocks); + stream.Write(FreeBlocks); + stream.Write(AvailableBlocks); + stream.Write(TotalNodes); + stream.Write(FreeNodes); + stream.Write(AvailableNodes); + stream.Write(Sid); + stream.Write(_flag); + stream.Write(MaxNameLenght); + } } } \ No newline at end of file diff --git a/src/Renci.SshNet/Sftp/SftpMessage.cs b/src/Renci.SshNet/Sftp/SftpMessage.cs index d6161497..8f131fa7 100644 --- a/src/Renci.SshNet/Sftp/SftpMessage.cs +++ b/src/Renci.SshNet/Sftp/SftpMessage.cs @@ -1,21 +1,11 @@ -using System; -using System.IO; +using System.IO; using Renci.SshNet.Common; using System.Globalization; -using Renci.SshNet.Sftp.Responses; -using System.Text; namespace Renci.SshNet.Sftp { internal abstract class SftpMessage : SshData { - public static SftpMessage Load(uint protocolVersion, byte[] data, int offset, int count, Encoding encoding) - { - var messageType = (SftpMessageTypes) data[offset]; - - return Load(protocolVersion, messageType, data, offset + 1, count - 1, encoding); - } - /// /// Gets the size of the message in bytes. /// @@ -79,42 +69,6 @@ namespace Renci.SshNet.Sftp return SftpFileAttributes.FromBytes(DataStream); } - private static SftpMessage Load(uint protocolVersion, SftpMessageTypes messageType, byte[] data, int offset, int count, Encoding encoding) - { - SftpMessage message; - - switch (messageType) - { - case SftpMessageTypes.Version: - message = new SftpVersionResponse(); - break; - case SftpMessageTypes.Status: - message = new SftpStatusResponse(protocolVersion); - break; - case SftpMessageTypes.Data: - message = new SftpDataResponse(protocolVersion); - break; - case SftpMessageTypes.Handle: - message = new SftpHandleResponse(protocolVersion); - break; - case SftpMessageTypes.Name: - message = new SftpNameResponse(protocolVersion, encoding); - break; - case SftpMessageTypes.Attrs: - message = new SftpAttrsResponse(protocolVersion); - break; - case SftpMessageTypes.ExtendedReply: - message = new SftpExtendedReplyResponse(protocolVersion); - break; - default: - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Message type '{0}' is not supported.", messageType)); - } - - message.Load(data, offset, count); - - return message; - } - public override string ToString() { return string.Format(CultureInfo.CurrentCulture, "SFTP Message : {0}", SftpMessageType); diff --git a/src/Renci.SshNet/Sftp/SftpMessageFactory.cs b/src/Renci.SshNet/Sftp/SftpMessageFactory.cs new file mode 100644 index 00000000..4fcbec1e --- /dev/null +++ b/src/Renci.SshNet/Sftp/SftpMessageFactory.cs @@ -0,0 +1,48 @@ +using System; +using System.Text; +using Renci.SshNet.Sftp.Responses; +using System.Globalization; + +namespace Renci.SshNet.Sftp +{ + internal sealed class SftpResponseFactory : ISftpResponseFactory + { + public SftpMessage Create(uint protocolVersion, byte messageType, Encoding encoding) + { + var sftpMessageType = (SftpMessageTypes) messageType; + + SftpMessage message; + + switch (sftpMessageType) + { + case SftpMessageTypes.Version: + message = new SftpVersionResponse(); + break; + case SftpMessageTypes.Status: + message = new SftpStatusResponse(protocolVersion); + break; + case SftpMessageTypes.Data: + message = new SftpDataResponse(protocolVersion); + break; + case SftpMessageTypes.Handle: + message = new SftpHandleResponse(protocolVersion); + break; + case SftpMessageTypes.Name: + message = new SftpNameResponse(protocolVersion, encoding); + break; + case SftpMessageTypes.Attrs: + message = new SftpAttrsResponse(protocolVersion); + break; + case SftpMessageTypes.ExtendedReply: + message = new SftpExtendedReplyResponse(protocolVersion); + break; + default: + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Message type '{0}' is not supported.", sftpMessageType)); + } + + return message; + } + + public + } +} diff --git a/src/Renci.SshNet/Sftp/SftpSession.cs b/src/Renci.SshNet/Sftp/SftpSession.cs index 4f74f884..71846bdb 100644 --- a/src/Renci.SshNet/Sftp/SftpSession.cs +++ b/src/Renci.SshNet/Sftp/SftpSession.cs @@ -11,15 +11,21 @@ namespace Renci.SshNet.Sftp { internal class SftpSession : SubsystemSession, ISftpSession { - private const int MaximumSupportedVersion = 3; + internal const int MaximumSupportedVersion = 3; private const int MinimumSupportedVersion = 0; private readonly Dictionary _requests = new Dictionary(); + private readonly ISftpResponseFactory _sftpMessageFactory; //FIXME: obtain from SftpClient! private readonly List _data = new List(32 * 1024); private EventWaitHandle _sftpVersionConfirmed = new AutoResetEvent(false); private IDictionary _supportedExtensions; + /// + /// Gets the character encoding to use. + /// + protected Encoding Encoding { get; private set; } + /// /// Gets the remote working directory. /// @@ -49,9 +55,11 @@ namespace Renci.SshNet.Sftp } } - public SftpSession(ISession session, int operationTimeout, Encoding encoding) - : base(session, "sftp", operationTimeout, encoding) + public SftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpMessageFactory) + : base(session, "sftp", operationTimeout) { + Encoding = encoding; + _sftpMessageFactory = sftpMessageFactory; } /// @@ -263,7 +271,7 @@ namespace Renci.SshNet.Sftp // remove loaded data and bytes for length from _data holder if (_data.Count == packetTotalLength) { - // the only buffered data is the data we're processing + // the only buffered data is the data we're processing _data.Clear(); } else @@ -282,8 +290,10 @@ namespace Renci.SshNet.Sftp private bool TryLoadSftpMessage(byte[] packetData, int offset, int count) { - // Load SFTP Message and handle it - var response = SftpMessage.Load(ProtocolVersion, packetData, offset, count, Encoding); + // Create SFTP message + var response = _sftpMessageFactory.Create(ProtocolVersion, packetData[offset], Encoding); + // Load message data into it + response.Load(packetData, offset + 1, count - 1); try { @@ -1513,6 +1523,8 @@ namespace Renci.SshNet.Sftp // bytes 6 to 9: response id // bytes 10 to 13: length of payload‏ // + // WinSCP uses a payload length of 32755 bytes + // // most ssh servers limit the size of the payload of a SSH_MSG_CHANNEL_DATA // response to 16 KB; if we requested 16 KB of data, then the SSH_FXP_DATA // payload of the SSH_MSG_CHANNEL_DATA message would be too big (16 KB + 13 bytes), and @@ -1544,6 +1556,10 @@ namespace Renci.SshNet.Sftp // // 14-21: offset // 22-25: data length + + // Putty uses data length of 4096 bytes + // WinSCP uses data length of 32739 bytes (total 32768 bytes; 32739 + 25 + 4 bytes for handle) + var lengthOfNonDataProtocolFields = 25u + (uint)handle.Length; var maximumPacketSize = Channel.RemotePacketSize; return Math.Min(bufferSize, maximumPacketSize) - lengthOfNonDataProtocolFields; diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index d6b83aa2..2de6cbfe 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -240,7 +240,7 @@ namespace Renci.SshNet : base(connectionInfo, ownsConnectionInfo, serviceFactory) { _operationTimeout = SshNet.Session.Infinite; - BufferSize = 1024 * 32; + _bufferSize = 1024 * 32; } #endregion Constructors @@ -1375,8 +1375,7 @@ namespace Renci.SshNet { CheckDisposed(); - return new SftpFileStream(_sftpSession, path, FileMode.OpenOrCreate, FileAccess.Write, - (int) _bufferSize); + return new SftpFileStream(_sftpSession, path, FileMode.OpenOrCreate, FileAccess.Write, (int) _bufferSize); } /// @@ -2123,7 +2122,7 @@ namespace Renci.SshNet { base.OnConnected(); - _sftpSession = ServiceFactory.CreateSftpSession(Session, _operationTimeout, ConnectionInfo.Encoding); + _sftpSession = ServiceFactory.CreateSftpSession(Session, _operationTimeout, ConnectionInfo.Encoding, ServiceFactory.CreateSftpMessageFactory()); _sftpSession.Connect(); } diff --git a/src/Renci.SshNet/SubsystemSession.cs b/src/Renci.SshNet/SubsystemSession.cs index d8a64305..4aa52d5c 100644 --- a/src/Renci.SshNet/SubsystemSession.cs +++ b/src/Renci.SshNet/SubsystemSession.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using System.Text; using System.Threading; using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; @@ -72,32 +71,23 @@ namespace Renci.SshNet get { return _channel != null && _channel.IsOpen; } } - /// - /// Gets the character encoding to use. - /// - protected Encoding Encoding { get; private set; } - /// /// Initializes a new instance of the SubsystemSession class. /// /// The session. /// Name of the subsystem. /// The number of milliseconds to wait for a given operation to complete, or -1 to wait indefinitely. - /// The character encoding to use. - /// or or is null. - protected SubsystemSession(ISession session, string subsystemName, int operationTimeout, Encoding encoding) + /// or is null. + protected SubsystemSession(ISession session, string subsystemName, int operationTimeout) { if (session == null) throw new ArgumentNullException("session"); if (subsystemName == null) throw new ArgumentNullException("subsystemName"); - if (encoding == null) - throw new ArgumentNullException("encoding"); _session = session; _subsystemName = subsystemName; OperationTimeout = operationTimeout; - Encoding = encoding; } ///