WIP
parent
89dddbc07b
commit
79b3eabde6
150
HTTPServer.cs
150
HTTPServer.cs
|
@ -1,14 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using ln.http.resources.session;
|
||||
using ln.logging;
|
||||
using ln.types.threads;
|
||||
using ln.types;
|
||||
using ln.http.listener;
|
||||
using ln.http.connections;
|
||||
using ln.types.net;
|
||||
using ln.http.cert;
|
||||
using System.Globalization;
|
||||
using System.Net.Sockets;
|
||||
|
||||
|
||||
namespace ln.http
|
||||
|
@ -20,53 +21,59 @@ namespace ln.http
|
|||
public static bool exclusivePortListener = false;
|
||||
|
||||
public HttpApplication DefaultApplication { get; set; }
|
||||
|
||||
public SessionCache SessionCache { get; set; }
|
||||
|
||||
public bool IsRunning => (currentListenerThreads.Count > 0) || (threadPool.CurrentPoolSize > 0);
|
||||
|
||||
public Func<HTTPServer,TcpClient,HTTPServerConnection> CreateServerConnection { get; set; }
|
||||
public bool IsRunning => !shutdown && (threadPool.CurrentPoolSize > 0);
|
||||
public Func<HTTPServer,HTTPServerConnection> CreateServerConnection { get; set; }
|
||||
|
||||
public Logger Logger { get; set; }
|
||||
|
||||
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<TcpListener, Thread> currentListenerThreads = new Dictionary<TcpListener, Thread>();
|
||||
|
||||
DynamicPool threadPool;
|
||||
public DynamicPool ThreadPool => threadPool;
|
||||
|
||||
public HTTPServer()
|
||||
{
|
||||
Logger = Logger.Default;
|
||||
CreateServerConnection = (HTTPServer httpServer, TcpClient tcpClient) => new HTTPServerConnection(httpServer, tcpClient);
|
||||
|
||||
SessionCache = new SessionCache();
|
||||
|
||||
threadPool = new DynamicPool(1024);
|
||||
}
|
||||
|
||||
public void AddEndpoint(IPEndPoint endpoint)
|
||||
public void AddListener(Listener listener)
|
||||
{
|
||||
if (this.tcpListeners.ContainsKey(endpoint))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(endpoint), "EndPoint already added");
|
||||
}
|
||||
|
||||
this.tcpListeners.Add(endpoint, new TcpListener(endpoint));
|
||||
listeners.Add(listener);
|
||||
if (IsRunning)
|
||||
StartListener(listener);
|
||||
}
|
||||
public void RemoveEndpoint(IPEndPoint endpoint)
|
||||
|
||||
public void StartListener(Listener listener)
|
||||
{
|
||||
if (this.tcpListeners.ContainsKey(endpoint))
|
||||
{
|
||||
this.tcpListeners[endpoint].Stop();
|
||||
this.tcpListeners.Remove(endpoint);
|
||||
}
|
||||
listener.Open();
|
||||
|
||||
threadPool.Enqueue(
|
||||
() => 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)
|
||||
|
@ -89,13 +96,8 @@ namespace ln.http
|
|||
{
|
||||
threadPool.Start();
|
||||
|
||||
foreach (TcpListener tcpListener in this.tcpListeners.Values)
|
||||
{
|
||||
tcpListener.Start(backlog);
|
||||
|
||||
Thread listenerThread = new Thread(() => AcceptConnections(tcpListener));
|
||||
listenerThread.Start();
|
||||
}
|
||||
foreach (Listener listener in listeners)
|
||||
StartListener(listener);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
|
@ -104,64 +106,54 @@ namespace ln.http
|
|||
{
|
||||
this.shutdown = true;
|
||||
}
|
||||
foreach (Listener listener in listeners)
|
||||
StopListener(listener);
|
||||
|
||||
foreach (TcpListener tcpListener in tcpListeners.Values)
|
||||
{
|
||||
tcpListener.Stop();
|
||||
}
|
||||
|
||||
foreach (HTTPServerConnection connection in HTTPServerConnection.CurrentConnections)
|
||||
connection?.Abort();
|
||||
|
||||
if (threadPool.State == PoolState.RUN)
|
||||
threadPool.Stop(true);
|
||||
threadPool.Stop(true);
|
||||
}
|
||||
|
||||
private void AcceptConnections(TcpListener tcpListener)
|
||||
private void HandleConnection(Connection connection)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (currentListenerThreads.ContainsKey(tcpListener))
|
||||
{
|
||||
throw new Exception("HTTP listener thread already running");
|
||||
}
|
||||
currentListenerThreads.Add(tcpListener, Thread.CurrentThread);
|
||||
}
|
||||
|
||||
HttpRequest httpRequest = null;
|
||||
bool keepAlive = true;
|
||||
try
|
||||
{
|
||||
|
||||
while (!this.shutdown)
|
||||
do
|
||||
{
|
||||
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)
|
||||
{
|
||||
Logging.Log(LogLevel.ERROR, "HTTPServer: Listener thread caught exception {0}", 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
connection.GetStream().Close();
|
||||
}
|
||||
|
||||
public void Log(DateTime startTime,double duration,HttpRequest httpRequest,HttpResponse httpResponse)
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.IO;
|
|||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using ln.types.net;
|
||||
|
||||
namespace ln.http
|
||||
{
|
||||
|
@ -17,7 +18,7 @@ namespace ln.http
|
|||
private int blen;
|
||||
private int bptr;
|
||||
|
||||
public IPEndPoint RemoteEndpoint { get; private set; }
|
||||
public Endpoint RemoteEndpoint { get; private set; }
|
||||
|
||||
public String Method { get; private set; }
|
||||
public String URL { get; private set; }
|
||||
|
@ -31,7 +32,7 @@ namespace ln.http
|
|||
{
|
||||
Stream = stream;
|
||||
}
|
||||
public HttpReader(Stream stream,IPEndPoint remoteEndpoint)
|
||||
public HttpReader(Stream stream,Endpoint remoteEndpoint)
|
||||
{
|
||||
Stream = stream;
|
||||
RemoteEndpoint = remoteEndpoint;
|
||||
|
@ -84,7 +85,11 @@ namespace ln.http
|
|||
bptr = 0;
|
||||
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))
|
||||
{
|
||||
if (
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Net;
|
|||
using System.Net.Sockets;
|
||||
using ln.http.exceptions;
|
||||
using ln.http.resources.session;
|
||||
using ln.types.net;
|
||||
|
||||
namespace ln.http
|
||||
{
|
||||
|
@ -15,10 +16,10 @@ namespace ln.http
|
|||
Dictionary<String, String> requestHeaders;
|
||||
Dictionary<String, String> requestCookies;
|
||||
|
||||
public HTTPServer HTTPServer { get; }
|
||||
public HTTPServer HTTPServer { get; }
|
||||
|
||||
public IPEndPoint RemoteEndpoint { get; private set; }
|
||||
public IPEndPoint LocalEndpoint { get; private set; }
|
||||
public Endpoint RemoteEndpoint { get; private set; }
|
||||
public Endpoint LocalEndpoint { get; private set; }
|
||||
|
||||
public Uri BaseURI { get; set; }
|
||||
public Uri URI { get; private set; }
|
||||
|
@ -54,7 +55,7 @@ namespace ln.http
|
|||
byte[] requestBody;
|
||||
Stream connectionStream;
|
||||
|
||||
public HttpRequest(HTTPServer httpServer, HttpReader httpReader, IPEndPoint localEndpoint)
|
||||
public HttpRequest(HTTPServer httpServer, HttpReader httpReader, Endpoint localEndpoint)
|
||||
{
|
||||
HTTPServer = httpServer;
|
||||
connectionStream = httpReader.Stream;
|
||||
|
|
|
@ -49,11 +49,12 @@ namespace ln.http
|
|||
SetHeader("content-type", contentType);
|
||||
}
|
||||
|
||||
|
||||
public String GetHeader(string name)
|
||||
public String GetHeader(string name) => GetHeader(name, null);
|
||||
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)
|
||||
{
|
||||
return headers[name.ToUpper()].ToArray();
|
||||
|
@ -84,6 +85,10 @@ namespace ln.http
|
|||
headers.Remove(name.ToUpper());
|
||||
}
|
||||
|
||||
public bool ContainsHeader(string headerName)
|
||||
{
|
||||
return headers.ContainsKey(headerName.ToUpper());
|
||||
}
|
||||
|
||||
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>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="Mono.Security" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
@ -57,12 +58,22 @@
|
|||
<Compile Include="websocket\WebSocketEventArgs.cs" />
|
||||
<Compile Include="websocket\WebSocketFrame.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>
|
||||
<Folder Include="exceptions\" />
|
||||
<Folder Include="session\" />
|
||||
<Folder Include="client\" />
|
||||
<Folder Include="websocket\" />
|
||||
<Folder Include="connections\" />
|
||||
<Folder Include="cert\" />
|
||||
<Folder Include="listener\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ln.logging\ln.logging.csproj">
|
||||
|
|
|
@ -5,6 +5,7 @@ using ln.logging;
|
|||
using ln.http.exceptions;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using ln.types;
|
||||
|
||||
namespace ln.http.websocket
|
||||
{
|
||||
|
@ -32,7 +33,7 @@ namespace ln.http.websocket
|
|||
public delegate void WebSocketEventDelegate(WebSocket sender,WebSocketEventArgs e);
|
||||
|
||||
|
||||
public class WebSocket
|
||||
public abstract class WebSocket
|
||||
{
|
||||
public HTTPServer HTTPServer => HttpRequest.HTTPServer;
|
||||
public HttpRequest HttpRequest { get; }
|
||||
|
@ -40,10 +41,6 @@ namespace ln.http.websocket
|
|||
|
||||
public WebSocketState State { get; private set; } = WebSocketState.HANDSHAKE;
|
||||
|
||||
public event WebSocketEventDelegate WebSocketEvent;
|
||||
|
||||
Thread receiverThread;
|
||||
|
||||
public WebSocket(HttpRequest httpRequest)
|
||||
{
|
||||
HttpRequest = httpRequest;
|
||||
|
@ -72,22 +69,11 @@ namespace ln.http.websocket
|
|||
);
|
||||
|
||||
HTTPServerConnection.SendResponse(Stream, httpResponse);
|
||||
|
||||
HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
|
||||
|
||||
State = WebSocketState.OPEN;
|
||||
}
|
||||
|
||||
public bool IsAlive => ((receiverThread != null) && receiverThread.IsAlive);
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if ((receiverThread == null) || !receiverThread.IsAlive)
|
||||
{
|
||||
receiverThread = new Thread(() => Run());
|
||||
receiverThread.Start();
|
||||
}
|
||||
}
|
||||
public bool IsAlive => false;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
|
@ -122,8 +108,10 @@ namespace ln.http.websocket
|
|||
switch (webSocketFrame.Opcode)
|
||||
{
|
||||
case WebSocketOpcode.TEXT:
|
||||
Received(Encoding.UTF8.GetString(webSocketFrame.ApplicationData));
|
||||
break;
|
||||
case WebSocketOpcode.BINARY:
|
||||
WebSocketEvent(this, new WebSocketEventArgs(webSocketFrame));
|
||||
Received(webSocketFrame.ApplicationData);
|
||||
break;
|
||||
case WebSocketOpcode.CLOSE:
|
||||
if (State == WebSocketState.OPEN)
|
||||
|
@ -159,8 +147,17 @@ namespace ln.http.websocket
|
|||
} 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)
|
||||
|
|
Loading…
Reference in New Issue