From a26c4e33b57e762dbf2ca59cb31085b79794132e Mon Sep 17 00:00:00 2001 From: haraldwolff Date: Fri, 27 May 2022 07:25:41 +0200 Subject: [PATCH] Added HttpListener.cs, HttpsListener.cs, etc. , changed structure to increase compatibility with DI frameworks --- ln.http.service/Program.cs | 14 +- ln.http.service/bootstrap.json | 24 ++ ln.http.service/ln.http.service.csproj | 10 + ln.http.tests/UnitTest1.cs | 14 +- ln.http/HTTPServer.cs | 319 ++++++++----------------- ln.http/HttpConnection.cs | 26 ++ ln.http/HttpListener.cs | 71 ++++++ ln.http/HttpRouter.cs | 114 +-------- ln.http/HttpsListener.cs | 140 +++++++++++ ln.http/connections/Connection.cs | 63 ----- ln.http/connections/HttpConnection.cs | 41 ---- ln.http/connections/HttpsConnection.cs | 46 ---- ln.http/listener/HttpListener.cs | 58 ----- ln.http/listener/HttpsListener.cs | 27 --- ln.http/listener/Listener.cs | 55 ----- ln.http/ln.http.csproj | 2 +- ln.http/router/StaticRouter.cs | 35 ++- ln.http/websocket/WebSocket.cs | 2 - 18 files changed, 424 insertions(+), 637 deletions(-) create mode 100644 ln.http.service/bootstrap.json create mode 100644 ln.http/HttpConnection.cs create mode 100644 ln.http/HttpListener.cs create mode 100644 ln.http/HttpsListener.cs delete mode 100644 ln.http/connections/Connection.cs delete mode 100644 ln.http/connections/HttpConnection.cs delete mode 100644 ln.http/connections/HttpsConnection.cs delete mode 100644 ln.http/listener/HttpListener.cs delete mode 100644 ln.http/listener/HttpsListener.cs delete mode 100644 ln.http/listener/Listener.cs diff --git a/ln.http.service/Program.cs b/ln.http.service/Program.cs index 41c8b75..f260492 100644 --- a/ln.http.service/Program.cs +++ b/ln.http.service/Program.cs @@ -1,7 +1,5 @@ -using System; -using System.Threading; -using ln.http; -using ln.http.listener; +using System.Threading; +using ln.bootstrap; using ln.http.router; namespace ln.http.service @@ -10,13 +8,7 @@ namespace ln.http.service { static void Main(string[] args) { - StaticRouter staticRouter = new StaticRouter("."); - HTTPServer httpServer = new HTTPServer(new HttpListener(8888), new LoggingRouter(staticRouter.Route).Route); - - httpServer.Start(); - - lock (httpServer) - Monitor.Wait(httpServer); + Bootstrap.Start(); } } } diff --git a/ln.http.service/bootstrap.json b/ln.http.service/bootstrap.json new file mode 100644 index 0000000..f7d302a --- /dev/null +++ b/ln.http.service/bootstrap.json @@ -0,0 +1,24 @@ +{ + "ln.http.HTTPServer, ln.http": { + "services": [ + ], + "properties": { + } + }, + "ln.http.HttpListener, ln.http": { + "services": [ + + ], + "properties": { + "DefaultPort": 8180 + } + }, + "ln.http.HttpsListener, ln.http": { + "services": [ + + ], + "properties": { + "DefaultPort": 8443 + } + } +} \ No newline at end of file diff --git a/ln.http.service/ln.http.service.csproj b/ln.http.service/ln.http.service.csproj index 602adda..c7696ba 100644 --- a/ln.http.service/ln.http.service.csproj +++ b/ln.http.service/ln.http.service.csproj @@ -10,4 +10,14 @@ + + + + + + + Always + + + diff --git a/ln.http.tests/UnitTest1.cs b/ln.http.tests/UnitTest1.cs index 629cac4..d3765d1 100644 --- a/ln.http.tests/UnitTest1.cs +++ b/ln.http.tests/UnitTest1.cs @@ -21,18 +21,18 @@ namespace ln.http.tests if (server != null) return; - HttpRouter testRouter = new HttpRouter(); + server = new HTTPServer(); + + HttpRouter testRouter = new HttpRouter(server); testRouter.Map(HttpMethod.ANY, "/controller/*", HttpRoutePriority.NORMAL, new TestApiController().Route); StaticRouter staticRouter = new StaticRouter(AppContext.BaseDirectory); testRouter.Map(HttpMethod.ANY, "/static/*", staticRouter.Route); + + HttpListener.DefaultPort = 0; + HttpListener httpListener = new HttpListener(server); - server = new HTTPServer(testRouter.Route); - server.AddEndpoint(new Endpoint(IPv6.ANY,0)); - - server.Start(); - - testPort = server.Listeners[0].LocalEndpoint.Port; + testPort = httpListener.LocalEndpoint.Port; TestContext.Error.WriteLine("Using Port {0}", testPort); } diff --git a/ln.http/HTTPServer.cs b/ln.http/HTTPServer.cs index 873a40c..c1c2fe8 100644 --- a/ln.http/HTTPServer.cs +++ b/ln.http/HTTPServer.cs @@ -1,259 +1,136 @@ using System; using System.Collections.Generic; using ln.logging; -using ln.threading; -using ln.http.listener; -using ln.http.connections; using ln.http.exceptions; -using System.Threading; -using ln.type; -using ln.http.router; using System.IO; +using System.Text; +using ln.protocols.helper; namespace ln.http { public class HTTPServer { - public static int backlog = 5; - public static int defaultPort = 8080; - public static bool exclusivePortListener = false; - public static bool DefaultRouteAuthentication { get; set; } = false; - - public HttpRouterDelegate Router { get; set; } - public bool IsRunning => !shutdown; - public Logger Logger { get; set; } + private HashSet _routerDelegates = new HashSet(); + public IEnumerable Routers => _routerDelegates; - bool shutdown = false; + public TextWriter LoggingWriter { get; set; } - List listeners = new List(); - public Listener[] Listeners => listeners.ToArray(); - - HashSet currentConnections = new HashSet(); - public IEnumerable CurrentConnections => currentConnections; - - public HTTPServer() + public HTTPServer() : this(Console.Out) { - Logger = Logger.Default; + } + public HTTPServer(TextWriter loggingWriter) + { + LoggingWriter = loggingWriter; } public HTTPServer(HttpRouterDelegate router) : this() { - Router = router; - } - public HTTPServer(Listener listener, HttpRouterDelegate router) - : this(router) - { - AddListener(listener); - } - public HTTPServer(Endpoint endpoint, HttpRouterDelegate router) - : this(new HttpListener(endpoint), router) { } - - public void AddListener(Listener listener) - { - listeners.Add(listener); - if (IsRunning) - StartListener(listener); + AddRouter(router); } - public void StartListener(Listener listener) + public void AddRouter(HttpRouter httpRouter) => AddRouter(httpRouter.Route); + public void AddRouter(HttpRouterDelegate routerDelegate) => _routerDelegates.Add(routerDelegate); + + public void RemoveRouter(HttpRouter httpRouter) => RemoveRouter(httpRouter.Route); + public void RemoveRouter(HttpRouterDelegate routerDelegate) => _routerDelegates.Remove(routerDelegate); + + public void Connection(HttpConnection httpConnection) { - if (listener.IsOpen) - return; - - listener.Open(); - - DynamicThreadPool.DefaultPool.Enqueue( - () => listener.AcceptMany( - (connection) => this.HandleConnection(connection)) - ); - } - - public void StopListener(Listener listener) - { - listener.Close(); - } - - - public void AddEndpoint(Endpoint endpoint) - { - AddListener(new HttpListener(endpoint)); - } - - public void Start() - { - foreach (Listener listener in listeners) - if (!listener.IsOpen) StartListener(listener); - } - - public void Stop() - { - lock (this) + try { - this.shutdown = true; - } - foreach (Listener listener in listeners) - StopListener(listener); - - for (int n = 0; n < 150; n++) - { - lock (currentConnections) + bool keepalive = false; + do { - if (currentConnections.Count == 0) - break; - if ((n % 20) == 0) - { - Logging.Log(LogLevel.INFO, "HTTPServer: still waiting for {0} connections to close", currentConnections.Count); - } - } - Thread.Sleep(100); - } - - lock (currentConnections) - { - foreach (Connection connection in currentConnections) - { - connection.Close(); - } - } - } - - private void HandleConnection(Connection connection, bool dontClose = false) - { - lock (this.currentConnections) - currentConnections.Add(connection); - - bool keepalive = false; - - do - { - using (HttpRequest httpRequest = connection.ReadRequest(this)) - { - if (httpRequest == null) - break; - - HttpContext httpContext = new HttpContext() - { Request = httpRequest, RoutableUri = httpRequest.RequestUri.AbsolutePath }; + DateTime start = DateTime.Now; - try + using (HttpRequest httpRequest = ReadRequest(httpConnection)) { - if (!Router(httpContext) && httpContext.Response is null) - httpContext.Response = HttpResponse.NotFound(); - } - catch (Exception exception) - { - Logging.Log(exception); - if ((exception is HttpException httpException) && (httpException.HttpResponse != null)) - httpContext.Response = httpException.HttpResponse; - else - httpContext.Response = HttpResponse.InternalServerError() - .Content(String.Format("An internal error occured ({0})", exception.ToString())); - } + if (httpRequest == null) + break; - try - { - httpContext.Response.WriteTo(connection.GetStream()); - httpContext.Response?.ContentStream?.Dispose(); - } - catch (IOException ioexception) - { - break; - } + HttpContext httpContext = new HttpContext() + { Request = httpRequest, RoutableUri = httpRequest.RequestUri.AbsolutePath }; - keepalive = httpContext.Response.GetHeader("connection", "keep-alive").Equals("keep-alive") && httpRequest - .GetRequestHeader("connection", - httpRequest.Protocol.Equals("HTTP/1.1") ? "keep-alive" : "close").Contains("keep-alive", - StringComparison.InvariantCultureIgnoreCase); - } - } while (keepalive && false); - - lock (this.currentConnections) - currentConnections.Remove(connection); - connection.Close(); - - // new threading.Promise((resolve, reject)=>{ - // resolve(connection.ReadRequest(this)); - // }) - // .Then((httpRequest)=> - // { - // if (httpRequest == null) - // return null; - // return Router.Route(new HttpRoutingContext(httpRequest),httpRequest); - // }) - // // .Then((httpResponse)=>{ - // // if (httpResponse == null) - // // throw new Exception("no response returned"); - // // return httpResponse; - // // }) - // .Catch((exception)=>{ - // Logging.Log(exception); - // if ((exception is HttpException httpException) && (httpException.HttpResponse != null)) - // return httpException.HttpResponse; - // return HttpResponse - // .InternalServerError() - // .Content(String.Format("An internal error occured ({0})", exception.ToString())); - // }) - // .Then((httpResponse)=> - // { - // if (httpResponse == null) - // return false; - // - // httpResponse.SendResponse(connection.GetStream()); - // httpResponse?.ContentStream?.Dispose(); - // return httpResponse.GetHeader("connection", "keep-alive").Equals("keep-alive"); - // }) - // .Then((keepalive)=>{ - // if (keepalive) - // HandleConnection(connection, true); - // }) - // .Finally(()=>{ - // lock (this.currentConnections) - // currentConnections.Remove(connection); - // connection.Close(); - // }) - // ; + try + { + foreach (var routerDelegate in _routerDelegates) + { + if (!routerDelegate(httpContext) && httpContext.Response is not null) + break; + } + + if (httpContext.Response is null) + httpContext.Response = HttpResponse.NotFound(); + } + catch (Exception exception) + { + Logging.Log(exception); + if ((exception is HttpException httpException) && (httpException.HttpResponse != null)) + httpContext.Response = httpException.HttpResponse; + else + httpContext.Response = HttpResponse.InternalServerError() + .Content(String.Format("An internal error occured ({0})", exception.ToString())); + } + + try + { + httpContext.Response.WriteTo(httpConnection.ClientStream); + httpContext.Response?.ContentStream?.Dispose(); + } + catch (IOException ioexception) + { + break; + } + + DateTime end = DateTime.Now; + TimeSpan duration = end - start; + + LoggingWriter.WriteLine("{0} {1} {2} {3} {4} {5} {6}", + start, + end, + duration, + httpContext.Response?.StatusCode.ToString() ?? "-", + httpContext.AuthenticatedPrincipal?.ToString() ?? "-", + httpContext.Request.Method, + httpContext.Request.RequestUri + ); + + keepalive = httpContext.Response.GetHeader("connection", "keep-alive").Equals("keep-alive") && + httpRequest + .GetRequestHeader("connection", + httpRequest.Protocol.Equals("HTTP/1.1") ? "keep-alive" : "close").Contains( + "keep-alive", + StringComparison.InvariantCultureIgnoreCase); + } + } while (keepalive); + } + finally + { + httpConnection.ClientStream.Close(); + httpConnection.ClientStream.Dispose(); + } } - /* - public static void StartSimpleServer(string[] arguments) + private HttpRequest ReadRequest(HttpConnection httpConnection) { - ArgumentContainer argumentContainer = new ArgumentContainer(new Argument[] + try { - new Argument('p',"port",8080), - new Argument('l',"listen","127.0.0.1"), - new Argument('c', "catch",null) - }); - - argumentContainer.Parse(ref arguments); - - HttpRouter router = new HttpRouter(); - router.AddSimpleRoute("/*", new RouterTarget((request) => + if (HttpLikeProtocolReader.ReadRequest(httpConnection.ClientStream, Encoding.UTF8, out Request request)) + return new HttpRequest(this, request); + return null; + } catch (IOException) { - HttpResponse response = new HttpResponse(request); - response.StatusCode = 404; - response.SetHeader("content-type", "text/plain"); - response.ContentWriter.WriteLine("404 Not Found"); - response.ContentWriter.Flush(); - return response; - }), -100); - - foreach (String path in arguments) + return null; + } catch (ConnectionClosedException) { - StaticRouter staticRouter = new StaticRouter(path); - staticRouter.AddIndex("index.html"); - staticRouter.AddIndex("index.htm"); - router.AddSimpleRoute("/*", staticRouter); + return null; + } catch (Exception e) + { + Logging.Log(e); + return null; } - - if (argumentContainer['c'].Value != null) - router.AddSimpleRoute("/*", new RouterTarget((request) => router.Route(new HttpRoutingContext(request, argumentContainer['c'].Value), request)), 0); - - HTTPServer server = new HTTPServer(new Endpoint(IPv6.Parse(argumentContainer['l'].Value),int.Parse(argumentContainer['p'].Value)), - new LoggingRouter(router)); - server.Start(); } - */ - } } diff --git a/ln.http/HttpConnection.cs b/ln.http/HttpConnection.cs new file mode 100644 index 0000000..937d8ed --- /dev/null +++ b/ln.http/HttpConnection.cs @@ -0,0 +1,26 @@ +using System.IO; +using System.Net; + +namespace ln.http +{ + public class HttpConnection + { + public Stream ClientStream { get; } + public IPEndPoint RemoteEndPoint { get; } + public IPEndPoint LocalEndPoint { get; } + + public bool IsEncrypted { get; } + + public HttpConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, Stream clientStream) + :this(localEndPoint, remoteEndPoint, clientStream, false) + {} + + public HttpConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, Stream clientStream, bool isEncrypted) + { + ClientStream = clientStream; + LocalEndPoint = localEndPoint; + RemoteEndPoint = remoteEndPoint; + IsEncrypted = isEncrypted; + } + } +} \ No newline at end of file diff --git a/ln.http/HttpListener.cs b/ln.http/HttpListener.cs new file mode 100644 index 0000000..69b99ac --- /dev/null +++ b/ln.http/HttpListener.cs @@ -0,0 +1,71 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace ln.http +{ + public class HttpListener : IDisposable + { + public static int DefaultPort = 80; + + + private IPEndPoint _localEndPoint; + private Socket _socket; + private HTTPServer _httpServer; + + public IPEndPoint LocalEndpoint => _localEndPoint; + + public HttpListener(HTTPServer httpServer) : this(httpServer, DefaultPort) + { + } + + public HttpListener(HTTPServer httpServer, int port) + { + _httpServer = httpServer; + _localEndPoint = new IPEndPoint(IPAddress.IPv6Any, port); + + Initialize(); + } + + private void Initialize() + { + _socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + _socket.ExclusiveAddressUse = false; + _socket.Bind(_localEndPoint); + _localEndPoint = (IPEndPoint)_socket.LocalEndPoint; + _socket.Listen(); + + ThreadPool.QueueUserWorkItem((state )=> ListenerThread()); + } + + private void ListenerThread() + { + while (_socket?.IsBound ?? false) + { + try + { + Socket clientSocket = _socket.Accept(); + _httpServer.Connection( + new HttpConnection( + _localEndPoint, + (IPEndPoint)clientSocket.RemoteEndPoint, + new NetworkStream(clientSocket) + ) + ); + } + catch + { + throw; + } + } + } + + public void Dispose() + { + _socket?.Close(); + _socket?.Dispose(); + _socket = null; + } + } +} \ No newline at end of file diff --git a/ln.http/HttpRouter.cs b/ln.http/HttpRouter.cs index a58ca7b..2591e57 100644 --- a/ln.http/HttpRouter.cs +++ b/ln.http/HttpRouter.cs @@ -13,19 +13,27 @@ namespace ln.http public enum HttpRoutePriority : int { HIGHEST = 0, HIGH = 1, NORMAL = 2, LOW = 3, LOWEST = 4 } - public class HttpRouter + public class HttpRouter : IDisposable { public event HttpFilterDelegate HttpFilters; public event HttpAuthenticationDelegate AuthenticationDelegates; private List[] _mappings = new List[5]; - + private HTTPServer _httpServer; + public HttpRouter() { for (int n = 0; n < 5; n++) _mappings[n] = new List(); } + public HttpRouter(HTTPServer httpServer) + : this() + { + _httpServer = httpServer; + httpServer.AddRouter(this); + } + public HttpMapping Map(HttpMethod httpMethod, string uri, HttpRouterDelegate routerDelegate) => Map(httpMethod, uri, HttpRoutePriority.NORMAL, routerDelegate); @@ -165,107 +173,9 @@ namespace ln.http } } - - /* - List routes = new List(); - public SimpleRoute[] Routes => routes.ToArray(); - */ - - - - - /* - public void AddSimpleRoute(string simpleRoute, Func target) => AddSimpleRoute(simpleRoute, new RouterTarget(target)); - public void AddSimpleRoute(string simpleRoute, Func target) => AddSimpleRoute(simpleRoute, new RouterTarget(target)); - public void AddSimpleRoute(string simpleRoute, Func target) => AddSimpleRoute(simpleRoute, new RouterTarget(target)); - public void AddSimpleRoute(string simpleRoute, IHttpRouter target) => AddSimpleRoute(simpleRoute, target, simpleRoute.Split('/').Length); - public void AddSimpleRoute(string simpleRoute, IHttpRouter target, int priority) + public void Dispose() { - string[] parts = simpleRoute.Split(new char[] { '/' }); - string[] reparts = parts.Select((part) => - { - if (part.StartsWith(":", StringComparison.InvariantCulture)) - if (part.EndsWith("*", StringComparison.InvariantCulture)) - return string.Format("(?<{0}>[^/]+)(?<_>/.*)?", part.Substring(1, part.Length - 2)); - else - return string.Format("(?<{0}>[^/]+)", part.Substring(1)); - else if (part.Equals("*")) - return string.Format("(?<_>.*)"); - else - return string.Format("{0}", part); - }).ToArray(); - - string reroute = string.Format("{0}\\/?$", string.Join("/", reparts)); - - AddRoute(reroute, target, priority); + _httpServer?.RemoveRouter(this); } - - public void AddRoute(String route, IHttpRouter target) => AddRoute(route, target, 0); - public void AddRoute(String route, IHttpRouter target,int priority) - { - lock (this) - { - routes.Add(new SimpleRoute(route, target, priority)); - routes.Sort((SimpleRoute a, SimpleRoute b) => b.Priority - a.Priority); - } - } - public void Remove(SimpleRoute simpleRoute) => routes.Remove(simpleRoute); - - public HttpResponse Route(HttpRoutingContext routingContext, HttpRequest httpRequest) - { - HttpResponse httpResponse; - - if (OnRoute != null) - { - foreach (RouterFilterDelegate filterDelegate in OnRoute.GetInvocationList()) - { - if (filterDelegate(this, ref routingContext, httpRequest, out httpResponse)) - return httpResponse; - } - } - - foreach (SimpleRoute simpleRoute in routes.ToArray()) - { - Match match = simpleRoute.Route.Match(routingContext.Path); - if (match.Success) - { - string residual = ""; - - foreach (Group group in match.Groups) - { - httpRequest?.SetParameter(group.Name, group.Value); - if (group.Name.Equals("_")) - if (group.Value.StartsWith("/", StringComparison.InvariantCulture)) - residual = group.Value; - else - residual = "/" + group.Value; - } - - httpResponse = simpleRoute.Target.Route(routingContext.Routed(residual), httpRequest); - if (httpResponse != null) - return httpResponse; - } - } - return null; - } - - - public class SimpleRoute - { - public int Priority { get; } - - public Regex Route { get; } - public IHttpRouter Target { get; } - - public SimpleRoute(string regex, IHttpRouter target) : this(regex, target, 0) { } - public SimpleRoute(string regex, IHttpRouter target,int priority) - { - Route = new Regex(regex); - Target = target; - Priority = priority; - } - } - */ - } } diff --git a/ln.http/HttpsListener.cs b/ln.http/HttpsListener.cs new file mode 100644 index 0000000..6bf3c28 --- /dev/null +++ b/ln.http/HttpsListener.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Threading; + +namespace ln.http +{ + public class HttpsListener : IDisposable + { + public static int DefaultPort = 443; + public string CertificateStore = Path.Combine(AppContext.BaseDirectory, "certs"); + + + private IPEndPoint _localEndPoint; + private Socket _socket; + private HTTPServer _httpServer; + + private X509Certificate _defaultCertificate; + private Dictionary _certificateCache = new Dictionary(); + + public HttpsListener(HTTPServer httpServer) : this(httpServer, DefaultPort) + { + } + + public HttpsListener(HTTPServer httpServer, int port) + { + _httpServer = httpServer; + _localEndPoint = new IPEndPoint(IPAddress.IPv6Any, port); + + Initialize(); + } + + private void Initialize() + { + _socket = new Socket(SocketType.Stream, ProtocolType.Tcp); + _socket.ExclusiveAddressUse = false; + _socket.Bind(_localEndPoint); + _socket.Listen(); + + if (File.Exists("localhost.crt")) + _defaultCertificate = X509Certificate.CreateFromCertFile("localhost.crt"); + else + _defaultCertificate = buildSelfSignedServerCertificate(); + + ThreadPool.QueueUserWorkItem((state )=> ListenerThread()); + } + + private void ListenerThread() + { + while (_socket?.IsBound ?? false) + { + try + { + Socket clientSocket = _socket.Accept(); + InitializeTLS(clientSocket); + } + catch + { + throw; + } + } + } + + private void InitializeTLS(Socket clientSocket) + { + SslStream sslStream = new SslStream(new NetworkStream(clientSocket), false, null, CertificateSelectionCallback); + sslStream.AuthenticateAsServer(_defaultCertificate, false, false); + + _httpServer.Connection( + new HttpConnection( + _localEndPoint, + (IPEndPoint)clientSocket.RemoteEndPoint, + sslStream + ) + ); + + } + + private X509Certificate CertificateSelectionCallback(object sender, string targethost, + X509CertificateCollection localcertificates, X509Certificate? remotecertificate, string[] acceptableissuers) + { + Console.Error.WriteLine("Certificate Selection for: {0}", targethost); + if (_certificateCache.TryGetValue(targethost, out X509Certificate localCertificate)) + { + return localCertificate; + } + else if (File.Exists(Path.Combine(CertificateStore ?? ".", String.Format("{0}.crt", targethost)))) + { + localCertificate = X509Certificate.CreateFromCertFile( + Path.Combine(CertificateStore ?? ".", String.Format("{0}.crt", targethost)) + ); + _certificateCache.Add(targethost, localCertificate); + return localCertificate; + } + + return _defaultCertificate; + } + + private X509Certificate2 buildSelfSignedServerCertificate() + { + SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder(); + sanBuilder.AddIpAddress(IPAddress.Loopback); + sanBuilder.AddIpAddress(IPAddress.IPv6Loopback); + sanBuilder.AddDnsName("localhost"); + sanBuilder.AddDnsName(Environment.MachineName); + + X500DistinguishedName distinguishedName = new X500DistinguishedName($"CN=localhost"); + + using (RSA rsa = RSA.Create(4096)) + { + var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add( + new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature , false)); + request.CertificateExtensions.Add( + new X509EnhancedKeyUsageExtension( + new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false)); + request.CertificateExtensions.Add(sanBuilder.Build()); + + var certificate= request.CreateSelfSigned(new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(3650))); + //certificate.FriendlyName = "localhost"; + using (FileStream fs = new FileStream("localhost.crt", FileMode.Create, FileAccess.Write)) + fs.Write(certificate.Export(X509ContentType.Pfx)); + + return certificate; + } + } + + public void Dispose() + { + _socket?.Close(); + _socket?.Dispose(); + _socket = null; + } + } +} \ No newline at end of file diff --git a/ln.http/connections/Connection.cs b/ln.http/connections/Connection.cs deleted file mode 100644 index 51395f1..0000000 --- a/ln.http/connections/Connection.cs +++ /dev/null @@ -1,63 +0,0 @@ -// /** -// * File: Connection.cs -// * Author: haraldwolff -// * -// * This file and it's content is copyrighted by the Author and / or copyright holder. -// * Any use wihtout proper permission is illegal and may lead to legal actions. -// * -// * -// **/ -using System; -using ln.type; -using System.IO; -using System.Text; -using ln.logging; -using ln.http.listener; -using ln.http.exceptions; -using ln.protocols.helper; - -namespace ln.http.connections -{ - public abstract class Connection : IDisposable - { - public Listener Listener { get; private set; } - - public abstract IPv6 RemoteHost { get; } - public abstract int RemotePort { get; } - - public abstract Stream GetStream(); - - public Connection(Listener listener) - { - Listener = listener; - } - - public virtual HttpRequest ReadRequest(HTTPServer httpServer) - { - try - { - if (HttpLikeProtocolReader.ReadRequest(GetStream(), Encoding.UTF8, out Request request)) - return new HttpRequest(httpServer, request); - return null; - } catch (IOException) - { - return null; - } catch (ConnectionClosedException) - { - return null; - } catch (Exception e) - { - Logging.Log(e); - return null; - } - } - - public abstract void Close(); - public virtual void Dispose() - { - Close(); - Listener = null; - } - - } -} diff --git a/ln.http/connections/HttpConnection.cs b/ln.http/connections/HttpConnection.cs deleted file mode 100644 index bd4c692..0000000 --- a/ln.http/connections/HttpConnection.cs +++ /dev/null @@ -1,41 +0,0 @@ -// /** -// * File: HttpConnection.cs -// * Author: haraldwolff -// * -// * This file and it's content is copyrighted by the Author and / or copyright holder. -// * Any use wihtout proper permission is illegal and may lead to legal actions. -// * -// * -// **/ -using System; -using System.IO; -using ln.type; -using System.Net.Sockets; -using ln.http.listener; - -namespace ln.http.connections -{ - public class HttpConnection : Connection - { - public TcpClient TcpClient { get; } - public Endpoint RemoteEndpoint { get; } - - public HttpConnection(Listener listener, TcpClient tcpClient) - :base(listener) - { - TcpClient = tcpClient; - RemoteEndpoint = new Endpoint(TcpClient.Client.RemoteEndPoint); - } - - public override IPv6 RemoteHost => RemoteEndpoint.Address; - public override int RemotePort => RemoteEndpoint.Port; - - public override Stream GetStream() => TcpClient.GetStream(); - - public override void Close() - { - TcpClient.Close(); - } - - } -} diff --git a/ln.http/connections/HttpsConnection.cs b/ln.http/connections/HttpsConnection.cs deleted file mode 100644 index aaa581a..0000000 --- a/ln.http/connections/HttpsConnection.cs +++ /dev/null @@ -1,46 +0,0 @@ -// /** -// * File: HttpsConnection.cs -// * Author: haraldwolff -// * -// * This file and it's content is copyrighted by the Author and / or copyright holder. -// * Any use wihtout proper permission is illegal and may lead to legal actions. -// * -// * -// **/ -using System; -using System.IO; -using ln.type; -using System.Net.Security; -using ln.http.listener; - -namespace ln.http.connections -{ - public class HttpsConnection : Connection - { - Connection Connection { get; } - SslStream sslStream { get; } - - public override IPv6 RemoteHost => Connection.RemoteHost; - public override int RemotePort => Connection.RemotePort; - - public HttpsConnection(Listener listener,Connection connection,LocalCertificateSelectionCallback localCertificateSelectionCallback) - :base(listener) - { - Connection = connection; - sslStream = new SslStream(connection.GetStream(),false, null, localCertificateSelectionCallback); - sslStream.AuthenticateAsServer(new System.Security.Cryptography.X509Certificates.X509Certificate()); - } - - public override HttpRequest ReadRequest(HTTPServer server) - { - throw new NotImplementedException(); - } - - public override Stream GetStream() => sslStream; - - public override void Close() - { - sslStream.Close(); - } - } -} diff --git a/ln.http/listener/HttpListener.cs b/ln.http/listener/HttpListener.cs deleted file mode 100644 index 4c505b5..0000000 --- a/ln.http/listener/HttpListener.cs +++ /dev/null @@ -1,58 +0,0 @@ -// /** -// * File: HttpListener.cs -// * Author: haraldwolff -// * -// * This file and it's content is copyrighted by the Author and / or copyright holder. -// * Any use wihtout proper permission is illegal and may lead to legal actions. -// * -// * -// **/ -using System.Net.Sockets; -using ln.type; -using ln.http.connections; - -namespace ln.http.listener -{ - public class HttpListener : Listener - { - - protected TcpListener tcpListener; - - public HttpListener(int port) :this(IPv6.ANY,port){} - public HttpListener(Endpoint endpoint) : this(endpoint.Address, endpoint.Port) {} - public HttpListener(IPv6 listen, int port) - : base(listen, port) - { - } - - public override Endpoint LocalEndpoint => new Endpoint(tcpListener.LocalEndpoint); - - public override Connection Accept() - { - return new HttpConnection(this,tcpListener.AcceptTcpClient()); - } - - public override bool IsOpen => (tcpListener != null); - - public override void Open() - { - tcpListener = new TcpListener(Listen, Port); - tcpListener.Start(); - } - public override void Close() - { - if (tcpListener != null) - { - tcpListener.Stop(); - tcpListener = null; - } - } - - public override void Dispose() - { - if (IsOpen) - Close(); - } - - } -} diff --git a/ln.http/listener/HttpsListener.cs b/ln.http/listener/HttpsListener.cs deleted file mode 100644 index 20b2f30..0000000 --- a/ln.http/listener/HttpsListener.cs +++ /dev/null @@ -1,27 +0,0 @@ -// /** -// * File: HttpsListener.cs -// * Author: haraldwolff -// * -// * This file and it's content is copyrighted by the Author and / or copyright holder. -// * Any use wihtout proper permission is illegal and may lead to legal actions. -// * -// * -// **/ -using ln.type; -using ln.http.connections; -using ln.http.cert; -namespace ln.http.listener -{ - public class HttpsListener : HttpListener - { - public HttpsListener(int port) : this(IPv6.ANY, port) { } - public HttpsListener(IPv6 listen, int port) : base(listen, port) { } - - public CertContainer CertContainer { get; set; } = new CertContainer(); - - public override Connection Accept() - { - return new HttpsConnection(this, base.Accept(),CertContainer.SelectCertificate); - } - } -} diff --git a/ln.http/listener/Listener.cs b/ln.http/listener/Listener.cs deleted file mode 100644 index e1691a1..0000000 --- a/ln.http/listener/Listener.cs +++ /dev/null @@ -1,55 +0,0 @@ -// /** -// * File: Listener.cs -// * Author: haraldwolff -// * -// * This file and it's content is copyrighted by the Author and / or copyright holder. -// * Any use wihtout proper permission is illegal and may lead to legal actions. -// * -// * -// **/ -using System; -using System.Net.Sockets; -using ln.http.connections; -using ln.logging; -using ln.type; -namespace ln.http.listener -{ - public abstract class Listener : IDisposable - { - public IPv6 Listen { get; } - public int Port { get; } - - public virtual Endpoint LocalEndpoint => new Endpoint(Listen, Port); - - public abstract bool IsOpen { get; } - - protected Listener(IPv6 listen, int port) - { - Listen = listen; - Port = port; - } - - public virtual void AcceptMany(Action handler) - { - while (IsOpen) - { - try - { - handler(Accept()); - } - catch (SocketException soe) - { - if (IsOpen) - Logging.Log(soe); - } - } - } - - public abstract void Open(); - public abstract void Close(); - - public abstract Connection Accept(); - - public abstract void Dispose(); - } -} diff --git a/ln.http/ln.http.csproj b/ln.http/ln.http.csproj index 9fd1d76..60ef035 100644 --- a/ln.http/ln.http.csproj +++ b/ln.http/ln.http.csproj @@ -10,7 +10,7 @@ (c) 2020 Harald Wolff-Thobaben http server 9 - 0.5.2 + 0.6.0 diff --git a/ln.http/router/StaticRouter.cs b/ln.http/router/StaticRouter.cs index 6010938..fd41830 100644 --- a/ln.http/router/StaticRouter.cs +++ b/ln.http/router/StaticRouter.cs @@ -14,19 +14,42 @@ using ln.http.mime; namespace ln.http.router { - public class StaticRouter + public class StaticRouter : IDisposable { - public String RootPath { get; } + private string _rootPath; + public String RootPath + { + get => _rootPath; + private set + { + _rootPath = Path.GetFullPath(value); + } + } List indexNames = new List(); public String[] IndexNames => indexNames.ToArray(); + private HTTPServer _httpServer; + + + public StaticRouter(HTTPServer httpServer) + { + _httpServer = httpServer; + httpServer.AddRouter(this.Route); + } + + public StaticRouter(HTTPServer httpServer, string path) + :this(path) + { + _httpServer = httpServer; + httpServer.AddRouter(this.Route); + } public StaticRouter(string path) { if (!Directory.Exists(path)) throw new FileNotFoundException(); - RootPath = Path.GetFullPath(path); + RootPath = path; AddIndex("index.html"); AddIndex("index.htm"); @@ -63,6 +86,12 @@ namespace ln.http.router } return false; } + + public void Dispose() + { + _httpServer?.RemoveRouter(this.Route); + _httpServer = null; + } } } diff --git a/ln.http/websocket/WebSocket.cs b/ln.http/websocket/WebSocket.cs index 1823d72..aa2219d 100644 --- a/ln.http/websocket/WebSocket.cs +++ b/ln.http/websocket/WebSocket.cs @@ -1,12 +1,10 @@ using System; using System.IO; -using System.Threading; using ln.logging; using ln.http.exceptions; using System.Security.Cryptography; using System.Text; using ln.type; -using ln.http.connections; namespace ln.http.websocket {