Modified closing and ping

This commit is contained in:
sta 2013-10-01 14:52:39 +09:00
parent f103cd2faf
commit 2b57a58da2
12 changed files with 491 additions and 454 deletions

View File

@ -41,7 +41,7 @@ namespace WebSocketSharp
/// </remarks> /// </remarks>
public class CloseEventArgs : MessageEventArgs public class CloseEventArgs : MessageEventArgs
{ {
#region Fields #region Private Fields
private bool _clean; private bool _clean;
private ushort _code; private ushort _code;
@ -49,19 +49,19 @@ namespace WebSocketSharp
#endregion #endregion
#region Constructors #region Internal Constructors
internal CloseEventArgs (PayloadData data) internal CloseEventArgs (PayloadData payload)
: base (Opcode.CLOSE, data) : base (Opcode.CLOSE, payload)
{ {
_code = getCodeFrom (data); _code = getCodeFrom (RawData);
_reason = getReasonFrom (data); _reason = getReasonFrom (RawData);
_clean = false; _clean = false;
} }
#endregion #endregion
#region Properties #region Public Properties
/// <summary> /// <summary>
/// Gets the status code for closure. /// Gets the status code for closure.
@ -107,23 +107,19 @@ namespace WebSocketSharp
#region Private Methods #region Private Methods
private static ushort getCodeFrom (PayloadData data) private static ushort getCodeFrom (byte [] data)
{ {
var appData = data.ApplicationData; return data.Length > 1
return appData.Length >= 2 ? data.SubArray (0, 2).To<ushort> (ByteOrder.BIG)
? appData.SubArray (0, 2).To<ushort> (ByteOrder.BIG) : (ushort) 0;
: (ushort) CloseStatusCode.NO_STATUS_CODE;
} }
private static string getReasonFrom (PayloadData data) private static string getReasonFrom (byte [] data)
{ {
var appData = data.ApplicationData; var len = data.Length;
var appDataLen = appData.Length; return len > 2
if (appDataLen <= 2) ? Encoding.UTF8.GetString (data.SubArray (2, len - 2))
return String.Empty; : String.Empty;
var reason = appData.SubArray (2, appDataLen - 2);
return Encoding.UTF8.GetString (reason);
} }
#endregion #endregion

View File

@ -168,7 +168,7 @@ namespace WebSocketSharp
internal static string CheckIfStarted (this ServerState state) internal static string CheckIfStarted (this ServerState state)
{ {
return state != ServerState.START return state != ServerState.START
? "Not started, on shutdown or stopped." ? "Any of not started, on shutdown or stopped."
: null; : null;
} }
@ -396,6 +396,22 @@ namespace WebSocketSharp
return value > 0 && value < 65536; return value > 0 && value < 65536;
} }
internal static bool IsReserved (this ushort code)
{
return code == (ushort) CloseStatusCode.UNDEFINED ||
code == (ushort) CloseStatusCode.NO_STATUS_CODE ||
code == (ushort) CloseStatusCode.ABNORMAL ||
code == (ushort) CloseStatusCode.TLS_HANDSHAKE_FAILURE;
}
internal static bool IsReserved (this CloseStatusCode code)
{
return code == CloseStatusCode.UNDEFINED ||
code == CloseStatusCode.NO_STATUS_CODE ||
code == CloseStatusCode.ABNORMAL ||
code == CloseStatusCode.TLS_HANDSHAKE_FAILURE;
}
internal static bool IsText (this string value) internal static bool IsText (this string value)
{ {
int len = value.Length; int len = value.Length;

View File

@ -29,8 +29,8 @@
using System; using System;
using System.Text; using System.Text;
namespace WebSocketSharp { namespace WebSocketSharp
{
/// <summary> /// <summary>
/// Contains the event data associated with a <see cref="WebSocket.OnMessage"/> event. /// Contains the event data associated with a <see cref="WebSocket.OnMessage"/> event.
/// </summary> /// </summary>
@ -51,16 +51,16 @@ namespace WebSocketSharp {
#region Internal Constructors #region Internal Constructors
internal MessageEventArgs(Opcode opcode, byte[] rawData) internal MessageEventArgs (Opcode opcode, byte[] rawData)
{ {
if ((ulong)rawData.LongLength > PayloadData.MaxLength) if ((ulong) rawData.LongLength > PayloadData.MaxLength)
throw new WebSocketException(CloseStatusCode.TOO_BIG); throw new WebSocketException (CloseStatusCode.TOO_BIG);
_opcode = opcode; _opcode = opcode;
_rawData = rawData; _rawData = rawData;
} }
internal MessageEventArgs(Opcode opcode, PayloadData data) internal MessageEventArgs (Opcode opcode, PayloadData data)
{ {
_opcode = opcode; _opcode = opcode;
_rawData = data.ApplicationData; _rawData = data.ApplicationData;
@ -82,8 +82,8 @@ namespace WebSocketSharp {
_data = _rawData.LongLength == 0 _data = _rawData.LongLength == 0
? String.Empty ? String.Empty
: _opcode == Opcode.TEXT : _opcode == Opcode.TEXT
? Encoding.UTF8.GetString(_rawData) ? Encoding.UTF8.GetString (_rawData)
: _opcode.ToString(); : _opcode.ToString ();
return _data; return _data;
} }
@ -95,7 +95,7 @@ namespace WebSocketSharp {
/// <value> /// <value>
/// An array of <see cref="byte"/> that contains the received data. /// An array of <see cref="byte"/> that contains the received data.
/// </value> /// </value>
public byte[] RawData { public byte [] RawData {
get { get {
return _rawData; return _rawData;
} }
@ -105,7 +105,7 @@ namespace WebSocketSharp {
/// Gets the type of the received data. /// Gets the type of the received data.
/// </summary> /// </summary>
/// <value> /// <value>
/// One of the <see cref="Opcode"/> values that indicates the type of the received data. /// One of the <see cref="Opcode"/> values, indicates the type of the received data.
/// </value> /// </value>
public Opcode Type { public Opcode Type {
get { get {

View File

@ -33,11 +33,11 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace WebSocketSharp { namespace WebSocketSharp
{
internal class PayloadData : IEnumerable<byte> internal class PayloadData : IEnumerable<byte>
{ {
#region Public Fields #region Public Const Fields
public const ulong MaxLength = long.MaxValue; public const ulong MaxLength = long.MaxValue;
@ -45,35 +45,35 @@ namespace WebSocketSharp {
#region Public Constructors #region Public Constructors
public PayloadData() public PayloadData ()
: this(new byte[]{}) : this (new byte []{})
{ {
} }
public PayloadData(byte[] appData) public PayloadData (byte [] appData)
: this(new byte[]{}, appData) : this (new byte []{}, appData)
{ {
} }
public PayloadData(string appData) public PayloadData (string appData)
: this(Encoding.UTF8.GetBytes(appData)) : this (Encoding.UTF8.GetBytes (appData))
{ {
} }
public PayloadData(byte[] appData, bool masked) public PayloadData (byte [] appData, bool masked)
: this(new byte[]{}, appData, masked) : this (new byte []{}, appData, masked)
{ {
} }
public PayloadData(byte[] extData, byte[] appData) public PayloadData (byte [] extData, byte [] appData)
: this(extData, appData, false) : this (extData, appData, false)
{ {
} }
public PayloadData(byte[] extData, byte[] appData, bool masked) public PayloadData (byte [] extData, byte [] appData, bool masked)
{ {
if ((ulong)extData.LongLength + (ulong)appData.LongLength > MaxLength) if ((ulong) extData.LongLength + (ulong) appData.LongLength > MaxLength)
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException (
"The length of 'extData' plus 'appData' must be less than MaxLength."); "The length of 'extData' plus 'appData' must be less than MaxLength.");
ExtensionData = extData; ExtensionData = extData;
@ -87,17 +87,9 @@ namespace WebSocketSharp {
internal bool ContainsReservedCloseStatusCode { internal bool ContainsReservedCloseStatusCode {
get { get {
if (Length >= 2) return ApplicationData.Length > 1
{ ? ApplicationData.SubArray (0, 2).To<ushort> (ByteOrder.BIG).IsReserved ()
var code = ToByteArray().SubArray(0, 2).To<ushort>(ByteOrder.BIG); : false;
if (code == (ushort)CloseStatusCode.UNDEFINED ||
code == (ushort)CloseStatusCode.NO_STATUS_CODE ||
code == (ushort)CloseStatusCode.ABNORMAL ||
code == (ushort)CloseStatusCode.TLS_HANDSHAKE_FAILURE)
return true;
}
return false;
} }
} }
@ -105,32 +97,32 @@ namespace WebSocketSharp {
get; private set; get; private set;
} }
internal ulong Length {
get {
return (ulong)(ExtensionData.LongLength + ApplicationData.LongLength);
}
}
#endregion #endregion
#region Public Properties #region Public Properties
public byte[] ExtensionData { public byte [] ExtensionData {
get; private set; get; private set;
} }
public byte[] ApplicationData { public byte [] ApplicationData {
get; private set; get; private set;
} }
public ulong Length {
get {
return (ulong) (ExtensionData.LongLength + ApplicationData.LongLength);
}
}
#endregion #endregion
#region Private Methods #region Private Methods
private static void mask(byte[] src, byte[] key) private static void mask (byte [] src, byte [] key)
{ {
for (long i = 0; i < src.LongLength; i++) for (long i = 0; i < src.LongLength; i++)
src[i] = (byte)(src[i] ^ key[i % 4]); src [i] = (byte) (src [i] ^ key [i % 4]);
} }
#endregion #endregion
@ -141,7 +133,7 @@ namespace WebSocketSharp {
#region Public Methods #region Public Methods
public IEnumerator<byte> GetEnumerator() public IEnumerator<byte> GetEnumerator ()
{ {
foreach (byte b in ExtensionData) foreach (byte b in ExtensionData)
yield return b; yield return b;
@ -150,36 +142,36 @@ namespace WebSocketSharp {
yield return b; yield return b;
} }
public void Mask(byte[] maskingKey) public void Mask (byte [] maskingKey)
{ {
if (ExtensionData.LongLength > 0) if (ExtensionData.LongLength > 0)
mask(ExtensionData, maskingKey); mask (ExtensionData, maskingKey);
if (ApplicationData.LongLength > 0) if (ApplicationData.LongLength > 0)
mask(ApplicationData, maskingKey); mask (ApplicationData, maskingKey);
IsMasked = !IsMasked; IsMasked = !IsMasked;
} }
public byte[] ToByteArray() public byte [] ToByteArray ()
{ {
return ExtensionData.LongLength > 0 return ExtensionData.LongLength > 0
? this.ToArray() ? this.ToArray ()
: ApplicationData; : ApplicationData;
} }
public override string ToString() public override string ToString ()
{ {
return BitConverter.ToString(ToByteArray()); return BitConverter.ToString (ToByteArray ());
} }
#endregion #endregion
#region Explicitly Implemented Interface Members #region Explicitly Implemented Interface Members
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator ()
{ {
return GetEnumerator(); return GetEnumerator ();
} }
#endregion #endregion

View File

@ -321,7 +321,7 @@ namespace WebSocketSharp.Server
} }
_listening = false; _listening = false;
_serviceHosts.Stop (((ushort) CloseStatusCode.SERVER_ERROR).ToByteArray (ByteOrder.BIG)); _serviceHosts.Stop (((ushort) CloseStatusCode.SERVER_ERROR).ToByteArray (ByteOrder.BIG), true);
_listener.Abort (); _listener.Abort ();
_state = ServerState.STOP; _state = ServerState.STOP;
@ -617,7 +617,7 @@ namespace WebSocketSharp.Server
_state = ServerState.SHUTDOWN; _state = ServerState.SHUTDOWN;
} }
_serviceHosts.Stop (); _serviceHosts.Stop (new byte []{}, true);
stopListener (); stopListener ();
_state = ServerState.STOP; _state = ServerState.STOP;
@ -651,7 +651,7 @@ namespace WebSocketSharp.Server
_state = ServerState.SHUTDOWN; _state = ServerState.SHUTDOWN;
} }
_serviceHosts.Stop (data); _serviceHosts.Stop (data, !code.IsReserved ());
stopListener (); stopListener ();
_state = ServerState.STOP; _state = ServerState.STOP;
@ -685,7 +685,7 @@ namespace WebSocketSharp.Server
_state = ServerState.SHUTDOWN; _state = ServerState.SHUTDOWN;
} }
_serviceHosts.Stop (data); _serviceHosts.Stop (data, !code.IsReserved ());
stopListener (); stopListener ();
_state = ServerState.STOP; _state = ServerState.STOP;

View File

@ -201,7 +201,7 @@ namespace WebSocketSharp.Server
} }
StopListener (); StopListener ();
_serviceHosts.Stop (((ushort) CloseStatusCode.SERVER_ERROR).ToByteArray (ByteOrder.BIG)); _serviceHosts.Stop (((ushort) CloseStatusCode.SERVER_ERROR).ToByteArray (ByteOrder.BIG), true);
_state = ServerState.STOP; _state = ServerState.STOP;
} }
@ -313,7 +313,7 @@ namespace WebSocketSharp.Server
base.Start (); base.Start ();
if (!IsListening) if (!IsListening)
{ {
_serviceHosts.Stop (); _serviceHosts.Stop (new byte []{}, false);
return; return;
} }
@ -339,7 +339,7 @@ namespace WebSocketSharp.Server
} }
base.Stop (); base.Stop ();
_serviceHosts.Stop (); _serviceHosts.Stop (new byte []{}, true);
_state = ServerState.STOP; _state = ServerState.STOP;
} }
@ -373,7 +373,7 @@ namespace WebSocketSharp.Server
} }
base.Stop (); base.Stop ();
_serviceHosts.Stop (data); _serviceHosts.Stop (data, !code.IsReserved ());
_state = ServerState.STOP; _state = ServerState.STOP;
} }
@ -407,7 +407,7 @@ namespace WebSocketSharp.Server
} }
base.Stop (); base.Stop ();
_serviceHosts.Stop (data); _serviceHosts.Stop (data, !code.IsReserved ());
_state = ServerState.STOP; _state = ServerState.STOP;
} }

