Ver Fonte

Fix NRE when stopping port without ever starting it.

Fix NRE when stopping port without ever starting it.
Simplify code, and eliminate extra ManualResetEvent.
drieseng há 9 anos atrás
pai
commit
5daf00d7c7

+ 35 - 39
src/Renci.SshNet/ForwardedPortDynamic.NET.cs

@@ -16,10 +16,6 @@ namespace Renci.SshNet
         private Socket _listener;
         private int _pendingRequests;
 
-#if FEATURE_SOCKET_EAP
-        private ManualResetEvent _stoppingListener;
-#endif // FEATURE_SOCKET_EAP
-
         partial void InternalStart()
         {
             var ip = IPAddress.Any;
@@ -49,33 +45,35 @@ namespace Renci.SshNet
                     try
                     {
 #if FEATURE_SOCKET_EAP
-                        _stoppingListener = new ManualResetEvent(false);
-
                         StartAccept();
-
-                        _stoppingListener.WaitOne();
 #elif FEATURE_SOCKET_APM
-                        while (true)
-                        {
-                            // accept new inbound connection
-                            var asyncResult = _listener.BeginAccept(AcceptCallback, _listener);
-                            // wait for the connection to be established
-                            asyncResult.AsyncWaitHandle.WaitOne();
-                        }
-                    }
-                    catch (ObjectDisposedException)
-                    {
-                        // BeginAccept will throw an ObjectDisposedException when the
-                        // socket is closed
+                        _listener.BeginAccept(AcceptCallback, _listener);
 #elif FEATURE_SOCKET_TAP
 #error Accepting new socket connections is not implemented.
 #else
 #error Accepting new socket connections is not implemented.
 #endif
+
+                        // wait until listener is stopped
+                        _listenerCompleted.WaitOne();
+                    }
+                    catch (ObjectDisposedException)
+                    {
+                        // BeginAccept / AcceptAsync will throw an ObjectDisposedException when the
+                        // server is closed before the listener has started accepting connections.
+                        //
+                        // As we start accepting connection on a separate thread, this is possible
+                        // when the listener is stopped right after it was started.
+
+                        // mark listener stopped
+                        _listenerCompleted.Set();
                     }
                     catch (Exception ex)
                     {
                         RaiseExceptionEvent(ex);
+
+                        // mark listener stopped
+                        _listenerCompleted.Set();
                     }
                     finally
                     {
@@ -84,9 +82,6 @@ namespace Renci.SshNet
                             Session.ErrorOccured -= Session_ErrorOccured;
                             Session.Disconnected -= Session_Disconnected;
                         }
-
-                        // mark listener stopped
-                        _listenerCompleted.Set();
                     }
                 });
         }
@@ -115,15 +110,24 @@ namespace Renci.SshNet
 
         private void AcceptCompleted(object sender, SocketAsyncEventArgs acceptAsyncEventArgs)
         {
+            if (acceptAsyncEventArgs.SocketError == SocketError.OperationAborted)
+            {
+                // server was stopped
+                return;
+            }
+
             if (acceptAsyncEventArgs.SocketError != SocketError.Success)
             {
+                // accept new connection
                 StartAccept();
+                // dispose broken socket
                 acceptAsyncEventArgs.AcceptSocket.Dispose();
                 return;
             }
 
+            // accept new connection
             StartAccept();
-
+            // process connection
             ProcessAccept(acceptAsyncEventArgs.AcceptSocket);
         }
 #elif FEATURE_SOCKET_APM
@@ -140,11 +144,14 @@ namespace Renci.SshNet
             }
             catch (ObjectDisposedException)
             {
-                // when the socket is closed, an ObjectDisposedException is thrown
+                // when the server socket is closed, an ObjectDisposedException is thrown
                 // by Socket.EndAccept(IAsyncResult)
                 return;
             }
 
+            // accept new connection
+            _listener.BeginAccept(AcceptCallback, _listener);
+            // process connection
             ProcessAccept(clientSocket);
         }
 #endif
@@ -284,14 +291,11 @@ namespace Renci.SshNet
             if (!IsStarted)
                 return;
 
-#if FEATURE_SOCKET_EAP
-            _stoppingListener.Set();
-#endif // FEATURE_SOCKET_EAP
-
             // close listener socket
             _listener.Dispose();
-            // wait for listener loop to finish
-            _listenerCompleted.WaitOne();
+
+            // allow listener thread to stop
+            _listenerCompleted.Set();
         }
 
         /// <summary>
@@ -334,14 +338,6 @@ namespace Renci.SshNet
                     _listener.Dispose();
                     _listener = null;
                 }
-
-#if FEATURE_SOCKET_EAP
-                if (_stoppingListener != null)
-                {
-                    _stoppingListener.Dispose();
-                    _stoppingListener = null;
-                }
-#endif // FEATURE_SOCKET_EAP
             }
         }
 

+ 36 - 36
src/Renci.SshNet/ForwardedPortLocal.NET.cs

@@ -16,10 +16,6 @@ namespace Renci.SshNet
         private Socket _listener;
         private int _pendingRequests;
 
-#if FEATURE_SOCKET_EAP
-        private ManualResetEvent _stoppingListener;
-#endif // FEATURE_SOCKET_EAP
-
         partial void InternalStart()
         {
             var addr = DnsAbstraction.GetHostAddresses(BoundHost)[0];
@@ -46,36 +42,43 @@ namespace Renci.SshNet
                     try
                     {
 #if FEATURE_SOCKET_EAP
-                        _stoppingListener = new ManualResetEvent(false);
                         StartAccept();
-                        _stoppingListener.WaitOne();
 #elif FEATURE_SOCKET_APM
-                        while (true)
-                        {
-                            // accept new inbound connection
-                            var asyncResult = _listener.BeginAccept(AcceptCallback, _listener);
-                            // wait for the connection to be established
-                            asyncResult.AsyncWaitHandle.WaitOne();
-                        }
+                        _listener.BeginAccept(AcceptCallback, _listener);
 #elif FEATURE_SOCKET_TAP
 #error Accepting new socket connections is not implemented.
 #else
 #error Accepting new socket connections is not implemented.
 #endif
+
+                        // wait until listener is stopped
+                        _listenerTaskCompleted.WaitOne();
                     }
                     catch (ObjectDisposedException)
                     {
-                        // BeginAccept will throw an ObjectDisposedException when the
-                        // socket is closed
+                        // BeginAccept / AcceptAsync will throw an ObjectDisposedException when the
+                        // server is closed before the listener has started accepting connections.
+                        //
+                        // As we start accepting connection on a separate thread, this is possible
+                        // when the listener is stopped right after it was started.
+
+                        // mark listener stopped
+                        _listenerTaskCompleted.Set();
                     }
                     catch (Exception ex)
                     {
                         RaiseExceptionEvent(ex);
+
+                        // mark listener stopped
+                        _listenerTaskCompleted.Set();
                     }
                     finally
                     {
-                        // mark listener stopped
-                        _listenerTaskCompleted.Set();
+                        if (Session != null)
+                        {
+                            Session.Disconnected -= Session_Disconnected;
+                            Session.ErrorOccured -= Session_ErrorOccured;
+                        }
                     }
                 });
         }
@@ -94,15 +97,24 @@ namespace Renci.SshNet
 
         private void AcceptCompleted(object sender, SocketAsyncEventArgs acceptAsyncEventArgs)
         {
+            if (acceptAsyncEventArgs.SocketError == SocketError.OperationAborted)
+            {
+                // server was stopped
+                return;
+            }
+
             if (acceptAsyncEventArgs.SocketError != SocketError.Success)
             {
+                // accept new connection
                 StartAccept();
+                // dispose broken socket
                 acceptAsyncEventArgs.AcceptSocket.Dispose();
                 return;
             }
 
+            // accept new connection
             StartAccept();
-
+            // process connection
             ProcessAccept(acceptAsyncEventArgs.AcceptSocket);
         }
 #elif FEATURE_SOCKET_APM
@@ -119,11 +131,14 @@ namespace Renci.SshNet
             }
             catch (ObjectDisposedException)
             {
-                // when the socket is closed, an ObjectDisposedException is thrown
+                // when the server socket is closed, an ObjectDisposedException is thrown
                 // by Socket.EndAccept(IAsyncResult)
                 return;
             }
 
+            // accept new connection
+            _listener.BeginAccept(AcceptCallback, _listener);
+            // process connection
             ProcessAccept(clientSocket);
         }
 #endif
@@ -206,17 +221,10 @@ namespace Renci.SshNet
             if (!IsStarted)
                 return;
 
-            Session.Disconnected -= Session_Disconnected;
-            Session.ErrorOccured -= Session_ErrorOccured;
-
-#if FEATURE_SOCKET_EAP
-            _stoppingListener.Set();
-#endif // FEATURE_SOCKET_EAP
-
             // close listener socket
             _listener.Dispose();
-            // wait for listener loop to finish
-            _listenerTaskCompleted.WaitOne();
+            // allow listener thread to stop
+            _listenerTaskCompleted.Set();
         }
 
         partial void InternalDispose(bool disposing)
@@ -228,14 +236,6 @@ namespace Renci.SshNet
                     _listener.Dispose();
                     _listener = null;
                 }
-
-#if FEATURE_SOCKET_EAP
-                if (_stoppingListener != null)
-                {
-                    _stoppingListener.Dispose();
-                    _stoppingListener = null;
-                }
-#endif // FEATURE_SOCKET_EAP
             }
         }