Added HttpListener.cs, HttpsListener.cs, etc. , changed structure to increase compatibility with DI frameworks

master
haraldwolff 2022-05-27 07:25:41 +02:00
parent caf1ba201f
commit a26c4e33b5
18 changed files with 424 additions and 637 deletions

View File

@ -1,7 +1,5 @@
using System; using System.Threading;
using System.Threading; using ln.bootstrap;
using ln.http;
using ln.http.listener;
using ln.http.router; using ln.http.router;
namespace ln.http.service namespace ln.http.service
@ -10,13 +8,7 @@ namespace ln.http.service
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
StaticRouter staticRouter = new StaticRouter("."); Bootstrap.Start();
HTTPServer httpServer = new HTTPServer(new HttpListener(8888), new LoggingRouter(staticRouter.Route).Route);
httpServer.Start();
lock (httpServer)
Monitor.Wait(httpServer);
} }
} }
} }

View File

@ -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
}
}
}

View File

@ -10,4 +10,14 @@
<ProjectReference Include="../ln.http/ln.http.csproj" /> <ProjectReference Include="../ln.http/ln.http.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="ln.bootstrap" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<None Update="bootstrap.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>

View File

@ -21,18 +21,18 @@ namespace ln.http.tests
if (server != null) if (server != null)
return; return;
HttpRouter testRouter = new HttpRouter(); server = new HTTPServer();
HttpRouter testRouter = new HttpRouter(server);
testRouter.Map(HttpMethod.ANY, "/controller/*", HttpRoutePriority.NORMAL, new TestApiController().Route); testRouter.Map(HttpMethod.ANY, "/controller/*", HttpRoutePriority.NORMAL, new TestApiController().Route);
StaticRouter staticRouter = new StaticRouter(AppContext.BaseDirectory); StaticRouter staticRouter = new StaticRouter(AppContext.BaseDirectory);
testRouter.Map(HttpMethod.ANY, "/static/*", staticRouter.Route); testRouter.Map(HttpMethod.ANY, "/static/*", staticRouter.Route);
HttpListener.DefaultPort = 0;
HttpListener httpListener = new HttpListener(server);
server = new HTTPServer(testRouter.Route); testPort = httpListener.LocalEndpoint.Port;
server.AddEndpoint(new Endpoint(IPv6.ANY,0));
server.Start();
testPort = server.Listeners[0].LocalEndpoint.Port;
TestContext.Error.WriteLine("Using Port {0}", testPort); TestContext.Error.WriteLine("Using Port {0}", testPort);
} }

View File