View File

@ -294,7 +294,7 @@ namespace WebSocketSharp.Server
} }
StopListener (); StopListener ();
_sessions.Stop (((ushort) CloseStatusCode.SERVER_ERROR).ToByteArray (ByteOrder.BIG)); _sessions.Stop (((ushort) CloseStatusCode.SERVER_ERROR).ToByteArray (ByteOrder.BIG), true);
_state = ServerState.STOP; _state = ServerState.STOP;
} }
@ -346,7 +346,7 @@ namespace WebSocketSharp.Server
base.Start (); base.Start ();
if (!IsListening) if (!IsListening)
{ {
_sessions.Stop (); _sessions.Stop (new byte []{}, false);
return; return;
} }
@ -372,7 +372,7 @@ namespace WebSocketSharp.Server
} }
base.Stop (); base.Stop ();
_sessions.Stop (); _sessions.Stop (new byte []{}, true);
_state = ServerState.STOP; _state = ServerState.STOP;
} }
@ -406,7 +406,7 @@ namespace WebSocketSharp.Server
} }
base.Stop (); base.Stop ();
_sessions.Stop (data); _sessions.Stop (data, !code.IsReserved ());
_state = ServerState.STOP; _state = ServerState.STOP;
} }
@ -440,7 +440,7 @@ namespace WebSocketSharp.Server
} }
base.Stop (); base.Stop ();
_sessions.Stop (data); _sessions.Stop (data, !code.IsReserved ());
_state = ServerState.STOP; _state = ServerState.STOP;
} }

