ln.http/ln.http/Listener.cs

139 lines
4.0 KiB
C#

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using ln.http.exceptions;
using ln.threading;
namespace ln.http;
public class Listener : IDisposable
{
static string _http2_preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
static string _http2_preface1 = "PRI * HTTP/2.0";
protected Socket Socket;
public IPEndPoint LocalEndpoint => (IPEndPoint)Socket.LocalEndPoint;
public HttpVersion AcceptedHttpVersion { get; set; } = HttpVersion.ALL;
public HttpRouter HttpRouter { get; set; }
public Listener(HttpRouter httpRouter)
: this(httpRouter, new IPEndPoint(IPAddress.IPv6Any, 0))
{
}
public Listener(HttpRouter httpRouter, int port)
: this(httpRouter, new IPEndPoint(IPAddress.IPv6Any, port))
{
}
public Listener(HttpRouter httpRouter, IPAddress bind, int port)
: this(httpRouter, new IPEndPoint(bind, port))
{
}
public Listener(HttpRouter httpRouter, IPEndPoint bind)
{
HttpRouter = httpRouter;
Socket = new Socket(bind?.AddressFamily ?? AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
Socket.ExclusiveAddressUse = false;
if (bind is null)
bind = new IPEndPoint(IPAddress.IPv6Any, 0);
Socket.Bind(bind);
Socket.Listen();
Socket.ReceiveTimeout = 10000;
DynamicThreadPool.DefaultPool.Enqueue(ListenerLoop);
}
private void ListenerLoop()
{
while (Socket.IsBound)
{
Socket clientSocket = Socket.Accept();
ThreadPool.QueueUserWorkItem((state => ConnectionHandler(clientSocket)));
// DynamicThreadPool.DefaultPool.Enqueue(()=>ConnectionHandler(clientSocket));
}
}
private void ConnectionHandler(Socket clientSocket)
{
// clientSocket.ReceiveTimeout = 10000;
try
{
using (NetworkStream networkStream = new NetworkStream(clientSocket))
Accepted(clientSocket, networkStream);
}
catch (SocketException se)
{
}
catch (IOException e)
{
}
finally
{
clientSocket.Dispose();
}
}
protected virtual void Accepted(Socket connectedSocket, Stream connectionStream)
{
if (HttpConnection.ReadRequestLine(connectionStream, out string method, out string requestUri, out HttpVersion httpVersion))
{
if ((AcceptedHttpVersion & httpVersion) == HttpVersion.None)
return;
HttpConnection httpConnection;
switch (httpVersion)
{
case HttpVersion.HTTP2:
httpConnection = new Http2Connection(this, (IPEndPoint) connectedSocket.RemoteEndPoint, connectionStream, method, requestUri, httpVersion);
break;
case HttpVersion.HTTP10:
case HttpVersion.HTTP11:
default:
httpConnection = new Http1XConnection(this, (IPEndPoint) connectedSocket.RemoteEndPoint, connectionStream, method, requestUri, httpVersion);
break;
}
Accepted(httpConnection);
}
}
protected virtual void Accepted(HttpConnection httpConnection)
{
httpConnection.Run();
}
public void Dispatch(HttpRequestContext requestContext)
{
try
{
if (!HttpRouter.RouteRequest(requestContext, requestContext.Request.RequestUri.AbsolutePath))
throw new NotFoundException();
}
catch (HttpException httpException)
{
requestContext.Response = new HttpResponse(httpException.HttpStatusCode);
}
catch (Exception e)
{
requestContext.Response = HttpResponse.InternalServerError().Content(e);
}
}
public virtual void Dispose()
{
Socket?.Dispose();
}
}