Skip to content

blocking-http-transport-reqwest silently ignores http.proxy (unlike the curl backend) #3015

Description

Current behavior 😯

When gix is compiled with the blocking reqwest backend (blocking-http-transport-reqwest*) and a proxy is configured in git configuration, the setting is parsed but silently ignored: the request is sent directly, the configured proxy never receives a connection, and nothing reports that the configuration had no effect.

gix-transport/src/client/blocking_io/http/reqwest/ does not reference proxy anywhere.

$ git config --local http.proxy http://127.0.0.1:18080
$ # reqwest backend
$ transport options: proxy=Some("http://127.0.0.1:18080") no_proxy=Some("") ssl_verify=true
$ ref_map: ERROR: An IO error occurred when talking to the server
  caused by: error sending request for url (http://git.invalid/repo.git/info/refs?service=git-upload-pack)
  caused by: client error (Connect)
  caused by: dns error
  caused by: error resolving DNS
$ # ^ the recording proxy on port 18080 logged *no* connection

$ # curl backend, same repository configuration
$ transport options: proxy=Some("http://127.0.0.1:18080") no_proxy=None ssl_verify=true
$ ref_map: ERROR: An IO error occurred when talking to the server
  caused by: Received HTTP status 502
$ # ^ recording proxy logged: CONNECTION / GET http://git.invalid/repo.git/info/refs?service=git-upload-pack HTTP/1.1

Measured with a recording proxy that only logs the first request line and answers 502 (so a request that reaches it is unambiguous):

backend proxy given via request reached the proxy?
curl http.proxy config yes
reqwest http.proxy config no — direct DNS failure
reqwest http_proxy environment variable yes (see workaround below)

So the behaviour depends on the compiled-in backend, and the reqwest case degrades to a direct connection without any diagnostic — the only visible symptom is an unrelated DNS/connection error.

Expected behavior 🤔

http.proxy — and the equivalent gix-specific keys — should be honoured by every blocking HTTP backend, as they already are by the curl backend. If a backend cannot honour them, that should be surfaced (error/warning) or documented as a per-key deviation instead of being dropped silently.

Git behavior

git (via libcurl) honours http.proxy, http.<url>.proxy, remote.<name>.proxy, and the http_proxy/HTTPS_PROXY/ALL_PROXY/no_proxy environment variables. gix's config layer already models this faithfully and fills the shared http::Options — the value simply never reaches the reqwest client.

Steps to reproduce 🕹

  1. Create a repository that configures a proxy pointing at a port where a trivial "recording proxy" listens (it only has to log incoming connections):
    $ git init repo && git -C repo config http.proxy http://127.0.0.1:18080
  2. Run any HTTP request against a host that would only be reachable through that proxy (an unresolvable name is enough, since we only care whether the proxy is contacted at all). With the gix crate, Repository::transport_options() + remote.connect(Direction::Fetch) + Connection::ref_map() is sufficient:
    let repo = gix::open("repo")?;
    let options = repo.transport_options("http://git.invalid/repo.git", None)?; // prints proxy=Some(..)
    let remote = repo.remote_at("http://git.invalid/repo.git")?;
    let connection = remote.connect(gix::remote::Direction::Fetch)?;
    connection.ref_map(gix::progress::Discard, Default::default())
    Cargo features: gix/blocking-http-transport-reqwest-rust-tls versus gix/blocking-http-transport-curl-rustls.
  3. Make sure the proxy is not also inherited from the environment (http_proxy, https_proxy) — otherwise the reqwest backend accidentally works through reqwest's own environment handling and the bug is masked. The curl and reqwest runs above were done with all proxy environment variables unset.

The failure is not specific to a hand-written client: gix parses the value into gix_transport::client::blocking_io::http::Options { proxy: Some(..) } (shown in the output above) and passes it to the transport via TransportWithoutIO::configure(); the reqwest transport stores it and then never reads it.

Where the value is lost

step location (main @ c02041d41) fact
config is parsed correctly gix/src/repository/config/transport.rs:170-221 http.proxy, remote.<name>.proxy, gitoxide.http.proxy/allProxy, gitoxide.http.noProxy → http::Options { proxy, no_proxy, proxy_auth_method, .. }
shared option exists and is documented gix-transport/src/client/blocking_io/http/mod.rs:142-148 pub proxy: Option<String>, pub no_proxy: Option<String>
the config key claims support gix/src/config/tree/sections/http.rs:16-18 http.proxy deviation is only "fails on strings with illformed UTF-8" — nothing backend-specific
upstream tracks it as done issue #470, "http transport configuration" → [x] http proxy settings; issue #450 → http transport configuration (i.e. proxy settings, ...) (#586) both are only true for the curl backend
config reaches the reqwest worker thread reqwest/remote.rs:300 (config: self.config.clone()) → :110-116 (destructured per request) the whole http::Options is available where the request is made
... but is only partially consumed reqwest/remote.rs:290 (extra_headers), :161 (follow_redirects) extraHeader and followRedirects do work — so a blanket "no shared options" reading cannot be right
the client is built without any proxy reqwest/remote.rs:50-108, ClientBuilder::new() at :63 built once inside the worker thread from Default for Remote, i.e. before/without the configuration; no proxy()/no_proxy() call exists
the curl backend does it curl/remote.rs:453-478 handle.proxy(), proxy_type() (incl. socks), handle.noproxy()

The module documentation at gix-transport/src/client/blocking_io/http/mod.rs:31-34 says the backend "doesn't support any of the shared http options yet", but it already honours http.extraHeader and http.followRedirects from that same struct, and no per-key deviation mentions the backend — so the current state is a silent trap rather than a declared limitation.

Relationship to reqwest environment handling

reqwest enables system/environment proxy detection by default, so http_proxy/https_proxy in the environment do work with this backend even today. That is likely why the gap is easy to miss — but the environment is not a substitute for the configuration, and note that gix's own gitoxide.http.noProxy (http/mod.rs:148) is equally ignored, so the env-only path cannot express everything the configuration can.

Environment

  • gix 0.87.1, gix-transport 0.59.2, gitoxide main @ c02041d41 (2026-09-23)
  • Windows 11, rustc 1.98.0
  • feature sets: gix/blocking-http-transport-reqwest-rust-tls vs. gix/blocking-http-transport-curl-rustls

Question

Is this a bug you would like fixed (i.e. the reqwest backend should honour http.proxy/noProxy), or is it a deliberately accepted limitation of the experimental reqwest backend?

If it is the latter, would you accept a documentation-only change that makes the deviation explicit — e.g. stating in the http.proxy / gitoxide.http.noProxy deviations that they currently only take effect with the curl backend — so users are not silently connected directly?


AI disclosure: this issue was written and filed by an AI agent (DeepSeek Harness) on behalf of 沙漠之子 (@maboloshi). Every command, output and code reference above was produced and verified against main at c02041d41 on a local checkout.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions