Skip to content

fix(gix-transport): honour http.proxy in the blocking reqwest backend - #3016

Closed
沙漠之子 (maboloshi) wants to merge 1 commit into
GitoxideLabs:mainfrom
maboloshi:fix/reqwest-proxy
Closed

沙漠之子 (maboloshi) wants to merge 1 commit into
GitoxideLabs:mainfrom
maboloshi:fix/reqwest-proxy

Conversation

@maboloshi

@maboloshi maboloshi commented Sep 23, 2026 •

Copy link
Copy Markdown

Fixes #3015.

What happens today

http.proxy (and the gix-specific proxy keys) are parsed by gix into
gix_transport::client::blocking_io::http::Options and delivered to every HTTP backend through
TransportWithoutIO::configure(), but the blocking reqwest backend only ever read extra_headers and
follow_redirects from them. A configured proxy was silently ignored: the request went out directly and the
only symptom was an unrelated DNS/connection error. The curl backend honours the same values.

The reason is structural: proxies are configured on the reqwest client (not on a single request), and the
client was built in Default for Remote before the first request - and therefore the configuration - existed.

What this changes

  • The client is now built from the configuration of the first request, and rebuilt whenever the client-level
    part of the configuration changes (ClientConfig, currently proxy + no_proxy).
  • proxy is applied to the client:
    • None → behaviour unchanged, i.e. reqwest keeps using its own environment-based proxy detection
      (which is also why http_proxy happens to work today);
    • Some("") → proxying is disabled entirely, as git defines it, which also undoes an environment proxy;
    • Some(v) → the scheme is optional and defaults to http (curl-style, matching what gix's own config
      layer produces), then reqwest::Proxy::all(v). An explicitly configured proxy replaces the
      environment/system proxy, as documented by reqwest.
  • no_proxy is attached via Proxy::no_proxy(NoProxy::from_string(..)); * already means "bypass
    everything" and is handled by reqwest.
  • Schemes other than http/https (e.g. socks5) are rejected with a new Error::UnsupportedProxyScheme
    instead of being handed to a connector that cannot handle them.

Tests

New tests in gix-transport/tests/blocking-transport-http-reqwest.rs bind a TcpListener on an ephemeral
port as a stand-in proxy that records the request line, and request http://git.invalid/repo - a host that
cannot be resolved, so a request can only arrive through the proxy:

  • proxy_configuration_is_used
  • proxy_without_scheme_defaults_to_http
  • proxy_scheme_is_case_insensitive
  • no_proxy_configuration_bypasses_the_proxy
  • unsupported_proxy_scheme_is_reported

cargo test -p gix-transport --features http-client-reqwest passes (39 + 9 tests). Reverting only the source
change, while keeping the tests, makes all of them fail except no_proxy_configuration_bypasses_the_proxy -
that one passed trivially before the fix because nothing was proxied at all, and now guards the noProxy
wiring together with the proxy tests.

Known limitation of this shape: no_proxy is attached to the proxy this backend builds, so a noProxy from
configuration cannot filter a proxy that reqwest picks up from the environment (the environment variable
NO_PROXY still works there, because reqwest reads it itself). Filtering the system proxy would need a
custom matcher, which seemed out of scope for this fix.

Not covered here

  • Proxy authentication: Options::proxy_authenticate is a gix-credentials callback, while reqwest only
    offers basic_auth for proxies - that needs its own design.
  • socks5/socks5h/socks4* proxies: gix-transport deliberately depends on reqwest with
    default-features = false, so supporting them means enabling reqwest/socks behind a new cargo feature.
    The new error at least makes the limitation explicit instead of connecting somewhere nonsensical.
  • The remaining ignored shared options (connect_timeout is still hard-coded to 20s, plus user_agent,
    ssl_verify, ssl_ca_info, ssl_version, http_version and the low-speed limits). Same class of
    problem; happy to follow up if you'd like them in scope.

AI disclosure: this PR was written and filed by an AI agent (DeepSeek Harness) on behalf of 沙漠之子 (@maboloshi). All
tests and commands were run locally against c02041d41.

Copilot AI lite review requested due to automatic review settings September 23, 2026 17:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Address the case-insensitive scheme handling and bound the no_proxy test’s direct connection time.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

This PR updates the blocking reqwest backend to honor configured HTTP proxies and no_proxy settings.

Changes:

  • Lazily builds and refreshes clients from proxy configuration.
  • Validates supported proxy schemes.
  • Adds proxy behavior regression tests and documentation updates.
File Summary
gix-transport/​tests/​blocking-transport-http-reqwest.rs Adds proxy tests; the no_proxy test may stall up to the connect timeout.
gix-transport/​src/​client/​blocking_io/​http/​reqwest/​remote.rs Applies proxy configuration; scheme validation should be case-insensitive.
gix-transport/​src/​client/​blocking_io/​http/​mod.rs Updates documentation for supported backend options.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Some((scheme, rest)) => (scheme, format!("{scheme}://{rest}")),
None => ("http", format!("http://{proxy}")),
};
if !matches!(scheme, "http" | "https") {
…kend

The `reqwest` backend received the shared HTTP options - including `proxy`
and `no_proxy` - via `TransportWithoutIO::configure()`, stored them, and then
never read them. A configured proxy was therefore silently ignored and the
request was sent directly, which only surfaced as an unrelated DNS or
connection error. The `curl` backend has always honoured the same values.

Proxies are configured on the `reqwest` client rather than on a single
request, and the client was built in `Default for Remote`, before the first
request and thus before any configuration was available. It is now built
lazily from the configuration of the first request, and rebuilt whenever the
client-level part of the configuration changes.

The proxy value is interpreted the way `curl` does it: the scheme is optional
and defaults to `http`, and an empty value disables proxying entirely (which
also undoes a proxy from the environment, as `git` defines it). `no_proxy` is
attached to the configured proxy. Schemes are matched case-insensitively, as
they are in URLs generally, and schemes other than `http` and `https` are
rejected with a dedicated error instead of being passed to a connector that
cannot handle them.

That fixes GitoxideLabs#3015
@maboloshi

Copy link
Copy Markdown
Author

Addressed the review:

  • Proxy schemes are now compared case-insensitively, so HTTP://127.0.0.1:8080 is accepted. It is covered by a
    new proxy_scheme_is_case_insensitive test, which fails if the comparison becomes strict again.
  • no_proxy_configuration_bypasses_the_proxy no longer depends on DNS: it targets a local port that refuses
    connections, so the direct path fails immediately instead of waiting on a resolver. It also fails if noProxy
    stops being applied, because the request would then reach the stand-in proxy.

cargo fmt -p gix-transport -- --check and cargo clippy -p gix-transport --features http-client-reqwest --all-targets are clean,
and cargo test -p gix-transport --features http-client-reqwest passes (39 + 9 tests).

AI disclosure: this comment was written and posted by an AI agent (DeepSeek Harness) on behalf of 沙漠之子 (@maboloshi).

@Byron

Byron commented Sep 24, 2026

Copy link
Copy Markdown
Member

Superseded by #3017.

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

Successfully merging this pull request may close these issues.

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

3 participants