View File

@ -195,7 +195,7 @@ namespace WebSocketSharp.Server
#region Private Methods #region Private Methods
private Dictionary<string, Dictionary<string, bool>> broadping (byte [] data) private Dictionary<string, Dictionary<string, bool>> broadping (byte [] frameAsBytes, int timeOut)
{ {
var result = new Dictionary<string, Dictionary<string, bool>> (); var result = new Dictionary<string, Dictionary<string, bool>> ();
foreach (var host in ServiceHosts) foreach (var host in ServiceHosts)
@ -203,7 +203,7 @@ namespace WebSocketSharp.Server
if (_state != ServerState.START) if (_state != ServerState.START)
break; break;
result.Add (host.ServicePath, host.Sessions.BroadpingInternally (data)); result.Add (host.ServicePath, host.Sessions.BroadpingInternally (frameAsBytes, timeOut));
} }
return result; return result;
@ -250,7 +250,7 @@ namespace WebSocketSharp.Server
} }
if (host.Sessions.State == ServerState.START) if (host.Sessions.State == ServerState.START)
host.Sessions.Stop (((ushort) CloseStatusCode.AWAY).ToByteArray (ByteOrder.BIG)); host.Sessions.Stop (((ushort) CloseStatusCode.AWAY).ToByteArray (ByteOrder.BIG), true);
return true; return true;
} }
@ -266,28 +266,23 @@ namespace WebSocketSharp.Server
} }
} }
internal void Stop () internal void Stop (byte [] data, bool send)
{ {
lock (_sync) lock (_sync)
{ {
_state = ServerState.SHUTDOWN; _state = ServerState.SHUTDOWN;
var payload = new PayloadData (data);
var args = new CloseEventArgs (payload);
var frameAsBytes = send
? WsFrame.CreateCloseFrame (Mask.UNMASK, payload).ToByteArray ()
: null;
foreach (var host in _serviceHosts.Values) foreach (var host in _serviceHosts.Values)
host.Sessions.Stop (); host.Sessions.Stop (args, frameAsBytes);
_serviceHosts.Clear (); _serviceHosts.Clear ();
_state = ServerState.STOP;
}
}
internal void Stop (byte [] data)
{
lock (_sync)
{
_state = ServerState.SHUTDOWN;
foreach (var host in _serviceHosts.Values)
host.Sessions.Stop (data);
_serviceHosts.Clear ();
_state = ServerState.STOP; _state = ServerState.STOP;
} }
} }
@ -430,7 +425,7 @@ namespace WebSocketSharp.Server
return null; return null;
} }
return broadping (new byte [] {}); return broadping (WsFrame.CreatePingFrame (Mask.UNMASK).ToByteArray (), 1000);
} }
/// <summary> /// <summary>
@ -459,7 +454,7 @@ namespace WebSocketSharp.Server
return null; return null;
} }
return broadping (data); return broadping (WsFrame.CreatePingFrame (Mask.UNMASK, data).ToByteArray (), 1000);
} }
/// <summary> /// <summary>
@ -489,7 +484,7 @@ namespace WebSocketSharp.Server
return null; return null;
} }
return host.Sessions.BroadpingInternally (new byte [] {}); return host.Sessions.BroadpingInternally ();
} }
/// <summary> /// <summary>
@ -527,7 +522,8 @@ namespace WebSocketSharp.Server
return null; return null;
} }
return host.Sessions.BroadpingInternally (data); return host.Sessions.BroadpingInternally (
WsFrame.CreatePingFrame (Mask.UNMASK, data).ToByteArray (), 1000);
} }
/// <summary> /// <summary>

