|
|
|
|
@ -32,13 +32,13 @@ using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
|
|
|
|
namespace WebSocketSharp {
|
|
|
|
|
|
|
|
|
|
namespace WebSocketSharp
|
|
|
|
|
{
|
|
|
|
|
internal class WsFrame : IEnumerable<byte>
|
|
|
|
|
{
|
|
|
|
|
#region Private Constructors
|
|
|
|
|
|
|
|
|
|
private WsFrame()
|
|
|
|
|
private WsFrame ()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -46,26 +46,26 @@ namespace WebSocketSharp {
|
|
|
|
|
|
|
|
|
|
#region Public Constructors
|
|
|
|
|
|
|
|
|
|
public WsFrame(Opcode opcode, PayloadData payloadData)
|
|
|
|
|
: this(opcode, Mask.MASK, payloadData)
|
|
|
|
|
public WsFrame (Opcode opcode, PayloadData payload)
|
|
|
|
|
: this (opcode, Mask.MASK, payload)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public WsFrame(Opcode opcode, Mask mask, PayloadData payloadData)
|
|
|
|
|
: this(Fin.FINAL, opcode, mask, payloadData)
|
|
|
|
|
public WsFrame (Opcode opcode, Mask mask, PayloadData payload)
|
|
|
|
|
: this (Fin.FINAL, opcode, mask, payload)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public WsFrame(Fin fin, Opcode opcode, Mask mask, PayloadData payloadData)
|
|
|
|
|
: this(fin, opcode, mask, payloadData, false)
|
|
|
|
|
public WsFrame (Fin fin, Opcode opcode, Mask mask, PayloadData payload)
|
|
|
|
|
: this (fin, opcode, mask, payload, false)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public WsFrame(
|
|
|
|
|
Fin fin, Opcode opcode, Mask mask, PayloadData payloadData, bool compressed)
|
|
|
|
|
public WsFrame (
|
|
|
|
|
Fin fin, Opcode opcode, Mask mask, PayloadData payload, bool compressed)
|
|
|
|
|
{
|
|
|
|
|
Fin = fin;
|
|
|
|
|
Rsv1 = isData(opcode) && compressed ? Rsv.ON : Rsv.OFF;
|
|
|
|
|
Rsv1 = isData (opcode) && compressed ? Rsv.ON : Rsv.OFF;
|
|
|
|
|
Rsv2 = Rsv.OFF;
|
|
|
|
|
Rsv3 = Rsv.OFF;
|
|
|
|
|
Opcode = opcode;
|
|
|
|
|
@ -73,38 +73,38 @@ namespace WebSocketSharp {
|
|
|
|
|
|
|
|
|
|
/* PayloadLen */
|
|
|
|
|
|
|
|
|
|
ulong dataLen = payloadData.Length;
|
|
|
|
|
var dataLen = payload.Length;
|
|
|
|
|
var payloadLen = dataLen < 126
|
|
|
|
|
? (byte)dataLen
|
|
|
|
|
? (byte) dataLen
|
|
|
|
|
: dataLen < 0x010000
|
|
|
|
|
? (byte)126
|
|
|
|
|
: (byte)127;
|
|
|
|
|
? (byte) 126
|
|
|
|
|
: (byte) 127;
|
|
|
|
|
|
|
|
|
|
PayloadLen = payloadLen;
|
|
|
|
|
|
|
|
|
|
/* ExtPayloadLen */
|
|
|
|
|
|
|
|
|
|
ExtPayloadLen = payloadLen < 126
|
|
|
|
|
? new byte[]{}
|
|
|
|
|
? new byte []{}
|
|
|
|
|
: payloadLen == 126
|
|
|
|
|
? ((ushort)dataLen).ToByteArray(ByteOrder.BIG)
|
|
|
|
|
: dataLen.ToByteArray(ByteOrder.BIG);
|
|
|
|
|
? ((ushort) dataLen).ToByteArray (ByteOrder.BIG)
|
|
|
|
|
: dataLen.ToByteArray (ByteOrder.BIG);
|
|
|
|
|
|
|
|
|
|
/* MaskingKey */
|
|
|
|
|
|
|
|
|
|
var masking = mask == Mask.MASK;
|
|
|
|
|
var maskingKey = masking
|
|
|
|
|
? createMaskingKey()
|
|
|
|
|
: new byte[]{};
|
|
|
|
|
? createMaskingKey ()
|
|
|
|
|
: new byte []{};
|
|
|
|
|
|
|
|
|
|
MaskingKey = maskingKey;
|
|
|
|
|
|
|
|
|
|
/* PayloadData */
|
|
|
|
|
|
|
|
|
|
if (masking)
|
|
|
|
|
payloadData.Mask(maskingKey);
|
|
|
|
|
payload.Mask (maskingKey);
|
|
|
|
|
|
|
|
|
|
PayloadData = payloadData;
|
|
|
|
|
PayloadData = payload;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
@ -194,7 +194,7 @@ namespace WebSocketSharp {
|
|
|
|
|
|
|
|
|
|
internal ulong Length {
|
|
|
|
|
get {
|
|
|
|
|
return 2 + (ulong)(ExtPayloadLen.Length + MaskingKey.Length) + PayloadData.Length;
|
|
|
|
|
return 2 + (ulong) (ExtPayloadLen.Length + MaskingKey.Length) + PayloadData.Length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -216,9 +216,9 @@ namespace WebSocketSharp {
|
|
|
|
|
|
|
|
|
|
public byte PayloadLen { get; private set; }
|
|
|
|
|
|
|
|
|
|
public byte[] ExtPayloadLen { get; private set; }
|
|
|
|
|
public byte [] ExtPayloadLen { get; private set; }
|
|
|
|
|
|
|
|
|
|
public byte[] MaskingKey { get; private set; }
|
|
|
|
|
public byte [] MaskingKey { get; private set; }
|
|
|
|
|
|
|
|
|
|
public PayloadData PayloadData { get; private set; }
|
|
|
|
|
|
|
|
|
|
@ -226,26 +226,20 @@ namespace WebSocketSharp {
|
|
|
|
|
|
|
|
|
|
#region Private Methods
|
|
|
|
|
|
|
|
|
|
private static WsFrame createCloseFrame(CloseStatusCode code, string reason, Mask mask)
|
|
|
|
|
{
|
|
|
|
|
var data = ((ushort)code).Append(reason);
|
|
|
|
|
return new WsFrame(Fin.FINAL, Opcode.CLOSE, mask, new PayloadData(data));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static byte[] createMaskingKey()
|
|
|
|
|
private static byte [] createMaskingKey ()
|
|
|
|
|
{
|
|
|
|
|
var key = new byte[4];
|
|
|
|
|
var rand = new Random();
|
|
|
|
|
rand.NextBytes(key);
|
|
|
|
|
var key = new byte [4];
|
|
|
|
|
var rand = new Random ();
|
|
|
|
|
rand.NextBytes (key);
|
|
|
|
|
|
|
|
|
|
return key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void dump(WsFrame frame)
|
|
|
|
|
private static void dump (WsFrame frame)
|
|
|
|
|
{
|
|
|
|
|
var len = frame.Length;
|
|
|
|
|
var count = (long)(len / 4);
|
|
|
|
|
var remainder = (int)(len % 4);
|
|
|
|
|
var count = (long) (len / 4);
|
|
|
|
|
var remainder = (int) (len % 4);
|
|
|
|
|
|
|
|
|
|
int countDigit;
|
|
|
|
|
string countFmt;
|
|
|
|
|
@ -270,120 +264,121 @@ namespace WebSocketSharp {
|
|
|
|
|
countFmt = "{0,16:X}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var spFmt = String.Format("{{0,{0}}}", countDigit);
|
|
|
|
|
var headerFmt = String.Format(@"
|
|
|
|
|
var spFmt = String.Format ("{{0,{0}}}", countDigit);
|
|
|
|
|
var headerFmt = String.Format (@"
|
|
|
|
|
{0} 01234567 89ABCDEF 01234567 89ABCDEF
|
|
|
|
|
{0}+--------+--------+--------+--------+", spFmt);
|
|
|
|
|
var footerFmt = String.Format(" {0}+--------+--------+--------+--------+", spFmt);
|
|
|
|
|
var footerFmt = String.Format (" {0}+--------+--------+--------+--------+", spFmt);
|
|
|
|
|
|
|
|
|
|
Func<Action<string, string, string, string>> linePrinter = () =>
|
|
|
|
|
{
|
|
|
|
|
long lineCount = 0;
|
|
|
|
|
var lineFmt = String.Format(" {0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|", countFmt);
|
|
|
|
|
var lineFmt = String.Format (" {0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|", countFmt);
|
|
|
|
|
return (arg1, arg2, arg3, arg4) =>
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(lineFmt, ++lineCount, arg1, arg2, arg3, arg4);
|
|
|
|
|
Console.WriteLine (lineFmt, ++lineCount, arg1, arg2, arg3, arg4);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
var printLine = linePrinter();
|
|
|
|
|
var printLine = linePrinter ();
|
|
|
|
|
|
|
|
|
|
Console.WriteLine(headerFmt, String.Empty);
|
|
|
|
|
Console.WriteLine (headerFmt, String.Empty);
|
|
|
|
|
|
|
|
|
|
var buffer = frame.ToByteArray();
|
|
|
|
|
var buffer = frame.ToByteArray ();
|
|
|
|
|
int i, j;
|
|
|
|
|
for (i = 0; i <= count; i++)
|
|
|
|
|
{
|
|
|
|
|
j = i * 4;
|
|
|
|
|
if (i < count)
|
|
|
|
|
printLine(
|
|
|
|
|
Convert.ToString(buffer[j], 2).PadLeft(8, '0'),
|
|
|
|
|
Convert.ToString(buffer[j + 1], 2).PadLeft(8, '0'),
|
|
|
|
|
Convert.ToString(buffer[j + 2], 2).PadLeft(8, '0'),
|
|
|
|
|
Convert.ToString(buffer[j + 3], 2).PadLeft(8, '0'));
|
|
|
|
|
printLine (
|
|
|
|
|
Convert.ToString (buffer [j], 2).PadLeft (8, '0'),
|
|
|
|
|
Convert.ToString (buffer [j + 1], 2).PadLeft (8, '0'),
|
|
|
|
|
Convert.ToString (buffer [j + 2], 2).PadLeft (8, '0'),
|
|
|
|
|
Convert.ToString (buffer [j + 3], 2).PadLeft (8, '0'));
|
|
|
|
|
else if (remainder > 0)
|
|
|
|
|
printLine(
|
|
|
|
|
Convert.ToString(buffer[j], 2).PadLeft(8, '0'),
|
|
|
|
|
remainder >= 2 ? Convert.ToString(buffer[j + 1], 2).PadLeft(8, '0') : String.Empty,
|
|
|
|
|
remainder == 3 ? Convert.ToString(buffer[j + 2], 2).PadLeft(8, '0') : String.Empty,
|
|
|
|
|
printLine (
|
|
|
|
|
Convert.ToString (buffer [j], 2).PadLeft (8, '0'),
|
|
|
|
|
remainder >= 2 ? Convert.ToString (buffer [j + 1], 2).PadLeft (8, '0') : String.Empty,
|
|
|
|
|
remainder == 3 ? Convert.ToString (buffer [j + 2], 2).PadLeft (8, '0') : String.Empty,
|
|
|
|
|
String.Empty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Console.WriteLine(footerFmt, String.Empty);
|
|
|
|
|
Console.WriteLine (footerFmt, String.Empty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isBinary(Opcode opcode)
|
|
|
|
|
private static bool isBinary (Opcode opcode)
|
|
|
|
|
{
|
|
|
|
|
return opcode == Opcode.BINARY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isClose(Opcode opcode)
|
|
|
|
|
private static bool isClose (Opcode opcode)
|
|
|
|
|
{
|
|
|
|
|
return opcode == Opcode.CLOSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isContinuation(Opcode opcode)
|
|
|
|
|
private static bool isContinuation (Opcode opcode)
|
|
|
|
|
{
|
|
|
|
|
return opcode == Opcode.CONT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isControl(Opcode opcode)
|
|
|
|
|
private static bool isControl (Opcode opcode)
|
|
|
|
|
{
|
|
|
|
|
return opcode == Opcode.CLOSE || opcode == Opcode.PING || opcode == Opcode.PONG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isData(Opcode opcode)
|
|
|
|
|
private static bool isData (Opcode opcode)
|
|
|
|
|
{
|
|
|
|
|
return opcode == Opcode.TEXT || opcode == Opcode.BINARY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isFinal(Fin fin)
|
|
|
|
|
private static bool isFinal (Fin fin)
|
|
|
|
|
{
|
|
|
|
|
return fin == Fin.FINAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isMasked(Mask mask)
|
|
|
|
|
private static bool isMasked (Mask mask)
|
|
|
|
|
{
|
|
|
|
|
return mask == Mask.MASK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isPing(Opcode opcode)
|
|
|
|
|
private static bool isPing (Opcode opcode)
|
|
|
|
|
{
|
|
|
|
|
return opcode == Opcode.PING;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isPong(Opcode opcode)
|
|
|
|
|
private static bool isPong (Opcode opcode)
|
|
|
|
|
{
|
|
|
|
|
return opcode == Opcode.PONG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool isText(Opcode opcode)
|
|
|
|
|
private static bool isText (Opcode opcode)
|
|
|
|
|
{
|
|
|
|
|
return opcode == Opcode.TEXT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static WsFrame parse(byte[] header, Stream stream, bool unmask)
|
|
|
|
|
private static WsFrame parse (byte [] header, Stream stream, bool unmask)
|
|
|
|
|
{
|
|
|
|
|
/* Header */
|
|
|
|
|
|
|
|
|
|
// FIN
|
|
|
|
|
var fin = (header[0] & 0x80) == 0x80 ? Fin.FINAL : Fin.MORE;
|
|
|
|
|
var fin = (header [0] & 0x80) == 0x80 ? Fin.FINAL : Fin.MORE;
|
|
|
|
|
// RSV1
|
|
|
|
|
var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.ON : Rsv.OFF;
|
|
|
|
|
var rsv1 = (header [0] & 0x40) == 0x40 ? Rsv.ON : Rsv.OFF;
|
|
|
|
|
// RSV2
|
|
|
|
|
var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.ON : Rsv.OFF;
|
|
|
|
|
var rsv2 = (header [0] & 0x20) == 0x20 ? Rsv.ON : Rsv.OFF;
|
|
|
|
|
// RSV3
|
|
|
|
|
var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.ON : Rsv.OFF;
|
|
|
|
|
var rsv3 = (header [0] & 0x10) == 0x10 ? Rsv.ON : Rsv.OFF;
|
|
|
|
|
// Opcode
|
|
|
|
|
var opcode = (Opcode)(header[0] & 0x0f);
|
|
|
|
|
var opcode = (Opcode) (header [0] & 0x0f);
|
|
|
|
|
// MASK
|
|
|
|
|
var mask = (header[1] & 0x80) == 0x80 ? Mask.MASK : Mask.UNMASK;
|
|
|
|
|
var mask = (header [1] & 0x80) == 0x80 ? Mask.MASK : Mask.UNMASK;
|
|
|
|
|
// Payload len
|
|
|
|
|
var payloadLen = (byte)(header[1] & 0x7f);
|
|
|
|
|
var payloadLen = (byte) (header [1] & 0x7f);
|
|
|
|
|
|
|
|
|
|
if (isControl(opcode) && payloadLen > 125)
|
|
|
|
|
return createCloseFrame(CloseStatusCode.INCONSISTENT_DATA,
|
|
|
|
|
"The payload length of a control frame must be 125 bytes or less.",
|
|
|
|
|
Mask.UNMASK);
|
|
|
|
|
if (isControl (opcode) && payloadLen > 125)
|
|
|
|
|
return CreateCloseFrame (
|
|
|
|
|
Mask.UNMASK,
|
|
|
|
|
CloseStatusCode.INCONSISTENT_DATA,
|
|
|
|
|
"The payload length of a control frame must be 125 bytes or less.");
|
|
|
|
|
|
|
|
|
|
var frame = new WsFrame {
|
|
|
|
|
Fin = fin,
|
|
|
|
|
@ -404,27 +399,29 @@ namespace WebSocketSharp {
|
|
|
|
|
: 8;
|
|
|
|
|
|
|
|
|
|
var extPayloadLen = extLen > 0
|
|
|
|
|
? stream.ReadBytesInternal(extLen)
|
|
|
|
|
: new byte[]{};
|
|
|
|
|
? stream.ReadBytesInternal (extLen)
|
|
|
|
|
: new byte []{};
|
|
|
|
|
|
|
|
|
|
if (extLen > 0 && extPayloadLen.Length != extLen)
|
|
|
|
|
return createCloseFrame(CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Extended Payload Length' of a frame cannot be read from the data stream.",
|
|
|
|
|
Mask.UNMASK);
|
|
|
|
|
return CreateCloseFrame (
|
|
|
|
|
Mask.UNMASK,
|
|
|
|
|
CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Extended Payload Length' of a frame cannot be read from the data stream.");
|
|
|
|
|
|
|
|
|
|
frame.ExtPayloadLen = extPayloadLen;
|
|
|
|
|
|
|
|
|
|
/* Masking Key */
|
|
|
|
|
|
|
|
|
|
var masked = mask == Mask.MASK ? true : false;
|
|
|
|
|
var masked = mask == Mask.MASK;
|
|
|
|
|
var maskingKey = masked
|
|
|
|
|
? stream.ReadBytesInternal(4)
|
|
|
|
|
: new byte[]{};
|
|
|
|
|
? stream.ReadBytesInternal (4)
|
|
|
|
|
: new byte []{};
|
|
|
|
|
|
|
|
|
|
if (masked && maskingKey.Length != 4)
|
|
|
|
|
return createCloseFrame(CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Masking Key' of a frame cannot be read from the data stream.",
|
|
|
|
|
Mask.UNMASK);
|
|
|
|
|
return CreateCloseFrame (
|
|
|
|
|
Mask.UNMASK,
|
|
|
|
|
CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Masking Key' of a frame cannot be read from the data stream.");
|
|
|
|
|
|
|
|
|
|
frame.MaskingKey = maskingKey;
|
|
|
|
|
|
|
|
|
|
@ -433,64 +430,65 @@ namespace WebSocketSharp {
|
|
|
|
|
ulong dataLen = payloadLen < 126
|
|
|
|
|
? payloadLen
|
|
|
|
|
: payloadLen == 126
|
|
|
|
|
? extPayloadLen.To<ushort>(ByteOrder.BIG)
|
|
|
|
|
: extPayloadLen.To<ulong>(ByteOrder.BIG);
|
|
|
|
|
? extPayloadLen.To<ushort> (ByteOrder.BIG)
|
|
|
|
|
: extPayloadLen.To<ulong> (ByteOrder.BIG);
|
|
|
|
|
|
|
|
|
|
byte[] data = null;
|
|
|
|
|
byte [] data = null;
|
|
|
|
|
if (dataLen > 0)
|
|
|
|
|
{
|
|
|
|
|
if (payloadLen > 126 && dataLen > PayloadData.MaxLength)
|
|
|
|
|
{
|
|
|
|
|
var code = CloseStatusCode.TOO_BIG;
|
|
|
|
|
return createCloseFrame(code, code.GetMessage(), Mask.UNMASK);
|
|
|
|
|
return CreateCloseFrame (Mask.UNMASK, code, code.GetMessage ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = dataLen > 1024
|
|
|
|
|
? stream.ReadBytesInternal((long)dataLen, 1024)
|
|
|
|
|
: stream.ReadBytesInternal((int)dataLen);
|
|
|
|
|
|
|
|
|
|
if (data.LongLength != (long)dataLen)
|
|
|
|
|
return createCloseFrame(CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Payload Data' of a frame cannot be read from the data stream.",
|
|
|
|
|
Mask.UNMASK);
|
|
|
|
|
? stream.ReadBytesInternal ((long) dataLen, 1024)
|
|
|
|
|
: stream.ReadBytesInternal ((int) dataLen);
|
|
|
|
|
|
|
|
|
|
if (data.LongLength != (long) dataLen)
|
|
|
|
|
return CreateCloseFrame (
|
|
|
|
|
Mask.UNMASK,
|
|
|
|
|
CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Payload Data' of a frame cannot be read from the data stream.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
data = new byte[]{};
|
|
|
|
|
data = new byte []{};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var payloadData = new PayloadData(data, masked);
|
|
|
|
|
var payload = new PayloadData (data, masked);
|
|
|
|
|
if (masked && unmask)
|
|
|
|
|
{
|
|
|
|
|
payloadData.Mask(maskingKey);
|
|
|
|
|
payload.Mask (maskingKey);
|
|
|
|
|
frame.Mask = Mask.UNMASK;
|
|
|
|
|
frame.MaskingKey = new byte[]{};
|
|
|
|
|
frame.MaskingKey = new byte []{};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frame.PayloadData = payloadData;
|
|
|
|
|
frame.PayloadData = payload;
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void print(WsFrame frame)
|
|
|
|
|
private static void print (WsFrame frame)
|
|
|
|
|
{
|
|
|
|
|
var len = frame.ExtPayloadLen.Length;
|
|
|
|
|
var extPayloadLen = len == 2
|
|
|
|
|
? frame.ExtPayloadLen.To<ushort>(ByteOrder.BIG).ToString()
|
|
|
|
|
? frame.ExtPayloadLen.To<ushort> (ByteOrder.BIG).ToString ()
|
|
|
|
|
: len == 8
|
|
|
|
|
? frame.ExtPayloadLen.To<ulong>(ByteOrder.BIG).ToString()
|
|
|
|
|
? frame.ExtPayloadLen.To<ulong> (ByteOrder.BIG).ToString ()
|
|
|
|
|
: String.Empty;
|
|
|
|
|
|
|
|
|
|
var masked = frame.IsMasked;
|
|
|
|
|
var maskingKey = masked
|
|
|
|
|
? BitConverter.ToString(frame.MaskingKey)
|
|
|
|
|
? BitConverter.ToString (frame.MaskingKey)
|
|
|
|
|
: String.Empty;
|
|
|
|
|
|
|
|
|
|
var opcode = frame.Opcode;
|
|
|
|
|
var payloadData = frame.PayloadData.Length == 0
|
|
|
|
|
? String.Empty
|
|
|
|
|
: masked || frame.IsFragmented || frame.IsBinary || frame.IsClose
|
|
|
|
|
? BitConverter.ToString(frame.PayloadData.ToByteArray())
|
|
|
|
|
: Encoding.UTF8.GetString(frame.PayloadData.ToByteArray());
|
|
|
|
|
? BitConverter.ToString (frame.PayloadData.ToByteArray ())
|
|
|
|
|
: Encoding.UTF8.GetString (frame.PayloadData.ToByteArray ());
|
|
|
|
|
|
|
|
|
|
var format = @"
|
|
|
|
|
FIN: {0}
|
|
|
|
|
@ -504,168 +502,210 @@ namespace WebSocketSharp {
|
|
|
|
|
Masking Key: {8}
|
|
|
|
|
Payload Data: {9}";
|
|
|
|
|
|
|
|
|
|
Console.WriteLine(
|
|
|
|
|
Console.WriteLine (
|
|
|
|
|
format, frame.Fin, frame.Rsv1, frame.Rsv2, frame.Rsv3, opcode, frame.Mask, frame.PayloadLen, extPayloadLen, maskingKey, payloadData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Internal Methods
|
|
|
|
|
|
|
|
|
|
internal static WsFrame CreateCloseFrame (Mask mask, PayloadData payload)
|
|
|
|
|
{
|
|
|
|
|
return new WsFrame (Opcode.CLOSE, mask, payload);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal static WsFrame CreatePongFrame (Mask mask, PayloadData payload)
|
|
|
|
|
{
|
|
|
|
|
return new WsFrame (Opcode.PONG, mask, payload);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Public Methods
|
|
|
|
|
|
|
|
|
|
public IEnumerator<byte> GetEnumerator()
|
|
|
|
|
public static WsFrame CreateCloseFrame (Mask mask, byte [] data)
|
|
|
|
|
{
|
|
|
|
|
return new WsFrame (Opcode.CLOSE, mask, new PayloadData (data));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WsFrame CreateCloseFrame (Mask mask, CloseStatusCode code, string reason)
|
|
|
|
|
{
|
|
|
|
|
return new WsFrame (Opcode.CLOSE, mask, new PayloadData (((ushort) code).Append (reason)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WsFrame CreateFrame (
|
|
|
|
|
Fin fin, Opcode opcode, Mask mask, byte [] data, bool compressed)
|
|
|
|
|
{
|
|
|
|
|
return new WsFrame (fin, opcode, mask, new PayloadData (data), compressed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WsFrame CreatePingFrame (Mask mask)
|
|
|
|
|
{
|
|
|
|
|
return new WsFrame (Opcode.PING, mask, new PayloadData ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WsFrame CreatePingFrame (Mask mask, byte [] data)
|
|
|
|
|
{
|
|
|
|
|
return new WsFrame (Opcode.PING, mask, new PayloadData (data));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerator<byte> GetEnumerator ()
|
|
|
|
|
{
|
|
|
|
|
foreach (byte b in ToByteArray())
|
|
|
|
|
foreach (byte b in ToByteArray ())
|
|
|
|
|
yield return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WsFrame Parse(byte[] src)
|
|
|
|
|
public static WsFrame Parse (byte [] src)
|
|
|
|
|
{
|
|
|
|
|
return Parse(src, true);
|
|
|
|
|
return Parse (src, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WsFrame Parse(Stream stream)
|
|
|
|
|
public static WsFrame Parse (Stream stream)
|
|
|
|
|
{
|
|
|
|
|
return Parse(stream, true);
|
|
|
|
|
return Parse (stream, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WsFrame Parse(byte[] src, bool unmask)
|
|
|
|
|
public static WsFrame Parse (byte [] src, bool unmask)
|
|
|
|
|
{
|
|
|
|
|
using (MemoryStream ms = new MemoryStream(src))
|
|
|
|
|
using (var stream = new MemoryStream (src))
|
|
|
|
|
{
|
|
|
|
|
return Parse(ms, unmask);
|
|
|
|
|
return Parse (stream, unmask);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WsFrame Parse(Stream stream, bool unmask)
|
|
|
|
|
public static WsFrame Parse (Stream stream, bool unmask)
|
|
|
|
|
{
|
|
|
|
|
return Parse(stream, unmask, null);
|
|
|
|
|
return Parse (stream, unmask, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static WsFrame Parse(Stream stream, bool unmask, Action<Exception> error)
|
|
|
|
|
public static WsFrame Parse (Stream stream, bool unmask, Action<Exception> error)
|
|
|
|
|
{
|
|
|
|
|
WsFrame frame = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var header = stream.ReadBytesInternal(2);
|
|
|
|
|
var header = stream.ReadBytesInternal (2);
|
|
|
|
|
frame = header.Length == 2
|
|
|
|
|
? parse(header, stream, unmask)
|
|
|
|
|
: createCloseFrame(CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Header' of a frame cannot be read from the data stream.",
|
|
|
|
|
Mask.UNMASK);
|
|
|
|
|
? parse (header, stream, unmask)
|
|
|
|
|
: CreateCloseFrame (
|
|
|
|
|
Mask.UNMASK,
|
|
|
|
|
CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Header' of a frame cannot be read from the data stream.");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (error != null)
|
|
|
|
|
error(ex);
|
|
|
|
|
error (ex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void ParseAsync(Stream stream, Action<WsFrame> completed)
|
|
|
|
|
public static void ParseAsync (Stream stream, Action<WsFrame> completed)
|
|
|
|
|
{
|
|
|
|
|
ParseAsync(stream, true, completed, null);
|
|
|
|
|
ParseAsync (stream, true, completed, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void ParseAsync(Stream stream, Action<WsFrame> completed, Action<Exception> error)
|
|
|
|
|
public static void ParseAsync (Stream stream, Action<WsFrame> completed, Action<Exception> error)
|
|
|
|
|
{
|
|
|
|
|
ParseAsync(stream, true, completed, error);
|
|
|
|
|
ParseAsync (stream, true, completed, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void ParseAsync(
|
|
|
|
|
public static void ParseAsync (
|
|
|
|
|
Stream stream, bool unmask, Action<WsFrame> completed, Action<Exception> error)
|
|
|
|
|
{
|
|
|
|
|
var header = new byte[2];
|
|
|
|
|
var header = new byte [2];
|
|
|
|
|
AsyncCallback callback = ar =>
|
|
|
|
|
{
|
|
|
|
|
WsFrame frame = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var readLen = stream.EndRead(ar);
|
|
|
|
|
var readLen = stream.EndRead (ar);
|
|
|
|
|
if (readLen == 1)
|
|
|
|
|
{
|
|
|
|
|
var tmp = stream.ReadByte();
|
|
|
|
|
var tmp = stream.ReadByte ();
|
|
|
|
|
if (tmp > -1)
|
|
|
|
|
{
|
|
|
|
|
header[1] = (byte)tmp;
|
|
|
|
|
header [1] = (byte) tmp;
|
|
|
|
|
readLen++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frame = readLen == 2
|
|
|
|
|
? parse(header, stream, unmask)
|
|
|
|
|
: createCloseFrame(CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Header' of a frame cannot be read from the data stream.",
|
|
|
|
|
Mask.UNMASK);
|
|
|
|
|
? parse (header, stream, unmask)
|
|
|
|
|
: CreateCloseFrame (
|
|
|
|
|
Mask.UNMASK,
|
|
|
|
|
CloseStatusCode.ABNORMAL,
|
|
|
|
|
"'Header' of a frame cannot be read from the data stream.");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
if (error != null)
|
|
|
|
|
error(ex);
|
|
|
|
|
error (ex);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (completed != null)
|
|
|
|
|
completed(frame);
|
|
|
|
|
completed (frame);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
stream.BeginRead(header, 0, 2, callback, null);
|
|
|
|
|
stream.BeginRead (header, 0, 2, callback, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Print(bool dumped)
|
|
|
|
|
public void Print (bool dumped)
|
|
|
|
|
{
|
|
|
|
|
if (dumped)
|
|
|
|
|
dump(this);
|
|
|
|
|
dump (this);
|
|
|
|
|
else
|
|
|
|
|
print(this);
|
|
|
|
|
print (this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] ToByteArray()
|
|
|
|
|
public byte [] ToByteArray()
|
|
|
|
|
{
|
|
|
|
|
using (var buffer = new MemoryStream())
|
|
|
|
|
using (var buffer = new MemoryStream ())
|
|
|
|
|
{
|
|
|
|
|
int 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) + (int)PayloadLen;
|
|
|
|
|
buffer.Write(((ushort)header).ToByteArray(ByteOrder.BIG), 0, 2);
|
|
|
|
|
int 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) + (int) PayloadLen;
|
|
|
|
|
buffer.Write (((ushort) header).ToByteArray (ByteOrder.BIG), 0, 2);
|
|
|
|
|
|
|
|
|
|
if (PayloadLen > 125)
|
|
|
|
|
buffer.Write(ExtPayloadLen, 0, ExtPayloadLen.Length);
|
|
|
|
|
buffer.Write (ExtPayloadLen, 0, ExtPayloadLen.Length);
|
|
|
|
|
|
|
|
|
|
if (Mask == Mask.MASK)
|
|
|
|
|
buffer.Write(MaskingKey, 0, MaskingKey.Length);
|
|
|
|
|
buffer.Write (MaskingKey, 0, MaskingKey.Length);
|
|
|
|
|
|
|
|
|
|
if (PayloadLen > 0)
|
|
|
|
|
{
|
|
|
|
|
var payload = PayloadData.ToByteArray();
|
|
|
|
|
var payload = PayloadData.ToByteArray ();
|
|
|
|
|
if (PayloadLen < 127)
|
|
|
|
|
buffer.Write(payload, 0, payload.Length);
|
|
|
|
|
buffer.Write (payload, 0, payload.Length);
|
|
|
|
|
else
|
|
|
|
|
buffer.WriteBytes(payload);
|
|
|
|
|
buffer.WriteBytes (payload);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer.Close();
|
|
|
|
|
return buffer.ToArray();
|
|
|
|
|
buffer.Close ();
|
|
|
|
|
return buffer.ToArray ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
public override string ToString ()
|
|
|
|
|
{
|
|
|
|
|
return BitConverter.ToString(ToByteArray());
|
|
|
|
|
return BitConverter.ToString (ToByteArray ());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Explicitly Implemented Interface Members
|
|
|
|
|
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
|
|
|
IEnumerator IEnumerable.GetEnumerator ()
|
|
|
|
|
{
|
|
|
|
|
return GetEnumerator();
|
|
|
|
|
return GetEnumerator ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|