Refactored WebSocket.cs

This commit is contained in:
sta 2014-01-13 14:32:39 +09:00
parent 82015cdb1a
commit 7c610d06ff
3 changed files with 209 additions and 225 deletions

View File

@ -193,13 +193,6 @@ 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)
{
return stream == null
@ -209,10 +202,23 @@ namespace WebSocketSharp
: null;
}
internal static string CheckIfClosable (this WebSocketState state)
{
return state == WebSocketState.CLOSING
? "While closing the WebSocket connection."
: state == WebSocketState.CLOSED
? "The WebSocket connection has already been closed."
: null;
}
internal static string CheckIfOpen (this WebSocketState state)
{
return state != WebSocketState.OPEN
? "A WebSocket connection isn't established or has been closed."
return state == WebSocketState.CONNECTING
? "A WebSocket connection isn't established."
: state == WebSocketState.CLOSING
? "While closing the WebSocket connection."
: state == WebSocketState.CLOSED
? "The WebSocket connection has already been closed."
: null;
}

View File

@ -484,6 +484,74 @@ namespace WebSocketSharp
#region Private Methods
private bool acceptCloseFrame (WsFrame frame)
{
var payload = frame.PayloadData;
close (payload, !payload.ContainsReservedCloseStatusCode, false);
return false;
}
private bool acceptDataFrame (WsFrame frame)
{
var args = frame.IsCompressed
? new MessageEventArgs (
frame.Opcode,
frame.PayloadData.ApplicationData.Decompress (_compression))
: new MessageEventArgs (frame.Opcode, frame.PayloadData);
OnMessage.Emit (this, args);
return true;
}
private bool acceptFragmentedFrame (WsFrame frame)
{
return frame.IsContinuation // Not first fragment
? true
: acceptFragments (frame);
}
private bool acceptFragments (WsFrame 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 ();
}
OnMessage.Emit (this, new MessageEventArgs (first.Opcode, data));
return true;
}
}
private bool acceptFrame (WsFrame frame)
{
return frame.IsCompressed && _compression == CompressionMethod.NONE
? acceptUnsupportedFrame (
frame,
CloseStatusCode.INCORRECT_DATA,
"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.POLICY_VIOLATION, null);
}
// As server
private bool acceptHandshake ()
{
@ -516,6 +584,32 @@ namespace WebSocketSharp
return send (createHandshakeResponse ());
}
private bool acceptPingFrame (WsFrame frame)
{
var mask = _client ? Mask.MASK : Mask.UNMASK;
if (send (WsFrame.CreatePongFrame (mask, frame.PayloadData)))
_logger.Trace ("Returned a Pong.");
return true;
}
private bool acceptPongFrame (WsFrame frame)
{
_receivePong.Set ();
_logger.Trace ("Received a Pong.");
return true;
}
private bool acceptUnsupportedFrame (
WsFrame frame, CloseStatusCode code, string reason)
{
_logger.Debug ("Unsupported frame:\n" + frame.PrintToString (false));
processException (new WebSocketException (code, reason), null);
return false;
}
// As server
private string checkIfValidHandshakeRequest (WebSocketContext context)
{
@ -566,10 +660,10 @@ namespace WebSocketSharp
private void close (PayloadData payload, bool send, bool wait)
{
lock (_forConn) {
var msg = _readyState.CheckIfCanClose ();
if (msg != null) {
_logger.Info (String.Format ("{0}\nstate: {1}", msg, _readyState));
if (_readyState == WebSocketState.CLOSING ||
_readyState == WebSocketState.CLOSED) {
_logger.Info (
"Closing the WebSocket connection has already been done.");
return;
}
@ -663,48 +757,40 @@ namespace WebSocketSharp
}
private bool concatenateFragmentsInto (Stream dest)
{
while (true)
{
var frame = _stream.ReadFrame ();
// MORE & CONT
if (!frame.IsFinal && frame.IsContinuation)
{
if (!frame.IsFinal && frame.IsContinuation) {
dest.WriteBytes (frame.PayloadData.ApplicationData);
continue;
return concatenateFragmentsInto (dest);
}
// FINAL & CONT
if (frame.IsFinal && frame.IsContinuation)
{
if (frame.IsFinal && frame.IsContinuation) {
dest.WriteBytes (frame.PayloadData.ApplicationData);
break;
return true;
}
// FINAL & PING
if (frame.IsFinal && frame.IsPing)
{
processPingFrame (frame);
continue;
if (frame.IsFinal && frame.IsPing) {
acceptPingFrame (frame);
return concatenateFragmentsInto (dest);
}
// FINAL & PONG
if (frame.IsFinal && frame.IsPong)
{
processPongFrame ();
continue;
if (frame.IsFinal && frame.IsPong) {
acceptPongFrame (frame);
return concatenateFragmentsInto (dest);
}
// FINAL & CLOSE
if (frame.IsFinal && frame.IsClose)
return processCloseFrame (frame);
return acceptCloseFrame (frame);
// ?
return processUnsupportedFrame (frame, CloseStatusCode.INCORRECT_DATA, null);
}
return true;
return acceptUnsupportedFrame (
frame, CloseStatusCode.INCORRECT_DATA, null);
}
// As client
@ -869,6 +955,7 @@ namespace WebSocketSharp
{
try {
OnOpen.Emit (this, EventArgs.Empty);
if (_readyState == WebSocketState.OPEN)
startReceiving ();
}
catch (Exception ex) {
@ -877,25 +964,6 @@ namespace WebSocketSharp
}
}
private bool processCloseFrame (WsFrame frame)
{
var payload = frame.PayloadData;
close (payload, !payload.ContainsReservedCloseStatusCode, false);
return false;
}
private bool processDataFrame (WsFrame frame)
{
var args = frame.IsCompressed
? new MessageEventArgs (
frame.Opcode, frame.PayloadData.ApplicationData.Decompress (_compression))
: new MessageEventArgs (frame.Opcode, frame.PayloadData);
OnMessage.Emit (this, args);
return true;
}
private void processException (Exception exception, string reason)
{
var code = CloseStatusCode.ABNORMAL;
@ -923,87 +991,17 @@ namespace WebSocketSharp
close (code, reason ?? code.GetMessage (), false);
}
private bool processFragmentedFrame (WsFrame frame)
{
return frame.IsContinuation // Not first fragment
? true
: processFragments (frame);
}
private bool processFragments (WsFrame 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 ();
}
OnMessage.Emit (this, new MessageEventArgs (first.Opcode, data));
return true;
}
}
private bool processFrame (WsFrame frame)
{
return frame.IsCompressed && _compression == CompressionMethod.NONE
? processUnsupportedFrame (
frame,
CloseStatusCode.INCORRECT_DATA,
"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.IsClose
? processCloseFrame (frame)
: processUnsupportedFrame (frame, CloseStatusCode.POLICY_VIOLATION, null);
}
private bool processPingFrame (WsFrame frame)
{
if (send (WsFrame.CreatePongFrame (_client ? Mask.MASK : Mask.UNMASK, frame.PayloadData)))
_logger.Trace ("Returned Pong.");
return true;
}
private bool processPongFrame ()
{
_receivePong.Set ();
_logger.Trace ("Received Pong.");
return true;
}
// As server
private void processRequestedExtensions (string extensions)
{
var comp = false;
var buffer = new List<string> ();
foreach (var e in extensions.SplitHeaderValue (','))
{
foreach (var e in extensions.SplitHeaderValue (',')) {
var extension = e.Trim ();
var tmp = extension.RemovePrefix ("x-webkit-");
if (!comp && tmp.IsCompressionExtension ())
{
if (!comp && tmp.IsCompressionExtension ()) {
var method = tmp.ToCompressionMethod ();
if (method != CompressionMethod.NONE)
{
if (method != CompressionMethod.NONE) {
_compression = method;
comp = true;
buffer.Add (extension);
@ -1020,10 +1018,8 @@ namespace WebSocketSharp
{
var comp = _compression != CompressionMethod.NONE ? true : false;
var hasComp = false;
if (extensions != null && extensions.Length > 0)
{
foreach (var e in extensions.SplitHeaderValue (','))
{
if (extensions != null && extensions.Length > 0) {
foreach (var e in extensions.SplitHeaderValue (',')) {
var extension = e.Trim ();
if (comp && !hasComp && extension.Equals (_compression))
hasComp = true;
@ -1036,14 +1032,6 @@ namespace WebSocketSharp
_compression = CompressionMethod.NONE;
}
private bool processUnsupportedFrame (WsFrame frame, CloseStatusCode code, string reason)
{
_logger.Debug ("Unsupported frame:\n" + frame.PrintToString (false));
processException (new WebSocketException (code, reason), null);
return false;
}
// As client
private HandshakeResponse receiveHandshakeResponse ()
{
@ -1055,8 +1043,9 @@ namespace WebSocketSharp
private bool send (byte [] frameAsBytes)
{
lock (_forConn) {
if (_readyState != WebSocketState.OPEN) {
var msg = "A WebSocket connection isn't established or has been closed.";
var msg = "The WebSocket connection isn't available.";
_logger.Error (msg);
error (msg);
@ -1065,6 +1054,7 @@ namespace WebSocketSharp
return _stream.Write (frameAsBytes);
}
}
// As client
private void send (HandshakeRequest request)
@ -1087,8 +1077,9 @@ namespace WebSocketSharp
private bool send (WsFrame frame)
{
lock (_forConn) {
if (_readyState != WebSocketState.OPEN) {
var msg = "A WebSocket connection isn't established or has been closed.";
var msg = "The WebSocket connection isn't available.";
_logger.Error (msg);
error (msg);
@ -1097,6 +1088,7 @@ namespace WebSocketSharp
return _stream.Write (frame.ToByteArray ());
}
}
private bool send (Opcode opcode, byte [] data)
{
@ -1304,21 +1296,19 @@ namespace WebSocketSharp
var host = _uri.DnsSafeHost;
var port = _uri.Port;
_tcpClient = new TcpClient (host, port);
_stream = WsStream.CreateClientStream (_tcpClient, _secure, host, _certValidationCallback);
_stream = WsStream.CreateClientStream (
_tcpClient, _secure, host, _certValidationCallback);
}
private void startReceiving ()
{
if (_readyState != WebSocketState.OPEN)
return;
_exitReceiving = new AutoResetEvent (false);
_receivePong = new AutoResetEvent (false);
Action receive = null;
receive = () => _stream.ReadFrameAsync (
frame => {
if (processFrame (frame))
if (acceptFrame (frame))
receive ();
else
_exitReceiving.Set ();
@ -1382,10 +1372,10 @@ namespace WebSocketSharp
CloseEventArgs args, byte [] frameAsBytes, int waitTimeOut)
{
lock (_forConn) {
var msg = _readyState.CheckIfCanClose ();
if (msg != null) {
_logger.Info (String.Format ("{0}\nstate: {1}", msg, _readyState));
if (_readyState == WebSocketState.CLOSING ||
_readyState == WebSocketState.CLOSED) {
_logger.Info (
"Closing the WebSocket connection has already been done.");
return;
}
@ -1446,14 +1436,13 @@ namespace WebSocketSharp
}
// As server, used to broadcast
internal void Send (Opcode opcode, byte [] data, Dictionary<CompressionMethod, byte []> cache)
{
lock (_forSend)
internal void Send (
Opcode opcode, byte [] data, Dictionary<CompressionMethod, byte []> cache)
{
lock (_forSend) {
try {
byte [] cached;
if (!cache.TryGetValue (_compression, out cached))
{
if (!cache.TryGetValue (_compression, out cached)) {
cached = WsFrame.CreateFrame (
Fin.FINAL,
opcode,
@ -1468,31 +1457,31 @@ namespace WebSocketSharp
}
catch (Exception ex) {
_logger.Fatal (ex.ToString ());
error ("An exception has occurred.");
error ("An exception has occurred while sending a data.");
}
}
}
// As server, used to broadcast
internal void Send (Opcode opcode, Stream stream, Dictionary <CompressionMethod, Stream> cache)
{
lock (_forSend)
internal void Send (
Opcode opcode, Stream stream, Dictionary <CompressionMethod, Stream> cache)
{
lock (_forSend) {
try {
Stream cached;
if (!cache.TryGetValue (_compression, out cached))
{
if (!cache.TryGetValue (_compression, out cached)) {
cached = stream.Compress (_compression);
cache.Add (_compression, cached);
}
else
cached.Position = 0;
sendFragmented (opcode, cached, Mask.UNMASK, _compression != CompressionMethod.NONE);
sendFragmented (
opcode, cached, Mask.UNMASK, _compression != CompressionMethod.NONE);
}
catch (Exception ex) {
_logger.Fatal (ex.ToString ());
error ("An exception has occurred.");
error ("An exception has occurred while sending a data.");
}
}
}
@ -1506,9 +1495,9 @@ namespace WebSocketSharp
/// </summary>
public void Close ()
{
var msg = _readyState.CheckIfCanClose ();
var msg = _readyState.CheckIfClosable ();
if (msg != null) {
_logger.Error (String.Format ("{0}\nstate: {1}", msg, _readyState));
_logger.Error (msg);
error (msg);
return;
@ -1565,14 +1554,13 @@ namespace WebSocketSharp
public void Close (ushort code, string reason)
{
byte [] data = null;
var msg = _readyState.CheckIfCanClose () ??
var msg = _readyState.CheckIfClosable () ??
code.CheckIfValidCloseStatusCode () ??
(data = code.Append (reason)).CheckIfValidCloseData ();
if (msg != null) {
_logger.Error (
String.Format (
"{0}\nstate: {1} code: {2} reason: {3}", msg, _readyState, code, reason));
String.Format ("{0}\ncode: {1} reason: {2}", msg, code, reason));
error (msg);
return;
@ -1600,13 +1588,12 @@ namespace WebSocketSharp
public void Close (CloseStatusCode code, string reason)
{
byte [] data = null;
var msg = _readyState.CheckIfCanClose () ??
var msg = _readyState.CheckIfClosable () ??
(data = ((ushort) code).Append (reason)).CheckIfValidCloseData ();
if (msg != null) {
_logger.Error (
String.Format (
"{0}\nstate: {1} reason: {2}", msg, _readyState, reason));
String.Format ("{0}\ncode: {1} reason: {2}", msg, code, reason));
error (msg);
return;
@ -1625,9 +1612,9 @@ namespace WebSocketSharp
/// </remarks>
public void CloseAsync ()
{
var msg = _readyState.CheckIfCanClose ();
var msg = _readyState.CheckIfClosable ();
if (msg != null) {
_logger.Error (String.Format ("{0}\nstate: {1}", msg, _readyState));
_logger.Error (msg);
error (msg);
return;
@ -1698,14 +1685,13 @@ namespace WebSocketSharp
public void CloseAsync (ushort code, string reason)
{
byte [] data = null;
var msg = _readyState.CheckIfCanClose () ??
var msg = _readyState.CheckIfClosable () ??
code.CheckIfValidCloseStatusCode () ??
(data = code.Append (reason)).CheckIfValidCloseData ();
if (msg != null) {
_logger.Error (
String.Format (
"{0}\nstate: {1} code: {2} reason: {3}", msg, _readyState, code, reason));
String.Format ("{0}\ncode: {1} reason: {2}", msg, code, reason));
error (msg);
return;
@ -1739,13 +1725,12 @@ namespace WebSocketSharp
public void CloseAsync (CloseStatusCode code, string reason)
{
byte [] data = null;
var msg = _readyState.CheckIfCanClose () ??
var msg = _readyState.CheckIfClosable () ??
(data = ((ushort) code).Append (reason)).CheckIfValidCloseData ();
if (msg != null) {
_logger.Error (
String.Format (
"{0}\nstate: {1} reason: {2}", msg, _readyState, reason));
String.Format ("{0}\ncode: {1} reason: {2}", msg, code, reason));
error (msg);
return;
@ -1850,8 +1835,7 @@ namespace WebSocketSharp
var data = Encoding.UTF8.GetBytes (message);
var msg = data.CheckIfValidPingData ();
if (msg != null)
{
if (msg != null) {
_logger.Error (msg);
error (msg);

View File

@ -104,12 +104,10 @@ namespace WebSocketSharp
TcpClient client,
bool secure,
string host,
System.Net.Security.RemoteCertificateValidationCallback validationCallback
)
System.Net.Security.RemoteCertificateValidationCallback validationCallback)
{
var netStream = client.GetStream ();
if (secure)
{
if (secure) {
if (validationCallback == null)
validationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
@ -144,8 +142,7 @@ namespace WebSocketSharp
internal bool Write (byte [] data)
{
lock (_forWrite)
{
lock (_forWrite) {
try {
_innerStream.Write (data, 0, data.Length);
return true;
@ -182,16 +179,15 @@ namespace WebSocketSharp
public string [] ReadHandshake ()
{
var read = false;
var exception = false;
var read = false;
var timeout = false;
var buffer = new List<byte> ();
Action<int> add = i => buffer.Add ((byte) i);
var timeout = false;
var timer = new Timer (
state =>
{
state => {
timeout = true;
_innerStream.Close ();
},
@ -200,13 +196,11 @@ namespace WebSocketSharp
-1);
try {
while (buffer.Count < _handshakeLimitLen)
{
while (buffer.Count < _handshakeLimitLen) {
if (_innerStream.ReadByte ().EqualsWith ('\r', add) &&
_innerStream.ReadByte ().EqualsWith ('\n', add) &&
_innerStream.ReadByte ().EqualsWith ('\r', add) &&
_innerStream.ReadByte ().EqualsWith ('\n', add))
{
_innerStream.ReadByte ().EqualsWith ('\n', add)) {
read = true;
break;
}