Modified closing

This commit is contained in:
sta 2014-01-08 12:14:24 +09:00
parent 7147cde22c
commit e03f9eb631
2 changed files with 132 additions and 97 deletions

View File

@ -180,12 +180,10 @@ namespace WebSocketSharp
internal static byte [] Append (this ushort code, string reason) internal static byte [] Append (this ushort code, string reason)
{ {
using (var buffer = new MemoryStream ()) using (var buffer = new MemoryStream ()) {
{
var tmp = code.ToByteArrayInternally (ByteOrder.BIG); var tmp = code.ToByteArrayInternally (ByteOrder.BIG);
buffer.Write (tmp, 0, 2); buffer.Write (tmp, 0, 2);
if (reason != null && reason.Length > 0) if (reason != null && reason.Length > 0) {
{
tmp = Encoding.UTF8.GetBytes (reason); tmp = Encoding.UTF8.GetBytes (reason);
buffer.Write (tmp, 0, tmp.Length); buffer.Write (tmp, 0, tmp.Length);
} }
@ -195,6 +193,13 @@ namespace WebSocketSharp
} }
} }
internal static string CheckIfCanClose (this WebSocketState state)
{
return state == WebSocketState.CLOSING || state == WebSocketState.CLOSED
? "While closing the WebSocket connection, or already closed."
: null;
}
internal static string CheckIfCanRead (this Stream stream) internal static string CheckIfCanRead (this Stream stream)
{ {
return stream == null return stream == null
@ -1149,8 +1154,8 @@ namespace WebSocketSharp
} }
/// <summary> /// <summary>
/// Determines whether the specified <see cref="ushort"/> is in the allowable range of /// Determines whether the specified <see cref="ushort"/> is in the allowable
/// the WebSocket close status code. /// range of the WebSocket close status code.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Not allowable ranges are the followings. /// Not allowable ranges are the followings.
@ -1162,14 +1167,15 @@ namespace WebSocketSharp
/// </item> /// </item>
/// <item> /// <item>
/// <term> /// <term>
/// Numbers which are greater than 4999 are out of the reserved close status code ranges. /// Numbers which are greater than 4999 are out of the reserved close
/// status code ranges.
/// </term> /// </term>
/// </item> /// </item>
/// </list> /// </list>
/// </remarks> /// </remarks>
/// <returns> /// <returns>
/// <c>true</c> if <paramref name="value"/> is in the allowable range of the WebSocket close status code; /// <c>true</c> if <paramref name="value"/> is in the allowable range of the
/// otherwise, <c>false</c>. /// WebSocket close status code; otherwise, <c>false</c>.
/// </returns> /// </returns>
/// <param name="value"> /// <param name="value">
/// A <see cref="ushort"/> to test. /// A <see cref="ushort"/> to test.

View File

