Added WebSocketResponse

master
Harald Wolff 2020-12-18 09:17:36 +01:00
parent a76088b628
commit a2f114216c
1 changed files with 190 additions and 0 deletions

View File

@ -0,0 +1,190 @@
using System;
using System.IO;
using ln.logging;
using ln.http.exceptions;
using System.Security.Cryptography;
using System.Text;
using ln.type;
namespace ln.http.websocket
{
public delegate void WebSocketStateChanged(WebSocketResponse sender, WebSocketState newState);
public delegate void WebSocketReceivedText(WebSocketResponse sender, string text);
public delegate void WebSocketReceivedBytes(WebSocketResponse sender, byte[] bytes);
public class WebSocketResponse : HttpResponse
{
public event WebSocketStateChanged OnWebSocketStateChanged;
public event WebSocketReceivedText OnWebSocketReceivedText;
public event WebSocketReceivedBytes OnWebSocketReceivedBytes;
public Stream Stream { get; private set; }
WebSocketState state = WebSocketState.HANDSHAKE;
public WebSocketState State {
get => state;
private set {
if (state != value)
{
state = value;
OnWebSocketStateChanged?.Invoke(this, value);
}
}
}
public WebSocketResponse()
{}
public void Close()
{
lock (this)
{
switch (State)
{
case WebSocketState.HANDSHAKE:
case WebSocketState.ERROR:
case WebSocketState.CLOSING:
State = WebSocketState.CLOSED;
break;
case WebSocketState.CLOSED:
break;
case WebSocketState.OPEN:
WebSocketFrame closeFrame = new WebSocketFrame(WebSocketOpcode.CLOSE);
Send(closeFrame);
State = WebSocketState.CLOSING;
break;
}
}
}
public void Run()
{
try
{
while (State != WebSocketState.CLOSED)
{
WebSocketFrame webSocketFrame = new WebSocketFrame(Stream);
if (webSocketFrame.Opcode != WebSocketOpcode.TEXT)
Logging.Log(LogLevel.DEBUG, "ws.opcode: {0}", webSocketFrame.Opcode);
switch (webSocketFrame.Opcode)
{
case WebSocketOpcode.TEXT:
Received(Encoding.UTF8.GetString(webSocketFrame.ApplicationData));
break;
case WebSocketOpcode.BINARY:
Received(webSocketFrame.ApplicationData);
break;
case WebSocketOpcode.CLOSE:
if (State == WebSocketState.OPEN)
{
WebSocketFrame closeFrame = new WebSocketFrame(WebSocketOpcode.CLOSE);
closeFrame.FIN = true;
lock (this)
{
Send(closeFrame);
State = WebSocketState.CLOSING;
}
}
State = WebSocketState.CLOSED;
break;
case WebSocketOpcode.PING:
WebSocketFrame pong = new WebSocketFrame(WebSocketOpcode.PONG);
pong.ApplicationData = webSocketFrame.ApplicationData;
Send(pong);
break;
}
}
} catch (IOException)
{
State = WebSocketState.ERROR;
Logging.Log(LogLevel.DEBUG, "WebSocket connection was dropped unexpected");
Close();
} catch (Exception e)
{
Logging.Log(LogLevel.ERROR, "WebSocket caught Exception: {0}", e.ToString());
Logging.Log(e);
} finally
{
State = WebSocketState.CLOSED;
}
}
public virtual void Received(string textMessage) => OnWebSocketReceivedText?.Invoke(this, textMessage);
public virtual void Received(byte[] binaryMessage) => OnWebSocketReceivedBytes?.Invoke(this, binaryMessage);
public void Send(WebSocketFrame frame)
{
if (Stream == null)
return;
lock (this)
{
if (State == WebSocketState.OPEN)
{
try
{
frame.WriteTo(Stream);
} catch (IOException)
{
if (State != WebSocketState.ERROR)
{
Logging.Log(LogLevel.ERROR, "WebSocket.Send(): Websocket connection was dropped unexpected");
State = WebSocketState.ERROR;
Close();
}
}
}
else
throw new IOException("WebSocket is not open");
}
}
public void Send(string textMessage)
{
WebSocketFrame webSocketFrame = new WebSocketFrame(textMessage);
Send(webSocketFrame);
}
public void Send(byte[] binaryMessage)
{
WebSocketFrame webSocketFrame = new WebSocketFrame(binaryMessage);
Send(webSocketFrame);
}
public override void SendResponse(Stream stream, HttpRequest httpRequest)
{
lock (this)
{
Stream = stream;
if ((!httpRequest.GetRequestHeader("upgrade", "").Contains("websocket")) && (!httpRequest.GetRequestHeader("connection", "").Contains("Upgrade")))
throw new HttpException(400, "This resource is a websocket endpoint only");
if (!httpRequest.GetRequestHeader("Sec-WebSocket-Version", "").Equals("13"))
throw new HttpException(400, "Unsupported Protocol Version (WebSocket)");
String wsKey = httpRequest.GetRequestHeader("Sec-WebSocket-Key");
HttpResponse httpResponse = new HttpResponse(HttpStatusCode.SwitchingProtocols);
httpResponse.AddHeader("upgrade", "websocket");
httpResponse.AddHeader("connection", "Upgrade");
httpResponse.AddHeader("Sec-WebSocket-Version", "13");
string acceptKey = String.Format("{0}258EAFA5-E914-47DA-95CA-C5AB0DC85B11", httpRequest.GetRequestHeader("Sec-WebSocket-Key"));
httpResponse.AddHeader(
"Sec-Websocket-Accept",
Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(acceptKey)))
);
HTTPServer.SendResponse(Stream, httpRequest, httpResponse);
//HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
State = WebSocketState.OPEN;
}
Run();
}
}
}