Skip to content

Commit 3e304ea

Browse files
committed
* Add TryWait overloads to (I)Session that return the result of the wait instead of throwing an exception.
* Introduce a ChannelCloseTimeout property on ConnectionInfo that allows you to control how long you want to wait for the server to send a SSH_MSG_CHANNEL_CLOSE. The default value is 1 second. * Use the newly introduced TryWait on Session with ChannelCloseTimeout as timeout in Channel to avoid throwing an exception when the server does not send a SSH_MSG_CHANNEL_CLOSE. Fixes #335. * Removed unused WaitHandle from Channel.
1 parent 66471e6 commit 3e304ea

File tree

61 files changed

+2554
-1696
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2554
-1696
lines changed

src/Renci.SshNet.Tests/Classes/Channels/ChannelDirectTcpipTest.cs

Lines changed: 104 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public class ChannelDirectTcpipTestTest : TestBase
2727
private uint _remoteWindowSize;
2828
private uint _remotePacketSize;
2929
private uint _remoteChannelNumber;
30+
private TimeSpan _channelCloseTimeout;
3031

3132
protected override void OnInit()
3233
{
@@ -40,8 +41,9 @@ protected override void OnInit()
4041
_port = (uint) random.Next(IPEndPoint.MinPort, IPEndPoint.MaxPort);
4142
_localChannelNumber = (uint) random.Next(0, int.MaxValue);
4243
_remoteWindowSize = (uint) random.Next(0, int.MaxValue);
43-
_remotePacketSize = (uint)random.Next(100, 200);
44-
_remoteChannelNumber = (uint)random.Next(0, int.MaxValue);
44+
_remotePacketSize = (uint) random.Next(100, 200);
45+
_remoteChannelNumber = (uint) random.Next(0, int.MaxValue);
46+
_channelCloseTimeout = TimeSpan.FromSeconds(random.Next(10, 20));
4547

4648
_sessionMock = new Mock<ISession>(MockBehavior.Strict);
4749
_forwardedPortMock = new Mock<IForwardedPort>(MockBehavior.Strict);
@@ -53,37 +55,42 @@ public void SocketShouldBeClosedAndBindShouldEndWhenForwardedPortSignalsClosingE
5355
{
5456
_sessionMock.Setup(p => p.IsConnected).Returns(true);
5557
_sessionMock.Setup(p => p.SendMessage(It.IsAny<ChannelOpenMessage>()))
56-
.Callback<Message>(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null,
57-
new MessageEventArgs<ChannelOpenConfirmationMessage>(
58-
new ChannelOpenConfirmationMessage(((ChannelOpenMessage)m).LocalChannelNumber, _remoteWindowSize, _remotePacketSize, _remoteChannelNumber))));
58+
.Callback<Message>(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null,
59+
new MessageEventArgs<ChannelOpenConfirmationMessage>(
60+
new ChannelOpenConfirmationMessage(((ChannelOpenMessage) m).LocalChannelNumber,
61+
_remoteWindowSize,
62+
_remotePacketSize,
63+
_remoteChannelNumber))));
5964
_sessionMock.Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
60-
.Callback<WaitHandle>(p => p.WaitOne(Session.Infinite));
65+
.Callback<WaitHandle>(p => p.WaitOne(Session.Infinite));
6166

