public void ProcessExited(int exitCode)
        {
            Trace.TraceInformation("Received host client process exit notification, remote session {0}", _remoteSessionManager.RemoteSession.Id);

            try
            {
                // remote session is now disconnected
                _remoteSessionManager.RemoteSession.State = RemoteSessionState.Disconnected;

                // process exit code
                _remoteSessionManager.RemoteSession.ExitCode = exitCode;

                // stop monitoring the remote session owner activity, if enabled
                if (_remoteSessionManager.ClientIdleTimeout != null)
                {
                    _remoteSessionManager.ClientIdleTimeout.Cancel();
                }

                // release the communication pipes, if any
                if (_remoteSessionManager.Pipes != null)
                {
                    _remoteSessionManager.Pipes.DeletePipes();
                }

                if (_remoteSessionManager.RemoteSession.Reconnect)
                {
                    _remoteSessionManager.RemoteSession.Reconnect   = false;
                    _remoteSessionManager.HostClient.ProcessStarted = false;
                    _remoteSessionManager.RemoteSession.State       = RemoteSessionState.Connecting;
                }

                _remoteSessionManager.SendMessage(new RemoteSessionMessage {
                    Type = MessageType.Disconnected, Prefix = "disconnected"
                });
            }
            catch (Exception exc)
            {
                Trace.TraceError("Failed to cleanup disconnected session, remote session {0} ({1})", _remoteSessionManager.RemoteSession.Id, exc);
                throw;
            }
        }
