Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting HTTP version is overridden by ALPN #2116

Open
nihaals opened this issue Jan 29, 2024 · 1 comment
Open

Setting HTTP version is overridden by ALPN #2116

nihaals opened this issue Jan 29, 2024 · 1 comment

Comments

@nihaals
Copy link
Contributor

nihaals commented Jan 29, 2024

MWE

[dependencies]
reqwest = { version = "0.11.23", default-features = false, features = ["rustls-tls"] }
tokio = { version = "1.35.1", features = ["full"] }
#[tokio::main]
async fn main() {
    let client = reqwest::Client::new();
    let version = client.get("https://www.google.com/")
        .version(reqwest::Version::HTTP_11)
        .send()
        .await
        .unwrap()
        .version();
    println!("{:?}", version);
}

This gives HTTP/2.0 and when enabling rustls tracing you can see that it is setting the protocol to HTTP/2 based on ALPN.

@cxw620
Copy link
Contributor

cxw620 commented Feb 23, 2024

I check source code.

rustls will send ClientHello with both protocols h2 & http/1.1 by default (It seems that if you don't set http_version_pref when building reqwest client) and server side replies with ServerHelloPayload with only protocols h2, which will be saved to CommonState.

then let's see here RustlsTlsConn:

reqwest/src/connect.rs

Lines 861 to 869 in e639bdc

impl<T: Connection + AsyncRead + AsyncWrite + Unpin> Connection for RustlsTlsConn<T> {
fn connected(&self) -> Connected {
if self.inner.get_ref().1.alpn_protocol() == Some(b"h2") {
self.inner.get_ref().0.connected().negotiated_h2()
} else {
self.inner.get_ref().0.connected()
}
}
}

You can see it read CommonState for alpn, and certainly you will fail to .version(reqwest::Version::HTTP_11).
image

I also check native-tls backend with the following MWE, this issue stills.

[dependencies]
reqwest = { version = "0.11.24", features = ["rustls", "native-tls-alpn", "socks"] }
tokio = { version = "1.36.0", features = ["full"] }

[patch.crates-io]
# patch to fix #2118 
reqwest = { git = 'https://github.com/cxw620/lib_reqwest.git', rev = '8a8f839483a00e7d855cac35f5d49cd7df4e8abd' }
#[tokio::main]
async fn main() {
    let client = reqwest::Client::builder()
        .use_native_tls()
        // .use_rustls_tls()
        // .proxy(reqwest::Proxy::all("socks5://127.0.0.1:20023").unwrap())
        // .danger_accept_invalid_certs(true)
        .build()
        .unwrap();
    let version = client
        .get("https://www.google.com/")
        .version(reqwest::Version::HTTP_11)
        .send()
        .await
        .unwrap()
        .version();
    println!("{:?}", version); // get HTTP/2.0 instead of HTTP/1.1
}

This issue will be hard to resolve.

P.S.:
Through testing, I even found a new problem in branch hyper-v1 that it seems that when using native-tls backend reqwest will not perform alpn negotiation, and socks5 proxy doesn't work with error hyper::Error(Parse(Version)) (when use Charles), while rustls backend works well...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants