253 lines
8.8 KiB
C#
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();
|
|
}
|
|
*/
|
|
|
|
}
|
|
}
|