private void CleanupDisconnectedSession(RemoteSessionExitCode exitCode) { try { _application.Lock(); #region session // 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); } #endregion #region session sharing // remove 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); } } foreach (var guest in guests) { sharedSessions.Remove(guest.GuestInfo.Id); } #endregion #region application pool recycling /* * application pool recycling 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 */ bool idleAppPoolRecycling; if (!bool.TryParse(ConfigurationManager.AppSettings["IdleAppPoolRecycling"], out idleAppPoolRecycling)) { idleAppPoolRecycling = false; } // connect from a login page or url bool loginEnabled; if (!bool.TryParse(ConfigurationManager.AppSettings["LoginEnabled"], out loginEnabled)) { loginEnabled = true; } // if enabled, url of the login page var loginUrl = string.Empty; if (loginEnabled) { loginUrl = ConfigurationManager.AppSettings["LoginUrl"]; } // recycle only if enabled and when there is no active remote session // don't recycle if using the enterprise mode (if there are enterprise sessions, they musn't be dropped!) // don't recycle in case of connection failure, so that the page can handle it (show the related error dialog) if (idleAppPoolRecycling && remoteSessions.Count == 0 && (_enterpriseClient.GetMode() == EnterpriseMode.None) && (exitCode == RemoteSessionExitCode.Success || exitCode == RemoteSessionExitCode.SessionDisconnectFromMenu || exitCode == RemoteSessionExitCode.SessionLogoutFromMenu)) { // if using a custom login page, the application pool must be recycled after the redirect if (!string.IsNullOrEmpty(loginUrl)) { // redirect to the custom login page _remoteSessionManager.SendMessage(new RemoteSessionMessage { Type = MessageType.Disconnected, Prefix = "disconnected" }); // give some time for the redirection 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); } // otherwise, the application pool must be recycled before the redirect // the browser will acquire a new http session else { // the gateway doesn't have enough rights to recycle the application pool, this is delegated to the myrtille services _applicationPoolClient.RecycleApplicationPool(Environment.UserName); // give some time for the recycling Thread.Sleep(2000); // redirect to the default login page (empty if login is not enabled) _remoteSessionManager.SendMessage(new RemoteSessionMessage { Type = MessageType.Disconnected, Prefix = "disconnected" }); } } else { // redirect to the login page (or the hosts dashboard in enterprise mode) _remoteSessionManager.SendMessage(new RemoteSessionMessage { Type = MessageType.Disconnected, Prefix = "disconnected" }); } #endregion } catch (Exception exc) { Trace.TraceError("Failed to cleanup disconnected session, remote session {0} ({1})", _remoteSessionManager.RemoteSession.Id, exc); throw; } finally { _application.UnLock(); } }
public bool SetConnectionExitCode(Guid connectionId, string IPAddress, Guid vmGuid, RemoteSessionExitCode exitCode) { if (string.IsNullOrWhiteSpace(IPAddress) && vmGuid == Guid.Empty) { throw new ArgumentException("ipAddress and VmGuid cannot both be null"); } // the exit code enum list is not exhaustive (wfreerdp returns a GetLastError() integer and I wasn't able to test every possible ways for the process to exit or crash) // you may want to allow an (int) exit code which is not present into the enum // if/when you identify its meaning, you can add it into the enum //if (!Enum.IsDefined(typeof(RemoteSessionExitCode), exitCode)) //{ // throw new ArgumentException(nameof(exitCode)); //} return(_connectionService.SetConnectionExitCode(connectionId, IPAddress, vmGuid, exitCode)); }
public bool SetConnectionExitCode(Guid connectionId, string IPAddress, Guid vmGuid, RemoteSessionExitCode exitCode) { var restRequest = new RestRequest($"SetConnectionExitCode?connectionId={connectionId}&IPAddress={IPAddress}&vmGuid={vmGuid}&exitCode={exitCode}", Method.GET); var restResponse = restClient.Execute(restRequest); if (restResponse.ResponseStatus != ResponseStatus.Completed) { throw new Exception(string.Format("Failed to call SetConnectionExitCode; response status: {0}, code: {1}, message: {2}", restResponse.ResponseStatus, restResponse.StatusCode, restResponse.ErrorMessage)); } return(JsonConvert.DeserializeObject <bool>(restResponse.Content)); }
public bool SetConnectionExitCode(Guid connectionId, string IPAddress, Guid VMGuid, RemoteSessionExitCode exitCode) { var connection = connections[connectionId] as Connection; if (connection != null) { Trace.TraceInformation("connection: {0}, ip={1}, vm={2}, exit code={3}", connectionId, IPAddress, VMGuid != Guid.Empty ? VMGuid.ToString() : string.Empty, exitCode); connection.ExitCode = exitCode; return(true); } Trace.TraceInformation("invalid connection: {0}", connectionId); return(false); }