View File

@ -106,7 +106,7 @@ namespace WebSocketSharp.Server
/// </value> /// </value>
public IEnumerable<string> ActiveIDs { public IEnumerable<string> ActiveIDs {
get { get {
return from result in BroadpingInternally (new byte [] {}) return from result in BroadpingInternally ()
where result.Value where result.Value
select result.Key; select result.Key;
} }
@ -150,7 +150,7 @@ namespace WebSocketSharp.Server
/// </value> /// </value>
public IEnumerable<string> InactiveIDs { public IEnumerable<string> InactiveIDs {
get { get {
return from result in BroadpingInternally (new byte [] {}) return from result in BroadpingInternally ()
where !result.Value where !result.Value
select result.Key; select result.Key;
} }
@ -282,7 +282,12 @@ namespace WebSocketSharp.Server
services.Current.SendAsync (data, completed); services.Current.SendAsync (data, completed);
} }
internal Dictionary<string, bool> BroadpingInternally (byte [] data) internal Dictionary<string, bool> BroadpingInternally ()
{
return BroadpingInternally (WsFrame.CreatePingFrame (Mask.UNMASK).ToByteArray (), 1000);
}
internal Dictionary<string, bool> BroadpingInternally (byte [] frameAsBytes, int timeOut)
{ {
var result = new Dictionary<string, bool> (); var result = new Dictionary<string, bool> ();
foreach (var session in ServiceInstances) foreach (var session in ServiceInstances)
@ -290,7 +295,7 @@ namespace WebSocketSharp.Server
if (_state != ServerState.START) if (_state != ServerState.START)
break; break;
result.Add (session.ID, session.Context.WebSocket.Ping (data)); result.Add (session.ID, session.Context.WebSocket.Ping (frameAsBytes, timeOut));
} }
return result; return result;
@ -310,7 +315,18 @@ namespace WebSocketSharp.Server
_state = ServerState.START; _state = ServerState.START;
} }
internal void Stop () internal void Stop (byte [] data, bool send)
{
var payload = new PayloadData (data);
var args = new CloseEventArgs (payload);
var frameAsBytes = send
? WsFrame.CreateCloseFrame (Mask.UNMASK, payload).ToByteArray ()
: null;
Stop (args, frameAsBytes);
}
internal void Stop (CloseEventArgs args, byte [] frameAsBytes)
{ {
lock (_sync) lock (_sync)
{ {
@ -318,21 +334,7 @@ namespace WebSocketSharp.Server
_sweepTimer.Enabled = false; _sweepTimer.Enabled = false;
foreach (var session in _sessions.Values.ToList ()) foreach (var session in _sessions.Values.ToList ())
session.Context.WebSocket.Close (); session.Context.WebSocket.Close (args, frameAsBytes, 1000);
_state = ServerState.STOP;
}
}
internal void Stop (byte [] data)
{
lock (_sync)
{
_state = ServerState.SHUTDOWN;
_sweepTimer.Enabled = false;
foreach (var session in _sessions.Values.ToList ())
session.Context.WebSocket.Close (data);
_state = ServerState.STOP; _state = ServerState.STOP;
} }
@ -402,7 +404,7 @@ namespace WebSocketSharp.Server
return null; return null;
} }
return BroadpingInternally (new byte [] {}); return BroadpingInternally ();
} }
/// <summary> /// <summary>
@ -428,7 +430,7 @@ namespace WebSocketSharp.Server
return null; return null;
} }
return BroadpingInternally (data); return BroadpingInternally (WsFrame.CreatePingFrame (Mask.UNMASK, data).ToByteArray (), 1000);
} }
/// <summary> /// <summary>

