Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make WebSockets Timeout #448

Merged
merged 14 commits into from
Dec 5, 2023
Merged

Make WebSockets Timeout #448

merged 14 commits into from
Dec 5, 2023

Conversation

acodrington
Copy link
Contributor

@acodrington acodrington commented Aug 18, 2023

[sc-57052]

Background

Web sockets currently do not time out. This is a problem, as it means connections will hang.

Results

Before

Web sockets wrap a WebSocket context, and interact with that link a Stream. But they would never timeout the operations, which allowed them to go on forever.

After

To ensure timeouts, we wrapped the WebSocketStream within a NetworkTimeoutStream to ensure that the operations would indeed time out.

JSON Async vs Sync

JSON serialization is not async. So we read off/write to the stream asynchronously, and fall back to 'sync' reading/writing if the payload is too large.

But when this happened, web sockets never timed out (even with NetworkTimeoutStream). This is because it would used the synchronous versions of Read and Write, which the NetworkTimeoutStream cannot timeout with.

To solve this, we introduced the CallUnderlyingAsyncMethodsStream, so that when JSON called the synchronous Read or Write methods, they would use the async versions instead. This would allow the NetworkTimeoutStream to actually time out.

WebSocketStream Special Methods

The IStreamFactory.CreateStream method for web sockets returned a WebSocketStream. This made it hard to wrap in the other streams, as WebSocketStream had some special methods for doing the initial read and write.

To solve this, we simply moved the special methods to WebSocketExtensionMethods.

Tests

We turned on web sockets for all the tests we could (not just the timeout tests).

The only places we did not reenable web sockets were

  • Tests that were also disabling regular polling (web sockets wouldn't work anyway)
  • Tests that explicitly commented that they could not work with web sockets.
  • Tests in TimeoutsApplyDuringHandShake. There were issues, so we made a separate card for that.

How to review this PR

Quality ✔️

Pre-requisites

  • I have read How we use GitHub Issues for help deciding when and where it's appropriate to make an issue.
  • I have considered informing or consulting the right people, according to the ownership map.
  • I have considered appropriate testing for my change.

@shortcut-integration
Copy link

This pull request has been linked to Shortcut Story #57052: Wrap the WebSocket stream in a NetworkTimeoutStream.


namespace Halibut.Transport.Streams
{
class CallUnderlyingAsyncMethodsStream : AsyncStream
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To ensure we covered all the methods that NetworkTimeoutStream would call, we copied that class, and altered appropriately.

@sburmanoctopus sburmanoctopus changed the title WIP: Enable timeouts on WebSockets Enable timeouts on WebSockets Dec 5, 2023
@sburmanoctopus sburmanoctopus changed the title Enable timeouts on WebSockets Make WebSockets Timeout Dec 5, 2023
@sburmanoctopus sburmanoctopus marked this pull request as ready for review December 5, 2023 03:05
@sburmanoctopus sburmanoctopus requested a review from a team as a code owner December 5, 2023 03:05
@@ -20,6 +20,7 @@ public class TcpKeepAliveTests : BaseTest
{
[Test]
[LatestClientAndLatestServiceTestCases(testNetworkConditions: false, testListening:false, testWebSocket:false)]
// TCP Keep alives were for TcpClients, not web sockets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting find - side project: Do keep alives work as expected for WebSockets or did we miss something?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I've added a card to the board to look into this.

@@ -122,7 +123,8 @@ await using (var clientAndService = await clientAndServiceTestCase.CreateTestCas
}

[Test]
[LatestClientAndLatestServiceTestCases(testNetworkConditions: false, testWebSocket: false)]
[Timeout(120000)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Was there a reason for defining the timeout here? Worth a comment if there is?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely spotted. That was there when I picked up this PR. Talking to the author, it was likely a temporary thing. Plus, it runs fine on my PC without it. So we decided to deleted it 🙂

Copy link
Contributor

@nathanwoctopusdeploy nathanwoctopusdeploy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, lovely job

@sburmanoctopus sburmanoctopus enabled auto-merge (squash) December 5, 2023 03:43
@sburmanoctopus sburmanoctopus merged commit 23582d1 into main Dec 5, 2023
14 of 15 checks passed
@sburmanoctopus sburmanoctopus deleted the sast/websocket-timeouts branch December 5, 2023 03:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants