2019-02-14 09:14:50 +01:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Sockets;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.IO;
|
2019-02-26 22:00:37 +01:00
|
|
|
|
using ln.http.resources.session;
|
2019-03-13 14:17:46 +01:00
|
|
|
|
using ln.logging;
|
|
|
|
|
using ln.types.threads;
|
2019-09-11 09:29:05 +02:00
|
|
|
|
using ln.types;
|
2019-10-17 10:17:14 +02:00
|
|
|
|
using System.Globalization;
|
2019-02-14 09:14:50 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; }
|
|
|
|
|
|
2019-03-13 14:17:46 +01:00
|
|
|
|
public SessionCache SessionCache { get; set; }
|
|
|
|
|
|
2019-03-15 15:35:35 +01:00
|
|
|
|
public bool IsRunning => (currentListenerThreads.Count > 0) || (threadPool.CurrentPoolSize > 0);
|
2019-03-13 14:17:46 +01:00
|
|
|
|
|
2019-09-11 09:29:05 +02:00
|
|
|
|
public Func<HTTPServer,TcpClient,HTTPServerConnection> CreateServerConnection { get; set; }
|
2019-03-13 14:17:46 +01:00
|
|
|
|
|
2019-10-17 10:17:14 +02:00
|
|
|
|
public Logger Logger { get; set; }
|
|
|
|
|
|
2019-02-14 09:14:50 +01:00
|
|
|
|
bool shutdown = false;
|
|
|
|
|
|
2019-10-17 10:17:14 +02:00
|
|
|
|
|
|
|
|
|
|
2019-02-14 09:14:50 +01:00
|
|
|
|
Dictionary<IPEndPoint, TcpListener> tcpListeners = new Dictionary<IPEndPoint, TcpListener>();
|
2019-09-11 09:29:05 +02:00
|
|
|
|
Dictionary<URI, HttpApplication> applications = new Dictionary<URI, HttpApplication>();
|
2019-02-14 09:14:50 +01:00
|
|
|
|
|
2019-03-13 14:17:46 +01:00
|
|
|
|
Dictionary<TcpListener, Thread> currentListenerThreads = new Dictionary<TcpListener, Thread>();
|
2019-02-26 22:00:37 +01:00
|
|
|
|
|
2019-09-11 09:29:05 +02:00
|
|
|
|
DynamicPool threadPool;
|
2019-02-26 22:00:37 +01:00
|
|
|
|
|
2019-02-14 09:14:50 +01:00
|
|
|
|
public HTTPServer()
|
|
|
|
|
{
|
2019-10-17 10:17:14 +02:00
|
|
|
|
Logger = Logger.Default;
|
2019-09-11 09:29:05 +02:00
|
|
|
|
CreateServerConnection = (HTTPServer httpServer, TcpClient tcpClient) => new HTTPServerConnection(httpServer, tcpClient);
|
|
|
|
|
|
2019-02-26 22:00:37 +01:00
|
|
|
|
SessionCache = new SessionCache();
|
2019-09-11 09:29:05 +02:00
|
|
|
|
|
|
|
|
|
threadPool = new DynamicPool(1024);
|
2019-02-14 09:14:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-11 09:29:05 +02:00
|
|
|
|
public void AddApplication(URI BaseURI, HttpApplication application)
|
2019-02-14 09:14:50 +01:00
|
|
|
|
{
|
|
|
|
|
applications[BaseURI] = application;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-11 09:29:05 +02:00
|
|
|
|
public HttpApplication GetHttpApplication(URI baseURI)
|
|
|
|
|
{
|
|
|
|
|
HttpApplication application = DefaultApplication;
|
|
|
|
|
|
|
|
|
|
if (applications.ContainsKey(baseURI))
|
|
|
|
|
application = applications[baseURI];
|
|
|
|
|
|
|
|
|
|
return application;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-02-14 09:14:50 +01:00
|
|
|
|
public void Start()
|
|
|
|
|
{
|
2019-09-11 09:29:05 +02:00
|
|
|
|
threadPool.Start();
|
|
|
|
|
|
2019-02-14 09:14:50 +01:00
|
|
|
|
foreach (TcpListener tcpListener in this.tcpListeners.Values)
|
|
|
|
|
{
|
|
|
|
|
tcpListener.Start(backlog);
|
2019-03-13 14:17:46 +01:00
|
|
|
|
|
|
|
|
|
Thread listenerThread = new Thread(() => AcceptConnections(tcpListener));
|
|
|
|
|
listenerThread.Start();
|
2019-02-14 09:14:50 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Stop()
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
this.shutdown = true;
|
2019-03-14 08:35:54 +01:00
|
|
|
|
}
|
2019-03-13 14:17:46 +01:00
|
|
|
|
|
2019-03-14 08:35:54 +01:00
|
|
|
|
foreach (TcpListener tcpListener in tcpListeners.Values)
|
|
|
|
|
{
|
|
|
|
|
tcpListener.Stop();
|
|
|
|
|
}
|
2019-03-13 14:17:46 +01:00
|
|
|
|
|
2019-09-11 09:29:05 +02:00
|
|
|
|
foreach (HTTPServerConnection connection in HTTPServerConnection.CurrentConnections)
|
2019-10-11 12:38:07 +02:00
|
|
|
|
connection?.Abort();
|
2019-03-13 14:17:46 +01:00
|
|
|
|
|
2019-10-11 12:38:07 +02:00
|
|
|
|
if (threadPool.State == PoolState.RUN)
|
|
|
|
|
threadPool.Stop(true);
|
2019-02-14 09:14:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AcceptConnections(TcpListener tcpListener)
|
|
|
|
|
{
|
2019-03-13 14:17:46 +01:00
|
|
|
|
lock (this)
|
2019-02-14 09:14:50 +01:00
|
|
|
|
{
|
2019-03-13 14:17:46 +01:00
|
|
|
|
if (currentListenerThreads.ContainsKey(tcpListener))
|
2019-02-14 09:14:50 +01:00
|
|
|
|
{
|
2019-03-13 14:17:46 +01:00
|
|
|
|
throw new Exception("HTTP listener thread already running");
|
2019-02-14 09:14:50 +01:00
|
|
|
|
}
|
2019-03-13 14:17:46 +01:00
|
|
|
|
currentListenerThreads.Add(tcpListener, Thread.CurrentThread);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
while (!this.shutdown)
|
|
|
|
|
{
|
|
|
|
|
AcceptConnection(tcpListener);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-03 12:56:33 +02:00
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
2019-03-13 14:17:46 +01:00
|
|
|
|
{
|
2019-08-03 12:56:33 +02:00
|
|
|
|
Logging.Log(LogLevel.ERROR, "HTTPServer: Listener thread caught exception {0}", e);
|
2019-03-13 14:17:46 +01:00
|
|
|
|
Logging.Log(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
currentListenerThreads.Remove(tcpListener);
|
2019-02-14 09:14:50 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AcceptConnection(TcpListener tcpListener)
|
|
|
|
|
{
|
|
|
|
|
TcpClient tcpClient = tcpListener.AcceptTcpClient();
|
|
|
|
|
|
2019-09-11 09:29:05 +02:00
|
|
|
|
HTTPServerConnection connection = CreateServerConnection(this, tcpClient);
|
|
|
|
|
if (connection == null)
|
2019-02-14 09:14:50 +01:00
|
|
|
|
{
|
2019-09-11 09:29:05 +02:00
|
|
|
|
Logging.Log(LogLevel.ERROR, "HTTPServer: CreateServerConnection(): returned null");
|
2019-02-14 09:14:50 +01:00
|
|
|
|
}
|
2019-08-03 12:56:33 +02:00
|
|
|
|
else
|
2019-02-26 22:00:37 +01:00
|
|
|
|
{
|
2019-09-11 09:29:05 +02:00
|
|
|
|
this.threadPool.Enqueue(connection);
|
2019-02-26 22:00:37 +01:00
|
|
|
|
}
|
2019-08-03 12:56:33 +02:00
|
|
|
|
}
|
2019-02-26 22:00:37 +01:00
|
|
|
|
|
2019-10-17 10:17:14 +02:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-14 09:14:50 +01:00
|
|
|
|
}
|
|
|
|
|
}
|