From 1cbdc5aaef114abe000b4eb4dc8396f423713e28 Mon Sep 17 00:00:00 2001 From: jdevito Date: Tue, 17 Jan 2023 09:50:39 -0600 Subject: [PATCH] feat: added crestron web server (cws) generic base --- .../CrestronWebServer/GenericCwsBase.cs | 266 ++++++++++++++++++ .../RequestHandlerUnknown.cs | 16 ++ .../Pepperdash Core/PepperDash_Core.csproj | 6 + 3 files changed, 288 insertions(+) create mode 100644 Pepperdash Core/Pepperdash Core/CrestronWebServer/GenericCwsBase.cs create mode 100644 Pepperdash Core/Pepperdash Core/CrestronWebServer/RequestHandlerUnknown.cs diff --git a/Pepperdash Core/Pepperdash Core/CrestronWebServer/GenericCwsBase.cs b/Pepperdash Core/Pepperdash Core/CrestronWebServer/GenericCwsBase.cs new file mode 100644 index 0000000..9446e6a --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/CrestronWebServer/GenericCwsBase.cs @@ -0,0 +1,266 @@ +using System; +using Crestron.SimplSharp; +using Crestron.SimplSharp.WebScripting; + +namespace PepperDash.Core +{ + public class GenericCwsBase : Device + { + private const string SplusKey = "Uninitialized CWS Server"; + private const string DefaultBasePath = "/api"; + + private const uint DebugTrace = 0; + private const uint DebugInfo = 1; + private const uint DebugVerbose = 2; + + private HttpCwsServer _server; + private readonly CCriticalSection _serverLock = new CCriticalSection(); + + /// + /// CWS base path, will default to "/api" if not set via initialize method + /// + public string BasePath { get; private set; } + + /// + /// Indicates CWS is registered with base path + /// + public bool IsRegistered { get; private set; } + + /// + /// Constructor for S+. Make sure to set necessary properties using init method + /// + public GenericCwsBase() + : base(SplusKey) + { + CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; + CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; + } + + /// + /// Constructor + /// + /// + /// + public GenericCwsBase(string key, string basePath) + : base(key) + { + + BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; + } + + /// + /// + /// + /// + /// + /// + public GenericCwsBase(string key, string name, string basePath) + : base(key, name) + { + + BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; + } + + /// + /// Program status event handler + /// + /// + void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType != eProgramStatusEventType.Stopping) + return; + + Debug.Console(DebugInfo, this, "Program stopping. Disabling Server"); + + Stop(); + } + + /// + /// Ethernet event handler + /// + /// + void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) + { + // Re-enable the server if the link comes back up and the status should be connected + if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp + && IsRegistered) + { + Debug.Console(DebugInfo, this, "Ethernet link up. Starting server"); + + Start(); + } + } + + /// + /// Initializes CWS class + /// + public void Initialize(string key, string basePath) + { + Key = key; + BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; + } + + /// + /// Starts CWS instance + /// + public void Start() + { + try + { + _serverLock.Enter(); + + if (_server != null) + { + Debug.Console(DebugInfo, this, "Server has already been started"); + return; + } + + Debug.Console(DebugInfo, this, "Starting server"); + + _server = new HttpCwsServer(BasePath) + { + HttpRequestHandler = new RequestHandlerUnknown() + }; + + IsRegistered = _server.Register(); + } + catch (Exception ex) + { + Debug.Console(DebugInfo, this, "Start Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "Start Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "Start Exception InnerException: {0}", ex.InnerException); + } + finally + { + _serverLock.Leave(); + } + } + + /// + /// Stop CWS instance + /// + public void Stop() + { + try + { + _serverLock.Enter(); + + if (_server == null) + { + Debug.Console(DebugInfo, this, "Servier has already been stopped"); + return; + } + + if (_server.Unregister()) + { + IsRegistered = false; + } + + Dispose(true); + } + catch (Exception ex) + { + Debug.Console(DebugInfo, this, "ServerStop Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "ServerStop Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "ServerStop Exception InnerException: {0}", ex.InnerException); + } + finally + { + _serverLock.Leave(); + } + } + + /// + /// Received request handler + /// + /// + /// This is here for development and testing + /// + /// + /// + public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args) + { + try + { + // TODO [ ] Add logic for received requests + Debug.Console(DebugInfo, this, @"RecieveRequestEventHandler +Method: {0} +Path: {1} +PathInfo: {2} +PhysicalPath: {3} +ContentType: {4} +RawUrl: {5} +Url: {6} +UserAgent: {7} +UserHostAddress: {8} +UserHostName: {9}", + args.Context.Request.HttpMethod, + args.Context.Request.Path, + args.Context.Request.PathInfo, + args.Context.Request.PhysicalPath, + args.Context.Request.ContentType, + args.Context.Request.RawUrl, + args.Context.Request.Url, + args.Context.Request.UserAgent, + args.Context.Request.UserHostAddress, + args.Context.Request.UserHostName); + + } + catch (Exception ex) + { + Debug.Console(DebugInfo, this, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception InnerException: {0}", ex.InnerException); + } + } + + /// + /// Tracks if CWS is disposed + /// + public bool Disposed + { + get + { + return (_server == null); + } + } + + /// + /// Disposes CWS instance + /// + public void Dispose() + { + Dispose(true); + CrestronEnvironment.GC.SuppressFinalize(this); + } + + /// + /// Disposes CWS instance + /// + /// + protected void Dispose(bool disposing) + { + if (Disposed) + { + Debug.Console(DebugInfo, this, "Server has already been disposed"); + return; + } + + if (!disposing) return; + + if (_server != null) + { + _server.Dispose(); + _server = null; + } + } + + ~GenericCwsBase() + { + Dispose(true); + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/CrestronWebServer/RequestHandlerUnknown.cs b/Pepperdash Core/Pepperdash Core/CrestronWebServer/RequestHandlerUnknown.cs new file mode 100644 index 0000000..5a2adac --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/CrestronWebServer/RequestHandlerUnknown.cs @@ -0,0 +1,16 @@ +using Crestron.SimplSharp.WebScripting; + +namespace PepperDash.Core +{ + public class RequestHandlerUnknown : IHttpCwsHandler + { + public void ProcessRequest(HttpCwsContext context) + { + // TODO [ ] Modify unknown request handler + context.Response.StatusCode = 418; + context.Response.StatusDescription = "I'm a teapot"; + context.Response.ContentType = "application/json"; + context.Response.Write(string.Format("{0} {1}", context.Request.HttpMethod, context.Request.RawUrl), true); + } + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj index 86ae71d..8e49d82 100644 --- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj +++ b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj @@ -51,6 +51,10 @@ False ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + + False + ..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCWSHelperInterface.dll + False ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll @@ -89,6 +93,8 @@ + +