From 60cb6f5896d76d41d7aeb6f45eaadf7b9e8a14c6 Mon Sep 17 00:00:00 2001 From: yhirose Date: Fri, 8 May 2026 23:21:47 +0900 Subject: [PATCH] Document set_no_proxy and set_proxy_from_env in README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two subsections under "Proxy server support": - "Bypass the proxy for specific hosts (NO_PROXY)" — set_no_proxy, pattern syntax, dot-boundary rule, IP normalization, limitations (no port-specific entries, no v4-mapped v6 cross-match, replace semantics). - "Read proxy settings from the environment" — set_proxy_from_env, which variables are read, the lowercase-only http_proxy rule with an inline httpoxy / CVE-2016-5385 explanation, threading expectations. Documentation only. Closes the doc gap from #2446. --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/README.md b/README.md index 1037ab2..730ee57 100644 --- a/README.md +++ b/README.md @@ -1181,6 +1181,76 @@ cli.set_proxy_bearer_token_auth("pass"); > [!NOTE] > OpenSSL is required for Digest Authentication. +#### Bypass the proxy for specific hosts (`NO_PROXY`) + +`set_no_proxy` configures a bypass list. Each pattern is one of: + +- `*` — bypass the proxy for every target +- a hostname suffix, e.g. `example.com` — matches `example.com` and any + subdomain (`foo.example.com`). A leading dot (`.example.com`) is + permitted but informational; both forms are equivalent. +- a single IP literal, e.g. `192.168.1.1`, `::1` +- a CIDR block, e.g. `10.0.0.0/8`, `fe80::/10` + +```cpp +cli.set_proxy("proxy.corp", 3128); +cli.set_no_proxy({"internal.corp", "10.0.0.0/8", "*.dev.local"}); +``` + +When a NO_PROXY entry matches the target host, the request is sent +directly and the corresponding `Proxy-Authorization` header is +suppressed. + +Hostname matching is **case-insensitive** and uses a dot-boundary rule, +so `evilexample.com` does **not** match an entry of `example.com`. IP +comparisons are normalized through `inet_pton`, so `127.0.0.1` cannot +be bypassed via alternate string forms (e.g. leading-zero octets or +decimal-form integers). Malformed entries are silently dropped. + +Limitations: + +- Port-specific entries (`example.com:8080`) are not supported. cpp-httplib's + other host-keyed APIs (e.g. `set_hostname_addr_map`) are also keyed on + hostname only; supporting host:port for NO_PROXY alone would be + inconsistent. +- IPv4-mapped IPv6 addresses (`::ffff:1.2.3.4`) are not cross-matched + against IPv4 NO_PROXY entries. +- `set_no_proxy` replaces any previously configured list; there is no + append API. + +#### Read proxy settings from the environment + +`set_proxy_from_env` configures the client from proxy-related +environment variables. + +```cpp +httplib::Client cli("https://api.example.com"); +cli.set_proxy_from_env(); +``` + +Variables read: + +- `https_proxy` / `HTTPS_PROXY` — used by HTTPS clients (`SSLClient`) +- `http_proxy` (lowercase only — see security note below) — used by HTTP clients +- `no_proxy` / `NO_PROXY` — comma-separated list of bypass patterns + +Returns `true` if at least one variable was found and applied. + +> [!IMPORTANT] +> The uppercase `HTTP_PROXY` is intentionally ignored to mitigate the +> "httpoxy" class of bugs ([CVE-2016-5385](https://httpoxy.org/)). In +> CGI / FastCGI environments the variable name collides with the +> `HTTP_*` namespace used to expose request headers, allowing a remote +> attacker to set the proxy URL via the `Proxy:` request header. +> cpp-httplib follows curl, Go, and Python `requests` in honoring only +> the lowercase `http_proxy`. `HTTPS_PROXY` and `NO_PROXY` are safe in +> either case because their names do not begin with `HTTP_`. + +> [!NOTE] +> `set_proxy_from_env` reads `getenv` synchronously. Call it once at +> startup before issuing any requests; concurrent `setenv` from other +> threads is undefined. + ### Range ```cpp