@ -1,259 +1,136 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using ln.logging; using ln.logging;
using ln.threading;
using ln.http.listener;
using ln.http.connections;
using ln.http.exceptions; using ln.http.exceptions;
using System.Threading;
using ln.type;
using ln.http.router;
using System.IO; using System.IO;
using System.Text;
using ln.protocols.helper;
namespace ln.http namespace ln.http
{ {
public class HTTPServer 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 static bool DefaultRouteAuthentication { get; set; } = false;
public HttpRouterDelegate Router { get; set; }
public bool IsRunning => !shutdown; private HashSet<HttpRouterDelegate> _routerDelegates = new HashSet<HttpRouterDelegate>();
public Logger Logger { get; set; } public IEnumerable<HttpRouterDelegate> Routers => _routerDelegates;
bool shutdown = false; public TextWriter LoggingWriter { get; set; }
List<Listener> listeners = new List<Listener>(); public HTTPServer() : this(Console.Out)
public Listener[] Listeners => listeners.ToArray();
HashSet<Connection> currentConnections = new HashSet<Connection>();
public IEnumerable<Connection> CurrentConnections => currentConnections;
public HTTPServer()
{ {
Logger = Logger.Default; }
public HTTPServer(TextWriter loggingWriter)
{
LoggingWriter = loggingWriter;
} }
public HTTPServer(HttpRouterDelegate router) public HTTPServer(HttpRouterDelegate router)
: this() : this()
{ {
Router = router; AddRouter(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);
} }
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) try
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)
{ {
this.shutdown = true; bool keepalive = false;
} do
foreach (Listener listener in listeners)
StopListener(listener);
for (int n = 0; n < 150; n++)
{
lock (currentConnections)
{ {
if (currentConnections.Count == 0) DateTime start = DateTime.Now;
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 };
try using (HttpRequest httpRequest = ReadRequest(httpConnection))
{ {
if (!Router(httpContext) && httpContext.Response is null) if (httpRequest == null)
httpContext.Response = HttpResponse.NotFound(); break;
}
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 httpContext = new HttpContext()
{ { Request = httpRequest, RoutableUri = httpRequest.RequestUri.AbsolutePath };
httpContext.Response.WriteTo(connection.GetStream());
httpContext.Response?.ContentStream?.Dispose();
}
catch (IOException ioexception)
{
break;
}
keepalive = httpContext.Response.GetHeader("connection", "keep-alive").Equals("keep-alive") && httpRequest try
.GetRequestHeader("connection", {
httpRequest.Protocol.Equals("HTTP/1.1") ? "keep-alive" : "close").Contains("keep-alive", foreach (var routerDelegate in _routerDelegates)
StringComparison.InvariantCultureIgnoreCase); {
} if (!routerDelegate(httpContext) && httpContext.Response is not null)
} while (keepalive && false); break;
}
lock (this.currentConnections)
currentConnections.Remove(connection); if (httpContext.Response is null)
connection.Close(); httpContext.Response = HttpResponse.NotFound();
}
// new threading.Promise<HttpRequest>((resolve, reject)=>{ catch (Exception exception)
// resolve(connection.ReadRequest(this)); {
// }) Logging.Log(exception);
// .Then((httpRequest)=> if ((exception is HttpException httpException) && (httpException.HttpResponse != null))
// { httpContext.Response = httpException.HttpResponse;
// if (httpRequest == null) else
// return null; httpContext.Response = HttpResponse.InternalServerError()
// return Router.Route(new HttpRoutingContext(httpRequest),httpRequest); .Content(String.Format("An internal error occured ({0})", exception.ToString()));
// }) }
// // .Then((httpResponse)=>{
// // if (httpResponse == null) try
// // throw new Exception("no response returned"); {
// // return httpResponse; httpContext.Response.WriteTo(httpConnection.ClientStream);
// // }) httpContext.Response?.ContentStream?.Dispose();
// .Catch((exception)=>{ }
// Logging.Log(exception); catch (IOException ioexception)
// if ((exception is HttpException httpException) && (httpException.HttpResponse != null)) {
// return httpException.HttpResponse; break;
// return HttpResponse }
// .InternalServerError()
// .Content(String.Format("An internal error occured ({0})", exception.ToString())); DateTime end = DateTime.Now;
// }) TimeSpan duration = end - start;
// .Then((httpResponse)=>
// { LoggingWriter.WriteLine("{0} {1} {2} {3} {4} {5} {6}",
// if (httpResponse == null) start,
// return false; end,
// duration,
// httpResponse.SendResponse(connection.GetStream()); httpContext.Response?.StatusCode.ToString() ?? "-",
// httpResponse?.ContentStream?.Dispose(); httpContext.AuthenticatedPrincipal?.ToString() ?? "-",
// return httpResponse.GetHeader("connection", "keep-alive").Equals("keep-alive"); httpContext.Request.Method,
// }) httpContext.Request.RequestUri
// .Then((keepalive)=>{ );
// if (keepalive)
// HandleConnection(connection, true); keepalive = httpContext.Response.GetHeader("connection", "keep-alive").Equals("keep-alive") &&
// }) httpRequest
// .Finally(()=>{ .GetRequestHeader("connection",
// lock (this.currentConnections) httpRequest.Protocol.Equals("HTTP/1.1") ? "keep-alive" : "close").Contains(
// currentConnections.Remove(connection); "keep-alive",
// connection.Close(); StringComparison.InvariantCultureIgnoreCase);
// }) }
// ; } while (keepalive);
}
finally
{
httpConnection.ClientStream.Close();
httpConnection.ClientStream.Dispose();
}
} }
/* private HttpRequest ReadRequest(HttpConnection httpConnection)
public static void StartSimpleServer(string[] arguments)
{ {
ArgumentContainer argumentContainer = new ArgumentContainer(new Argument[] try
{ {
new Argument('p',"port",8080), if (HttpLikeProtocolReader.ReadRequest(httpConnection.ClientStream, Encoding.UTF8, out Request request))
new Argument('l',"listen","127.0.0.1"), return new HttpRequest(this, request);
new Argument('c', "catch",null) return null;
}); } catch (IOException)
argumentContainer.Parse(ref arguments);
HttpRouter router = new HttpRouter();
router.AddSimpleRoute("/*", new RouterTarget((request) =>
{ {
HttpResponse response = new HttpResponse(request); return null;
response.StatusCode = 404; } catch (ConnectionClosedException)
response.SetHeader("content-type", "text/plain");
response.ContentWriter.WriteLine("404 Not Found");
response.ContentWriter.Flush();
return response;
}), -100);
foreach (String path in arguments)
{ {
StaticRouter staticRouter = new StaticRouter(path); return null;
staticRouter.AddIndex("index.html"); } catch (Exception e)
staticRouter.AddIndex("index.htm"); {
router.AddSimpleRoute("/*", staticRouter); 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();
} }
*/
} }
} }

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -13,19 +13,27 @@ namespace ln.http
public enum HttpRoutePriority : int { HIGHEST = 0, HIGH = 1, NORMAL = 2, LOW = 3, LOWEST = 4 } 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 HttpFilterDelegate HttpFilters;
public event HttpAuthenticationDelegate AuthenticationDelegates; public event HttpAuthenticationDelegate AuthenticationDelegates;
private List<HttpMapping>[] _mappings = new List<HttpMapping>[5]; private List<HttpMapping>[] _mappings = new List<HttpMapping>[5];
private HTTPServer _httpServer;
public HttpRouter() public HttpRouter()
{ {
for (int n = 0; n < 5; n++) for (int n = 0; n < 5; n++)
_mappings[n] = new List<HttpMapping>(); _mappings[n] = new List<HttpMapping>();
} }
public HttpRouter(HTTPServer httpServer)
: this()
{
_httpServer = httpServer;
httpServer.AddRouter(this);
}
public HttpMapping Map(HttpMethod httpMethod, string uri, HttpRouterDelegate routerDelegate) => public HttpMapping Map(HttpMethod httpMethod, string uri, HttpRouterDelegate routerDelegate) =>
Map(httpMethod, uri, HttpRoutePriority.NORMAL, routerDelegate); Map(httpMethod, uri, HttpRoutePriority.NORMAL, routerDelegate);
@ -165,107 +173,9 @@ namespace ln.http
} }
} }
public void Dispose()
/*
List<SimpleRoute> routes = new List<SimpleRoute>();
public SimpleRoute[] Routes => routes.ToArray();
*/
/*
public void AddSimpleRoute(string simpleRoute, Func<HttpRoutingContext, HttpRequest, HttpResponse> target) => AddSimpleRoute(simpleRoute, new RouterTarget(target));
public void AddSimpleRoute(string simpleRoute, Func<string, HttpRequest, HttpResponse> target) => AddSimpleRoute(simpleRoute, new RouterTarget(target));
public void AddSimpleRoute(string simpleRoute, Func<HttpRequest, HttpResponse> 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)
{ {
string[] parts = simpleRoute.Split(new char[] { '/' }); _httpServer?.RemoveRouter(this);
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);
} }
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;
}
}
*/
} }
} }

