housleyjk / ws-rs

Lightweight, event-driven WebSockets for Rust.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ws::connect API swallows connection errors

jdarpinian opened this issue · comments

The ws::connect API provides no way to learn about connection errors. The return value is Ok(()) even if the socket fails to connect for some reason.

That is due to the async nature of the library. Have you enabled a logger? If you run the example client without running the example server, you can see a connection error being printed from Handler::on_error(). That's where you would handle connection errors.

Handler::on_error is not called

Can you give me a minimal example to replicate this issue?

struct WsHandler;
impl ws::Handler for WsHandler {
    fn on_error(&mut self, _ : ws::Error) {
        panic!("this never happens")
    }
}
ws::connect("ws://fail.example.com/404", |_| {WsHandler}).unwrap();

I'm also affected by this, apparently there's really no clean way to detect connection errors (except measuring the duration between connect and disconnect).

I tried to fix it, but didn't really find out how. But here's a test case that should be able to reproduce this bug:

diff --git a/src/handler.rs b/src/handler.rs
index f5450db..8c5951b 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -310,6 +310,8 @@ impl<F> Handler for F
 
 mod test {
     #![allow(unused_imports, unused_variables, dead_code)]
+    use std::sync::mpsc;
+
     use super::*;
     use url;
     use mio;
@@ -376,4 +378,29 @@ mod test {
 
         close.on_message(message::Message::Binary(vec![1, 2, 3])).unwrap();
     }
+
+    /// Ensure that `Handler::on_error` is called when connecting fails.
+    #[test]
+    fn on_error() {
+        struct WsHandler {
+            tx: mpsc::Sender<bool>,
+        }
+
+        impl Handler for WsHandler {
+            fn on_error(&mut self, _ : Error) {
+                // Confirm that `on_error` was called
+                self.tx.send(true).unwrap();
+            }
+        }
+
+        let (transfer_tx, transfer_rx) = mpsc::channel();
+        ::connect("ws://fail.example.com/404", move |_| {
+            let (tx, rx) = mpsc::channel();
+            transfer_tx.send(rx).unwrap();
+            WsHandler { tx }
+        }).unwrap();
+
+        let rx = transfer_rx.recv().unwrap();
+        rx.try_recv().expect("on_error was not called");
+    }
 }

I am afflicted by the same problem with non-propagating errors. They are handled here or here. The problem is that they are not propagated up in any way other than through logging. This is a serious flaw.

Why aren't errors generated within the connect method passed to the handler just after its creation there? There shouldn't be any kind of recoverable error that is not able to be handled and that gives no indication that it even happened. Either the connect method needs to pass the error to the newly created handler, or the process_queue method needs to propagate errors up to its caller and out to WebSocket::run.

Having only two days of experience with this library I'd like to get a better understanding before I start making any changes myself. Please offer me some guidance and design insight and I'll put together a pull request.