Added WebSocketResponse
parent
a76088b628
commit
a2f114216c
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue