127 lines
3.7 KiB
C#
127 lines
3.7 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
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(ConnectionHandler);
|
|
}
|
|
|
|
private void ConnectionHandler()
|
|
{
|
|
Socket clientSocket = Socket.Accept();
|
|
if (Socket.IsBound)
|
|
DynamicThreadPool.DefaultPool.Enqueue(ConnectionHandler);
|
|
|
|
clientSocket.ReceiveTimeout = 10000;
|
|
|
|
try
|
|
{
|
|
using (NetworkStream networkStream = new NetworkStream(clientSocket))
|
|
Accepted(clientSocket, networkStream);
|
|
}
|
|
finally
|
|
{
|
|
clientSocket.Close();
|
|
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();
|
|
}
|
|
|
|
} |