snapview / tokio-tungstenite

Future-based Tungstenite for Tokio. Lightweight stream-based WebSocket implementation
MIT License
1.88k stars 236 forks source link

Using a Tor connector from `arti-client` #301

Closed Alorel closed 11 months ago

Alorel commented 1 year ago

I'm trying to make my websocket connection use Tor using arti-client and keep getting stuck on the handshake with WebSocket protocol error: Handshake not finished - could you point me in the right direction please?

My understanding of Tungstenite's init process for wss endpoints, assuming native-tls is used:

Therefore, if I construct my Tor stream in place of the TCP stream, create the NativeTls connector myself, connect & pass that stream on to client_async (without _tls) I should be able to connect to my wss endpoint the same way as with the regular tls connector, shouldn't I?

The arti-client crate has an integration with hyper and an associated connector, but it uses APIs from tls_api_native_tls which don't appear to be compatible.

Things I've confirmed to be working:

My connect code ```rust let (ws, _) = { let host = if let Some(host) = cfg.uri.authority() { host.as_str() } else { return Err(anyhow::Error::msg("URI missing host")); }; let port = if let Some(p) = cfg.uri.port() { p.as_u16() } else { 443 }; let mut req = Request::builder().uri(cfg.uri.clone()) .header::<_, &'static str>(header::USER_AGENT, USER_AGENT) .header::<_, &'static str>(header::UPGRADE, "websocket") .header::<_, &'static str>(header::CONNECTION, "Upgrade") .header::<_, &'static str>(header::SEC_WEBSOCKET_VERSION, "13") .header::<_, &str>(header::HOST, host) .header::<_, String>(header::SEC_WEBSOCKET_KEY, BASE64.encode(rand::random::<[u8; 16]>())); if let Some(c) = cfg.cookie { req = req.header::<_, String>(header::COOKIE, c); } let tls_stream = { let tor_stream = tor_client.connect((host, port)).await?; let tls_conn = TlsConnector::from(native_tls::TlsConnector::new()?); // wrap native tls in tokio-native-tls let tls_stream = tls_conn.connect(host, tor_stream).await?; MaybeTlsStream::NativeTls(tls_stream) }; tokio_tungstenite::client_async(req.body(())?, tls_stream) }.await?; // we time out here let (tx, rx) = ws.split(); ```

If I work with the raw TCP stream and forego tungstenite, I'm able to write & flush the handshake headers, but not read the response.

daniel-abramov commented 1 year ago

This does not look like a bug on our side so far, though I wonder why it did not work on your side and if there are any specifics that your client stream expects (do you ensure that you read from the stream?). Have you checked what happens over the wire (with Wireshark or something)?

Also, I don't quite understand the step with a TLS connector - is there a reason to not use the provided client_async_tls()?

If I work with the raw TCP stream and forego tungstenite, I'm able to write & flush the handshake headers, but not read the response.

Hmm, maybe this is the reason why tungstenite does not work? 🙂 What happens when you try reading from the stream? Do you get any errors? - From your description, it looks like there is either a bug in a library that you're using or there is a certain bug in a user code (something that the library expects the user to perform in order to properly work).