2019-08-03 12:56:33 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using ln.logging;
|
|
|
|
|
using ln.http.exceptions;
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
using System.Text;
|
2019-11-04 10:00:33 +01:00
|
|
|
|
using ln.types;
|
2019-11-26 12:20:50 +01:00
|
|
|
|
using ln.http.connections;
|
2019-09-11 09:29:05 +02:00
|
|
|
|
|
2019-08-03 12:56:33 +02:00
|
|
|
|
namespace ln.http.websocket
|
|
|
|
|
{
|
|
|
|
|
public enum WebSocketOpcode : int
|
|
|
|
|
{
|
|
|
|
|
CONTINUATION = 0x00,
|
|
|
|
|
TEXT = 0x01,
|
|
|
|
|
BINARY = 0x02,
|
|
|
|
|
CLOSE = 0x08,
|
|
|
|
|
PING = 0x09,
|
|
|
|
|
PONG = 0x0A,
|
|
|
|
|
|
|
|
|
|
INVALIDOPCODE = -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum WebSocketState
|
|
|
|
|
{
|
|
|
|
|
HANDSHAKE,
|
|
|
|
|
OPEN,
|
|
|
|
|
CLOSING,
|
|
|
|
|
CLOSED,
|
|
|
|
|
ERROR
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public delegate void WebSocketEventDelegate(WebSocket sender,WebSocketEventArgs e);
|
|
|
|
|
|
2019-11-04 10:00:33 +01:00
|
|
|
|
public abstract class WebSocket
|
2019-08-03 12:56:33 +02:00
|
|
|
|
{
|
2019-10-18 12:31:41 +02:00
|
|
|
|
public HTTPServer HTTPServer => HttpRequest.HTTPServer;
|
2019-08-03 12:56:33 +02:00
|
|
|
|
public HttpRequest HttpRequest { get; }
|
|
|
|
|
public Stream Stream { get; }
|
|
|
|
|
|
|
|
|
|
public WebSocketState State { get; private set; } = WebSocketState.HANDSHAKE;
|
|
|
|
|
|
|
|
|
|
public WebSocket(HttpRequest httpRequest)
|
|
|
|
|
{
|
|
|
|
|
HttpRequest = httpRequest;
|
|
|
|
|
Stream = httpRequest.GetConnectionStream();
|
|
|
|
|
|
|
|
|
|
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(httpRequest);
|
|
|
|
|
httpResponse.StatusCode = 101;
|
|
|
|
|
httpResponse.StatusMessage = "Switching Protocols";
|
|
|
|
|
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)))
|
|
|
|
|
);
|
|
|
|
|
|
2019-11-26 12:20:50 +01:00
|
|
|
|
Connection.SendResponse(Stream, httpResponse);
|
2019-11-15 13:47:01 +01:00
|
|
|
|
//HTTPServerConnection.Current.Value.AbortRequested += (connection) => Close();
|
2019-08-03 12:56:33 +02:00
|
|
|
|
State = WebSocketState.OPEN;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-04 10:00:33 +01:00
|
|
|
|
public bool IsAlive => false;
|
2019-08-03 12:56:33 +02:00
|
|
|
|
|
|
|
|
|
public void Close()
|
|
|
|
|
{
|
|
|
|
|
switch (State)
|
|
|
|
|
{
|
|
|
|
|
case WebSocketState.HANDSHAKE:
|
|
|
|
|
case WebSocketState.ERROR:
|
|
|
|
|
case WebSocketState.CLOSING:
|
|
|
|
|
State = WebSocketState.CLOSED;
|
|
|
|
|
Stream.Close();
|
|
|
|
|
break;
|
|
|
|
|
case WebSocketState.CLOSED:
|
|
|
|
|
break;
|
|
|
|
|
case WebSocketState.OPEN:
|
|
|
|
|
WebSocketFrame closeFrame = new WebSocketFrame(WebSocketOpcode.CLOSE);
|
|
|
|
|
lock (Stream)
|
|
|
|
|
{
|
|
|
|
|
Send(closeFrame);
|
|
|
|
|
State = WebSocketState.CLOSING;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Run()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
while (State != WebSocketState.CLOSED)
|
|
|
|
|
{
|
|
|
|
|
WebSocketFrame webSocketFrame = new WebSocketFrame(Stream);
|
|
|
|
|
switch (webSocketFrame.Opcode)
|
|
|
|
|
{
|
|
|
|
|
case WebSocketOpcode.TEXT:
|
2019-11-04 10:00:33 +01:00
|
|
|
|
Received(Encoding.UTF8.GetString(webSocketFrame.ApplicationData));
|
|
|
|
|
break;
|
2019-08-03 12:56:33 +02:00
|
|
|
|
case WebSocketOpcode.BINARY:
|
2019-11-04 10:00:33 +01:00
|
|
|
|
Received(webSocketFrame.ApplicationData);
|
2019-08-03 12:56:33 +02:00
|
|
|
|
break;
|
|
|
|
|
case WebSocketOpcode.CLOSE:
|
|
|
|
|
if (State == WebSocketState.OPEN)
|
|
|
|
|
{
|
|
|
|
|
WebSocketFrame closeFrame = new WebSocketFrame(WebSocketOpcode.CLOSE);
|
|
|
|
|
closeFrame.FIN = true;
|
|
|
|
|
lock (Stream)
|
|
|
|
|
{
|
|
|
|
|
Send(closeFrame);
|
|
|
|
|
State = WebSocketState.CLOSING;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
State = WebSocketState.CLOSED;
|
|
|
|
|
Stream.Close();
|
|
|
|
|
break;
|
|
|
|
|
case WebSocketOpcode.PING:
|
|
|
|
|
WebSocketFrame pong = new WebSocketFrame(WebSocketOpcode.PONG);
|
|
|
|
|
pong.ApplicationData = webSocketFrame.ApplicationData;
|
|
|
|
|
Send(pong);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-09-02 12:18:35 +02:00
|
|
|
|
} catch (IOException)
|
|
|
|
|
{
|
|
|
|
|
State = WebSocketState.ERROR;
|
|
|
|
|
Logging.Log(LogLevel.DEBUG, "WebSocket connection was dropped unexpected");
|
|
|
|
|
Close();
|
2019-08-03 12:56:33 +02:00
|
|
|
|
} catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
Logging.Log(LogLevel.ERROR, "WebSocket caught Exception: {0}", e.ToString());
|
|
|
|
|
Logging.Log(e);
|
2019-09-16 20:40:47 +02:00
|
|
|
|
} finally
|
|
|
|
|
{
|
|
|
|
|
}
|
2019-11-04 10:00:33 +01:00
|
|
|
|
}
|
2019-08-03 12:56:33 +02:00
|
|
|
|
|
2019-11-04 10:00:33 +01:00
|
|
|
|
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;
|
2019-08-03 12:56:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Send(WebSocketFrame frame)
|
|
|
|
|
{
|
|
|
|
|
lock (Stream)
|
|
|
|
|
{
|
|
|
|
|
if (State == WebSocketState.OPEN)
|
2019-09-02 12:18:35 +02:00
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-03 12:56:33 +02:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|