aboutsummaryrefslogtreecommitdiff
path: root/lib/Plugins.Essentials.ServiceStack/src
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-09-23 00:38:45 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-09-23 00:38:45 -0400
commit73b8514f38f30fe6e0b3802c18a893b7a5713ca4 (patch)
treef11ed19ee9d5730397ed43aa48ee2af0d7a391ba /lib/Plugins.Essentials.ServiceStack/src
parent04c8fa8017a04da05cd8e1d104cfcd0a9aba17f5 (diff)
service stack building integrations and helper defaults
Diffstat (limited to 'lib/Plugins.Essentials.ServiceStack/src')
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/HttpServiceStackBuilder.cs1
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/IAccountSecUpdateable.cs44
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/IDomainBuilder.cs52
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs4
-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.cs77
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/ManagedPlugin.cs59
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs22
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/SsBuilderExtensions.cs319
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/VirtualHostConfiguration.cs117
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();
+ }
+}