ln.http/ln.http/HTTPServer.cs

253 lines
8.8 KiB
C#

using System;
using System.Collections.Generic;
using ln.logging;
using ln.threading;
using ln.http.listener;
using ln.http.connections;
using ln.http.exceptions;
using System.Threading;
using ln.type;
using ln.http.router;
using System.IO;
namespace ln.http
{
public class HTTPServer
{
public static int backlog = 5;
public static int defaultPort = 8080;
public static bool exclusivePortListener = false;
public static bool DefaultRouteAuthentication { get; set; } = false;
public HttpRouterDelegate Router { get; set; }
public bool IsRunning => !shutdown;
public Logger Logger { get; set; }
bool shutdown = false;
List<Listener> listeners = new List<Listener>();
public Listener[] Listeners => listeners.ToArray();
HashSet<Connection> currentConnections = new HashSet<Connection>();
public IEnumerable<Connection> CurrentConnections => currentConnections;
public HTTPServer()
{
Logger = Logger.Default;
}
public HTTPServer(HttpRouterDelegate router)
: this()
{
Router = router;
}
public HTTPServer(Listener listener, HttpRouterDelegate router)
: this(router)
{
AddListener(listener);
}
public HTTPServer(Endpoint endpoint, HttpRouterDelegate router)
: this(new HttpListener(endpoint), router) { }
public void AddListener(Listener listener)
{
listeners.Add(listener);
if (IsRunning)
StartListener(listener);
}
public void StartListener(Listener listener)
{
if (listener.IsOpen)
return;
listener.Open();
DynamicThreadPool.DefaultPool.Enqueue(
() => listener.AcceptMany(
(connection) => this.HandleConnection(connection))
);
}
public void StopListener(Listener listener)
{
listener.Close();
}
public void AddEndpoint(Endpoint endpoint)
{
AddListener(new HttpListener(endpoint));
}
public void Start()
{
foreach (Listener listener in listeners)
if (!listener.IsOpen) StartListener(listener);
}
public void Stop()
{
lock (this)
{
this.shutdown = true;
}
foreach (Listener listener in listeners)
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();
}
}
}
private void HandleConnection(Connection connection, bool dontClose = false)
{
lock (this.currentConnections)
currentConnections.Add(connection);
bool keepalive = false;
do
{
using (HttpRequest httpRequest = connection.ReadRequest(this))
{
if (httpRequest == null)
break;
HttpContext httpContext = new HttpContext()
{ Request = httpRequest, RoutableUri = httpRequest.RequestUri.AbsolutePath };
try
{
if (!Router(httpContext) && httpContext.Response is null)
httpContext.Response = HttpResponse.NotFound();
}
catch (Exception exception)
{
Logging.Log(exception);
if ((exception is HttpException httpException) && (httpException.HttpResponse != null))
httpContext.Response = httpException.HttpResponse;
else
httpContext.Response = HttpResponse.InternalServerError()
.Content(String.Format("An internal error occured ({0})", exception.ToString()));
}
httpContext.Response.WriteTo(connection.GetStream());
httpContext.Response?.ContentStream?.Dispose();
keepalive = httpContext.Response.GetHeader("connection", "keep-alive").Equals("keep-alive") && httpRequest
.GetRequestHeader("connection",
httpRequest.Protocol.Equals("HTTP/1.1") ? "keep-alive" : "close").Contains("keep-alive",
StringComparison.InvariantCultureIgnoreCase);
}
} while (keepalive && false);
lock (this.currentConnections)
currentConnections.Remove(connection);
connection.Close();
// new threading.Promise<HttpRequest>((resolve, reject)=>{
// resolve(connection.ReadRequest(this));
// })
// .Then((httpRequest)=>
// {
// if (httpRequest == null)
// return null;
// return Router.Route(new HttpRoutingContext(httpRequest),httpRequest);
// })
// // .Then((httpResponse)=>{
// // if (httpResponse == null)
// // throw new Exception("no response returned");
// // return httpResponse;
// // })
// .Catch((exception)=>{
// Logging.Log(exception);
// if ((exception is HttpException httpException) && (httpException.HttpResponse != null))
// return httpException.HttpResponse;
// return HttpResponse
// .InternalServerError()
// .Content(String.Format("An internal error occured ({0})", exception.ToString()));
// })
// .Then((httpResponse)=>
// {
// if (httpResponse == null)
// return false;
//
// httpResponse.SendResponse(connection.GetStream());
// httpResponse?.ContentStream?.Dispose();
// return httpResponse.GetHeader("connection", "keep-alive").Equals("keep-alive");
// })
// .Then((keepalive)=>{
// if (keepalive)
// HandleConnection(connection, true);
// })
// .Finally(()=>{
// lock (this.currentConnections)
// currentConnections.Remove(connection);
// connection.Close();
// })
// ;
}
/*
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(ref arguments);
HttpRouter router = new HttpRouter();
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 arguments)
{
StaticRouter staticRouter = new StaticRouter(path);
staticRouter.AddIndex("index.html");
staticRouter.AddIndex("index.htm");
router.AddSimpleRoute("/*", staticRouter);
}
if (argumentContainer['c'].Value != null)
router.AddSimpleRoute("/*", new RouterTarget((request) => router.Route(new HttpRoutingContext(request, argumentContainer['c'].Value), request)), 0);
HTTPServer server = new HTTPServer(new Endpoint(IPv6.Parse(argumentContainer['l'].Value),int.Parse(argumentContainer['p'].Value)),
new LoggingRouter(router));
server.Start();
}
*/
}
}