diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ab9f2732..235fee48 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,6 +1,6 @@ - 2.36.6-local + 2.42.1-local $(Version) PepperDash Technology PepperDash Technology diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs index df44ab51..546a2a67 100644 --- a/src/PepperDash.Core/Comm/GenericSshClient.cs +++ b/src/PepperDash.Core/Comm/GenericSshClient.cs @@ -151,6 +151,8 @@ namespace PepperDash.Core // Thread-safety lock for state changes private readonly object _stateLock = new object(); + private volatile bool _isProgramStopping; + private bool disconnectLogged = false; /// @@ -207,11 +209,9 @@ namespace PepperDash.Core { if (programEventType == eProgramStatusEventType.Stopping) { - if (client != null) - { - this.LogDebug("Program stopping. Closing connection"); - Disconnect(); - } + _isProgramStopping = true; + this.LogDebug("Program stopping. Closing connection"); + Disconnect(); } } @@ -228,6 +228,12 @@ namespace PepperDash.Core return; } + if (_isProgramStopping) + { + this.LogDebug("Skipping connect because program is stopping"); + return; + } + ConnectEnabled = true; try @@ -287,13 +293,7 @@ namespace PepperDash.Core } catch (SshConnectionException e) { - var ie = e.InnerException; // The details are inside!! - - if (ie is SocketException) - { - this.LogError("CONNECTION failure: Cannot reach host"); - this.LogVerbose(ie, "Exception details: "); - } + var ie = e.InnerException; // The details are inside, when present - remote can close the connection with no inner exception at all if (ie is System.Net.Sockets.SocketException socketException) { @@ -301,20 +301,20 @@ namespace PepperDash.Core Hostname, Port); this.LogVerbose(socketException, "SocketException details: "); } - if (ie is SshAuthenticationException) + else if (ie is SshAuthenticationException) { this.LogError("Authentication failure for username {userName}", Username); this.LogVerbose(ie, "AuthenticationException details: "); } else { - this.LogError("Error on connect: {error}", ie.Message); - this.LogVerbose(ie, "Exception details: "); + this.LogError("Error on connect: {error}", ie?.Message ?? e.Message); + this.LogVerbose(ie ?? e, "Exception details: "); } disconnectLogged = true; KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - if (AutoReconnect) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -326,7 +326,7 @@ namespace PepperDash.Core disconnectLogged = true; KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - if (AutoReconnect) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -338,7 +338,7 @@ namespace PepperDash.Core this.LogVerbose(e, "Exception details: "); disconnectLogged = true; KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - if (AutoReconnect) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -473,7 +473,7 @@ namespace PepperDash.Core { connectLock.Release(); } - if (AutoReconnect && ConnectEnabled) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -516,7 +516,10 @@ namespace PepperDash.Core this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim()); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - StartReconnectTimer(); + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) + { + StartReconnectTimer(); + } } catch (Exception ex) { @@ -549,7 +552,10 @@ namespace PepperDash.Core this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes)); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - StartReconnectTimer(); + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) + { + StartReconnectTimer(); + } } catch (Exception ex) { diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs index dcd0d56e..262cb34e 100644 --- a/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs +++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/MobileControlTouchpanelController.cs @@ -554,13 +554,6 @@ namespace PepperDash.Essentials.Touchpanel return false; }) ? csIpAddress.ToString() : processorIp; - var match = Regex.Match(url, @"^(https?)://([^:/]+):\d+/mc/app\?token=.+$"); - if (match.Success) - { - string ipa = match.Groups[2].Value; - // ip will be "192.168.1.100" - } - // replace the host but preserve whatever scheme (http/https) is already present in the URL var updatedUrl = Regex.Replace(url, @"^(https?)://[^:/]+", $"$1://{ip}"); diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index 3bcbcdf6..c96dc116 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -414,7 +414,7 @@ namespace PepperDash.Essentials.WebSocketServer ip = csIpAddress.ToString(); } - var appUrl = $"{HttpScheme}://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}"; + var appUrl = $"{HttpScheme}://{ip}:{Port}/mc/app?token={touchpanel.Key}"; this.LogVerbose("Sending URL {appUrl} to touchpanel {touchpanelKey}", appUrl, touchpanel.Touchpanel.Key); @@ -498,7 +498,7 @@ namespace PepperDash.Essentials.WebSocketServer { var config = new MobileControlApplicationConfig { - ApiPath = string.Format("{0}://{1}:{2}/mc/api", HttpScheme, processorIp, _parent.Config.DirectServer.Port), + ApiPath = string.Format("{0}://{1}:{2}/mc/api", HttpScheme, processorIp, Port), GatewayAppPath = "", LogoPath = _parent.Config.ApplicationConfig?.LogoPath ?? "logo/logo.png", EnableDev = _parent.Config.ApplicationConfig?.EnableDev ?? false, @@ -1109,7 +1109,8 @@ namespace PepperDash.Essentials.WebSocketServer res.StatusCode = 200; res.Close(); - var logRequest = new HttpRequestMessage(HttpMethod.Post, $"{HttpScheme}://{_parent.Config.DirectServer.Logging.Host}:{_parent.Config.DirectServer.Logging.Port}/logs") + // remote log collector has no dedicated secure flag; keep it on http regardless of DirectServer.Secure + var logRequest = new HttpRequestMessage(HttpMethod.Post, $"http://{_parent.Config.DirectServer.Logging.Host}:{_parent.Config.DirectServer.Logging.Port}/logs") { Content = new StringContent(body, Encoding.UTF8, "application/json"), }; @@ -1161,6 +1162,11 @@ namespace PepperDash.Essentials.WebSocketServer var qp = req.QueryString; var token = qp["token"]; + // Each join mints a single-use clientId; the panel webview must never replay a cached + // response, or it reconnects forever with an already-consumed id (1008 loop). + res.Headers.Add("Cache-Control", "no-store"); + res.Headers.Add("Pragma", "no-cache"); + this.LogVerbose("Join Room Request with token: {token}", token); byte[] body; @@ -1262,6 +1268,8 @@ namespace PepperDash.Essentials.WebSocketServer { res.StatusCode = 200; res.ContentType = "application/json"; + res.Headers.Add("Cache-Control", "no-store"); + res.Headers.Add("Pragma", "no-cache"); var version = new Version() { ServerVersion = _parent.GetConfigWithPluginVersion().RuntimeInfo.PluginVersion }; var message = JsonConvert.SerializeObject(version); this.LogVerbose("{message}", message);