Modified closing

This commit is contained in:
sta 2013-09-27 11:30:29 +09:00
parent c7a895f530
commit 133ff88b0d
2 changed files with 184 additions and 209 deletions

View File

@ -259,7 +259,7 @@ namespace WebSocketSharp
set { set {
if (IsOpened) if (IsOpened)
{ {
var msg = "The WebSocket connection has already been established."; var msg = "A WebSocket connection has already been established.";
_logger.Error (msg); _logger.Error (msg);
error (msg); error (msg);
@ -386,7 +386,7 @@ namespace WebSocketSharp
string msg = null; string msg = null;
if (IsOpened) if (IsOpened)
{ {
msg = "The WebSocket connection has already been established."; msg = "A WebSocket connection has already been established.";
} }
else if (value.IsNullOrEmpty ()) else if (value.IsNullOrEmpty ())
{ {
@ -531,49 +531,6 @@ namespace WebSocketSharp
return send (createHandshakeResponse ()); return send (createHandshakeResponse ());
} }
private void close (PayloadData data)
{
_logger.Debug ("Is this thread background?: " + Thread.CurrentThread.IsBackground);
var sent = false;
CloseEventArgs args = null;
lock (_forClose)
{
if (_readyState == WebSocketState.CLOSING || _readyState == WebSocketState.CLOSED)
return;
var current = _readyState;
_readyState = WebSocketState.CLOSING;
args = new CloseEventArgs (data);
if (current == WebSocketState.CONNECTING)
{
if (!_client)
{
close (HttpStatusCode.BadRequest);
return;
}
}
else
{
if (!data.ContainsReservedCloseStatusCode)
sent = send (createControlFrame (Opcode.CLOSE, data, _client));
}
}
var received = Thread.CurrentThread.IsBackground ||
(_exitReceiving != null && _exitReceiving.WaitOne (5 * 1000));
var released = closeResources ();
args.WasClean = sent && received && released;
_logger.Debug ("Was clean?: " + args.WasClean);
_readyState = WebSocketState.CLOSED;
OnClose.Emit (this, args);
_logger.Trace ("Exit close method.");
}
// As server // As server
private void close (HttpStatusCode code) private void close (HttpStatusCode code)
{ {
@ -587,19 +544,46 @@ namespace WebSocketSharp
_readyState = WebSocketState.CLOSED; _readyState = WebSocketState.CLOSED;
} }
private void close (ushort code, string reason) private void close (PayloadData data, bool received)
{ {
var data = code.Append (reason); var sent = false;
var msg = data.CheckIfValidCloseData (); CloseEventArgs args = null;
if (msg != null) lock (_forClose)
{ {
_logger.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); if (_readyState == WebSocketState.CLOSING || _readyState == WebSocketState.CLOSED)
error (msg); return;
_logger.Trace ("Start closing handshake.");
var current = _readyState;
_readyState = WebSocketState.CLOSING;
if (current == WebSocketState.CONNECTING && !_client)
{
close (HttpStatusCode.BadRequest);
return; return;
} }
close (new PayloadData (data)); args = new CloseEventArgs (data);
if (current == WebSocketState.OPEN && !data.ContainsReservedCloseStatusCode)
sent = send (createControlFrame (Opcode.CLOSE, data, _client));
}
received = received || (sent && _exitReceiving != null && _exitReceiving.WaitOne (5 * 1000));
var released = closeResources ();
args.WasClean = sent && received && released;
_logger.Debug (String.Format (
"Was clean?: {0}\nsent: {1} received: {2} released: {3}", args.WasClean, sent, received, released));
_readyState = WebSocketState.CLOSED;
_logger.Trace ("End closing handshake.");
OnClose.Emit (this, args);
}
private void close (CloseStatusCode code, string reason, bool received)
{
var data = ((ushort) code).Append (reason);
close (new PayloadData (data), received);
} }
// As client // As client
@ -629,7 +613,7 @@ namespace WebSocketSharp
return true; return true;
} }
catch (Exception ex) { catch (Exception ex) {
_logger.Fatal (ex.Message); _logger.Fatal (ex.ToString ());
error ("An exception has occured."); error ("An exception has occured.");
return false; return false;
@ -648,49 +632,49 @@ namespace WebSocketSharp
private bool concatenateFragments (Stream dest) private bool concatenateFragments (Stream dest)
{ {
Func<WsFrame, bool> processContinuation = contFrame =>
{
if (!contFrame.IsContinuation)
return false;
dest.WriteBytes (contFrame.PayloadData.ApplicationData);
return true;
};
while (true) while (true)
{ {
var frame = _stream.ReadFrame (); var frame = _stream.ReadFrame ();
if (processAbnormal (frame)) if (frame == null)
return false; return processAbnormalFrame ();
if (frame.IsCompressed)
return processIncorrectFrame ();
if (!frame.IsFinal)
{
// MORE & CONT // MORE & CONT
if (processContinuation (frame)) if (!frame.IsFinal && frame.IsContinuation)
{
dest.WriteBytes (frame.PayloadData.ApplicationData);
continue; continue;
} }
else
{
// FINAL & CONT // FINAL & CONT
if (processContinuation (frame)) if (frame.IsFinal && frame.IsContinuation)
{
dest.WriteBytes (frame.PayloadData.ApplicationData);
break; break;
}
// FINAL & PING // FINAL & PING
if (processPing (frame)) if (frame.IsFinal && frame.IsPing)
{
processPingFrame (frame);
continue; continue;
// FINAL & PONG
if (processPong (frame))
continue;
// FINAL & CLOSE
if (processClose (frame))
return false;
} }
// FINAL & PONG
if (frame.IsFinal && frame.IsPong)
{
processPongFrame ();
continue;
}
// FINAL & CLOSE
if (frame.IsFinal && frame.IsClose)
return processCloseFrame (frame);
// ? // ?
processIncorrectFrame (); return processIncorrectFrame ();
return false;
} }
return true; return true;
@ -828,7 +812,7 @@ namespace WebSocketSharp
{ {
_logger.Error (msg); _logger.Error (msg);
error (msg); error (msg);
Close (CloseStatusCode.ABNORMAL, msg); Close (CloseStatusCode.ABNORMAL);
return false; return false;
} }
@ -873,37 +857,22 @@ namespace WebSocketSharp
send (frame); send (frame);
} }
private bool processAbnormal (WsFrame frame) private bool processAbnormalFrame ()
{ {
if (frame != null)
return false;
_logger.Trace ("Start closing handshake.");
var code = CloseStatusCode.ABNORMAL; var code = CloseStatusCode.ABNORMAL;
Close (code, code.GetMessage ()); close (code, code.GetMessage (), true);
return true; return false;
} }
private bool processClose (WsFrame frame) private bool processCloseFrame (WsFrame frame)
{ {
if (!frame.IsClose) close (frame.PayloadData, true);
return false; return false;
_logger.Trace ("Start closing handshake.");
close (frame.PayloadData);
return true;
} }
private bool processData (WsFrame frame) private bool processDataFrame (WsFrame frame)
{ {
if (!frame.IsData)
return false;
if (frame.IsCompressed && _compression == CompressionMethod.NONE)
return false;
var args = frame.IsCompressed var args = frame.IsCompressed
? new MessageEventArgs ( ? new MessageEventArgs (
frame.Opcode, frame.PayloadData.ApplicationData.Decompress (_compression)) frame.Opcode, frame.PayloadData.ApplicationData.Decompress (_compression))
@ -913,34 +882,22 @@ namespace WebSocketSharp
return true; return true;
} }
private bool processFragmented (WsFrame frame) private bool processFragmentedFrame (WsFrame frame)
{ {
// Not first fragment return frame.IsContinuation // Not first fragment
if (frame.IsContinuation) ? true
return true; : frame.IsData
? processFragments (frame)
// Not fragmented : processIncorrectFrame ();
if (frame.IsFinal)
return false;
bool incorrect = !frame.IsData ||
(frame.IsCompressed && _compression == CompressionMethod.NONE);
if (!incorrect)
processFragments (frame);
else
processIncorrectFrame ();
return true;
} }
private void processFragments (WsFrame first) private bool processFragments (WsFrame first)
{ {
using (var concatenated = new MemoryStream ()) using (var concatenated = new MemoryStream ())
{ {
concatenated.WriteBytes (first.PayloadData.ApplicationData); concatenated.WriteBytes (first.PayloadData.ApplicationData);
if (!concatenateFragments (concatenated)) if (!concatenateFragments (concatenated))
return; return false;
byte [] data; byte [] data;
if (_compression != CompressionMethod.NONE) if (_compression != CompressionMethod.NONE)
@ -954,44 +911,46 @@ namespace WebSocketSharp
} }
OnMessage.Emit (this, new MessageEventArgs (first.Opcode, data)); OnMessage.Emit (this, new MessageEventArgs (first.Opcode, data));
return true;
} }
} }
private void processFrame (WsFrame frame) private bool processFrame (WsFrame frame)
{ {
bool processed = processAbnormal (frame) || return frame == null
processFragmented (frame) || ? processAbnormalFrame ()
processData (frame) || : (!frame.IsData && frame.IsCompressed) ||
processPing (frame) || (frame.IsCompressed && _compression == CompressionMethod.NONE)
processPong (frame) || ? processIncorrectFrame ()
processClose (frame); : frame.IsFragmented
? processFragmentedFrame (frame)
if (!processed) : frame.IsData
processIncorrectFrame (); ? processDataFrame (frame)
: frame.IsPing
? processPingFrame (frame)
: frame.IsPong
? processPongFrame ()
: frame.IsClose
? processCloseFrame (frame)
: processIncorrectFrame ();
} }
private void processIncorrectFrame () private bool processIncorrectFrame ()
{ {
_logger.Trace ("Start closing handshake."); close (CloseStatusCode.INCORRECT_DATA, null, true);
Close (CloseStatusCode.INCORRECT_DATA);
}
private bool processPing (WsFrame frame)
{
if (!frame.IsPing)
return false; return false;
}
private bool processPingFrame (WsFrame frame)
{
_logger.Trace ("Return Pong."); _logger.Trace ("Return Pong.");
pong (frame.PayloadData); pong (frame.PayloadData);
return true; return true;
} }
private bool processPong (WsFrame frame) private bool processPongFrame ()
{ {
if (!frame.IsPong)
return false;
_logger.Trace ("Receive Pong."); _logger.Trace ("Receive Pong.");
_receivePong.Set (); _receivePong.Set ();
@ -1072,17 +1031,13 @@ namespace WebSocketSharp
{ {
lock (_forFrame) lock (_forFrame)
{ {
var ready = _stream == null var ready = _stream != null &&
? false (_readyState == WebSocketState.OPEN ||
: _readyState == WebSocketState.OPEN (_readyState == WebSocketState.CLOSING && frame.IsClose));
? true
: _readyState == WebSocketState.CLOSING
? frame.IsClose
: false;
if (!ready) if (!ready)
{ {
var msg = "The 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);
error (msg); error (msg);
@ -1100,7 +1055,7 @@ namespace WebSocketSharp
try { try {
if (_readyState != WebSocketState.OPEN) if (_readyState != WebSocketState.OPEN)
{ {
var msg = "The 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);
error (msg); error (msg);
@ -1123,7 +1078,7 @@ namespace WebSocketSharp
} }
} }
catch (Exception ex) { catch (Exception ex) {
_logger.Fatal (ex.Message); _logger.Fatal (ex.ToString ());
error ("An exception has occured."); error ("An exception has occured.");
} }
finally { finally {
@ -1152,7 +1107,7 @@ namespace WebSocketSharp
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.Fatal (ex.Message); _logger.Fatal (ex.ToString ());
error ("An exception has occured."); error ("An exception has occured.");
} }
}; };
@ -1238,19 +1193,20 @@ namespace WebSocketSharp
completed = frame => completed = frame =>
{ {
try { try {
processFrame (frame); if (processFrame (frame))
if (_readyState == WebSocketState.OPEN)
_stream.ReadFrameAsync (completed); _stream.ReadFrameAsync (completed);
else else
_exitReceiving.Set (); _exitReceiving.Set ();
} }
catch (WebSocketException ex) { catch (WebSocketException ex) {
_logger.Fatal (ex.Message); _logger.Fatal (ex.ToString ());
Close (ex.Code, ex.Message); error ("An exception has occured.");
close (ex.Code, ex.Message, true);
} }
catch (Exception ex) { catch (Exception ex) {
_logger.Fatal (ex.Message); _logger.Fatal (ex.ToString ());
Close (CloseStatusCode.ABNORMAL, "An exception has occured."); error ("An exception has occured.");
close (CloseStatusCode.ABNORMAL, null, true);
} }
}; };
@ -1310,7 +1266,7 @@ namespace WebSocketSharp
// As server // As server
internal void Close (byte [] data) internal void Close (byte [] data)
{ {
close (new PayloadData (data)); close (new PayloadData (data), false);
} }
// As server // As server
@ -1339,15 +1295,15 @@ namespace WebSocketSharp
/// </summary> /// </summary>
public void Close () public void Close ()
{ {
close (new PayloadData ()); close (new PayloadData (), false);
} }
/// <summary> /// <summary>
/// Closes the WebSocket connection with the specified <paramref name="code"/> and /// Closes the WebSocket connection with the specified <see cref="ushort"/>,
/// releases all associated resources. /// and releases all associated resources.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This Close method emits a <see cref="OnError"/> event if <paramref name="code"/> is not /// This method emits a <see cref="OnError"/> event if <paramref name="code"/> is not
/// in the allowable range of the WebSocket close status code. /// in the allowable range of the WebSocket close status code.
/// </remarks> /// </remarks>
/// <param name="code"> /// <param name="code">
@ -1364,28 +1320,29 @@ namespace WebSocketSharp
return; return;
} }
close (new PayloadData (code.ToByteArray (ByteOrder.BIG))); close (new PayloadData (code.ToByteArray (ByteOrder.BIG)), false);
} }
/// <summary> /// <summary>
/// Closes the WebSocket connection with the specified <paramref name="code"/> and /// Closes the WebSocket connection with the specified <see cref="CloseStatusCode"/>,
/// releases all associated resources. /// and releases all associated resources.
/// </summary> /// </summary>
/// <param name="code"> /// <param name="code">
/// One of the <see cref="CloseStatusCode"/> values that indicates the status code for closure. /// One of the <see cref="CloseStatusCode"/> values that indicate the status codes for closure.
/// </param> /// </param>
public void Close (CloseStatusCode code) public void Close (CloseStatusCode code)
{ {
close (new PayloadData (((ushort) code).ToByteArray (ByteOrder.BIG))); close (new PayloadData (((ushort) code).ToByteArray (ByteOrder.BIG)), false);
} }
/// <summary> /// <summary>
/// Closes the WebSocket connection with the specified <paramref name="code"/> and /// Closes the WebSocket connection with the specified <see cref="ushort"/> and <see cref="string"/>,
/// <paramref name="reason"/>, and releases all associated resources. /// and releases all associated resources.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This Close method emits a <see cref="OnError"/> event if <paramref name="code"/> is not /// This method emits a <see cref="OnError"/> event if <paramref name="code"/> is not
/// in the allowable range of the WebSocket close status code. /// in the allowable range of the WebSocket close status code
/// or the length of <paramref name="reason"/> is greater than 123 bytes.
/// </remarks> /// </remarks>
/// <param name="code"> /// <param name="code">
/// A <see cref="ushort"/> that indicates the status code for closure. /// A <see cref="ushort"/> that indicates the status code for closure.
@ -1395,31 +1352,48 @@ namespace WebSocketSharp
/// </param> /// </param>
public void Close (ushort code, string reason) public void Close (ushort code, string reason)
{ {
var msg = code.CheckIfValidCloseStatusCode (); byte [] data = null;
var msg = code.CheckIfValidCloseStatusCode () ??
(data = code.Append (reason)).CheckIfValidCloseData ();
if (msg != null) if (msg != null)
{ {
_logger.Error (String.Format ("{0}\ncode: {1}", msg, code)); _logger.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason));
error (msg); error (msg);
return; return;
} }
close (code, reason); close (new PayloadData (data), false);
} }
/// <summary> /// <summary>
/// Closes the WebSocket connection with the specified <paramref name="code"/> and /// Closes the WebSocket connection with the specified <see cref="CloseStatusCode"/> and
/// <paramref name="reason"/>, and releases all associated resources. /// <see cref="string"/>, and releases all associated resources.
/// </summary> /// </summary>
/// <remarks>
/// This method emits a <see cref="OnError"/> event if the length of <paramref name="reason"/>
/// is greater than 123 bytes.
/// </remarks>
/// <param name="code"> /// <param name="code">
/// One of the <see cref="CloseStatusCode"/> values that indicates the status code for closure. /// One of the <see cref="CloseStatusCode"/> values that indicate the status codes for closure.
/// </param> /// </param>
/// <param name="reason"> /// <param name="reason">
/// A <see cref="string"/> that contains the reason for closure. /// A <see cref="string"/> that contains the reason for closure.
/// </param> /// </param>
public void Close (CloseStatusCode code, string reason) public void Close (CloseStatusCode code, string reason)
{ {
close ((ushort) code, reason); var data = ((ushort) code).Append (reason);
var msg = data.CheckIfValidCloseData ();
if (msg != null)
{
_logger.Error (String.Format ("{0}\nreason: {1}", msg, reason));
error (msg);
return;
}
close (new PayloadData (data), false);
} }
/// <summary> /// <summary>
@ -1429,7 +1403,7 @@ namespace WebSocketSharp
{ {
if (IsOpened) if (IsOpened)
{ {
var msg = "The WebSocket connection has already been established."; var msg = "A WebSocket connection has already been established.";
_logger.Error (msg); _logger.Error (msg);
error (msg); error (msg);
@ -1441,10 +1415,9 @@ namespace WebSocketSharp
open (); open ();
} }
catch (Exception ex) { catch (Exception ex) {
_logger.Fatal (ex.Message); _logger.Fatal (ex.ToString ());
var msg = "An exception has occured."; error ("An exception has occured.");
error (msg); Close (CloseStatusCode.ABNORMAL);
Close (CloseStatusCode.ABNORMAL, msg);
} }
} }
@ -1644,7 +1617,7 @@ namespace WebSocketSharp
public void SetCookie (Cookie cookie) public void SetCookie (Cookie cookie)
{ {
var msg = IsOpened var msg = IsOpened
? "The WebSocket connection has already been established." ? "A WebSocket connection has already been established."
: cookie == null : cookie == null
? "'cookie' must not be null." ? "'cookie' must not be null."
: null; : null;
@ -1681,7 +1654,7 @@ namespace WebSocketSharp
string msg = null; string msg = null;
if (IsOpened) if (IsOpened)
{ {
msg = "The WebSocket connection has already been established."; msg = "A WebSocket connection has already been established.";
} }
else if (userName == null) else if (userName == null)
{ {

View File

@ -28,32 +28,33 @@
using System; using System;
namespace WebSocketSharp { namespace WebSocketSharp
{
/// <summary> /// <summary>
/// Represents the exception that occurred when attempting to perform an operation on the WebSocket connection. /// Represents the exception that occurred when attempting to perform an operation on
/// the WebSocket connection.
/// </summary> /// </summary>
public class WebSocketException : Exception public class WebSocketException : Exception
{ {
#region Internal Constructors #region Internal Constructors
internal WebSocketException() internal WebSocketException ()
: this(CloseStatusCode.ABNORMAL) : this (CloseStatusCode.ABNORMAL)
{ {
} }
internal WebSocketException(CloseStatusCode code) internal WebSocketException (CloseStatusCode code)
: this(code, code.GetMessage()) : this (code, code.GetMessage ())
{ {
} }
internal WebSocketException(string message) internal WebSocketException (string message)
: this(CloseStatusCode.NO_STATUS_CODE, message) : this (CloseStatusCode.NO_STATUS_CODE, message)
{ {
} }
internal WebSocketException(CloseStatusCode code, string message) internal WebSocketException (CloseStatusCode code, string message)
: base(message) : base (message)
{ {
Code = code; Code = code;
} }
@ -63,10 +64,11 @@ namespace WebSocketSharp {
#region Public Properties #region Public Properties
/// <summary> /// <summary>
/// Gets the <see cref="CloseStatusCode"/> associated with a <see cref="WebSocketException"/>. /// Gets the <see cref="CloseStatusCode"/> associated with the <see cref="WebSocketException"/>.
/// </summary> /// </summary>
/// <value> /// <value>
/// One of the <see cref="CloseStatusCode"/> values that indicates the cause of the exception. /// One of the <see cref="CloseStatusCode"/> values that indicate the causes of
/// the <see cref="WebSocketException"/>.
/// </value> /// </value>
public CloseStatusCode Code { public CloseStatusCode Code {
get; private set; get; private set;