Beispiel #2
0
        public void ProcessExited(int exitCode)
        {
            Trace.TraceInformation("Received host client process exit notification, remote session {0}", _remoteSessionManager.RemoteSession.Id);

            try
            {
                // remote session is now disconnected
                _remoteSessionManager.RemoteSession.State = RemoteSessionState.Disconnected;

                // process exit code
                _remoteSessionManager.RemoteSession.ExitCode = exitCode;

                // stop monitoring the remote session owner activity, if enabled
                if (_remoteSessionManager.ClientIdleTimeout != null)
                {
                    _remoteSessionManager.ClientIdleTimeout.Cancel();
                    _remoteSessionManager.ClientIdleTimeout.Dispose();
                }

                // release the communication pipes, if any
                if (_remoteSessionManager.Pipes != null)
                {
                    _remoteSessionManager.Pipes.DeletePipes();
                }

                // if the remote session is marked for reconnection, reconnect it
                if (_remoteSessionManager.RemoteSession.Reconnect)
                {
                    _remoteSessionManager.RemoteSession.Reconnect     = false;
                    _remoteSessionManager.RemoteSession.BrowserResize = null;
                    _remoteSessionManager.HostClient.ProcessStarted   = false;
                    if (_remoteSessionManager.ClientIdleTimeout != null)
                    {
                        _remoteSessionManager.ClientIdleTimeout = new CancellationTokenSource();
                    }
                    _remoteSessionManager.RemoteSession.State = RemoteSessionState.Connecting;
                    _remoteSessionManager.SendMessage(new RemoteSessionMessage {
                        Type = MessageType.Disconnected, Prefix = "disconnected"
                    });
                }
                // otherwise, redirect to the login page (or the hosts dashboard in enterprise mode)
                else
                {
                    // CAUTION! exit code list is not exhaustive (that's why RemoteSession.ExitCode is an int)
                    RemoteSessionExitCode _exitCode;
                    if (!Enum.TryParse(_remoteSessionManager.RemoteSession.ExitCode.ToString(), out _exitCode))
                    {
                        _exitCode = RemoteSessionExitCode.Unknown;
                    }

                    // if using a connection service, send the connection state and exit code
                    if (_remoteSessionManager.RemoteSession.ConnectionService)
                    {
                        _connectionClient.SetConnectionState(
                            _remoteSessionManager.RemoteSession.Id,
                            string.IsNullOrEmpty(_remoteSessionManager.RemoteSession.VMAddress) ? _remoteSessionManager.RemoteSession.ServerAddress : _remoteSessionManager.RemoteSession.VMAddress,
                            GuidHelper.ConvertFromString(_remoteSessionManager.RemoteSession.VMGuid),
                            _remoteSessionManager.RemoteSession.State);

                        _connectionClient.SetConnectionExitCode(
                            _remoteSessionManager.RemoteSession.Id,
                            string.IsNullOrEmpty(_remoteSessionManager.RemoteSession.VMAddress) ? _remoteSessionManager.RemoteSession.ServerAddress : _remoteSessionManager.RemoteSession.VMAddress,
                            GuidHelper.ConvertFromString(_remoteSessionManager.RemoteSession.VMGuid),
                            _exitCode);
                    }

                    CleanupDisconnectedSession(_exitCode);
                }
            }
            catch (Exception exc)
            {
                Trace.TraceError("Failed to handle session disconnect, remote session {0} ({1})", _remoteSessionManager.RemoteSession.Id, exc);
                throw;
            }
        }
        public void ProcessExited(int exitCode)
        {
            Trace.TraceInformation("Received host client process exit notification, remote session {0}", _remoteSessionManager.RemoteSession.Id);

            try
            {
                // remote session is now disconnected
                _remoteSessionManager.RemoteSession.State = RemoteSessionState.Disconnected;

                // process exit code
                _remoteSessionManager.RemoteSession.ExitCode = exitCode;

                // stop monitoring the remote session owner activity, if enabled
                if (_remoteSessionManager.ClientIdleTimeout != null)
                {
                    _remoteSessionManager.ClientIdleTimeout.Cancel();
                    _remoteSessionManager.ClientIdleTimeout.Dispose();
                }

                // release the communication pipes, if any
                if (_remoteSessionManager.Pipes != null)
                {
                    _remoteSessionManager.Pipes.DeletePipes();
                }

                // if the remote session is marked for reconnection, reconnect it
                if (_remoteSessionManager.RemoteSession.Reconnect)
                {
                    _remoteSessionManager.RemoteSession.Reconnect     = false;
                    _remoteSessionManager.RemoteSession.BrowserResize = null;
                    _remoteSessionManager.HostClient.ProcessStarted   = false;
                    if (_remoteSessionManager.ClientIdleTimeout != null)
                    {
                        _remoteSessionManager.ClientIdleTimeout = new CancellationTokenSource();
                    }
                    _remoteSessionManager.RemoteSession.State = RemoteSessionState.Connecting;
                }

                // send a disconnect notification to the browser
                _remoteSessionManager.SendMessage(new RemoteSessionMessage {
                    Type = MessageType.Disconnected, Prefix = "disconnected"
                });

                if (_remoteSessionManager.RemoteSession.State == RemoteSessionState.Disconnected)
                {
                    // if using a connection service, send the connection state and exit code
                    if (_remoteSessionManager.RemoteSession.ConnectionService)
                    {
                        _connectionClient.SetConnectionState(
                            _remoteSessionManager.RemoteSession.Id,
                            string.IsNullOrEmpty(_remoteSessionManager.RemoteSession.VMAddress) ? _remoteSessionManager.RemoteSession.ServerAddress : _remoteSessionManager.RemoteSession.VMAddress,
                            GuidHelper.ConvertFromString(_remoteSessionManager.RemoteSession.VMGuid),
                            _remoteSessionManager.RemoteSession.State);

                        // CAUTION! exit code list is not exhaustive (that's why RemoteSession.ExitCode is an int)
                        RemoteSessionExitCode _exitCode;
                        if (!Enum.TryParse(_remoteSessionManager.RemoteSession.ExitCode.ToString(), out _exitCode))
                        {
                            _exitCode = RemoteSessionExitCode.Unknown;
                        }

                        _connectionClient.SetConnectionExitCode(
                            _remoteSessionManager.RemoteSession.Id,
                            string.IsNullOrEmpty(_remoteSessionManager.RemoteSession.VMAddress) ? _remoteSessionManager.RemoteSession.ServerAddress : _remoteSessionManager.RemoteSession.VMAddress,
                            GuidHelper.ConvertFromString(_remoteSessionManager.RemoteSession.VMGuid),
                            _exitCode);
                    }

                    // cleanup
                    try
                    {
                        _application.Lock();

                        // unregister the remote session at the application level
                        var remoteSessions = (IDictionary <Guid, RemoteSession>)_application[HttpApplicationStateVariables.RemoteSessions.ToString()];

                        if (remoteSessions.ContainsKey(_remoteSessionManager.RemoteSession.Id))
                        {
                            remoteSessions.Remove(_remoteSessionManager.RemoteSession.Id);
                        }

                        // retrieve the remote session guest(s)
                        var guests         = new List <SharingInfo>();
                        var sharedSessions = (IDictionary <Guid, SharingInfo>)_application[HttpApplicationStateVariables.SharedRemoteSessions.ToString()];
                        foreach (var sharingInfo in sharedSessions.Values)
                        {
                            if (sharingInfo.RemoteSession.Id.Equals(_remoteSessionManager.RemoteSession.Id))
                            {
                                guests.Add(sharingInfo);
                            }
                        }

                        // remove them
                        foreach (var guest in guests)
                        {
                            if (guest.GuestInfo.Active && guest.HttpSession != null)
                            {
                                if (guest.HttpSession[HttpSessionStateVariables.RemoteSession.ToString()] != null)
                                {
                                    guest.HttpSession.Remove(HttpSessionStateVariables.RemoteSession.ToString());
                                }

                                if (guest.HttpSession[HttpSessionStateVariables.GuestInfo.ToString()] != null)
                                {
                                    guest.HttpSession.Remove(HttpSessionStateVariables.GuestInfo.ToString());
                                }

                                if (guest.RemoteSession.ActiveGuests > 0)
                                {
                                    guest.RemoteSession.ActiveGuests--;
                                }
                            }
                            sharedSessions.Remove(guest.GuestInfo.Id);
                        }

                        // recycle the application pool when there is no active remote session
                        bool idleAppPoolRecycling;
                        if (!bool.TryParse(ConfigurationManager.AppSettings["IdleAppPoolRecycling"], out idleAppPoolRecycling))
                        {
                            idleAppPoolRecycling = true;
                        }

                        /*
                         *
                         * it may seem a bit extreme, but the garbage collector doesn't return the freed memory to the operating system
                         * instead, it makes it available to the memory workspace of the application pool process, which in turn uses it for faster memory allocation later
                         * while this is fine for most usage, this becomes critical when the OS is under memory pressure
                         * if that occurs, the process is meant to return its unused memory to the OS
                         * in reality, this is not always true; so the system becomes slow (hdd swap) and unstable
                         *
                         * memory usage of a process under Windows: https://dzone.com/articles/windows-process-memory-usage-demystified
                         * tool: https://technet.microsoft.com/en-us/sysinternals/vmmap.aspx
                         * garbage collector: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals
                         * reallocation of freed memory: https://stackoverflow.com/questions/28614210/why-doesnt-net-release-unused-memory-back-to-os-when-physical-95
                         *
                         */

                        if (idleAppPoolRecycling && remoteSessions.Count == 0)
                        {
                            // give time for the browser to receive the disconnect notification and get back to the login page
                            // the browser window/tab may also be closed, but there is no way to know it at this step
                            Thread.Sleep(2000);

                            // the gateway doesn't have enough rights to recycle the application pool, this is delegated to the myrtille services
                            _applicationPoolClient.RecycleApplicationPool(Environment.UserName);
                        }
                    }
                    catch (Exception exc)
                    {
                        Trace.TraceError("Failed to delete remote session ({0})", exc);
                    }
                    finally
                    {
                        _application.UnLock();
                    }

                    // the remote session is still bound to an http session
                    // if the related page is still active, the page will end the cleanup (unbind the remote session from the http session)
                    // otherwise (the browser window/tab was closed), the cleanup will be done automatically when the http session expires
                }
            }
            catch (Exception exc)
            {
                Trace.TraceError("Failed to cleanup disconnected session, remote session {0} ({1})", _remoteSessionManager.RemoteSession.Id, exc);
                throw;
            }
        }