Skip to content

Commit 66471e6

Browse files
committed
When the request to open a subsystem fails, close the channel session and throw a SshException.
Fixes #308.
1 parent 7cecd86 commit 66471e6

File tree

4 files changed

+138
-2
lines changed

4 files changed

+138
-2
lines changed

src/Renci.SshNet.Tests.NET35/Renci.SshNet.Tests.NET35.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1551,6 +1551,9 @@
15511551
<Compile Include="..\Renci.SshNet.Tests\Classes\SubsystemSession_Connect_NeverConnected.cs">
15521552
<Link>Classes\SubsystemSession_Connect_NeverConnected.cs</Link>
15531553
</Compile>
1554+
<Compile Include="..\Renci.SshNet.Tests\Classes\SubsystemSession_Connect_SendSubsystemRequestFails.cs">
1555+
<Link>Classes\SubsystemSession_Connect_SendSubsystemRequestFails.cs</Link>
1556+
</Compile>
15541557
<Compile Include="..\Renci.SshNet.Tests\Classes\SubsystemSession_Disconnect_Connected.cs">
15551558
<Link>Classes\SubsystemSession_Disconnect_Connected.cs</Link>
15561559
</Compile>
@@ -1707,7 +1710,7 @@
17071710
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
17081711
<ProjectExtensions>
17091712
<VisualStudio>
1710-
<UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" />
1713+
<UserProperties ProjectLinkReference="c45379b9-17b1-4e89-bc2e-6d41726413e8" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
17111714
</VisualStudio>
17121715
</ProjectExtensions>
17131716
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
using Moq;
6+
using Renci.SshNet.Channels;
7+
using Renci.SshNet.Common;
8+
9+
namespace Renci.SshNet.Tests.Classes
10+
{
11+
[TestClass]
12+
public class SubsystemSession_Connect_SendSubsystemRequestFails
13+
{
14+
private Mock<ISession> _sessionMock;
15+
private Mock<IChannelSession> _channelMock;
16+
private string _subsystemName;
17+
private SubsystemSessionStub _subsystemSession;
18+
private int _operationTimeout;
19+
private IList<EventArgs> _disconnectedRegister;
20+
private IList<ExceptionEventArgs> _errorOccurredRegister;
21+
private SshException _actualException;
22+
private MockSequence _sequence;
23+
24+
[TestInitialize]
25+
public void Setup()
26+
{
27+
Arrange();
28+
Act();
29+
}
30+
31+
protected void Arrange()
32+
{
33+
var random = new Random();
34+
_subsystemName = random.Next().ToString(CultureInfo.InvariantCulture);
35+
_operationTimeout = 30000;
36+
_disconnectedRegister = new List<EventArgs>();
37+
_errorOccurredRegister = new List<ExceptionEventArgs>();
38+
39+
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
40+
_channelMock = new Mock<IChannelSession>(MockBehavior.Strict);
41+
42+
_sequence = new MockSequence();
43+
_sessionMock.InSequence(_sequence).Setup(p => p.CreateChannelSession()).Returns(_channelMock.Object);
44+
_channelMock.InSequence(_sequence).Setup(p => p.Open());
45+
_channelMock.InSequence(_sequence).Setup(p => p.SendSubsystemRequest(_subsystemName)).Returns(false);
46+
_channelMock.InSequence(_sequence).Setup(p => p.Dispose());
47+
48+
_subsystemSession = new SubsystemSessionStub(_sessionMock.Object,
49+
_subsystemName,
50+
_operationTimeout);
51+
_subsystemSession.Disconnected += (sender, args) => _disconnectedRegister.Add(args);
52+
_subsystemSession.ErrorOccurred += (sender, args) => _errorOccurredRegister.Add(args);
53+
}
54+
55+
protected void Act()
56+
{
57+
try
58+
{
59+
_subsystemSession.Connect();
60+
Assert.Fail();
61+
}
62+
catch (SshException ex)
63+
{
64+
_actualException = ex;
65+
}
66+
}
67+
68+
[TestMethod]
69+
public void ConnectShouldHaveThrownSshException()
70+
{
71+
Assert.IsNotNull(_actualException);
72+
Assert.IsNull(_actualException.InnerException);
73+
Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, "Subsystem '{0}' could not be executed.", _subsystemName), _actualException.Message);
74+
}
75+
76+
[TestMethod]
77+
public void ChannelShouldBeNull()
78+
{
79+
Assert.IsNull(_subsystemSession.Channel);
80+
}
81+
82+
[TestMethod]
83+
public void DisconnectHasNeverFired()
84+
{
85+
Assert.AreEqual(0, _disconnectedRegister.Count);
86+
}
87+
88+
[TestMethod]
89+
public void ErrorOccurredHasNeverFired()
90+
{
91+
Assert.AreEqual(0, _errorOccurredRegister.Count);
92+
}
93+
94+
[TestMethod]
95+
public void IsOpenShouldReturnFalse()
96+
{
97+
Assert.IsFalse(_subsystemSession.IsOpen);
98+
}
99+
100+
[TestMethod]
101+
public void DisposeOnChannelShouldBeInvokedOnce()
102+
{
103+
_channelMock.Verify(p => p.Dispose(), Times.Once);
104+
}
105+
106+
[TestMethod]
107+
public void ErrorOccuredOnSessionShouldNoLongerBeSignaledViaErrorOccurredOnSubsystemSession()
108+
{
109+
_sessionMock.Raise(p => p.ErrorOccured += null, new ExceptionEventArgs(new Exception()));
110+
111+
Assert.AreEqual(0, _errorOccurredRegister.Count);
112+
}
113+
114+
[TestMethod]
115+
public void DisconnectedOnSessionShouldNoLongerBeSignaledViaDisconnectedOnSubsystemSession()
116+
{
117+
_sessionMock.Raise(p => p.Disconnected += null, new EventArgs());
118+
119+
Assert.AreEqual(0, _disconnectedRegister.Count);
120+
}
121+
}
122+
}

