snapview / tungstenite-rs

Lightweight stream-based WebSocket implementation for Rust.
Apache License 2.0
1.93k stars 221 forks source link

Can't connect to server #86

Closed GoWebProd closed 5 years ago

GoWebProd commented 5 years ago

Hello, I'm trying to connect to ws server, but receive strage error: Http(200)

Connection code:

let req = Request {
    url: url::Url::parse("wss://cupis.winline.ru/?client=site").unwrap(),
    extra_headers: Some(vec![
        ("Origin".into(), "https://winline.ru".into()),
    ]),
};

let (mut ws_stream, _) = connect_async(req).await.expect("Failed to connect to winline ws");

Trace:

[2019-10-23T19:58:14Z TRACE mio::poll] registering with poller
[2019-10-23T19:58:14Z TRACE mio::sys::unix::kqueue] registering; token=Token(0); interests=Readable | Writable | Error | Hup
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::handshake] Setting ctx when starting handshake
[2019-10-23T19:58:14Z TRACE tungstenite::handshake::client] Client handshake initiated.
[2019-10-23T19:58:14Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:91 Write.write
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:45 AllowStd.with_context
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:96 Write.with_context write -> poll_write
[2019-10-23T19:58:14Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:71 Read.read
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:45 AllowStd.with_context
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:76 Read.with_context read -> poll_read
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::handshake] Setting context in handshake
[2019-10-23T19:58:14Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:71 Read.read
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:45 AllowStd.with_context
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:76 Read.with_context read -> poll_read
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::handshake] Setting context in handshake
[2019-10-23T19:58:14Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:71 Read.read
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:45 AllowStd.with_context
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:76 Read.with_context read -> poll_read
[2019-10-23T19:58:14Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:71 Read.read
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:45 AllowStd.with_context
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:76 Read.with_context read -> poll_read
[2019-10-23T19:58:14Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:71 Read.read
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:45 AllowStd.with_context
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:76 Read.with_context read -> poll_read
[2019-10-23T19:58:14Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:71 Read.read
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:45 AllowStd.with_context
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:76 Read.with_context read -> poll_read
[2019-10-23T19:58:14Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:71 Read.read
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:45 AllowStd.with_context
[2019-10-23T19:58:14Z TRACE tokio_tungstenite::compat] /Users/a.mayorsky/.cargo/git/checkouts/tokio-tungstenite-42c59291ea04b35c/0c27799/src/compat.rs:76 Read.with_context read -> poll_read
[2019-10-23T19:58:14Z TRACE mio::poll] deregistering handle with poller
thread 'main' panicked at 'Failed to connect to winline ws: Io(Custom { kind: Other, error: "" })', src/libcore/result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
daniel-abramov commented 5 years ago

Could you reproduce this issue with one of the stable issues from crates.io? Or at least on master?

Judging from the output you've posted, you're using an experimental tokio-tungstenite version with async syntax, which is not even a branch in tokio-tungstenite, but rather a WIP pull request which is still being reviewed and won't get to master until new tokio gets officially released as stable.

GoWebProd commented 5 years ago

Yes, this reproduces on stable: tungstenite = { version="0.9.1", features=["tls"] }

use tungstenite::{connect, Message};
use url::Url;

fn main() {
    env_logger::init();

    let (mut socket, response) =
        connect(Url::parse("wss://cupis.winline.ru/?client=site").unwrap()).expect("Can't connect");

    println!("Connected to the server");
    println!("Response HTTP code: {}", response.code);
    println!("Response contains the following headers:");
    for &(ref header, _ /*value*/) in response.headers.iter() {
        println!("* {}", header);
    }

    socket
        .write_message(Message::Text("Hello WebSocket".into()))
        .unwrap();
    loop {
        let msg = socket.read_message().expect("Error reading message");
        println!("Received: {}", msg);
    }
}

Errors:

[2019-10-24T09:18:18Z DEBUG tungstenite::client] Trying to contact wss://cupis.winline.ru/?client=site at 178.248.236.86:443...
[2019-10-24T09:18:19Z TRACE tungstenite::handshake::client] Client handshake initiated.
[2019-10-24T09:18:19Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-24T09:18:19Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-24T09:18:19Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-24T09:18:19Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-24T09:18:19Z TRACE tungstenite::handshake::machine] Doing handshake round.
[2019-10-24T09:18:19Z TRACE tungstenite::handshake::machine] Doing handshake round.
thread 'main' panicked at 'Can't connect: Http(200)', src/libcore/result.rs:1165:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
daniel-abramov commented 5 years ago

Ok, I also checked it locally on my machine and it seems like your server violates a WebSocket protocol and sends HTTP 200 instead of the HTTP 101 as defined in RFC 6455.

agalakhov commented 5 years ago

From RFC6455:

The handshake from the server is much simpler than the client handshake. The first line is an HTTP Status-Line, with the status code 101:

    HTTP/1.1 101 Switching Protocols

Any status code other than 101 indicates that the WebSocket handshake has not completed and that the semantics of HTTP still apply. ... These fields are checked by the WebSocket client for scripted pages. If the |Sec-WebSocket-Accept| value does not match the expected value, if the header field is missing, or if the HTTP status code is not 101, the connection will not be established, and WebSocket frames will not be sent.

And from the normative section:

If the server chooses to accept the incoming connection, it MUST reply with a valid HTTP response indicating the following.

  1. A Status-Line with a 101 response code as per RFC 2616 [RFC2616]. Such a response could look like "HTTP/1.1 101 Switching Protocols".

Please fix your server. Sorry, according to the RFC this is only doable on server side:

If the server's response does not conform to the requirements for the server's handshake as defined in this section and in Section 4.2.2, the client MUST _Fail the WebSocket Connection_.

And that's what actually happens.

GoWebProd commented 5 years ago

It's not my server and browser correct establish ws connection. Other ws library (golang) also.

GoWebProd commented 5 years ago

Curl also receive valid response:

curl -v -H 'Upgrade: websocket' -H 'Host: cupis.winline.ru' -H 'Sec-WebSocket-Key: 6LZ7Fhp91mZW2mn8zrCuEQ==' -H 'Sec-WebSocket-Version: 13' -X GET 'https://cupis.winline.ru/?client=site'\n                                                                 ✘  14:38:13
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 178.248.236.86...
* TCP_NODELAY set
* Connected to cupis.winline.ru (178.248.236.86) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / DHE-RSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=*.winline.ru
*  start date: Apr 24 00:00:00 2019 GMT
*  expire date: Jun 22 12:00:00 2020 GMT
*  subjectAltName: host "cupis.winline.ru" matched cert's "*.winline.ru"
*  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=RapidSSL RSA CA 2018
*  SSL certificate verify ok.
> GET /?client=siten HTTP/1.1
> Host: cupis.winline.ru
> User-Agent: Mozilla/5.0 Gecko
> Accept: */*
> Referer:
> Upgrade: websocket
> Sec-WebSocket-Key: 6LZ7Fhp91mZW2mn8zrCuEQ==
> Sec-WebSocket-Version: 13
>
< HTTP/1.1 101 Web Socket Protocol Handshake
< Upgrade: WebSocket
< Connection: Upgrade
< Sec-WebSocket-Accept: pplgeCJtBbaXFvCSBPWe68RzKDA=
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: content-type
< Access-Control-Allow-Headers: authorization
< Access-Control-Allow-Headers: x-websocket-extensions
< Access-Control-Allow-Headers: x-websocket-version
< Access-Control-Allow-Headers: x-websocket-protocol
<
daniel-abramov commented 5 years ago

Alright, then there must be some difference in the headers. I've just made a comparison, it's clear that the Connection: upgrade header causes the issue. The server reacts to it in a different way. I checked the RFC and I think the reason is that Upgrade is case-sensitive. Apparently this part is not checked by most servers (or by autobahn tests). I've just fixed the issue and now it seems to work.

@GoWebProd let me know if there are any issues with the new version (you can test it against the version from our master or wait until @agalakhov publishes the crate).