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 _routerDelegates = new HashSet(); public IEnumerable 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(this, httpRequest); 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; } } } }