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

Http Proxy Support (+ relevant tokio-tungstenite discussion) #258

Open UE2020 opened 3 years ago

UE2020 commented 3 years ago

Is it possible to proxy a connection using http proxies?

vi commented 3 years ago

Maybe.

If you want to connect using HTTP CONNECT proxy, you need to supply custom underlying channel instead of usual automatic TCP for the implementation.

If you expect to the proxy to hangle HTTP upgrades properly and send something like GET ws://host/path HTTP/1.1, you'll probably need to either modify the library; or to provide HTTP handling part yourself and only use this library for WebSocket message format encoding/decoding.

Note that this library is not well-maintained, and you should try tungstenite and its satellites first.

qm3ster commented 2 years ago

An example of going through HTTP proxy to a secure websocket (with tokio and tungstenite, obviously, as prescribed by the previous commenter):

let tcp = TcpStream::connect("proxy_domain:80").await?;
let (mut request_sender, conn) = conn::handshake(tcp).await?;
let conn = tokio::spawn(conn.without_shutdown());
// create an HTTP method CONNECT request, port mandatory, even if 80 or 443
let req = Request::connect("domain.tld:443")
    .header("Proxy-Authorization", format!("Basic {}", proxy_base64))
    .body(())?;
let res = request_sender.send_request(req).await?;
assert!(response.status() == StatusCode::OK);
// `into_parts` panics if the connection is HTTP/2! Which might be negotiated if the proxy_domain is https! So maybe specify to use HTTP 1.1  above! (we don't worry this time, we are connecting to HTTP, on port 80. Does `without_shutdown` solve that instead? can we do a HTTP/2 connection to the proxy? find out next time on dragonballz
// unwrapping the joinhandle against panic, then the withoutshutdown
let tcp = connection.await??.io;
let req = Request::get("wss://domain.tld/path?query")
    .header("Sec-WebSocket-Key", tungstenite::handshake::client::generate_key())
    .header("Connection", "Upgrade")
    .header("Upgrade", "websocket")
    .header("Sec-WebSocket-Version", "13")
    .header("Host", "domain.tld")
    // technically not required, but many servers will demand it, so we are basically spoofing that we came from a webpage (can't have path though, just "origin" ...or is it "authority"? Idk I forgor. Don't remember which places `user:password@` goes and goesn't... ¯\_(ツ)_/¯   )
    .header("Origin", "https://domain.tld")
    .body(())?;
let (mut ws_stream, _) = tokio_tungstenite::client_async_tls(req, tcp).await?;

Note all the different variants of urls, don't mix them up.

UE2020 commented 2 years ago

Thank you very much! FYI, I don't think you're supposed to include ws:// in the Origin header.

qm3ster commented 2 years ago

@UE2020 you have to include the schema/protocol in a non-null Origin: header, but perhaps https:// would be more appropriate than wss:// here(you wrote ws, I had wss). As in "we came here to upgrade to websocket from your https site". I will change my example now.

Praying commented 2 years ago

@UE2020 you have to include the schema/protocol in a non-null Origin: header, but perhaps https:// would be more appropriate than wss:// here(you wrote ws, I had wss). As in "we came here to upgrade to websocket from your https site". I will change my example now.

It's Ok, but do you have any experience with async-socks5 which maybe easier?

qm3ster commented 2 years ago
  1. What is "Ok"? 🤣
  2. No, I don't have experience with async-socks5, but async_socks5::connect fast_socks5::client::Socks5Stream and tokio_socks::tcp::Socks5Stream should all work.
Praying commented 2 years ago

But I don't know how to write the code, do you have any example?

vi commented 2 years ago

async-socks5

It is based on Tokio 1, but async in current version of rust-websocket is based on legacy Tokio 0.1.

qm3ster commented 2 years ago

This Issue is derailed and we are talking about tokio_tungstenite here instead :v

UE2020 commented 2 years ago

This Issue is derailed and we are talking about tokio_tungstenite here instead :v

To be fair, the README of this crate suggests tungstenite as an alternative.