Refactored WebSocket.cs

This commit is contained in:
sta 2014-07-22 15:36:59 +09:00
parent cff0253b9d
commit 96f4cef21f

View File

@ -492,95 +492,6 @@ namespace WebSocketSharp
#region Private Methods #region Private Methods
private bool acceptCloseFrame (WebSocketFrame frame)
{
var payload = frame.PayloadData;
close (payload, !payload.ContainsReservedCloseStatusCode, false);
return false;
}
private bool acceptDataFrame (WebSocketFrame frame)
{
var e = frame.IsCompressed
? new MessageEventArgs (
frame.Opcode, frame.PayloadData.ApplicationData.Decompress (_compression))
: new MessageEventArgs (frame.Opcode, frame.PayloadData);
enqueueToMessageEventQueue (e);
return true;
}
private void acceptException (Exception exception, string message)
{
var code = CloseStatusCode.Abnormal;
var reason = message;
if (exception is WebSocketException) {
var wsex = (WebSocketException) exception;
code = wsex.Code;
reason = wsex.Message;
}
if (code == CloseStatusCode.Abnormal || code == CloseStatusCode.TlsHandshakeFailure)
_logger.Fatal (exception.ToString ());
else
_logger.Error (reason);
error (message ?? code.GetMessage ());
if (_readyState == WebSocketState.Connecting && !_client)
Close (HttpStatusCode.BadRequest);
else
close (code, reason ?? code.GetMessage (), false);
}
private bool acceptFragmentedFrame (WebSocketFrame frame)
{
return frame.IsContinuation // Not first fragment
? true
: acceptFragments (frame);
}
private bool acceptFragments (WebSocketFrame first)
{
using (var concatenated = new MemoryStream ()) {
concatenated.WriteBytes (first.PayloadData.ApplicationData);
if (!concatenateFragmentsInto (concatenated))
return false;
byte[] data;
if (_compression != CompressionMethod.None) {
data = concatenated.DecompressToArray (_compression);
}
else {
concatenated.Close ();
data = concatenated.ToArray ();
}
enqueueToMessageEventQueue (new MessageEventArgs (first.Opcode, data));
return true;
}
}
private bool acceptFrame (WebSocketFrame frame)
{
return frame.IsCompressed && _compression == CompressionMethod.None
? acceptUnsupportedFrame (
frame,
CloseStatusCode.IncorrectData,
"A compressed data has been received without available decompression method.")
: frame.IsFragmented
? acceptFragmentedFrame (frame)
: frame.IsData
? acceptDataFrame (frame)
: frame.IsPing
? acceptPingFrame (frame)
: frame.IsPong
? acceptPongFrame (frame)
: frame.IsClose
? acceptCloseFrame (frame)
: acceptUnsupportedFrame (frame, CloseStatusCode.PolicyViolation, null);
}
// As server // As server
private bool acceptHandshake () private bool acceptHandshake ()
{ {
@ -603,64 +514,11 @@ namespace WebSocketSharp
var extensions = _context.Headers["Sec-WebSocket-Extensions"]; var extensions = _context.Headers["Sec-WebSocket-Extensions"];
if (extensions != null && extensions.Length > 0) if (extensions != null && extensions.Length > 0)
acceptSecWebSocketExtensionsHeader (extensions); processSecWebSocketExtensionsHeader (extensions);
return sendHandshakeResponse (createHandshakeResponse ()); return sendHandshakeResponse (createHandshakeResponse ());
} }
private bool acceptPingFrame (WebSocketFrame frame)
{
var mask = _client ? Mask.Mask : Mask.Unmask;
if (send (WebSocketFrame.CreatePongFrame (mask, frame.PayloadData).ToByteArray ()))
_logger.Trace ("Returned a Pong.");
return true;
}
private bool acceptPongFrame (WebSocketFrame frame)
{
_receivePong.Set ();
_logger.Trace ("Received a Pong.");
return true;
}
// As server
private void acceptSecWebSocketExtensionsHeader (string value)
{
var extensions = new StringBuilder (32);
var compress = false;
foreach (var extension in value.SplitHeaderValue (',')) {
var trimed = extension.Trim ();
var unprefixed = trimed.RemovePrefix ("x-webkit-");
if (!compress && unprefixed.IsCompressionExtension ()) {
var method = unprefixed.ToCompressionMethod ();
if (method != CompressionMethod.None) {
_compression = method;
compress = true;
extensions.Append (trimed + ", ");
}
}
}
var len = extensions.Length;
if (len > 0) {
extensions.Length = len - 2;
_extensions = extensions.ToString ();
}
}
private bool acceptUnsupportedFrame (WebSocketFrame frame, CloseStatusCode code, string reason)
{
_logger.Debug ("Unsupported frame:\n" + frame.PrintToString (false));
acceptException (new WebSocketException (code, reason), null);
return false;
}
private string checkIfAvailable ( private string checkIfAvailable (
string operation, bool availableAsServer, bool availableAsConnected) string operation, bool availableAsServer, bool availableAsConnected)
{ {
@ -698,8 +556,7 @@ namespace WebSocketSharp
{ {
var headers = response.Headers; var headers = response.Headers;
return response.IsUnauthorized return response.IsUnauthorized
? String.Format ( ? "HTTP authentication is required."
"HTTP {0} authentication is required.", response.AuthenticationChallenge.Scheme)
: !response.IsWebSocketResponse : !response.IsWebSocketResponse
? "Not WebSocket connection response." ? "Not WebSocket connection response."
: !validateSecWebSocketAcceptHeader (headers["Sec-WebSocket-Accept"]) : !validateSecWebSocketAcceptHeader (headers["Sec-WebSocket-Accept"])
@ -816,9 +673,8 @@ namespace WebSocketSharp
{ {
while (true) { while (true) {
var frame = _stream.ReadWebSocketFrame (); var frame = _stream.ReadWebSocketFrame ();
if (frame.IsFinal) { if (frame.IsFinal) {
// FINAL /* FINAL */
// CONT // CONT
if (frame.IsContinuation) { if (frame.IsContinuation) {
@ -828,22 +684,22 @@ namespace WebSocketSharp
// PING // PING
if (frame.IsPing) { if (frame.IsPing) {
acceptPingFrame (frame); processPingFrame (frame);
continue; continue;
} }
// PONG // PONG
if (frame.IsPong) { if (frame.IsPong) {
acceptPongFrame (frame); processPongFrame (frame);
continue; continue;
} }
// CLOSE // CLOSE
if (frame.IsClose) if (frame.IsClose)
return acceptCloseFrame (frame); return processCloseFrame (frame);
} }
else { else {
// MORE /* MORE */
// CONT // CONT
if (frame.IsContinuation) { if (frame.IsContinuation) {
@ -853,7 +709,7 @@ namespace WebSocketSharp
} }
// ? // ?
return acceptUnsupportedFrame ( return processUnsupportedFrame (
frame, frame,
CloseStatusCode.IncorrectData, CloseStatusCode.IncorrectData,
"An incorrect data has been received while receiving fragmented data."); "An incorrect data has been received while receiving fragmented data.");
@ -880,7 +736,7 @@ namespace WebSocketSharp
} }
} }
catch (Exception ex) { catch (Exception ex) {
acceptException (ex, "An exception has occurred while connecting."); processException (ex, "An exception has occurred while connecting.");
} }
return false; return false;
@ -888,15 +744,15 @@ namespace WebSocketSharp
} }
// As client // As client
private string createExtensionsRequest () private string createExtensions ()
{ {
var extensions = new StringBuilder (32); var res = new StringBuilder (32);
if (_compression != CompressionMethod.None) if (_compression != CompressionMethod.None)
extensions.Append (_compression.ToExtensionString ()); res.Append (_compression.ToExtensionString ());
return extensions.Length > 0 return res.Length > 0
? extensions.ToString () ? res.ToString ()
: null; : null;
} }
@ -923,7 +779,7 @@ namespace WebSocketSharp
if (_protocols != null) if (_protocols != null)
headers["Sec-WebSocket-Protocol"] = _protocols.ToString (", "); headers["Sec-WebSocket-Protocol"] = _protocols.ToString (", ");
var extensions = createExtensionsRequest (); var extensions = createExtensions ();
if (extensions != null) if (extensions != null)
headers["Sec-WebSocket-Extensions"] = extensions; headers["Sec-WebSocket-Extensions"] = extensions;
@ -1036,15 +892,156 @@ namespace WebSocketSharp
OnOpen.Emit (this, EventArgs.Empty); OnOpen.Emit (this, EventArgs.Empty);
} }
catch (Exception ex) { catch (Exception ex) {
acceptException (ex, "An exception has occurred while OnOpen."); processException (ex, "An exception has occurred while OnOpen.");
} }
} }
} }
catch (Exception ex) { catch (Exception ex) {
acceptException (ex, "An exception has occurred while opening."); processException (ex, "An exception has occurred while opening.");
} }
} }
private bool processCloseFrame (WebSocketFrame frame)
{
var payload = frame.PayloadData;
close (payload, !payload.ContainsReservedCloseStatusCode, false);
return false;
}
private bool processDataFrame (WebSocketFrame frame)
{
var e = frame.IsCompressed
? new MessageEventArgs (
frame.Opcode, frame.PayloadData.ApplicationData.Decompress (_compression))
: new MessageEventArgs (frame.Opcode, frame.PayloadData);
enqueueToMessageEventQueue (e);
return true;
}
private void processException (Exception exception, string message)
{
var code = CloseStatusCode.Abnormal;
var reason = message;
if (exception is WebSocketException) {
var wsex = (WebSocketException) exception;
code = wsex.Code;
reason = wsex.Message;
}
if (code == CloseStatusCode.Abnormal || code == CloseStatusCode.TlsHandshakeFailure)
_logger.Fatal (exception.ToString ());
else
_logger.Error (reason);
error (message ?? code.GetMessage ());
if (_readyState == WebSocketState.Connecting && !_client)
Close (HttpStatusCode.BadRequest);
else
close (code, reason ?? code.GetMessage (), false);
}
private bool processFragmentedFrame (WebSocketFrame frame)
{
return frame.IsContinuation // Not first fragment
? true
: processFragments (frame);
}
private bool processFragments (WebSocketFrame first)
{
using (var buff = new MemoryStream ()) {
buff.WriteBytes (first.PayloadData.ApplicationData);
if (!concatenateFragmentsInto (buff))
return false;
byte[] data;
if (_compression != CompressionMethod.None) {
data = buff.DecompressToArray (_compression);
}
else {
buff.Close ();
data = buff.ToArray ();
}
enqueueToMessageEventQueue (new MessageEventArgs (first.Opcode, data));
return true;
}
}
private bool processPingFrame (WebSocketFrame frame)
{
var mask = _client ? Mask.Mask : Mask.Unmask;
if (send (WebSocketFrame.CreatePongFrame (mask, frame.PayloadData).ToByteArray ()))
_logger.Trace ("Returned a Pong.");
return true;
}
private bool processPongFrame (WebSocketFrame frame)
{
_receivePong.Set ();
_logger.Trace ("Received a Pong.");
return true;
}
// As server
private void processSecWebSocketExtensionsHeader (string value)
{
var buff = new StringBuilder (32);
var compress = false;
foreach (var extension in value.SplitHeaderValue (',')) {
var trimed = extension.Trim ();
var unprefixed = trimed.RemovePrefix ("x-webkit-");
if (!compress && unprefixed.IsCompressionExtension ()) {
var method = unprefixed.ToCompressionMethod ();
if (method != CompressionMethod.None) {
_compression = method;
compress = true;
buff.Append (trimed + ", ");
}
}
}
var len = buff.Length;
if (len > 0) {
buff.Length = len - 2;
_extensions = buff.ToString ();
}
}
private bool processUnsupportedFrame (WebSocketFrame frame, CloseStatusCode code, string reason)
{
_logger.Debug ("Unsupported frame:\n" + frame.PrintToString (false));
processException (new WebSocketException (code, reason), null);
return false;
}
private bool processWebSocketFrame (WebSocketFrame frame)
{
return frame.IsCompressed && _compression == CompressionMethod.None
? processUnsupportedFrame (
frame,
CloseStatusCode.IncorrectData,
"A compressed data has been received without available decompression method.")
: frame.IsFragmented
? processFragmentedFrame (frame)
: frame.IsData
? processDataFrame (frame)
: frame.IsPing
? processPingFrame (frame)
: frame.IsPong
? processPongFrame (frame)
: frame.IsClose
? processCloseFrame (frame)
: processUnsupportedFrame (frame, CloseStatusCode.PolicyViolation, null);
}
private bool send (byte[] frameAsBytes) private bool send (byte[] frameAsBytes)
{ {
lock (_forConn) { lock (_forConn) {
@ -1063,7 +1060,6 @@ namespace WebSocketSharp
var src = stream; var src = stream;
var compressed = false; var compressed = false;
var sent = false; var sent = false;
string msg = null;
try { try {
if (_compression != CompressionMethod.None) { if (_compression != CompressionMethod.None) {
stream = stream.Compress (_compression); stream = stream.Compress (_compression);
@ -1072,11 +1068,11 @@ namespace WebSocketSharp
sent = send (opcode, stream, _client ? Mask.Mask : Mask.Unmask, compressed); sent = send (opcode, stream, _client ? Mask.Mask : Mask.Unmask, compressed);
if (!sent) if (!sent)
msg = "Sending a data has been interrupted."; error ("Sending a data has been interrupted.");
} }
catch (Exception ex) { catch (Exception ex) {
_logger.Fatal (ex.ToString ()); _logger.Fatal (ex.ToString ());
msg = "An exception has occurred while sending a data."; error ("An exception has occurred while sending a data.");
} }
finally { finally {
if (compressed) if (compressed)
@ -1085,9 +1081,6 @@ namespace WebSocketSharp
src.Dispose (); src.Dispose ();
} }
if (msg != null)
error (msg);
return sent; return sent;
} }
} }
@ -1123,18 +1116,18 @@ namespace WebSocketSharp
!send (Fin.More, opcode, mask, buff, compressed)) !send (Fin.More, opcode, mask, buff, compressed))
return false; return false;
var times = rem == 0 ? quo - 2 : quo - 1; var n = rem == 0 ? quo - 2 : quo - 1;
for (long i = 0; i < times; i++) for (long i = 0; i < n; i++)
if (stream.Read (buff, 0, FragmentLength) != FragmentLength || if (stream.Read (buff, 0, FragmentLength) != FragmentLength ||
!send (Fin.More, Opcode.Cont, mask, buff, compressed)) !send (Fin.More, Opcode.Cont, mask, buff, compressed))
return false; return false;
// End // End
var tmpLen = FragmentLength; var endLen = FragmentLength;
if (rem != 0) if (rem != 0)
buff = new byte[tmpLen = rem]; buff = new byte[endLen = rem];
return stream.Read (buff, 0, tmpLen) == tmpLen && return stream.Read (buff, 0, endLen) == endLen &&
send (Fin.Final, Opcode.Cont, mask, buff, compressed); send (Fin.Final, Opcode.Cont, mask, buff, compressed);
} }
@ -1234,7 +1227,7 @@ namespace WebSocketSharp
Action receive = null; Action receive = null;
receive = () => _stream.ReadWebSocketFrameAsync ( receive = () => _stream.ReadWebSocketFrameAsync (
frame => { frame => {
if (acceptFrame (frame) && _readyState != WebSocketState.Closed) { if (processWebSocketFrame (frame) && _readyState != WebSocketState.Closed) {
receive (); receive ();
if (!frame.IsData) if (!frame.IsData)
@ -1247,7 +1240,7 @@ namespace WebSocketSharp
OnMessage.Emit (this, e); OnMessage.Emit (this, e);
} }
catch (Exception ex) { catch (Exception ex) {
acceptException (ex, "An exception has occurred while OnMessage."); processException (ex, "An exception has occurred while OnMessage.");
} }
} }
} }
@ -1255,7 +1248,7 @@ namespace WebSocketSharp
_exitReceiving.Set (); _exitReceiving.Set ();
} }
}, },
ex => acceptException (ex, "An exception has occurred while receiving a message.")); ex => processException (ex, "An exception has occurred while receiving a message."));
receive (); receive ();
} }
@ -1378,7 +1371,7 @@ namespace WebSocketSharp
} }
} }
catch (Exception ex) { catch (Exception ex) {
acceptException (ex, "An exception has occurred while connecting."); processException (ex, "An exception has occurred while connecting.");
} }
} }