222 lines
6.5 KiB
C#
222 lines
6.5 KiB
C#
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<IPEndPoint, TcpListener> tcpListeners = new Dictionary<IPEndPoint, TcpListener>();
|
|
Dictionary<Uri, HttpApplication> applications = new Dictionary<Uri, HttpApplication>();
|
|
|
|
Dictionary<TcpListener, Thread> currentListenerThreads = new Dictionary<TcpListener, Thread>();
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|