diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs | 1 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/IAccountSecUpdateable.cs | 44 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/IDomainBuilder.cs | 52 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs | 4 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/IVirtualHostBuilder.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/IUnloadableServiceProvider.cs) | 20 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/IVirtualHostHooks.cs | 77 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs | 59 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs | 22 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/SsBuilderExtensions.cs | 319 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/VirtualHostConfiguration.cs | 117 |
10 files changed, 644 insertions, 71 deletions
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 { /// <summary> 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 +{ + /// <summary> + /// Adds functionality to event processors to allow them to be updated + /// with a new <see cref="IAccountSecurityProvider"/> instance dynamically + /// at runtime + /// </summary> + public interface IAccountSecUpdateable + { + /// <summary> + /// Sets the <see cref="IAccountSecurityProvider"/> instance for the event processor + /// may be null to remove a previous provider + /// </summary> + /// <param name="provider">The new <see cref="IAccountSecurityProvider"/> instance</param> + 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 +{ + /// <summary> + /// Allows for defining virtual hosts for the service stack + /// </summary> + public interface IDomainBuilder + { + /// <summary> + /// Adds a single virtual host to the domain that must be configured. + /// </summary> + /// <param name="rootDirectory">The service root directory</param> + /// <param name="hooks">The virtual host event hook handler</param> + /// <param name="Logger">The log provider</param> + /// <returns>The <see cref="IVirtualHostBuilder"/> instance</returns> + IVirtualHostBuilder WithVirtualHost(DirectoryInfo rootDirectory, IVirtualHostHooks hooks, ILogProvider Logger); + + /// <summary> + /// Adds a single pre-configured virtual host to the domain + /// </summary> + /// <param name="config">The pre-configured virtual host configuration</param> + /// <returns>The current instance</returns> + 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. /// </remarks> - IUnloadableServiceProvider Services { get; } + IServiceContainer Services { get; } } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/IUnloadableServiceProvider.cs b/lib/Plugins.Essentials.ServiceStack/src/IVirtualHostBuilder.cs index fa334bd..8db3103 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/IUnloadableServiceProvider.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/IVirtualHostBuilder.cs @@ -3,10 +3,10 @@ * * Library: VNLib * Package: VNLib.Plugins.Essentials.ServiceStack -* File: IUnloadableServiceProvider.cs +* File: IVirtualHostBuilder.cs * -* IUnloadableServiceProvider.cs is part of VNLib.Plugins.Essentials.ServiceStack -* which is part of the larger VNLib collection of libraries and utilities. +* 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 @@ -23,20 +23,20 @@ */ using System; -using System.Threading; + namespace VNLib.Plugins.Essentials.ServiceStack { /// <summary> - /// A <see cref="IServiceProvider"/> that may be unloaded when the - /// assembly that is sharing the types are being disposed. + /// Allows for configuring a single virtual host /// </summary> - public interface IUnloadableServiceProvider : IServiceProvider + public interface IVirtualHostBuilder { /// <summary> - /// A token that is set cancelled state when the service provider - /// is unloaded. + /// Allows configuring a virtual host options /// </summary> - CancellationToken UnloadToken { get; } + /// <param name="configCallback">A callback function used to modify the virtual host configuration</param> + /// <returns></returns> + IVirtualHostBuilder WithOption(Action<VirtualHostConfiguration> 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 +{ + /// <summary> + /// Represents a type that will handle http events for a virtual host + /// </summary> + public interface IVirtualHostHooks + { + /// <summary> + /// <para> + /// Called when the server intends to process a file and requires translation from a + /// uri path to a usable filesystem path + /// </para> + /// <para> + /// NOTE: This function must be thread-safe! + /// </para> + /// </summary> + /// <param name="requestPath">The path requested by the request </param> + /// <returns>The translated and filtered filesystem path used to identify the file resource</returns> + string TranslateResourcePath(string requestPath); + + /// <summary> + /// <para> + /// When an error occurs and is handled by the library, this event is invoked + /// </para> + /// <para> + /// NOTE: This function must be thread-safe! + /// </para> + /// </summary> + /// <param name="errorCode">The error code that was created during processing</param> + /// <param name="entity">The active IHttpEvent representing the faulted request</param> + /// <returns>A value indicating if the entity was proccsed by this call</returns> + bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity); + + /// <summary> + /// For pre-processing a request entity before all endpoint lookups are performed + /// </summary> + /// <param name="entity">The http entity to process</param> + /// <returns>The results to return to the file processor, or null of the entity requires further processing</returns> + ValueTask<FileProcessArgs> PreProcessEntityAsync(HttpEntity entity); + + /// <summary> + /// Allows for post processing of a selected <see cref="FileProcessArgs"/> for the given entity + /// </summary> + /// <param name="entity">The http entity to process</param> + /// <param name="chosenRoutine">The selected file processing routine for the given request</param> + 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; } ///<inheritdoc/> public string PluginPath => Plugin.Config.AssemblyFile; - private UnloadableServiceContainer? _services; + private ServiceContainer? _services; public ManagedPlugin(RuntimePluginLoader loader) => Plugin = loader; ///<inheritdoc/> - public IUnloadableServiceProvider Services + public IServiceContainer Services { get { - Check(); + _ = _services ?? throw new InvalidOperationException("The service container is not currently loaded"); return _services!; } } ///<inheritdoc/> - 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(); - } - - ///<inheritdoc/> - CancellationToken IUnloadableServiceProvider.UnloadToken => _cts.Token; - - /// <summary> - /// Signals to listensers that the service container will be unloading - /// </summary> - 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 +{ + + /// <summary> + /// + /// </summary> + public static class SsBuilderExtensions + { + + /// <summary> + /// Creates a new <see cref="IDomainBuilder"/> instance to define your + /// virtual hosts using a built-in event processor type + /// </summary> + /// <param name="stack"></param> + /// <returns>The <see cref="IDomainBuilder"/> used to define your service domain</returns> + public static IDomainBuilder WithDomain(this HttpServiceStackBuilder stack) => WithDomain(stack, p => new BasicVirtualHost(p.Clone())); + + /// <summary> + /// Creates a new <see cref="IDomainBuilder"/> instance to define your + /// virtual hosts with the supplied callback method + /// </summary> + /// <param name="stack"></param> + /// <param name="domainBuilder">The callback function to pass the domain builder to</param> + /// <returns>The service stack builder instance</returns> + public static HttpServiceStackBuilder WithDomain(this HttpServiceStackBuilder stack, Action<IDomainBuilder> domainBuilder) + { + domainBuilder(stack.WithDomain()); + return stack; + } + + /// <summary> + /// Creates a new <see cref="IDomainBuilder"/> with your custom <see cref="EventProcessor"/> type + /// that will be wrapped for runtime processing. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="stack"></param> + /// <param name="callback">The custom event processor type</param> + /// <returns></returns> + public static IDomainBuilder WithDomain<T>(this HttpServiceStackBuilder stack, Func<VirtualHostConfiguration, T> callback) where T : EventProcessor, IAccountSecUpdateable + { + List<VirtualHostConfiguration> configs = new(); + DomainBuilder domains = new (configs, stack); + + //Add callback to capture this collection of configs when built + stack.AddHosts(() => configs.Select(c => new CustomServiceHost<T>(c.Clone(), callback(c))).ToArray()); + + return domains; + } + + /// <summary> + /// Adds a single <see cref="IHttpMiddleware"/> instance to the virtual host + /// </summary> + /// <param name="vhBuilder"></param> + /// <param name="middleware">The middleware instance to add</param> + /// <returns></returns> + 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<string>)defaultFiles); + } + + public static IVirtualHostBuilder WithDefaultFiles(this IVirtualHostBuilder vhBuidler, IReadOnlyCollection<string> defaultFiles) + { + vhBuidler.WithOption(c => c.DefaultFiles = defaultFiles); + return vhBuidler; + } + + public static IVirtualHostBuilder WithExcludedExtensions(this IVirtualHostBuilder vhBuilder, params string[] excludedExtensions) + { + return vhBuilder.WithExcludedExtensions(new HashSet<string>(excludedExtensions)); + } + + public static IVirtualHostBuilder WithExcludedExtensions(this IVirtualHostBuilder vhBuilder, IReadOnlySet<string> 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<IPAddress> 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<IPAddress>(addresses)); + return vhBuilder; + } + + private static void AddHosts(this HttpServiceStackBuilder stack, Func<IServiceHost[]> hosts) => stack.WithDomain(p => Array.ForEach(hosts(), h => p.Add(h))); + + private static void OnPluginServiceEvent<T>(this IManagedPlugin plugin, Action<T> loader) + { + object? service = plugin.Services.GetService(typeof(T)); + + if (service is T s) + { + loader(s); + } + } + + private sealed record class DomainBuilder(List<VirtualHostConfiguration> Configs, HttpServiceStackBuilder Stack) : IDomainBuilder + { + + ///<inheritdoc/> + 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); + } + + ///<inheritdoc/> + public IDomainBuilder WithVirtualHost(VirtualHostConfiguration config) + { + Configs.Add(config); + return this; + } + + private sealed record class VHostBuilder(VirtualHostConfiguration Config) : IVirtualHostBuilder + { + public IVirtualHostBuilder WithOption(Action<VirtualHostConfiguration> configCallback) + { + configCallback(Config); + return this; + } + } + } + + private sealed class CustomServiceHost<T> : 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); + } + } + + ///<inheritdoc/> + public IWebRoot Processor => Instance; + + ///<inheritdoc/> + public IHostTransportInfo TransportInfo => Config; + + ///<inheritdoc/> + void IServiceHost.OnRuntimeServiceAttach(IManagedPlugin plugin, IEndpoint[] endpoints) + { + //Add endpoints to service + Instance.EndpointTable.AddEndpoint(endpoints); + + //Add all services + plugin.OnPluginServiceEvent<ISessionProvider>(Instance.SetSessionProvider); + plugin.OnPluginServiceEvent<IPageRouter>(Instance.SetPageRouter); + plugin.OnPluginServiceEvent<IAccountSecurityProvider>(p => Instance.SetAccountSecProvider(p)); + + //Add all middleware to the chain + plugin.OnPluginServiceEvent<IHttpMiddleware[]>(p => Array.ForEach(p, mw => Instance.MiddlewareChain.AddLast(mw))); + } + + ///<inheritdoc/> + void IServiceHost.OnRuntimeServiceDetach(IManagedPlugin plugin, IEndpoint[] endpoints) + { + //Remove endpoints + Instance.EndpointTable.RemoveEndpoint(endpoints); + + //Remove all services + plugin.OnPluginServiceEvent<ISessionProvider>(p => Instance.SetSessionProvider(null)); + plugin.OnPluginServiceEvent<IPageRouter>(p => Instance.SetPageRouter(null)); + plugin.OnPluginServiceEvent<IAccountSecurityProvider>(p => Instance.SetAccountSecProvider(null)); + + //Remove all middleware from the chain + plugin.OnPluginServiceEvent<IHttpMiddleware[]>(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; + } + + ///<inheritdoc/> + public override string Directory { get; } + + ///<inheritdoc/> + public override string Hostname => Config.Hostname; + + ///<inheritdoc/> + public override IEpProcessingOptions Options => Config; + + ///<inheritdoc/> + public override IAccountSecurityProvider? AccountSecurity => SecProvider; + + ///<inheritdoc/> + protected override ILogProvider Log => Config.LogProvider; + + ///<inheritdoc/> + public override bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity) => Hooks.ErrorHandler(errorCode, entity); + + ///<inheritdoc/> + public override void PostProcessFile(HttpEntity entity, in FileProcessArgs chosenRoutine) => Hooks.PostProcessFile(entity, chosenRoutine); + + ///<inheritdoc/> + public override ValueTask<FileProcessArgs> PreProcessEntityAsync(HttpEntity entity) => Hooks.PreProcessEntityAsync(entity); + + ///<inheritdoc/> + public override string TranslateResourcePath(string requestPath) => Hooks.TranslateResourcePath(requestPath); + + ///<inheritdoc/> + 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 +{ + /// <summary> + /// A virtual host configuration container + /// </summary> + public class VirtualHostConfiguration : IHostTransportInfo, IEpProcessingOptions + { + /// <summary> + /// The directory that this virtual host will serve files from + /// </summary> + public DirectoryInfo RootDir { get; set; } = null!; + + /// <summary> + /// The hostname, or domain name, that this virtual host will respond to + /// <para>Default: *</para> + /// </summary> + public string Hostname { get; set; } = "*"; + + /// <summary> + /// The transport endpoint that this virtual host will listen on + /// <para>Default: 0.0.0.0:80</para> + /// </summary> + public IPEndPoint TransportEndpoint { get; set; } = new IPEndPoint(IPAddress.Any, 80); + + /// <summary> + /// An optional certificate to use for TLS connections + /// </summary> + public X509Certificate? Certificate { get; set; } + + /// <summary> + /// A log provider to use for this virtual host + /// </summary> + public ILogProvider LogProvider { get; set; } = null!; + + /// <summary> + /// The name of a default file to search for within a directory if no file is specified (index.html). + /// This array should be ordered. + /// <para>Default: empty set</para> + /// </summary> + public IReadOnlyCollection<string> DefaultFiles { get; set; } = Array.Empty<string>(); + + /// <summary> + /// File extensions that are denied from being read from the filesystem + /// <para>Default: empty set</para> + /// </summary> + public IReadOnlySet<string> ExcludedExtensions { get; set; } = new HashSet<string>(); + + /// <summary> + /// File attributes that must be matched for the file to be accessed, defaults to all allowed + /// <para> Default: 0xFFFFFFFF</para> + /// </summary> + public FileAttributes AllowedAttributes { get; set; } = unchecked((FileAttributes)(0xFFFFFFFF)); + + /// <summary> + /// Files that match any attribute flag set will be denied + /// <para>Default: <see cref="FileAttributes.System"/></para> + /// </summary> + public FileAttributes DissallowedAttributes { get; set; } = FileAttributes.System; + + /// <summary> + /// A table of known downstream servers/ports that can be trusted to proxy connections + /// <para>Default: empty set</para> + /// </summary> + public IReadOnlySet<IPAddress> DownStreamServers { get; set; } = new HashSet<IPAddress>(); + + /// <summary> + /// A <see cref="TimeSpan"/> for how long a connection may remain open before all operations are cancelled + /// <para>Default: 60 seconds</para> + /// </summary> + public TimeSpan ExecutionTimeout { get; set; } = TimeSpan.FromSeconds(60); + + /// <summary> + /// A <see cref="TimeSpan"/> for how long a connection may remain idle before all operations are cancelled + /// </summary> + public IVirtualHostHooks EventHooks { get; set; } = null!; + + /// <summary> + /// A set of custom middleware to add to virtual host middleware pipeline + /// </summary> + public ICollection<IHttpMiddleware> CustomMiddleware { get; } = new List<IHttpMiddleware>(); + + internal VirtualHostConfiguration Clone() => (VirtualHostConfiguration)MemberwiseClone(); + } +} |