websockets-rs / rust-websocket

A WebSocket (RFC6455) library written in Rust
http://websockets-rs.github.io/rust-websocket/
MIT License
1.55k stars 223 forks source link

async client stream does not know when a connection closes #136

Open alex-shapiro opened 7 years ago

alex-shapiro commented 7 years ago

Problem: When a connection closes abnormally, the client stream does not return or acknowledge the broken connection.

Desired Behavior: When a connection closes abnormally, the client stream resolves immediately.

Quick demo of the problem, using the async-client + async-server examples:

  1. start the server
  2. start the client (and it connects to the server)
  3. kill the server with ctrl-C

The client stream remains open and shows no sign that the connection has closed. If the user sends a message via stdin_ch, the client accepts the message. If you send a second message, the client finally panics with a Broken Pipe IO error.

alex-shapiro commented 7 years ago

It looks like the problem may be a more fundamental Tokio issue: https://github.com/tokio-rs/tokio-core/issues/192

illegalprime commented 7 years ago

this is very strange, the async stuff is just a wrapper around a raw TcpStream, I'll try it out with two raw async TcpStreams and see if I can reproduce it.

alex-shapiro commented 7 years ago

I solved the problem. The issue is in the async-client example, not in the websocket crate itself.

The example uses select to combine two streams: incoming websocket messages and incoming channel messages. When the websocket connection closes, the stream of incoming websocket messages resolves.

However, the select future is essentially an "or" future - it doesn't resolve until both streams resolve. Since the channel receiver stream hasn't resolved, it continues sending messages to the websocket sink. Since the connection is closed, the sink will start rejecting messages.

I fixed the problem by replacing the built-in select with my own combinator, and_select, which resolves as soon as one of the streams resolves. So as soon as the websocket connection closes, the client closes. Here's the gist: https://gist.github.com/alex-shapiro/ab398f5a6a59ebf182b50fd50790d375

illegalprime commented 7 years ago

@alex-shapiro thanks for the fix, I'm re-opening so I can fix the example. I think it could be fixed without a new combinator and selecting over channel messages and results of polling the websocket

XiangQingW commented 5 years ago

@illegalprime I meet the same issue when tcp is closed by peer. There is a way to reproduce this issue:

  1. cargo run --example async-client
  2. cargo run --example async-server
  3. exit the async-server by C-c, and the async-client doesn't know this closed status until I send a message in the terminal.

Have any idea to handle this issue? Thank you very much.

vi commented 5 years ago

A non-websocket-related answer:

There's rather radical way: clone the underlying TCP socket and use the cloned sibling specifically for detecting connection reset.

Relevant Tokio issue: https://github.com/tokio-rs/tokio/issues/483

XiangQingW commented 5 years ago

Thanks for reply. This is an amazing progress, however I can't get TcpStream in websocket-async-client.