using GenHTTP.Api.Content; using GenHTTP.Api.Infrastructure; using GenHTTP.Api.Protocol; using GenHTTP.Engine; using GenHTTP.Modules.Practices; using InABox.API; using InABox.Core; using NPOI.SS.Formula.Functions; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Net; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using System.Timers; using Timer = System.Timers.Timer; namespace PRSServices; public abstract class Handler : IHandler { public IHandler Parent { get; set; } public abstract void Init(TProperties properties); public IAsyncEnumerable GetContentAsync(IRequest request) { throw new NotImplementedException(); } public abstract ValueTask HandleAsync(IRequest request); public ValueTask PrepareAsync() { return new ValueTask(); } public IEnumerable GetContent(IRequest request) { return Enumerable.Empty(); } } public class HandlerBuilder : IHandlerBuilder> where THandler : Handler, new() { private readonly List _Concerns = new(); public HandlerBuilder(TProperties properties) { Properties = properties; } private TProperties Properties; public HandlerBuilder Add(IConcernBuilder concern) { _Concerns.Add(concern); return this; } public IHandler Build(IHandler parent) { return Concerns.Chain(parent, _Concerns, p => { var handler = new THandler { Parent = p }; handler.Init(Properties); return handler; }); } } public class Listener where THandler : Handler, new() where TProperties : notnull { private X509Certificate2? certificate; private Timer? CertificateRefreshTimer; private Timer? CertificateHaltTimer; private string? CertificateFile; private ushort Port; private IServerHost host; private TProperties Properties; public Listener(TProperties properties) { Init(properties); } [MemberNotNull("host", "Properties")] public void Init(TProperties properties) { Properties = properties; host = Host.Create() .Console() .Handler(new HandlerBuilder(properties)) .Defaults(); } public void InitCertificate(ushort port, X509Certificate2 certificate) { this.certificate = certificate; host.Bind(IPAddress.Any, port, certificate); } public void InitPort(ushort port) { Port = port; host.Bind(IPAddress.Any, port); } public void InitHTTPS(ushort port, string certificateFile) { Port = port; var useHTTP = true; if (File.Exists(certificateFile)) { Logger.Send(LogType.Information, "", "Certificate found; verifying HTTPS Certificate"); try { var certificate = new X509Certificate2(certificateFile); if (certificate.NotAfter > DateTime.Now) { var names = certificate.GetNameInfo(X509NameType.DnsName, false); Logger.Send(LogType.Information, "", $"Certificate valid for {names}"); CertificateFile = certificateFile; InitCertificate(port, certificate); useHTTP = false; } else { Logger.Send(LogType.Error, "", "HTTPS Certificate has expired, using HTTP instead"); } } catch (Exception) { Logger.Send(LogType.Error, "", "Error validating HTTPS Certificate, using HTTP instead"); } } if (useHTTP) { InitPort(port); } else { if (CertificateRefreshTimer == null) { CertificateRefreshTimer = new Timer(1000 * 60 * 60 * 24); CertificateRefreshTimer.Elapsed += CertificateTimer_Elapsed; CertificateRefreshTimer.AutoReset = true; } CertificateRefreshTimer.Start(); } } public void Start() { host.Start(); } public void Stop() { host.Stop(); } private void Restart() { Clear(); Init(Properties); if(CertificateFile != null) { InitHTTPS(Port, CertificateFile); } else { InitPort(Port); } Start(); } /// /// Clears certificate and host information, and stops the listener. /// private void Clear() { host?.Stop(); certificate = null; } #region Certificate Management private void CertificateTimer_Elapsed(object? sender, ElapsedEventArgs e) { if (certificate != null) { X509Certificate2? cert = null; if (File.Exists(CertificateFile)) { cert = new X509Certificate2(CertificateFile); } if (cert != null && cert.NotAfter > certificate.NotAfter && cert.NotAfter > DateTime.Now) { Logger.Send(LogType.Information, "", "HTTPS Certificate with greater expiry date found; restarting HTTPS listener..."); Restart(); } var expiry = certificate.NotAfter; var untilExpiry = expiry - DateTime.Now; if (untilExpiry.TotalDays <= 7) { SendCertificateExpiryNotification(expiry); if (untilExpiry.TotalDays <= 1) { CertificateRefreshTimer?.Stop(); CertificateHaltTimer = new Timer(untilExpiry.TotalMilliseconds); CertificateHaltTimer.Elapsed += HTTPS_Halt_Elapsed; CertificateHaltTimer.AutoReset = false; CertificateHaltTimer.Start(); } } } } private void SendCertificateExpiryNotification(DateTime expiry) { string message; if (expiry.Date == DateTime.Now.Date) { message = $"HTTPS Certificate will expire today at {expiry.TimeOfDay:hh\\:mm}"; } else { message = $"HTTPS Certificate will expire in {(expiry - DateTime.Now).Days} at {expiry:dd/MM/yyyy hh:mm}"; } Logger.Send(LogType.Information, "", message); } /// /// Restarts listener in HTTP mode /// /// /// private void HTTPS_Halt_Elapsed(object? sender, ElapsedEventArgs e) { CertificateHaltTimer?.Dispose(); CertificateHaltTimer = null; Logger.Send(LogType.Information, "", "Expiry of certificate reached; restarting HTTPS listener..."); Restart(); } #endregion }