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

Feature request: access to WebSocket state and (dis)connection events #11

Open
matthijs110 opened this issue May 4, 2024 · 2 comments

Comments

@matthijs110
Copy link

matthijs110 commented May 4, 2024

I am using the TeamsMonitor.Core NuGet package to connect with Microsoft Teams. I want to implement a retry mechanism, just like Microsoft does with the Stream Deck plugin. This way, a new connection attempt is made when the connection is closed, with an incrementing timeout in between.

I'd love to have access to the WebSocket instance to know if the WebSocket connection is (still) open. Are you willing to expose the ClientWebSocket.State and perhaps implement addition event listeners like Connected and Disconnected as well? :)

Really love this package, as this takes away a lot of research and reverse engineering! I did both, but this package still makes it a lot easier!

@svrooij
Copy link
Owner

svrooij commented May 4, 2024

I'm all up for adding code like this:

public WebSocketState CurrentState => webSocket.State;
public event EventHandler<State> StateChanged;

Not sure how we would go about adding a retry mechanism, but that would be cool.

@matthijs110
Copy link
Author

matthijs110 commented May 4, 2024

Yeah, looks good! I was thinking in seperate events for Connecting and Disconncted though, but I guess your suggestion may work as well :)

public event EventHandler Connected;
public event EventHandler Disconnected;

And perhaps you may as well add a TokenRefreshed event in case you want to handle this event in own solutions.


The retry mechanism I am implementing simply does exactly the same as the Stream Deck solution. What you could do, is the following (quick and dirty proposal):

private const int MinTimeoutMs = 250; // 1/4 seconds
private const int MaxTimeoutMs = 60_000; // 60 seconds
private int NextReconnectTimeoutMs = 0;

public async Task ConnectAsync(bool blocking, bool reconnectOnClose, CancellationToken cancellationToken)
{
    try {
        // Attempt first connection
        await webSocket.ConnectAsync(options.SocketUri, cancellationToken);

        // Continue on succes
        if (blocking)
            await ReadUntilCancelled(cancellationToken);
        else
            backgroundTask = Task.Run(() => ReadUntilCancelled(cancellationToken), cancellationToken);
    }
    catch (WebSocketException ex) {
        if (!IsConnected && reconnectOnClose && !cancellationToken.IsCancellationRequested)
        {
            var timeout = NextReconnectTimeoutMs;

            // Get the (next) timeout up until 60 seconds max.
            NextReconnectTimeoutMs = Math.Min(MaxTimeoutMs, timeout * 2);
            
            Console.WriteLine($"Reconnection attempt in {timeout / 1000} seconds ({timeout} ms)...");
            
            // Execute timeout
            await Task.Delay(timeout, cancellationToken);

            // Do another attempt.
            await ConnectAsync(cancellationToken, reconnectOnClose);
        }
    }
}

You may as well introduce a ConnectWithRetryAsync() method executing the original ConnectAsync with the retry mechanism around it.

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

No branches or pull requests

2 participants