using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; using System.IO; using System.Text; using ln.http.resources.session; using ln.logging; using ln.types.threads; 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); bool shutdown = false; Dictionary tcpListeners = new Dictionary(); Dictionary applications = new Dictionary(); Dictionary currentListenerThreads = new Dictionary(); Pool threadPool = new Pool(32); public HTTPServer() { SessionCache = new SessionCache(); } 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 void 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(); } threadPool.Close(); while (IsRunning) { Thread.Sleep(50); } } 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(); this.threadPool.Enqueue(() => HandleConnection(tcpClient)); } private void HandleConnection(TcpClient tcpClient) { HttpReader httpReader = new HttpReader(tcpClient.GetStream()); httpReader.Read(); HttpRequest httpRequest = new HttpRequest(httpReader, (IPEndPoint)tcpClient.Client.LocalEndPoint); HttpResponse response = null; httpRequest.ApplySession(SessionCache); try { HttpApplication application = DefaultApplication; if (applications.ContainsKey(httpRequest.BaseURI)) application = applications[httpRequest.BaseURI]; application.Authenticate(httpRequest); application.Authorize(httpRequest); response = application.GetResponse(httpRequest); } catch (Exception e) { response = new HttpResponse(httpRequest, "text/plain"); response.StatusCode = 500; response.ContentWriter.WriteLine("Exception caught: {0}", e); } if (response == null) { Logging.Log(LogLevel.DEBUG, "Request {0} returned no Response", httpRequest); } else { if (!response.HasCustomContentStream) { response.ContentWriter.Flush(); MemoryStream cstream = (MemoryStream)response.ContentStream; cstream.Position = 0; response.SetHeader("content-length", cstream.Length.ToString()); } if (SessionCache != null) { SessionCache.ApplySessionID(response, httpRequest.Session); } response.AddCookie("LN_SEEN", DateTime.Now.ToString()); SendResponse(tcpClient.GetStream(), response); tcpClient.Close(); } } public static void SendResponse(Stream stream, HttpResponse response) { StreamWriter streamWriter = new StreamWriter(stream); streamWriter.NewLine = "\r\n"; streamWriter.WriteLine("{0} {1} {2}", response.HttpRequest.Protocol, response.StatusCode, response.StatusMessage); 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.CopyTo(stream); response.ContentStream.Close(); response.ContentStream.Dispose(); streamWriter.Flush(); } } }