ln.http/HTTPServer.cs

246 lines
7.9 KiB
C#
Raw Normal View History

2019-02-14 09:14:50 +01:00
using System;
using System.Collections.Generic;
2019-03-13 14:17:46 +01:00
using ln.logging;
using ln.types.threads;
2019-11-04 10:00:33 +01:00
using ln.http.listener;
using ln.http.connections;
using ln.types.net;
using System.Globalization;
2019-11-26 12:20:50 +01:00
using ln.http.exceptions;
2020-01-07 12:23:14 +01:00
using System.Threading;
2020-02-04 08:59:58 +01:00
using ln.types;
using ln.http.router;
2019-02-14 09:14:50 +01:00
namespace ln.http
{
public class HTTPServer
{
public static int backlog = 5;
public static int defaultPort = 8080;
public static bool exclusivePortListener = false;
2020-01-07 12:23:14 +01:00
public IHttpRouter Router { get; set; }
2019-03-13 14:17:46 +01:00
2019-11-04 10:00:33 +01:00
public bool IsRunning => !shutdown && (threadPool.CurrentPoolSize > 0);
public Logger Logger { get; set; }
2019-11-26 12:20:50 +01:00
2019-02-14 09:14:50 +01:00
bool shutdown = false;
2019-11-04 10:00:33 +01:00
List<Listener> listeners = new List<Listener>();
public Listener[] Listeners => listeners.ToArray();
DynamicPool threadPool;
public DynamicPool ThreadPool => threadPool;
2019-02-26 22:00:37 +01:00
2020-01-07 12:23:14 +01:00
HashSet<Connection> currentConnections = new HashSet<Connection>();
public IEnumerable<Connection> CurrentConnections => currentConnections;
2019-02-14 09:14:50 +01:00
public HTTPServer()
{
Logger = Logger.Default;
threadPool = new DynamicPool(1024);
2019-02-14 09:14:50 +01:00
}
2020-01-07 12:23:14 +01:00
public HTTPServer(IHttpRouter router)
2019-11-26 12:20:50 +01:00
: this()
{
Router = router;
}
2020-01-07 12:23:14 +01:00
public HTTPServer(Listener listener, IHttpRouter router)
2019-11-26 12:20:50 +01:00
: this(router)
{
AddListener(listener);
}
2020-01-07 12:23:14 +01:00
public HTTPServer(Endpoint endpoint, IHttpRouter router)
2019-11-26 12:20:50 +01:00
: this(new HttpListener(endpoint), router) { }
2019-02-14 09:14:50 +01:00
2019-11-04 10:00:33 +01:00
public void AddListener(Listener listener)
2019-02-14 09:14:50 +01:00
{
2019-11-04 10:00:33 +01:00
listeners.Add(listener);
if (IsRunning)
StartListener(listener);
}
2019-02-14 09:14:50 +01:00
2019-11-04 10:00:33 +01:00
public void StartListener(Listener listener)
{
listener.Open();
threadPool.Enqueue(
() => listener.AcceptMany(
(connection) => threadPool.Enqueue(
() => this.HandleConnection(connection)
)
)
);
2019-02-14 09:14:50 +01:00
}
2019-11-04 10:00:33 +01:00
public void StopListener(Listener listener)
2019-02-14 09:14:50 +01:00
{
2019-11-04 10:00:33 +01:00
listener.Close();
}
public void AddEndpoint(Endpoint endpoint)
{
AddListener(new HttpListener(endpoint));
2019-02-14 09:14:50 +01:00
}
public void Start()
{
threadPool.Start();
2019-11-04 10:00:33 +01:00
foreach (Listener listener in listeners)
StartListener(listener);
2019-02-14 09:14:50 +01:00
}
public void Stop()
{
lock (this)
{
this.shutdown = true;
2019-03-14 08:35:54 +01:00
}
2019-11-04 10:00:33 +01:00
foreach (Listener listener in listeners)
StopListener(listener);
2019-03-13 14:17:46 +01:00
2020-01-07 12:23:14 +01:00
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();
}
}
2019-11-04 10:00:33 +01:00
threadPool.Stop(true);
2019-02-14 09:14:50 +01:00
}
2019-11-04 10:00:33 +01:00
private void HandleConnection(Connection connection)
2019-02-14 09:14:50 +01:00
{
2020-01-07 12:23:14 +01:00
lock (this.currentConnections)
currentConnections.Add(connection);
2019-11-04 10:00:33 +01:00
try
2019-02-14 09:14:50 +01:00
{
2020-01-07 12:23:14 +01:00
HttpRequest httpRequest = null;
bool keepAlive = true;
try
2019-02-14 09:14:50 +01:00
{
2020-01-07 12:23:14 +01:00
do
2019-11-26 12:20:50 +01:00
{
2020-01-07 12:23:14 +01:00
httpRequest = connection.ReadRequest(this);
if (httpRequest == null)
break;
HttpResponse response;
try
{
2020-03-03 17:13:31 +01:00
response = Router.Route(new HttpRoutingContext(httpRequest),httpRequest);
2020-01-07 12:23:14 +01:00
}
catch (HttpException httpExc)
{
response = new HttpResponse(httpRequest);
response.StatusCode = httpExc.StatusCode;
response.StatusMessage = httpExc.Message;
response.ContentWriter.WriteLine(httpExc.Message);
}
if (response != null)
{
keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
connection.SendResponse(response);
}
else
{
keepAlive = false;
}
2020-02-04 08:59:58 +01:00
response?.ContentStream?.Dispose();
2020-01-07 12:23:14 +01:00
} while (keepAlive);
}
catch (Exception e)
{
Logging.Log(e);
if (httpRequest != null)
2019-11-15 13:47:01 +01:00
{
2020-01-07 12:23:14 +01:00
HttpResponse httpResponse = new HttpResponse(httpRequest);
httpResponse.StatusCode = 500;
httpResponse.ContentWriter.WriteLine("500 Internal Server Error");
httpResponse.ContentWriter.Flush();
2019-11-15 13:47:01 +01:00
2020-01-07 12:23:14 +01:00
connection.SendResponse(httpResponse);
2019-11-15 13:47:01 +01:00
}
2019-11-04 10:00:33 +01:00
}
2019-02-14 09:14:50 +01:00
2020-01-07 12:23:14 +01:00
HttpRequest.ClearCurrent();
connection.GetStream().Close();
} finally
{
lock (currentConnections)
currentConnections.Remove(connection);
}
2019-08-03 12:56:33 +02:00
}
2019-02-26 22:00:37 +01:00
public void Log(DateTime startTime,double duration,HttpRequest httpRequest,HttpResponse httpResponse)
{
Logger.Log(LogLevel.INFO, "{0} {1} {2} {3}",startTime.ToString("yyyyMMdd-HH:mm:ss"),duration.ToString(CultureInfo.InvariantCulture),httpRequest.Hostname,httpRequest.RequestURL);
}
2020-02-04 08:59:58 +01:00
public static void StartSimpleServer(string[] arguments)
{
ArgumentContainer argumentContainer = new ArgumentContainer(new Argument[]
{
new Argument('p',"port",8080),
new Argument('l',"listen","127.0.0.1"),
new Argument('c', "catch",null)
});
argumentContainer.Parse(arguments);
SimpleRouter router = new SimpleRouter();
router.AddSimpleRoute("/*", new RouterTarget((request) =>
{
HttpResponse response = new HttpResponse(request);
response.StatusCode = 404;
response.SetHeader("content-type", "text/plain");
response.ContentWriter.WriteLine("404 Not Found");
response.ContentWriter.Flush();
return response;
}), -100);
foreach (String path in argumentContainer.AdditionalArguments)
{
StaticRouter staticRouter = new StaticRouter(path);
staticRouter.AddIndex("index.html");
staticRouter.AddIndex("index.htm");
router.AddSimpleRoute("/*", staticRouter);
}
if (argumentContainer['c'].IsSet)
2020-03-03 17:13:31 +01:00
router.AddSimpleRoute("/*", new RouterTarget((request) => router.Route(new HttpRoutingContext(request, argumentContainer['c'].Value), request)), 0);
2020-02-04 08:59:58 +01:00
HTTPServer server = new HTTPServer(new Endpoint(IPv6.Parse(argumentContainer['l'].Value),argumentContainer['p'].IntegerValue),
new LoggingRouter(router));
server.Start();
}
2019-02-14 09:14:50 +01:00
}
}