@ -557,34 +557,49 @@ namespace WebSocketSharp
private void close (CloseStatusCode code, string reason, bool wait) private void close (CloseStatusCode code, string reason, bool wait)
{ {
close (new PayloadData (((ushort) code).Append (reason)), !code.IsReserved (), wait); close (
new PayloadData (((ushort) code).Append (reason)),
!code.IsReserved (),
wait);
} }
private void close (PayloadData payload, bool send, bool wait) private void close (PayloadData payload, bool send, bool wait)
{ {
lock (_forClose) lock (_forClose) {
{ var msg = _readyState.CheckIfCanClose ();
if (_readyState == WebSocketState.CLOSING || _readyState == WebSocketState.CLOSED) if (msg != null) {
_logger.Info (String.Format ("{0}\nstate: {1}", msg, _readyState));
return; return;
}
_readyState = WebSocketState.CLOSING; _readyState = WebSocketState.CLOSING;
} }
_logger.Trace ("Start closing handshake."); _logger.Trace ("Start closing handshake.");
try {
var args = new CloseEventArgs (payload); var args = new CloseEventArgs (payload);
args.WasClean = _client args.WasClean =
_client
? close ( ? close (
send ? WsFrame.CreateCloseFrame (Mask.MASK, payload).ToByteArray () : null, send ? WsFrame.CreateCloseFrame (Mask.MASK, payload).ToByteArray ()
: null,
wait ? 5000 : 0, wait ? 5000 : 0,
closeClientResources) closeClientResources)
: close ( : close (
send ? WsFrame.CreateCloseFrame (Mask.UNMASK, payload).ToByteArray () : null, send ? WsFrame.CreateCloseFrame (Mask.UNMASK, payload).ToByteArray ()
: null,
wait ? 1000 : 0, wait ? 1000 : 0,
closeServerResources); closeServerResources);
_readyState = WebSocketState.CLOSED; _readyState = WebSocketState.CLOSED;
OnClose.Emit (this, args); OnClose.Emit (this, args);
}
catch (Exception ex) {
_logger.Fatal (ex.ToString ());
error ("An exception has occurred while closing.");
}
_logger.Trace ("End closing handshake."); _logger.Trace ("End closing handshake.");
} }
@ -592,11 +607,17 @@ namespace WebSocketSharp
private bool close (byte [] frameAsBytes, int timeOut, Func<bool> release) private bool close (byte [] frameAsBytes, int timeOut, Func<bool> release)
{ {
var sent = frameAsBytes != null && _stream.Write (frameAsBytes); var sent = frameAsBytes != null && _stream.Write (frameAsBytes);
var received = timeOut == 0 || (sent && _exitReceiving.WaitOne (timeOut)); var received = timeOut == 0 ||
(sent && _exitReceiving != null && _exitReceiving.WaitOne (timeOut));
var released = release (); var released = release ();
var result = sent && received && released; var result = sent && received && released;
_logger.Debug (String.Format ( _logger.Debug (
"Was clean?: {0}\nsent: {1} received: {2} released: {3}", result, sent, received, released)); String.Format (
"Was clean?: {0}\nsent: {1} received: {2} released: {3}",
result,
sent,
received,
released));
return result; return result;
} }
@ -605,14 +626,12 @@ namespace WebSocketSharp
private bool closeClientResources () private bool closeClientResources ()
{ {
try { try {
if (_stream != null) if (_stream != null) {
{
_stream.Dispose (); _stream.Dispose ();
_stream = null; _stream = null;
} }
if (_tcpClient != null) if (_tcpClient != null) {
{
_tcpClient.Close (); _tcpClient.Close ();
_tcpClient = null; _tcpClient = null;
} }
@ -621,11 +640,11 @@ namespace WebSocketSharp
} }
catch (Exception ex) { catch (Exception ex) {
_logger.Fatal (ex.ToString ()); _logger.Fatal (ex.ToString ());
error ("An exception has occurred."); error ("An exception has occurred while releasing resources.");
}
return false; return false;
} }
}
// As server // As server
private bool closeServerResources () private bool closeServerResources ()
@ -641,11 +660,11 @@ namespace WebSocketSharp
} }
catch (Exception ex) { catch (Exception ex) {
_logger.Fatal (ex.ToString ()); _logger.Fatal (ex.ToString ());
error ("An exception has occurred."); error ("An exception has occurred while releasing resources.");
}
return false; return false;
} }
}
private bool concatenateFragmentsInto (Stream dest) private bool concatenateFragmentsInto (Stream dest)
{ {
@ -886,21 +905,18 @@ namespace WebSocketSharp
{ {
var code = CloseStatusCode.ABNORMAL; var code = CloseStatusCode.ABNORMAL;
var msg = reason; var msg = reason;
if (exception.GetType () == typeof (WebSocketException)) if (exception is WebSocketException) {
{
var wsex = (WebSocketException) exception; var wsex = (WebSocketException) exception;
code = wsex.Code; code = wsex.Code;
reason = wsex.Message; reason = wsex.Message;
} }
if (code == CloseStatusCode.ABNORMAL || if (code == CloseStatusCode.ABNORMAL ||
code == CloseStatusCode.TLS_HANDSHAKE_FAILURE) code == CloseStatusCode.TLS_HANDSHAKE_FAILURE) {
{
_logger.Fatal (exception.ToString ()); _logger.Fatal (exception.ToString ());
reason = msg; reason = msg;
} }
else else {
{
_logger.Error (reason); _logger.Error (reason);
msg = null; msg = null;
} }
@ -1365,22 +1381,29 @@ namespace WebSocketSharp
} }
// As server // As server
internal void Close (CloseEventArgs args, byte [] frameAsBytes, int waitTimeOut) internal void Close (
CloseEventArgs args, byte [] frameAsBytes, int waitTimeOut)
{ {
lock (_forClose) lock (_forClose) {
{ var msg = _readyState.CheckIfCanClose ();
if (_readyState == WebSocketState.CLOSING || _readyState == WebSocketState.CLOSED) if (msg != null) {
_logger.Info (String.Format ("{0}\nstate: {1}", msg, _readyState));
return; return;
}
_readyState = WebSocketState.CLOSING; _readyState = WebSocketState.CLOSING;
} }
try {
args.WasClean = close (frameAsBytes, waitTimeOut, closeServerResources); args.WasClean = close (frameAsBytes, waitTimeOut, closeServerResources);
_readyState = WebSocketState.CLOSED; _readyState = WebSocketState.CLOSED;
OnClose.Emit (this, args); OnClose.Emit (this, args);
} }
catch (Exception ex) {
_logger.Fatal (ex.ToString ());
}
}
// As server // As server
internal void ConnectAsServer () internal void ConnectAsServer ()
@ -1480,11 +1503,20 @@ namespace WebSocketSharp
#region Public Methods #region Public Methods
/// <summary> /// <summary>
/// Closes the WebSocket connection and releases all associated resources. /// Closes the WebSocket connection, and releases all associated resources.
/// </summary> /// </summary>
public void Close () public void Close ()
{ {
close (new PayloadData (), _readyState == WebSocketState.OPEN, true); var msg = _readyState.CheckIfCanClose ();
if (msg != null) {
_logger.Error (String.Format ("{0}\nstate: {1}", msg, _readyState));
error (msg);
return;
}
var send = _readyState == WebSocketState.OPEN;
close (new PayloadData (), send, send);
} }
/// <summary> /// <summary>
@ -1492,25 +1524,15 @@ namespace WebSocketSharp
/// and releases all associated resources. /// and releases all associated resources.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This 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"/>
/// in the allowable range of the WebSocket close status code. /// isn't in the allowable range of the WebSocket close status code.
/// </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.
/// </param> /// </param>
public void Close (ushort code) public void Close (ushort code)
{ {
var msg = code.CheckIfValidCloseStatusCode (); Close (code, null);
if (msg != null)
{
_logger.Error (String.Format ("{0}\ncode: {1}", msg, code));
error (msg);
return;
}
var send = _readyState == WebSocketState.OPEN && !code.IsReserved ();
close (new PayloadData (code.ToByteArrayInternally (ByteOrder.BIG)), send, true);
} }
/// <summary> /// <summary>
@ -1518,75 +1540,81 @@ namespace WebSocketSharp
/// and 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 indicate the status codes 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)
{ {
var send = _readyState == WebSocketState.OPEN && !code.IsReserved (); Close (code, null);
close (new PayloadData (((ushort) code).ToByteArrayInternally (ByteOrder.BIG)), send, true);
} }
/// <summary> /// <summary>
/// Closes the WebSocket connection with the specified <see cref="ushort"/> and <see cref="string"/>, /// Closes the WebSocket connection with the specified <see cref="ushort"/>
/// and releases all associated resources. /// and <see cref="string"/>, and releases all associated resources.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This 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"/>
/// in the allowable range of the WebSocket close status code /// isn't in the allowable range of the WebSocket close status code or the
/// or the length of <paramref name="reason"/> is greater than 123 bytes. /// 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.
/// </param> /// </param>
/// <param name="reason"> /// <param name="reason">
/// A <see cref="string"/> that contains the reason for closure. /// A <see cref="string"/> that represents the reason for closure.
/// </param> /// </param>
public void Close (ushort code, string reason) public void Close (ushort code, string reason)
{ {
byte [] data = null; byte [] data = null;
var msg = code.CheckIfValidCloseStatusCode () ?? var msg = _readyState.CheckIfCanClose () ??
code.CheckIfValidCloseStatusCode () ??
(data = code.Append (reason)).CheckIfValidCloseData (); (data = code.Append (reason)).CheckIfValidCloseData ();
if (msg != null) if (msg != null) {
{ _logger.Error (
_logger.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); String.Format (
"{0}\nstate: {1} code: {2} reason: {3}", msg, _readyState, code, reason));
error (msg); error (msg);
return; return;
} }
var send = _readyState == WebSocketState.OPEN && !code.IsReserved (); var send = _readyState == WebSocketState.OPEN && !code.IsReserved ();
close (new PayloadData (data), send, true); close (new PayloadData (data), send, send);
} }
/// <summary> /// <summary>
/// Closes the WebSocket connection with the specified <see cref="CloseStatusCode"/> and /// Closes the WebSocket connection with the specified <see cref="CloseStatusCode"/>
/// <see cref="string"/>, and releases all associated resources. /// and <see cref="string"/>, and releases all associated resources.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This method emits a <see cref="OnError"/> event if the length of <paramref name="reason"/> /// This method emits a <see cref="OnError"/> event if the length of
/// is greater than 123 bytes. /// <paramref name="reason"/> is greater than 123 bytes.
/// </remarks> /// </remarks>
/// <param name="code"> /// <param name="code">
/// One of the <see cref="CloseStatusCode"/> values that indicate the status codes 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 represents the reason for closure.
/// </param> /// </param>
public void Close (CloseStatusCode code, string reason) public void Close (CloseStatusCode code, string reason)
{ {
var data = ((ushort) code).Append (reason); byte [] data = null;
var msg = data.CheckIfValidCloseData (); var msg = _readyState.CheckIfCanClose () ??
if (msg != null) (data = ((ushort) code).Append (reason)).CheckIfValidCloseData ();
{
_logger.Error (String.Format ("{0}\nreason: {1}", msg, reason)); if (msg != null) {
_logger.Error (
String.Format (
"{0}\nstate: {1} reason: {2}", msg, _readyState, reason));
error (msg); error (msg);
return; return;
} }
var send = _readyState == WebSocketState.OPEN && !code.IsReserved (); var send = _readyState == WebSocketState.OPEN && !code.IsReserved ();
close (new PayloadData (data), send, true); close (new PayloadData (data), send, send);
} }
/// <summary> /// <summary>
@ -1642,14 +1670,15 @@ namespace WebSocketSharp
} }
/// <summary> /// <summary>
/// Closes the WebSocket connection and releases all associated resources. /// Closes the WebSocket connection, and releases all associated resources.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This method closes the WebSocket connection with the <see cref="CloseStatusCode.AWAY"/>. /// This method closes the WebSocket connection with the
/// <see cref="CloseStatusCode.AWAY"/>.
/// </remarks> /// </remarks>
public void Dispose () public void Dispose ()
{ {
Close (CloseStatusCode.AWAY); Close (CloseStatusCode.AWAY, null);
} }
/// <summary> /// <summary>