176 lines
4.8 KiB
C#
176 lines
4.8 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using ln.types;
|
|
using ln.logging;
|
|
namespace ln.http.websocket
|
|
{
|
|
public class WebSocketFrame
|
|
{
|
|
public bool FIN;
|
|
public bool RSV1;
|
|
public bool RSV2;
|
|
public bool RSV3;
|
|
|
|
public WebSocketOpcode Opcode = WebSocketOpcode.INVALIDOPCODE;
|
|
|
|
public bool Mask;
|
|
|
|
public int MaskingKey;
|
|
|
|
public byte[] ExtensionData;
|
|
public byte[] ApplicationData;
|
|
|
|
public byte[] Payload => ExtensionData.Concat(ApplicationData);
|
|
|
|
public WebSocketFrame()
|
|
{
|
|
ExtensionData = new byte[0];
|
|
ApplicationData = new byte[0];
|
|
}
|
|
|
|
public WebSocketFrame(WebSocketOpcode opcode)
|
|
:this(opcode,new byte[0])
|
|
{
|
|
}
|
|
public WebSocketFrame(WebSocketOpcode opcode,byte[] applicationData)
|
|
{
|
|
Opcode = opcode;
|
|
ExtensionData = new byte[0];
|
|
ApplicationData = applicationData;
|
|
}
|
|
|
|
public WebSocketFrame(string applicationData)
|
|
:this(Encoding.UTF8.GetBytes(applicationData),new byte[0])
|
|
{
|
|
Opcode = WebSocketOpcode.TEXT;
|
|
}
|
|
public WebSocketFrame(byte[] applicationData)
|
|
: this(applicationData, new byte[0]) { }
|
|
public WebSocketFrame(byte[] applicationData,byte[] extensionData)
|
|
{
|
|
FIN = true;
|
|
Opcode = WebSocketOpcode.BINARY;
|
|
ExtensionData = extensionData;
|
|
ApplicationData = applicationData;
|
|
}
|
|
|
|
public WebSocketFrame(Stream stream)
|
|
{
|
|
ReadFrom(stream);
|
|
}
|
|
|
|
public void ReadFrom(Stream stream)
|
|
{
|
|
int firstByte = stream.ReadByte();
|
|
if (firstByte == -1)
|
|
throw new IOException();
|
|
|
|
FIN = (firstByte & 0x80) != 0;
|
|
RSV1 = (firstByte & 0x40) != 0;
|
|
RSV3 = (firstByte & 0x20) != 0;
|
|
RSV1 = (firstByte & 0x10) != 0;
|
|
|
|
Opcode = (WebSocketOpcode)(firstByte & 0x0F);
|
|
|
|
Logging.Log(LogLevel.DEBUG, "WebSocket: FirstByte=0x{0:x8}",firstByte);
|
|
|
|
int secondByte = stream.ReadByte();
|
|
if (secondByte == -1)
|
|
throw new IOException();
|
|
|
|
Logging.Log(LogLevel.DEBUG, "WebSocket: SecondByte=0x{0:x8}", secondByte);
|
|
|
|
Mask = (secondByte & 0x80) != 0;
|
|
|
|
int pLength = (secondByte) & 0x7F;
|
|
if (pLength == 126)
|
|
{
|
|
pLength = stream.ReadUShort(true);
|
|
}
|
|
else if (pLength == 127)
|
|
{
|
|
ulong ulpLength = stream.ReadULong(true);
|
|
if (ulpLength > int.MaxValue)
|
|
throw new NotSupportedException(String.Format("Maximum supported frame size is: {0} bytes", int.MaxValue));
|
|
|
|
pLength = (int)ulpLength;
|
|
}
|
|
|
|
if (Mask)
|
|
{
|
|
MaskingKey = stream.ReadInteger();
|
|
}
|
|
|
|
ExtensionData = new byte[0];
|
|
ApplicationData = stream.ReadBytes(pLength);
|
|
|
|
if (Mask)
|
|
{
|
|
MaskPayload(ApplicationData, MaskingKey);
|
|
}
|
|
}
|
|
|
|
public void WriteTo(Stream stream)
|
|
{
|
|
stream.WriteByte(
|
|
(byte)(
|
|
(FIN ? 0x80 : 0x00) |
|
|
(RSV1 ? 0x40 : 0x00) |
|
|
(RSV2 ? 0x20 : 0x00) |
|
|
(RSV3 ? 0x10 : 0x00) |
|
|
(((int)Opcode) & 0x0F)
|
|
)
|
|
);
|
|
|
|
int dLength = ExtensionData.Length + ApplicationData.Length;
|
|
int pLength = 0;
|
|
|
|
if (dLength < 126)
|
|
{
|
|
pLength = (dLength) | (Mask ? 0x80 : 0x00);
|
|
|
|
stream.WriteByte((byte)pLength);
|
|
|
|
} else if (dLength < (1U<<16))
|
|
{
|
|
pLength = (126) | (Mask ? 0x80 : 0x00);
|
|
|
|
stream.WriteByte((byte)pLength);
|
|
stream.WriteBytes(((ushort)dLength).GetBytes(true));
|
|
}
|
|
else
|
|
{
|
|
pLength = (127) | (Mask ? 0x80 : 0x00);
|
|
|
|
stream.WriteByte((byte)pLength);
|
|
stream.WriteBytes(((ulong)dLength).GetBytes(true));
|
|
}
|
|
|
|
byte[] payload = Payload;
|
|
|
|
if (Mask)
|
|
{
|
|
stream.WriteBytes(MaskingKey.GetBytes());
|
|
MaskPayload(payload, MaskingKey);
|
|
}
|
|
|
|
stream.WriteBytes(payload);
|
|
|
|
stream.Flush();
|
|
}
|
|
|
|
public static void MaskPayload(byte[] data,int maskingKey)
|
|
{
|
|
byte[] mk = BitConverter.GetBytes(maskingKey);
|
|
for (int n = 0; n < data.Length; n++)
|
|
{
|
|
data[n] = (byte)(data[n] ^ mk[n % 4]);
|
|
}
|
|
}
|
|
|
|
|
|
static Random random = new Random();
|
|
}
|
|
}
|