From 73b8514f38f30fe6e0b3802c18a893b7a5713ca4 Mon Sep 17 00:00:00 2001 From: vnugent Date: Sat, 23 Sep 2023 00:38:45 -0400 Subject: service stack building integrations and helper defaults --- .../src/HttpServiceStackBuilder.cs | 1 + .../src/IAccountSecUpdateable.cs | 44 +++ .../src/IDomainBuilder.cs | 52 ++++ .../src/IManagedPlugin.cs | 4 +- .../src/IUnloadableServiceProvider.cs | 42 --- .../src/IVirtualHostBuilder.cs | 42 +++ .../src/IVirtualHostHooks.cs | 77 +++++ .../src/ManagedPlugin.cs | 59 +--- .../src/PluginManager.cs | 22 +- .../src/SsBuilderExtensions.cs | 319 +++++++++++++++++++++ .../src/VirtualHostConfiguration.cs | 117 ++++++++ 11 files changed, 676 insertions(+), 103 deletions(-) create mode 100644 lib/Plugins.Essentials.ServiceStack/src/IAccountSecUpdateable.cs create mode 100644 lib/Plugins.Essentials.ServiceStack/src/IDomainBuilder.cs delete mode 100644 lib/Plugins.Essentials.ServiceStack/src/IUnloadableServiceProvider.cs create mode 100644 lib/Plugins.Essentials.ServiceStack/src/IVirtualHostBuilder.cs create mode 100644 lib/Plugins.Essentials.ServiceStack/src/IVirtualHostHooks.cs create mode 100644 lib/Plugins.Essentials.ServiceStack/src/SsBuilderExtensions.cs create mode 100644 lib/Plugins.Essentials.ServiceStack/src/VirtualHostConfiguration.cs (limited to 'lib/Plugins.Essentials.ServiceStack/src') diff --git a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs index 95c6878..d85d164 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using VNLib.Net.Http; using VNLib.Plugins.Runtime; + namespace VNLib.Plugins.Essentials.ServiceStack { /// diff --git a/lib/Plugins.Essentials.ServiceStack/src/IAccountSecUpdateable.cs b/lib/Plugins.Essentials.ServiceStack/src/IAccountSecUpdateable.cs new file mode 100644 index 0000000..43c55ce --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/IAccountSecUpdateable.cs @@ -0,0 +1,44 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: IAccountSecUpdateable.cs +* +* IAccountSecUpdateable.cs is part of VNLib.Plugins.Essentials.ServiceStack which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.ServiceStack is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 2 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.ServiceStack is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using VNLib.Plugins.Essentials.Accounts; + + +namespace VNLib.Plugins.Essentials.ServiceStack +{ + /// + /// Adds functionality to event processors to allow them to be updated + /// with a new instance dynamically + /// at runtime + /// + public interface IAccountSecUpdateable + { + /// + /// Sets the instance for the event processor + /// may be null to remove a previous provider + /// + /// The new instance + void SetAccountSecProvider(IAccountSecurityProvider? provider); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/IDomainBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/IDomainBuilder.cs new file mode 100644 index 0000000..dc6dac5 --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/IDomainBuilder.cs @@ -0,0 +1,52 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: IDomainBuilder.cs +* +* IDomainBuilder.cs is part of VNLib.Plugins.Essentials.ServiceStack which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.ServiceStack is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 2 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.ServiceStack is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System.IO; + +using VNLib.Utils.Logging; + +namespace VNLib.Plugins.Essentials.ServiceStack +{ + /// + /// Allows for defining virtual hosts for the service stack + /// + public interface IDomainBuilder + { + /// + /// Adds a single virtual host to the domain that must be configured. + /// + /// The service root directory + /// The virtual host event hook handler + /// The log provider + /// The instance + IVirtualHostBuilder WithVirtualHost(DirectoryInfo rootDirectory, IVirtualHostHooks hooks, ILogProvider Logger); + + /// + /// Adds a single pre-configured virtual host to the domain + /// + /// The pre-configured virtual host configuration + /// The current instance + IDomainBuilder WithVirtualHost(VirtualHostConfiguration config); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs index c883e29..852bb95 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs @@ -22,6 +22,8 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ +using System.ComponentModel.Design; + using VNLib.Plugins.Runtime; namespace VNLib.Plugins.Essentials.ServiceStack @@ -50,6 +52,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// WARNING: Services exposed by the plugin will abide by the plugin lifecycle, so consumers /// must listen for plugin load/unload events to respect lifecycles properly. /// - IUnloadableServiceProvider Services { get; } + IServiceContainer Services { get; } } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/IUnloadableServiceProvider.cs b/lib/Plugins.Essentials.ServiceStack/src/IUnloadableServiceProvider.cs deleted file mode 100644 index fa334bd..0000000 --- a/lib/Plugins.Essentials.ServiceStack/src/IUnloadableServiceProvider.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* -* Copyright (c) 2023 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Plugins.Essentials.ServiceStack -* File: IUnloadableServiceProvider.cs -* -* IUnloadableServiceProvider.cs is part of VNLib.Plugins.Essentials.ServiceStack -* which is part of the larger VNLib collection of libraries and utilities. -* -* VNLib.Plugins.Essentials.ServiceStack is free software: you can redistribute it and/or modify -* it under the terms of the GNU Affero General Public License as -* published by the Free Software Foundation, either version 2 of the -* License, or (at your option) any later version. -* -* VNLib.Plugins.Essentials.ServiceStack is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Affero General Public License for more details. -* -* You should have received a copy of the GNU Affero General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ - -using System; -using System.Threading; - -namespace VNLib.Plugins.Essentials.ServiceStack -{ - /// - /// A that may be unloaded when the - /// assembly that is sharing the types are being disposed. - /// - public interface IUnloadableServiceProvider : IServiceProvider - { - /// - /// A token that is set cancelled state when the service provider - /// is unloaded. - /// - CancellationToken UnloadToken { get; } - } -} diff --git a/lib/Plugins.Essentials.ServiceStack/src/IVirtualHostBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/IVirtualHostBuilder.cs new file mode 100644 index 0000000..8db3103 --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/IVirtualHostBuilder.cs @@ -0,0 +1,42 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: IVirtualHostBuilder.cs +* +* IVirtualHostBuilder.cs is part of VNLib.Plugins.Essentials.ServiceStack which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.ServiceStack is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 2 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.ServiceStack is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; + + +namespace VNLib.Plugins.Essentials.ServiceStack +{ + /// + /// Allows for configuring a single virtual host + /// + public interface IVirtualHostBuilder + { + /// + /// Allows configuring a virtual host options + /// + /// A callback function used to modify the virtual host configuration + /// + IVirtualHostBuilder WithOption(Action configCallback); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/IVirtualHostHooks.cs b/lib/Plugins.Essentials.ServiceStack/src/IVirtualHostHooks.cs new file mode 100644 index 0000000..e2d5382 --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/IVirtualHostHooks.cs @@ -0,0 +1,77 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: IVirtualHostHooks.cs +* +* IVirtualHostHooks.cs is part of VNLib.Plugins.Essentials.ServiceStack which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.ServiceStack is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 2 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.ServiceStack is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System.Net; +using System.Threading.Tasks; + +using VNLib.Net.Http; + +namespace VNLib.Plugins.Essentials.ServiceStack +{ + /// + /// Represents a type that will handle http events for a virtual host + /// + public interface IVirtualHostHooks + { + /// + /// + /// Called when the server intends to process a file and requires translation from a + /// uri path to a usable filesystem path + /// + /// + /// NOTE: This function must be thread-safe! + /// + /// + /// The path requested by the request + /// The translated and filtered filesystem path used to identify the file resource + string TranslateResourcePath(string requestPath); + + /// + /// + /// When an error occurs and is handled by the library, this event is invoked + /// + /// + /// NOTE: This function must be thread-safe! + /// + /// + /// The error code that was created during processing + /// The active IHttpEvent representing the faulted request + /// A value indicating if the entity was proccsed by this call + bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity); + + /// + /// For pre-processing a request entity before all endpoint lookups are performed + /// + /// The http entity to process + /// The results to return to the file processor, or null of the entity requires further processing + ValueTask PreProcessEntityAsync(HttpEntity entity); + + /// + /// Allows for post processing of a selected for the given entity + /// + /// The http entity to process + /// The selected file processing routine for the given request + void PostProcessFile(HttpEntity entity, in FileProcessArgs chosenRoutine); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs index 429a465..0c0dfa5 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs @@ -22,48 +22,41 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ +using System; using System.Linq; -using System.Threading; using System.Reflection; using System.ComponentModel.Design; -using VNLib.Utils; using VNLib.Plugins.Runtime; using VNLib.Plugins.Attributes; + namespace VNLib.Plugins.Essentials.ServiceStack { - internal sealed class ManagedPlugin : VnDisposeable, IManagedPlugin + internal sealed class ManagedPlugin : IManagedPlugin { internal RuntimePluginLoader Plugin { get; } /// public string PluginPath => Plugin.Config.AssemblyFile; - private UnloadableServiceContainer? _services; + private ServiceContainer? _services; public ManagedPlugin(RuntimePluginLoader loader) => Plugin = loader; /// - public IUnloadableServiceProvider Services + public IServiceContainer Services { get { - Check(); + _ = _services ?? throw new InvalidOperationException("The service container is not currently loaded"); return _services!; } } /// - public PluginController Controller - { - get - { - Check(); - return Plugin.Controller; - } - } + public PluginController Controller => Plugin.Controller; /* * Automatically called after the plugin has successfully loaded @@ -98,45 +91,9 @@ namespace VNLib.Plugins.Essentials.ServiceStack internal void OnPluginUnloaded() { //Cleanup services no longer in use. Plugin is still valid until this method returns - using (_services) - { - //signal service cancel before disposing - _services?.SignalUnload(); - } - + _services?.Dispose(); //Remove ref to services _services = null; } - - protected override void Free() - { - //Dispose services - _services?.Dispose(); - } - - - private sealed class UnloadableServiceContainer : ServiceContainer, IUnloadableServiceProvider - { - private readonly CancellationTokenSource _cts; - - public UnloadableServiceContainer() : base() - { - _cts = new(); - } - - /// - CancellationToken IUnloadableServiceProvider.UnloadToken => _cts.Token; - - /// - /// Signals to listensers that the service container will be unloading - /// - internal void SignalUnload() => _cts.Cancel(); - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - _cts.Dispose(); - } - } } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs index eb26e92..476f3f8 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs @@ -197,8 +197,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack protected override void Free() { - //Dispose all managed plugins and clear the table - _managedPlugins.TryForeach(p => p.Value.Dispose()); + //Clear plugin table _managedPlugins.Clear(); //Dispose the plugin stack @@ -238,14 +237,19 @@ namespace VNLib.Plugins.Essentials.ServiceStack return; } - //Run onload method before invoking other handlers - mp.OnPluginUnloaded(); - - //Get event listeners at event time because deps may be modified by the domain - ServiceGroup[] deps = _dependents.ServiceGroups.Select(static d => d).ToArray(); + try + { + //Get event listeners at event time because deps may be modified by the domain + ServiceGroup[] deps = _dependents.ServiceGroups.Select(static d => d).ToArray(); - //Run unloaded method - deps.TryForeach(d => d.OnPluginUnloaded(mp)); + //Run unloaded method + deps.TryForeach(d => d.OnPluginUnloaded(mp)); + } + finally + { + //always unload the plugin wrapper + mp.OnPluginUnloaded(); + } } } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/SsBuilderExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/SsBuilderExtensions.cs new file mode 100644 index 0000000..b6c21fb --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/SsBuilderExtensions.cs @@ -0,0 +1,319 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: SsBuilderExtensions.cs +* +* SsBuilderExtensions.cs is part of VNLib.Plugins.Essentials.ServiceStack which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.ServiceStack is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 2 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.ServiceStack is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.IO; +using System.Net; +using System.Linq; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; + +using VNLib.Utils.Logging; +using VNLib.Net.Http; +using VNLib.Plugins.Essentials.Accounts; +using VNLib.Plugins.Essentials.Content; +using VNLib.Plugins.Essentials.Sessions; +using VNLib.Plugins.Essentials.Middleware; + + +namespace VNLib.Plugins.Essentials.ServiceStack +{ + + /// + /// + /// + public static class SsBuilderExtensions + { + + /// + /// Creates a new instance to define your + /// virtual hosts using a built-in event processor type + /// + /// + /// The used to define your service domain + public static IDomainBuilder WithDomain(this HttpServiceStackBuilder stack) => WithDomain(stack, p => new BasicVirtualHost(p.Clone())); + + /// + /// Creates a new instance to define your + /// virtual hosts with the supplied callback method + /// + /// + /// The callback function to pass the domain builder to + /// The service stack builder instance + public static HttpServiceStackBuilder WithDomain(this HttpServiceStackBuilder stack, Action domainBuilder) + { + domainBuilder(stack.WithDomain()); + return stack; + } + + /// + /// Creates a new with your custom type + /// that will be wrapped for runtime processing. + /// + /// + /// + /// The custom event processor type + /// + public static IDomainBuilder WithDomain(this HttpServiceStackBuilder stack, Func callback) where T : EventProcessor, IAccountSecUpdateable + { + List configs = new(); + DomainBuilder domains = new (configs, stack); + + //Add callback to capture this collection of configs when built + stack.AddHosts(() => configs.Select(c => new CustomServiceHost(c.Clone(), callback(c))).ToArray()); + + return domains; + } + + /// + /// Adds a single instance to the virtual host + /// + /// + /// The middleware instance to add + /// + public static IVirtualHostBuilder WithMiddleware(this IVirtualHostBuilder vhBuilder, IHttpMiddleware middleware) + { + vhBuilder.WithOption(c => c.CustomMiddleware.Add(middleware)); + return vhBuilder; + } + + public static IVirtualHostBuilder WithLogger(this IVirtualHostBuilder vhBuilder, ILogProvider logger) + { + vhBuilder.WithOption(c => c.LogProvider = logger); + return vhBuilder; + } + + public static IVirtualHostBuilder WithEndpoint(this IVirtualHostBuilder vhBuilder, IPEndPoint endpoint) + { + vhBuilder.WithOption(c => c.TransportEndpoint = endpoint); + return vhBuilder; + } + + public static IVirtualHostBuilder WithTlsCertificate(this IVirtualHostBuilder vhBuilder, X509Certificate? cert) + { + vhBuilder.WithOption(c => c.Certificate = cert); + return vhBuilder; + } + + public static IVirtualHostBuilder WithHostname(this IVirtualHostBuilder virtualHostBuilder, string hostname) + { + virtualHostBuilder.WithOption(c => c.Hostname = hostname); + return virtualHostBuilder; + } + + public static IVirtualHostBuilder WithDefaultFiles(this IVirtualHostBuilder vhBuidler, params string[] defaultFiles) + { + return vhBuidler.WithDefaultFiles((IReadOnlyCollection)defaultFiles); + } + + public static IVirtualHostBuilder WithDefaultFiles(this IVirtualHostBuilder vhBuidler, IReadOnlyCollection defaultFiles) + { + vhBuidler.WithOption(c => c.DefaultFiles = defaultFiles); + return vhBuidler; + } + + public static IVirtualHostBuilder WithExcludedExtensions(this IVirtualHostBuilder vhBuilder, params string[] excludedExtensions) + { + return vhBuilder.WithExcludedExtensions(new HashSet(excludedExtensions)); + } + + public static IVirtualHostBuilder WithExcludedExtensions(this IVirtualHostBuilder vhBuilder, IReadOnlySet excludedExtensions) + { + vhBuilder.WithOption(c => c.ExcludedExtensions = excludedExtensions); + return vhBuilder; + } + + public static IVirtualHostBuilder WithAllowedAttributes(this IVirtualHostBuilder vhBuilder, FileAttributes attributes) + { + vhBuilder.WithOption(c => c.AllowedAttributes = attributes); + return vhBuilder; + } + + public static IVirtualHostBuilder WithDisallowedAttributes(this IVirtualHostBuilder vhBuilder, FileAttributes attributes) + { + vhBuilder.WithOption(c => c.DissallowedAttributes = attributes); + return vhBuilder; + } + + public static IVirtualHostBuilder WithDownstreamServers(this IVirtualHostBuilder vhBuilder, IReadOnlySet addresses) + { + vhBuilder.WithOption(c => c.DownStreamServers = addresses); + return vhBuilder; + } + + public static IVirtualHostBuilder WithDownstreamServers(this IVirtualHostBuilder vhBuilder, params IPAddress[] addresses) + { + vhBuilder.WithOption(c => c.DownStreamServers = new HashSet(addresses)); + return vhBuilder; + } + + private static void AddHosts(this HttpServiceStackBuilder stack, Func hosts) => stack.WithDomain(p => Array.ForEach(hosts(), h => p.Add(h))); + + private static void OnPluginServiceEvent(this IManagedPlugin plugin, Action loader) + { + object? service = plugin.Services.GetService(typeof(T)); + + if (service is T s) + { + loader(s); + } + } + + private sealed record class DomainBuilder(List Configs, HttpServiceStackBuilder Stack) : IDomainBuilder + { + + /// + public IVirtualHostBuilder WithVirtualHost(DirectoryInfo rootDirectory, IVirtualHostHooks hooks, ILogProvider logger) + { + //Create new config instance and add to list + VirtualHostConfiguration config = new() + { + EventHooks = hooks, + RootDir = rootDirectory, + LogProvider = logger + }; + Configs.Add(config); + return new VHostBuilder(config); + } + + /// + public IDomainBuilder WithVirtualHost(VirtualHostConfiguration config) + { + Configs.Add(config); + return this; + } + + private sealed record class VHostBuilder(VirtualHostConfiguration Config) : IVirtualHostBuilder + { + public IVirtualHostBuilder WithOption(Action configCallback) + { + configCallback(Config); + return this; + } + } + } + + private sealed class CustomServiceHost : IServiceHost where T : EventProcessor, IAccountSecUpdateable + { + private readonly VirtualHostConfiguration Config; + private readonly T Instance; + + public CustomServiceHost(VirtualHostConfiguration Config, T Instance) + { + this.Config = Config; + this.Instance = Instance; + + //Add middleware to the chain + foreach(IHttpMiddleware mw in Config.CustomMiddleware) + { + Instance.MiddlewareChain.AddLast(mw); + } + } + + /// + public IWebRoot Processor => Instance; + + /// + public IHostTransportInfo TransportInfo => Config; + + /// + void IServiceHost.OnRuntimeServiceAttach(IManagedPlugin plugin, IEndpoint[] endpoints) + { + //Add endpoints to service + Instance.EndpointTable.AddEndpoint(endpoints); + + //Add all services + plugin.OnPluginServiceEvent(Instance.SetSessionProvider); + plugin.OnPluginServiceEvent(Instance.SetPageRouter); + plugin.OnPluginServiceEvent(p => Instance.SetAccountSecProvider(p)); + + //Add all middleware to the chain + plugin.OnPluginServiceEvent(p => Array.ForEach(p, mw => Instance.MiddlewareChain.AddLast(mw))); + } + + /// + void IServiceHost.OnRuntimeServiceDetach(IManagedPlugin plugin, IEndpoint[] endpoints) + { + //Remove endpoints + Instance.EndpointTable.RemoveEndpoint(endpoints); + + //Remove all services + plugin.OnPluginServiceEvent(p => Instance.SetSessionProvider(null)); + plugin.OnPluginServiceEvent(p => Instance.SetPageRouter(null)); + plugin.OnPluginServiceEvent(p => Instance.SetAccountSecProvider(null)); + + //Remove all middleware from the chain + plugin.OnPluginServiceEvent(p => Array.ForEach(p, mw => Instance.MiddlewareChain.RemoveMiddleware(mw))); + } + } + + + private sealed class BasicVirtualHost : EventProcessor, IAccountSecUpdateable + { + private readonly VirtualHostConfiguration Config; + private readonly IVirtualHostHooks Hooks; + + internal IAccountSecurityProvider? SecProvider; + + public BasicVirtualHost(VirtualHostConfiguration config) + { + Config = config; + Hooks = config.EventHooks; + Directory = config.RootDir.FullName; + } + + /// + public override string Directory { get; } + + /// + public override string Hostname => Config.Hostname; + + /// + public override IEpProcessingOptions Options => Config; + + /// + public override IAccountSecurityProvider? AccountSecurity => SecProvider; + + /// + protected override ILogProvider Log => Config.LogProvider; + + /// + public override bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity) => Hooks.ErrorHandler(errorCode, entity); + + /// + public override void PostProcessFile(HttpEntity entity, in FileProcessArgs chosenRoutine) => Hooks.PostProcessFile(entity, chosenRoutine); + + /// + public override ValueTask PreProcessEntityAsync(HttpEntity entity) => Hooks.PreProcessEntityAsync(entity); + + /// + public override string TranslateResourcePath(string requestPath) => Hooks.TranslateResourcePath(requestPath); + + /// + public void SetAccountSecProvider(IAccountSecurityProvider? provider) => SecProvider = provider; + + } + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/VirtualHostConfiguration.cs b/lib/Plugins.Essentials.ServiceStack/src/VirtualHostConfiguration.cs new file mode 100644 index 0000000..4734a5a --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/VirtualHostConfiguration.cs @@ -0,0 +1,117 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: VirtualHostConfiguration.cs +* +* VirtualHostConfiguration.cs is part of VNLib.Plugins.Essentials.ServiceStack which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.ServiceStack is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 2 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.ServiceStack is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + +using System; +using System.IO; +using System.Net; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; + +using VNLib.Utils.Logging; +using VNLib.Plugins.Essentials.Middleware; + +namespace VNLib.Plugins.Essentials.ServiceStack +{ + /// + /// A virtual host configuration container + /// + public class VirtualHostConfiguration : IHostTransportInfo, IEpProcessingOptions + { + /// + /// The directory that this virtual host will serve files from + /// + public DirectoryInfo RootDir { get; set; } = null!; + + /// + /// The hostname, or domain name, that this virtual host will respond to + /// Default: * + /// + public string Hostname { get; set; } = "*"; + + /// + /// The transport endpoint that this virtual host will listen on + /// Default: 0.0.0.0:80 + /// + public IPEndPoint TransportEndpoint { get; set; } = new IPEndPoint(IPAddress.Any, 80); + + /// + /// An optional certificate to use for TLS connections + /// + public X509Certificate? Certificate { get; set; } + + /// + /// A log provider to use for this virtual host + /// + public ILogProvider LogProvider { get; set; } = null!; + + /// + /// The name of a default file to search for within a directory if no file is specified (index.html). + /// This array should be ordered. + /// Default: empty set + /// + public IReadOnlyCollection DefaultFiles { get; set; } = Array.Empty(); + + /// + /// File extensions that are denied from being read from the filesystem + /// Default: empty set + /// + public IReadOnlySet ExcludedExtensions { get; set; } = new HashSet(); + + /// + /// File attributes that must be matched for the file to be accessed, defaults to all allowed + /// Default: 0xFFFFFFFF + /// + public FileAttributes AllowedAttributes { get; set; } = unchecked((FileAttributes)(0xFFFFFFFF)); + + /// + /// Files that match any attribute flag set will be denied + /// Default: + /// + public FileAttributes DissallowedAttributes { get; set; } = FileAttributes.System; + + /// + /// A table of known downstream servers/ports that can be trusted to proxy connections + /// Default: empty set + /// + public IReadOnlySet DownStreamServers { get; set; } = new HashSet(); + + /// + /// A for how long a connection may remain open before all operations are cancelled + /// Default: 60 seconds + /// + public TimeSpan ExecutionTimeout { get; set; } = TimeSpan.FromSeconds(60); + + /// + /// A for how long a connection may remain idle before all operations are cancelled + /// + public IVirtualHostHooks EventHooks { get; set; } = null!; + + /// + /// A set of custom middleware to add to virtual host middleware pipeline + /// + public ICollection CustomMiddleware { get; } = new List(); + + internal VirtualHostConfiguration Clone() => (VirtualHostConfiguration)MemberwiseClone(); + } +} -- cgit