272 lines
10 KiB
C#
272 lines
10 KiB
C#
using System;
|
|
using System.Text.RegularExpressions;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace ln.http
|
|
{
|
|
public delegate bool HttpRouterDelegate(HttpContext context);
|
|
public delegate bool HttpAuthenticationDelegate(HttpContext httpContext, out HttpPrincipal principal);
|
|
public delegate bool HttpAuthorizationDelegate(HttpContext httpContext);
|
|
public delegate void HttpFilterDelegate(HttpContext httpContext);
|
|
|
|
|
|
public enum HttpRoutePriority : int { HIGHEST = 0, HIGH = 1, NORMAL = 2, LOW = 3, LOWEST = 4 }
|
|
|
|
public class HttpRouter
|
|
{
|
|
public event HttpFilterDelegate HttpFilters;
|
|
public event HttpAuthenticationDelegate AuthenticationDelegates;
|
|
|
|
private List<HttpMapping>[] _mappings = new List<HttpMapping>[5];
|
|
|
|
public HttpRouter()
|
|
{
|
|
for (int n = 0; n < 5; n++)
|
|
_mappings[n] = new List<HttpMapping>();
|
|
}
|
|
|
|
public HttpMapping Map(HttpMethod httpMethod, string uri, HttpRouterDelegate routerDelegate) =>
|
|
Map(httpMethod, uri, HttpRoutePriority.NORMAL, routerDelegate);
|
|
|
|
public HttpMapping Map(HttpMethod httpMethod, string uri, HttpRoutePriority priority, HttpRouterDelegate routerDelegate)
|
|
{
|
|
HttpMapping httpMapping = new HttpMapping(httpMethod, RegexFromRoute(uri), routerDelegate);
|
|
_mappings[(int)priority].Add(httpMapping);
|
|
return httpMapping;
|
|
}
|
|
|
|
public bool Route(HttpContext httpContext)
|
|
{
|
|
string residual = "";
|
|
|
|
if (AuthenticationDelegates is not null)
|
|
{
|
|
foreach (HttpAuthenticationDelegate authenticationDelegate in AuthenticationDelegates.GetInvocationList())
|
|
{
|
|
if (httpContext.Authenticate(authenticationDelegate))
|
|
break;
|
|
}
|
|
}
|
|
|
|
HttpFilters?.Invoke(httpContext);
|
|
|
|
for (int n = 0; n < _mappings.Length; n++)
|
|
{
|
|
foreach (HttpMapping httpMapping in _mappings[n])
|
|
{
|
|
if ((httpMapping.Method == httpContext.Request.Method) || (httpMapping.Method == HttpMethod.ANY))
|
|
{
|
|
Match match = httpMapping.UriRegex.Match(httpContext.RoutableUri);
|
|
if (match.Success)
|
|
{
|
|
foreach (Group group in match.Groups)
|
|
{
|
|
httpContext.Request?.SetParameter(group.Name, group.Value);
|
|
if (group.Name.Equals("_"))
|
|
if (group.Value.StartsWith("/", StringComparison.InvariantCulture))
|
|
residual = group.Value;
|
|
else
|
|
residual = "/" + group.Value;
|
|
}
|
|
|
|
string saveRoutableUri = httpContext.RoutableUri;
|
|
httpContext.RoutableUri = residual;
|
|
if (httpMapping.Route(httpContext))
|
|
return true;
|
|
|
|
httpContext.RoutableUri = saveRoutableUri;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (httpContext.Response is not null)
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
private string RegexFromRoute(string route)
|
|
{
|
|
string[] parts = route.Split(new char[] { '/' });
|
|
string[] reparts = parts.Select((part) =>
|
|
{
|
|
if (part.StartsWith(":", StringComparison.InvariantCulture))
|
|
if (part.EndsWith("*", StringComparison.InvariantCulture))
|
|
return string.Format("(?<{0}>[^/]+)(?<_>/.*)?", part.Substring(1, part.Length - 2));
|
|
else
|
|
return string.Format("(?<{0}>[^/]+)", part.Substring(1));
|
|
else if (part.Equals("*"))
|
|
return string.Format("(?<_>.*)");
|
|
else
|
|
return string.Format("{0}", part);
|
|
}).ToArray();
|
|
|
|
return string.Format("^{0}/?$", string.Join("/", reparts));
|
|
}
|
|
|
|
public class HttpMapping
|
|
{
|
|
public HttpMethod Method { get; set; }
|
|
public Regex UriRegex { get; set; }
|
|
public HttpRouterDelegate RouterDelegate { get; set; }
|
|
|
|
public bool AuthenticationRequired { get; set; }
|
|
public HttpAuthorizationDelegate AuthorizationDelegate { get; set; }
|
|
|
|
public event HttpFilterDelegate HttpFilters;
|
|
|
|
public HttpMapping(HttpMethod httpMethod, string reUri, HttpRouterDelegate routerDelegate)
|
|
{
|
|
Method = httpMethod;
|
|
UriRegex = new Regex(reUri);
|
|
RouterDelegate = routerDelegate;
|
|
AuthenticationRequired = HTTPServer.DefaultRouteAuthentication;
|
|
}
|
|
|
|
public HttpMapping UseFilter(HttpFilterDelegate filterDelegate)
|
|
{
|
|
HttpFilters += filterDelegate;
|
|
return this;
|
|
}
|
|
|
|
public HttpMapping Authenticate()
|
|
{
|
|
AuthenticationRequired = true;
|
|
return this;
|
|
}
|
|
public HttpMapping Anonymous()
|
|
{
|
|
AuthenticationRequired = false;
|
|
return this;
|
|
}
|
|
|
|
public HttpMapping Authorize(HttpAuthorizationDelegate authorizationDelegate)
|
|
{
|
|
AuthorizationDelegate = authorizationDelegate;
|
|
return this;
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool Route(HttpContext httpContext)
|
|
{
|
|
if (AuthenticationRequired && httpContext.AuthenticatedPrincipal is null)
|
|
return false;
|
|
|
|
if ((AuthorizationDelegate is not null) && (!AuthorizationDelegate(httpContext)))
|
|
return false;
|
|
|
|
HttpFilters?.Invoke(httpContext);
|
|
|
|
return RouterDelegate(httpContext);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
List<SimpleRoute> routes = new List<SimpleRoute>();
|
|
public SimpleRoute[] Routes => routes.ToArray();
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
public void AddSimpleRoute(string simpleRoute, Func<HttpRoutingContext, HttpRequest, HttpResponse> target) => AddSimpleRoute(simpleRoute, new RouterTarget(target));
|
|
public void AddSimpleRoute(string simpleRoute, Func<string, HttpRequest, HttpResponse> target) => AddSimpleRoute(simpleRoute, new RouterTarget(target));
|
|
public void AddSimpleRoute(string simpleRoute, Func<HttpRequest, HttpResponse> target) => AddSimpleRoute(simpleRoute, new RouterTarget(target));
|
|
public void AddSimpleRoute(string simpleRoute, IHttpRouter target) => AddSimpleRoute(simpleRoute, target, simpleRoute.Split('/').Length);
|
|
public void AddSimpleRoute(string simpleRoute, IHttpRouter target, int priority)
|
|
{
|
|
string[] parts = simpleRoute.Split(new char[] { '/' });
|
|
string[] reparts = parts.Select((part) =>
|
|
{
|
|
if (part.StartsWith(":", StringComparison.InvariantCulture))
|
|
if (part.EndsWith("*", StringComparison.InvariantCulture))
|
|
return string.Format("(?<{0}>[^/]+)(?<_>/.*)?", part.Substring(1, part.Length - 2));
|
|
else
|
|
return string.Format("(?<{0}>[^/]+)", part.Substring(1));
|
|
else if (part.Equals("*"))
|
|
return string.Format("(?<_>.*)");
|
|
else
|
|
return string.Format("{0}", part);
|
|
}).ToArray();
|
|
|
|
string reroute = string.Format("{0}\\/?$", string.Join("/", reparts));
|
|
|
|
AddRoute(reroute, target, priority);
|
|
}
|
|
|
|
public void AddRoute(String route, IHttpRouter target) => AddRoute(route, target, 0);
|
|
public void AddRoute(String route, IHttpRouter target,int priority)
|
|
{
|
|
lock (this)
|
|
{
|
|
routes.Add(new SimpleRoute(route, target, priority));
|
|
routes.Sort((SimpleRoute a, SimpleRoute b) => b.Priority - a.Priority);
|
|
}
|
|
}
|
|
public void Remove(SimpleRoute simpleRoute) => routes.Remove(simpleRoute);
|
|
|
|
public HttpResponse Route(HttpRoutingContext routingContext, HttpRequest httpRequest)
|
|
{
|
|
HttpResponse httpResponse;
|
|
|
|
if (OnRoute != null)
|
|
{
|
|
foreach (RouterFilterDelegate filterDelegate in OnRoute.GetInvocationList())
|
|
{
|
|
if (filterDelegate(this, ref routingContext, httpRequest, out httpResponse))
|
|
return httpResponse;
|
|
}
|
|
}
|
|
|
|
foreach (SimpleRoute simpleRoute in routes.ToArray())
|
|
{
|
|
Match match = simpleRoute.Route.Match(routingContext.Path);
|
|
if (match.Success)
|
|
{
|
|
string residual = "";
|
|
|
|
foreach (Group group in match.Groups)
|
|
{
|
|
httpRequest?.SetParameter(group.Name, group.Value);
|
|
if (group.Name.Equals("_"))
|
|
if (group.Value.StartsWith("/", StringComparison.InvariantCulture))
|
|
residual = group.Value;
|
|
else
|
|
residual = "/" + group.Value;
|
|
}
|
|
|
|
httpResponse = simpleRoute.Target.Route(routingContext.Routed(residual), httpRequest);
|
|
if (httpResponse != null)
|
|
return httpResponse;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
public class SimpleRoute
|
|
{
|
|
public int Priority { get; }
|
|
|
|
public Regex Route { get; }
|
|
public IHttpRouter Target { get; }
|
|
|
|
public SimpleRoute(string regex, IHttpRouter target) : this(regex, target, 0) { }
|
|
public SimpleRoute(string regex, IHttpRouter target,int priority)
|
|
{
|
|
Route = new Regex(regex);
|
|
Target = target;
|
|
Priority = priority;
|
|
}
|
|
}
|
|
*/
|
|
|
|
}
|
|
}
|