ln.http/HTTPServerConnection.cs

158 lines
5.4 KiB
C#

using System;
using System.Net.Sockets;
using ln.types.threads;
using System.Net;
using ln.types;
using ln.logging;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
namespace ln.http
{
public delegate void HTTPServerConnectionEvent(HTTPServerConnection connection);
public class HTTPServerConnection : PoolJob
{
public static ThreadLocal<HTTPServerConnection> Current { get; } = new ThreadLocal<HTTPServerConnection>();
static HashSet<HTTPServerConnection> currentConnections = new HashSet<HTTPServerConnection>();
public static HTTPServerConnection[] CurrentConnections => currentConnections.ToArray();
public HTTPServer HTTPServer { get; }
public TcpClient TcpClient { get; }
public HttpRequest CurrentRequest { get; protected set; }
public event HTTPServerConnectionEvent AbortRequested;
public DateTime Created { get; }
public DateTime Interpreted { get; set; }
public DateTime Finished { get; set; }
public HTTPServerConnection(HTTPServer httpServer,TcpClient tcpClient)
{
HTTPServer = httpServer;
TcpClient = tcpClient;
Created = DateTime.Now;
}
public virtual HttpResponse GetResponse(HttpRequest httpRequest,HttpApplication httpApplication) => httpApplication.GetResponse(httpRequest);
public virtual void Abort()
{
if (AbortRequested != null)
AbortRequested(this);
}
public override void RunJob()
{
HTTPServerConnection saveCurrent = Current.Value;
Current.Value = this;
lock (currentConnections)
currentConnections.Add(this);
try
{
setState("reading http request");
HttpReader httpReader = new HttpReader(TcpClient.GetStream());
httpReader.Read();
if (!httpReader.Valid)
return;
HttpResponse response = null;
using (CurrentRequest = new HttpRequest(this.HTTPServer,httpReader, (IPEndPoint)TcpClient.Client.LocalEndPoint))
{
Interpreted = DateTime.Now;
try
{
HttpApplication application = HTTPServer.GetHttpApplication(new URI(CurrentRequest.BaseURI.ToString()));
application.Authenticate(CurrentRequest);
application.Authorize(CurrentRequest);
setState("handling http request");
response = GetResponse(CurrentRequest, application);
}
catch (Exception e)
{
setState("handling exception");
response = new HttpResponse(CurrentRequest, "text/plain");
response.StatusCode = 500;
response.ContentWriter.WriteLine("Exception caught: {0}", e);
}
setState("sending response");
if (response == null)
{
Logging.Log(LogLevel.DEBUG, "Request {0} returned no Response", CurrentRequest);
}
else
{
if (!response.HasCustomContentStream)
{
response.ContentWriter.Flush();
MemoryStream cstream = (MemoryStream)response.ContentStream;
cstream.Position = 0;
response.SetHeader("content-length", cstream.Length.ToString());
}
if (CurrentRequest.Session != null)
HTTPServer?.SessionCache?.ApplySessionID(response, CurrentRequest.Session);
response.AddCookie("LN_SEEN", DateTime.Now.ToString());
SendResponse(TcpClient.GetStream(), response);
TcpClient.Close();
Finished = DateTime.Now;
HTTPServer.Log(Created, (Finished - Created).TotalMilliseconds, CurrentRequest, response);
}
}
}
finally
{
Current.Value = saveCurrent;
lock (currentConnections)
currentConnections.Remove(this);
}
}
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();
}
}
}