snapview/tokio-tungstenite

Hang on when using connect_async

araraloren opened this issue · 11 comments

Hi, I write a program, it's okay connect a echo service.
But it is hang on when connect to other websocket server.
The websocket service is test okay using the testing service such as https://wstool.js.org/
7bf356c98ac6f7d2e171e082057807e5
The example branch https://github.com/araraloren/example/tree/tokio_tungstenite_bug.
I am not familiar with websocket, let me know if I am doing something wrong.
Here is running log of cargo r:

 --> Request { method: GET, uri: wss://echo.websocket.org/, version: HTTP/1.1, headers: {"connection": "Upgrade", "host": "echo.websocket.org", "upgrade": "websocket", "sec-websocket-version": "13", "sec-websocket-key": "rtqVNPpCQaY9OCm4f7XZYQ=="}, body: () }
-- connected!
Got message: Text("Request served by 7811941c69e658")
Got message: Text("{\"task_id\":\"202407262337451c6sowxc36q865nb5p\",\"task_token\":\"bd4c908a626dc75c\"}")
Done!
 --> Request { method: GET, uri: wss://www.itdog.cn/websockets, version: HTTP/1.1, headers: {"connection": "Upgrade", "host": "www.itdog.cn", "upgrade": "websocket", "sec-websocket-version": "13", "sec-websocket-key": "BRSYTnKt3Okkv1F074vBMw=="}, body: () }
Error:
   0: HTTP error: 504 Gateway Timeout

Location:
   src/main.rs:41

Thanks

You're awaiting the writer before even giving the reader a chance to work. Of course this won't receive any bigger messages.

You're awaiting the writer before even giving the reader a chance to work. Of course this won't receive any bigger messages.

There two request in main. I added a connected! log. The second one is stuck at connect_async.
And I don't know what you mean awaiting.

You use the await keyword in your code. This makes the program stop and wait at the point. The WebSocket standard requires reaction on incoming messages, you can't just send and wait. Your code must start receiving before sending anything in order to be ready for incoming messages.

Just do receiving and sending at the same time, not after each other. And start receiving just after connecting. No sending before receiving is running.

You use the await keyword in your code. This makes the program stop and wait at the point. The WebSocket standard requires reaction on incoming messages, you can't just send and wait. Your code must start receiving before sending anything in order to be ready for incoming messages.

Just do receiving and sending at the same time, not after each other. And start receiving just after connecting. No sending before receiving is running.

No, it's not connect yet. The second one not output -- connected!.

What's the point of manually writing WebSocket-related HTTP headers including the key? Tungstenite does it for you. There is no need to over-engineer here. Just provide the URL.

Error 504 is definitely not a Tungstenite bug. It is server-side. It could be that Tungstenite just triggers it somehow.

@araraloren, after reading the issue and the discussion, I realized the whole "issue" could be reduced to a single line of code: connect_async("wss://www.itdog.cn/websockets").await with the question' Why does this fail for this particular server but does not fail for other test servers?' The rest of the code in the provided example is irrelevant.

The answer is: it depends, but oftentimes when we get issues like this, it turns out that the remote server expects specific headers to be set (authentication or whatnot). I would suggest that you look into the server's documentation. If the service provides a working test client (typically one written in JS/TS), check which headers are sent to see where it differs from Tungstenite. Unfortunately we can't do debugging of every third-party server.

I consider this issue closed as there is nothing to do from a library's standpoint. I hope that the provided information brings some clarity and helps you to get further!

PS: If you find an issue, feel free to open one, but please be specific. Sometimes, reading through issues containing a lot of irrelevant information is very time-consuming (especially if an ongoing discussion diverges).

What's the point of manually writing WebSocket-related HTTP headers including the key? Tungstenite does it for you. There is no need to over-engineer here. Just provide the URL.

Yes, I tried pass only url.

@araraloren, after reading the issue and the discussion, I realized the whole "issue" could be reduced to a single line of code: connect_async("wss://www.itdog.cn/websockets").await with the question' Why does this fail for this particular server but does not fail for other test servers?' The rest of the code in the provided example is irrelevant.

The answer is: it depends, but oftentimes when we get issues like this, it turns out that the remote server expects specific headers to be set (authentication or whatnot). I would suggest that you look into the server's documentation. If the service provides a working test client (typically one written in JS/TS), check which headers are sent to see where it differs from Tungstenite. Unfortunately we can't do debugging of every third-party server.

I consider this issue closed as there is nothing to do from a library's standpoint. I hope that the provided information brings some clarity and helps you to get further!

PS: If you find an issue, feel free to open one, but please be specific. Sometimes, reading through issues containing a lot of irrelevant information is very time-consuming (especially if an ongoing discussion diverges).

Thanks.
I've had success with reqwest-websocket which also base on tungstenite. The code is pretty much the same as tokio-tungstenite, but it's totally working. Maybe I am just not lucky with tokio-tungstenite.

pub async fn request_reqw(url: &str) -> color_eyre::Result<()> {
    let web_socket = reqwest_websocket::websocket(url).await?;

    println!("REQW: -- connected!");

    let (mut writer, mut reader) = web_socket.split();

    writer
        .send(reqwest_websocket::Message::Text(String::from(
            r#"{"task_id":"202407262337451c6sowxc36q865nb5p","task_token":"bd4c908a626dc75c"}"#,
        )))
        .await?;

The log:

REQW: -- connected!
REQW: Got message: Text("Request served by 7811941c69e658")
REQW: Got message: Text("{\"task_id\":\"202407262337451c6sowxc36q865nb5p\",\"task_token\":\"bd4c908a626dc75c\"}")
REQW: Done!
REQW: -- connected!
REQW: Got message: Text("{\"type\":\"finished\"}")
REQW: Done!
TOKIO: -- connected!
TOKIO: Got message: Text("Request served by 7811941c69e658")
TOKIO: Got message: Text("{\"task_id\":\"202407262337451c6sowxc36q865nb5p\",\"task_token\":\"bd4c908a626dc75c\"}")
TOKIO: Done!
Error:
   0: HTTP error: 504 Gateway Timeout

Location:
   src/main.rs:28

reqwest-websocket uses the same tungstenite internally. The server just doesn't like your headers for some reason. You can compare headers sent by reqwest-websocket with yours.

reqwest-websocket uses the same tungstenite internally. The server just doesn't like your headers for some reason. You can compare headers sent by reqwest-websocket with yours.

Thanks for your advices.