aboutsummaryrefslogtreecommitdiff
path: root/lib/Plugins.Essentials.ServiceStack
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-10-18 00:55:12 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-10-18 00:55:12 -0400
commit44a05ac3854d6cd0fa65d8ffc0f6efe7abfc87ad (patch)
tree31a1c2af8f264ca1c53128f91e22827cf0f5a1a0 /lib/Plugins.Essentials.ServiceStack
parent62f9e126912fa9a620a361fb5b88d33506e096fb (diff)
Essentials, plugins, and http core experimental updates
Diffstat (limited to 'lib/Plugins.Essentials.ServiceStack')
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs9
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/IVirtualHostHooks.cs16
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs4
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs2
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs3
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs6
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs80
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs48
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs36
9 files changed, 132 insertions, 72 deletions
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs
index c4d602b..91184bd 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs
@@ -150,6 +150,11 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
servers.AddLast(server);
}
+ return new(servers, sd, GetPluginStack(sd));
+ }
+
+ private PluginStackInitializer GetPluginStack(ServiceDomain domain)
+ {
//Always init manual array
manualPlugins ??= Array.Empty<IManualPlugin>();
@@ -160,9 +165,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
plugins ??= new EmptyPluginStack();
#pragma warning restore CA2000 // Dispose objects before losing scope
- IPluginInitializer init = new PluginStackInitializer(plugins, manualPlugins);
-
- return new(servers, sd, init);
+ return new (domain.GetListener(), plugins, manualPlugins);
}
/*
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/IVirtualHostHooks.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/IVirtualHostHooks.cs
index 6691d6b..acfc6e2 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/Construction/IVirtualHostHooks.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/IVirtualHostHooks.cs
@@ -23,7 +23,6 @@
*/
using System.Net;
-using System.Threading.Tasks;
using VNLib.Net.Http;
@@ -52,7 +51,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
/// When an error occurs and is handled by the library, this event is invoked
/// </para>
/// <para>
- /// NOTE: This function must be thread-safe!
+ /// NOTE: This function must be thread-safe! And should not throw exceptions
/// </para>
/// </summary>
/// <param name="errorCode">The error code that was created during processing</param>
@@ -64,14 +63,19 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
/// 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);
+ /// <param name="args">The results to return to the file processor, or <see cref="FileProcessArgs.Continue"/> if processing should continue</param>
+ /// <returns></returns>
+ void PreProcessEntityAsync(HttpEntity entity, out FileProcessArgs args);
/// <summary>
- /// Allows for post processing of a selected <see cref="FileProcessArgs"/> for the given entity
+ /// Allows for post processing of a selected <see cref="FileProcessArgs"/> for the given entity.
+ /// <para>
+ /// This method may mutate the <paramref name="chosenRoutine"/> argument referrence to change the
+ /// the routine that will be used to process the file.
+ /// </para>
/// </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);
+ void PostProcessFile(HttpEntity entity, ref FileProcessArgs chosenRoutine);
}
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs
index d518218..54b7ff2 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs
@@ -316,10 +316,10 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
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);
+ public override void PreProcessEntity(HttpEntity entity, out FileProcessArgs preProcArgs) => Hooks.PreProcessEntityAsync(entity, out preProcArgs);
///<inheritdoc/>
- public override ValueTask<FileProcessArgs> PreProcessEntityAsync(HttpEntity entity) => Hooks.PreProcessEntityAsync(entity);
+ public override void PostProcessEntity(HttpEntity entity, ref FileProcessArgs chosenRoutine) => Hooks.PostProcessFile(entity, ref chosenRoutine);
///<inheritdoc/>
public override string TranslateResourcePath(string requestPath) => Hooks.TranslateResourcePath(requestPath);
diff --git a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs
index 7cd5ac4..16bc1a0 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs
@@ -65,7 +65,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
{
_servers = servers;
_serviceDomain = serviceDomain;
- _plugins = new(serviceDomain, plugins);
+ _plugins = new(plugins);
WaitForAllTask = Task.CompletedTask;
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs
index ac91f45..a9aa103 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs
@@ -24,14 +24,11 @@
using VNLib.Utils.Logging;
-using VNLib.Plugins.Runtime;
namespace VNLib.Plugins.Essentials.ServiceStack
{
internal interface IPluginInitializer
{
- void PrepareStack(IPluginEventListener listener);
-
IManagedPlugin[] InitializePluginStack(ILogProvider eventLogger);
void UnloadPlugins();
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs
index 010039d..d93df6d 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs
@@ -25,8 +25,8 @@
using System.Linq;
using System.Collections.Generic;
-using VNLib.Plugins.Runtime;
using VNLib.Utils.Logging;
+using VNLib.Plugins.Runtime;
namespace VNLib.Plugins.Essentials.ServiceStack
{
@@ -47,7 +47,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// </summary>
/// <param name="controller"></param>
/// <returns></returns>
- internal static IEnumerable<LivePlugin> GetOnlyWebPlugins(this PluginController controller) => controller.Plugins.Where(p => p.Plugin is IWebPlugin);
+ internal static IEnumerable<LivePlugin> GetOnlyWebPlugins(this PluginController controller) => controller.Plugins.Where(static p => p.Plugin is IWebPlugin);
/// <summary>
/// Loads all plugins that implement <see cref="IWebPlugin"/> interface into the
@@ -56,5 +56,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// <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);
}
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs
new file mode 100644
index 0000000..0eaa3a8
--- /dev/null
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginLoadEventListener.cs
@@ -0,0 +1,80 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Essentials.ServiceStack
+* File: PluginLoadEventListener.cs
+*
+* PluginLoadEventListener.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.Linq;
+
+using VNLib.Utils.Extensions;
+using VNLib.Plugins.Runtime;
+
+namespace VNLib.Plugins.Essentials.ServiceStack
+{
+ internal sealed record class PluginLoadEventListener(ServiceDomain Domain) : IPluginEventListener
+ {
+ ///<inheritdoc/>
+ void IPluginEventListener.OnPluginLoaded(PluginController controller, object? state) => OnPluginLoaded((state as IManagedPlugin)!);
+
+ ///<inheritdoc/>
+ void IPluginEventListener.OnPluginUnloaded(PluginController controller, object? state) => OnPluginUnloaded((state as IManagedPlugin)!);
+
+ /// <summary>
+ /// Called when a plugin has been successfully loaded and
+ /// should be put into service
+ /// </summary>
+ /// <param name="plugin">The plugin that was loaded</param>
+ public void OnPluginLoaded(IManagedPlugin plugin)
+ {
+ //Run onload method before invoking other handlers
+ plugin.OnPluginLoaded();
+
+ //Get event listeners at event time because deps may be modified by the domain
+ ServiceGroup[] deps = Domain.ServiceGroups.Select(static d => d).ToArray();
+
+ //run onload method
+ deps.TryForeach(d => d.OnPluginLoaded(plugin));
+ }
+
+ /// <summary>
+ /// Called when a plugin is about to be unloaded and should
+ /// be removed from service.
+ /// </summary>
+ /// <param name="plugin">The plugin instance to unload</param>
+ public void OnPluginUnloaded(IManagedPlugin plugin)
+ {
+ try
+ {
+ //Get event listeners at event time because deps may be modified by the domain
+ ServiceGroup[] deps = Domain.ServiceGroups.Select(static d => d).ToArray();
+
+ //Run unloaded method
+ deps.TryForeach(d => d.OnPluginUnloaded(plugin));
+ }
+ finally
+ {
+ //always unload the plugin wrapper
+ plugin.OnPluginUnloaded();
+ }
+ }
+ }
+}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
index 2f57367..ba3b91a 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
@@ -24,13 +24,10 @@
using System;
-using System.Linq;
using System.Collections.Generic;
using VNLib.Utils;
using VNLib.Utils.Logging;
-using VNLib.Utils.Extensions;
-using VNLib.Plugins.Runtime;
namespace VNLib.Plugins.Essentials.ServiceStack
{
@@ -39,9 +36,8 @@ 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, IPluginEventListener
- {
- private readonly ServiceDomain _dependents;
+ internal sealed class PluginManager : VnDisposeable, IHttpPluginManager
+ {
private readonly IPluginInitializer _stack;
/// <summary>
@@ -51,9 +47,8 @@ namespace VNLib.Plugins.Essentials.ServiceStack
private IManagedPlugin[] _loadedPlugins;
- public PluginManager(ServiceDomain dependents, IPluginInitializer stack)
+ public PluginManager(IPluginInitializer stack)
{
- _dependents = dependents;
_stack = stack;
_loadedPlugins = Array.Empty<IManagedPlugin>();
}
@@ -62,11 +57,13 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// Configures the manager to capture and manage plugins within a plugin stack
/// </summary>
/// <param name="debugLog"></param>
+ /// <exception cref="InvalidOperationException"></exception>
+ /// <exception cref="AggregateException"></exception>
public void LoadPlugins(ILogProvider debugLog)
{
_ = _stack ?? throw new InvalidOperationException("Plugin stack has not been set.");
- _stack.PrepareStack(this);
+ Check();
//Initialize the plugin stack and store the loaded plugins
_loadedPlugins = _stack.InitializePluginStack(debugLog);
@@ -124,38 +121,5 @@ namespace VNLib.Plugins.Essentials.ServiceStack
//Dispose the plugin stack
_stack.Dispose();
}
-
- void IPluginEventListener.OnPluginLoaded(PluginController controller, object? state)
- {
- IManagedPlugin mp = (state as IManagedPlugin)!;
-
- //Run onload method before invoking other handlers
- mp.OnPluginLoaded();
-
- //Get event listeners at event time because deps may be modified by the domain
- ServiceGroup[] deps = _dependents.ServiceGroups.Select(static d => d).ToArray();
-
- //run onload method
- deps.TryForeach(d => d.OnPluginLoaded(mp));
- }
-
- void IPluginEventListener.OnPluginUnloaded(PluginController controller, object? state)
- {
- IManagedPlugin plugin = (state as IManagedPlugin)!;
-
- 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(plugin));
- }
- finally
- {
- //always unload the plugin wrapper
- plugin.OnPluginUnloaded();
- }
- }
}
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs
index 6ccd862..8a4e801 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs
@@ -40,13 +40,12 @@ using VNLib.Plugins.Attributes;
namespace VNLib.Plugins.Essentials.ServiceStack
{
- internal sealed record class PluginStackInitializer(IPluginStack Stack, IManualPlugin[] ManualPlugins) : IPluginInitializer
+ internal sealed record class PluginStackInitializer(PluginLoadEventListener Listener, IPluginStack Stack, IManualPlugin[] ManualPlugins) : IPluginInitializer
{
private readonly LinkedList<IManagedPlugin> _managedPlugins = new();
private readonly LinkedList<ManualPluginWrapper> _manualPlugins = new();
-
- ///<inheritdoc/>
- public void PrepareStack(IPluginEventListener listener)
+
+ private void PrepareStack()
{
/*
* Since we own the plugin stack, it is safe to build it here.
@@ -62,7 +61,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
Array.ForEach(wrapper, p => _managedPlugins.AddLast(p));
//Register for all plugins and pass the plugin instance as the state object
- Array.ForEach(wrapper, p => p.Plugin.Controller.Register(listener, p));
+ 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)));
@@ -71,6 +70,9 @@ namespace VNLib.Plugins.Essentials.ServiceStack
///<inheritdoc/>
public IManagedPlugin[] InitializePluginStack(ILogProvider debugLog)
{
+ //Prepare the plugin stack before initializing
+ PrepareStack();
+
//single thread initialziation
LinkedList<IManagedPlugin> _loadedPlugins = new();
@@ -96,6 +98,9 @@ namespace VNLib.Plugins.Essentials.ServiceStack
public void UnloadPlugins()
{
Stack.UnloadAll();
+
+ //Unload manual plugins in listener
+ _managedPlugins.TryForeach(mp => Listener.OnPluginUnloaded(mp));
_manualPlugins.TryForeach(static mp => mp.Unload());
}
@@ -104,9 +109,13 @@ namespace VNLib.Plugins.Essentials.ServiceStack
{
Stack.ReloadAll();
- //Reload manual plugins
+ //Unload manual plugins in listener, then call the unload method
+ _managedPlugins.TryForeach(mp => Listener.OnPluginUnloaded(mp));
_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/>
@@ -155,7 +164,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
return false;
}
- private static void LoadPlugin(IManagedPlugin plugin, ILogProvider debugLog)
+ private void LoadPlugin(IManagedPlugin plugin, ILogProvider debugLog)
{
Stopwatch sw = new();
try
@@ -170,6 +179,8 @@ 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
{
@@ -313,13 +324,12 @@ namespace VNLib.Plugins.Essentials.ServiceStack
return false;
}
+
+ void IManagedPlugin.OnPluginLoaded()
+ { }
-
- /*
- * SHOULD NEVER BE CALLED
- */
- void IManagedPlugin.OnPluginLoaded() => throw new NotImplementedException();
- void IManagedPlugin.OnPluginUnloaded() => throw new NotImplementedException();
+ void IManagedPlugin.OnPluginUnloaded()
+ { }
///<inheritdoc/>
public override string ToString() => Plugin.Name;