Modified closing handshake

This commit is contained in:
sta 2013-08-03 13:44:45 +09:00
parent 4375a5c2a3
commit 68d1a7b9ba
2 changed files with 134 additions and 178 deletions

View File

@ -29,15 +29,15 @@
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.OnClose"/> event. /// Contains the event data associated with a <see cref="WebSocket.OnClose"/> event.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The <see cref="WebSocket.OnClose"/> event occurs when the WebSocket receives a close control frame or /// A <see cref="WebSocket.OnClose"/> event occurs when the WebSocket connection has been closed.
/// the <c>WebSocket.Close</c> method is called. If you want to get the reason for closure, you should access /// If you want to get the reason for closure, you use the <see cref="CloseEventArgs.Code"/> or
/// the <see cref="CloseEventArgs.Code"/> or <see cref="CloseEventArgs.Reason"/> properties. /// <see cref="CloseEventArgs.Reason"/> property.
/// </remarks> /// </remarks>
public class CloseEventArgs : MessageEventArgs public class CloseEventArgs : MessageEventArgs
{ {
@ -56,7 +56,7 @@ namespace WebSocketSharp {
{ {
_code = getCodeFrom (data); _code = getCodeFrom (data);
_reason = getReasonFrom (data); _reason = getReasonFrom (data);
_clean = false; _clean = true;
} }
#endregion #endregion
@ -67,7 +67,7 @@ namespace WebSocketSharp {
/// Gets the status code for closure. /// Gets the status code for closure.
/// </summary> /// </summary>
/// <value> /// <value>
/// A <see cref="ushort"/> that contains a status code for closure. /// A <see cref="ushort"/> that contains a status code for closure if any.
/// </value> /// </value>
public ushort Code { public ushort Code {
get { get {
@ -79,7 +79,7 @@ namespace WebSocketSharp {
/// Gets the reason for closure. /// Gets the reason for closure.
/// </summary> /// </summary>
/// <value> /// <value>
/// A <see cref="string"/> that contains a reason for closure. /// A <see cref="string"/> that contains the reason for closure if any.
/// </value> /// </value>
public string Reason { public string Reason {
get { get {
@ -88,10 +88,10 @@ namespace WebSocketSharp {
} }
/// <summary> /// <summary>
/// Indicates whether the WebSocket connection closed cleanly. /// Indicates whether the WebSocket connection has been closed cleanly.
/// </summary> /// </summary>
/// <value> /// <value>
/// <c>true</c> if the WebSocket connection closed cleanly; otherwise, <c>false</c>. /// <c>true</c> if the WebSocket connection has been closed cleanly; otherwise, <c>false</c>.
/// </value> /// </value>
public bool WasClean { public bool WasClean {
get { get {
@ -122,8 +122,8 @@ namespace WebSocketSharp {
if (appDataLen <= 2) if (appDataLen <= 2)
return String.Empty; return String.Empty;
var buffer = appData.SubArray(2, appDataLen - 2); var reason = appData.SubArray (2, appDataLen - 2);
return Encoding.UTF8.GetString(buffer); return Encoding.UTF8.GetString (reason);
} }
#endregion #endregion

View File

@ -473,7 +473,7 @@ namespace WebSocketSharp {
#region Public Events #region Public Events
/// <summary> /// <summary>
/// Occurs when the <see cref="WebSocket"/> receives a Close frame or the Close method is called. /// Occurs when the WebSocket connection has been closed.
/// </summary> /// </summary>
public event EventHandler<CloseEventArgs> OnClose; public event EventHandler<CloseEventArgs> OnClose;
@ -499,65 +499,52 @@ namespace WebSocketSharp {
// As server // As server
private bool acceptHandshake () private bool acceptHandshake ()
{ {
if (!processRequestHandshake()) return processRequestHandshake ()
return false; ? send (createResponseHandshake ())
: false;
sendResponseHandshake();
return true;
} }
private void close (CloseEventArgs eventArgs) private void close (CloseEventArgs eventArgs)
{ {
if (!Thread.CurrentThread.IsBackground && _exitReceiving != null) if (!Thread.CurrentThread.IsBackground && _exitReceiving != null)
_exitReceiving.WaitOne(5 * 1000); if (!_exitReceiving.WaitOne (5 * 1000))
eventArgs.WasClean = false;
if (!closeResources ()) if (!closeResources ())
eventArgs.WasClean = false; eventArgs.WasClean = false;
_readyState = WsState.CLOSED;
OnClose.Emit (this, eventArgs); OnClose.Emit (this, eventArgs);
} }
private void close (PayloadData data) private void close (PayloadData data)
{ {
_logger.Debug ("Is this thread background?: " + Thread.CurrentThread.IsBackground); _logger.Debug ("Is this thread background?: " + Thread.CurrentThread.IsBackground);
CloseEventArgs args = null;
lock (_forClose) lock (_forClose)
{ {
// Whether the closing handshake has been started already?
if (_readyState == WsState.CLOSING || _readyState == WsState.CLOSED) if (_readyState == WsState.CLOSING || _readyState == WsState.CLOSED)
return; return;
// Whether the closing handshake on server is started before the connection has been established? var state = _readyState;
if (_readyState == WsState.CONNECTING && !_client)
{
sendResponseHandshake(HttpStatusCode.BadRequest);
close(new CloseEventArgs(data));
return;
}
_readyState = WsState.CLOSING; _readyState = WsState.CLOSING;
} args = new CloseEventArgs (data);
if (state == WsState.CONNECTING)
// Whether a payload data contains the close status code which must not be set for send?
if (data.ContainsReservedCloseStatusCode)
{ {
close(new CloseEventArgs(data)); if (!_client)
return; args.WasClean = send (createResponseHandshake (HttpStatusCode.BadRequest));
}
else
{
if (!data.ContainsReservedCloseStatusCode)
args.WasClean = send (createControlFrame (Opcode.CLOSE, data, _client));
}
} }
closeHandshake(data); close (args);
_logger.Trace ("Exit close method."); _logger.Trace ("Exit close method.");
} }
private void close(HttpStatusCode code)
{
if (_readyState != WsState.CONNECTING || _client)
return;
sendResponseHandshake(code);
closeResources();
}
private void close (ushort code, string reason) private void close (ushort code, string reason)
{ {
var data = code.Append (reason); var data = code.Append (reason);
@ -573,40 +560,8 @@ namespace WebSocketSharp {
close (new PayloadData (data)); close (new PayloadData (data));
} }
private void closeHandshake(PayloadData data)
{
var args = new CloseEventArgs(data);
var frame = createControlFrame(Opcode.CLOSE, data, _client);
if (send(frame))
args.WasClean = true;
close(args);
}
private bool closeResources()
{
_readyState = WsState.CLOSED;
try
{
if (_client)
closeResourcesAsClient();
else
closeResourcesAsServer();
return true;
}
catch (Exception ex)
{
_logger.Fatal(ex.Message);
error("An exception has occured.");
return false;
}
}
// As client // As client
private void closeResourcesAsClient() private void closeClientResources ()
{ {
if (_stream != null) if (_stream != null)
{ {
@ -621,8 +576,26 @@ namespace WebSocketSharp {
} }
} }
private bool closeResources ()
{
try {
if (_client)
closeClientResources ();
else
closeServerResources ();
return true;
}
catch (Exception ex) {
_logger.Fatal (ex.Message);
error ("An exception has occured.");
return false;
}
}
// As server // As server
private void closeResourcesAsServer() private void closeServerResources ()
{ {
if (_context != null && _closeContext != null) if (_context != null && _closeContext != null)
{ {
@ -1089,25 +1062,22 @@ namespace WebSocketSharp {
var msg = "Invalid WebSocket connection request."; var msg = "Invalid WebSocket connection request.";
_logger.Error (msg); _logger.Error (msg);
error (msg); error (msg);
close(HttpStatusCode.BadRequest); Close (HttpStatusCode.BadRequest);
return false; return false;
} }
_base64key = _context.SecWebSocketKey; _base64key = _context.SecWebSocketKey;
processRequestProtocols(_context.Headers["Sec-WebSocket-Protocol"]);
var protocols = _context.Headers ["Sec-WebSocket-Protocol"];
if (!protocols.IsNullOrEmpty ())
_protocols = protocols;
processRequestExtensions (_context.Headers ["Sec-WebSocket-Extensions"]); processRequestExtensions (_context.Headers ["Sec-WebSocket-Extensions"]);
return true; return true;
} }
// As server
private void processRequestProtocols(string protocols)
{
if (!protocols.IsNullOrEmpty())
_protocols = protocols;
}
// As client // As client
private void processResponseCookies(CookieCollection cookies) private void processResponseCookies(CookieCollection cookies)
{ {
@ -1190,10 +1160,10 @@ namespace WebSocketSharp {
} }
// As server // As server
private void send(ResponseHandshake response) private bool send (ResponseHandshake response)
{ {
_logger.Debug ("Response handshake to client:\n" + response.ToString ()); _logger.Debug ("Response handshake to client:\n" + response.ToString ());
_stream.WriteHandshake(response); return _stream.WriteHandshake (response);
} }
private bool send (WsFrame frame) private bool send (WsFrame frame)
@ -1352,20 +1322,6 @@ namespace WebSocketSharp {
return receiveResponseHandshake (); return receiveResponseHandshake ();
} }
// As server
private void sendResponseHandshake()
{
var res = createResponseHandshake();
send(res);
}
// As server
private void sendResponseHandshake(HttpStatusCode code)
{
var res = createResponseHandshake(code);
send(res);
}
// As client // As client
private void setClientStream() private void setClientStream()
{ {
@ -1381,23 +1337,20 @@ namespace WebSocketSharp {
_receivePong = new AutoResetEvent (false); _receivePong = new AutoResetEvent (false);
Action<WsFrame> completed = null; Action<WsFrame> completed = null;
completed = (frame) => completed = frame =>
{
try
{ {
try {
processFrame (frame); processFrame (frame);
if (_readyState == WsState.OPEN) if (_readyState == WsState.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.Message);
Close (ex.Code, ex.Message); Close (ex.Code, ex.Message);
} }
catch (Exception ex) catch (Exception ex) {
{
_logger.Fatal (ex.Message); _logger.Fatal (ex.Message);
Close (CloseStatusCode.ABNORMAL, "An exception has occured."); Close (CloseStatusCode.ABNORMAL, "An exception has occured.");
} }
@ -1413,7 +1366,10 @@ namespace WebSocketSharp {
// As server // As server
internal void Close (HttpStatusCode code) internal void Close (HttpStatusCode code)
{ {
close(code); _readyState = WsState.CLOSING;
send (createResponseHandshake (code));
closeResources ();
_readyState = WsState.CLOSED;
} }
#endregion #endregion