namespace DTLib.Web.Routes; public class SimpleRouter : IRouter { /// route for any url that doesn't have its own handler public record RouteWithMethod(HttpMethod method, IRouteHandler routeHandler) { public bool CheckMethod(HttpMethod requestMethod) => (requestMethod & method) != 0; public bool CheckMethod(string requestMethodStr) => Enum.TryParse(requestMethodStr, out var requestMethod) && CheckMethod(requestMethod); } private readonly Dictionary _routes = new(); private readonly ILogger _logger; public RouteWithMethod? DefaultRoute { get; set; } public SimpleRouter(ILogger logger) { _logger = new ContextLogger(nameof(SimpleRouter), logger); } public void MapRoute(string url, HttpMethod method, IRouteHandler route) => _routes.Add(url, new RouteWithMethod(method, route)); public void MapRoute(string url, HttpMethod method, Func> route) => MapRoute(url, method, new DelegateRouteHandler(route)); public async Task Resolve(HttpListenerContext ctx, ContextLogger requestLogger) { HttpStatusCode status = HttpStatusCode.InternalServerError; try { string? requestPath = ctx.Request.Url?.AbsolutePath; if (string.IsNullOrEmpty(requestPath)) requestPath = "/"; if(!_routes.TryGetValue(requestPath!, out var routeWithMethod)) routeWithMethod = DefaultRoute; if (routeWithMethod is null) { _logger.LogWarn(nameof(SimpleRouter), $"couldn't resolve request path {ctx.Request.HttpMethod} {requestPath}"); status = HttpStatusCode.NotFound; } else if (!routeWithMethod.CheckMethod(ctx.Request.HttpMethod)) { _logger.LogWarn(nameof(SimpleRouter), $"received request with invalid method {ctx.Request.HttpMethod} {requestPath}"); status = HttpStatusCode.MethodNotAllowed; } else status = await routeWithMethod.routeHandler.HandleRequest(ctx, requestLogger); } finally { ctx.Response.StatusCode = (int)status; await ctx.Response.OutputStream.FlushAsync(); ctx.Response.OutputStream.Close(); } } }