From 7d814ee4cb2155d54522a5b79da1c755466436ad Mon Sep 17 00:00:00 2001 From: Timerix Date: Sun, 23 Mar 2025 00:07:06 +0500 Subject: [PATCH] IRouter --- DTLib.Web/DTLib.Web.csproj | 2 +- ...legateRoute.cs => DelegateRouteHandler.cs} | 2 +- DTLib.Web/Routes/IRouter.cs | 6 +++ .../Routes/{Route.cs => RouteHandler.cs} | 2 +- ...ilesRoute.cs => ServeFilesRouteHandler.cs} | 2 +- DTLib.Web/Routes/SimpleRouter.cs | 48 +++++++++++++++++ DTLib.Web/WebApp.cs | 53 ++++--------------- 7 files changed, 67 insertions(+), 48 deletions(-) rename DTLib.Web/Routes/{DelegateRoute.cs => DelegateRouteHandler.cs} (54%) create mode 100644 DTLib.Web/Routes/IRouter.cs rename DTLib.Web/Routes/{Route.cs => RouteHandler.cs} (74%) rename DTLib.Web/Routes/{ServeFilesRoute.cs => ServeFilesRouteHandler.cs} (89%) create mode 100644 DTLib.Web/Routes/SimpleRouter.cs diff --git a/DTLib.Web/DTLib.Web.csproj b/DTLib.Web/DTLib.Web.csproj index 688783f..edf821e 100644 --- a/DTLib.Web/DTLib.Web.csproj +++ b/DTLib.Web/DTLib.Web.csproj @@ -2,7 +2,7 @@ DTLib.Web - 1.0.0 + 1.1.0 Timerix HTTP Server with simple routing GIT diff --git a/DTLib.Web/Routes/DelegateRoute.cs b/DTLib.Web/Routes/DelegateRouteHandler.cs similarity index 54% rename from DTLib.Web/Routes/DelegateRoute.cs rename to DTLib.Web/Routes/DelegateRouteHandler.cs index f4ff3e0..add62c0 100644 --- a/DTLib.Web/Routes/DelegateRoute.cs +++ b/DTLib.Web/Routes/DelegateRouteHandler.cs @@ -1,6 +1,6 @@ namespace DTLib.Web.Routes; -public class DelegateRoute(Func> routeHandler) : Route +public class DelegateRouteHandler(Func> routeHandler) : RouteHandler { public override Task HandleRequest(HttpListenerContext ctx) => routeHandler(ctx); } \ No newline at end of file diff --git a/DTLib.Web/Routes/IRouter.cs b/DTLib.Web/Routes/IRouter.cs new file mode 100644 index 0000000..45f66fc --- /dev/null +++ b/DTLib.Web/Routes/IRouter.cs @@ -0,0 +1,6 @@ +namespace DTLib.Web.Routes; + +public interface IRouter +{ + Task Resolve(HttpListenerContext ctx); +} \ No newline at end of file diff --git a/DTLib.Web/Routes/Route.cs b/DTLib.Web/Routes/RouteHandler.cs similarity index 74% rename from DTLib.Web/Routes/Route.cs rename to DTLib.Web/Routes/RouteHandler.cs index c72a3e2..2889276 100644 --- a/DTLib.Web/Routes/Route.cs +++ b/DTLib.Web/Routes/RouteHandler.cs @@ -1,6 +1,6 @@ namespace DTLib.Web.Routes; -public abstract class Route +public abstract class RouteHandler { public abstract Task HandleRequest(HttpListenerContext ctx); } \ No newline at end of file diff --git a/DTLib.Web/Routes/ServeFilesRoute.cs b/DTLib.Web/Routes/ServeFilesRouteHandler.cs similarity index 89% rename from DTLib.Web/Routes/ServeFilesRoute.cs rename to DTLib.Web/Routes/ServeFilesRouteHandler.cs index 2f68f9f..d209c5c 100644 --- a/DTLib.Web/Routes/ServeFilesRoute.cs +++ b/DTLib.Web/Routes/ServeFilesRouteHandler.cs @@ -1,6 +1,6 @@ namespace DTLib.Web.Routes; -public class ServeFilesRoute(IOPath _publicDir, string _homePageUrl = "index.html") : Route +public class ServeFilesRouteHandler(IOPath _publicDir, string _homePageUrl = "index.html") : RouteHandler { public override async Task HandleRequest(HttpListenerContext ctx) { diff --git a/DTLib.Web/Routes/SimpleRouter.cs b/DTLib.Web/Routes/SimpleRouter.cs new file mode 100644 index 0000000..a291056 --- /dev/null +++ b/DTLib.Web/Routes/SimpleRouter.cs @@ -0,0 +1,48 @@ +namespace DTLib.Web.Routes; + +public class SimpleRouter : IRouter +{ + /// route for base url + public RouteHandler? HomePageRoute = null; + /// route for any url that doesn't have its own handler + public RouteHandler? DefaultRoute = null; + + + private readonly Dictionary _routes = new(); + private readonly ILogger _logger; + + public SimpleRouter(ILogger logger) + { + _logger = logger; + } + + + public void MapRoute(string url, Func> route) + => MapRoute(url, new DelegateRouteHandler(route)); + + public void MapRoute(string url, RouteHandler route) => _routes.Add(url, route); + + public async Task Resolve(HttpListenerContext ctx) + { + string requestPath = ctx.Request.Url?.AbsolutePath ?? "/"; + RouteHandler? route; + if(HomePageRoute != null && requestPath == "/") + route = HomePageRoute; + else if (_routes.TryGetValue(requestPath, out var routeDelegate)) + route = routeDelegate; + else route = DefaultRoute; + + HttpStatusCode status; + if (route == null) + { + _logger.LogWarn(nameof(SimpleRouter), $"couldn't resolve request path {requestPath}"); + status = HttpStatusCode.NotFound; + } + else status = await route.HandleRequest(ctx); + + ctx.Response.StatusCode = (int)status; + await ctx.Response.OutputStream.FlushAsync(); + ctx.Response.OutputStream.Close(); + return status; + } +} \ No newline at end of file diff --git a/DTLib.Web/WebApp.cs b/DTLib.Web/WebApp.cs index 9bdc947..1ffb7ab 100644 --- a/DTLib.Web/WebApp.cs +++ b/DTLib.Web/WebApp.cs @@ -3,8 +3,6 @@ global using System.Collections.Generic; global using System.Text; global using System.Threading; global using System.Threading.Tasks; -global using DTLib; -global using DTLib.Demystifier; global using DTLib.Filesystem; global using DTLib.Logging; global using System.Net; @@ -12,23 +10,19 @@ using DTLib.Web.Routes; namespace DTLib.Web; -internal class WebApp +public class WebApp { - /// route for base url - public Route? HomePageRoute = null; - /// route for any url that doesn't have its own handler - public Route? DefaultRoute = null; - - private ContextLogger _logger; - private string _baseUrl; - private CancellationToken _stopToken; - private Dictionary routes = new(); + private readonly string _baseUrl; + private readonly ContextLogger _logger; + private readonly IRouter _router; + private readonly CancellationToken _stopToken; - public WebApp(ILogger logger, string baseUrl, CancellationToken stopToken) + public WebApp(string baseUrl, ILogger logger, IRouter router, CancellationToken stopToken = default) { _logger = new ContextLogger(nameof(WebApp), logger); _baseUrl = baseUrl; _stopToken = stopToken; + _router = router; } public async Task Run() @@ -38,7 +32,7 @@ internal class WebApp server.Prefixes.Add(_baseUrl); server.Start(); _logger.LogInfo("server started"); - long requestId = 0; + long requestId = 1; while (!_stopToken.IsCancellationRequested) { var ctx = await server.GetContextAsync(); @@ -58,7 +52,7 @@ internal class WebApp try { _logger.LogInfo(logContext, $"[{ctx.Request.HttpMethod}] {ctx.Request.RawUrl} from {ctx.Request.RemoteEndPoint} ..."); - var status = await Resolve(ctx); + var status = await _router.Resolve(ctx); _logger.LogInfo(logContext, status); } catch (Exception ex) @@ -67,33 +61,4 @@ internal class WebApp } } - - public void MapRoute(string url, Func> route) - => MapRoute(url, new DelegateRoute(route)); - - public void MapRoute(string url, Route route) => routes.Add(url, route); - - public async Task Resolve(HttpListenerContext ctx) - { - string requestPath = ctx.Request.Url?.AbsolutePath ?? "/"; - Route? route; - if(HomePageRoute != null && requestPath == "/") - route = HomePageRoute; - else if (routes.TryGetValue(requestPath, out var routeDelegate)) - route = routeDelegate; - else route = DefaultRoute; - - HttpStatusCode status; - if (route == null) - { - _logger.LogWarn("couldn't resolve request path {requestPath}"); - status = HttpStatusCode.NotFound; - } - else status = await route.HandleRequest(ctx); - - ctx.Response.StatusCode = (int)status; - await ctx.Response.OutputStream.FlushAsync(_stopToken); - ctx.Response.OutputStream.Close(); - return status; - } } \ No newline at end of file