diff options
author | vnugent <public@vaughnnugent.com> | 2024-02-14 14:10:27 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2024-02-14 14:10:27 -0500 |
commit | 2b1314c1475e7e1831c691cf349cb89c66fa320c (patch) | |
tree | 091fc132a2bee2e79a68d8c6d5eb20f1d989a3d2 /lib/Plugins.Essentials.ServiceStack | |
parent | f4e4db7c5320976406feb252ae8f8bdbe9b3e351 (diff) |
Squashed commit of the following:
commit ddd8a651b6eb43cfdd49d84056f8b9c34b543992
Author: vnugent <public@vaughnnugent.com>
Date: Wed Feb 14 00:15:50 2024 -0500
ci: reduce output noise and update Argon2 build
commit cf942959ff2feea03d3eda2ff2a263bdac4d6bc6
Author: vnugent <public@vaughnnugent.com>
Date: Mon Feb 12 18:39:18 2024 -0500
chore: update packages and minor fixes
commit ab506af9e2de2876b11bb45b3c7e787616c80155
Author: vnugent <public@vaughnnugent.com>
Date: Fri Feb 9 21:27:24 2024 -0500
fix: patch and update core runtime service injection
commit 7ed5e8b19164c28d3a238bd56878d2161fbea2e4
Author: vnugent <public@vaughnnugent.com>
Date: Thu Feb 8 18:26:11 2024 -0500
fork dotnetplugins and make some intial updates/upgrades
commit f4cab88d67be5da0953b14bd46fc972d4acc8606
Author: vnugent <public@vaughnnugent.com>
Date: Thu Feb 8 12:16:13 2024 -0500
update some heap api functions
commit 6035bf7ed8412f1da361cc5feddd860abfaf4fc1
Author: vnugent <public@vaughnnugent.com>
Date: Wed Feb 7 22:09:11 2024 -0500
working file-watcher notifications/rework
commit 698f8edf694ad9700ee2ce2220e692b496448ff9
Author: vnugent <public@vaughnnugent.com>
Date: Wed Feb 7 20:37:28 2024 -0500
remove mem-template and add file-watcher utility
commit b17591e0fb363222fcd7d93c2bad4ab1b102385f
Author: vnugent <public@vaughnnugent.com>
Date: Wed Feb 7 18:28:21 2024 -0500
add small memmove support for known small blocks
commit 631be4d4b27fdbcd4b0526e17a128bb0d86911eb
Author: vnugent <public@vaughnnugent.com>
Date: Wed Feb 7 18:08:02 2024 -0500
setup some readonly ref arguments and convert copy apis to readonly refs
commit 2ba8dec68d5cb192e61ad0141d4b460076d3f90a
Author: vnugent <public@vaughnnugent.com>
Date: Mon Feb 5 18:30:38 2024 -0500
restructure internal memmove strategies
commit 25cf02872da980893ad7fb51d4eccc932380582b
Author: vnugent <public@vaughnnugent.com>
Date: Sun Feb 4 01:29:18 2024 -0500
add http stream interface, profiling -> file read updates
commit 757668c44e78864dc69d5713a2cfba6db2ed9a2a
Author: vnugent <public@vaughnnugent.com>
Date: Fri Feb 2 14:27:04 2024 -0500
streamline data-copy api with proper large block support and net8 feature updates
commit f22c1765fd72ab40a10d8ec92a8cb6d9ec1b1a04
Author: vnugent <public@vaughnnugent.com>
Date: Mon Jan 29 16:16:23 2024 -0500
check for compression lib updates to close #2 and fix some ci build stuff
commit f974bfdef6a795b4a1c04602502ef506ef2587a9
Author: vnugent <public@vaughnnugent.com>
Date: Tue Jan 23 17:36:17 2024 -0500
switch allocator libs to lgpl2.1
commit 1fe5e01b329cd27b675000f1a557b784d3c88b56
Author: vnugent <public@vaughnnugent.com>
Date: Tue Jan 23 17:05:59 2024 -0500
consolidate allocator packages and close #1
commit 74e1107e522f00b670526193396217f40a6bade7
Author: vnugent <public@vaughnnugent.com>
Date: Tue Jan 23 15:43:40 2024 -0500
cache extension api tweaks
commit 96ca2b0388a6326b9bb74f3ab2f62eaede6681e0
Author: vnugent <public@vaughnnugent.com>
Date: Mon Jan 22 17:54:23 2024 -0500
explicit tcp server args reuse
Diffstat (limited to 'lib/Plugins.Essentials.ServiceStack')
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs | 2 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/Construction/IAccountSecUpdateable.cs | 44 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/Construction/IRuntimeServiceInjection.cs | 49 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs | 169 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs | 9 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs | 11 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs | 23 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/PluginRutimeEventHandler.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs) | 10 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs | 30 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs | 9 | ||||
-rw-r--r-- | lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs | 29 |
11 files changed, 201 insertions, 184 deletions
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs index f100c1d..3ae2183 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs @@ -168,7 +168,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction private PluginStackInitializer GetPluginStack(ServiceDomain domain) { //Always init manual array - manualPlugins ??= Array.Empty<IManualPlugin>(); + manualPlugins ??= []; //Only load plugins if the callback is configured IPluginStack? plugins = _getPlugins?.Invoke(); diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/IAccountSecUpdateable.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/IAccountSecUpdateable.cs deleted file mode 100644 index eee7a2e..0000000 --- a/lib/Plugins.Essentials.ServiceStack/src/Construction/IAccountSecUpdateable.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* -* 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.Construction -{ - /// <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/Construction/IRuntimeServiceInjection.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/IRuntimeServiceInjection.cs new file mode 100644 index 0000000..c18289e --- /dev/null +++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/IRuntimeServiceInjection.cs @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2024 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.ServiceStack +* File: IRuntimeServiceInjection.cs +* +* IRuntimeServiceInjection.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.Construction +{ + /// <summary> + /// Adds functionality to event processors for runtime service injection + /// that can be used to add and remove services from the service provider + /// at runtime. + /// </summary> + public interface IRuntimeServiceInjection + { + /// <summary> + /// Adds a collection of services to event processors + /// that can be used. + /// </summary> + /// <param name="services">The collection of exported services</param> + void AddServices(IServiceProvider services); + + /// <summary> + /// Removes a collection of services from event processors + /// </summary> + /// <param name="services">The service container that contains services to remove</param> + void RemoveServices(IServiceProvider services); + } +} diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs index d96809b..5512e49 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs @@ -26,14 +26,14 @@ using System; using System.IO; using System.Net; using System.Linq; +using System.Collections.Frozen; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Security.Cryptography.X509Certificates; using VNLib.Utils.Logging; +using VNLib.Utils.Extensions; 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.Construction @@ -51,7 +51,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction /// </summary> /// <param name="stack"></param> /// <returns>The <see cref="IDomainBuilder"/> used to define your service domain</returns> - public static IDomainBuilder WithDomain(this HttpServiceStackBuilder stack) => stack.WithDomain(p => new BasicVirtualHost(p.Clone())); + public static IDomainBuilder WithDomain(this HttpServiceStackBuilder stack) => WithDomain(stack, vhc => FromVirtualHostConfig(vhc.Clone())); /// <summary> /// Creates a new <see cref="IDomainBuilder"/> instance to define your @@ -74,7 +74,8 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction /// <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 + public static IDomainBuilder WithDomain<T>(this HttpServiceStackBuilder stack, Func<VirtualHostConfiguration, T> callback) + where T : EventProcessor, IRuntimeServiceInjection { List<VirtualHostConfiguration> configs = new(); DomainBuilder domains = new(configs, stack); @@ -174,14 +175,54 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction return vhBuilder; } + /// <summary> + /// Adds an array of IP addresses to the downstream server collection. This is a security + /// features that allows event handles to trust connections/ipaddresses that originate from + /// trusted downstream servers + /// </summary> + /// <param name="vhBuilder"></param> + /// <param name="addresses">The collection of IP addresses to set as trusted servers</param> + /// <returns></returns> public static IVirtualHostBuilder WithDownstreamServers(this IVirtualHostBuilder vhBuilder, params IPAddress[] addresses) { vhBuilder.WithOption(c => c.DownStreamServers = new HashSet<IPAddress>(addresses)); return vhBuilder; } + private static BasicVirtualHost FromVirtualHostConfig(VirtualHostConfiguration configuration) + { + /* + * Event processors configurations are considered immutable. That is, + * top-level elements are not allowed to be changed after the processor + * has been created. Some properties/containers are allowed to be modified + * such as middleware chains, and the service pool. + */ + + EventProcessorConfig conf = new( + configuration.RootDir.FullName, + configuration.Hostname, + configuration.LogProvider, + configuration) + { + AllowedAttributes = configuration.AllowedAttributes, + DissallowedAttributes = configuration.DissallowedAttributes, + DefaultFiles = configuration.DefaultFiles, + ExecutionTimeout = configuration.ExecutionTimeout, + + //Frozen sets are required for the event processor, for performance reasons + DownStreamServers = configuration.DownStreamServers.ToFrozenSet(), + ExcludedExtensions = configuration.ExcludedExtensions.ToFrozenSet(), + }; + + //Add all pre-configured middleware to the chain + configuration.CustomMiddleware.ForEach(conf.MiddlewareChain.Add); + + return new(configuration.EventHooks, conf); + } + - private static void AddHosts(this HttpServiceStackBuilder stack, Func<IServiceHost[]> hosts) => stack.WithDomain(p => Array.ForEach(hosts(), h => p.Add(h))); + 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) { @@ -193,7 +234,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction private sealed record class DomainBuilder(List<VirtualHostConfiguration> Configs, HttpServiceStackBuilder Stack) : IDomainBuilder { - ///<inheritdoc/> public IVirtualHostBuilder WithVirtualHost(DirectoryInfo rootDirectory, IVirtualHostHooks hooks, ILogProvider logger) { @@ -217,6 +257,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction private sealed record class VHostBuilder(VirtualHostConfiguration Config) : IVirtualHostBuilder { + ///<inheritdoc/> public IVirtualHostBuilder WithOption(Action<VirtualHostConfiguration> configCallback) { configCallback(Config); @@ -225,23 +266,9 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction } } - private sealed class CustomServiceHost<T> : IServiceHost where T : EventProcessor, IAccountSecUpdateable + private sealed class CustomServiceHost<T>(IHostTransportInfo Config, T Instance) : IServiceHost + where T : EventProcessor, IRuntimeServiceInjection { - 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.Add(mw); - } - } - ///<inheritdoc/> public IWebRoot Processor => Instance; @@ -252,78 +279,90 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction 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>(Instance.SetAccountSecProvider); + Instance.Options.EndpointTable.AddEndpoint(endpoints); + + //Inject services into the event processor service pool + Instance.AddServices(plugin.Services); - //Add all middleware to the chain - plugin.OnPluginServiceEvent<IHttpMiddleware[]>(p => Array.ForEach(p, mw => Instance.MiddlewareChain.Add(mw))); + //Add all exposed middleware to the chain + plugin.OnPluginServiceEvent<ICollection<IHttpMiddleware>>(p => p.TryForeach(Instance.Options.MiddlewareChain.Add)); } ///<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)); + Instance.Options.EndpointTable.RemoveEndpoint(endpoints); + Instance.RemoveServices(plugin.Services); //Remove all middleware from the chain - plugin.OnPluginServiceEvent<IHttpMiddleware[]>(p => Array.ForEach(p, mw => Instance.MiddlewareChain.RemoveMiddleware(mw))); + plugin.OnPluginServiceEvent<ICollection<IHttpMiddleware>>(p => p.TryForeach(Instance.Options.MiddlewareChain.Remove)); } } - private sealed class BasicVirtualHost : EventProcessor, IAccountSecUpdateable + private sealed class BasicVirtualHost(IVirtualHostHooks Hooks, EventProcessorConfig config) : EventProcessor(config), IRuntimeServiceInjection { - private readonly VirtualHostConfiguration Config; - private readonly IVirtualHostHooks Hooks; - - internal IAccountSecurityProvider? SecProvider; - - public BasicVirtualHost(VirtualHostConfiguration config) - { - Config = config; - Hooks = config.EventHooks; - Directory = config.RootDir.FullName; - } + /* + * Runtime service injection can be tricky, at least in my architecture. If all we have + * is am IServiceProvider instance, we cannot trust that the services availabe are + * exactly the same as the ones initially provided. So we can store the known types + * that a given service container DID export, and then use that to remove the services + * when the service provider is removed. + */ + private readonly ConditionalWeakTable<IServiceProvider, Type[]> _exposedTypes = new(); ///<inheritdoc/> - public override string Directory { get; } + public override bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity) => Hooks.ErrorHandler(errorCode, entity); ///<inheritdoc/> - public override string Hostname => Config.Hostname; + public override void PreProcessEntity(HttpEntity entity, out FileProcessArgs preProcArgs) => Hooks.PreProcessEntityAsync(entity, out preProcArgs); ///<inheritdoc/> - public override IEpProcessingOptions Options => Config; + public override void PostProcessEntity(HttpEntity entity, ref FileProcessArgs chosenRoutine) => Hooks.PostProcessFile(entity, ref chosenRoutine); ///<inheritdoc/> - public override IAccountSecurityProvider? AccountSecurity => SecProvider; + public override string TranslateResourcePath(string requestPath) => Hooks.TranslateResourcePath(requestPath); ///<inheritdoc/> - protected override ILogProvider Log => Config.LogProvider; + public void AddServices(IServiceProvider services) + { + Type[] exposedForHandler = []; - ///<inheritdoc/> - public override bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity) => Hooks.ErrorHandler(errorCode, entity); + foreach (Type type in ServicePool.Types) + { + //Get exported service by the desired type + object? service = services.GetService(type); - ///<inheritdoc/> - public override void PreProcessEntity(HttpEntity entity, out FileProcessArgs preProcArgs) => Hooks.PreProcessEntityAsync(entity, out preProcArgs); + //If its not null, then add it to the service pool + if (service is not null) + { + ServicePool.SetService(type, service); - ///<inheritdoc/> - public override void PostProcessEntity(HttpEntity entity, ref FileProcessArgs chosenRoutine) => Hooks.PostProcessFile(entity, ref chosenRoutine); + //Add to the exposed types list + exposedForHandler = [.. exposedForHandler, type]; + } + } - ///<inheritdoc/> - public override string TranslateResourcePath(string requestPath) => Hooks.TranslateResourcePath(requestPath); + //Add to the exposed types table + _exposedTypes.Add(services, exposedForHandler); + } ///<inheritdoc/> - public void SetAccountSecProvider(IAccountSecurityProvider? provider) => SecProvider = provider; - + public void RemoveServices(IServiceProvider services) + { + //Get all exposed types for this service provider + if (_exposedTypes.TryGetValue(services, out Type[]? exposed)) + { + foreach (Type type in exposed) + { + ServicePool.SetService(type, null); + } + + //Remove from the exposed types table + _exposedTypes.Remove(services); + } + } } } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs index 16bc1a0..71db38f 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials.ServiceStack @@ -29,6 +29,7 @@ using System.Collections.Generic; using VNLib.Utils; using VNLib.Net.Http; +using VNLib.Utils.Logging; namespace VNLib.Plugins.Essentials.ServiceStack { @@ -96,6 +97,12 @@ namespace VNLib.Plugins.Essentials.ServiceStack } /// <summary> + /// Loads all plugins into the service stack + /// </summary> + /// <param name="logProvider">A log provider for writing loading logs to</param> + public void LoadPlugins(ILogProvider logProvider) => _plugins.LoadPlugins(logProvider); + + /// <summary> /// Stops listening on all configured servers and returns a task that completes /// when the service host has stopped all servers and unloaded resources /// </summary> diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs index d8cdf75..52377ee 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs @@ -25,7 +25,6 @@ using System.Linq; using System.Collections.Generic; -using VNLib.Utils.Logging; using VNLib.Plugins.Essentials.Runtime; namespace VNLib.Plugins.Essentials.ServiceStack @@ -53,14 +52,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack return Enumerable.Empty<IEndpoint>(); } - /// <summary> - /// Loads all plugins that implement <see cref="IWebPlugin"/> interface into the - /// service stack - /// </summary> - /// <param name="stack"></param> - /// <param name="logProvider">A log provider for writing loading logs to</param> - public static void LoadPlugins(this HttpServiceStack stack, ILogProvider logProvider) => (stack.PluginManager as PluginManager)!.LoadPlugins(logProvider); - - internal static PluginLoadEventListener GetListener(this ServiceDomain domain) => new(domain); + internal static PluginRutimeEventHandler GetListener(this ServiceDomain domain) => new(domain); } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs index f5f2abc..f43da78 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs @@ -36,22 +36,15 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// A sealed type that manages the plugin interaction layer. Manages the lifetime of plugin /// instances, exposes controls, and relays stateful plugin events. /// </summary> - internal sealed class PluginManager : VnDisposeable, IHttpPluginManager + internal sealed class PluginManager(IPluginInitializer stack) : VnDisposeable, IHttpPluginManager { - private readonly IPluginInitializer _stack; /// <summary> /// The collection of internal controllers /// </summary> public IEnumerable<IManagedPlugin> Plugins => _loadedPlugins; - private IManagedPlugin[] _loadedPlugins; - - public PluginManager(IPluginInitializer stack) - { - _stack = stack; - _loadedPlugins = Array.Empty<IManagedPlugin>(); - } + private IManagedPlugin[] _loadedPlugins = []; /// <summary> /// Configures the manager to capture and manage plugins within a plugin stack @@ -61,12 +54,12 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// <exception cref="AggregateException"></exception> public void LoadPlugins(ILogProvider debugLog) { - _ = _stack ?? throw new InvalidOperationException("Plugin stack has not been set."); + _ = stack ?? throw new InvalidOperationException("Plugin stack has not been set."); Check(); //Initialize the plugin stack and store the loaded plugins - _loadedPlugins = _stack.InitializePluginStack(debugLog); + _loadedPlugins = stack.InitializePluginStack(debugLog); debugLog.Information("Plugin loading completed"); } @@ -94,7 +87,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack Check(); //Reload all plugins, causing an event cascade - _stack.ReloadPlugins(); + stack.ReloadPlugins(); } /// <inheritdoc/> @@ -103,7 +96,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack Check(); //Unload all plugin controllers - _stack.UnloadPlugins(); + stack.UnloadPlugins(); /* * All plugin instances must be destroyed because the @@ -116,10 +109,10 @@ namespace VNLib.Plugins.Essentials.ServiceStack protected override void Free() { //Clear plugin table - _loadedPlugins = Array.Empty<IManagedPlugin>(); + _loadedPlugins = []; //Dispose the plugin stack - _stack.Dispose(); + stack.Dispose(); } } } diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginRutimeEventHandler.cs index b24019b..f892823 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginRutimeEventHandler.cs @@ -3,9 +3,9 @@ * * Library: VNLib * Package: VNLib.Plugins.Essentials.ServiceStack -* File: PluginLoadEventListener.cs +* File: PluginRutimeEventHandler.cs * -* PluginLoadEventListener.cs is part of VNLib.Plugins.Essentials.ServiceStack which +* PluginRutimeEventHandler.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 @@ -30,7 +30,7 @@ using VNLib.Plugins.Runtime; namespace VNLib.Plugins.Essentials.ServiceStack { - internal sealed record class PluginLoadEventListener(ServiceDomain Domain) : IPluginEventListener + internal sealed class PluginRutimeEventHandler(ServiceDomain Domain) : IPluginEventListener { ///<inheritdoc/> void IPluginEventListener.OnPluginLoaded(PluginController controller, object? state) => OnPluginLoaded((state as IManagedPlugin)!); @@ -43,7 +43,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// should be put into service /// </summary> /// <param name="plugin">The plugin that was loaded</param> - public void OnPluginLoaded(IManagedPlugin plugin) + internal void OnPluginLoaded(IManagedPlugin plugin) { //Run onload method before invoking other handlers plugin.OnPluginLoaded(); @@ -60,7 +60,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// be removed from service. /// </summary> /// <param name="plugin">The plugin instance to unload</param> - public void OnPluginUnloaded(IManagedPlugin plugin) + internal void OnPluginUnloaded(IManagedPlugin plugin) { try { diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs index e6489c9..5a33425 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs @@ -39,7 +39,7 @@ using VNLib.Plugins.Runtime.Services; namespace VNLib.Plugins.Essentials.ServiceStack { - internal sealed record class PluginStackInitializer(PluginLoadEventListener Listener, IPluginStack Stack, IManualPlugin[] ManualPlugins, bool ConcurrentLoad) + internal sealed class PluginStackInitializer(PluginRutimeEventHandler Listener, IPluginStack Stack, IManualPlugin[] ManualPlugins, bool ConcurrentLoad) : IPluginInitializer { private readonly LinkedList<IManagedPlugin> _managedPlugins = new(); @@ -64,7 +64,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack Array.ForEach(wrapper, p => p.Plugin.Controller.Register(Listener, p)); //Add manual plugins to list of managed plugins - Array.ForEach(ManualPlugins, p => _manualPlugins.AddLast(new ManualPluginWrapper(p))); + Array.ForEach(ManualPlugins, p => _manualPlugins.AddLast(new ManualPluginWrapper(Listener, p))); } ///<inheritdoc/> @@ -103,7 +103,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack _loadedPlugins.TryForeach(_loadedPlugins => LoadPlugin(_loadedPlugins, debugLog)); } - return _loadedPlugins.ToArray(); + return [.. _loadedPlugins]; } ///<inheritdoc/> @@ -112,7 +112,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack Stack.UnloadAll(); //Unload manual plugins in listener - _managedPlugins.TryForeach(mp => Listener.OnPluginUnloaded(mp)); _manualPlugins.TryForeach(static mp => mp.Unload()); } @@ -121,13 +120,11 @@ namespace VNLib.Plugins.Essentials.ServiceStack { Stack.ReloadAll(); - //Unload manual plugins in listener, then call the unload method - _managedPlugins.TryForeach(mp => Listener.OnPluginUnloaded(mp)); + //Unload manual plugins in listener _manualPlugins.TryForeach(static mp => mp.Unload()); //Load, then invoke on-loaded events _manualPlugins.TryForeach(static mp => mp.Load()); - _managedPlugins.TryForeach(mp => Listener.OnPluginLoaded(mp)); } ///<inheritdoc/> @@ -191,8 +188,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack else if (plugin is ManualPluginWrapper mpw) { mpw.Load(); - //Call the on-load event in listener explicitly - Listener.OnPluginLoaded(plugin); } else { @@ -219,14 +214,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack private ServiceContainer? _services; ///<inheritdoc/> - public IServiceContainer Services - { - get - { - _ = _services ?? throw new InvalidOperationException("The service container is not currently loaded"); - return _services!; - } - } + public IServiceContainer Services => _services ?? throw new InvalidOperationException("The service container is not currently loaded"); /* * Automatically called after the plugin has successfully loaded @@ -275,7 +263,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack public override string ToString() => Path.GetFileName(Plugin.Config.AssemblyFile); } - private sealed record class ManualPluginWrapper(IManualPlugin Plugin) : IManagedPlugin, IDisposable + private sealed record class ManualPluginWrapper(PluginRutimeEventHandler Listener, IManualPlugin Plugin) : IManagedPlugin, IDisposable { private ServiceContainer _container = new(); @@ -286,10 +274,16 @@ namespace VNLib.Plugins.Essentials.ServiceStack { Plugin.Load(); Plugin.GetAllExportedServices(Services); + + //Finally notify of load + Listener.OnPluginLoaded(this); } public void Unload() { + //Notify of unload + Listener.OnPluginUnloaded(this); + Plugin.Unload(); //Unload and re-init container diff --git a/lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs b/lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs index 4cff671..35bf65b 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials.ServiceStack @@ -38,7 +38,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// </summary> public sealed class ServiceDomain { - private readonly LinkedList<ServiceGroup> _serviceGroups; + private readonly LinkedList<ServiceGroup> _serviceGroups = new(); /// <summary> /// Gets all service groups loaded in the service manager @@ -46,11 +46,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack public IReadOnlyCollection<ServiceGroup> ServiceGroups => _serviceGroups; /// <summary> - /// Initializes a new empty <see cref="ServiceDomain"/> - /// </summary> - public ServiceDomain() => _serviceGroups = new(); - - /// <summary> /// Uses the supplied callback to get a collection of virtual hosts /// to build the current domain with /// </summary> diff --git a/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs b/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs index da34d54..29b9fdc 100644 --- a/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs +++ b/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs @@ -37,15 +37,21 @@ namespace VNLib.Plugins.Essentials.ServiceStack /// common transport (interface, port, and SSL status) /// and may be loaded by a single server instance. /// </summary> - public sealed class ServiceGroup + /// <remarks> + /// Initalizes a new <see cref="ServiceGroup"/> of virtual hosts + /// with common transport + /// </remarks> + /// <param name="serviceEndpoint">The <see cref="IPEndPoint"/> to listen for connections on</param> + /// <param name="hosts">The hosts that share a common interface endpoint</param> + public sealed class ServiceGroup(IPEndPoint serviceEndpoint, IEnumerable<IServiceHost> hosts) { - private readonly LinkedList<IServiceHost> _vHosts; - private readonly ConditionalWeakTable<IManagedPlugin, IEndpoint[]> _endpointsForPlugins; + private readonly LinkedList<IServiceHost> _vHosts = new(hosts); + private readonly ConditionalWeakTable<IManagedPlugin, IEndpoint[]> _endpointsForPlugins = new(); /// <summary> /// The <see cref="IPEndPoint"/> transport endpoint for all loaded service hosts /// </summary> - public IPEndPoint ServiceEndpoint { get; } + public IPEndPoint ServiceEndpoint => serviceEndpoint; /// <summary> /// The collection of hosts that are loaded by this group @@ -53,26 +59,13 @@ namespace VNLib.Plugins.Essentials.ServiceStack public IReadOnlyCollection<IServiceHost> Hosts => _vHosts; /// <summary> - /// Initalizes a new <see cref="ServiceGroup"/> of virtual hosts - /// with common transport - /// </summary> - /// <param name="serviceEndpoint">The <see cref="IPEndPoint"/> to listen for connections on</param> - /// <param name="hosts">The hosts that share a common interface endpoint</param> - public ServiceGroup(IPEndPoint serviceEndpoint, IEnumerable<IServiceHost> hosts) - { - _endpointsForPlugins = new(); - _vHosts = new(hosts); - ServiceEndpoint = serviceEndpoint; - } - - /// <summary> /// Manually detatches runtime services and their loaded endpoints from all /// endpoints. /// </summary> internal void UnloadAll() { //Remove all loaded endpoints - _vHosts.TryForeach(v => _endpointsForPlugins.TryForeach(eps => v.OnRuntimeServiceDetach(eps.Key, eps.Value))); + _vHosts.TryForeach(v => _endpointsForPlugins.ForEach(eps => v.OnRuntimeServiceDetach(eps.Key, eps.Value))); //Clear all hosts _vHosts.Clear(); |