6267
var localPortEndPoint = new IPEndPoint(IPAddress.Loopback, 8122);
6368
using (var localPortListener = new AsyncSocketListener(localPortEndPoint))
6469
{
6570
localPortListener.Start();
6671

6772
localPortListener.Connected += socket =>
68-
{
69-
var channel = new ChannelDirectTcpip(_sessionMock.Object, _localChannelNumber, _localWindowSize,
70-
_localPacketSize);
71-
channel.Open(_remoteHost, _port, _forwardedPortMock.Object, socket);
72-
73-
var closeForwardedPortThread =
74-
new Thread(() =>
75-
{
76-
// sleep for a short period to allow channel to actually start receiving from socket
77-
Thread.Sleep(100);
78-
// raise Closing event on forwarded port
79-
_forwardedPortMock.Raise(p => p.Closing += null, EventArgs.Empty);
80-
});
81-
closeForwardedPortThread.Start();
82-
83-
channel.Bind();
84-
85-
closeForwardedPortThread.Join();
86-
};
73+
{
74+
var channel = new ChannelDirectTcpip(_sessionMock.Object,
75+
_localChannelNumber,
76+
_localWindowSize,
77+
_localPacketSize);
78+
channel.Open(_remoteHost, _port, _forwardedPortMock.Object, socket);
79+
80+
var closeForwardedPortThread =
81+
new Thread(() =>
82+
{
83+
// sleep for a short period to allow channel to actually start receiving from socket
84+
Thread.Sleep(100);
85+
// raise Closing event on forwarded port
86+
_forwardedPortMock.Raise(p => p.Closing += null, EventArgs.Empty);
87+
});
88+
closeForwardedPortThread.Start();
89+
90+
channel.Bind();
91+
92+
closeForwardedPortThread.Join();
93+
};
8794

8895
var client = new Socket(localPortEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
8996
client.Connect(localPortEndPoint);
@@ -103,11 +110,14 @@ public void SocketShouldBeClosedAndBindShouldEndWhenOnErrorOccurredIsInvoked()
103110
{
104111
_sessionMock.Setup(p => p.IsConnected).Returns(true);
105112
_sessionMock.Setup(p => p.SendMessage(It.IsAny<ChannelOpenMessage>()))
106-
.Callback<Message>(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null,
107-
new MessageEventArgs<ChannelOpenConfirmationMessage>(
108-
new ChannelOpenConfirmationMessage(((ChannelOpenMessage)m).LocalChannelNumber, _remoteWindowSize, _remotePacketSize, _remoteChannelNumber))));
113+
.Callback<Message>(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null,
114+
new MessageEventArgs<ChannelOpenConfirmationMessage>(
115+
new ChannelOpenConfirmationMessage(((ChannelOpenMessage) m).LocalChannelNumber,
116+
_remoteWindowSize,
117+
_remotePacketSize,
118+
_remoteChannelNumber))));
109119
_sessionMock.Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
110-
.Callback<WaitHandle>(p => p.WaitOne(Session.Infinite));
120+
.Callback<WaitHandle>(p => p.WaitOne(Session.Infinite));
111121

112122
var localPortEndPoint = new IPEndPoint(IPAddress.Loopback, 8122);
113123
using (var localPortListener = new AsyncSocketListener(localPortEndPoint))
@@ -116,19 +126,21 @@ public void SocketShouldBeClosedAndBindShouldEndWhenOnErrorOccurredIsInvoked()
116126

117127
localPortListener.Connected += socket =>
118128
{
119-
var channel = new ChannelDirectTcpip(_sessionMock.Object, _localChannelNumber, _localWindowSize,
120-
_localPacketSize);
129+
var channel = new ChannelDirectTcpip(_sessionMock.Object,
130+
_localChannelNumber,
131+
_localWindowSize,
132+
_localPacketSize);
121133
channel.Open(_remoteHost, _port, _forwardedPortMock.Object, socket);
122134

123135
var signalSessionErrorOccurredThread =
124136
new Thread(() =>
125-
{
126-
// sleep for a short period to allow channel to actually start receiving from socket
127-
Thread.Sleep(100);
128-
// raise ErrorOccured event on session
129-
_sessionMock.Raise(s => s.ErrorOccured += null,
130-
new ExceptionEventArgs(new SystemException()));
131-
});
137+
{
138+
// sleep for a short period to allow channel to actually start receiving from socket
139+
Thread.Sleep(100);
140+
// raise ErrorOccured event on session
141+
_sessionMock.Raise(s => s.ErrorOccured += null,
142+
new ExceptionEventArgs(new SystemException()));
143+
});
132144
signalSessionErrorOccurredThread.Start();
133145

134146
channel.Bind();
@@ -152,34 +164,49 @@ public void SocketShouldBeClosedAndBindShouldEndWhenOnErrorOccurredIsInvoked()
152164
[TestMethod]
153165
public void SocketShouldBeClosedAndEofShouldBeSentToServerWhenClientShutsDownSocket()
154166
{
155-
_sessionMock.Setup(p => p.IsConnected).Returns(true);
156-
_sessionMock.Setup(p => p.SendMessage(It.IsAny<ChannelOpenMessage>()))
157-
.Callback<Message>(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null,
158-
new MessageEventArgs<ChannelOpenConfirmationMessage>(
159-
new ChannelOpenConfirmationMessage(((ChannelOpenMessage) m).LocalChannelNumber,
160-
_remoteWindowSize, _remotePacketSize, _remoteChannelNumber))));
161-
_sessionMock.Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
162-
.Callback<WaitHandle>(p => p.WaitOne(Session.Infinite));
163-
_sessionMock.Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
164-
_connectionInfoMock.Setup(p => p.Timeout).Returns(TimeSpan.FromSeconds(60));
165-
_sessionMock.Setup(p => p.TrySendMessage(It.IsAny<ChannelEofMessage>()))
166-
.Returns(true)
167-
.Callback<Message>(
168-
m => new Thread(() =>
169-
{
170-
Thread.Sleep(50);
171-
_sessionMock.Raise(s => s.ChannelEofReceived += null,
172-
new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
173-
}).Start());
174-
_sessionMock.Setup(p => p.TrySendMessage(It.IsAny<ChannelCloseMessage>()))
175-
.Returns(true)
176-
.Callback<Message>(
177-
m => new Thread(() =>
178-
{
179-
Thread.Sleep(50);
180-
_sessionMock.Raise(s => s.ChannelCloseReceived += null,
181-
new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
182-
}).Start());
167+
var sequence = new MockSequence();
168+
169+
_sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
170+
_sessionMock.InSequence(sequence)
171+
.Setup(p => p.SendMessage(It.IsAny<ChannelOpenMessage>()))
172+
.Callback<Message>(m => _sessionMock.Raise(p => p.ChannelOpenConfirmationReceived += null,
173+
new MessageEventArgs<ChannelOpenConfirmationMessage>(
174+
new ChannelOpenConfirmationMessage(((ChannelOpenMessage) m).LocalChannelNumber,
175+
_remoteWindowSize,
176+
_remotePacketSize,
177+
_remoteChannelNumber))));
178+
_sessionMock.InSequence(sequence)
179+
.Setup(p => p.WaitOnHandle(It.IsAny<EventWaitHandle>()))
180+
.Callback<WaitHandle>(p => p.WaitOne(Session.Infinite));
181+
_sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
182+
_sessionMock.InSequence(sequence)
183+
.Setup(p => p.TrySendMessage(It.IsAny<ChannelEofMessage>()))
184+
.Returns(true)
185+
.Callback<Message>(
186+
m => new Thread(() =>
187+
{
188+
Thread.Sleep(50);
189+
_sessionMock.Raise(s => s.ChannelEofReceived += null,
190+
new MessageEventArgs<ChannelEofMessage>(new ChannelEofMessage(_localChannelNumber)));
191+
}).Start());
192+
_sessionMock.InSequence(sequence).Setup(p => p.IsConnected).Returns(true);
193+
_sessionMock.InSequence(sequence)
194+
.Setup(p => p.TrySendMessage(It.IsAny<ChannelCloseMessage>()))
195+
.Returns(true)
196+
.Callback<Message>(
197+
m => new Thread(() =>
198+
{
199+
Thread.Sleep(50);
200+
_sessionMock.Raise(s => s.ChannelCloseReceived += null,
201+
new MessageEventArgs<ChannelCloseMessage>(new ChannelCloseMessage(_localChannelNumber)));
202+
}).Start());
203+
_sessionMock.InSequence(sequence).Setup(p => p.ConnectionInfo).Returns(_connectionInfoMock.Object);
204+
_connectionInfoMock.InSequence(sequence).Setup(p => p.ChannelCloseTimeout).Returns(_channelCloseTimeout);
205+
_sessionMock.InSequence(sequence)
206+
.Setup(p => p.TryWait(It.IsAny<EventWaitHandle>(), _channelCloseTimeout))
207+
.Callback<WaitHandle, TimeSpan>((waitHandle, channelCloseTimeout) => waitHandle.WaitOne())
208+
.Returns(WaitResult.Success);
209+
183210
var channelBindFinishedWaitHandle = new ManualResetEvent(false);
184211
Socket handler = null;
185212
ChannelDirectTcpip channel = null;
@@ -190,17 +217,19 @@ public void SocketShouldBeClosedAndEofShouldBeSentToServerWhenClientShutsDownSoc
190217
localPortListener.Start();
191218

192219
localPortListener.Connected += socket =>
193-
{
194-
channel = new ChannelDirectTcpip(_sessionMock.Object, _localChannelNumber, _localWindowSize,
195-
_localPacketSize);
196-
channel.Open(_remoteHost, _port, _forwardedPortMock.Object, socket);
197-
channel.Bind();
198-
channel.Dispose();
220+
{
221+
channel = new ChannelDirectTcpip(_sessionMock.Object,
222+
_localChannelNumber,
223+
_localWindowSize,
224+
_localPacketSize);
225+
channel.Open(_remoteHost, _port, _forwardedPortMock.Object, socket);
226+
channel.Bind();
227+
channel.Dispose();
199228

200-
handler = socket;
229+
handler = socket;
201230

202-
channelBindFinishedWaitHandle.Set();
203-
};
231+
channelBindFinishedWaitHandle.Set();
232+
};
204233

205234
var client = new Socket(localPortEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
206235
client.Connect(localPortEndPoint);

0 commit comments

Comments
 (0)