ln.http/HTTPServer.cs

162 lines
4.6 KiB
C#

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<HTTPServer,TcpClient,HTTPServerConnection> CreateServerConnection { get; set; }
bool shutdown = false;
Dictionary<IPEndPoint, TcpListener> tcpListeners = new Dictionary<IPEndPoint, TcpListener>();
Dictionary<URI, HttpApplication> applications = new Dictionary<URI, HttpApplication>();
Dictionary<TcpListener, Thread> currentListenerThreads = new Dictionary<TcpListener, Thread>();
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);
}
}
}
}