aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-06-28 15:48:22 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-06-28 15:48:22 -0400
commit6b8c67888731f7dd210acdb2b1160cdbdbe30d47 (patch)
treed4eb2d4b2b522bb6936671f7648a29521446b72b
parent12391e9a207b60b41a074600fc2373ad3eb1c3ab (diff)
refactor: Update service stack to reflect new loading patterns
-rw-r--r--lib/Net.Http/src/Core/HttpServerBase.cs35
-rw-r--r--lib/Net.Http/src/Core/HttpServerProcessing.cs20
-rw-r--r--lib/Net.Http/src/HttpConfig.cs12
-rw-r--r--lib/Net.Messaging.FBM/src/Server/FBMListenerSessionParams.cs12
-rw-r--r--lib/Net.Transport.SimpleTCP/src/TCPConfig.cs26
-rw-r--r--lib/Net.Transport.SimpleTCP/src/TcpServer.cs15
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs68
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/HttpTransportMapping.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/IHostTransportInfo.cs)34
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/IDomainBuilder.cs24
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/IServiceGroupBuilder.cs60
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/ServiceBuilder.cs79
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs322
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/VirtualHostConfiguration.cs33
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Construction/VirtualHostConstructionExtensions.cs107
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs10
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/IServiceHost.cs7
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/IHttpPluginManager.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/IHttpPluginManager.cs)4
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/IManagedPlugin.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs)2
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/IManualPlugin.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/IManualPlugin.cs)2
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/IPluginInitializer.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs)4
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginExtensions.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs)2
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginManager.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs)6
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginRutimeEventHandler.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/PluginRutimeEventHandler.cs)2
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginStackInitializer.cs (renamed from lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs)14
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs54
-rw-r--r--lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs9
26 files changed, 556 insertions, 407 deletions
diff --git a/lib/Net.Http/src/Core/HttpServerBase.cs b/lib/Net.Http/src/Core/HttpServerBase.cs
index 1ccace9..0eb5cef 100644
--- a/lib/Net.Http/src/Core/HttpServerBase.cs
+++ b/lib/Net.Http/src/Core/HttpServerBase.cs
@@ -115,7 +115,7 @@ namespace VNLib.Net.Http
/// <summary>
/// Gets a value indicating whether the server is listening for connections
/// </summary>
- public bool Running { get; private set; }
+ public bool Running => Transports.Any(static t => t.Running);
/// <summary>
/// Cached supported compression methods
@@ -284,22 +284,11 @@ namespace VNLib.Net.Http
//Listen to connections on all transports async
IEnumerable<Task> runTasks = Transports.Select(ListenAsync);
- //Set running flag and will be reset when all listening tasks are done
- Running = true;
-
//Calling WhenAll() will force the numeration and schedule listening tasks
- return Task.WhenAll(runTasks)
- .ContinueWith(
- OnAllStopped,
- CancellationToken.None,
- TaskContinuationOptions.RunContinuationsAsynchronously,
- TaskScheduler.Default
- );
+ return Task.WhenAll(runTasks);
//Defer listening tasks to the task scheduler to avoid blocking this thread
Task ListenAsync(ListenerState tp) => Task.Run(() => ListenWorkerDoWork(tp), cancellationToken);
-
- void OnAllStopped(Task _) => Running = false;
}
/*
@@ -308,11 +297,22 @@ namespace VNLib.Net.Http
private async Task ListenWorkerDoWork(ListenerState state)
{
state.Running = true;
-
- _config.ServerLog.Information("HTTP server {hc} listening for connections", GetHashCode());
+
+ if (_config.ServerLog.IsEnabled(LogLevel.Verbose))
+ {
+ _config.ServerLog.Verbose(
+ format: "HTTP server {hc} listening for connections on {iface}",
+ GetHashCode(),
+ state.OriginServer
+ );
+ }
+ else
+ {
+ _config.ServerLog.Information("HTTP server {hc} listening for connections", GetHashCode());
+ }
//Listen for connections until canceled
- while (true)
+ do
{
try
{
@@ -331,7 +331,8 @@ namespace VNLib.Net.Http
{
_config.ServerLog.Error(ex);
}
- }
+
+ } while (true);
//Clear all caches before leaving to aid gc
CacheHardClear();
diff --git a/lib/Net.Http/src/Core/HttpServerProcessing.cs b/lib/Net.Http/src/Core/HttpServerProcessing.cs
index f5dbbc7..8594ea0 100644
--- a/lib/Net.Http/src/Core/HttpServerProcessing.cs
+++ b/lib/Net.Http/src/Core/HttpServerProcessing.cs
@@ -29,7 +29,6 @@ using System.Threading;
using System.Net.Sockets;
using System.Diagnostics;
using System.Threading.Tasks;
-using System.Collections.Generic;
using System.Runtime.CompilerServices;
using VNLib.Utils.Memory;
@@ -386,16 +385,17 @@ namespace VNLib.Net.Http
private async Task<bool> ProcessRequestAsync(ListenerState listenState, HttpContext context)
{
//Get the server root for the specified location or fallback to a wildcard host if one is selected
- IWebRoot? root = listenState.Roots.GetValueOrDefault(
- context.Request.State.Location.DnsSafeHost,
- listenState.DefaultRoute
- );
-
- if (root == null)
+
+ if (!listenState.Roots.TryGetValue(context.Request.State.Location.DnsSafeHost, out IWebRoot? root))
{
- context.Respond(HttpStatusCode.NotFound);
- //make sure control leaves
- return true;
+ if (listenState.DefaultRoute is null)
+ {
+ context.Respond(HttpStatusCode.NotFound);
+ //make sure control leaves
+ return true;
+ }
+
+ root = listenState.DefaultRoute;
}
//Check the expect header and return an early status code
diff --git a/lib/Net.Http/src/HttpConfig.cs b/lib/Net.Http/src/HttpConfig.cs
index aa6e34a..40e9f88 100644
--- a/lib/Net.Http/src/HttpConfig.cs
+++ b/lib/Net.Http/src/HttpConfig.cs
@@ -53,17 +53,11 @@ namespace VNLib.Net.Http
/// <summary>
/// Initializes a new instance of the <see cref="HttpConfig"/> struct
/// </summary>
- /// <param name="serverLog"></param>
- /// <param name="memoryPool"></param>
/// <param name="httpEncoding"></param>
- public HttpConfig(ILogProvider serverLog, IHttpMemoryPool memoryPool, Encoding httpEncoding)
+ public HttpConfig(Encoding httpEncoding)
{
- ArgumentNullException.ThrowIfNull(serverLog);
- ArgumentNullException.ThrowIfNull(memoryPool);
ArgumentNullException.ThrowIfNull(httpEncoding);
- ServerLog = serverLog;
- MemoryPool = memoryPool;
HttpEncoding = httpEncoding;
//Init pre-encded segments
@@ -77,12 +71,12 @@ namespace VNLib.Net.Http
/// <summary>
/// A log provider that all server related log entiries will be written to
/// </summary>
- public readonly ILogProvider ServerLog { get; init; }
+ public required readonly ILogProvider ServerLog { get; init; }
/// <summary>
/// Server memory pool to use for allocating buffers
/// </summary>
- public readonly IHttpMemoryPool MemoryPool { get; init; }
+ public required readonly IHttpMemoryPool MemoryPool { get; init; }
/// <summary>
/// The absolute request entity body size limit in bytes
diff --git a/lib/Net.Messaging.FBM/src/Server/FBMListenerSessionParams.cs b/lib/Net.Messaging.FBM/src/Server/FBMListenerSessionParams.cs
index 0b4fa5b..435284b 100644
--- a/lib/Net.Messaging.FBM/src/Server/FBMListenerSessionParams.cs
+++ b/lib/Net.Messaging.FBM/src/Server/FBMListenerSessionParams.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Net.Messaging.FBM
@@ -36,27 +36,27 @@ namespace VNLib.Net.Messaging.FBM.Server
/// The size of the buffer to use while reading data from the websocket
/// in the listener loop
/// </summary>
- public readonly int RecvBufferSize { get; init; }
+ public required readonly int RecvBufferSize { get; init; }
/// <summary>
/// The size of the buffer to store <see cref="FBMMessageHeader"/> values in
/// the <see cref="FBMRequestMessage"/>
/// </summary>
- public readonly int MaxHeaderBufferSize { get; init; }
+ public required readonly int MaxHeaderBufferSize { get; init; }
/// <summary>
/// The size of the internal message response buffer when
/// not streaming
/// </summary>
- public readonly int ResponseBufferSize { get; init; }
+ public required readonly int ResponseBufferSize { get; init; }
/// <summary>
/// The FMB message header character encoding
/// </summary>
- public readonly Encoding HeaderEncoding { get; init; }
+ public required readonly Encoding HeaderEncoding { get; init; }
/// <summary>
/// The absolute maxium size (in bytes) message to process before
/// closing the websocket connection. This value should be negotiaed
/// by clients or hard-coded to avoid connection issues
/// </summary>
- public readonly int MaxMessageSize { get; init; }
+ public required readonly int MaxMessageSize { get; init; }
}
}
diff --git a/lib/Net.Transport.SimpleTCP/src/TCPConfig.cs b/lib/Net.Transport.SimpleTCP/src/TCPConfig.cs
index 9003e0d..f5c8893 100644
--- a/lib/Net.Transport.SimpleTCP/src/TCPConfig.cs
+++ b/lib/Net.Transport.SimpleTCP/src/TCPConfig.cs
@@ -39,44 +39,44 @@ namespace VNLib.Net.Transport.Tcp
/// <summary>
/// The <see cref="IPEndPoint"/> the listening socket will bind to
/// </summary>
- public readonly IPEndPoint LocalEndPoint { get; init; }
+ public required readonly IPEndPoint LocalEndPoint { get; init; }
/// <summary>
/// The log provider used to write logging information to
/// </summary>
- public readonly ILogProvider Log { get; init; }
+ public required readonly ILogProvider Log { get; init; }
/// <summary>
/// If TCP keepalive is enabled, the amount of time the connection is considered alive before another probe message is sent
/// </summary>
- public readonly int TcpKeepAliveTime { get; init; }
+ public required readonly int TcpKeepAliveTime { get; init; }
/// <summary>
/// If TCP keepalive is enabled, the amount of time the connection will wait for a keepalive message
/// </summary>
- public readonly int KeepaliveInterval { get; init; }
+ public required readonly int KeepaliveInterval { get; init; }
/// <summary>
/// Enables TCP keepalive
/// </summary>
- public readonly bool TcpKeepalive { get; init; }
+ public required readonly bool TcpKeepalive { get; init; }
/// <summary>
/// The maximum number of waiting WSA asynchronous socket accept operations
/// </summary>
- public readonly uint AcceptThreads { get; init; }
+ public required readonly uint AcceptThreads { get; init; }
/// <summary>
/// The maximum size (in bytes) the transport will buffer in
/// the receiving pipeline.
/// </summary>
- public readonly int MaxRecvBufferData { get; init; }
+ public required readonly int MaxRecvBufferData { get; init; }
/// <summary>
/// The maximum number of allowed socket connections to this server
/// </summary>
- public readonly long MaxConnections { get; init; }
+ public required readonly long MaxConnections { get; init; }
/// <summary>
/// The listener socket backlog count
/// </summary>
- public readonly int BackLog { get; init; }
+ public required readonly int BackLog { get; init; }
/// <summary>
/// The <see cref="MemoryPool{T}"/> to allocate transport buffers from
/// </summary>
- public readonly MemoryPool<byte> BufferPool { get; init; }
+ public required readonly MemoryPool<byte> BufferPool { get; init; }
/// <summary>
/// <para>
/// The maxium number of event objects that will be cached
@@ -86,16 +86,16 @@ namespace VNLib.Net.Transport.Tcp
/// WARNING: Setting this value too low will cause significant CPU overhead and GC load
/// </para>
/// </summary>
- public readonly int CacheQuota { get; init; }
+ public required readonly int CacheQuota { get; init; }
/// <summary>
/// An optional callback invoked after the socket has been created
/// for optional appliction specific socket configuration
/// </summary>
- public readonly Action<Socket>? OnSocketCreated { get; init; }
+ public required readonly Action<Socket>? OnSocketCreated { get; init; }
/// <summary>
/// Enables verbose logging of TCP operations using the <see cref="LogLevel.Verbose"/>
/// level
/// </summary>
- public readonly bool DebugTcpLog { get; init; }
+ public required readonly bool DebugTcpLog { get; init; }
}
} \ No newline at end of file
diff --git a/lib/Net.Transport.SimpleTCP/src/TcpServer.cs b/lib/Net.Transport.SimpleTCP/src/TcpServer.cs
index ad8987e..5a82298 100644
--- a/lib/Net.Transport.SimpleTCP/src/TcpServer.cs
+++ b/lib/Net.Transport.SimpleTCP/src/TcpServer.cs
@@ -66,19 +66,14 @@ namespace VNLib.Net.Transport.Tcp
if (pipeOptions == null)
{
//Pool is required when using default pipe options
- _ = config.BufferPool ?? throw new ArgumentException("Buffer pool argument cannot be null");
+ ArgumentNullException.ThrowIfNull(config.BufferPool);
}
- _ = config.Log ?? throw new ArgumentException("Log argument is required");
+ ArgumentNullException.ThrowIfNull(config.Log, nameof(config.Log));
+
+ ArgumentOutOfRangeException.ThrowIfLessThan(config.MaxRecvBufferData, 4096);
+ ArgumentOutOfRangeException.ThrowIfLessThan(config.AcceptThreads, 1u);
- if (config.MaxRecvBufferData < 4096)
- {
- throw new ArgumentException("MaxRecvBufferData size must be at least 4096 bytes to avoid data pipeline pefromance issues");
- }
- if (config.AcceptThreads < 1)
- {
- throw new ArgumentException("Accept thread count must be greater than 0");
- }
if (config.AcceptThreads > Environment.ProcessorCount)
{
config.Log.Debug("Suggestion: Setting accept threads to {pc}", Environment.ProcessorCount);
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs
index 3ae2183..3240330 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpServiceStackBuilder.cs
@@ -28,16 +28,18 @@ using System.Collections.Generic;
using VNLib.Net.Http;
using VNLib.Plugins.Runtime;
-
+using VNLib.Plugins.Essentials.ServiceStack.Plugins;
namespace VNLib.Plugins.Essentials.ServiceStack.Construction
{
+
/// <summary>
/// A data structure used to build/create a <see cref="HttpServiceStack"/>
/// around a <see cref="ServiceDomain"/>
/// </summary>
public sealed class HttpServiceStackBuilder
{
+ private readonly ServiceBuilder _serviceBuilder = new();
/// <summary>
/// Initializes a new <see cref="HttpServiceStack"/> that will
@@ -47,8 +49,10 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
public HttpServiceStackBuilder()
{ }
- private Action<ICollection<IServiceHost>>? _hostBuilder;
- private Func<ServiceGroup, IHttpServer>? _getServers;
+ internal ServiceBuilder ServiceBuilder => _serviceBuilder;
+
+ private Action<ServiceBuilder>? _hostBuilder;
+ private Func<IReadOnlyCollection<ServiceGroup>, IHttpServer[]>? _getServers;
private Func<IPluginStack>? _getPlugins;
private IManualPlugin[]? manualPlugins;
private bool loadConcurrently;
@@ -59,7 +63,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
/// </summary>
/// <param name="hostBuilder">The callback method to build virtual hosts</param>
/// <returns>The current instance for chaining</returns>
- public HttpServiceStackBuilder WithDomain(Action<ICollection<IServiceHost>> hostBuilder)
+ public HttpServiceStackBuilder WithDomain(Action<ServiceBuilder> hostBuilder)
{
_hostBuilder = hostBuilder;
return this;
@@ -70,7 +74,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
/// </summary>
/// <param name="getServers">A callback method that gets the http server implementation for the service group</param>
/// <returns>The current instance for chaining</returns>
- public HttpServiceStackBuilder WithHttp(Func<ServiceGroup, IHttpServer> getServers)
+ public HttpServiceStackBuilder WithHttp(Func<IReadOnlyCollection<ServiceGroup>, IHttpServer[]> getServers)
{
_getServers = getServers;
return this;
@@ -90,24 +94,34 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
/// <summary>
/// Configures the stack to use the built-in http server implementation
/// </summary>
- /// <param name="transport">The transport builder callback function</param>
+ /// <param name="getTransports">The transport builder callback function</param>
/// <param name="config">The http configuration structure used to initalize servers</param>
/// <returns>The current instance for chaining</returns>
- public HttpServiceStackBuilder WithBuiltInHttp(Func<ServiceGroup, ITransportProvider> transport, HttpConfig config)
- {
- return WithBuiltInHttp(transport, sg => config);
- }
+ public HttpServiceStackBuilder WithBuiltInHttp(Func<IReadOnlyCollection<ServiceGroup>, HttpTransportMapping[]> getTransports, HttpConfig config)
+ => WithBuiltInHttp(getTransports, _ => config);
/// <summary>
/// Configures the stack to use the built-in http server implementation
/// </summary>
- /// <param name="transport">The transport builder callback function</param>
+ /// <param name="getBindings">A callback function that gets transport bindings for servie groups</param>
/// <param name="configCallback">The http configuration builder callback method</param>
/// <returns>The current instance for chaining</returns>
- public HttpServiceStackBuilder WithBuiltInHttp(Func<ServiceGroup, ITransportProvider> transport, Func<ServiceGroup, HttpConfig> configCallback)
- {
- return WithHttp(sg => new HttpServer(configCallback(sg), transport(sg), sg.Hosts.Select(static p => p.Processor)));
- }
+ public HttpServiceStackBuilder WithBuiltInHttp(
+ Func<IReadOnlyCollection<ServiceGroup>, HttpTransportMapping[]> getBindings,
+ Func<IReadOnlyCollection<ServiceGroup>, HttpConfig> configCallback
+ ) => WithHttp((sgs) => {
+
+ HttpTransportBinding[] vhBindings = getBindings(sgs)
+ .Select(s =>
+ {
+ IEnumerable<IWebRoot> procs = s.Hosts.Select(static s => s.Processor);
+ return new HttpTransportBinding(s.Transport, procs);
+ })
+ .ToArray();
+
+ // A single built-in http server can service an entire domain
+ return [ new HttpServer(configCallback(sgs), vhBindings) ];
+ });
/// <summary>
/// Adds a collection of manual plugin instances to the stack. Every call
@@ -139,27 +153,27 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
/// <exception cref="ArgumentNullException"></exception>
public HttpServiceStack Build()
{
- _ = _hostBuilder ?? throw new ArgumentNullException("WithDomainBuilder", "You have not configured a service domain configuration callback");
_ = _getServers ?? throw new ArgumentNullException("WithHttp", "You have not configured a IHttpServer configuration callback");
+ //Host builder callback is optional
+ _hostBuilder?.Invoke(ServiceBuilder);
+
//Inint the service domain
ServiceDomain sd = new();
- if (!sd.BuildDomain(_hostBuilder))
- {
- throw new ArgumentException("Failed to configure the service domain, you must expose at least one service host");
- }
+ sd.BuildDomain(_serviceBuilder);
- LinkedList<IHttpServer> servers = new();
+ //Get http servers from the user callback for the service domain, let the caller decide how to route them
+ IHttpServer[] servers = _getServers.Invoke(sd.ServiceGroups);
- //enumerate hosts groups
- foreach (ServiceGroup hosts in sd.ServiceGroups)
+ if (servers.Length == 0)
{
- //Create new server
- IHttpServer server = _getServers.Invoke(hosts);
+ throw new ArgumentException("No service hosts were configured. You must define at least one virtual host for the domain");
+ }
- //Add server to internal list
- servers.AddLast(server);
+ if(servers.Any(servers => servers is null))
+ {
+ throw new ArgumentException("One or more servers were not initialized correctly. Check the server configuration callback");
}
return new(servers, sd, GetPluginStack(sd));
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IHostTransportInfo.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpTransportMapping.cs
index 5c663a9..c5fae41 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IHostTransportInfo.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/HttpTransportMapping.cs
@@ -1,12 +1,12 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
-* File: IHostTransportInfo.cs
+* File: HttpTransportMapping.cs
*
-* IHostTransportInfo.cs is part of VNLib.Plugins.Essentials.ServiceStack which is part of the larger
-* VNLib collection of libraries and utilities.
+* HttpTransportMapping.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
@@ -22,26 +22,16 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
-using System.Net;
-using System.Security.Cryptography.X509Certificates;
+using System.Collections.Generic;
-namespace VNLib.Plugins.Essentials.ServiceStack
+using VNLib.Net.Http;
+
+namespace VNLib.Plugins.Essentials.ServiceStack.Construction
{
/// <summary>
- /// Represents the service host's network/transport
- /// information including the optional certificate and
- /// the endpoint to listen on
+ /// Represents a mapping of service hosts to a transport provider
/// </summary>
- public interface IHostTransportInfo
- {
- /// <summary>
- /// Optional TLS certificate to use
- /// </summary>
- X509Certificate? Certificate { get; }
-
- /// <summary>
- /// The endpoint to listen on
- /// </summary>
- IPEndPoint TransportEndpoint { get; }
- }
+ /// <param name="Hosts">The collection of service hosts to map to transports</param>
+ /// <param name="Transport">The transport that will provide networking to the host collection</param>
+ public record class HttpTransportMapping(IEnumerable<IServiceHost> Hosts, ITransportProvider Transport);
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/IDomainBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/IDomainBuilder.cs
index 19d2a96..9bccf54 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/Construction/IDomainBuilder.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/IDomainBuilder.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -22,9 +22,7 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
-using System.IO;
-
-using VNLib.Utils.Logging;
+using System;
namespace VNLib.Plugins.Essentials.ServiceStack.Construction
{
@@ -34,19 +32,17 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
public interface IDomainBuilder
{
/// <summary>
- /// Adds a single virtual host to the domain that must be configured.
+ /// Allows for defining a new virtual host for the domain by manually configuring it.
/// </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);
+ /// <param name="builder">A callback function that passes the new host builder</param>
+ /// <returns>The current instance</returns>
+ IDomainBuilder WithServiceGroups(Action<IServiceGroupBuilder> builder);
/// <summary>
- /// Adds a single pre-configured virtual host to the domain
+ /// Adds a collection of hosts to the domain
/// </summary>
- /// <param name="config">The pre-configured virtual host configuration</param>
- /// <returns>The current instance</returns>
- IDomainBuilder WithVirtualHost(VirtualHostConfiguration config);
+ /// <param name="host"></param>
+ /// <returns></returns>
+ IDomainBuilder WithHosts(IServiceHost[] host);
}
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/IServiceGroupBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/IServiceGroupBuilder.cs
new file mode 100644
index 0000000..1ffeb5c
--- /dev/null
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/IServiceGroupBuilder.cs
@@ -0,0 +1,60 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Essentials.ServiceStack
+* File: IServiceGroupBuilder.cs
+*
+* IServiceGroupBuilder.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 VNLib.Utils.Logging;
+
+namespace VNLib.Plugins.Essentials.ServiceStack.Construction
+{
+ /// <summary>
+ /// Allows for defining service groups for the service stack to manage
+ /// </summary>
+ public interface IServiceGroupBuilder
+ {
+ /// <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>
+ /// Allows for defining a new virtual host for the domain by manually configuring it.
+ /// </summary>
+ /// <param name="builder">A callback function that passes the new host builder</param>
+ /// <returns>The current instance</returns>
+ IServiceGroupBuilder WithVirtualHost(Action<IVirtualHostBuilder> builder);
+
+ /// <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>
+ IServiceGroupBuilder WithVirtualHost(VirtualHostConfiguration config, object? userState);
+ }
+}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/ServiceBuilder.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/ServiceBuilder.cs
new file mode 100644
index 0000000..2464d9b
--- /dev/null
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/ServiceBuilder.cs
@@ -0,0 +1,79 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Plugins.Essentials.ServiceStack
+* File: HttpServiceStackBuilder.cs
+*
+* HttpServiceStackBuilder.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.Linq;
+using System.Collections.Generic;
+
+
+namespace VNLib.Plugins.Essentials.ServiceStack.Construction
+{
+ /// <summary>
+ /// A data structure used to build groupings of service hosts used
+ /// to configure http servers.
+ /// </summary>
+ public sealed class ServiceBuilder
+ {
+ private readonly List<Action<ICollection<IServiceHost>>> _callbacks = [];
+
+ /// <summary>
+ /// Adds callback function that will add a collection of service hosts
+ /// and passes a state paramter to the callback
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="state">The optional state parameter</param>
+ /// <param name="host">The host collection to add new service hosts to</param>
+ /// <returns>The current instance for chaining</returns>
+ public ServiceBuilder AddHostCollection<T>(T state, Action<ICollection<IServiceHost>, T> host)
+ => AddHostCollection(col => host.Invoke(col, state));
+
+ /// <summary>
+ /// Adds a callback function that will add a collection of service hosts
+ /// </summary>
+ /// <param name="host">The callback function to return the collection of hosts</param>
+ /// <returns>The current instance for chaining</returns>
+ public ServiceBuilder AddHostCollection(Action<ICollection<IServiceHost>> host)
+ {
+ _callbacks.Add(host);
+ return this;
+ }
+
+ /// <summary>
+ /// Builds the <see cref="ServiceGroup"/> collection from the user
+ /// defined service host arrays
+ /// </summary>
+ /// <returns>The numeration that builds the service groups</returns>
+ internal IEnumerable<ServiceGroup> BuildGroups()
+ {
+ return _callbacks.Select(static cb =>
+ {
+ LinkedList<IServiceHost> hosts = new();
+
+ cb.Invoke(hosts);
+
+ return new ServiceGroup(hosts);
+ });
+ }
+ }
+}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs
index 4195553..62b5070 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/SsBuilderExtensions.cs
@@ -29,12 +29,12 @@ 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.Middleware;
+using VNLib.Plugins.Essentials.ServiceStack.Plugins;
namespace VNLib.Plugins.Essentials.ServiceStack.Construction
{
@@ -46,14 +46,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
{
/// <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, vhc => FromVirtualHostConfig(vhc.Clone()));
-
- /// <summary>
/// Creates a new <see cref="IDomainBuilder"/> instance to define your
/// virtual hosts with the supplied callback method
/// </summary>
@@ -62,167 +54,144 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
/// <returns>The service stack builder instance</returns>
public static HttpServiceStackBuilder WithDomain(this HttpServiceStackBuilder stack, Action<IDomainBuilder> domainBuilder)
{
- domainBuilder(stack.WithDomain());
+ domainBuilder(WithDomain(stack));
return stack;
}
/// <summary>
- /// Creates a new <see cref="IDomainBuilder"/> with your custom <see cref="EventProcessor"/> type
- /// that will be wrapped for runtime processing.
+ /// Creates a new <see cref="IDomainBuilder"/> instance to define your
+ /// virtual hosts using a built-in event processor type
/// </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, IRuntimeServiceInjection
- {
- 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;
- }
-
- /// <summary>
- /// Adds multiple <see cref="IHttpMiddleware"/> instances to the virtual host
- /// </summary>
- /// <param name="vhBuilder"></param>
- /// <param name="middleware">The array of middleware instances to add to the collection</param>
- /// <returns></returns>
- public static IVirtualHostBuilder WithMiddleware(this IVirtualHostBuilder vhBuilder, params IHttpMiddleware[] middleware)
- {
- vhBuilder.WithOption(c => Array.ForEach(middleware, m => c.CustomMiddleware.Add(m)));
- 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;
- }
+ /// <returns>The <see cref="IDomainBuilder"/> used to define your service domain</returns>
+ public static IDomainBuilder WithDomain(this HttpServiceStackBuilder stack)
+ => new DomainBuilder(stack.ServiceBuilder);
- 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)
+ private sealed class DomainBuilder(ServiceBuilder svcBuilder) : IDomainBuilder
{
- return vhBuidler.WithDefaultFiles((IReadOnlyCollection<string>)defaultFiles);
- }
-
- public static IVirtualHostBuilder WithDefaultFiles(this IVirtualHostBuilder vhBuidler, IReadOnlyCollection<string> defaultFiles)
- {
- vhBuidler.WithOption(c => c.DefaultFiles = defaultFiles);
- return vhBuidler;
- }
+ ///<inheritdoc/>
+ public IDomainBuilder WithServiceGroups(Action<IServiceGroupBuilder> builder)
+ {
+ svcBuilder.AddHostCollection((col) =>
+ {
+ SvGroupBuilder group = new();
- public static IVirtualHostBuilder WithExcludedExtensions(this IVirtualHostBuilder vhBuilder, params string[] excludedExtensions)
- {
- return vhBuilder.WithExcludedExtensions(new HashSet<string>(excludedExtensions));
- }
+ builder(group);
- public static IVirtualHostBuilder WithExcludedExtensions(this IVirtualHostBuilder vhBuilder, IReadOnlySet<string> excludedExtensions)
- {
- vhBuilder.WithOption(c => c.ExcludedExtensions = excludedExtensions);
- return vhBuilder;
- }
+ group.Configs
+ .SelectMany(static vc => FromVirtualHostConfig(vc)
+ .Select(vh => new CustomServiceHost<BasicVirtualHost>(vh, vc.UserState)
+ ))
+ .ForEach(col.Add); //Force enumeration
+ });
- 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;
- }
+ return this;
+ }
- public static IVirtualHostBuilder WithDownstreamServers(this IVirtualHostBuilder vhBuilder, IReadOnlySet<IPAddress> addresses)
- {
- vhBuilder.WithOption(c => c.DownStreamServers = addresses);
- return vhBuilder;
- }
+ ///<inheritdoc/>
+ public IDomainBuilder WithHosts(IServiceHost[] hosts)
+ {
+ svcBuilder.AddHostCollection(col => Array.ForEach(hosts, col.Add));
+ return this;
+ }
- /// <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 IEnumerable<BasicVirtualHost> FromVirtualHostConfig(VirtualHostConfiguration configuration)
+ {
+ /*
+ * Configurations are allowed to define multiple hostnames for a single
+ * virtual host.
+ */
- 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.
- */
+ return configuration.Hostnames
+ .Select<string, BasicVirtualHost>((string hostname) =>
+ {
+ /*
+ * 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(
+ Directory: configuration.RootDir.FullName,
+ Hostname: hostname,
+ Log: configuration.LogProvider,
+ Options: configuration
+ )
+ {
+ AllowedAttributes = configuration.AllowedAttributes,
+ DissallowedAttributes = configuration.DissallowedAttributes,
+ DefaultFiles = configuration.DefaultFiles,
+ ExecutionTimeout = configuration.ExecutionTimeout,
+ FilePathCacheMaxAge = configuration.FilePathCacheMaxAge,
+
+ //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);
+ });
+ }
- EventProcessorConfig conf = new(
- configuration.RootDir.FullName,
- configuration.Hostname,
- configuration.LogProvider,
- configuration)
+ private sealed record class SvGroupBuilder : IServiceGroupBuilder
{
- AllowedAttributes = configuration.AllowedAttributes,
- DissallowedAttributes = configuration.DissallowedAttributes,
- DefaultFiles = configuration.DefaultFiles,
- ExecutionTimeout = configuration.ExecutionTimeout,
+ internal readonly List<VirtualHostConfiguration> Configs = new();
- //Frozen sets are required for the event processor, for performance reasons
- DownStreamServers = configuration.DownStreamServers.ToFrozenSet(),
- ExcludedExtensions = configuration.ExcludedExtensions.ToFrozenSet(),
- };
+ ///<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);
+ }
- //Add all pre-configured middleware to the chain
- configuration.CustomMiddleware.ForEach(conf.MiddlewareChain.Add);
+ ///<inheritdoc/>
+ public IServiceGroupBuilder WithVirtualHost(Action<IVirtualHostBuilder> builder)
+ {
+ //Create new config instance and add to list
+ VirtualHostConfiguration config = new()
+ {
+ RootDir = null!,
+ LogProvider = null!
+ };
+
+ //Pass the builder to the callback
+ builder(new VHostBuilder(config));
+
+ return WithVirtualHost(config, null);
+ }
- return new(configuration.EventHooks, conf);
- }
+ ///<inheritdoc/>
+ public IServiceGroupBuilder WithVirtualHost(VirtualHostConfiguration config, object? userState)
+ {
+ config.UserState = userState;
+ Configs.Add(config);
+ return this;
+ }
+ private sealed record class VHostBuilder(VirtualHostConfiguration Config) : IVirtualHostBuilder
+ {
+ ///<inheritdoc/>
+ public IVirtualHostBuilder WithOption(Action<VirtualHostConfiguration> configCallback)
+ {
+ configCallback(Config);
+ return this;
+ }
+ }
+ }
+ }
- 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)
{
@@ -232,48 +201,21 @@ 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)
- {
- //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;
- }
+ /*
+ * The goal of this class is to added the extra service injection
+ * and manage the IWebRoot instance that will be served by a
+ * webserver
+ */
- private sealed record class VHostBuilder(VirtualHostConfiguration Config) : IVirtualHostBuilder
- {
- ///<inheritdoc/>
- public IVirtualHostBuilder WithOption(Action<VirtualHostConfiguration> configCallback)
- {
- configCallback(Config);
- return this;
- }
- }
- }
-
- private sealed class CustomServiceHost<T>(IHostTransportInfo Config, T Instance) : IServiceHost
+ private sealed class CustomServiceHost<T>(T Instance, object? userState) : IServiceHost
where T : EventProcessor, IRuntimeServiceInjection
{
///<inheritdoc/>
public IWebRoot Processor => Instance;
///<inheritdoc/>
- public IHostTransportInfo TransportInfo => Config;
+ public object? UserState => userState;
///<inheritdoc/>
void IServiceHost.OnRuntimeServiceAttach(IManagedPlugin plugin, IEndpoint[] endpoints)
@@ -300,10 +242,12 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
plugin.OnPluginServiceEvent<IEnumerable<IHttpMiddleware>>(p => p.ForEach(Instance.Options.MiddlewareChain.Remove));
plugin.OnPluginServiceEvent<IHttpMiddleware[]>(p => p.ForEach(Instance.Options.MiddlewareChain.Remove));
}
+
}
- private sealed class BasicVirtualHost(IVirtualHostHooks Hooks, EventProcessorConfig config) : EventProcessor(config), IRuntimeServiceInjection
+ private sealed class BasicVirtualHost(IVirtualHostHooks Hooks, EventProcessorConfig config)
+ : EventProcessor(config), IRuntimeServiceInjection
{
/*
* Runtime service injection can be tricky, at least in my architecture. If all we have
@@ -315,16 +259,20 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
private readonly ConditionalWeakTable<IServiceProvider, Type[]> _exposedTypes = new();
///<inheritdoc/>
- public override bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity) => Hooks.ErrorHandler(errorCode, entity);
+ public override bool ErrorHandler(HttpStatusCode errorCode, IHttpEvent entity)
+ => Hooks.ErrorHandler(errorCode, entity);
///<inheritdoc/>
- public override void PreProcessEntity(HttpEntity entity, out FileProcessArgs preProcArgs) => Hooks.PreProcessEntityAsync(entity, out preProcArgs);
+ public override void PreProcessEntity(HttpEntity entity, out FileProcessArgs preProcArgs)
+ => Hooks.PreProcessEntityAsync(entity, out preProcArgs);
///<inheritdoc/>
- public override void PostProcessEntity(HttpEntity entity, ref FileProcessArgs chosenRoutine) => Hooks.PostProcessFile(entity, ref chosenRoutine);
+ public override void PostProcessEntity(HttpEntity entity, ref FileProcessArgs chosenRoutine)
+ => Hooks.PostProcessFile(entity, ref chosenRoutine);
///<inheritdoc/>
- public override string TranslateResourcePath(string requestPath) => Hooks.TranslateResourcePath(requestPath);
+ public override string TranslateResourcePath(string requestPath)
+ => Hooks.TranslateResourcePath(requestPath);
///<inheritdoc/>
public void AddServices(IServiceProvider services)
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/VirtualHostConfiguration.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/VirtualHostConfiguration.cs
index 97ad905..20e346e 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/Construction/VirtualHostConfiguration.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/VirtualHostConfiguration.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -26,7 +26,6 @@ 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;
@@ -36,34 +35,28 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
/// <summary>
/// A virtual host configuration container
/// </summary>
- public class VirtualHostConfiguration : IHostTransportInfo, IEpProcessingOptions
+ public class VirtualHostConfiguration : 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>
+ /// Optional user state object
/// </summary>
- public string Hostname { get; set; } = "*";
+ internal object? UserState { get; set; }
/// <summary>
- /// The transport endpoint that this virtual host will listen on
- /// <para>Default: 0.0.0.0:80</para>
+ /// The directory that this virtual host will serve files from
/// </summary>
- public IPEndPoint TransportEndpoint { get; set; } = new IPEndPoint(IPAddress.Any, 80);
+ public required DirectoryInfo RootDir { get; set; }
/// <summary>
- /// An optional certificate to use for TLS connections
+ /// The hostname, or domain name, that this virtual host will respond to
+ /// <para>Default: *</para>
/// </summary>
- public X509Certificate? Certificate { get; set; }
+ public string[] Hostnames { get; set; } = [ "*" ];
/// <summary>
/// A log provider to use for this virtual host
/// </summary>
- public ILogProvider LogProvider { get; set; } = null!;
+ public required ILogProvider LogProvider { get; set; }
/// <summary>
/// The name of a default file to search for within a directory if no file is specified (index.html).
@@ -112,6 +105,12 @@ namespace VNLib.Plugins.Essentials.ServiceStack.Construction
/// </summary>
public ICollection<IHttpMiddleware> CustomMiddleware { get; } = new List<IHttpMiddleware>();
+ /// <summary>
+ /// A <see cref="TimeSpan"/> for how long a file path may be cached before being revalidated. Setting to
+ /// zero will disable path caching
+ /// </summary>
+ public TimeSpan FilePathCacheMaxAge { get; set; } = TimeSpan.Zero;
+
internal VirtualHostConfiguration Clone() => (VirtualHostConfiguration)MemberwiseClone();
}
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/Construction/VirtualHostConstructionExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/Construction/VirtualHostConstructionExtensions.cs
new file mode 100644
index 0000000..acf1b53
--- /dev/null
+++ b/lib/Plugins.Essentials.ServiceStack/src/Construction/VirtualHostConstructionExtensions.cs
@@ -0,0 +1,107 @@
+/*
+* Copyright (c) 2024 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.Collections.Generic;
+
+using VNLib.Utils.Logging;
+using VNLib.Plugins.Essentials.Middleware;
+
+namespace VNLib.Plugins.Essentials.ServiceStack.Construction
+{
+ public static class VirtualHostConstructionExtensions
+ {
+ /// <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));
+
+ /// <summary>
+ /// Adds multiple <see cref="IHttpMiddleware"/> instances to the virtual host
+ /// </summary>
+ /// <param name="vhBuilder"></param>
+ /// <param name="middleware">The array of middleware instances to add to the collection</param>
+ /// <returns></returns>
+ public static IVirtualHostBuilder WithMiddleware(this IVirtualHostBuilder vhBuilder, params IHttpMiddleware[] middleware)
+ => vhBuilder.WithOption(c => Array.ForEach(middleware, m => c.CustomMiddleware.Add(m)));
+
+
+ /// <summary>
+ /// Takes a callback to allow you to inject middelware applications into
+ /// your virtual host
+ /// </summary>
+ /// <param name="vhBuilder"></param>
+ /// <param name="middleware">The array of middleware instances to add to the collection</param>
+ /// <returns></returns>
+ public static IVirtualHostBuilder WithMiddleware(this IVirtualHostBuilder vhBuilder, Action<ICollection<IHttpMiddleware>> middleware)
+ => vhBuilder.WithOption(c => middleware.Invoke(c.CustomMiddleware));
+
+ public static IVirtualHostBuilder WithLogger(this IVirtualHostBuilder vhBuilder, ILogProvider logger)
+ => vhBuilder.WithOption(c => c.LogProvider = logger);
+
+ public static IVirtualHostBuilder WithHostnames(this IVirtualHostBuilder virtualHostBuilder, string[] hostnames)
+ => virtualHostBuilder.WithOption(c => c.Hostnames = hostnames);
+
+ public static IVirtualHostBuilder WithDefaultFiles(this IVirtualHostBuilder vhBuidler, params string[] defaultFiles)
+ => vhBuidler.WithDefaultFiles((IReadOnlyCollection<string>)defaultFiles);
+
+ public static IVirtualHostBuilder WithDefaultFiles(this IVirtualHostBuilder vhBuidler, IReadOnlyCollection<string> defaultFiles)
+ => vhBuidler.WithOption(c => c.DefaultFiles = defaultFiles);
+
+ public static IVirtualHostBuilder WithExcludedExtensions(this IVirtualHostBuilder vhBuilder, params string[] excludedExtensions)
+ => vhBuilder.WithExcludedExtensions(new HashSet<string>(excludedExtensions));
+
+ public static IVirtualHostBuilder WithExcludedExtensions(this IVirtualHostBuilder vhBuilder, IReadOnlySet<string> excludedExtensions)
+ => vhBuilder.WithOption(c => c.ExcludedExtensions = excludedExtensions);
+
+ public static IVirtualHostBuilder WithAllowedAttributes(this IVirtualHostBuilder vhBuilder, FileAttributes attributes)
+ => vhBuilder.WithOption(c => c.AllowedAttributes = attributes);
+
+ public static IVirtualHostBuilder WithDisallowedAttributes(this IVirtualHostBuilder vhBuilder, FileAttributes attributes)
+ => vhBuilder.WithOption(c => c.DissallowedAttributes = attributes);
+
+ public static IVirtualHostBuilder WithDownstreamServers(this IVirtualHostBuilder vhBuilder, IReadOnlySet<IPAddress> addresses)
+ => vhBuilder.WithOption(c => c.DownStreamServers = addresses);
+
+ public static IVirtualHostBuilder WithFilePathCache(this IVirtualHostBuilder vhBuilder, TimeSpan maxAge = default)
+ => vhBuilder.WithOption(c => c.FilePathCacheMaxAge = maxAge);
+
+ /// <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));
+ }
+}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs
index 71db38f..2f1bb0b 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/HttpServiceStack.cs
@@ -30,6 +30,7 @@ using System.Collections.Generic;
using VNLib.Utils;
using VNLib.Net.Http;
using VNLib.Utils.Logging;
+using VNLib.Plugins.Essentials.ServiceStack.Plugins;
namespace VNLib.Plugins.Essentials.ServiceStack
{
@@ -39,7 +40,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// </summary>
public sealed class HttpServiceStack : VnDisposeable
{
- private readonly LinkedList<IHttpServer> _servers;
+ private readonly IReadOnlyCollection<IHttpServer> _servers;
private readonly ServiceDomain _serviceDomain;
private readonly PluginManager _plugins;
@@ -49,7 +50,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// <summary>
/// A collection of all loaded servers
/// </summary>
- public IReadOnlyCollection<IHttpServer> Servers => _servers;
+ public IEnumerable<IHttpServer> Servers => _servers;
/// <summary>
/// Gets the internal <see cref="IHttpPluginManager"/> that manages plugins for the entire
@@ -62,7 +63,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// generate servers to listen for services exposed by the
/// specified host context
/// </summary>
- internal HttpServiceStack(LinkedList<IHttpServer> servers, ServiceDomain serviceDomain, IPluginInitializer plugins)
+ internal HttpServiceStack(IReadOnlyCollection<IHttpServer> servers, ServiceDomain serviceDomain, IPluginInitializer plugins)
{
_servers = servers;
_serviceDomain = serviceDomain;
@@ -130,9 +131,6 @@ namespace VNLib.Plugins.Essentials.ServiceStack
_cts?.Dispose();
_plugins.Dispose();
-
- //remove all lists
- _servers.Clear();
}
}
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IServiceHost.cs b/lib/Plugins.Essentials.ServiceStack/src/IServiceHost.cs
index 2517d66..2b5dc64 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IServiceHost.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/IServiceHost.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -23,6 +23,7 @@
*/
using VNLib.Net.Http;
+using VNLib.Plugins.Essentials.ServiceStack.Plugins;
namespace VNLib.Plugins.Essentials.ServiceStack
{
@@ -40,9 +41,9 @@ namespace VNLib.Plugins.Essentials.ServiceStack
IWebRoot Processor { get; }
/// <summary>
- /// The host's transport information
+ /// Optional user state to be set during initialization and read at a later time
/// </summary>
- IHostTransportInfo TransportInfo { get; }
+ object? UserState { get; }
/// <summary>
/// Called when a plugin is loaded and is endpoints are extracted
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IHttpPluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IHttpPluginManager.cs
index 6cece1f..23e436b 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IHttpPluginManager.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IHttpPluginManager.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -25,7 +25,7 @@
using System;
using System.Collections.Generic;
-namespace VNLib.Plugins.Essentials.ServiceStack
+namespace VNLib.Plugins.Essentials.ServiceStack.Plugins
{
/// <summary>
/// Represents a live plugin controller that manages all
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManagedPlugin.cs
index 8332f7e..b2506c5 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IManagedPlugin.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManagedPlugin.cs
@@ -25,7 +25,7 @@
using System;
using System.ComponentModel.Design;
-namespace VNLib.Plugins.Essentials.ServiceStack
+namespace VNLib.Plugins.Essentials.ServiceStack.Plugins
{
/// <summary>
/// Represents a plugin managed by a <see cref="IHttpPluginManager"/> that includes dynamically loaded plugins
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IManualPlugin.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManualPlugin.cs
index cecd481..57fd631 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IManualPlugin.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IManualPlugin.cs
@@ -25,7 +25,7 @@
using System;
using System.ComponentModel.Design;
-namespace VNLib.Plugins.Essentials.ServiceStack
+namespace VNLib.Plugins.Essentials.ServiceStack.Plugins
{
/// <summary>
/// Represents a plugin that may be added to a service stack in user-code
diff --git a/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IPluginInitializer.cs
index a9aa103..9435d9b 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/IPluginInitializer.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/IPluginInitializer.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Plugins.Essentials.ServiceStack
@@ -25,7 +25,7 @@
using VNLib.Utils.Logging;
-namespace VNLib.Plugins.Essentials.ServiceStack
+namespace VNLib.Plugins.Essentials.ServiceStack.Plugins
{
internal interface IPluginInitializer
{
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginExtensions.cs
index 52377ee..4d04c54 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginExtensions.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginExtensions.cs
@@ -27,7 +27,7 @@ using System.Collections.Generic;
using VNLib.Plugins.Essentials.Runtime;
-namespace VNLib.Plugins.Essentials.ServiceStack
+namespace VNLib.Plugins.Essentials.ServiceStack.Plugins
{
/// <summary>
/// Internal and service stack specific extensions for plugins
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginManager.cs
index f43da78..ce80a9e 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginManager.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginManager.cs
@@ -29,7 +29,7 @@ using System.Collections.Generic;
using VNLib.Utils;
using VNLib.Utils.Logging;
-namespace VNLib.Plugins.Essentials.ServiceStack
+namespace VNLib.Plugins.Essentials.ServiceStack.Plugins
{
/// <summary>
@@ -70,9 +70,9 @@ namespace VNLib.Plugins.Essentials.ServiceStack
{
Check();
- foreach(IManagedPlugin plugin in _loadedPlugins)
+ foreach (IManagedPlugin plugin in _loadedPlugins)
{
- if(plugin.SendCommandToPlugin(pluginName, message, nameComparison))
+ if (plugin.SendCommandToPlugin(pluginName, message, nameComparison))
{
return true;
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginRutimeEventHandler.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginRutimeEventHandler.cs
index f892823..c5b094f 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginRutimeEventHandler.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginRutimeEventHandler.cs
@@ -28,7 +28,7 @@ using System.Linq;
using VNLib.Utils.Extensions;
using VNLib.Plugins.Runtime;
-namespace VNLib.Plugins.Essentials.ServiceStack
+namespace VNLib.Plugins.Essentials.ServiceStack.Plugins
{
internal sealed class PluginRutimeEventHandler(ServiceDomain Domain) : IPluginEventListener
{
diff --git a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginStackInitializer.cs
index 5f4e6e0..fb685f7 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/PluginStackInitializer.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/Plugins/PluginStackInitializer.cs
@@ -36,15 +36,15 @@ using VNLib.Plugins.Runtime;
using VNLib.Utils.Extensions;
using VNLib.Plugins.Runtime.Services;
-namespace VNLib.Plugins.Essentials.ServiceStack
+namespace VNLib.Plugins.Essentials.ServiceStack.Plugins
{
- internal sealed class PluginStackInitializer(PluginRutimeEventHandler 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();
private readonly LinkedList<ManualPluginWrapper> _manualPlugins = new();
-
+
private void PrepareStack()
{
/*
@@ -79,7 +79,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
//Combine all managed plugins and initialize them individually
IEnumerable<IManagedPlugin> plugins = _managedPlugins.Union(_manualPlugins);
- foreach(IManagedPlugin p in plugins)
+ foreach (IManagedPlugin p in plugins)
{
//Try init plugin and add it to the list of loaded plugins
if (InitializePluginCore(p, debugLog))
@@ -153,7 +153,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
debugLog.Warn("No plugin instances were exposed via {asm} assembly. This may be due to an assebmly mismatch", plugin.ToString());
}
}
- else if(plugin is ManualPluginWrapper mpw)
+ else if (plugin is ManualPluginWrapper mpw)
{
//Initialzie plugin wrapper
mpw.Plugin.Initialize();
@@ -312,8 +312,8 @@ namespace VNLib.Plugins.Essentials.ServiceStack
return false;
}
-
- void IManagedPlugin.OnPluginLoaded()
+
+ void IManagedPlugin.OnPluginLoaded()
{ }
void IManagedPlugin.OnPluginUnloaded()
diff --git a/lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs b/lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs
index 35bf65b..52e4984 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/ServiceDomain.cs
@@ -23,11 +23,10 @@
*/
using System;
-using System.Net;
-using System.Linq;
using System.Collections.Generic;
using VNLib.Utils.Extensions;
+using VNLib.Plugins.Essentials.ServiceStack.Construction;
namespace VNLib.Plugins.Essentials.ServiceStack
{
@@ -38,7 +37,7 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// </summary>
public sealed class ServiceDomain
{
- private readonly LinkedList<ServiceGroup> _serviceGroups = new();
+ private ServiceGroup[] _serviceGroups = [];
/// <summary>
/// Gets all service groups loaded in the service manager
@@ -51,15 +50,11 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// </summary>
/// <param name="hostBuilder">The callback method to build virtual hosts</param>
/// <returns>A value that indicates if any virtual hosts were successfully loaded</returns>
- public bool BuildDomain(Action<ICollection<IServiceHost>> hostBuilder)
+ public void BuildDomain(ServiceBuilder hostBuilder)
{
- //LL to store created hosts
- LinkedList<IServiceHost> hosts = new();
+ ArgumentNullException.ThrowIfNull(hostBuilder);
- //build hosts
- hostBuilder.Invoke(hosts);
-
- return FromExisting(hosts);
+ FromExisting(hostBuilder.BuildGroups());
}
/// <summary>
@@ -67,36 +62,11 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// </summary>
/// <param name="hosts">The enumeration of virtual hosts</param>
/// <returns>A value that indicates if any virtual hosts were successfully loaded</returns>
- public bool FromExisting(IEnumerable<IServiceHost> hosts)
- {
- //Get service groups and pass service group list
- CreateServiceGroups(_serviceGroups, hosts);
- return _serviceGroups.Any();
- }
-
- private static void CreateServiceGroups(ICollection<ServiceGroup> groups, IEnumerable<IServiceHost> hosts)
+ public void FromExisting(IEnumerable<ServiceGroup> hosts)
{
- //Get distinct interfaces
- IPEndPoint[] interfaces = hosts.Select(static s => s.TransportInfo.TransportEndpoint).Distinct().ToArray();
-
- //Select hosts of the same interface to create a group from
- foreach (IPEndPoint iface in interfaces)
- {
- IEnumerable<IServiceHost> groupHosts = hosts.Where(host => host.TransportInfo.TransportEndpoint.Equals(iface));
+ ArgumentNullException.ThrowIfNull(hosts);
- //Find any duplicate hostnames for the same service gorup
- IServiceHost[] overlap = groupHosts.Where(vh => groupHosts.Select(static s => s.Processor.Hostname).Count(hostname => vh.Processor.Hostname == hostname) > 1).ToArray();
-
- if(overlap.Length > 0)
- {
- throw new ArgumentException($"The hostname '{overlap.Last().Processor.Hostname}' is already in use by another virtual host");
- }
-
- //init new service group around an interface and its roots
- ServiceGroup group = new(iface, groupHosts);
-
- groups.Add(group);
- }
+ hosts.ForEach(h => _serviceGroups = [.. _serviceGroups, h]);
}
/// <summary>
@@ -106,9 +76,11 @@ namespace VNLib.Plugins.Essentials.ServiceStack
public void TearDown()
{
//Manually cleanup if unload missed data
- _serviceGroups.TryForeach(static sg => sg.UnloadAll());
- //empty service groups
- _serviceGroups.Clear();
+ Array.ForEach(_serviceGroups, static sg => sg.UnloadAll());
+
+ Array.Clear(_serviceGroups);
+
+ _serviceGroups = [];
}
}
}
diff --git a/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs b/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs
index 29b9fdc..a446e37 100644
--- a/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs
+++ b/lib/Plugins.Essentials.ServiceStack/src/ServiceGroup.cs
@@ -28,6 +28,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using VNLib.Utils.Extensions;
+using VNLib.Plugins.Essentials.ServiceStack.Plugins;
namespace VNLib.Plugins.Essentials.ServiceStack
{
@@ -41,19 +42,13 @@ namespace VNLib.Plugins.Essentials.ServiceStack
/// 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)
+ public sealed class ServiceGroup(IEnumerable<IServiceHost> hosts)
{
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 => serviceEndpoint;
-
- /// <summary>
/// The collection of hosts that are loaded by this group
/// </summary>
public IReadOnlyCollection<IServiceHost> Hosts => _vHosts;