using System; using System.Collections.Generic; using ln.logging; using ln.threading; using ln.application; using ln.http.listener; using ln.http.connections; using System.Globalization; 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 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((HttpStatusCode)httpExc.StatusCode).Content(httpExc.Message); } if (response == null) response = HttpResponse.NotFound().Content(String.Format("The URI {0} could not be found on this server.", httpRequest.URI)); keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive"); response.SetHeader("connection", keepAlive ? "keep-alive" : "close"); SendResponse(connection.GetStream(), httpRequest, response); response?.ContentStream?.Dispose(); } while (keepAlive); } catch (Exception e) { Logging.Log(e); HttpResponse .InternalServerError() .Content(e) .SendResponse(connection.GetStream(), httpRequest); } HttpRequest.ClearCurrent(); connection.Close(); } finally { lock (currentConnections) currentConnections.Remove(connection); } } public static void SendResponse(Stream stream, HttpRequest request, HttpResponse response) { response.SendResponse(stream, request); /* request.FinishRequest(); response.SetHeader("Content-Length", response.ContentStream.Length.ToString()); StreamWriter streamWriter = new StreamWriter(stream); streamWriter.NewLine = "\r\n"; streamWriter.WriteLine("{0} {1} {2}", request.Protocol, (int)response.HttpStatusCode, response.HttpStatusCode.ToString()); foreach (String headerName in response.GetHeaderNames()) { streamWriter.WriteLine("{0}: {1}", headerName, response.GetHeader(headerName)); } foreach (HttpCookie httpCookie in response.Cookies) { streamWriter.WriteLine("Set-Cookie: {0}", httpCookie.ToString()); } streamWriter.WriteLine(); streamWriter.Flush(); response.ContentStream.Position = 0; response.ContentStream.CopyTo(stream); response.ContentStream.Close(); response.ContentStream.Dispose(); stream.Flush(); */ } 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); 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 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(); } } }