diff options
author | vnugent <public@vaughnnugent.com> | 2023-05-29 17:35:08 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-05-29 17:35:08 -0400 |
commit | 765d3d328af49f92f1d0b296bfba2d7791e0cdf5 (patch) | |
tree | 4cdea3095b53f06508dbf05f4181c53e2844fc48 /plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model | |
parent | 31220eaf6583c28f2df5070c3c8841a02a17cdbe (diff) |
XML file backed content routing, no more db required
Diffstat (limited to 'plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model')
-rw-r--r-- | plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/DbRouteStore.cs (renamed from plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/RouteStore.cs) | 31 | ||||
-rw-r--r-- | plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/Route.cs | 37 | ||||
-rw-r--r-- | plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/XmlRouteStore.cs | 161 |
3 files changed, 211 insertions, 18 deletions
diff --git a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/RouteStore.cs b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/DbRouteStore.cs index e623228..0c2fca6 100644 --- a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/RouteStore.cs +++ b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/DbRouteStore.cs @@ -1,11 +1,11 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Plugins.Essentials.Content.Routing -* File: RouteStore.cs +* File: DbRouteStore.cs * -* RouteStore.cs is part of VNLib.Plugins.Essentials.Content.Routing which is part of the larger +* DbRouteStore.cs is part of VNLib.Plugins.Essentials.Content.Routing which is part of the larger * VNLib collection of libraries and utilities. * * VNLib.Plugins.Essentials.Content.Routing is free software: you can redistribute it and/or modify @@ -24,24 +24,37 @@ using System; using System.Linq; +using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using VNLib.Plugins.Extensions.Data; +using VNLib.Plugins.Extensions.Loading.Sql; namespace VNLib.Plugins.Essentials.Content.Routing.Model { - internal class RouteStore : DbStore<Route> + internal class DbRouteStore : DbStore<Route>, IRouteStore { private readonly DbContextOptions Options; - public RouteStore(DbContextOptions options) + public DbRouteStore(PluginBase plugin) { - Options = options; + //Load the db context options + Options = plugin.GetContextOptions(); } + ///<inheritdoc/> + public Task GetAllRoutesAsync(ICollection<Route> routes) + { + //Get all routes as a single page from the database + return GetPageAsync(routes, 0, int.MaxValue); + } + + ///<inheritdoc/> public override string RecordIdBuilder => Guid.NewGuid().ToString("N"); + ///<inheritdoc/> protected override IQueryable<Route> GetCollectionQueryBuilder(TransactionalDbContext context, params string[] constraints) { string hostname = constraints[0]; @@ -50,6 +63,7 @@ namespace VNLib.Plugins.Essentials.Content.Routing.Model select route; } + ///<inheritdoc/> protected override IQueryable<Route> GetSingleQueryBuilder(TransactionalDbContext context, params string[] constraints) { string id = constraints[0]; @@ -58,11 +72,14 @@ namespace VNLib.Plugins.Essentials.Content.Routing.Model select route; } + ///<inheritdoc/> public override TransactionalDbContext NewContext() => new RoutingContext(Options); + ///<inheritdoc/> protected override void OnRecordUpdate(Route newRecord, Route currentRecord) { - throw new NotImplementedException(); + throw new NotSupportedException(); } + } } diff --git a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/Route.cs b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/Route.cs index c1531f7..acceb0c 100644 --- a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/Route.cs +++ b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/Route.cs @@ -35,38 +35,53 @@ namespace VNLib.Plugins.Essentials.Content.Routing.Model [Index(nameof(Id), IsUnique = true)] internal class Route : DbModelBase { + public const FpRoutine RewriteRoutine = (FpRoutine)50; + [Key] public override string Id { get; set; } public override DateTime Created { get; set; } public override DateTime LastModified { get; set; } public string Hostname { get; set; } + public string MatchPath { get; set; } + [Column("Privilage")] public long _privilage { get => (long)Privilage; set => Privilage = (ulong)value; } + [NotMapped] public ulong Privilage { get; set; } - public string Alternate { get; set; } - public FpRoutine Routine { get; set; } + public string? Alternate { get; set; } = string.Empty; + + public FpRoutine Routine { get; set; } + + public string? RewriteSearch { get; set; } /// <summary> - /// The processing arguments that match the route + /// Creates the <see cref="FileProcessArgs"/> to return to the processor + /// for the current rule, which may include rewriting the url. /// </summary> - [NotMapped] - public FileProcessArgs MatchArgs + /// <param name="entity">The connection to get the args for</param> + /// <returns>The <see cref="FileProcessArgs"/> for the connection</returns> + public FileProcessArgs GetArgs(HttpEntity entity) { - get + //Check for rewrite routine + if (Routine == RewriteRoutine) + { + //Rewrite the request url and return the args, processor will clean and parse url + string rewritten = entity.Server.Path.Replace(RewriteSearch!, Alternate!, StringComparison.OrdinalIgnoreCase); + + //Set to rewrite args + return new FileProcessArgs(FpRoutine.ServeOther, rewritten); + } + else { - return new FileProcessArgs() - { - Alternate = this.Alternate, - Routine = (FpRoutine) Routine - }; + return new FileProcessArgs(Routine, Alternate!); } } } diff --git a/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/XmlRouteStore.cs b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/XmlRouteStore.cs new file mode 100644 index 0000000..5420996 --- /dev/null +++ b/plugins/VNLib.Plugins.Essentials.Content.Routing/src/Model/XmlRouteStore.cs @@ -0,0 +1,161 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Plugins.Essentials.Content.Routing +* File: XmlRouteStore.cs +* +* XmlRouteStore.cs is part of VNLib.Plugins.Essentials.Content.Routing which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Plugins.Essentials.Content.Routing 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 3 of the +* License, or (at your option) any later version. +* +* VNLib.Plugins.Essentials.Content.Routing 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.Xml; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; + +using VNLib.Utils.IO; +using VNLib.Utils.Memory; +using VNLib.Utils.Logging; +using VNLib.Utils.Extensions; +using VNLib.Plugins.Extensions.Loading; + +namespace VNLib.Plugins.Essentials.Content.Routing.Model +{ + [ConfigurationName("store")] + internal sealed class XmlRouteStore : IRouteStore + { + private readonly string _routeFile; + + public XmlRouteStore(PluginBase plugin, IConfigScope config) + { + //Get the route file path + _routeFile = config["route_file"].GetString() ?? throw new KeyNotFoundException("Missing required key 'route_file' in 'route_store' configuration element"); + + //Make sure the file exists + if (!FileOperations.FileExists(_routeFile)) + { + throw new FileNotFoundException("Missing required route xml file", _routeFile); + } + + plugin.Log.Debug("Loading routes from {0}", _routeFile); + } + + ///<inheritdoc/> + public async Task GetAllRoutesAsync(ICollection<Route> routes) + { + using VnMemoryStream memStream = new(); + + //Load the route file + await using (FileStream routeFile = new(_routeFile, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + //Read the route file into memory + await routeFile.CopyToAsync(memStream, 4096, MemoryUtil.Shared, CancellationToken.None); + } + + //Rewind the memory stream + memStream.Seek(0, SeekOrigin.Begin); + + //Parse elements into routes + ParseElements(memStream, routes); + } + + private static void ParseElements(VnMemoryStream ms, ICollection<Route> routes) + { + //Read contents into xml doc for reading + XmlDocument xmlDoc = new(); + xmlDoc.Load(ms); + + //Get route elements + XmlNodeList? routeElements = xmlDoc.SelectNodes("routes/route"); + + //If no route elements, exit + if (routeElements == null) + { + return; + } + + foreach (XmlNode routeEl in routeElements) + { + Route route = new(); + + //See if route is disabled + string? disabledAtr = routeEl.Attributes["disabled"]?.Value; + //If disabled, skip route + if (disabledAtr != null) + { + continue; + } + + //Get the route routine value + string? routineAtr = routeEl.Attributes["routine"]?.Value; + _ = routineAtr ?? throw new XmlException("Missing required attribute 'routine' in route element"); + + //Try to get the routime enum value + if (uint.TryParse(routineAtr, out uint r)) + { + route.Routine = (FpRoutine)r; + } + else + { + throw new XmlException("The value of the 'routine' attribute is not a valid FpRoutine enum value"); + } + + //read priv level attribute + string? privAtr = routeEl.Attributes["privilage"]?.Value; + _ = privAtr ?? throw new XmlException("Missing required attribute 'priv' in route element"); + + //Try to get the priv level enum value + if (ulong.TryParse(privAtr, out ulong priv)) + { + route.Privilage = priv; + } + else + { + throw new XmlException("The value of the 'priv' attribute is not a valid unsigned 64-bit integer"); + } + + //Get hostname element value + string? hostEl = routeEl["hostname"]?.InnerText; + route.Hostname = hostEl ?? throw new XmlException("Missing required element 'hostname' in route element"); + + //Get the path element value + string? pathEl = routeEl["path"]?.InnerText; + route.MatchPath = pathEl ?? throw new XmlException("Missing required element 'path' in route element"); + + //Get the optional alternate path element value + route.Alternate = routeEl["alternate"]?.InnerText; + + //Check for rewrite routine, if rewrite, get rewrite and replace elements + if (route.Routine == Route.RewriteRoutine) + { + //Get the rewrite element value + string? rewriteEl = routeEl["rewrite"]?.InnerText; + route.RewriteSearch = rewriteEl ?? throw new XmlException("Missing required element 'rewrite' in route element"); + + //Get the rewrite element value + string? replaceEl = routeEl["replace"]?.InnerText; + route.Alternate = replaceEl ?? throw new XmlException("Missing required element 'replace' in route element"); + } + + //add route to the collection + routes.Add(route); + } + } + } +} |