Files
Stationeers-RemoteControl/Vendor/EmbedIO-3.5.2/WebSockets/Internal/WebSocketFrame.cs

239 lines
7.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using EmbedIO.Net.Internal;
using Swan;
namespace EmbedIO.WebSockets.Internal
{
internal class WebSocketFrame
{
internal static readonly byte[] EmptyPingBytes = CreatePingFrame().ToArray();
internal WebSocketFrame(Opcode opcode, PayloadData payloadData)
: this(Fin.Final, opcode, payloadData)
{
}
internal WebSocketFrame(Fin fin, Opcode opcode, byte[] data, bool compressed)
: this(fin, opcode, new PayloadData(data), compressed)
{
}
internal WebSocketFrame(
Fin fin,
Opcode opcode,
PayloadData payloadData,
bool compressed = false)
{
Fin = fin;
Rsv1 = IsOpcodeData(opcode) && compressed ? Rsv.On : Rsv.Off;
Rsv2 = Rsv.Off;
Rsv3 = Rsv.Off;
Opcode = opcode;
var len = payloadData.Length;
if (len < 126)
{
PayloadLength = (byte)len;
ExtendedPayloadLength = Array.Empty<byte>();
}
else if (len < 0x010000)
{
PayloadLength = 126;
ExtendedPayloadLength = ((ushort)len).ToByteArray(Endianness.Big);
}
else
{
PayloadLength = 127;
ExtendedPayloadLength = len.ToByteArray(Endianness.Big);
}
Mask = Mask.Off;
MaskingKey = Array.Empty<byte>();
PayloadData = payloadData;
}
internal WebSocketFrame(
Fin fin,
Rsv rsv1,
Rsv rsv2,
Rsv rsv3,
Opcode opcode,
Mask mask,
byte payloadLength)
{
Fin = fin;
Rsv1 = rsv1;
Rsv2 = rsv2;
Rsv3 = rsv3;
Opcode = opcode;
Mask = mask;
PayloadLength = payloadLength;
}
public byte[]? ExtendedPayloadLength { get; internal set; }
public Fin Fin { get; internal set; }
public bool IsCompressed => Rsv1 == Rsv.On;
public bool IsFragment => Fin == Fin.More || Opcode == Opcode.Cont;
public bool IsMasked => Mask == Mask.On;
public Mask Mask { get; internal set; }
public byte[] MaskingKey { get; internal set; }
public Opcode Opcode { get; internal set; }
public PayloadData PayloadData { get; internal set; }
public byte PayloadLength { get; internal set; }
public Rsv Rsv1 { get; internal set; }
public Rsv Rsv2 { get; internal set; }
public Rsv Rsv3 { get; internal set; }
internal int ExtendedPayloadLengthCount => PayloadLength < 126 ? 0 : (PayloadLength == 126 ? 2 : 8);
internal ulong FullPayloadLength => PayloadLength < 126
? PayloadLength
: PayloadLength == 126
? BitConverter.ToUInt16(ExtendedPayloadLength.ToHostOrder(Endianness.Big), 0)
: BitConverter.ToUInt64(ExtendedPayloadLength.ToHostOrder(Endianness.Big), 0);
public IEnumerator<byte> GetEnumerator() => ((IEnumerable<byte>)ToArray()).GetEnumerator();
public string PrintToString()
{
// Payload Length
var payloadLen = PayloadLength;
// Extended Payload Length
var extPayloadLen = payloadLen > 125 ? FullPayloadLength.ToString(CultureInfo.InvariantCulture) : string.Empty;
// Masking Key
var maskingKey = BitConverter.ToString(MaskingKey);
// Payload Data
var payload = payloadLen == 0
? string.Empty
: payloadLen > 125
? "---"
: Opcode == Opcode.Text && !(IsFragment || IsMasked || IsCompressed)
? PayloadData.ApplicationData.ToArray().ToText()
: PayloadData.ToString();
return $@"
FIN: {Fin}
RSV1: {Rsv1}
RSV2: {Rsv2}
RSV3: {Rsv3}
Opcode: {Opcode}
MASK: {Mask}
Payload Length: {payloadLen}
Extended Payload Length: {extPayloadLen}
Masking Key: {maskingKey}
Payload Data: {payload}";
}
public byte[] ToArray()
{
using var buff = new MemoryStream();
var header = (int)Fin;
header = (header << 1) + (int)Rsv1;
header = (header << 1) + (int)Rsv2;
header = (header << 1) + (int)Rsv3;
header = (header << 4) + (int)Opcode;
header = (header << 1) + (int)Mask;
header = (header << 7) + PayloadLength;
buff.Write(((ushort)header).ToByteArray(Endianness.Big), 0, 2);
if (PayloadLength > 125)
{
buff.Write(ExtendedPayloadLength, 0, PayloadLength == 126 ? 2 : 8);
}
if (Mask == Mask.On)
{
buff.Write(MaskingKey, 0, 4);
}
if (PayloadLength > 0)
{
var bytes = PayloadData.ToArray();
if (PayloadLength < 127)
{
buff.Write(bytes, 0, bytes.Length);
}
else
{
using var input = new MemoryStream(bytes);
input.CopyTo(buff, 1024);
}
}
return buff.ToArray();
}
public override string ToString() => BitConverter.ToString(ToArray());
internal static WebSocketFrame CreateCloseFrame(PayloadData? payloadData) => new (Fin.Final, Opcode.Close, payloadData ?? new PayloadData());
internal static WebSocketFrame CreatePingFrame() => new (Fin.Final, Opcode.Ping, new PayloadData());
internal static WebSocketFrame CreatePingFrame(byte[] data) => new (Fin.Final, Opcode.Ping, new PayloadData(data));
internal void Validate(WebSocket webSocket)
{
if (!IsMasked)
{
throw new WebSocketException(CloseStatusCode.ProtocolError, "A frame from a client isn't masked.");
}
if (webSocket.InContinuation && (Opcode == Opcode.Text || Opcode == Opcode.Binary))
{
throw new WebSocketException(CloseStatusCode.ProtocolError,
"A data frame has been received while receiving continuation frames.");
}
if (IsCompressed && webSocket.Compression == CompressionMethod.None)
{
throw new WebSocketException(CloseStatusCode.ProtocolError,
"A compressed frame has been received without any agreement for it.");
}
if (Rsv2 == Rsv.On)
{
throw new WebSocketException(CloseStatusCode.ProtocolError,
"The RSV2 of a frame is non-zero without any negotiation for it.");
}
if (Rsv3 == Rsv.On)
{
throw new WebSocketException(CloseStatusCode.ProtocolError,
"The RSV3 of a frame is non-zero without any negotiation for it.");
}
}
internal void Unmask()
{
if (Mask == Mask.Off)
{
return;
}
Mask = Mask.Off;
PayloadData.Mask(MaskingKey);
MaskingKey = Array.Empty<byte>();
}
private static bool IsOpcodeData(Opcode opcode) => opcode == Opcode.Text || opcode == Opcode.Binary;
}
}