mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +00:00
Bring the READMEs and the docs pages (en and ja) in line with the current header: - Fix the client sample that does not compile, the Transfer-Encoding statement for default headers, the verifier callback comment, the thread pool spawn rule, the sanitize_filename description, and the inverted Accept-Encoding sentence. - Describe recent behaviour: TLS errors behind Read/Write failures, Host validation, Connection: close for a body without length, content_reader rules, refused CONNECT, WebSocketClient proxies, CPPHTTPLIB_INTERIM_RESPONSE_MAX_COUNT, the SSE event size limit, and the payload limit of the Stream API. - Complete the API references of WebSocket, WebSocketClient and the stream:: helpers, and the SSE reconnect rules. - Fix cookbook pages: keychain loading on every backend, received Close status, a multipart sample that trusted the client's file name, an echo sample that sent binary as text, SNI, pre-request handler scope, stop() with WebSocket handlers, and the thread pool notes. - Restore the mutex in the LLM app's Chapter 5 code.
104 lines
4.1 KiB
Markdown
104 lines
4.1 KiB
Markdown
---
|
|
title: "C16. Send Requests Through a Proxy"
|
|
order: 16
|
|
status: "draft"
|
|
---
|
|
|
|
To route traffic through a corporate network or a specific path, send requests via an HTTP proxy. Just pass the proxy host and port to `set_proxy()`.
|
|
|
|
## Basic usage
|
|
|
|
```cpp
|
|
httplib::Client cli("https://api.example.com");
|
|
cli.set_proxy("proxy.internal", 8080);
|
|
|
|
auto res = cli.Get("/users");
|
|
```
|
|
|
|
The request goes through the proxy. For HTTPS, the client uses the CONNECT method to tunnel through — no extra setup required.
|
|
|
|
If the proxy refuses the tunnel, the request fails with `Error::ProxyConnection`. The exception is a 407: it comes back as the response, so `res` is truthy and `res->status` is 407, while `res.error()` still returns `Error::ProxyConnection`.
|
|
|
|
## Proxy authentication
|
|
|
|
If the proxy itself requires authentication, use `set_proxy_basic_auth()` or `set_proxy_bearer_token_auth()`.
|
|
|
|
```cpp
|
|
cli.set_proxy("proxy.internal", 8080);
|
|
cli.set_proxy_basic_auth("user", "password");
|
|
```
|
|
|
|
```cpp
|
|
cli.set_proxy_bearer_token_auth("token");
|
|
```
|
|
|
|
If cpp-httplib is built with OpenSSL (or another TLS backend), you can also use Digest authentication for the proxy.
|
|
|
|
```cpp
|
|
cli.set_proxy_digest_auth("user", "password");
|
|
```
|
|
|
|
## Combine with end-server authentication
|
|
|
|
Proxy authentication is separate from authenticating to the end server ([C05. Use Basic authentication](../c05-basic-auth), [C06. Call an API with a Bearer token](../c06-bearer-token)). When both are needed, set both.
|
|
|
|
```cpp
|
|
cli.set_proxy("proxy.internal", 8080);
|
|
cli.set_proxy_basic_auth("proxy-user", "proxy-pass");
|
|
|
|
cli.set_bearer_token_auth("api-token"); // for the end server
|
|
```
|
|
|
|
`Proxy-Authorization` is sent to the proxy, `Authorization` to the end server.
|
|
|
|
## Bypass the proxy for specific hosts
|
|
|
|
You often want internal endpoints to skip the proxy. Configure a bypass list with `set_no_proxy()`.
|
|
|
|
```cpp
|
|
cli.set_proxy("proxy.internal", 8080);
|
|
cli.set_no_proxy({"internal.corp", "10.0.0.0/8", ".dev.local"});
|
|
```
|
|
|
|
Each entry is one of:
|
|
|
|
- `*` — bypass the proxy for all hosts
|
|
- a hostname suffix (e.g. `example.com`) — matches `example.com` itself and any subdomain (`foo.example.com`). A leading dot 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`)
|
|
|
|
Hostname matching is case-insensitive and uses a dot-boundary rule, so an entry of `example.com` does **not** match `evilexample.com`. IP comparisons are normalized through `inet_pton`, so `127.0.0.1` cannot be bypassed via alternate string forms (e.g. `127.000.000.001`). When an entry matches, the `Proxy-Authorization` header is suppressed as well.
|
|
|
|
Malformed entries are silently dropped. Port-specific entries such as `example.com:8080` are not supported (cpp-httplib's other host-keyed APIs are also keyed on hostname only).
|
|
|
|
## Use a proxy for WebSocket
|
|
|
|
`ws::WebSocketClient` has the same proxy setters.
|
|
|
|
```cpp
|
|
httplib::ws::WebSocketClient cli("wss://ws.example.com/chat");
|
|
cli.set_proxy("proxy.internal", 8080);
|
|
cli.set_proxy_basic_auth("user", "password");
|
|
|
|
auto res = cli.connect();
|
|
```
|
|
|
|
For both `ws://` and `wss://`, the client opens a tunnel with the CONNECT method and then performs the WebSocket handshake through it. `set_no_proxy()` works the same way.
|
|
|
|
## Read proxy settings from the environment
|
|
|
|
cpp-httplib doesn't touch `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` on its own — the config API is always explicit, the same way `set_ca_cert_path()` is. If you'd like that behavior, read the variables in your application and feed them to `set_proxy()` and `set_no_proxy()`.
|
|
|
|
```cpp
|
|
if (const char *v = std::getenv("no_proxy")) {
|
|
std::vector<std::string> patterns;
|
|
std::stringstream ss(v);
|
|
for (std::string item; std::getline(ss, item, ',');) {
|
|
if (!item.empty()) { patterns.push_back(item); }
|
|
}
|
|
cli.set_no_proxy(patterns);
|
|
}
|
|
```
|
|
|
|
If you also read `HTTP_PROXY` yourself, honor the lowercase `http_proxy` only. The uppercase form is poisoned in CGI/FastCGI environments by the `Proxy:` request header ([CVE-2016-5385 / "httpoxy"](https://httpoxy.org/)). `HTTPS_PROXY` and `NO_PROXY` are safe in either case because their names don't begin with `HTTP_`.
|