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

feat(socketio): add ability to wait for socketio completion #3

Closed
wants to merge 1 commit into from

Conversation

sirkrypt0
Copy link
Collaborator

When the async client connects to the server, it spawns a new thread to handle the arriving messages asynchronously. Previously, this thread was immediately detached with no chance of awaiting its completion.

For long-running programs (e.g. a client program that never really disconnects from the server) this can be problematic, as unexpected stream completions would go unnoticed. This may happen if the underlying tungstenite websocket shuts down ('Received close frame: None') but there are no engineio/socketio close frames. Hence, since the stream terminates, the message handling task stops without a Close or Error event being fired.

Therefore, we now provide a new wait_for_completion function on the socketio client which allows to wait until the polling stream has terminated. Programs can then use this to determine whether an unexpected shutdown has happened and take further actions on their own.

@sirkrypt0 sirkrypt0 requested a review from felix-gohla July 4, 2024 18:22
@sirkrypt0 sirkrypt0 self-assigned this Jul 4, 2024
@sirkrypt0 sirkrypt0 force-pushed the feature/wait-for-socketio-completion branch 2 times, most recently from e04deca to 47f6540 Compare July 4, 2024 18:45
Copy link

@felix-gohla felix-gohla left a comment

Choose a reason for hiding this comment

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

While it works, I do not really know whether this makes the API design better.

It feels a little janky to have that wait_for_completion function while the client doesn't really have a notion of completion. I am thinking of alternative and ergonomic ways of using the client.

One should be able to:

  • manually disconnect
  • send messages
  • receive messages
  • receive an error and forcefully tell the user of the client about that

Without calling the wait_for_completion function, you wouldn't know that there was an error, ay?

/// If the connection has not been started yet, this terminates immediately,
/// so make sure to call [`Client::poll_stream`] (done by [`ClientBuilder::connect`])
/// first.
pub async fn wait_for_completion(&self) {

Choose a reason for hiding this comment

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

I think this should return some kind of result (like an error reason?), as it can fail in various ways:

  • Client was never connected
  • Client did successfully disconnect itself (graceful shutdown requested).
  • Server sent an error or whatever.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Client did successfully disconnect itself (graceful shutdown requested).

We could make the DisconnectReason public and return it here.

Server sent an error or whatever.

That's handled through .on(Event::Error.

Client was never connected

We could return a Result which contains an error if the client was not connected and the disconnect reason otherwise.

Choose a reason for hiding this comment

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

Then why don't handle the server-side disconnect via the on callbacks and make this poll function blocking? I see some kind of split API here and I feel like not knowing which method to use when.

@sirkrypt0
Copy link
Collaborator Author

sirkrypt0 commented Jul 7, 2024

It feels a little janky to have that wait_for_completion function while the client doesn't really have a notion of completion.

If the polling task is completed, the client is done, until you call poll_stream again of course. We could also rename it, e.g. wait_for_task_completion, wait_for_poll_completion, wait_for_termination, ...

Without calling the wait_for_completion function, you wouldn't know that there was an error, ay?

Depends on what you define as error. You do receive application errors from socket.io or engine.io through the Event::Error, but previously the termination of the polling task was not propergated, so you wouldn't know when the websocket stream closed properly but without any engine.io / socket.io close messages.
So in typical scenarios, where e.g. the server would shut down or the network breaks or whatever, where we don't receive any responses anymore, we would get notified. Just not in this weird case where the underlying websocket closes properly but without any engine.io / socket.io frames (or probably when the poll task panics).

@sirkrypt0 sirkrypt0 force-pushed the feature/wait-for-socketio-completion branch from 47f6540 to 2a9a6ba Compare July 7, 2024 16:48
@felix-gohla
Copy link

If the polling task is completed, the client is done, until you call poll_stream again of course.

This is also a little weird. Shouldn't I call connect or something else that notifies that it actually does something instead of the non-descriptive poll_stream? What does this mean anyways? This poll function is only called once, so you're not doing polling when calling the function.

But this aside, as said in the discussion above, not sure whether the additional function is the way to go. 😊

@sirkrypt0 sirkrypt0 force-pushed the feature/wait-for-socketio-completion branch 2 times, most recently from 4ead8c8 to e9b5ace Compare July 16, 2024 08:38
@felix-gohla
Copy link

I think closing the upper stream is a better option. Thank you. :)

@sirkrypt0 sirkrypt0 force-pushed the feature/wait-for-socketio-completion branch from e9b5ace to 2cafa9e Compare July 16, 2024 13:40
When the async client connects to the server, it spawns a new thread to
handle the arriving messages asynchronously, which is immediately
detached with no chance of awaiting its completion.

For long-running programs (e.g. a client program that never really disconnects
from the server) this can be problematic, as unexpected stream completions
would go unnoticed. This may happen if the underlying tungstenite websocket
shuts down ('Received close frame: None') but there are no engineio/socketio
close frames. Hence, since the stream terminates, the message handling task
stops without a Close or Error event being fired.

Thus, we now fire an additional Event::Close when the stream terminates,
to signal the (potentially unexpected) close to the user.
@sirkrypt0 sirkrypt0 force-pushed the feature/wait-for-socketio-completion branch from 2cafa9e to 0c492fb Compare July 16, 2024 14:16
@sirkrypt0 sirkrypt0 closed this Jul 16, 2024
@sirkrypt0
Copy link
Collaborator Author

Submitted to upstream.

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.

2 participants