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

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" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ln.bootstrap" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<None Update="bootstrap.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

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

View File

@ -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<HttpRouterDelegate> _routerDelegates = new HashSet<HttpRouterDelegate>();
public IEnumerable<HttpRouterDelegate> Routers => _routerDelegates;
bool shutdown = false;
public TextWriter LoggingWriter { get; set; }
List<Listener> listeners = new List<Listener>();
public Listener[] Listeners => listeners.ToArray();
HashSet<Connection> currentConnections = new HashSet<Connection>();
public IEnumerable<Connection> 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<HttpRequest>((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();
}
*/
}
}

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 class HttpRouter
public class HttpRouter : IDisposable
{
public event HttpFilterDelegate HttpFilters;
public event HttpAuthenticationDelegate AuthenticationDelegates;
private List<HttpMapping>[] _mappings = new List<HttpMapping>[5];
private HTTPServer _httpServer;
public HttpRouter()
{
for (int n = 0; n < 5; n++)
_mappings[n] = new List<HttpMapping>();
}
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<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)
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;
}
}
*/
}
}

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>
<PackageTags>http server</PackageTags>
<LangVersion>9</LangVersion>
<PackageVersion>0.5.2</PackageVersion>
<PackageVersion>0.6.0</PackageVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -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<string> indexNames = new List<string>();
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;
}
}
}

View File

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