src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@
553553
<Compile Include="Classes\SubsystemSession_Connect_Disconnected.cs" />
554554
<Compile Include="Classes\SubsystemSession_Connect_Disposed.cs" />
555555
<Compile Include="Classes\SubsystemSession_Connect_NeverConnected.cs" />
556+
<Compile Include="Classes\SubsystemSession_Connect_SendSubsystemRequestFails.cs" />
556557
<Compile Include="Classes\SubsystemSession_Disconnect_Connected.cs" />
557558
<Compile Include="Classes\SubsystemSession_Disconnect_Disposed.cs" />
558559
<Compile Include="Classes\SubsystemSession_Disconnect_NeverConnected.cs" />

src/Renci.SshNet/SubsystemSession.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ protected SubsystemSession(ISession session, string subsystemName, int operation
9595
/// </summary>
9696
/// <exception cref="InvalidOperationException">The session is already connected.</exception>
9797
/// <exception cref="ObjectDisposedException">The method was called after the session was disposed.</exception>
98+
/// <exception cref="SshException">The channel session could not be opened, or the subsystem could not be executed.</exception>
9899
public void Connect()
99100
{
100101
EnsureNotDisposed();
@@ -116,7 +117,16 @@ public void Connect()
116117
_channel.Exception += Channel_Exception;
117118
_channel.Closed += Channel_Closed;
118119
_channel.Open();
119-
_channel.SendSubsystemRequest(_subsystemName);
120+
121+
if (!_channel.SendSubsystemRequest(_subsystemName))
122+
{
123+
// close channel session
124+
Disconnect();
125+
// signal subsystem failure
126+
throw new SshException(string.Format(CultureInfo.InvariantCulture,
127+
"Subsystem '{0}' could not be executed.",
128+
_subsystemName));
129+
}
120130

121131
OnChannelOpen();
122132
}

0 commit comments

Comments
 (0)