From 3afc73a4469cd9b3101d7a17f17406e724547964 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Thu, 13 Aug 2026 17:50:26 -0500 Subject: [PATCH 1/6] fix(mobile-control): address PR review feedback - Use the actual listening Port instead of raw DirectServer.Port config when building the app URL and API path, so advertised URLs match the listener even when the configured port is 0. - Keep the remote logging endpoint on http:// since there is no dedicated secure flag for the log collector. - Remove dead Regex.Match/ipa variable in GetUrlWithCorrectIp; the scheme-preserving Regex.Replace below it already handles this. --- .../Touchpanel/MobileControlTouchpanelController.cs | 7 ------- .../WebSocketServer/MobileControlWebsocketServer.cs | 7 ++++--- 2 files changed, 4 insertions(+), 10 deletions(-) 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..a948d857 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"), }; From 85a4b635f69e9427c14d3cac9a93188dad2c3f5a Mon Sep 17 00:00:00 2001 From: Jonathan Arndt Date: Wed, 19 Aug 2026 10:24:23 -0700 Subject: [PATCH 2/6] fix(mobile-control): update version and improve response caching for join requests --- src/Directory.Build.props | 2 +- .../WebSocketServer/MobileControlWebsocketServer.cs | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) 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.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index a948d857..c96dc116 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -1162,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; @@ -1263,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); From ec9753b19e824f5c9113004feca1397b489d46bc Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 20 Aug 2026 10:26:09 -0600 Subject: [PATCH 3/6] fix(ssh-client): improve error handling for SSH connection exceptions --- src/PepperDash.Core/Comm/GenericSshClient.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs index df44ab51..16406978 100644 --- a/src/PepperDash.Core/Comm/GenericSshClient.cs +++ b/src/PepperDash.Core/Comm/GenericSshClient.cs @@ -287,13 +287,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,15 +295,15 @@ 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 ?? (Exception)e, "Exception details: "); } disconnectLogged = true; From 44e5ff4c3e04b343c856cb4537048ba2cc4c4f47 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 20 Aug 2026 10:47:30 -0600 Subject: [PATCH 4/6] fix: Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/PepperDash.Core/Comm/GenericSshClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs index 16406978..5a83fcad 100644 --- a/src/PepperDash.Core/Comm/GenericSshClient.cs +++ b/src/PepperDash.Core/Comm/GenericSshClient.cs @@ -303,7 +303,7 @@ namespace PepperDash.Core else { this.LogError("Error on connect: {error}", ie?.Message ?? e.Message); - this.LogVerbose(ie ?? (Exception)e, "Exception details: "); + this.LogVerbose(ie ?? e, "Exception details: "); } disconnectLogged = true; From a55e4811b3e64d43015d00d9e9d0cc8461844076 Mon Sep 17 00:00:00 2001 From: Robert Sanders Date: Thu, 20 Aug 2026 16:18:04 -0400 Subject: [PATCH 5/6] fix(core-ssh): stop reconnect loop during stopprog --- src/PepperDash.Core/Comm/GenericSshClient.cs | 35 ++++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs index 5a83fcad..a758378f 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(); } } @@ -230,6 +230,13 @@ namespace PepperDash.Core ConnectEnabled = true; + if (_isProgramStopping) + { + this.LogDebug("Skipping connect because program is stopping"); + ConnectEnabled = false; + return; + } + try { connectLock.Wait(); @@ -308,7 +315,7 @@ namespace PepperDash.Core disconnectLogged = true; KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - if (AutoReconnect) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -320,7 +327,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(); @@ -332,7 +339,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(); @@ -467,7 +474,7 @@ namespace PepperDash.Core { connectLock.Release(); } - if (AutoReconnect && ConnectEnabled) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -510,7 +517,10 @@ namespace PepperDash.Core this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim()); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - StartReconnectTimer(); + if (ConnectEnabled && !_isProgramStopping) + { + StartReconnectTimer(); + } } catch (Exception ex) { @@ -543,7 +553,10 @@ namespace PepperDash.Core this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes)); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - StartReconnectTimer(); + if (ConnectEnabled && !_isProgramStopping) + { + StartReconnectTimer(); + } } catch (Exception ex) { From 4ca59e3186c6404ec47f75c6c78c6916efb5dfc9 Mon Sep 17 00:00:00 2001 From: Robert Sanders Date: Thu, 20 Aug 2026 16:39:20 -0400 Subject: [PATCH 6/6] fix(core-ssh): harden GenericSshClient shutdown reconnect guards --- src/PepperDash.Core/Comm/GenericSshClient.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs index a758378f..546a2a67 100644 --- a/src/PepperDash.Core/Comm/GenericSshClient.cs +++ b/src/PepperDash.Core/Comm/GenericSshClient.cs @@ -228,15 +228,14 @@ namespace PepperDash.Core return; } - ConnectEnabled = true; - if (_isProgramStopping) { this.LogDebug("Skipping connect because program is stopping"); - ConnectEnabled = false; return; } + ConnectEnabled = true; + try { connectLock.Wait(); @@ -517,7 +516,7 @@ namespace PepperDash.Core this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim()); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - if (ConnectEnabled && !_isProgramStopping) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { StartReconnectTimer(); } @@ -553,7 +552,7 @@ namespace PepperDash.Core this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes)); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - if (ConnectEnabled && !_isProgramStopping) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { StartReconnectTimer(); }