View File

@ -80,7 +80,6 @@ namespace WebSocketSharp
private string _extensions; private string _extensions;
private AutoResetEvent _exitReceiving; private AutoResetEvent _exitReceiving;
private object _forClose; private object _forClose;
private object _forFrame;
private object _forSend; private object _forSend;
private volatile Logger _logger; private volatile Logger _logger;
private string _origin; private string _origin;
@ -104,7 +103,6 @@ namespace WebSocketSharp
_cookies = new CookieCollection (); _cookies = new CookieCollection ();
_extensions = String.Empty; _extensions = String.Empty;
_forClose = new object (); _forClose = new object ();
_forFrame = new object ();
_forSend = new object (); _forSend = new object ();
_logger = new Logger (); _logger = new Logger ();
_origin = String.Empty; _origin = String.Empty;
@ -319,9 +317,7 @@ namespace WebSocketSharp
/// </value> /// </value>
public bool IsAlive { public bool IsAlive {
get { get {
return _readyState == WebSocketState.OPEN return Ping ();
? Ping (new byte [] {})
: false;
} }
} }
@ -531,23 +527,13 @@ namespace WebSocketSharp
return send (createHandshakeResponse ()); return send (createHandshakeResponse ());
} }
// As server private void close (CloseStatusCode code, string reason, bool wait)
private void close (HttpStatusCode code)
{ {
send (createHandshakeResponse (code)); close (new PayloadData (((ushort) code).Append (reason)), !code.IsReserved (), wait);
try {
closeServerResources ();
}
catch {
} }
_readyState = WebSocketState.CLOSED; private void close (PayloadData payload, bool send, bool wait)
}
private void close (PayloadData data, bool received)
{ {
var sent = false;
CloseEventArgs args = null;
lock (_forClose) lock (_forClose)
{ {
if (_readyState == WebSocketState.CLOSING || _readyState == WebSocketState.CLOSED) if (_readyState == WebSocketState.CLOSING || _readyState == WebSocketState.CLOSED)
@ -555,40 +541,42 @@ namespace WebSocketSharp
_logger.Trace ("Start closing handshake."); _logger.Trace ("Start closing handshake.");
var current = _readyState;
_readyState = WebSocketState.CLOSING; _readyState = WebSocketState.CLOSING;
if (current == WebSocketState.CONNECTING && !_client)
{
close (HttpStatusCode.BadRequest);
return;
} }
args = new CloseEventArgs (data); var args = new CloseEventArgs (payload);
if (current == WebSocketState.OPEN && !data.ContainsReservedCloseStatusCode) args.WasClean = _client
sent = send (createControlFrame (Opcode.CLOSE, data, _client)); ? close (
} send ? WsFrame.CreateCloseFrame (Mask.MASK, payload).ToByteArray () : null,
wait ? 5000 : 0,
received = received || (sent && _exitReceiving != null && _exitReceiving.WaitOne (5 * 1000)); closeClientResources)
var released = closeResources (); : close (
args.WasClean = sent && received && released; send ? WsFrame.CreateCloseFrame (Mask.UNMASK, payload).ToByteArray () : null,
_logger.Debug (String.Format ( wait ? 1000 : 0,
"Was clean?: {0}\nsent: {1} received: {2} released: {3}", args.WasClean, sent, received, released)); closeServerResources);
_readyState = WebSocketState.CLOSED; _readyState = WebSocketState.CLOSED;
_logger.Trace ("End closing handshake.");
_logger.Trace ("End closing handshake.");
OnClose.Emit (this, args); OnClose.Emit (this, args);
} }
private void close (CloseStatusCode code, string reason, bool received) private bool close (byte [] frameAsBytes, int timeOut, Func<bool> release)
{ {
var data = ((ushort) code).Append (reason); var sent = frameAsBytes != null && _stream.Write (frameAsBytes);
close (new PayloadData (data), received); var received = timeOut == 0 || (sent && _exitReceiving.WaitOne (timeOut));
var released = release ();
var result = sent && received && released;
_logger.Debug (String.Format (
"Was clean?: {0}\nsent: {1} received: {2} released: {3}", result, sent, received, released));
return result;
} }
// As client // As client
private void closeClientResources () private bool closeClientResources ()
{ {
try {
if (_stream != null) if (_stream != null)
{ {
_stream.Dispose (); _stream.Dispose ();
@ -600,15 +588,6 @@ namespace WebSocketSharp
_tcpClient.Close (); _tcpClient.Close ();
_tcpClient = null; _tcpClient = null;
} }
}
private bool closeResources ()
{
try {
if (_client)
closeClientResources ();
else
closeServerResources ();
return true; return true;
} }
@ -621,13 +600,23 @@ namespace WebSocketSharp
} }
// As server // As server
private void closeServerResources () private bool closeServerResources ()
{ {
try {
if (_closeContext != null) if (_closeContext != null)
_closeContext (); _closeContext ();
_stream = null; _stream = null;
_context = null; _context = null;
return true;
}
catch (Exception ex) {
_logger.Fatal (ex.ToString ());
error ("An exception has occured.");
return false;
}
} }
private bool concatenateFragments (Stream dest) private bool concatenateFragments (Stream dest)
@ -697,23 +686,6 @@ namespace WebSocketSharp
return Convert.ToBase64String (src); return Convert.ToBase64String (src);
} }
private static WsFrame createControlFrame (Opcode opcode, PayloadData payloadData, bool client)
{
var mask = client ? Mask.MASK : Mask.UNMASK;
var frame = new WsFrame (Fin.FINAL, opcode, mask, payloadData);
return frame;
}
private static WsFrame createFrame (
Fin fin, Opcode opcode, PayloadData payloadData, bool compressed, bool client)
{
var mask = client ? Mask.MASK : Mask.UNMASK;
var frame = new WsFrame (fin, opcode, mask, payloadData, compressed);
return frame;
}
// As client // As client
private HandshakeRequest createHandshakeRequest () private HandshakeRequest createHandshakeRequest ()
{ {
@ -851,23 +823,19 @@ namespace WebSocketSharp
OnOpen.Emit (this, EventArgs.Empty); OnOpen.Emit (this, EventArgs.Empty);
} }
private void pong (PayloadData data)
{
var frame = createControlFrame (Opcode.PONG, data, _client);
send (frame);
}
private bool processAbnormalFrame () private bool processAbnormalFrame ()
{ {
var code = CloseStatusCode.ABNORMAL; var code = CloseStatusCode.ABNORMAL;
close (code, code.GetMessage (), true); close (code, code.GetMessage (), false);
return false; return false;
} }
private bool processCloseFrame (WsFrame frame) private bool processCloseFrame (WsFrame frame)
{ {
close (frame.PayloadData, true); var payload = frame.PayloadData;
close (payload, !payload.ContainsReservedCloseStatusCode, false);
return false; return false;
} }
@ -937,22 +905,23 @@ namespace WebSocketSharp
private bool processIncorrectFrame () private bool processIncorrectFrame ()
{ {
close (CloseStatusCode.INCORRECT_DATA, null, true); close (CloseStatusCode.INCORRECT_DATA, null, false);
return false; return false;
} }
private bool processPingFrame (WsFrame frame) private bool processPingFrame (WsFrame frame)
{ {
_logger.Trace ("Return Pong."); if (send (WsFrame.CreatePongFrame (
pong (frame.PayloadData); _client ? Mask.MASK : Mask.UNMASK, frame.PayloadData).ToByteArray ()))
_logger.Trace ("Returned Pong.");
return true; return true;
} }
private bool processPongFrame () private bool processPongFrame ()
{ {
_logger.Trace ("Receive Pong.");
_receivePong.Set (); _receivePong.Set ();
_logger.Trace ("Received Pong.");
return true; return true;
} }
@ -1012,11 +981,25 @@ namespace WebSocketSharp
return res; return res;
} }
private bool send (byte [] frameAsBytes)
{
if (_readyState != WebSocketState.OPEN)
{
var msg = "A WebSocket connection isn't established or has been closed.";
_logger.Error (msg);
error (msg);
return false;
}
return _stream.Write (frameAsBytes);
}
// As client // As client
private void send (HandshakeRequest request) private void send (HandshakeRequest request)
{ {
_logger.Debug (String.Format ("A WebSocket connection request to {0}:\n{1}", _logger.Debug (String.Format (
_uri, request)); "A WebSocket connection request to {0}:\n{1}", _uri, request));
_stream.WriteHandshake (request); _stream.WriteHandshake (request);
} }
@ -1029,13 +1012,7 @@ namespace WebSocketSharp
private bool send (WsFrame frame) private bool send (WsFrame frame)
{ {
lock (_forFrame) if (_readyState != WebSocketState.OPEN)
{
var ready = _stream != null &&
(_readyState == WebSocketState.OPEN ||
(_readyState == WebSocketState.CLOSING && frame.IsClose));
if (!ready)
{ {
var msg = "A WebSocket connection isn't established or has been closed."; var msg = "A WebSocket connection isn't established or has been closed.";
_logger.Error (msg); _logger.Error (msg);
@ -1046,7 +1023,6 @@ namespace WebSocketSharp
return _stream.WriteFrame (frame); return _stream.WriteFrame (frame);
} }
}
private void send (Opcode opcode, Stream stream) private void send (Opcode opcode, Stream stream)
{ {
@ -1091,8 +1067,8 @@ namespace WebSocketSharp
private bool send (Fin fin, Opcode opcode, byte [] data, bool compressed) private bool send (Fin fin, Opcode opcode, byte [] data, bool compressed)
{ {
var frame = createFrame (fin, opcode, new PayloadData (data), compressed, _client); return send (
return send (frame); WsFrame.CreateFrame (fin, opcode, _client ? Mask.MASK : Mask.UNMASK, data, compressed));
} }
private void sendAsync (Opcode opcode, Stream stream, Action completed) private void sendAsync (Opcode opcode, Stream stream, Action completed)
@ -1201,12 +1177,12 @@ namespace WebSocketSharp
catch (WebSocketException ex) { catch (WebSocketException ex) {
_logger.Fatal (ex.ToString ()); _logger.Fatal (ex.ToString ());
error ("An exception has occured."); error ("An exception has occured.");
close (ex.Code, ex.Message, true); close (ex.Code, ex.Message, false);
} }
catch (Exception ex) { catch (Exception ex) {
_logger.Fatal (ex.ToString ()); _logger.Fatal (ex.ToString ());
error ("An exception has occured."); error ("An exception has occured.");
close (CloseStatusCode.ABNORMAL, null, true); close (CloseStatusCode.ABNORMAL, null, false);
} }
}; };
@ -1263,27 +1239,39 @@ namespace WebSocketSharp
#region Internal Methods #region Internal Methods
// As server
internal void Close (byte [] data)
{
close (new PayloadData (data), false);
}
// As server // As server
internal void Close (HttpStatusCode code) internal void Close (HttpStatusCode code)
{ {
_readyState = WebSocketState.CLOSING; _readyState = WebSocketState.CLOSING;
close (code);
send (createHandshakeResponse (code));
closeServerResources ();
_readyState = WebSocketState.CLOSED;
} }
internal bool Ping (byte [] data) // As server
internal void Close (CloseEventArgs args, byte [] frameAsBytes, int waitTimeOut)
{ {
var frame = createControlFrame (Opcode.PING, new PayloadData (data), _client); lock (_forClose)
var timeOut = _client ? 5000 : 1000; {
if (_readyState == WebSocketState.CLOSING || _readyState == WebSocketState.CLOSED)
return;
return send (frame) _readyState = WebSocketState.CLOSING;
? _receivePong.WaitOne (timeOut) }
: false;
args.WasClean = close (frameAsBytes, waitTimeOut, closeServerResources);
_readyState = WebSocketState.CLOSED;
OnClose.Emit (this, args);
}
internal bool Ping (byte [] frameAsBytes, int timeOut)
{
return send (frameAsBytes) &&
_receivePong.WaitOne (timeOut);
} }
#endregion #endregion
@ -1295,7 +1283,7 @@ namespace WebSocketSharp
/// </summary> /// </summary>
public void Close () public void Close ()
{ {
close (new PayloadData (), false); close (new PayloadData (), _readyState == WebSocketState.OPEN, true);
} }
/// <summary> /// <summary>
@ -1320,7 +1308,8 @@ namespace WebSocketSharp
return; return;
} }
close (new PayloadData (code.ToByteArray (ByteOrder.BIG)), false); var send = _readyState == WebSocketState.OPEN && !code.IsReserved ();
close (new PayloadData (code.ToByteArray (ByteOrder.BIG)), send, true);
} }
/// <summary> /// <summary>
@ -1332,7 +1321,8 @@ namespace WebSocketSharp
/// </param> /// </param>
public void Close (CloseStatusCode code) public void Close (CloseStatusCode code)
{ {
close (new PayloadData (((ushort) code).ToByteArray (ByteOrder.BIG)), false); var send = _readyState == WebSocketState.OPEN && !code.IsReserved ();
close (new PayloadData (((ushort) code).ToByteArray (ByteOrder.BIG)), send, true);
} }
/// <summary> /// <summary>
@ -1364,7 +1354,8 @@ namespace WebSocketSharp
return; return;
} }
close (new PayloadData (data), false); var send = _readyState == WebSocketState.OPEN && !code.IsReserved ();
close (new PayloadData (data), send, true);
} }
/// <summary> /// <summary>
@ -1393,7 +1384,8 @@ namespace WebSocketSharp
return; return;
} }
close (new PayloadData (data), false); var send = _readyState == WebSocketState.OPEN && !code.IsReserved ();
close (new PayloadData (data), send, true);
} }
/// <summary> /// <summary>
@ -1417,7 +1409,10 @@ namespace WebSocketSharp
catch (Exception ex) { catch (Exception ex) {
_logger.Fatal (ex.ToString ()); _logger.Fatal (ex.ToString ());
error ("An exception has occured."); error ("An exception has occured.");
if (_client)
Close (CloseStatusCode.ABNORMAL); Close (CloseStatusCode.ABNORMAL);
else
Close (HttpStatusCode.BadRequest);
} }
} }
@ -1441,7 +1436,9 @@ namespace WebSocketSharp
/// </returns> /// </returns>
public bool Ping () public bool Ping ()
{ {
return Ping (new byte [] {}); return _client
? Ping (WsFrame.CreatePingFrame (Mask.MASK).ToByteArray (), 5000)
: Ping (WsFrame.CreatePingFrame (Mask.UNMASK).ToByteArray (), 1000);
} }
/// <summary> /// <summary>
@ -1457,7 +1454,7 @@ namespace WebSocketSharp
public bool Ping (string message) public bool Ping (string message)
{ {
if (message == null || message.Length == 0) if (message == null || message.Length == 0)
return Ping (new byte [] {}); return Ping ();
var data = Encoding.UTF8.GetBytes (message); var data = Encoding.UTF8.GetBytes (message);
var msg = data.CheckIfValidPingData (); var msg = data.CheckIfValidPingData ();
@ -1469,7 +1466,9 @@ namespace WebSocketSharp
return false; return false;
} }
return Ping (data); return _client
? Ping (WsFrame.CreatePingFrame (Mask.MASK, data).ToByteArray (), 5000)
: Ping (WsFrame.CreatePingFrame (Mask.UNMASK, data).ToByteArray (), 1000);
} }
/// <summary> /// <summary>

