WIP
parent
89dddbc07b
commit
79b3eabde6
150
HTTPServer.cs
150
HTTPServer.cs
|
@ -1,14 +1,15 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.IO;
|
|
||||||
using ln.http.resources.session;
|
using ln.http.resources.session;
|
||||||
using ln.logging;
|
using ln.logging;
|
||||||
using ln.types.threads;
|
using ln.types.threads;
|
||||||
using ln.types;
|
using ln.types;
|
||||||
|
using ln.http.listener;
|
||||||
|
using ln.http.connections;
|
||||||
|
using ln.types.net;
|
||||||
|
using ln.http.cert;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
|
@ -20,53 +21,59 @@ namespace ln.http
|
||||||
public static bool exclusivePortListener = false;
|
public static bool exclusivePortListener = false;
|
||||||
|
|
||||||
public HttpApplication DefaultApplication { get; set; }
|
public HttpApplication DefaultApplication { get; set; }
|
||||||
|
|
||||||
public SessionCache SessionCache { get; set; }
|
public SessionCache SessionCache { get; set; }
|
||||||
|
|
||||||
public bool IsRunning => (currentListenerThreads.Count > 0) || (threadPool.CurrentPoolSize > 0);
|
public bool IsRunning => !shutdown && (threadPool.CurrentPoolSize > 0);
|
||||||
|
public Func<HTTPServer,HTTPServerConnection> CreateServerConnection { get; set; }
|
||||||
public Func<HTTPServer,TcpClient,HTTPServerConnection> CreateServerConnection { get; set; }
|
|
||||||
|
|
||||||
public Logger Logger { get; set; }
|
public Logger Logger { get; set; }
|
||||||
|
|
||||||
bool shutdown = false;
|
bool shutdown = false;
|
||||||
|
|
||||||
|
List<Listener> listeners = new List<Listener>();
|
||||||
|
public Listener[] Listeners => listeners.ToArray();
|
||||||
|
|
||||||
|
|
||||||
Dictionary<IPEndPoint, TcpListener> tcpListeners = new Dictionary<IPEndPoint, TcpListener>();
|
|
||||||
Dictionary<URI, HttpApplication> applications = new Dictionary<URI, HttpApplication>();
|
Dictionary<URI, HttpApplication> applications = new Dictionary<URI, HttpApplication>();
|
||||||
|
|
||||||
Dictionary<TcpListener, Thread> currentListenerThreads = new Dictionary<TcpListener, Thread>();
|
|
||||||
|
|
||||||
DynamicPool threadPool;
|
DynamicPool threadPool;
|
||||||
public DynamicPool ThreadPool => threadPool;
|
public DynamicPool ThreadPool => threadPool;
|
||||||
|
|
||||||
public HTTPServer()
|
public HTTPServer()
|
||||||
{
|
{
|
||||||
Logger = Logger.Default;
|
Logger = Logger.Default;
|
||||||
CreateServerConnection = (HTTPServer httpServer, TcpClient tcpClient) => new HTTPServerConnection(httpServer, tcpClient);
|
|
||||||
|
|
||||||
SessionCache = new SessionCache();
|
SessionCache = new SessionCache();
|
||||||
|
|
||||||
threadPool = new DynamicPool(1024);
|
threadPool = new DynamicPool(1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEndpoint(IPEndPoint endpoint)
|
public void AddListener(Listener listener)
|
||||||
{
|
{
|
||||||
if (this.tcpListeners.ContainsKey(endpoint))
|
listeners.Add(listener);
|
||||||
{
|
if (IsRunning)
|
||||||
throw new ArgumentOutOfRangeException(nameof(endpoint), "EndPoint already added");
|
StartListener(listener);
|
||||||
}
|
|
||||||
|
|
||||||
this.tcpListeners.Add(endpoint, new TcpListener(endpoint));
|
|
||||||
}
|
}
|
||||||
public void RemoveEndpoint(IPEndPoint endpoint)
|
|
||||||
|
public void StartListener(Listener listener)
|
||||||
{
|
{
|
||||||
if (this.tcpListeners.ContainsKey(endpoint))
|
listener.Open();
|
||||||
{
|
|
||||||
this.tcpListeners[endpoint].Stop();
|
threadPool.Enqueue(
|
||||||
this.tcpListeners.Remove(endpoint);
|
() => listener.AcceptMany(
|
||||||
}
|
(connection) => threadPool.Enqueue(
|
||||||
|
() => this.HandleConnection(connection)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopListener(Listener listener)
|
||||||
|
{
|
||||||
|
listener.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void AddEndpoint(Endpoint endpoint)
|
||||||
|
{
|
||||||
|
AddListener(new HttpListener(endpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddApplication(URI BaseURI, HttpApplication application)
|
public void AddApplication(URI BaseURI, HttpApplication application)
|
||||||
|
@ -89,13 +96,8 @@ namespace ln.http
|
||||||
{
|
{
|
||||||
threadPool.Start();
|
threadPool.Start();
|
||||||
|
|
||||||
foreach (TcpListener tcpListener in this.tcpListeners.Values)
|
foreach (Listener listener in listeners)
|
||||||
{
|
StartListener(listener);
|
||||||
tcpListener.Start(backlog);
|
|
||||||
|
|
||||||
Thread listenerThread = new Thread(() => AcceptConnections(tcpListener));
|
|
||||||
listenerThread.Start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
|
@ -104,64 +106,54 @@ namespace ln.http
|
||||||
{
|
{
|
||||||
this.shutdown = true;
|
this.shutdown = true;
|
||||||
}
|
}
|
||||||
|
foreach (Listener listener in listeners)
|
||||||
|
StopListener(listener);
|
||||||
|
|
||||||
foreach (TcpListener tcpListener in tcpListeners.Values)
|
threadPool.Stop(true);
|
||||||
{
|
|
||||||
tcpListener.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (HTTPServerConnection connection in HTTPServerConnection.CurrentConnections)
|
|
||||||
connection?.Abort();
|
|
||||||
|
|
||||||
if (threadPool.State == PoolState.RUN)
|
|
||||||
threadPool.Stop(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AcceptConnections(TcpListener tcpListener)
|
private void HandleConnection(Connection connection)
|
||||||
{
|
{
|
||||||
lock (this)
|
HttpRequest httpRequest = null;
|
||||||
{
|
bool keepAlive = true;
|
||||||
if (currentListenerThreads.ContainsKey(tcpListener))
|
|
||||||
{
|
|
||||||
throw new Exception("HTTP listener thread already running");
|
|
||||||
}
|
|
||||||
currentListenerThreads.Add(tcpListener, Thread.CurrentThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
do
|
||||||
while (!this.shutdown)
|
|
||||||
{
|
{
|
||||||
AcceptConnection(tcpListener);
|
httpRequest = connection.ReadRequest(this);
|
||||||
}
|
if (httpRequest == null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
httpRequest.ApplySession(SessionCache);
|
||||||
|
|
||||||
|
HttpApplication application = GetHttpApplication(new URI(httpRequest.BaseURI.ToString()));
|
||||||
|
|
||||||
|
application.Authenticate(httpRequest);
|
||||||
|
application.Authorize(httpRequest);
|
||||||
|
|
||||||
|
HttpResponse response = application.GetResponse(httpRequest);
|
||||||
|
|
||||||
|
keepAlive = httpRequest.GetRequestHeader("connection","keep-alive").Equals("keep-alive") && response.GetHeader("connection", "keep-alive").Equals("keep-alive");
|
||||||
|
response.SetHeader("connection", keepAlive ? "keep-alive" : "close");
|
||||||
|
|
||||||
|
connection.SendResponse(response);
|
||||||
|
|
||||||
|
} while (keepAlive);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logging.Log(LogLevel.ERROR, "HTTPServer: Listener thread caught exception {0}", e);
|
|
||||||
Logging.Log(e);
|
Logging.Log(e);
|
||||||
|
if (httpRequest != null)
|
||||||
|
{
|
||||||
|
HttpResponse httpResponse = new HttpResponse(httpRequest);
|
||||||
|
httpResponse.StatusCode = 500;
|
||||||
|
httpResponse.ContentWriter.WriteLine("500 Internal Server Error");
|
||||||
|
|
||||||
|
connection.SendResponse(httpResponse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lock (this)
|
connection.GetStream().Close();
|
||||||
{
|
|
||||||
currentListenerThreads.Remove(tcpListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AcceptConnection(TcpListener tcpListener)
|
|
||||||
{
|
|
||||||
TcpClient tcpClient = tcpListener.AcceptTcpClient();
|
|
||||||
|
|
||||||
HTTPServerConnection connection = CreateServerConnection(this, tcpClient);
|
|
||||||
if (connection == null)
|
|
||||||
{
|
|
||||||
Logging.Log(LogLevel.ERROR, "HTTPServer: CreateServerConnection(): returned null");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.threadPool.Enqueue(connection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Log(DateTime startTime,double duration,HttpRequest httpRequest,HttpResponse httpResponse)
|
public void Log(DateTime startTime,double duration,HttpRequest httpRequest,HttpResponse httpResponse)
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using ln.types.net;
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
{
|
{
|
||||||
|
@ -17,7 +18,7 @@ namespace ln.http
|
||||||
private int blen;
|
private int blen;
|
||||||
private int bptr;
|
private int bptr;
|
||||||
|
|
||||||
public IPEndPoint RemoteEndpoint { get; private set; }
|
public Endpoint RemoteEndpoint { get; private set; }
|
||||||
|
|
||||||
public String Method { get; private set; }
|
public String Method { get; private set; }
|
||||||
public String URL { get; private set; }
|
public String URL { get; private set; }
|
||||||
|
@ -31,7 +32,7 @@ namespace ln.http
|
||||||
{
|
{
|
||||||
Stream = stream;
|
Stream = stream;
|
||||||
}
|
}
|
||||||
public HttpReader(Stream stream,IPEndPoint remoteEndpoint)
|
public HttpReader(Stream stream,Endpoint remoteEndpoint)
|
||||||
{
|
{
|
||||||
Stream = stream;
|
Stream = stream;
|
||||||
RemoteEndpoint = remoteEndpoint;
|
RemoteEndpoint = remoteEndpoint;
|
||||||
|
@ -84,7 +85,11 @@ namespace ln.http
|
||||||
bptr = 0;
|
bptr = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
blen += Stream.Read(buffer, blen, buffer.Length - blen);
|
int rlen = Stream.Read(buffer, blen, buffer.Length - blen);
|
||||||
|
if (rlen == 0)
|
||||||
|
throw new IOException();
|
||||||
|
|
||||||
|
blen += rlen;
|
||||||
while (bptr <= (blen - 4))
|
while (bptr <= (blen - 4))
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -7,6 +7,7 @@ using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using ln.http.exceptions;
|
using ln.http.exceptions;
|
||||||
using ln.http.resources.session;
|
using ln.http.resources.session;
|
||||||
|
using ln.types.net;
|
||||||
|
|
||||||
namespace ln.http
|
namespace ln.http
|
||||||
{
|
{
|
||||||
|
@ -15,10 +16,10 @@ namespace ln.http
|
||||||
Dictionary<String, String> requestHeaders;
|
Dictionary<String, String> requestHeaders;
|
||||||
Dictionary<String, String> requestCookies;
|
Dictionary<String, String> requestCookies;
|
||||||
|
|
||||||
public HTTPServer HTTPServer { get; }
|
public HTTPServer HTTPServer { get; }
|
||||||
|
|
||||||
public IPEndPoint RemoteEndpoint { get; private set; }
|
public Endpoint RemoteEndpoint { get; private set; }
|
||||||
public IPEndPoint LocalEndpoint { get; private set; }
|
public Endpoint LocalEndpoint { get; private set; }
|
||||||
|
|
||||||
public Uri BaseURI { get; set; }
|
public Uri BaseURI { get; set; }
|
||||||
public Uri URI { get; private set; }
|
public Uri URI { get; private set; }
|
||||||
|
@ -54,7 +55,7 @@ namespace ln.http
|
||||||
byte[] requestBody;
|
byte[] requestBody;
|
||||||
Stream connectionStream;
|
Stream connectionStream;
|
||||||
|
|
||||||
public HttpRequest(HTTPServer httpServer, HttpReader httpReader, IPEndPoint localEndpoint)
|
public HttpRequest(HTTPServer httpServer, HttpReader httpReader, Endpoint localEndpoint)
|
||||||
{
|
{
|
||||||
HTTPServer = httpServer;
|
HTTPServer = httpServer;
|
||||||
connectionStream = httpReader.Stream;
|
connectionStream = httpReader.Stream;
|
||||||
|
|
|
@ -49,11 +49,12 @@ namespace ln.http
|
||||||
SetHeader("content-type", contentType);
|
SetHeader("content-type", contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String GetHeader(string name) => GetHeader(name, null);
|
||||||
public String GetHeader(string name)
|
public String GetHeader(string name, string defValue)
|
||||||
{
|
{
|
||||||
return String.Join(",", headers[name.ToUpper()]);
|
return headers.ContainsKey(name) ? String.Join(",", headers[name.ToUpper()]) : defValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] GetHeaderValues(string name)
|
public String[] GetHeaderValues(string name)
|
||||||
{
|
{
|
||||||
return headers[name.ToUpper()].ToArray();
|
return headers[name.ToUpper()].ToArray();
|
||||||
|
@ -84,6 +85,10 @@ namespace ln.http
|
||||||
headers.Remove(name.ToUpper());
|
headers.Remove(name.ToUpper());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ContainsHeader(string headerName)
|
||||||
|
{
|
||||||
|
return headers.ContainsKey(headerName.ToUpper());
|
||||||
|
}
|
||||||
|
|
||||||
public void AddCookie(string name,string value)
|
public void AddCookie(string name,string value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
// /**
|
||||||
|
// * File: CertContainer.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.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace ln.http.cert
|
||||||
|
{
|
||||||
|
public class CertContainer
|
||||||
|
{
|
||||||
|
public string SearchPath { get; set; }
|
||||||
|
|
||||||
|
Dictionary<string, X509Certificate> certificates = new Dictionary<string, X509Certificate>();
|
||||||
|
|
||||||
|
public CertContainer(){ }
|
||||||
|
public CertContainer(string searchPath)
|
||||||
|
{
|
||||||
|
SearchPath = searchPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddCertificate(string targetHost, X509Certificate certificate) => certificates[targetHost] = certificate;
|
||||||
|
|
||||||
|
public virtual X509Certificate LookupCertificate(string targetHost)
|
||||||
|
{
|
||||||
|
String p = Path.Combine(SearchPath, String.Format("{0}.pem",targetHost));
|
||||||
|
if (File.Exists(p))
|
||||||
|
{
|
||||||
|
return X509Certificate.CreateFromCertFile(p);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public X509Certificate SelectCertificate(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
|
||||||
|
{
|
||||||
|
if (!certificates.ContainsKey(targetHost) && (SearchPath != null))
|
||||||
|
{
|
||||||
|
X509Certificate certificate = LookupCertificate(targetHost);
|
||||||
|
if (certificate != null)
|
||||||
|
{
|
||||||
|
certificates[targetHost] = certificate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return certificates[targetHost];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
// /**
|
||||||
|
// * 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.types;
|
||||||
|
using System.IO;
|
||||||
|
using ln.types.net;
|
||||||
|
using ln.logging;
|
||||||
|
using ln.http.listener;
|
||||||
|
namespace ln.http.connections
|
||||||
|
{
|
||||||
|
public abstract class Connection
|
||||||
|
{
|
||||||
|
public Listener Listener { get; }
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
HttpReader httpReader = new HttpReader(GetStream());
|
||||||
|
httpReader.Read();
|
||||||
|
|
||||||
|
return new HttpRequest(httpServer, httpReader, Listener.LocalEndpoint);
|
||||||
|
} catch (IOException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.Log(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void SendResponse(HttpResponse response) => SendResponse(GetStream(), response);
|
||||||
|
|
||||||
|
public static void SendResponse(Stream stream, HttpResponse response)
|
||||||
|
{
|
||||||
|
response.SetHeader("Content-Length", response.ContentStream.Length.ToString());
|
||||||
|
|
||||||
|
StreamWriter streamWriter = new StreamWriter(stream);
|
||||||
|
streamWriter.NewLine = "\r\n";
|
||||||
|
|
||||||
|
streamWriter.WriteLine("{0} {1} {2}", response.HttpRequest.Protocol, response.StatusCode, response.StatusMessage);
|
||||||
|
foreach (String headerName in response.GetHeaderNames())
|
||||||
|
{
|
||||||
|
streamWriter.WriteLine("{0}: {1}", headerName, response.GetHeader(headerName));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (HttpCookie httpCookie in response.Cookies)
|
||||||
|
{
|
||||||
|
streamWriter.WriteLine("Set-Cookie: {0}", httpCookie.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
streamWriter.WriteLine();
|
||||||
|
streamWriter.Flush();
|
||||||
|
|
||||||
|
response.ContentStream.CopyTo(stream);
|
||||||
|
response.ContentStream.Close();
|
||||||
|
response.ContentStream.Dispose();
|
||||||
|
|
||||||
|
stream.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// /**
|
||||||
|
// * 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.types;
|
||||||
|
using ln.types.net;
|
||||||
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
// /**
|
||||||
|
// * 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 System.Security.AccessControl;
|
||||||
|
using ln.types;
|
||||||
|
using System.Net.Security;
|
||||||
|
using ln.http.listener;
|
||||||
|
using ln.types.net;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override HttpRequest ReadRequest(HTTPServer server)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream GetStream() => sslStream;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
// /**
|
||||||
|
// * 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;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using ln.types;
|
||||||
|
using ln.types.net;
|
||||||
|
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(IPv4 listen, int port) : this((IPv6)listen,port){}
|
||||||
|
public HttpListener(IPv6 listen, int port)
|
||||||
|
: base(listen, port)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Connection Accept() => 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// /**
|
||||||
|
// * 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 System;
|
||||||
|
using ln.types;
|
||||||
|
using ln.types.net;
|
||||||
|
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(IPv4 listen, int port) : this((IPv6)listen, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// /**
|
||||||
|
// * 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.IO;
|
||||||
|
using ln.http.connections;
|
||||||
|
using ln.types.net;
|
||||||
|
namespace ln.http.listener
|
||||||
|
{
|
||||||
|
public abstract class Listener : IDisposable
|
||||||
|
{
|
||||||
|
public IPv6 Listen { get; }
|
||||||
|
public int Port { get; }
|
||||||
|
|
||||||
|
public 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)
|
||||||
|
handler(Accept());
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Open();
|
||||||
|
public abstract void Close();
|
||||||
|
|
||||||
|
public abstract Connection Accept();
|
||||||
|
|
||||||
|
public abstract void Dispose();
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Security" />
|
<Reference Include="System.Security" />
|
||||||
|
<Reference Include="Mono.Security" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
@ -57,12 +58,22 @@
|
||||||
<Compile Include="websocket\WebSocketEventArgs.cs" />
|
<Compile Include="websocket\WebSocketEventArgs.cs" />
|
||||||
<Compile Include="websocket\WebSocketFrame.cs" />
|
<Compile Include="websocket\WebSocketFrame.cs" />
|
||||||
<Compile Include="HTTPServerConnection.cs" />
|
<Compile Include="HTTPServerConnection.cs" />
|
||||||
|
<Compile Include="connections\HttpConnection.cs" />
|
||||||
|
<Compile Include="connections\Connection.cs" />
|
||||||
|
<Compile Include="connections\HttpsConnection.cs" />
|
||||||
|
<Compile Include="cert\CertContainer.cs" />
|
||||||
|
<Compile Include="listener\HttpListener.cs" />
|
||||||
|
<Compile Include="listener\HttpsListener.cs" />
|
||||||
|
<Compile Include="listener\Listener.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="exceptions\" />
|
<Folder Include="exceptions\" />
|
||||||
<Folder Include="session\" />
|
<Folder Include="session\" />
|
||||||
<Folder Include="client\" />
|
<Folder Include="client\" />
|
||||||
<Folder Include="websocket\" />
|
<Folder Include="websocket\" />
|
||||||
|
<Folder Include="connections\" />
|
||||||
|
<Folder Include="cert\" />
|
||||||
|
<Folder Include="listener\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
|
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
|
||||||
|
|
|
@ -5,6 +5,7 @@ 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.types;
|
||||||
|
|
||||||
namespace ln.http.websocket
|
namespace ln.http.websocket
|
||||||
{
|
{
|
||||||
|
@ -32,7 +33,7 @@ namespace ln.http.websocket
|
||||||
public delegate void WebSocketEventDelegate(WebSocket sender,WebSocketEventArgs e);
|
public delegate void WebSocketEventDelegate(WebSocket sender,WebSocketEventArgs e);
|
||||||
|
|
||||||
|
|
||||||
public class WebSocket
|
public abstract class WebSocket
|
||||||
{
|
{
|
||||||
public HTTPServer HTTPServer => HttpRequest.HTTPServer;
|
public HTTPServer HTTPServer => HttpRequest.HTTPServer;
|
||||||
public HttpRequest HttpRequest { get; }
|
public HttpRequest HttpRequest { get; }
|
||||||
|
@ -40,10 +41,6 @@ namespace ln.http.websocket
|
||||||
|
|
||||||
public WebSocketState State { get; private set; } = WebSocketState.HANDSHAKE;
|
public WebSocketState State { get; private set; } = WebSocketState.HANDSHAKE;
|
||||||
|
|
||||||
public event WebSocketEventDelegate WebSocketEvent;
|
|
||||||
|
|
||||||
Thread receiverThread;
|
|
||||||
|
|
||||||
public WebSocket(HttpRequest httpRequest)
|
public WebSocket(HttpRequest httpRequest)
|
||||||
{
|
{
|
||||||
HttpRequest = httpRequest;
|
HttpRequest = httpRequest;
|
||||||
|
@ -72,22 +69,11 @@ namespace ln.http.websocket
|
||||||
);
|
);
|
||||||
|
|
||||||
HTTPServerConnection.SendResponse(Stream, httpResponse);
|
HTTPServerConnection.SendResponse(Stream, httpResponse);
|
||||||
|
|
||||||
HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
|
HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
|
||||||
|
|
||||||
State = WebSocketState.OPEN;
|
State = WebSocketState.OPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAlive => ((receiverThread != null) && receiverThread.IsAlive);
|
public bool IsAlive => false;
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
if ((receiverThread == null) || !receiverThread.IsAlive)
|
|
||||||
{
|
|
||||||
receiverThread = new Thread(() => Run());
|
|
||||||
receiverThread.Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
|
@ -122,8 +108,10 @@ namespace ln.http.websocket
|
||||||
switch (webSocketFrame.Opcode)
|
switch (webSocketFrame.Opcode)
|
||||||
{
|
{
|
||||||
case WebSocketOpcode.TEXT:
|
case WebSocketOpcode.TEXT:
|
||||||
|
Received(Encoding.UTF8.GetString(webSocketFrame.ApplicationData));
|
||||||
|
break;
|
||||||
case WebSocketOpcode.BINARY:
|
case WebSocketOpcode.BINARY:
|
||||||
WebSocketEvent(this, new WebSocketEventArgs(webSocketFrame));
|
Received(webSocketFrame.ApplicationData);
|
||||||
break;
|
break;
|
||||||
case WebSocketOpcode.CLOSE:
|
case WebSocketOpcode.CLOSE:
|
||||||
if (State == WebSocketState.OPEN)
|
if (State == WebSocketState.OPEN)
|
||||||
|
@ -159,8 +147,17 @@ namespace ln.http.websocket
|
||||||
} finally
|
} finally
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
receiverThread = null;
|
public virtual bool Received(string textMessage)
|
||||||
|
{
|
||||||
|
Logging.Log(LogLevel.WARNING, "WebSocket received unexpected text message:\n{0}", textMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public virtual bool Received(byte[] binaryMessage)
|
||||||
|
{
|
||||||
|
Logging.Log(LogLevel.WARNING, "WebSocket received unexpected binary message:\n{0}",binaryMessage.ToHexString());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Send(WebSocketFrame frame)
|
public void Send(WebSocketFrame frame)
|
||||||
|
|
Loading…
Reference in New Issue