Skip to content

Cargo doesn't attempt to negotiate HTTP/2 on windows-gnu #15721

@Josh194

Description

@Josh194

Problem

In testing both with a custom registry and crates.io (at least on my system), cargo does not use HTTP/2 at all, failing to even attempt to negotiate the protocol via ALPN on HTTPS connections, or use the HTTP/2 upgrade headers for HTTP ones. See my notes/solutions below for much more info.

These tests were made both with a default cargo config and with http.multiplexing explicitly set to true. I am using cargo 1.88.0, which according to the output from CARGO_LOG was built with a version of curl not considered 'broken' for the purposes of cargo's internal disables_multiplexing_for_bad_curl(), and that was built with HTTP/2 support.

(network version details from `CARGO_LOG`)
network: Version {
    version: "8.12.1-DEV",
    rust_crate_version: "0.4.47",
    rust_sys_crate_version: "0.4.80+curl-8.12.1",
    vendored: true,
    host: "unknown",
    feature_ipv6: true,
    feature_ssl: true,
    feature_libz: true,
    feature_ntlm: false,
    feature_gss_negotiate: false,
    feature_debug: false,
    feature_spnego: true,
    feature_largefile: true,
    feature_idn: false,
    feature_sspi: true,
    feature_async_dns: true,
    feature_conv: false,
    feature_tlsauth_srp: false,
    feature_ntlm_wb: false,
    feature_unix_domain_socket: true,
    feature_https_proxy: true,
    feature_altsvc: true,
    feature_zstd: false,
    feature_unicode: false,
    feature_http3: false,
    feature_http2: true,
    feature_gsasl: false,
    feature_brotli: false,
    ssl_version: "Schannel",
    libz_version: "1.3.1",
    iconv_version_num: "0",
    brotli_version_num: "0",
    nghttp2_version_num: "14000",
    nghttp2_version: "1.64.0",
    zstd_ver_num: "0",
    protocols: [
        "file",
        "http",
        "https",
        "mqtt",
        "ws",
        "wss",
    ],
}

I would consider this a bug as the docs for http.multiplexing specifically state that cargo will attempt to use HTTP/2, which for an HTTPS connection should mean negotiation via ALPN.

Steps

(may be very system-specific)

  • create a cargo config with http.multiplexing and http.debug set
  • set CARGO_LOG=network=trace
  • perform any basic cargo command (eg search), observe that HTTP/1.1 is used

Possible Solution(s)

(notes from testing and looking through the code)

It has been nearly impossible for me to actually perform any debugging on cargo for this, since I am on windows and cannot find a single debugger that actually manages to stop on breakpoints in the tests while being reasonably usable. That being said, from looking through the source and spamming prints everywhere, here are some notes:

  • Cargo, the crate itself, looks like it is using HTTP/2 internally for some things
    try_old_curl_http2_pipewait!() sets HTTP/2 explicitly on a curl handle (if using multiplexing), and is used in several places, for example HttpRegistry::load(), where it is used to query some (registry indices?) information from a registry. In my very limited testing I was unable to actually find a test that triggers that code though.
  • Many of the core cargo commands call into crates-io's Registry struct to perform the final requests
    These generally delegate down to Registry::req() which currently never sets http_version() explicitly as far as I can see, all the way down to the eventual call to handle.perform().
  • If I forcibly set set http_version() in Registry::req(), HTTP/2 negotiation does seem to occur
    I can't be 100% sure that this works for HTTPS since I haven't tested, but at least HTTP connections (as generated by some tests) now generate proper HTTP/2 upgrade headers as per Wireshark (they did not before), so I'm quite sure it would.
  • Registry receives its curl handle from cargo::ops::registry()#174
    registries are constructed via new_handle(), which as far as I can tell is only called here
    This handle is passed unmodified from a call to cargo::...::http::http_handle().
  • http_handle() does not explicitly set the HTTP version
    http_handle() generates handles from Easy::new() along with some extra config mostly set in configure_http_handle(). configure_http_handle(), as far as I can see, never sets http_version(). I don't think this handle is ever exposed again outside of the Registry, so this is probably the last chance.

If I had to guess, http_version() should probably be set in configure_http_handle() (maybe with try_old_curl_http2_pipewait!()?). I'm a bit unsure about actually submitting a PR for this as again, I essentially cannot debug cargo on my system, I can't really run the container tests (since windows), and about half the tests currently fail for me before any modifications (ie from a fresh checkout). I could maybe run them in GH actions on a fork I guess?

Notes

I discovered this issue as I'm currently trying to set up a basic cargo web API registry, but have so far been unable to actually get cargo to connect to it properly. I've tracked down the issue to the fact that cargo does not advertise a single protocol via ALPN (connecting over HTTPS), and my registry is HTTP/2 only, resulting in cargo plowing forwards with a completely unsupported protocol. Specifically, I know that no protocols are being advertised (in fact, ALPN extensions are not even present in the TLS ClientHello) since it is not mentioned in the logs if I enable http.debug and set CARGO_LOG=network=trace, and I've also looked at the entire interaction through Wireshark.

In my testing, this occurs for essentially everything, ie every cargo command (including fetching .crate files), and to both my own registry and crates.io, but I'm hesitant to say that this is actually affecting everyone since I've only tested on my own system, and the docs seem to indicate that HTTP/2 is supposed to be supported (again, as per http.multiplexing).

Version

cargo 1.88.0 (873a06493 2025-05-10)
release: 1.88.0
commit-hash: 873a0649350c486caf67be772828a4f36bb4734c
commit-date: 2025-05-10
host: x86_64-pc-windows-gnu
libgit2: 1.9.0 (sys:0.20.0 vendored)
libcurl: 8.12.1-DEV (sys:0.4.80+curl-8.12.1 vendored ssl:Schannel)
os: Windows 10.0.19045 (Windows 10 Home) [64-bit]

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-networkingArea: networking issues, curl, etc.C-bugCategory: bugO-windowsOS: WindowsS-needs-infoStatus: Needs more info, such as a reproduction or more background for a feature request.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions