using System; using System.Collections.Generic; using ln.logging; using ln.types.threads; using ln.http.listener; using ln.http.connections; using ln.types.net; using System.Globalization; using ln.http.exceptions; using System.Threading; using ln.types; using ln.http.router; namespace ln.http { public class HTTPServer { public static int backlog = 5; public static int defaultPort = 8080; public static bool exclusivePortListener = false; public IHttpRouter Router { get; set; } public bool IsRunning => !shutdown && (threadPool.CurrentPoolSize > 0); public Logger Logger { get; set; } bool shutdown = false; List listeners = new List(); public Listener[] Listeners => listeners.ToArray(); DynamicPool threadPool; public DynamicPool ThreadPool => threadPool; HashSet currentConnections = new HashSet(); public IEnumerable CurrentConnections => currentConnections; public HTTPServer() { Logger = Logger.Default; threadPool = new DynamicPool(1024); } public HTTPServer(IHttpRouter router) : this() { Router = router; } public HTTPServer(Listener listener, IHttpRouter router) : this(router) { AddListener(listener); } public HTTPServer(Endpoint endpoint, IHttpRouter router) : this(new HttpListener(endpoint), router) { } public void AddListener(Listener listener) { listeners.Add(listener); if (IsRunning) StartListener(listener); } public void StartListener(Listener listener) { listener.Open(); threadPool.Enqueue( () => listener.AcceptMany( (connection) => threadPool.Enqueue( () => this.HandleConnection(connection) ) ) ); } public void StopListener(Listener listener) { listener.Close(); } public void AddEndpoint(Endpoint endpoint) { AddListener(new HttpListener(endpoint)); } public void Start() { threadPool.Start(); foreach (Listener listener in listeners) 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(); } } threadPool.Stop(true); } private void HandleConnection(Connection connection) { lock (this.currentConnections) currentConnections.Add(connection); try { HttpRequest httpRequest = null; bool keepAlive = true; try { do { httpRequest = connection.ReadRequest(this); if (httpRequest == null) break; HttpResponse response; try { response = Router.Route(new HttpRoutingContext(httpRequest),httpRequest); } 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; } response?.ContentStream?.Dispose(); } while (keepAlive); } catch (Exception e) { Logging.Log(e); if (httpRequest != null) { HttpResponse httpResponse = new HttpResponse(httpRequest); httpResponse.StatusCode = 500; httpResponse.ContentWriter.WriteLine("500 Internal Server Error"); httpResponse.ContentWriter.Flush(); connection.SendResponse(httpResponse); } } HttpRequest.ClearCurrent(); connection.GetStream().Close(); } finally { lock (currentConnections) currentConnections.Remove(connection); } } 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); } 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) 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),argumentContainer['p'].IntegerValue), new LoggingRouter(router)); server.Start(); } } }