Added HttpListener.cs, HttpsListener.cs, etc. , changed structure to increase compatibility with DI frameworks
parent
caf1ba201f
commit
a26c4e33b5
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue