ln.http/HTTPServer.cs

174 lines
5.0 KiB
C#

using System;
using System.Collections.Generic;
using ln.logging;
using ln.types.threads;
using ln.types;
using ln.http.listener;
using ln.http.connections;
using ln.types.net;
using ln.http.cert;
using System.Globalization;
using System.Net.Sockets;
using ln.http.session;
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 => !shutdown && (threadPool.CurrentPoolSize > 0);
public Func<HTTPServer,HTTPServerConnection> CreateServerConnection { get; set; }
public Logger Logger { get; set; }
bool shutdown = false;
List<Listener> listeners = new List<Listener>();
public Listener[] Listeners => listeners.ToArray();
Dictionary<URI, HttpApplication> applications = new Dictionary<URI, HttpApplication>();
DynamicPool threadPool;
public DynamicPool ThreadPool => threadPool;
public HTTPServer()
{
Logger = Logger.Default;
SessionCache = new SessionCache();
threadPool = new DynamicPool(1024);
}
public void AddListener(Listener listener)
{
listeners.Add(listener);
if (IsRunning)
StartListener(listener);
}
public void StartListener(Listener listener)
{
listener.Open();
threadPool.Enqueue(
() => listener.AcceptMany(
(connection) => threadPool.Enqueue(
() => this.HandleConnection(connection)
)
)
);
}
public void StopListener(Listener listener)
{
listener.Close();
}
public void AddEndpoint(Endpoint endpoint)
{
AddListener(new HttpListener(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 (Listener listener in listeners)
StartListener(listener);
}
public void Stop()
{
lock (this)
{
this.shutdown = true;
}
foreach (Listener listener in listeners)
StopListener(listener);
threadPool.Stop(true);
}
private void HandleConnection(Connection connection)
{
HttpRequest httpRequest = null;
bool keepAlive = true;
try
{
do
{
httpRequest = connection.ReadRequest(this);
if (httpRequest == null)
break;
httpRequest.MakeCurrent();
HttpApplication application = GetHttpApplication(new URI(httpRequest.BaseURI.ToString()));
application.Authenticate(httpRequest);
application.Authorize(httpRequest);
HttpResponse response = application.GetResponse(httpRequest);
if (response != null)
{
keepAlive = httpRequest.GetRequestHeader("connection", "keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
connection.SendResponse(response);
}
else
{
keepAlive = false;
}
} while (keepAlive);
}
catch (Exception e)
{
Logging.Log(e);
if (httpRequest != null)
{
HttpResponse httpResponse = new HttpResponse(httpRequest);
httpResponse.StatusCode = 500;
httpResponse.ContentWriter.WriteLine("500 Internal Server Error");
connection.SendResponse(httpResponse);
}
}
HttpRequest.ClearCurrent();
connection.GetStream().Close();
}
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);
}
}
}