aboutsummaryrefslogtreecommitdiff
path: root/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs')
-rw-r--r--lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs126
1 files changed, 121 insertions, 5 deletions
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs
index a1817a8..6665a75 100644
--- a/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs
+++ b/lib/VNLib.Plugins.Extensions.Loading/src/Routing/RoutingExtensions.cs
@@ -24,10 +24,19 @@
using System;
using System.Reflection;
+using System.Threading.Tasks;
+using System.Collections.Frozen;
using System.Collections.Generic;
+using System.Text.RegularExpressions;
using System.Runtime.CompilerServices;
+using VNLib.Net.Http;
+using VNLib.Utils.Logging;
+using VNLib.Utils.Resources;
using VNLib.Plugins.Essentials.Runtime;
+using VNLib.Plugins.Essentials;
+using VNLib.Plugins.Essentials.Endpoints;
+using VNLib.Plugins.Extensions.Loading.Routing.Mvc;
namespace VNLib.Plugins.Extensions.Loading.Routing
{
@@ -35,7 +44,7 @@ namespace VNLib.Plugins.Extensions.Loading.Routing
/// <summary>
/// Provides advanced QOL features to plugin loading
/// </summary>
- public static class RoutingExtensions
+ public static partial class RoutingExtensions
{
private static readonly ConditionalWeakTable<IEndpoint, PluginBase?> _pluginRefs = new();
private static readonly ConditionalWeakTable<PluginBase, EndpointCollection> _pluginEndpoints = new();
@@ -52,11 +61,14 @@ namespace VNLib.Plugins.Extensions.Loading.Routing
T endpoint = plugin.CreateService<T>();
//Route the endpoint
- plugin.Route(endpoint);
+ Route(plugin, endpoint);
//Store ref to plugin for endpoint
_pluginRefs.Add(endpoint, plugin);
+ //Function that initalizes the endpoint's path and logging variables
+ InitEndpointSettings(plugin, endpoint);
+
return endpoint;
}
@@ -96,15 +108,119 @@ namespace VNLib.Plugins.Extensions.Loading.Routing
{
_ = _pluginRefs.TryGetValue(ep, out PluginBase? pBase);
return pBase ?? throw new InvalidOperationException("Endpoint was not dynamically routed");
- }
-
+ }
+
+ private static readonly Regex ConfigSyntaxParser = ParserRegex();
+ private delegate void InitFunc(string path, ILogProvider log);
+
+ [GeneratedRegex("{{(.*?)}}", RegexOptions.Compiled)]
+ private static partial Regex ParserRegex();
+
+ private static void InitEndpointSettings<T>(PluginBase plugin, T endpoint) where T : IEndpoint
+ {
+ //Load optional config
+ IConfigScope config = plugin.GetConfigForType<T>();
+
+ ILogProvider logger = plugin.Log;
+
+ EndpointPathAttribute? pathAttr = typeof(T).GetCustomAttribute<EndpointPathAttribute>();
+
+ /*
+ * gets the protected function for assigning the endpoint path
+ * and logger instance.
+ */
+ InitFunc? initPathAndLog = ManagedLibrary.TryGetMethod<InitFunc>(endpoint, "InitPathAndLog", BindingFlags.NonPublic);
+
+ if (pathAttr is null || initPathAndLog is null)
+ {
+ return;
+ }
+
+ string? logName = typeof(T).GetCustomAttribute<EndpointLogNameAttribute>()?.LogName;
+
+ if (!string.IsNullOrWhiteSpace(logName))
+ {
+ logger = plugin.Log.CreateScope(SubsituteValue(logName, config));
+ }
+ try
+ {
+
+ //Invoke init function and pass in variable names
+ initPathAndLog(
+ path: SubsituteValue(pathAttr.Path, config),
+ logger
+ );
+ }
+ catch (ConfigurationException)
+ {
+ throw;
+ }
+ catch(Exception e)
+ {
+ throw new ConfigurationException($"Failed to initalize endpoint {endpoint.GetType().Name}", e);
+ }
+
+ static string SubsituteValue(string pathVar, IConfigScope? config)
+ {
+ if (config is null)
+ {
+ return pathVar;
+ }
+
+ // Replace the matched pattern with the corresponding value from the dictionary
+ return ConfigSyntaxParser.Replace(pathVar, match =>
+ {
+ string varName = match.Groups[1].Value;
+
+ //Get the value from the config scope or return the original variable unmodified
+ return config.GetValueOrDefault(varName, varName);
+ });
+ }
+ }
private sealed class EndpointCollection : IVirtualEndpointDefinition
{
public List<IEndpoint> Endpoints { get; } = new();
///<inheritdoc/>
- IEnumerable<IEndpoint> IVirtualEndpointDefinition.GetEndpoints() => Endpoints;
+ IEnumerable<IEndpoint> IVirtualEndpointDefinition.GetEndpoints() => Endpoints;
+ }
+
+
+ private delegate ValueTask<VfReturnType> EndpointWorkFunc(HttpEntity entity);
+
+ sealed record class HttpControllerEndpoint(MethodInfo MethodInfo, HttpEndpointAttribute Attr)
+ {
+ public string Path => Attr.Path;
+
+ public HttpMethod Method => Attr.Method;
+
+ public EndpointWorkFunc Func { get; } = MethodInfo.CreateDelegate<EndpointWorkFunc>();
+ }
+
+ private sealed class EndpointWrapper
+ : ResourceEndpointBase
+ {
+
+ private readonly FrozenDictionary<HttpMethod, EndpointWorkFunc> _wrappers;
+
+ public EndpointWrapper(FrozenDictionary<HttpMethod, EndpointWorkFunc> table, string path, ILogProvider log)
+ {
+ _wrappers = table;
+ InitPathAndLog(path, log);
+ }
+
+ protected override ValueTask<VfReturnType> OnProcessAsync(HttpEntity entity)
+ {
+ ref readonly EndpointWorkFunc func = ref _wrappers.GetValueRefOrNullRef(entity.Server.Method);
+
+ if (Unsafe.IsNullRef(in func))
+ {
+ return ValueTask.FromResult(VfReturnType.ProcessAsFile);
+ }
+
+ return func(entity);
+ }
}
}
}