140 lines
5.4 KiB
C#
140 lines
5.4 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using ln.logging;
|
|||
|
using ln.http.exceptions;
|
|||
|
using System.IO;
|
|||
|
using System.Text;
|
|||
|
using System.Threading;
|
|||
|
using ln.protocols.helper;
|
|||
|
|
|||
|
namespace ln.http
|
|||
|
{
|
|||
|
public class HttpServer
|
|||
|
{
|
|||
|
public static bool DefaultRouteAuthentication { get; set; } = false;
|
|||
|
|
|||
|
private HashSet<HttpRouterDelegate> _routerDelegates = new HashSet<HttpRouterDelegate>();
|
|||
|
public IEnumerable<HttpRouterDelegate> Routers => _routerDelegates;
|
|||
|
|
|||
|
public TextWriter LoggingWriter { get; set; }
|
|||
|
|
|||
|
public HttpServer() : this(Console.Out)
|
|||
|
{
|
|||
|
}
|
|||
|
public HttpServer(TextWriter loggingWriter)
|
|||
|
{
|
|||
|
LoggingWriter = loggingWriter;
|
|||
|
}
|
|||
|
public HttpServer(HttpRouterDelegate router)
|
|||
|
: this()
|
|||
|
{
|
|||
|
AddRouter(router);
|
|||
|
}
|
|||
|
|
|||
|
public void AddRouter(HttpRouter httpRouter) => AddRouter(httpRouter.Route);
|
|||
|
public void AddRouter(HttpRouterDelegate routerDelegate) => _routerDelegates.Add(routerDelegate);
|
|||
|
|
|||
|
public void RemoveRouter(HttpRouter httpRouter) => RemoveRouter(httpRouter.Route);
|
|||
|
public void RemoveRouter(HttpRouterDelegate routerDelegate) => _routerDelegates.Remove(routerDelegate);
|
|||
|
|
|||
|
public void Connection(HttpConnection httpConnection) =>
|
|||
|
ThreadPool.QueueUserWorkItem((state => ConnectionWorker(httpConnection)));
|
|||
|
public void ConnectionWorker(HttpConnection httpConnection)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
bool keepalive = false;
|
|||
|
do
|
|||
|
{
|
|||
|
DateTime start = DateTime.Now;
|
|||
|
|
|||
|
using (HttpRequest httpRequest = ReadRequest(httpConnection))
|
|||
|
{
|
|||
|
if (httpRequest == null)
|
|||
|
break;
|
|||
|
|
|||
|
HttpContext httpContext = new HttpContext()
|
|||
|
{ Request = httpRequest, RoutableUri = httpRequest.RequestUri.AbsolutePath };
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
foreach (var routerDelegate in _routerDelegates)
|
|||
|
{
|
|||
|
if (!routerDelegate(httpContext) && httpContext.Response is not null)
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (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()));
|
|||
|
}
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
httpContext.Response.WriteTo(httpConnection.ClientStream);
|
|||
|
httpContext.Response?.ContentStream?.Dispose();
|
|||
|
}
|
|||
|
catch (IOException ioexception)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
DateTime end = DateTime.Now;
|
|||
|
TimeSpan duration = end - start;
|
|||
|
|
|||
|
LoggingWriter.WriteLine("{0} {1} {2} {3} {4} {5} {6}",
|
|||
|
start,
|
|||
|
end,
|
|||
|
duration,
|
|||
|
httpContext.Response?.StatusCode.ToString() ?? "-",
|
|||
|
httpContext.AuthenticatedPrincipal?.ToString() ?? "-",
|
|||
|
httpContext.Request.Method,
|
|||
|
httpContext.Request.RequestUri
|
|||
|
);
|
|||
|
|
|||
|
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);
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
httpConnection.ClientStream.Close();
|
|||
|
httpConnection.ClientStream.Dispose();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private HttpRequest ReadRequest(HttpConnection httpConnection)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
if (HttpLikeProtocolReader.ReadRequest(httpConnection.ClientStream, Encoding.UTF8, out Request request))
|
|||
|
return new HttpRequest(this, request);
|
|||
|
return null;
|
|||
|
} catch (IOException)
|
|||
|
{
|
|||
|
return null;
|
|||
|
} catch (ConnectionClosedException)
|
|||
|
{
|
|||
|
return null;
|
|||
|
} catch (Exception e)
|
|||
|
{
|
|||
|
Logging.Log(e);
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|