From fbd52af36a77628afff27a25556192ac03eabbbb Mon Sep 17 00:00:00 2001 From: Glen Kleidon Date: Tue, 1 Nov 2022 23:27:36 +1100 Subject: [PATCH 1/6] SshNet.ShellStream. Removed unused USING, Added Stopping event. Updated comment about the dispose call. --- src/Renci.SshNet/ShellStream.cs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Renci.SshNet/ShellStream.cs b/src/Renci.SshNet/ShellStream.cs index 3274fe19c..304aea93b 100644 --- a/src/Renci.SshNet/ShellStream.cs +++ b/src/Renci.SshNet/ShellStream.cs @@ -1,12 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; +using Renci.SshNet.Abstractions; using Renci.SshNet.Channels; using Renci.SshNet.Common; -using System.Threading; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; using System.Text.RegularExpressions; -using Renci.SshNet.Abstractions; +using System.Threading; namespace Renci.SshNet { @@ -36,6 +36,11 @@ public class ShellStream : Stream /// public event EventHandler ErrorOccurred; + /// + /// Occurs when the closing channel has requested that the stream close + /// + public event EventHandler Stopping; + /// /// Gets a value that indicates whether data is available on the to be read. /// @@ -772,7 +777,13 @@ private void Session_Disconnected(object sender, EventArgs e) private void Channel_Closed(object sender, ChannelEventArgs e) { - // TODO: Do we need to call dispose here ?? + ThreadAbstraction.ExecuteThread(() => Stopping(this, new EventArgs())); + // FYI: Dispose should Probably NOT be called here as a class should NOT dispose of + // itself: It will be owned by someone else. Calling it here means it will be disappear + // out from under the owner. BUT removing it here now is a breaking change and because there + // is a workaround in the Flush method (raisign exception). I added the notify event + // here instead so that the user knows the channel is no longer usable. + // -- REMOVE NEXT LINE on next major release (## noted 2020.0.0.1) Dispose(); } From 7f89bd0fb4be87070747a69eaefb1ff1b617f932 Mon Sep 17 00:00:00 2001 From: Glen Kleidon Date: Tue, 1 Nov 2022 23:34:14 +1100 Subject: [PATCH 2/6] ShellStream Added Starting event for completeness. --- src/Renci.SshNet/ShellStream.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Renci.SshNet/ShellStream.cs b/src/Renci.SshNet/ShellStream.cs index 304aea93b..6ce5e8dc1 100644 --- a/src/Renci.SshNet/ShellStream.cs +++ b/src/Renci.SshNet/ShellStream.cs @@ -41,6 +41,11 @@ public class ShellStream : Stream /// public event EventHandler Stopping; + /// + /// Occurs when a channel has been established and the stream is about to start + /// + public event EventHandler Starting; + /// /// Gets a value that indicates whether data is available on the to be read. /// @@ -108,6 +113,7 @@ internal ShellStream(ISession session, string terminalName, uint columns, uint r { throw new SshException("The request to start a shell was not accepted by the server. Consult the server log for more information."); } + Starting(this, new EventArgs()); } catch { From 917814e33d6e72f8c4e85fc849865d88991e26e1 Mon Sep 17 00:00:00 2001 From: Glen Kleidon Date: Wed, 2 Nov 2022 00:03:20 +1100 Subject: [PATCH 3/6] Fixed null object reference when starting/stopping not assigned. --- src/Renci.SshNet/ShellStream.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Renci.SshNet/ShellStream.cs b/src/Renci.SshNet/ShellStream.cs index 6ce5e8dc1..125626385 100644 --- a/src/Renci.SshNet/ShellStream.cs +++ b/src/Renci.SshNet/ShellStream.cs @@ -113,7 +113,10 @@ internal ShellStream(ISession session, string terminalName, uint columns, uint r { throw new SshException("The request to start a shell was not accepted by the server. Consult the server log for more information."); } - Starting(this, new EventArgs()); + if (Starting != null) + { + Starting(this, new EventArgs()); + } } catch { @@ -783,7 +786,11 @@ private void Session_Disconnected(object sender, EventArgs e) private void Channel_Closed(object sender, ChannelEventArgs e) { - ThreadAbstraction.ExecuteThread(() => Stopping(this, new EventArgs())); + if (Stopping != null) + { + ThreadAbstraction.ExecuteThread(() => Stopping(this, new EventArgs())); + } + // FYI: Dispose should Probably NOT be called here as a class should NOT dispose of // itself: It will be owned by someone else. Calling it here means it will be disappear // out from under the owner. BUT removing it here now is a breaking change and because there From d99ce1c784a983a51941f046ffed42c6500d15e7 Mon Sep 17 00:00:00 2001 From: Glen Kleidon Date: Wed, 2 Nov 2022 00:56:56 +1100 Subject: [PATCH 4/6] Updated Factory interfaces and finally the call to Client in preparation for the test cases. --- .../Classes/ShellStreamTest.cs | 1 + src/Renci.SshNet/IServiceFactory.cs | 9 ++++++-- src/Renci.SshNet/ServiceFactory.cs | 9 ++++++-- src/Renci.SshNet/ShellStream.cs | 17 ++++++++------- src/Renci.SshNet/SshClient.cs | 21 ++++++++++++------- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/ShellStreamTest.cs b/src/Renci.SshNet.Tests/Classes/ShellStreamTest.cs index ecec9d30e..d62475f4d 100644 --- a/src/Renci.SshNet.Tests/Classes/ShellStreamTest.cs +++ b/src/Renci.SshNet.Tests/Classes/ShellStreamTest.cs @@ -46,6 +46,7 @@ protected override void OnInit() _sessionMock = new Mock(MockBehavior.Strict); _connectionInfoMock = new Mock(MockBehavior.Strict); _channelSessionMock = new Mock(MockBehavior.Strict); + } [TestMethod] // issue #2190 diff --git a/src/Renci.SshNet/IServiceFactory.cs b/src/Renci.SshNet/IServiceFactory.cs index dad93e237..559e85552 100644 --- a/src/Renci.SshNet/IServiceFactory.cs +++ b/src/Renci.SshNet/IServiceFactory.cs @@ -78,7 +78,9 @@ internal partial interface IServiceFactory /// The terminal height in pixels. /// The terminal height in pixels. /// Size of the buffer. - /// The terminal mode values. + /// List of Terminal Modes + /// Optional Starting Event handler + /// Optional Stopping Event handler /// /// The created instance. /// @@ -100,7 +102,10 @@ ShellStream CreateShellStream(ISession session, uint width, uint height, IDictionary terminalModeValues, - int bufferSize); + int bufferSize, + EventHandler starting = null, + EventHandler stopping = null + ); /// /// Creates an that encloses a path in double quotes, and escapes diff --git a/src/Renci.SshNet/ServiceFactory.cs b/src/Renci.SshNet/ServiceFactory.cs index f04e6116d..498dcba63 100644 --- a/src/Renci.SshNet/ServiceFactory.cs +++ b/src/Renci.SshNet/ServiceFactory.cs @@ -161,6 +161,9 @@ public ISftpResponseFactory CreateSftpResponseFactory() /// The terminal height in pixels. /// The terminal mode values. /// The size of the buffer. + /// Optional Starting Event handler + /// Optional Stopping Event handler + /// /// /// The created instance. /// @@ -175,9 +178,9 @@ public ISftpResponseFactory CreateSftpResponseFactory() /// to the drawable area of the window. /// /// - public ShellStream CreateShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues, int bufferSize) + public ShellStream CreateShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues, int bufferSize, EventHandler starting = null, EventHandler stopping = null) { - return new ShellStream(session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize); + return new ShellStream(session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize, starting, stopping); } /// @@ -251,5 +254,7 @@ public ISocketFactory CreateSocketFactory() { return new SocketFactory(); } + + } } diff --git a/src/Renci.SshNet/ShellStream.cs b/src/Renci.SshNet/ShellStream.cs index 125626385..cebfe9a53 100644 --- a/src/Renci.SshNet/ShellStream.cs +++ b/src/Renci.SshNet/ShellStream.cs @@ -84,11 +84,13 @@ internal int BufferSize /// The terminal height in pixels. /// The terminal height in pixels. /// The terminal mode values. - /// The size of the buffer. + /// Size of the buffer. + /// Optional Starting Event handler + /// Optional Stopping Event handler /// The channel could not be opened. /// The pseudo-terminal request was not accepted by the server. /// The request to start a shell was not accepted by the server. - internal ShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues, int bufferSize) + internal ShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues, int bufferSize, EventHandler starting = null, EventHandler stopping = null) { _encoding = session.ConnectionInfo.Encoding; _session = session; @@ -101,9 +103,14 @@ internal ShellStream(ISession session, string terminalName, uint columns, uint r _channel.Closed += Channel_Closed; _session.Disconnected += Session_Disconnected; _session.ErrorOccured += Session_ErrorOccured; - + Starting = starting; + Stopping = stopping; try { + if (Starting != null) + { + Starting(this, new EventArgs()); + } _channel.Open(); if (!_channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues)) { @@ -113,10 +120,6 @@ internal ShellStream(ISession session, string terminalName, uint columns, uint r { throw new SshException("The request to start a shell was not accepted by the server. Consult the server log for more information."); } - if (Starting != null) - { - Starting(this, new EventArgs()); - } } catch { diff --git a/src/Renci.SshNet/SshClient.cs b/src/Renci.SshNet/SshClient.cs index 881a173d4..7b2081ee5 100644 --- a/src/Renci.SshNet/SshClient.cs +++ b/src/Renci.SshNet/SshClient.cs @@ -1,10 +1,10 @@ -using System; +using Renci.SshNet.Common; +using System; using System.Collections.Generic; -using System.IO; -using System.Text; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Net; -using Renci.SshNet.Common; +using System.Text; namespace Renci.SshNet { @@ -413,6 +413,8 @@ public Shell CreateShell(Encoding encoding, string input, Stream output, Stream /// The terminal height in pixels. /// The terminal height in pixels. /// The size of the buffer. + /// Optional Starting Event handler + /// Optional Stopping Event handler /// /// The created instance. /// @@ -427,9 +429,9 @@ public Shell CreateShell(Encoding encoding, string input, Stream output, Stream /// to the drawable area of the window. /// /// - public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize) + public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, EventHandler starting = null, EventHandler stopping = null) { - return CreateShellStream(terminalName, columns, rows, width, height, bufferSize, null); + return CreateShellStream(terminalName, columns, rows, width, height, bufferSize, null, starting, stopping); } /// @@ -442,6 +444,9 @@ public ShellStream CreateShellStream(string terminalName, uint columns, uint row /// The terminal height in pixels. /// The size of the buffer. /// The terminal mode values. + /// Optional Starting Event handler + /// Optional Stopping Event handler + /// /// /// The created instance. /// @@ -456,11 +461,11 @@ public ShellStream CreateShellStream(string terminalName, uint columns, uint row /// to the drawable area of the window. /// /// - public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary terminalModeValues) + public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary terminalModeValues, EventHandler starting = null, EventHandler stopping = null) { EnsureSessionIsOpen(); - return ServiceFactory.CreateShellStream(Session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize); + return ServiceFactory.CreateShellStream(Session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize, starting, stopping); } /// From 594cedb8bddf992ac014829a5982308ce9cd57a1 Mon Sep 17 00:00:00 2001 From: Glen Kleidon Date: Wed, 2 Nov 2022 01:11:02 +1100 Subject: [PATCH 5/6] Added update to test project for .Net.6 --- src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj index 3b38bfbe8..ea589b1ac 100644 --- a/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj +++ b/src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj @@ -99,6 +99,20 @@ $(MSBuildProgramFiles32)\Microsoft Visual Studio\2019\Preview\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll $(MSTestV1UnitTestFrameworkAssemblyCandidate) + + + $(MSBuildProgramFiles32)\Microsoft Visual Studio\2022\Enterprise\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll + $(MSTestV1UnitTestFrameworkAssemblyCandidate) + + $(MSBuildProgramFiles32)\Microsoft Visual Studio\2022\Professional\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll + $(MSTestV1UnitTestFrameworkAssemblyCandidate) + + $(MSBuildProgramFiles32)\Microsoft Visual Studio\2022\Community\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll + $(MSTestV1UnitTestFrameworkAssemblyCandidate) + + $(MSBuildProgramFiles32)\Microsoft Visual Studio\2022\Preview\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll + $(MSTestV1UnitTestFrameworkAssemblyCandidate) + From 5fe0c0657e5ec0f36f41ee45cfbcfede6cc50687 Mon Sep 17 00:00:00 2001 From: Glen Kleidon Date: Wed, 2 Nov 2022 01:37:24 +1100 Subject: [PATCH 6/6] OK, fixed the existing tests. --- ...eightAndBufferSizeAndTerminalModes_Connected.cs | 14 +++++++++++--- ...RowsAndWidthAndHeightAndBufferSize_Connected.cs | 11 +++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs index df1ea835f..1383d26fd 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSizeAndTerminalModes_Connected.cs @@ -17,6 +17,8 @@ public class SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWid private uint _heightRows; private uint _widthPixels; private uint _heightPixels; + private EventHandler _starting; + private EventHandler _stopping; private Dictionary _terminalModes; private int _bufferSize; private ShellStream _expected; @@ -59,7 +61,9 @@ protected override void SetupMocks() _widthPixels, _heightPixels, _terminalModes, - _bufferSize)) + _bufferSize, + _starting, + _stopping)) .Returns(_expected); } @@ -79,7 +83,9 @@ protected override void Act() _widthPixels, _heightPixels, _bufferSize, - _terminalModes); + _terminalModes, + _starting, + _stopping); } [TestMethod] @@ -92,7 +98,9 @@ public void CreateShellStreamOnServiceFactoryShouldBeInvokedOnce() _widthPixels, _heightPixels, _terminalModes, - _bufferSize), + _bufferSize, + _starting, + _stopping), Times.Once); } diff --git a/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs b/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs index 337f9bce3..fb35af09e 100644 --- a/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs +++ b/src/Renci.SshNet.Tests/Classes/SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWidthAndHeightAndBufferSize_Connected.cs @@ -18,6 +18,8 @@ public class SshClientTest_CreateShellStream_TerminalNameAndColumnsAndRowsAndWid private uint _widthPixels; private uint _heightPixels; private int _bufferSize; + private EventHandler _starting; + private EventHandler _stopping; private ShellStream _expected; private ShellStream _actual; @@ -57,7 +59,10 @@ protected override void SetupMocks() _widthPixels, _heightPixels, null, - _bufferSize)) + _bufferSize, + _starting, + _stopping + )) .Returns(_expected); } @@ -89,7 +94,9 @@ public void CreateShellStreamOnServiceFactoryShouldBeInvokedOnce() _widthPixels, _heightPixels, null, - _bufferSize), + _bufferSize, + _starting, + _stopping), Times.Once); }