SimpleHttpRouter, RouterTarget
parent
92b83af7f0
commit
120c8d3de3
135
HTTPServer.cs
135
HTTPServer.cs
|
@ -2,15 +2,12 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using ln.logging;
|
using ln.logging;
|
||||||
using ln.types.threads;
|
using ln.types.threads;
|
||||||
using ln.types;
|
|
||||||
using ln.http.listener;
|
using ln.http.listener;
|
||||||
using ln.http.connections;
|
using ln.http.connections;
|
||||||
using ln.types.net;
|
using ln.types.net;
|
||||||
using ln.http.cert;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Net.Sockets;
|
|
||||||
using ln.http.session;
|
|
||||||
using ln.http.exceptions;
|
using ln.http.exceptions;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
|
@ -21,7 +18,7 @@ namespace ln.http
|
||||||
public static int defaultPort = 8080;
|
public static int defaultPort = 8080;
|
||||||
public static bool exclusivePortListener = false;
|
public static bool exclusivePortListener = false;
|
||||||
|
|
||||||
public HttpRouter Router { get; set; }
|
public IHttpRouter Router { get; set; }
|
||||||
|
|
||||||
public bool IsRunning => !shutdown && (threadPool.CurrentPoolSize > 0);
|
public bool IsRunning => !shutdown && (threadPool.CurrentPoolSize > 0);
|
||||||
public Logger Logger { get; set; }
|
public Logger Logger { get; set; }
|
||||||
|
@ -34,22 +31,25 @@ namespace ln.http
|
||||||
DynamicPool threadPool;
|
DynamicPool threadPool;
|
||||||
public DynamicPool ThreadPool => threadPool;
|
public DynamicPool ThreadPool => threadPool;
|
||||||
|
|
||||||
|
HashSet<Connection> currentConnections = new HashSet<Connection>();
|
||||||
|
public IEnumerable<Connection> CurrentConnections => currentConnections;
|
||||||
|
|
||||||
public HTTPServer()
|
public HTTPServer()
|
||||||
{
|
{
|
||||||
Logger = Logger.Default;
|
Logger = Logger.Default;
|
||||||
threadPool = new DynamicPool(1024);
|
threadPool = new DynamicPool(1024);
|
||||||
}
|
}
|
||||||
public HTTPServer(HttpRouter router)
|
public HTTPServer(IHttpRouter router)
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
Router = router;
|
Router = router;
|
||||||
}
|
}
|
||||||
public HTTPServer(Listener listener, HttpRouter router)
|
public HTTPServer(Listener listener, IHttpRouter router)
|
||||||
: this(router)
|
: this(router)
|
||||||
{
|
{
|
||||||
AddListener(listener);
|
AddListener(listener);
|
||||||
}
|
}
|
||||||
public HTTPServer(Endpoint endpoint, HttpRouter router)
|
public HTTPServer(Endpoint endpoint, IHttpRouter router)
|
||||||
: this(new HttpListener(endpoint), router) { }
|
: this(new HttpListener(endpoint), router) { }
|
||||||
|
|
||||||
public void AddListener(Listener listener)
|
public void AddListener(Listener listener)
|
||||||
|
@ -100,63 +100,96 @@ namespace ln.http
|
||||||
foreach (Listener listener in listeners)
|
foreach (Listener listener in listeners)
|
||||||
StopListener(listener);
|
StopListener(listener);
|
||||||
|
|
||||||
|
for (int n = 0; n < 150; n++)
|
||||||
|
{
|
||||||
|
lock (currentConnections)
|
||||||
|
{
|
||||||
|
if (currentConnections.Count == 0)
|
||||||
|
break;
|
||||||
|
if ((n % 20) == 0)
|
||||||
|
{
|
||||||
|
Logging.Log(LogLevel.INFO, "HTTPServer: still waiting for {0} connections to close", currentConnections.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (currentConnections)
|
||||||
|
{
|
||||||
|
foreach (Connection connection in currentConnections)
|
||||||
|
{
|
||||||
|
connection.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
threadPool.Stop(true);
|
threadPool.Stop(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleConnection(Connection connection)
|
private void HandleConnection(Connection connection)
|
||||||
{
|
{
|
||||||
HttpRequest httpRequest = null;
|
lock (this.currentConnections)
|
||||||
bool keepAlive = true;
|
currentConnections.Add(connection);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
do
|
HttpRequest httpRequest = null;
|
||||||
|
bool keepAlive = true;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
httpRequest = connection.ReadRequest(this);
|
do
|
||||||
if (httpRequest == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
HttpResponse response;
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
response = Router.Route(httpRequest);
|
httpRequest = connection.ReadRequest(this);
|
||||||
} catch (HttpException httpExc)
|
if (httpRequest == null)
|
||||||
{
|
break;
|
||||||
response = new HttpResponse(httpRequest);
|
|
||||||
response.StatusCode = httpExc.StatusCode;
|
|
||||||
response.StatusMessage = httpExc.Message;
|
|
||||||
response.ContentWriter.WriteLine(httpExc.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response != null)
|
HttpResponse response;
|
||||||
{
|
try
|
||||||
keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
|
{
|
||||||
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
|
response = Router.Route(httpRequest.URI.AbsolutePath,httpRequest);
|
||||||
|
}
|
||||||
|
catch (HttpException httpExc)
|
||||||
|
{
|
||||||
|
response = new HttpResponse(httpRequest);
|
||||||
|
response.StatusCode = httpExc.StatusCode;
|
||||||
|
response.StatusMessage = httpExc.Message;
|
||||||
|
response.ContentWriter.WriteLine(httpExc.Message);
|
||||||
|
}
|
||||||
|
|
||||||
connection.SendResponse(response);
|
if (response != null)
|
||||||
}
|
{
|
||||||
else
|
keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
|
||||||
{
|
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
|
||||||
keepAlive = false;
|
|
||||||
}
|
|
||||||
} while (keepAlive);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logging.Log(e);
|
|
||||||
if (httpRequest != null)
|
|
||||||
{
|
|
||||||
HttpResponse httpResponse = new HttpResponse(httpRequest);
|
|
||||||
httpResponse.StatusCode = 500;
|
|
||||||
httpResponse.ContentWriter.WriteLine("500 Internal Server Error");
|
|
||||||
httpResponse.ContentWriter.Flush();
|
|
||||||
|
|
||||||
connection.SendResponse(httpResponse);
|
connection.SendResponse(response);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
keepAlive = false;
|
||||||
|
}
|
||||||
|
} while (keepAlive);
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.Log(e);
|
||||||
|
if (httpRequest != null)
|
||||||
|
{
|
||||||
|
HttpResponse httpResponse = new HttpResponse(httpRequest);
|
||||||
|
httpResponse.StatusCode = 500;
|
||||||
|
httpResponse.ContentWriter.WriteLine("500 Internal Server Error");
|
||||||
|
httpResponse.ContentWriter.Flush();
|
||||||
|
|
||||||
|
connection.SendResponse(httpResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequest.ClearCurrent();
|
||||||
|
|
||||||
|
connection.GetStream().Close();
|
||||||
|
} finally
|
||||||
|
{
|
||||||
|
lock (currentConnections)
|
||||||
|
currentConnections.Remove(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpRequest.ClearCurrent();
|
|
||||||
|
|
||||||
connection.GetStream().Close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Log(DateTime startTime,double duration,HttpRequest httpRequest,HttpResponse httpResponse)
|
public void Log(DateTime startTime,double duration,HttpRequest httpRequest,HttpResponse httpResponse)
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace ln.http
|
||||||
|
|
||||||
Dictionary<String, String> requestHeaders;
|
Dictionary<String, String> requestHeaders;
|
||||||
Dictionary<String, String> requestCookies;
|
Dictionary<String, String> requestCookies;
|
||||||
|
Dictionary<string, String> requestParameters;
|
||||||
|
|
||||||
public HTTPServer HTTPServer { get; }
|
public HTTPServer HTTPServer { get; }
|
||||||
|
|
||||||
|
@ -71,6 +72,7 @@ namespace ln.http
|
||||||
|
|
||||||
requestHeaders = new Dictionary<string, string>(httpReader.Headers);
|
requestHeaders = new Dictionary<string, string>(httpReader.Headers);
|
||||||
requestCookies = new Dictionary<string, string>();
|
requestCookies = new Dictionary<string, string>();
|
||||||
|
requestParameters = new Dictionary<string, string>();
|
||||||
|
|
||||||
Setup();
|
Setup();
|
||||||
|
|
||||||
|
@ -171,6 +173,17 @@ namespace ln.http
|
||||||
return requestCookies[name];
|
return requestCookies[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String GetParameter(String parameterName) => GetParameter(parameterName, null);
|
||||||
|
public String GetParameter(String parameterName,String defaultValue)
|
||||||
|
{
|
||||||
|
if (!requestParameters.TryGetValue(parameterName, out string value))
|
||||||
|
value = defaultValue;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
public void SetParameter(String parameterName,String parameterValue) => requestParameters[parameterName] = parameterValue;
|
||||||
|
public IEnumerable<string> ParameterNames => requestParameters.Keys;
|
||||||
|
|
||||||
|
|
||||||
public string self()
|
public string self()
|
||||||
{
|
{
|
||||||
return BaseURI.ToString();
|
return BaseURI.ToString();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
using ln.logging;
|
using ln.logging;
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
{
|
{
|
||||||
public abstract class HttpRouter
|
public abstract class HttpRouter : IHttpRouter
|
||||||
{
|
{
|
||||||
public HttpRouter()
|
public HttpRouter()
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@ namespace ln.http
|
||||||
|
|
||||||
public abstract IHTTPResource FindResource(HttpRequest httpRequest);
|
public abstract IHTTPResource FindResource(HttpRequest httpRequest);
|
||||||
|
|
||||||
public virtual HttpResponse Route(HttpRequest httpRequest)
|
public virtual HttpResponse Route(string path,HttpRequest httpRequest)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
using System;
|
||||||
|
namespace ln.http
|
||||||
|
{
|
||||||
|
public interface IHttpRouter
|
||||||
|
{
|
||||||
|
HttpResponse Route(string path, HttpRequest httpRequest);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
namespace ln.http
|
||||||
|
{
|
||||||
|
public class RouterTarget :IHttpRouter
|
||||||
|
{
|
||||||
|
public Func<HttpRequest,HttpResponse> Target { get; }
|
||||||
|
|
||||||
|
public RouterTarget(Func<HttpRequest, HttpResponse> target)
|
||||||
|
{
|
||||||
|
Target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpResponse Route(string path, HttpRequest httpRequest)
|
||||||
|
{
|
||||||
|
return Target(httpRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
using System;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
namespace ln.http
|
||||||
|
{
|
||||||
|
public class SimpleHttpRouter : IHttpRouter
|
||||||
|
{
|
||||||
|
List<SimpleRoute> routes = new List<SimpleRoute>();
|
||||||
|
|
||||||
|
public SimpleHttpRouter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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[] { '/' },StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
StringBuilder stringBuilder = new StringBuilder("^");
|
||||||
|
foreach (string part in parts)
|
||||||
|
{
|
||||||
|
if (part.StartsWith(":", StringComparison.InvariantCulture))
|
||||||
|
stringBuilder.AppendFormat("/(?<{0}>[^/]+)", part.Substring(1));
|
||||||
|
else if (part.Equals("*"))
|
||||||
|
stringBuilder.AppendFormat("/(?<_>.*)");
|
||||||
|
else
|
||||||
|
stringBuilder.AppendFormat("/{0}", part);
|
||||||
|
}
|
||||||
|
stringBuilder.Append("/?$");
|
||||||
|
|
||||||
|
AddRoute(stringBuilder.ToString(), 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 HttpResponse Route(string path, HttpRequest httpRequest)
|
||||||
|
{
|
||||||
|
foreach (SimpleRoute simpleRoute in routes.ToArray())
|
||||||
|
{
|
||||||
|
Match match = simpleRoute.Route.Match(path);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
string residual = "";
|
||||||
|
|
||||||
|
foreach (Group group in match.Groups)
|
||||||
|
{
|
||||||
|
httpRequest?.SetParameter(group.Name, group.Value);
|
||||||
|
if (group.Name.Equals("_"))
|
||||||
|
residual = group.Value;
|
||||||
|
}
|
||||||
|
return simpleRoute.Target.Route(residual,httpRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,8 @@ namespace ln.http.listener
|
||||||
|
|
||||||
public override Connection Accept() => new HttpConnection(this,tcpListener.AcceptTcpClient());
|
public override Connection Accept() => new HttpConnection(this,tcpListener.AcceptTcpClient());
|
||||||
|
|
||||||
public override bool IsOpen => tcpListener != null;
|
public override bool IsOpen => (tcpListener != null);
|
||||||
|
|
||||||
public override void Open()
|
public override void Open()
|
||||||
{
|
{
|
||||||
tcpListener = new TcpListener(Listen, Port);
|
tcpListener = new TcpListener(Listen, Port);
|
||||||
|
|
|
@ -67,6 +67,9 @@
|
||||||
<Compile Include="HttpRouter.cs" />
|
<Compile Include="HttpRouter.cs" />
|
||||||
<Compile Include="IHTTPResource.cs" />
|
<Compile Include="IHTTPResource.cs" />
|
||||||
<Compile Include="router\VirtualHostRouter.cs" />
|
<Compile Include="router\VirtualHostRouter.cs" />
|
||||||
|
<Compile Include="SimpleHttpRouter.cs" />
|
||||||
|
<Compile Include="IHttpRouter.cs" />
|
||||||
|
<Compile Include="RouterTarget.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="exceptions\" />
|
<Folder Include="exceptions\" />
|
||||||
|
|
Loading…
Reference in New Issue