using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; using System.IO; using ln.http.resources.session; using ln.logging; using ln.types.threads; using ln.types; namespace ln.http { public class HTTPServer { public static int backlog = 5; public static int defaultPort = 8080; public static bool exclusivePortListener = false; public HttpApplication DefaultApplication { get; set; } public SessionCache SessionCache { get; set; } public bool IsRunning => (currentListenerThreads.Count > 0) || (threadPool.CurrentPoolSize > 0); public Func CreateServerConnection { get; set; } bool shutdown = false; Dictionary tcpListeners = new Dictionary(); Dictionary applications = new Dictionary(); Dictionary currentListenerThreads = new Dictionary(); DynamicPool threadPool; public HTTPServer() { CreateServerConnection = (HTTPServer httpServer, TcpClient tcpClient) => new HTTPServerConnection(httpServer, tcpClient); SessionCache = new SessionCache(); threadPool = new DynamicPool(1024); } public void AddEndpoint(IPEndPoint endpoint) { if (this.tcpListeners.ContainsKey(endpoint)) { throw new ArgumentOutOfRangeException(nameof(endpoint), "EndPoint already added"); } this.tcpListeners.Add(endpoint, new TcpListener(endpoint)); } public void RemoveEndpoint(IPEndPoint endpoint) { if (this.tcpListeners.ContainsKey(endpoint)) { this.tcpListeners[endpoint].Stop(); this.tcpListeners.Remove(endpoint); } } public void AddApplication(URI BaseURI, HttpApplication application) { applications[BaseURI] = application; } public HttpApplication GetHttpApplication(URI baseURI) { HttpApplication application = DefaultApplication; if (applications.ContainsKey(baseURI)) application = applications[baseURI]; return application; } public void Start() { threadPool.Start(); foreach (TcpListener tcpListener in this.tcpListeners.Values) { tcpListener.Start(backlog); Thread listenerThread = new Thread(() => AcceptConnections(tcpListener)); listenerThread.Start(); } } public void Stop() { lock (this) { this.shutdown = true; } foreach (TcpListener tcpListener in tcpListeners.Values) { tcpListener.Stop(); } foreach (HTTPServerConnection connection in HTTPServerConnection.CurrentConnections) connection?.Abort(); if (threadPool.State == PoolState.RUN) threadPool.Stop(true); } private void AcceptConnections(TcpListener tcpListener) { lock (this) { if (currentListenerThreads.ContainsKey(tcpListener)) { throw new Exception("HTTP listener thread already running"); } currentListenerThreads.Add(tcpListener, Thread.CurrentThread); } try { while (!this.shutdown) { AcceptConnection(tcpListener); } } catch (Exception e) { Logging.Log(LogLevel.ERROR, "HTTPServer: Listener thread caught exception {0}", e); Logging.Log(e); } lock (this) { currentListenerThreads.Remove(tcpListener); } } private void AcceptConnection(TcpListener tcpListener) { TcpClient tcpClient = tcpListener.AcceptTcpClient(); HTTPServerConnection connection = CreateServerConnection(this, tcpClient); if (connection == null) { Logging.Log(LogLevel.ERROR, "HTTPServer: CreateServerConnection(): returned null"); } else { this.threadPool.Enqueue(connection); } } } }