Repository navigation
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.1Measured 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 🕹
- 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 - 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
gixcrate,Repository::transport_options()+remote.connect(Direction::Fetch)+Connection::ref_map()is sufficient:Cargo features: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())
gix/blocking-http-transport-reqwest-rust-tlsversusgix/blocking-http-transport-curl-rustls. - Make sure the proxy is not also inherited from the environment (
http_proxy,https_proxy) — otherwise thereqwestbackend accidentally works through reqwest's own environment handling and the bug is masked. Thecurlandreqwestruns 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
gix0.87.1,gix-transport0.59.2, gitoxidemain@c02041d41(2026-09-23)- Windows 11, rustc 1.98.0
- feature sets:
gix/blocking-http-transport-reqwest-rust-tlsvs.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.
Activity
fix(gix-transport): honor proxy configuration with `reqwest` (#3015)
fix(gix-transport): honor proxy configuration with `reqwest` (#3015)
fix(gix-transport): honor proxy configuration with `reqwest` (#3015)
fix(gix-transport): honor proxy configuration with `reqwest` (#3015)
fix(gix-transport): honor proxy configuration with `reqwest` (#3015)
fix(gix-transport): honor proxy configuration with `reqwest` (#3015)
fix(gix-transport): honor proxy configuration with `reqwest` (#3015)