(_path);
_httpsServer.OnGet += HandleHttpGet;
Debug.LogInformation("Assigning Log Info");
_httpsServer.Log.Level = LogLevel.Trace;
_httpsServer.Log.Output = WriteWebSocketInternalLog;
Debug.LogInformation("Starting");
_httpsServer.Start();
Debug.LogInformation("Ready");
}
catch (Exception ex)
{
Debug.LogError(ex, "WebSocket Failed to start {0}", ex.Message);
Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
// Null out the server so callers can detect failure via IsRunning / Url null guards.
_httpsServer = null;
}
}
private void HandleHttpGet(object sender, HttpRequestEventArgs e)
{
var res = e.Response;
var body = System.Text.Encoding.UTF8.GetBytes(
"Certificate accepted.
You can close this tab and return to the application.
");
res.ContentType = "text/html";
res.ContentLength64 = body.Length;
res.Close(body, true);
}
///
/// Stops the WebSocket server if it is currently running.
///
/// This method halts the WebSocket server and releases any associated resources. After
/// calling this method, the server will no longer accept or process incoming connections.
public void StopServer()
{
Debug.LogInformation("Stopping Websocket Server");
try
{
if (_httpsServer == null || !_httpsServer.IsListening)
{
return;
}
// Prevent close-sequence internal websocket logs from re-entering the logging pipeline.
_httpsServer.Log.Output = (d, s) => { };
var serviceHost = _httpsServer.WebSocketServices[_path];
if (serviceHost == null)
{
_httpsServer.Stop();
_httpsServer = null;
return;
}
serviceHost.Sessions.Broadcast("Server is stopping");
foreach (var session in serviceHost.Sessions.Sessions)
{
if (session?.Context?.WebSocket != null && session.Context.WebSocket.IsAlive)
{
session.Context.WebSocket.Close(1001, "Server is stopping");
}
}
_httpsServer.Stop();
_httpsServer = null;
}
catch (Exception ex)
{
Debug.LogError(ex, "WebSocket Failed to stop gracefully {0}", ex.Message);
Debug.LogVerbose("Stack Trace\r\n{0}", ex.StackTrace);
}
}
private static void WriteWebSocketInternalLog(LogData data, string supplemental)
{
try
{
if (data == null)
{
return;
}
var message = string.IsNullOrWhiteSpace(data.Message) ? "" : data.Message;
var details = string.IsNullOrWhiteSpace(supplemental) ? string.Empty : string.Format(" | details: {0}", supplemental);
// Use direct console output to avoid recursive log sink calls.
CrestronConsole.PrintLine(string.Format("WS[{0}] {1} | message: {2}{3}", data.Level, data.Date, message, details));
}
catch
{
// Never throw from websocket log callback.
}
}
}
///
/// Configures the logger to write log events to a debug WebSocket sink.
///
/// This extension method allows you to direct log events to a WebSocket sink for debugging
/// purposes.
public static class DebugWebsocketSinkExtensions
{
///
/// Configures a logger to write log events to a debug WebSocket sink.
///
/// This method adds a sink that writes log events to a WebSocket for debugging purposes.
/// It is typically used during development to stream log events in real-time.
/// The logger sink configuration to apply the WebSocket sink to.
/// An optional text formatter to format the log events. If not provided, a default formatter will be used.
/// A object that can be used to further configure the logger.
public static LoggerConfiguration DebugWebsocketSink(
this LoggerSinkConfiguration loggerConfiguration,
ITextFormatter formatProvider = null)
{
return loggerConfiguration.Sink(new DebugWebsocketSink(formatProvider));
}
}
///
/// Represents a WebSocket client for debugging purposes, providing connection lifecycle management and message
/// handling functionality.
///
/// The class extends to handle
/// WebSocket connections, including events for opening, closing, receiving messages, and errors. It tracks the
/// duration of the connection and logs relevant events for debugging.
public class DebugClient : WebSocketBehavior
{
private DateTime _connectionTime;
///
/// Gets the duration of time the WebSocket connection has been active.
///
public TimeSpan ConnectedDuration
{
get
{
if (Context.WebSocket.IsAlive)
{
return DateTime.Now - _connectionTime;
}
else
{
return new TimeSpan(0);
}
}
}
///
/// Initializes a new instance of the class.
///
public DebugClient()
{
Debug.LogInformation("DebugClient Created");
}
///
protected override void OnOpen()
{
base.OnOpen();
var url = Context.WebSocket.Url;
Debug.LogInformation("New WebSocket Connection from: {0}", url);
_connectionTime = DateTime.Now;
}
///
protected override void OnMessage(MessageEventArgs e)
{
base.OnMessage(e);
Debug.LogVerbose("WebSocket UiClient Message: {0}", e.Data);
}
///
protected override void OnClose(CloseEventArgs e)
{
base.OnClose(e);
Debug.LogDebug("WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason);
}
///
protected override void OnError(WebSocketSharp.ErrorEventArgs e)
{
base.OnError(e);
Debug.LogError(e.Exception, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message);
Debug.LogVerbose("Stack Trace:\r{0}", e.Exception.StackTrace);
}
}