@ -36,12 +36,6 @@ namespace WebSocketSharp {
internal class WsFrame : IEnumerable < byte >
internal class WsFrame : IEnumerable < byte >
{
{
#region Private Const Fields
private const int _readBufferLen = 1024 ;
# endregion
#region Private Constructors
#region Private Constructors
private WsFrame ( )
private WsFrame ( )
@ -70,26 +64,47 @@ namespace WebSocketSharp {
public WsFrame (
public WsFrame (
Fin fin , Opcode opcode , Mask mask , PayloadData payloadData , bool compressed )
Fin fin , Opcode opcode , Mask mask , PayloadData payloadData , bool compressed )
{
{
if ( payloadData . IsNull ( ) )
throw new ArgumentNullException ( "payloadData" ) ;
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." ) ;
if ( isControl ( opcode ) & & compressed )
throw new ArgumentException ( "The control frame must not be compressed." ) ;
Fin = fin ;
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 ;
Opcode = opcode ;
Mask = mask ;
Mask = mask ;
PayloadData = payloadData ;
init ( ) ;
/* PayloadLen */
ulong dataLen = payloadData . Length ;
var payloadLen = dataLen < 126
? ( byte ) dataLen
: dataLen < 0x010000
? ( byte ) 126
: ( byte ) 127 ;
PayloadLen = payloadLen ;
/* ExtPayloadLen */
ExtPayloadLen = payloadLen < 126
? new byte [ ] { }
: payloadLen = = 126
? ( ( ushort ) dataLen ) . ToByteArray ( ByteOrder . BIG )
: dataLen . ToByteArray ( ByteOrder . BIG ) ;
/* MaskingKey */
var masking = mask = = Mask . MASK ;
var maskingKey = masking
? createMaskingKey ( )
: new byte [ ] { } ;
MaskingKey = maskingKey ;
/* PayloadData */
if ( masking )
payloadData . Mask ( maskingKey ) ;
PayloadData = payloadData ;
}
}
# endregion
# endregion
@ -215,6 +230,36 @@ namespace WebSocketSharp {
#region Private Methods
#region Private Methods
private static WsFrame createCloseFrame ( WebSocketException exception )
{
using ( var buffer = new MemoryStream ( ) )
{
var code = ( ushort ) exception . Code ;
var msg = exception . Message ;
buffer . Write ( code . ToByteArray ( ByteOrder . BIG ) , 0 , 2 ) ;
if ( msg . Length ! = 0 )
{
var tmp = Encoding . UTF8 . GetBytes ( msg ) ;
buffer . Write ( tmp , 0 , tmp . Length ) ;
}
buffer . Close ( ) ;
var payload = new PayloadData ( buffer . ToArray ( ) ) ;
var frame = new WsFrame ( Fin . FINAL , Opcode . CLOSE , Mask . UNMASK , payload ) ;
return frame ;
}
}
private static byte [ ] createMaskingKey ( )
{
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 len = frame . Length ;
@ -285,17 +330,6 @@ namespace WebSocketSharp {
Console . WriteLine ( footerFmt , String . Empty ) ;
Console . WriteLine ( footerFmt , String . Empty ) ;
}
}
private void init ( )
{
Rsv2 = Rsv . OFF ;
Rsv3 = Rsv . OFF ;
setPayloadLen ( PayloadData . Length ) ;
if ( IsMasked )
maskPayloadData ( ) ;
else
MaskingKey = new byte [ ] { } ;
}
private static bool isBinary ( Opcode opcode )
private static bool isBinary ( Opcode opcode )
{
{
return opcode = = Opcode . BINARY ;
return opcode = = Opcode . BINARY ;
@ -313,12 +347,12 @@ namespace WebSocketSharp {
private static bool isControl ( Opcode opcode )
private static bool isControl ( Opcode opcode )
{
{
return isClose( opcode ) | | isPing ( opcode ) | | isPong ( opcode ) ;
return opcode = = Opcode . CLOSE | | opcode = = Opcode . PING | | opcode = = Opcode . PONG ;
}
}
private static bool isData ( Opcode opcode )
private static bool isData ( Opcode opcode )
{
{
return isText( opcode ) | | isBinary ( opcode ) ;
return opcode = = Opcode . TEXT | | opcode = = Opcode . BINARY ;
}
}
private static bool isFinal ( Fin fin )
private static bool isFinal ( Fin fin )
@ -346,105 +380,33 @@ namespace WebSocketSharp {
return opcode = = Opcode . TEXT ;
return opcode = = Opcode . TEXT ;
}
}
private void maskPayloadData ( )
{
var key = new byte [ 4 ] ;
var rand = new Random ( ) ;
rand . NextBytes ( key ) ;
MaskingKey = key ;
PayloadData . Mask ( key ) ;
}
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 = = null | | header . Length ! = 2 )
return null ;
return null ;
var frame = readHeader ( header ) ;
/* Header */
readExtPayloadLen ( stream , frame ) ;
readMaskingKey ( stream , frame ) ;
readPayloadData ( stream , frame , unmask ) ;
return frame ;
}
private static void print ( WsFrame frame )
{
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 | | frame . IsFragmented | | frame . IsBinary | | frame . IsClose
? 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 )
{
int length = frame . PayloadLen < = 125
? 0
: frame . PayloadLen = = 126
? 2
: 8 ;
if ( length = = 0 )
{
frame . ExtPayloadLen = new byte [ ] { } ;
return ;
}
var extPayloadLen = stream . ReadBytes ( length ) ;
if ( extPayloadLen . Length ! = length )
throw new IOException ( ) ;
frame . ExtPayloadLen = extPayloadLen ;
}
private static WsFrame readHeader ( byte [ ] header )
{
// FIN
// FIN
Fin fin = ( header [ 0 ] & 0x80 ) = = 0x80 ? Fin . FINAL : Fin . MORE ;
var fin = ( header [ 0 ] & 0x80 ) = = 0x80 ? Fin . FINAL : Fin . MORE ;
// RSV1
// RSV1
Rsv rsv1 = ( header [ 0 ] & 0x40 ) = = 0x40 ? Rsv . ON : Rsv . OFF ;
var rsv1 = ( header [ 0 ] & 0x40 ) = = 0x40 ? Rsv . ON : Rsv . OFF ;
// RSV2
// RSV2
Rsv rsv2 = ( header [ 0 ] & 0x20 ) = = 0x20 ? Rsv . ON : Rsv . OFF ;
var rsv2 = ( header [ 0 ] & 0x20 ) = = 0x20 ? Rsv . ON : Rsv . OFF ;
// RSV3
// RSV3
Rsv rsv3 = ( header [ 0 ] & 0x10 ) = = 0x10 ? Rsv . ON : Rsv . OFF ;
var rsv3 = ( header [ 0 ] & 0x10 ) = = 0x10 ? Rsv . ON : Rsv . OFF ;
// Opcode
// Opcode
Opcode opcode = ( Opcode ) ( header [ 0 ] & 0x0f ) ;
var opcode = ( Opcode ) ( header [ 0 ] & 0x0f ) ;
// MASK
// MASK
Mask mask = ( header [ 1 ] & 0x80 ) = = 0x80 ? Mask . MASK : Mask . UNMASK ;
var mask = ( header [ 1 ] & 0x80 ) = = 0x80 ? Mask . MASK : Mask . UNMASK ;
// Payload len
// Payload len
byte payloadLen = ( byte ) ( header [ 1 ] & 0x7f ) ;
var payloadLen = ( byte ) ( header [ 1 ] & 0x7f ) ;
return new WsFrame {
if ( isControl ( opcode ) & & payloadLen > 125 )
throw new WebSocketException ( CloseStatusCode . INCONSISTENT_DATA ,
"The payload length of a control frame must be 125 bytes or less." ) ;
var frame = new WsFrame {
Fin = fin ,
Fin = fin ,
Rsv1 = rsv1 ,
Rsv1 = rsv1 ,
Rsv2 = rsv2 ,
Rsv2 = rsv2 ,
@ -453,87 +415,109 @@ namespace WebSocketSharp {
Mask = mask ,
Mask = mask ,
PayloadLen = payloadLen
PayloadLen = payloadLen
} ;
} ;
}
private static void readMaskingKey ( Stream stream , WsFrame frame )
/* Extended Payload Length */
{
if ( ! isMasked ( frame . Mask ) )
{
frame . MaskingKey = new byte [ ] { } ;
return ;
}
var maskingKey = stream . ReadBytes ( 4 ) ;
var extLen = payloadLen < 126
if ( maskingKey . Length ! = 4 )
? 0
throw new IOException ( ) ;
: payloadLen = = 126
? 2
: 8 ;
frame . MaskingKey = maskingKey ;
var extPayloadLen = extLen > 0
}
? stream . ReadBytesInternal ( extLen )
: new byte [ ] { } ;
private static void readPayloadData ( Stream stream , WsFrame frame , bool unmask )
if ( extLen > 0 & & extPayloadLen . Length ! = extLen )
{
throw new IOException ( ) ;
ulong length = frame . PayloadLen < = 125
? frame . PayloadLen
: frame . PayloadLen = = 126
? frame . ExtPayloadLen . To < ushort > ( ByteOrder . BIG )
: frame . ExtPayloadLen . To < ulong > ( ByteOrder . BIG ) ;
if ( length = = 0 )
frame . ExtPayloadLen = extPayloadLen ;
{
frame . PayloadData = new PayloadData ( ) ;
return ;
}
if ( frame . PayloadLen > 126 & & length > PayloadData . MaxLength )
/* Masking Key */
throw new WebSocketException ( CloseStatusCode . TOO_BIG ) ;
var buffer = length < = ( ulong ) _readBufferLen
var masked = mask = = Mask . MASK ? true : false ;
? stream . ReadBytes ( ( int ) length )
var maskingKey = masked
: stream . ReadBytes ( ( long ) length , _readBufferLen ) ;
? stream . ReadBytesInternal ( 4 )
: new byte [ ] { } ;
if ( buffer. LongLength ! = ( long ) length )
if ( masked & & maskingKey . Length ! = 4 )
throw new IOException ( ) ;
throw new IOException ( ) ;
var masked = isMasked ( frame . Mask ) ;
frame . MaskingKey = maskingKey ;
var payloadData = masked
? new PayloadData ( buffer , true )
/* Payload Data */
: new PayloadData ( buffer ) ;
ulong dataLen = payloadLen < 126
? payloadLen
: payloadLen = = 126
? extPayloadLen . To < ushort > ( ByteOrder . BIG )
: extPayloadLen . To < ulong > ( ByteOrder . BIG ) ;
byte [ ] data = null ;
if ( dataLen > 0 )
{
if ( payloadLen > 126 & & dataLen > PayloadData . MaxLength )
throw new WebSocketException ( CloseStatusCode . TOO_BIG ) ;
data = dataLen > 1024
? stream . ReadBytesInternal ( ( long ) dataLen , 1024 )
: stream . ReadBytesInternal ( ( int ) dataLen ) ;
if ( data . LongLength ! = ( long ) dataLen )
throw new IOException ( ) ;
}
else
{
data = new byte [ ] { } ;
}
var payloadData = new PayloadData ( data , masked ) ;
if ( masked & & unmask )
if ( masked & & unmask )
{
{
payloadData . Mask ( frame . MaskingKey ) ;
payloadData . Mask ( maskingKey) ;
frame . Mask = Mask . UNMASK ;
frame . Mask = Mask . UNMASK ;
frame . MaskingKey = new byte [ ] { } ;
frame . MaskingKey = new byte [ ] { } ;
}
}
frame . PayloadData = payloadData ;
frame . PayloadData = payloadData ;
return frame ;
}
}
private void setPayloadLen ( ulong length )
private static void print ( WsFrame frame )
{
{
if ( length < 126 )
var len = frame . ExtPayloadLen . Length ;
{
var extPayloadLen = len = = 2
PayloadLen = ( byte ) length ;
? frame . ExtPayloadLen . To < ushort > ( ByteOrder . BIG ) . ToString ( )
ExtPayloadLen = new byte [ ] { } ;
: len = = 8
return ;
? frame . ExtPayloadLen . To < ulong > ( ByteOrder . BIG ) . ToString ( )
}
: String . Empty ;
if ( length < 0x010000 )
var masked = frame . IsMasked ;
{
var maskingKey = masked
PayloadLen = ( byte ) 126 ;
? BitConverter . ToString ( frame . MaskingKey )
ExtPayloadLen = ( ( ushort ) length ) . ToByteArray ( ByteOrder . BIG ) ;
: String . Empty ;
return ;
}
PayloadLen = ( byte ) 127 ;
var opcode = frame . Opcode ;
ExtPayloadLen = length . ToByteArray ( ByteOrder . BIG ) ;
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 ( ) ) ;
private void unmaskPayloadData ( )
var format = @ "
{
FIN : { 0 }
PayloadData . Mask ( MaskingKey ) ;
RSV1 : { 1 }
Mask = Mask . UNMASK ;
RSV2 : { 2 }
MaskingKey = new byte [ ] { } ;
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 ) ;
}
}
# endregion
# endregion
@ -574,12 +558,16 @@ namespace WebSocketSharp {
WsFrame frame = null ;
WsFrame frame = null ;
try
try
{
{
var header = stream . ReadBytes ( 2 ) ;
var header = stream . ReadBytes Internal ( 2 ) ;
frame = parse ( header , stream , unmask ) ;
frame = parse ( header , stream , unmask ) ;
}
}
catch ( WebSocketException ex )
{
frame = createCloseFrame ( ex ) ;
}
catch ( Exception ex )
catch ( Exception ex )
{
{
if ( ! error . IsNull ( ) )
if ( error ! = null )
error ( ex ) ;
error ( ex ) ;
}
}
@ -605,25 +593,27 @@ namespace WebSocketSharp {
WsFrame frame = null ;
WsFrame frame = null ;
try
try
{
{
int readLen = stream . EndRead ( ar ) ;
var readLen = stream . EndRead ( ar ) ;
if ( readLen != 2 )
if ( readLen > 0 )
{
{
if ( readLen = = 1 )
if ( readLen = = 1 )
header [ 1 ] = ( byte ) stream . ReadByte ( ) ;
header [ 1 ] = ( byte ) stream . ReadByte ( ) ;
else
header = null ;
}
frame = parse ( header , stream , unmask ) ;
frame = parse ( header , stream , unmask ) ;
}
}
catch ( WebSocketException ex )
{
frame = createCloseFrame ( ex ) ;
}
}
catch ( Exception ex )
catch ( Exception ex )
{
{
if ( ! error . IsNull ( ) )
if ( error ! = null )
error ( ex ) ;
error ( ex ) ;
}
}
finally
finally
{
{
if ( ! completed . IsNull ( ) )
if ( completed ! = null )
completed ( frame ) ;
completed ( frame ) ;
}
}
} ;
} ;
@ -641,27 +631,35 @@ namespace WebSocketSharp {
public byte [ ] ToByteArray ( )
public byte [ ] ToByteArray ( )
{
{
var buffer = new List < byte > ( ) ;
using ( var buffer = new MemoryStream ( ) )
{
int 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 ;
header = ( header < < 1 ) + ( int ) Mask ;
header = ( header < < 7 ) + ( int ) PayloadLen ;
header = ( header < < 7 ) + ( int ) PayloadLen ;
buffer . AddRange ( ( ( ushort ) header ) . ToByteArray ( ByteOrder . BIG ) ) ;
buffer . Write ( ( ( ushort ) header ) . ToByteArray ( ByteOrder . BIG ) , 0 , 2 ) ;
if ( PayloadLen > = 126 )
if ( PayloadLen > 125 )
buffer . AddRange ( ExtPayloadLen ) ;
buffer . Write ( ExtPayloadLen , 0 , ExtPayloadLen . Length ) ;
if ( IsMasked )
if ( Mask = = Mask . MASK )
buffer . AddRange ( MaskingKey ) ;
buffer . Write ( MaskingKey , 0 , MaskingKey . Length ) ;
if ( PayloadLen > 0 )
if ( PayloadLen > 0 )
buffer . AddRange ( PayloadData . ToByteArray ( ) ) ;
{
var payload = PayloadData . ToByteArray ( ) ;
if ( PayloadLen < 127 )
buffer . Write ( payload , 0 , payload . Length ) ;
else
buffer . WriteBytes ( payload ) ;
}
return buffer . ToArray ( ) ;
buffer . Close ( ) ;
return buffer . ToArray ( ) ;
}
}
}
public override string ToString ( )
public override string ToString ( )