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(); } }