Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 41 additions & 37 deletions WebSockets/ReceiveAndControllThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
internal class ReceiveAndControllThread
{
private readonly WebSocket _webSocket;
private int _checkingTimeouts = 0;

public ReceiveAndControllThread(WebSocket webSocket)
{
Expand All @@ -21,8 +22,8 @@

public void WorkerThread() //this thread is always running and thus the best place for controlling ping and other messages
{
var timeoutCheckerTimer = new Timer(CheckTimeouts, Thread.CurrentThread, 5000, 5000);
var timeoutCheckerTimer = new Timer(CheckTimeouts, null, 5000, 5000);


while (!_webSocket.Stopped)
{
Expand Down Expand Up @@ -52,12 +53,12 @@
}
}

_webSocket.ReceiveStream.Close();
timeoutCheckerTimer.Change(Timeout.Infinite, Timeout.Infinite);
timeoutCheckerTimer.Dispose();
_webSocket.ReceiveStream.Close();
}

private void ProcessIncomingMessage()

Check failure on line 61 in WebSockets/ReceiveAndControllThread.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 28 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=nanoframework_nanoframework.System.Net.WebSockets&issues=AaDTyIWNWGWVc_hLtQJG&open=AaDTyIWNWGWVc_hLtQJG&pullRequest=249
{
var messageFrame = _webSocket.WebSocketReceiver.StartReceivingMessage();

Expand All @@ -80,7 +81,7 @@
{
byte[] buffer = _webSocket.WebSocketReceiver.ReadBuffer(messageFrame.MessageLength, messageFrame.Masks);

_webSocket.LastContactTimeStamp = DateTime.UtcNow;
_webSocket.TouchLastContact();

switch (messageFrame.OpCode)
{
Expand All @@ -95,7 +96,7 @@
case OpCode.PongFrame:
// received a Pong
// checking if content Pong matches Ping is not implemented due to thread safety and memory consumption considerations
_webSocket.Pinging = false;
_webSocket.OnPongReceived();
break;

case OpCode.ConnectionCloseFrame:
Expand All @@ -112,17 +113,19 @@
}

//connection asked to be closed return answer
if (_webSocket.State != WebSocketFrame.WebSocketState.CloseSent)
if (_webSocket.TryMarkCloseReceived())
{
_webSocket.State = WebSocketFrame.WebSocketState.CloseReceived;

_webSocket.RawClose(WebSocketCloseStatus.NormalClosure, buffer, true);
}
//response to connection close we can shut down the socket.
else
{
_webSocket.HardClose();
// our close message can still be queued behind pending messages (simultaneous close)
_webSocket.WaitForCloseMessageSent();
}

// either this is the response to our close, or RawClose lost a race with another close,
// so we can shut down the socket (no-op if already closed)
_webSocket.HardClose();
break;
}
}
Expand All @@ -138,7 +141,7 @@
{
messageFrame.Buffer = _webSocket.WebSocketReceiver.ReadBuffer(messageFrame.MessageLength, messageFrame.Masks);

_webSocket.LastContactTimeStamp = DateTime.UtcNow;
_webSocket.TouchLastContact();

OnNewMessage(messageFrame);
}
Expand All @@ -147,37 +150,41 @@
}


private void CheckTimeouts(object thread)
// Runs on the timer thread, concurrently with the receive thread.
// Shared state is accessed through WebSocket helpers that only hold a lock for field access,
// and nothing here blocks, so a receive thread holding a lock can't stall this check.
private void CheckTimeouts(object state)
{
var receiveThread = (Thread)thread;

#pragma warning disable S3889 // OK to use in .NET nanoFramework context
receiveThread.Suspend();
#pragma warning restore S3889 // Neither "Thread.Resume" nor "Thread.Suspend" should be used
// skip this tick if the previous one is still running
if (Interlocked.CompareExchange(ref _checkingTimeouts, 1, 0) != 0)
{
return;
}

try
{
//Controlling ping and ControllerMessagesTimeout
if (_webSocket.Pinging
&& _webSocket.PingTime.Add(_webSocket.ServerTimeout) < DateTime.UtcNow)
if (_webSocket.Stopped)
{
_webSocket.RawClose(WebSocketCloseStatus.PolicyViolation, Encoding.UTF8.GetBytes("Ping timeout"), true);

Debug.WriteLine($"{_webSocket.RemoteEndPoint} ping timed out");
return;
}

if (_webSocket.State == WebSocketFrame.WebSocketState.CloseSent
&& _webSocket.ClosingTime.Add(_webSocket.ServerTimeout) < DateTime.UtcNow)
//Controlling ping and ControllerMessagesTimeout
switch (_webSocket.EvaluateTimeouts(DateTime.UtcNow))
{
_webSocket.HardClose();
}
case WebSocket.TimeoutAction.PingTimeout:
Debug.WriteLine($"{_webSocket.RemoteEndPoint} ping timed out");

if (_webSocket.KeepAliveInterval != Timeout.InfiniteTimeSpan
&& _webSocket.State != WebSocketFrame.WebSocketState.CloseSent
&& !_webSocket.Pinging
&& _webSocket.LastContactTimeStamp.Add(_webSocket.KeepAliveInterval) < DateTime.UtcNow)
{
_webSocket.SendPing();
// don't wait for the close message to be sent, a following check will close the connection
_webSocket.BeginClose(WebSocketCloseStatus.PolicyViolation, Encoding.UTF8.GetBytes("Ping timeout"));
break;

case WebSocket.TimeoutAction.HardClose:
_webSocket.HardClose();
break;

case WebSocket.TimeoutAction.SendPing:
_webSocket.SendPing();
break;
}
}
catch (Exception ex)
Expand All @@ -186,10 +193,7 @@
}
finally
{
// always resume the receive thread, otherwise it will be left suspended forever
#pragma warning disable S3889 // OK to use in .NET nanoFramework context
receiveThread.Resume();
#pragma warning restore S3889 // Neither "Thread.Resume" nor "Thread.Suspend" should be used
Interlocked.Exchange(ref _checkingTimeouts, 0);
}
}

Expand Down
Loading