2013-04-08 14:11:57 +08:00
|
|
|
#region License
|
2013-01-11 19:32:38 +08:00
|
|
|
/*
|
2012-07-31 09:36:52 +08:00
|
|
|
* WsFrame.cs
|
|
|
|
*
|
|
|
|
* The MIT License
|
|
|
|
*
|
2013-01-15 14:29:05 +08:00
|
|
|
* Copyright (c) 2012-2013 sta.blockhead
|
2012-07-31 09:36:52 +08:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.IO;
|
2012-10-04 14:04:21 +08:00
|
|
|
using System.Collections;
|
2012-07-31 09:36:52 +08:00
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Text;
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
namespace WebSocketSharp
|
|
|
|
{
|
2013-01-15 14:29:05 +08:00
|
|
|
internal class WsFrame : IEnumerable<byte>
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
2013-10-10 15:38:41 +08:00
|
|
|
#region Internal Static Fields
|
|
|
|
|
|
|
|
internal static readonly byte [] EmptyUnmaskPingData;
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Static Constructor
|
|
|
|
|
|
|
|
static WsFrame ()
|
|
|
|
{
|
2014-03-01 14:16:25 +08:00
|
|
|
EmptyUnmaskPingData = CreatePingFrame (Mask.Unmask).ToByteArray ();
|
2013-10-10 15:38:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2013-04-08 14:11:57 +08:00
|
|
|
#region Private Constructors
|
2012-07-31 09:36:52 +08:00
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private WsFrame ()
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Public Constructors
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public WsFrame (Opcode opcode, PayloadData payload)
|
2014-03-01 14:16:25 +08:00
|
|
|
: this (opcode, Mask.Mask, payload)
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public WsFrame (Opcode opcode, Mask mask, PayloadData payload)
|
2014-03-01 14:00:51 +08:00
|
|
|
: this (Fin.Final, opcode, mask, payload)
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public WsFrame (Fin fin, Opcode opcode, Mask mask, PayloadData payload)
|
|
|
|
: this (fin, opcode, mask, payload, false)
|
2013-04-23 17:08:42 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public WsFrame (
|
|
|
|
Fin fin, Opcode opcode, Mask mask, PayloadData payload, bool compressed)
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
2013-04-12 13:42:25 +08:00
|
|
|
Fin = fin;
|
2014-03-01 14:24:03 +08:00
|
|
|
Rsv1 = isData (opcode) && compressed ? Rsv.On : Rsv.Off;
|
|
|
|
Rsv2 = Rsv.Off;
|
|
|
|
Rsv3 = Rsv.Off;
|
2013-04-12 13:42:25 +08:00
|
|
|
Opcode = opcode;
|
|
|
|
Mask = mask;
|
2012-08-19 15:41:59 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
/* PayloadLen */
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
var dataLen = payload.Length;
|
2013-05-17 22:02:11 +08:00
|
|
|
var payloadLen = dataLen < 126
|
2013-10-01 13:52:39 +08:00
|
|
|
? (byte) dataLen
|
2013-05-17 22:02:11 +08:00
|
|
|
: dataLen < 0x010000
|
2013-10-01 13:52:39 +08:00
|
|
|
? (byte) 126
|
|
|
|
: (byte) 127;
|
2013-05-17 22:02:11 +08:00
|
|
|
|
|
|
|
PayloadLen = payloadLen;
|
|
|
|
|
|
|
|
/* ExtPayloadLen */
|
|
|
|
|
|
|
|
ExtPayloadLen = payloadLen < 126
|
2013-10-01 13:52:39 +08:00
|
|
|
? new byte []{}
|
2013-05-17 22:02:11 +08:00
|
|
|
: payloadLen == 126
|
2014-02-28 16:17:44 +08:00
|
|
|
? ((ushort) dataLen).ToByteArrayInternally (ByteOrder.Big)
|
|
|
|
: dataLen.ToByteArrayInternally (ByteOrder.Big);
|
2013-05-17 22:02:11 +08:00
|
|
|
|
|
|
|
/* MaskingKey */
|
|
|
|
|
2014-03-01 14:16:25 +08:00
|
|
|
var masking = mask == Mask.Mask;
|
2013-05-17 22:02:11 +08:00
|
|
|
var maskingKey = masking
|
2013-10-01 13:52:39 +08:00
|
|
|
? createMaskingKey ()
|
|
|
|
: new byte []{};
|
2013-05-17 22:02:11 +08:00
|
|
|
|
|
|
|
MaskingKey = maskingKey;
|
|
|
|
|
|
|
|
/* PayloadData */
|
|
|
|
|
|
|
|
if (masking)
|
2013-10-01 13:52:39 +08:00
|
|
|
payload.Mask (maskingKey);
|
2013-05-17 22:02:11 +08:00
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
PayloadData = payload;
|
2012-07-31 09:36:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2013-04-12 13:42:25 +08:00
|
|
|
#region Internal Properties
|
|
|
|
|
2013-04-23 17:08:42 +08:00
|
|
|
internal bool IsBinary {
|
|
|
|
get {
|
2013-05-19 00:16:27 +08:00
|
|
|
return Opcode == Opcode.BINARY;
|
2013-04-23 17:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsClose {
|
|
|
|
get {
|
2013-05-19 00:16:27 +08:00
|
|
|
return Opcode == Opcode.CLOSE;
|
2013-04-23 17:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsCompressed {
|
|
|
|
get {
|
2014-03-01 14:24:03 +08:00
|
|
|
return Rsv1 == Rsv.On;
|
2013-04-23 17:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsContinuation {
|
|
|
|
get {
|
2013-05-19 00:16:27 +08:00
|
|
|
return Opcode == Opcode.CONT;
|
2013-04-23 17:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-12 13:42:25 +08:00
|
|
|
internal bool IsControl {
|
|
|
|
get {
|
2013-05-19 00:16:27 +08:00
|
|
|
var opcode = Opcode;
|
|
|
|
return opcode == Opcode.CLOSE || opcode == Opcode.PING || opcode == Opcode.PONG;
|
2013-04-12 13:42:25 +08:00
|
|
|
}
|
|
|
|
}
|
2012-10-09 11:14:55 +08:00
|
|
|
|
2013-04-12 13:42:25 +08:00
|
|
|
internal bool IsData {
|
|
|
|
get {
|
2013-05-19 00:16:27 +08:00
|
|
|
var opcode = Opcode;
|
|
|
|
return opcode == Opcode.BINARY || opcode == Opcode.TEXT;
|
2013-04-12 13:42:25 +08:00
|
|
|
}
|
|
|
|
}
|
2012-10-09 11:14:55 +08:00
|
|
|
|
2013-04-12 13:42:25 +08:00
|
|
|
internal bool IsFinal {
|
2013-03-25 14:17:31 +08:00
|
|
|
get {
|
2014-03-01 14:00:51 +08:00
|
|
|
return Fin == Fin.Final;
|
2013-03-25 14:17:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-23 17:08:42 +08:00
|
|
|
internal bool IsFragmented {
|
|
|
|
get {
|
2014-03-01 14:00:51 +08:00
|
|
|
return Fin == Fin.More || Opcode == Opcode.CONT;
|
2013-04-23 17:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-12 13:42:25 +08:00
|
|
|
internal bool IsMasked {
|
2012-11-13 15:38:48 +08:00
|
|
|
get {
|
2014-03-01 14:16:25 +08:00
|
|
|
return Mask == Mask.Mask;
|
2013-05-19 00:16:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsPerMessageCompressed {
|
|
|
|
get {
|
|
|
|
var opcode = Opcode;
|
2014-03-01 14:24:03 +08:00
|
|
|
return (opcode == Opcode.BINARY || opcode == Opcode.TEXT) && Rsv1 == Rsv.On;
|
2012-10-09 11:14:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-23 17:08:42 +08:00
|
|
|
internal bool IsPing {
|
|
|
|
get {
|
2013-05-19 00:16:27 +08:00
|
|
|
return Opcode == Opcode.PING;
|
2013-04-23 17:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsPong {
|
|
|
|
get {
|
2013-05-19 00:16:27 +08:00
|
|
|
return Opcode == Opcode.PONG;
|
2013-04-23 17:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal bool IsText {
|
|
|
|
get {
|
2013-05-19 00:16:27 +08:00
|
|
|
return Opcode == Opcode.TEXT;
|
2013-04-23 17:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-12 13:42:25 +08:00
|
|
|
internal ulong Length {
|
2012-11-13 15:38:48 +08:00
|
|
|
get {
|
2013-10-01 13:52:39 +08:00
|
|
|
return 2 + (ulong) (ExtPayloadLen.Length + MaskingKey.Length) + PayloadData.Length;
|
2012-10-09 11:14:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2013-04-12 13:42:25 +08:00
|
|
|
#region Public Properties
|
|
|
|
|
|
|
|
public Fin Fin { get; private set; }
|
|
|
|
|
|
|
|
public Rsv Rsv1 { get; private set; }
|
|
|
|
|
|
|
|
public Rsv Rsv2 { get; private set; }
|
|
|
|
|
|
|
|
public Rsv Rsv3 { get; private set; }
|
|
|
|
|
|
|
|
public Opcode Opcode { get; private set; }
|
|
|
|
|
|
|
|
public Mask Mask { get; private set; }
|
|
|
|
|
|
|
|
public byte PayloadLen { get; private set; }
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public byte [] ExtPayloadLen { get; private set; }
|
2013-04-12 13:42:25 +08:00
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public byte [] MaskingKey { get; private set; }
|
2013-04-12 13:42:25 +08:00
|
|
|
|
|
|
|
public PayloadData PayloadData { get; private set; }
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2012-10-04 14:04:21 +08:00
|
|
|
#region Private Methods
|
2012-07-31 09:36:52 +08:00
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static byte [] createMaskingKey ()
|
2013-05-17 22:02:11 +08:00
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
var key = new byte [4];
|
|
|
|
var rand = new Random ();
|
|
|
|
rand.NextBytes (key);
|
2013-05-17 22:02:11 +08:00
|
|
|
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
private static string dump (WsFrame frame)
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
2013-04-12 13:42:25 +08:00
|
|
|
var len = frame.Length;
|
2013-10-01 13:52:39 +08:00
|
|
|
var count = (long) (len / 4);
|
2013-11-05 20:42:59 +08:00
|
|
|
var rem = (int) (len % 4);
|
2013-04-12 13:42:25 +08:00
|
|
|
|
|
|
|
int countDigit;
|
|
|
|
string countFmt;
|
|
|
|
if (count < 10000)
|
|
|
|
{
|
|
|
|
countDigit = 4;
|
|
|
|
countFmt = "{0,4}";
|
|
|
|
}
|
|
|
|
else if (count < 0x010000)
|
|
|
|
{
|
|
|
|
countDigit = 4;
|
|
|
|
countFmt = "{0,4:X}";
|
|
|
|
}
|
|
|
|
else if (count < 0x0100000000)
|
|
|
|
{
|
|
|
|
countDigit = 8;
|
|
|
|
countFmt = "{0,8:X}";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
countDigit = 16;
|
|
|
|
countFmt = "{0,16:X}";
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
var spFmt = String.Format ("{{0,{0}}}", countDigit);
|
2013-11-05 20:42:59 +08:00
|
|
|
var headerFmt = String.Format (
|
|
|
|
@"{0} 01234567 89ABCDEF 01234567 89ABCDEF
|
|
|
|
{0}+--------+--------+--------+--------+\n", spFmt);
|
|
|
|
var footerFmt = String.Format ("{0}+--------+--------+--------+--------+", spFmt);
|
2013-04-12 13:42:25 +08:00
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
var buffer = new StringBuilder (64);
|
2013-04-15 12:47:51 +08:00
|
|
|
Func<Action<string, string, string, string>> linePrinter = () =>
|
2013-04-12 13:42:25 +08:00
|
|
|
{
|
|
|
|
long lineCount = 0;
|
2013-11-05 20:42:59 +08:00
|
|
|
var lineFmt = String.Format ("{0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n", countFmt);
|
2013-04-12 13:42:25 +08:00
|
|
|
return (arg1, arg2, arg3, arg4) =>
|
2013-11-05 20:42:59 +08:00
|
|
|
buffer.AppendFormat (lineFmt, ++lineCount, arg1, arg2, arg3, arg4);
|
2013-04-12 13:42:25 +08:00
|
|
|
};
|
2013-10-01 13:52:39 +08:00
|
|
|
var printLine = linePrinter ();
|
2013-04-12 13:42:25 +08:00
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
buffer.AppendFormat (headerFmt, String.Empty);
|
2013-04-12 13:42:25 +08:00
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
var frameAsBytes = frame.ToByteArray ();
|
2013-04-12 13:42:25 +08:00
|
|
|
int i, j;
|
|
|
|
for (i = 0; i <= count; i++)
|
|
|
|
{
|
|
|
|
j = i * 4;
|
|
|
|
if (i < count)
|
2013-10-01 13:52:39 +08:00
|
|
|
printLine (
|
2013-11-05 20:42:59 +08:00
|
|
|
Convert.ToString (frameAsBytes [j], 2).PadLeft (8, '0'),
|
|
|
|
Convert.ToString (frameAsBytes [j + 1], 2).PadLeft (8, '0'),
|
|
|
|
Convert.ToString (frameAsBytes [j + 2], 2).PadLeft (8, '0'),
|
|
|
|
Convert.ToString (frameAsBytes [j + 3], 2).PadLeft (8, '0'));
|
|
|
|
else if (rem > 0)
|
2013-10-01 13:52:39 +08:00
|
|
|
printLine (
|
2013-11-05 20:42:59 +08:00
|
|
|
Convert.ToString (frameAsBytes [j], 2).PadLeft (8, '0'),
|
|
|
|
rem >= 2 ? Convert.ToString (frameAsBytes [j + 1], 2).PadLeft (8, '0') : String.Empty,
|
|
|
|
rem == 3 ? Convert.ToString (frameAsBytes [j + 2], 2).PadLeft (8, '0') : String.Empty,
|
2013-04-12 13:42:25 +08:00
|
|
|
String.Empty);
|
|
|
|
}
|
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
buffer.AppendFormat (footerFmt, String.Empty);
|
|
|
|
return buffer.ToString ();
|
2012-07-31 09:36:52 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isBinary (Opcode opcode)
|
2013-04-23 17:08:42 +08:00
|
|
|
{
|
|
|
|
return opcode == Opcode.BINARY;
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isClose (Opcode opcode)
|
2013-04-23 17:08:42 +08:00
|
|
|
{
|
|
|
|
return opcode == Opcode.CLOSE;
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isContinuation (Opcode opcode)
|
2013-04-23 17:08:42 +08:00
|
|
|
{
|
|
|
|
return opcode == Opcode.CONT;
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isControl (Opcode opcode)
|
2013-04-12 13:42:25 +08:00
|
|
|
{
|
2013-05-17 22:02:11 +08:00
|
|
|
return opcode == Opcode.CLOSE || opcode == Opcode.PING || opcode == Opcode.PONG;
|
2013-04-12 13:42:25 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isData (Opcode opcode)
|
2013-04-12 13:42:25 +08:00
|
|
|
{
|
2013-05-17 22:02:11 +08:00
|
|
|
return opcode == Opcode.TEXT || opcode == Opcode.BINARY;
|
2013-04-12 13:42:25 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isFinal (Fin fin)
|
2013-04-12 13:42:25 +08:00
|
|
|
{
|
2014-03-01 14:00:51 +08:00
|
|
|
return fin == Fin.Final;
|
2013-04-12 13:42:25 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isMasked (Mask mask)
|
2013-04-12 13:42:25 +08:00
|
|
|
{
|
2014-03-01 14:16:25 +08:00
|
|
|
return mask == Mask.Mask;
|
2012-07-31 09:36:52 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isPing (Opcode opcode)
|
2013-04-23 17:08:42 +08:00
|
|
|
{
|
|
|
|
return opcode == Opcode.PING;
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isPong (Opcode opcode)
|
2013-04-23 17:08:42 +08:00
|
|
|
{
|
|
|
|
return opcode == Opcode.PONG;
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static bool isText (Opcode opcode)
|
2013-04-23 17:08:42 +08:00
|
|
|
{
|
|
|
|
return opcode == Opcode.TEXT;
|
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
private static WsFrame parse (byte [] header, Stream stream, bool unmask)
|
2012-11-13 15:38:48 +08:00
|
|
|
{
|
2013-05-17 22:02:11 +08:00
|
|
|
/* Header */
|
2012-10-04 14:04:21 +08:00
|
|
|
|
2012-07-31 09:36:52 +08:00
|
|
|
// FIN
|
2014-03-01 14:00:51 +08:00
|
|
|
var fin = (header [0] & 0x80) == 0x80 ? Fin.Final : Fin.More;
|
2012-07-31 09:36:52 +08:00
|
|
|
// RSV1
|
2014-03-01 14:24:03 +08:00
|
|
|
var rsv1 = (header [0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off;
|
2012-07-31 09:36:52 +08:00
|
|
|
// RSV2
|
2014-03-01 14:24:03 +08:00
|
|
|
var rsv2 = (header [0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off;
|
2012-07-31 09:36:52 +08:00
|
|
|
// RSV3
|
2014-03-01 14:24:03 +08:00
|
|
|
var rsv3 = (header [0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off;
|
2012-10-04 14:04:21 +08:00
|
|
|
// Opcode
|
2013-10-01 13:52:39 +08:00
|
|
|
var opcode = (Opcode) (header [0] & 0x0f);
|
2012-07-31 09:36:52 +08:00
|
|
|
// MASK
|
2014-03-01 14:16:25 +08:00
|
|
|
var mask = (header [1] & 0x80) == 0x80 ? Mask.Mask : Mask.Unmask;
|
2012-07-31 09:36:52 +08:00
|
|
|
// Payload len
|
2013-10-01 13:52:39 +08:00
|
|
|
var payloadLen = (byte) (header [1] & 0x7f);
|
2012-10-04 14:04:21 +08:00
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
// Check if correct frame.
|
2014-03-01 14:00:51 +08:00
|
|
|
var incorrect = isControl (opcode) && fin == Fin.More
|
2013-11-05 20:42:59 +08:00
|
|
|
? "A control frame is fragmented."
|
2014-03-01 14:24:03 +08:00
|
|
|
: !isData (opcode) && rsv1 == Rsv.On
|
2013-11-05 20:42:59 +08:00
|
|
|
? "A non data frame is compressed."
|
|
|
|
: null;
|
|
|
|
|
|
|
|
if (incorrect != null)
|
2014-03-01 16:32:06 +08:00
|
|
|
throw new WebSocketException (CloseStatusCode.IncorrectData, incorrect);
|
2013-11-05 20:42:59 +08:00
|
|
|
|
|
|
|
// Check if consistent frame.
|
2013-10-01 13:52:39 +08:00
|
|
|
if (isControl (opcode) && payloadLen > 125)
|
2013-11-05 20:42:59 +08:00
|
|
|
throw new WebSocketException (
|
2014-03-01 16:32:06 +08:00
|
|
|
CloseStatusCode.InconsistentData,
|
2013-11-05 20:42:59 +08:00
|
|
|
"The payload data length of a control frame is greater than 125 bytes.");
|
2013-05-17 22:02:11 +08:00
|
|
|
|
|
|
|
var frame = new WsFrame {
|
2013-04-12 13:42:25 +08:00
|
|
|
Fin = fin,
|
|
|
|
Rsv1 = rsv1,
|
|
|
|
Rsv2 = rsv2,
|
|
|
|
Rsv3 = rsv3,
|
|
|
|
Opcode = opcode,
|
|
|
|
Mask = mask,
|
2012-11-13 15:38:48 +08:00
|
|
|
PayloadLen = payloadLen
|
|
|
|
};
|
2012-10-04 14:04:21 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
/* Extended Payload Length */
|
2012-11-13 15:38:48 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
var extLen = payloadLen < 126
|
|
|
|
? 0
|
|
|
|
: payloadLen == 126
|
|
|
|
? 2
|
|
|
|
: 8;
|
2012-12-05 21:10:15 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
var extPayloadLen = extLen > 0
|
2013-10-08 10:36:47 +08:00
|
|
|
? stream.ReadBytes (extLen)
|
2013-10-01 13:52:39 +08:00
|
|
|
: new byte []{};
|
2012-07-31 09:36:52 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
if (extLen > 0 && extPayloadLen.Length != extLen)
|
2013-11-05 20:42:59 +08:00
|
|
|
throw new WebSocketException (
|
|
|
|
"The 'Extended Payload Length' of a frame cannot be read from the data source.");
|
2012-08-10 21:20:42 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
frame.ExtPayloadLen = extPayloadLen;
|
2012-12-05 21:10:15 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
/* Masking Key */
|
2012-12-05 21:10:15 +08:00
|
|
|
|
2014-03-01 14:16:25 +08:00
|
|
|
var masked = mask == Mask.Mask;
|
2013-05-17 22:02:11 +08:00
|
|
|
var maskingKey = masked
|
2013-10-08 10:36:47 +08:00
|
|
|
? stream.ReadBytes (4)
|
2013-10-01 13:52:39 +08:00
|
|
|
: new byte []{};
|
2012-08-10 21:20:42 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
if (masked && maskingKey.Length != 4)
|
2013-11-05 20:42:59 +08:00
|
|
|
throw new WebSocketException (
|
|
|
|
"The 'Masking Key' of a frame cannot be read from the data source.");
|
2012-07-31 09:36:52 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
frame.MaskingKey = maskingKey;
|
|
|
|
|
|
|
|
/* Payload Data */
|
|
|
|
|
|
|
|
ulong dataLen = payloadLen < 126
|
|
|
|
? payloadLen
|
|
|
|
: payloadLen == 126
|
2014-02-28 16:17:44 +08:00
|
|
|
? extPayloadLen.ToUInt16 (ByteOrder.Big)
|
|
|
|
: extPayloadLen.ToUInt64 (ByteOrder.Big);
|
2013-05-17 22:02:11 +08:00
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
byte [] data = null;
|
2013-05-17 22:02:11 +08:00
|
|
|
if (dataLen > 0)
|
|
|
|
{
|
2013-11-05 20:42:59 +08:00
|
|
|
// Check if allowable payload data length.
|
2013-05-17 22:02:11 +08:00
|
|
|
if (payloadLen > 126 && dataLen > PayloadData.MaxLength)
|
2013-11-11 16:17:27 +08:00
|
|
|
throw new WebSocketException (
|
2014-03-01 16:32:06 +08:00
|
|
|
CloseStatusCode.TooBig, "The 'Payload Data' length is greater than the allowable length.");
|
2013-05-17 22:02:11 +08:00
|
|
|
|
2013-10-08 10:36:47 +08:00
|
|
|
data = payloadLen > 126
|
|
|
|
? stream.ReadBytes ((long) dataLen, 1024)
|
|
|
|
: stream.ReadBytes ((int) dataLen);
|
2013-10-01 13:52:39 +08:00
|
|
|
|
|
|
|
if (data.LongLength != (long) dataLen)
|
2013-11-05 20:42:59 +08:00
|
|
|
throw new WebSocketException (
|
|
|
|
"The 'Payload Data' of a frame cannot be read from the data source.");
|
2013-05-17 22:02:11 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
data = new byte []{};
|
2013-05-17 22:02:11 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
var payload = new PayloadData (data, masked);
|
2013-04-12 13:42:25 +08:00
|
|
|
if (masked && unmask)
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
payload.Mask (maskingKey);
|
2014-03-01 14:16:25 +08:00
|
|
|
frame.Mask = Mask.Unmask;
|
2013-10-01 13:52:39 +08:00
|
|
|
frame.MaskingKey = new byte []{};
|
2012-07-31 09:36:52 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
frame.PayloadData = payload;
|
2013-05-17 22:02:11 +08:00
|
|
|
return frame;
|
2012-10-04 14:04:21 +08:00
|
|
|
}
|
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
private static string print (WsFrame frame)
|
2012-10-04 14:04:21 +08:00
|
|
|
{
|
2013-11-05 20:42:59 +08:00
|
|
|
/* Opcode */
|
|
|
|
|
|
|
|
var opcode = frame.Opcode.ToString ();
|
|
|
|
|
|
|
|
/* Payload Len */
|
|
|
|
|
|
|
|
var payloadLen = frame.PayloadLen;
|
|
|
|
|
|
|
|
/* Extended Payload Len */
|
|
|
|
|
|
|
|
var ext = frame.ExtPayloadLen;
|
|
|
|
var size = ext.Length;
|
|
|
|
var extLen = size == 2
|
2014-02-28 16:17:44 +08:00
|
|
|
? ext.ToUInt16 (ByteOrder.Big).ToString ()
|
2013-11-05 20:42:59 +08:00
|
|
|
: size == 8
|
2014-02-28 16:17:44 +08:00
|
|
|
? ext.ToUInt64 (ByteOrder.Big).ToString ()
|
2013-11-05 20:42:59 +08:00
|
|
|
: String.Empty;
|
|
|
|
|
|
|
|
/* Masking Key */
|
2012-10-04 14:04:21 +08:00
|
|
|
|
2013-05-17 22:02:11 +08:00
|
|
|
var masked = frame.IsMasked;
|
2013-11-05 20:42:59 +08:00
|
|
|
var key = masked
|
|
|
|
? BitConverter.ToString (frame.MaskingKey)
|
|
|
|
: String.Empty;
|
|
|
|
|
|
|
|
/* Payload Data */
|
|
|
|
|
|
|
|
var data = payloadLen == 0
|
|
|
|
? String.Empty
|
|
|
|
: size > 0
|
|
|
|
? String.Format ("A {0} data with {1} bytes.", opcode.ToLower (), extLen)
|
|
|
|
: masked || frame.IsFragmented || frame.IsBinary || frame.IsClose
|
|
|
|
? BitConverter.ToString (frame.PayloadData.ToByteArray ())
|
|
|
|
: Encoding.UTF8.GetString (frame.PayloadData.ApplicationData);
|
|
|
|
|
|
|
|
var format =
|
|
|
|
@" FIN: {0}
|
|
|
|
RSV1: {1}
|
|
|
|
RSV2: {2}
|
|
|
|
RSV3: {3}
|
|
|
|
Opcode: {4}
|
|
|
|
MASK: {5}
|
|
|
|
Payload Len: {6}
|
|
|
|
Extended Payload Len: {7}
|
|
|
|
Masking Key: {8}
|
|
|
|
Payload Data: {9}";
|
|
|
|
|
|
|
|
return String.Format (
|
|
|
|
format,
|
|
|
|
frame.Fin,
|
|
|
|
frame.Rsv1,
|
|
|
|
frame.Rsv2,
|
|
|
|
frame.Rsv3,
|
|
|
|
opcode,
|
|
|
|
frame.Mask,
|
|
|
|
payloadLen,
|
|
|
|
extLen,
|
|
|
|
key,
|
|
|
|
data);
|
2013-04-23 17:08:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
#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
|
|
|
|
|
2012-07-31 09:36:52 +08:00
|
|
|
#region Public Methods
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
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 ()
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
foreach (byte b in ToByteArray ())
|
2012-07-31 09:36:52 +08:00
|
|
|
yield return b;
|
2012-10-04 14:04:21 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public static WsFrame Parse (byte [] src)
|
2012-10-04 14:04:21 +08:00
|
|
|
{
|
2013-11-05 20:42:59 +08:00
|
|
|
return Parse (src, true);
|
2012-10-04 14:04:21 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public static WsFrame Parse (Stream stream)
|
2013-04-12 13:42:25 +08:00
|
|
|
{
|
2013-11-05 20:42:59 +08:00
|
|
|
return Parse (stream, true);
|
2012-07-31 09:36:52 +08:00
|
|
|
}
|
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
public static WsFrame Parse (byte [] src, bool unmask)
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
2013-10-18 22:07:43 +08:00
|
|
|
using (var stream = new MemoryStream (src))
|
|
|
|
{
|
2013-11-05 20:42:59 +08:00
|
|
|
return Parse (stream, unmask);
|
2013-10-18 22:07:43 +08:00
|
|
|
}
|
2012-10-04 14:04:21 +08:00
|
|
|
}
|
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
public static WsFrame Parse (Stream stream, bool unmask)
|
2012-10-04 14:04:21 +08:00
|
|
|
{
|
2013-11-05 20:42:59 +08:00
|
|
|
var header = stream.ReadBytes (2);
|
|
|
|
if (header.Length != 2)
|
|
|
|
throw new WebSocketException (
|
|
|
|
"The header part of a frame cannot be read from the data source.");
|
2013-04-12 13:42:25 +08:00
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
return parse (header, stream, unmask);
|
2012-11-13 15:38:48 +08:00
|
|
|
}
|
2012-10-04 14:04:21 +08:00
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public static void ParseAsync (Stream stream, Action<WsFrame> completed)
|
2012-11-13 15:38:48 +08:00
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
ParseAsync (stream, true, completed, null);
|
2012-11-13 15:38:48 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public static void ParseAsync (Stream stream, Action<WsFrame> completed, Action<Exception> error)
|
2012-11-13 15:38:48 +08:00
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
ParseAsync (stream, true, completed, error);
|
2013-04-07 16:05:48 +08:00
|
|
|
}
|
2012-11-13 15:38:48 +08:00
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public static void ParseAsync (
|
2013-04-07 16:05:48 +08:00
|
|
|
Stream stream, bool unmask, Action<WsFrame> completed, Action<Exception> error)
|
|
|
|
{
|
2013-10-18 22:07:43 +08:00
|
|
|
stream.ReadBytesAsync (
|
|
|
|
2,
|
|
|
|
header =>
|
2012-11-13 15:38:48 +08:00
|
|
|
{
|
2013-11-05 20:42:59 +08:00
|
|
|
if (header.Length != 2)
|
|
|
|
throw new WebSocketException (
|
|
|
|
"The header part of a frame cannot be read from the data source.");
|
2013-10-18 22:07:43 +08:00
|
|
|
|
2013-11-05 20:42:59 +08:00
|
|
|
var frame = parse (header, stream, unmask);
|
2013-10-18 22:07:43 +08:00
|
|
|
if (completed != null)
|
|
|
|
completed (frame);
|
|
|
|
},
|
2013-10-20 23:17:51 +08:00
|
|
|
error);
|
2012-07-31 09:36:52 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public void Print (bool dumped)
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
2013-11-05 20:42:59 +08:00
|
|
|
Console.WriteLine (dumped ? dump (this) : print (this));
|
|
|
|
}
|
|
|
|
|
|
|
|
public string PrintToString (bool dumped)
|
|
|
|
{
|
|
|
|
return dumped ? dump (this) : print (this);
|
2012-07-31 09:36:52 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public byte [] ToByteArray()
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
using (var buffer = new MemoryStream ())
|
2013-05-17 22:02:11 +08:00
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
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;
|
2014-02-28 16:17:44 +08:00
|
|
|
buffer.Write (((ushort) header).ToByteArrayInternally (ByteOrder.Big), 0, 2);
|
2013-05-17 22:02:11 +08:00
|
|
|
|
|
|
|
if (PayloadLen > 125)
|
2013-10-01 13:52:39 +08:00
|
|
|
buffer.Write (ExtPayloadLen, 0, ExtPayloadLen.Length);
|
2013-05-17 22:02:11 +08:00
|
|
|
|
2014-03-01 14:16:25 +08:00
|
|
|
if (Mask == Mask.Mask)
|
2013-10-01 13:52:39 +08:00
|
|
|
buffer.Write (MaskingKey, 0, MaskingKey.Length);
|
2013-05-17 22:02:11 +08:00
|
|
|
|
|
|
|
if (PayloadLen > 0)
|
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
var payload = PayloadData.ToByteArray ();
|
2013-05-17 22:02:11 +08:00
|
|
|
if (PayloadLen < 127)
|
2013-10-01 13:52:39 +08:00
|
|
|
buffer.Write (payload, 0, payload.Length);
|
2013-05-17 22:02:11 +08:00
|
|
|
else
|
2013-10-01 13:52:39 +08:00
|
|
|
buffer.WriteBytes (payload);
|
2013-05-17 22:02:11 +08:00
|
|
|
}
|
2012-07-31 09:36:52 +08:00
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
buffer.Close ();
|
|
|
|
return buffer.ToArray ();
|
2013-05-17 22:02:11 +08:00
|
|
|
}
|
2012-07-31 09:36:52 +08:00
|
|
|
}
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
public override string ToString ()
|
2012-07-31 09:36:52 +08:00
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
return BitConverter.ToString (ToByteArray ());
|
2013-04-12 13:42:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Explicitly Implemented Interface Members
|
|
|
|
|
2013-10-01 13:52:39 +08:00
|
|
|
IEnumerator IEnumerable.GetEnumerator ()
|
2013-04-12 13:42:25 +08:00
|
|
|
{
|
2013-10-01 13:52:39 +08:00
|
|
|
return GetEnumerator ();
|
2012-07-31 09:36:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
}
|