@ -36,7 +36,7 @@ namespace WebSocketSharp {
internal class WsFrame : IEnumerable < byte >
internal class WsFrame : IEnumerable < byte >
{
{
#region Fields
#region Private Const Fields
private const int _readBufferLen = 1024 ;
private const int _readBufferLen = 1024 ;
@ -46,11 +46,6 @@ namespace WebSocketSharp {
private WsFrame ( )
private WsFrame ( )
{
{
Rsv1 = Rsv . OFF ;
Rsv2 = Rsv . OFF ;
Rsv3 = Rsv . OFF ;
ExtPayloadLen = new byte [ ] { } ;
MaskingKey = new byte [ ] { } ;
}
}
# endregion
# endregion
@ -68,11 +63,20 @@ namespace WebSocketSharp {
}
}
public WsFrame ( Fin fin , Opcode opcode , Mask mask , PayloadData payloadData )
public WsFrame ( Fin fin , Opcode opcode , Mask mask , PayloadData payloadData )
: this ( )
{
{
Fin = fin ;
if ( payloadData . IsNull ( ) )
Opcode = opcode ;
throw new ArgumentNullException ( "payloadData" ) ;
Masked = mask ;
if ( isControl ( opcode ) & & payloadData . Length > 125 )
throw new ArgumentOutOfRangeException ( "payloadData" ,
"The control frame must have a payload length of 125 bytes or less." ) ;
if ( ! isFinal ( fin ) & & isControl ( opcode ) )
throw new ArgumentException ( "The control frame must not be fragmented." ) ;
Fin = fin ;
Opcode = opcode ;
Mask = mask ;
PayloadData = payloadData ;
PayloadData = payloadData ;
init ( ) ;
init ( ) ;
@ -80,57 +84,174 @@ namespace WebSocketSharp {
# endregion
# endregion
#region Properties
#region Internal Properties
internal bool IsControl {
get {
return isControl ( Opcode ) ;
}
}
public Fin Fin { get ; private set ; }
internal bool IsData {
public Rsv Rsv1 { get ; private set ; }
get {
public Rsv Rsv2 { get ; private set ; }
return isData ( Opcode ) ;
public Rsv Rsv3 { get ; private set ; }
}
public Opcode Opcode { get ; private set ; }
}
public Mask Masked { get ; private set ; }
public byte PayloadLen { get ; private set ; }
public byte [ ] ExtPayloadLen { get ; private set ; }
public byte [ ] MaskingKey { get ; private set ; }
public PayloadData PayloadData { get ; private set ; }
public bool IsData {
internal bool IsFinal {
get {
get {
Opcode data = Opcode . TEXT | Opcode . BINARY ;
return isFinal ( Fin ) ;
return ( data & Opcode ) = = Opcode ;
}
}
}
}
public ulong Length {
internal bool IsMasked {
get {
get {
return 2 + ( ulong ) ( ExtPayloadLen . Length + MaskingKey . Length ) + PayloadLength ;
return isMasked ( Mask ) ;
}
}
}
}
public ulong Payload Length {
internal ulong Length {
get {
get {
return PayloadData . Length ;
return 2 + ( ulong ) ( ExtPayloadLen . Length + MaskingKey . Length ) + PayloadData . Length ;
}
}
}
}
# endregion
# endregion
#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 ; }
public byte [ ] ExtPayloadLen { get ; private set ; }
public byte [ ] MaskingKey { get ; private set ; }
public PayloadData PayloadData { get ; private set ; }
# endregion
#region Private Methods
#region Private Methods
IEnumerator IEnumerable . GetEnumerator ( )
private static void dump ( WsFrame frame )
{
{
return GetEnumerator ( ) ;
var len = frame . Length ;
var count = ( long ) ( len / 4 ) ;
var remainder = ( int ) ( len % 4 ) ;
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}" ;
}
var spFmt = String . Format ( "{{0,{0}}}" , countDigit ) ;
var headerFmt = String . Format ( @ "
{ 0 } 01234567 89 ABCDEF 01234567 89 ABCDEF
{ 0 } + - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - - + ", spFmt);
var footerFmt = String . Format ( " {0}+--------+--------+--------+--------+" , spFmt ) ;
Func < string , Action < string , string , string , string > > linePrinter = lineNumberFmt = >
{
long lineCount = 0 ;
string lineFmt = String . Format ( " {0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|" , lineNumberFmt ) ;
return ( arg1 , arg2 , arg3 , arg4 ) = >
{
Console . WriteLine ( lineFmt , + + lineCount , arg1 , arg2 , arg3 , arg4 ) ;
} ;
} ;
var printLine = linePrinter ( countFmt ) ;
Console . WriteLine ( headerFmt , String . Empty ) ;
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' ) ) ;
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 ,
String . Empty ) ;
}
Console . WriteLine ( footerFmt , String . Empty ) ;
}
}
private void init ( )
private void init ( )
{
{
setPayloadLen ( PayloadLength ) ;
Rsv1 = Rsv . OFF ;
if ( Masked = = Mask . MASK )
Rsv2 = Rsv . OFF ;
Rsv3 = Rsv . OFF ;
setPayloadLen ( PayloadData . Length ) ;
if ( IsMasked )
maskPayloadData ( ) ;
maskPayloadData ( ) ;
else
MaskingKey = new byte [ ] { } ;
}
private static bool isControl ( Opcode opcode )
{
Opcode control = Opcode . CLOSE | Opcode . PING | Opcode . PONG ;
return ( control & opcode ) = = opcode ;
}
private static bool isData ( Opcode opcode )
{
Opcode data = Opcode . TEXT | Opcode . BINARY ;
return ( data & opcode ) = = opcode ;
}
private static bool isFinal ( Fin fin )
{
return fin = = Fin . FINAL ;
}
private static bool isMasked ( Mask mask )
{
return mask = = Mask . MASK ;
}
}
private void maskPayloadData ( )
private void maskPayloadData ( )
{
{
var key = new byte [ 4 ] ;
var key = new byte [ 4 ] ;
var rand = new Random ( ) ;
var rand = new Random ( ) ;
rand . NextBytes ( key ) ;
rand . NextBytes ( key ) ;
@ -138,47 +259,75 @@ namespace WebSocketSharp {
PayloadData . Mask ( key ) ;
PayloadData . Mask ( key ) ;
}
}
private static WsFrame parse ( Stream stream , bool unmask )
{
return parse ( stream . ReadBytes ( 2 ) , stream , unmask ) ;
}
private static WsFrame parse ( byte [ ] header , Stream stream , bool unmask )
private static WsFrame parse ( byte [ ] header , Stream stream , bool unmask )
{
{
if ( header . IsNull ( ) | | header . Length ! = 2 )
if ( header . IsNull ( ) | | header . Length ! = 2 )
return null ;
return null ;
try
var frame = readHeader ( header ) ;
{
readExtPayloadLen ( stream , frame ) ;
var frame = readHeader ( header ) ;
readMaskingKey ( stream , frame ) ;
readExtPayloadLen ( stream , frame ) ;
readPayloadData ( stream , frame , unmask ) ;
readMaskingKey ( stream , frame ) ;
readPayloadData ( stream , frame , unmask ) ;
return frame ;
return frame ;
}
}
catch
{
private static void print ( WsFrame frame )
return null ;
{
}
var len = frame . ExtPayloadLen . Length ;
var extPayloadLen = len = = 2
? frame . ExtPayloadLen . To < ushort > ( ByteOrder . BIG ) . ToString ( )
: len = = 8
? frame . ExtPayloadLen . To < ulong > ( ByteOrder . BIG ) . ToString ( )
: String . Empty ;
var masked = frame . IsMasked ;
var maskingKey = masked
? BitConverter . ToString ( frame . MaskingKey )
: String . Empty ;
var opcode = frame . Opcode ;
var payloadData = frame . PayloadData . Length = = 0
? String . Empty
: masked | | ( ( Opcode . CONT | Opcode . BINARY | Opcode . CLOSE ) & opcode ) = = opcode
? BitConverter . ToString ( frame . PayloadData . ToByteArray ( ) )
: Encoding . UTF8 . GetString ( frame . PayloadData . ToByteArray ( ) ) ;
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 } ";
Console . WriteLine (
format , frame . Fin , frame . Rsv1 , frame . Rsv2 , frame . Rsv3 , opcode , frame . Mask , frame . PayloadLen , extPayloadLen , maskingKey , payloadData ) ;
}
}
private static void readExtPayloadLen ( Stream stream , WsFrame frame )
private static void readExtPayloadLen ( Stream stream , WsFrame frame )
{
{
var length = frame . PayloadLen < = 125
int length = frame . PayloadLen < = 125
? 0
? 0
: frame . PayloadLen = = 126
: frame . PayloadLen = = 126
? 2
? 2
: 8 ;
: 8 ;
if ( length = = 0 )
if ( length = = 0 )
{
frame . ExtPayloadLen = new byte [ ] { } ;
return ;
return ;
}
var extLen = stream . ReadBytes ( length ) ;
var ext Payload Len = stream . ReadBytes ( length ) ;
if ( extLen . Length ! = length )
if ( ext Payload Len. Length ! = length )
throw new IOException ( ) ;
throw new IOException ( ) ;
frame . ExtPayloadLen = extLen ;
frame . ExtPayloadLen = ext Payload Len;
}
}
private static WsFrame readHeader ( byte [ ] header )
private static WsFrame readHeader ( byte [ ] header )
@ -194,25 +343,28 @@ namespace WebSocketSharp {
// Opcode
// Opcode
Opcode opcode = ( Opcode ) ( header [ 0 ] & 0x0f ) ;
Opcode opcode = ( Opcode ) ( header [ 0 ] & 0x0f ) ;
// MASK
// MASK
Mask mask ed = ( header [ 1 ] & 0x80 ) = = 0x80 ? Mask . MASK : Mask . UNMASK ;
Mask mask = ( header [ 1 ] & 0x80 ) = = 0x80 ? Mask . MASK : Mask . UNMASK ;
// Payload len
// Payload len
byte payloadLen = ( byte ) ( header [ 1 ] & 0x7f ) ;
byte payloadLen = ( byte ) ( header [ 1 ] & 0x7f ) ;
return new WsFrame {
return new WsFrame {
Fin = fin ,
Fin = fin ,
Rsv1 = rsv1 ,
Rsv1 = rsv1 ,
Rsv2 = rsv2 ,
Rsv2 = rsv2 ,
Rsv3 = rsv3 ,
Rsv3 = rsv3 ,
Opcode = opcode ,
Opcode = opcode ,
Mask ed = mask ed ,
Mask = mask ,
PayloadLen = payloadLen
PayloadLen = payloadLen
} ;
} ;
}
}
private static void readMaskingKey ( Stream stream , WsFrame frame )
private static void readMaskingKey ( Stream stream , WsFrame frame )
{
{
if ( frame . Masked = = Mask . UNMASK )
if ( ! isMasked ( frame . Mask ) )
{
frame . MaskingKey = new byte [ ] { } ;
return ;
return ;
}
var maskingKey = stream . ReadBytes ( 4 ) ;
var maskingKey = stream . ReadBytes ( 4 ) ;
if ( maskingKey . Length ! = 4 )
if ( maskingKey . Length ! = 4 )
@ -231,7 +383,7 @@ namespace WebSocketSharp {
if ( length = = 0 )
if ( length = = 0 )
{
{
frame . PayloadData = new PayloadData ( new byte [ ] { } ) ;
frame . PayloadData = new PayloadData ( ) ;
return ;
return ;
}
}
@ -245,14 +397,15 @@ namespace WebSocketSharp {
if ( buffer . LongLength ! = ( long ) length )
if ( buffer . LongLength ! = ( long ) length )
throw new IOException ( ) ;
throw new IOException ( ) ;
var payloadData = frame . Masked = = Mask . MASK
var masked = isMasked ( frame . Mask ) ;
var payloadData = masked
? new PayloadData ( buffer , true )
? new PayloadData ( buffer , true )
: new PayloadData ( buffer ) ;
: new PayloadData ( buffer ) ;
if ( fra me. M asked = = Mask . MASK & & unmask )
if ( masked & & unmask )
{
{
payloadData . Mask ( frame . MaskingKey ) ;
payloadData . Mask ( frame . MaskingKey ) ;
frame . Mask ed = Mask . UNMASK ;
frame . Mask = Mask . UNMASK ;
frame . MaskingKey = new byte [ ] { } ;
frame . MaskingKey = new byte [ ] { } ;
}
}
@ -264,18 +417,19 @@ namespace WebSocketSharp {
if ( length < 126 )
if ( length < 126 )
{
{
PayloadLen = ( byte ) length ;
PayloadLen = ( byte ) length ;
ExtPayloadLen = new byte [ ] { } ;
return ;
return ;
}
}
if ( length < 0x010000 )
if ( length < 0x010000 )
{
{
PayloadLen = ( byte ) 126 ;
PayloadLen = ( byte ) 126 ;
ExtPayloadLen = ( ( ushort ) length ) . ToByte s ( ByteOrder . BIG ) ;
ExtPayloadLen = ( ( ushort ) length ) . ToByte Array ( ByteOrder . BIG ) ;
return ;
return ;
}
}
PayloadLen = ( byte ) 127 ;
PayloadLen = ( byte ) 127 ;
ExtPayloadLen = length . ToByte s ( ByteOrder . BIG ) ;
ExtPayloadLen = length . ToByte Array ( ByteOrder . BIG ) ;
}
}
# endregion
# endregion
@ -284,7 +438,7 @@ namespace WebSocketSharp {
public IEnumerator < byte > GetEnumerator ( )
public IEnumerator < byte > GetEnumerator ( )
{
{
foreach ( byte b in ToByte s ( ) )
foreach ( byte b in ToByte Array ( ) )
yield return b ;
yield return b ;
}
}
@ -293,6 +447,11 @@ namespace WebSocketSharp {
return Parse ( src , true ) ;
return Parse ( src , true ) ;
}
}
public static WsFrame Parse ( Stream stream )
{
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 ( MemoryStream ms = new MemoryStream ( src ) )
@ -301,14 +460,26 @@ namespace WebSocketSharp {
}
}
}
}
public static WsFrame Parse ( Stream stream )
public static WsFrame Parse ( Stream stream , bool unmask )
{
{
return Parse ( stream , true ) ;
return Parse ( stream , unmask , null ) ;
}
}
public static WsFrame Parse ( Stream stream , bool unmask )
public static WsFrame Parse ( Stream stream , bool unmask , Action < Exception > error )
{
{
return parse ( stream , unmask ) ;
WsFrame frame = null ;
try
{
var header = stream . ReadBytes ( 2 ) ;
frame = parse ( header , stream , unmask ) ;
}
catch ( Exception ex )
{
if ( ! error . IsNull ( ) )
error ( ex ) ;
}
return frame ;
}
}
public static void ParseAsync ( Stream stream , Action < WsFrame > completed )
public static void ParseAsync ( Stream stream , Action < WsFrame > completed )
@ -356,138 +527,31 @@ namespace WebSocketSharp {
stream . BeginRead ( header , 0 , 2 , callback , null ) ;
stream . BeginRead ( header , 0 , 2 , callback , null ) ;
}
}
public void Print ( )
public void Print ( bool dumped )
{
{
byte [ ] buffer ;
if ( dumped )
long count , i , j ;
dump ( this ) ;
int countDigit , remainder ;
string countFmt , extPayloadLen , headerFmt , topLineFmt , bottomLineFmt , payloadData , spFmt ;
switch ( ExtPayloadLen . Length )
{
case 2 :
extPayloadLen = ExtPayloadLen . To < ushort > ( ByteOrder . BIG ) . ToString ( ) ;
break ;
case 8 :
extPayloadLen = ExtPayloadLen . To < ulong > ( ByteOrder . BIG ) . ToString ( ) ;
break ;
default :
extPayloadLen = String . Empty ;
break ;
}
if ( ( ( Opcode . TEXT | Opcode . PING | Opcode . PONG ) & Opcode ) = = Opcode & &
Masked = = Mask . UNMASK & &
PayloadLength > 0 )
{
payloadData = Encoding . UTF8 . GetString ( PayloadData . ToByteArray ( ) ) ;
}
else
{
payloadData = BitConverter . ToString ( PayloadData . ToByteArray ( ) ) ;
}
headerFmt = @ "
WsFrame :
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 } ";
buffer = ToBytes ( ) ;
count = ( long ) ( Length / 4 ) ;
remainder = ( int ) ( Length % 4 ) ;
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
else
{
print ( this ) ;
countDigit = 16 ;
countFmt = "{0,16:X}" ;
}
spFmt = String . Format ( "{{0,{0}}}" , countDigit ) ;
topLineFmt = String . Format ( @ "
{ 0 } 01234567 89 ABCDEF 01234567 89 ABCDEF
{ 0 } + - - - - - - - - + - - - - - - - - + - - - - - - - - + - - - - - - - - + ", spFmt);
Func < string , Action < string , string , string , string > > func = s = >
{
long lineCount = 0 ;
string lineFmt = String . Format ( " {0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|" , s ) ;
return ( arg1 , arg2 , arg3 , arg4 ) = >
{
Console . WriteLine ( lineFmt , + + lineCount , arg1 , arg2 , arg3 , arg4 ) ;
} ;
} ;
var printLine = func ( countFmt ) ;
bottomLineFmt = String . Format ( " {0}+--------+--------+--------+--------+" , spFmt ) ;
Console . WriteLine ( headerFmt ,
Fin , Rsv1 , Rsv2 , Rsv3 , Opcode ,
Masked , PayloadLen , extPayloadLen ,
BitConverter . ToString ( MaskingKey ) ,
payloadData ) ;
Console . WriteLine ( topLineFmt , String . Empty ) ;
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' ) ) ;
}
else if ( i = = count & & 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 ,
String . Empty ) ;
}
}
Console . WriteLine ( bottomLineFmt , String . Empty ) ;
}
}
public byte [ ] ToByte s ( )
public byte [ ] ToByteArray ( )
{
{
var buffer = new List < byte > ( ) ;
var buffer = new List < byte > ( ) ;
var header = ( int ) Fin ;
int header = ( int ) Fin ;
header = ( header < < 1 ) + ( int ) Rsv1 ;
header = ( header < < 1 ) + ( int ) Rsv1 ;
header = ( header < < 1 ) + ( int ) Rsv2 ;
header = ( header < < 1 ) + ( int ) Rsv2 ;
header = ( header < < 1 ) + ( int ) Rsv3 ;
header = ( header < < 1 ) + ( int ) Rsv3 ;
header = ( header < < 4 ) + ( int ) Opcode ;
header = ( header < < 4 ) + ( int ) Opcode ;
header = ( header < < 1 ) + ( int ) Mask ed ;
header = ( header < < 1 ) + ( int ) Mask ;
header = ( header < < 7 ) + ( int ) PayloadLen ;
header = ( header < < 7 ) + ( int ) PayloadLen ;
buffer . AddRange ( ( ( ushort ) header ) . ToByte s ( ByteOrder . BIG ) ) ;
buffer . AddRange ( ( ( ushort ) header ) . ToByteArray ( ByteOrder . BIG ) ) ;
if ( PayloadLen > = 126 )
if ( PayloadLen > = 126 )
buffer . AddRange ( ExtPayloadLen ) ;
buffer . AddRange ( ExtPayloadLen ) ;
if ( Masked = = Mask . MASK )
if ( IsMasked )
buffer . AddRange ( MaskingKey ) ;
buffer . AddRange ( MaskingKey ) ;
if ( PayloadLen > 0 )
if ( PayloadLen > 0 )
@ -498,7 +562,16 @@ namespace WebSocketSharp {
public override string ToString ( )
public override string ToString ( )
{
{
return BitConverter . ToString ( ToBytes ( ) ) ;
return BitConverter . ToString ( ToByteArray ( ) ) ;
}
# endregion
#region Explicitly Implemented Interface Members
IEnumerator IEnumerable . GetEnumerator ( )
{
return GetEnumerator ( ) ;
}
}
# endregion
# endregion