View File

@ -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<string, X509Certificate> _certificateCache = new Dictionary<string, X509Certificate>();
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;
}
}
}

View File

@ -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;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@
<Copyright>(c) 2020 Harald Wolff-Thobaben</Copyright> <Copyright>(c) 2020 Harald Wolff-Thobaben</Copyright>
<PackageTags>http server</PackageTags> <PackageTags>http server</PackageTags>
<LangVersion>9</LangVersion> <LangVersion>9</LangVersion>
<PackageVersion>0.5.2</PackageVersion> <PackageVersion>0.6.0</PackageVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -14,19 +14,42 @@ using ln.http.mime;
namespace ln.http.router 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<string> indexNames = new List<string>(); List<string> indexNames = new List<string>();
public String[] IndexNames => indexNames.ToArray(); 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) public StaticRouter(string path)
{ {
if (!Directory.Exists(path)) if (!Directory.Exists(path))
throw new FileNotFoundException(); throw new FileNotFoundException();
RootPath = Path.GetFullPath(path); RootPath = path;
AddIndex("index.html"); AddIndex("index.html");
AddIndex("index.htm"); AddIndex("index.htm");
@ -63,6 +86,12 @@ namespace ln.http.router
} }
return false; return false;
} }
public void Dispose()
{
_httpServer?.RemoveRouter(this.Route);
_httpServer = null;
}
} }
} }

View File

@ -1,12 +1,10 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading;
using ln.logging; using ln.logging;
using ln.http.exceptions; using ln.http.exceptions;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using ln.type; using ln.type;
using ln.http.connections;
namespace ln.http.websocket namespace ln.http.websocket
{ {