View File

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

View File

@ -98,24 +98,6 @@ namespace WebSocketSharp
#endregion #endregion
#region Private Methods
private bool write (byte [] data)
{
lock (_forWrite)
{
try {
_innerStream.Write (data, 0, data.Length);
return true;
}
catch {
return false;
}
}
}
#endregion
#region Internal Methods #region Internal Methods
internal static WsStream CreateClientStream ( internal static WsStream CreateClientStream (
@ -160,6 +142,20 @@ namespace WebSocketSharp
return new WsStream (conn.Stream, conn.IsSecure); return new WsStream (conn.Stream, conn.IsSecure);
} }
internal bool Write (byte [] data)
{
lock (_forWrite)
{
try {
_innerStream.Write (data, 0, data.Length);
return true;
}
catch {
return false;
}
}
}
#endregion #endregion
#region Public Methods #region Public Methods
@ -222,12 +218,12 @@ namespace WebSocketSharp
public bool WriteFrame (WsFrame frame) public bool WriteFrame (WsFrame frame)
{ {
return write (frame.ToByteArray ()); return Write (frame.ToByteArray ());
} }
public bool WriteHandshake (HandshakeBase handshake) public bool WriteHandshake (HandshakeBase handshake)
{ {
return write (handshake.ToByteArray ()); return Write (handshake.ToByteArray ());
} }
#endregion #endregion