mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-11-06 15:51:46 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e5d0683ac | ||
|
|
6df971434e | ||
|
|
39c98d4671 | ||
|
|
a1ba73a256 | ||
|
|
453b1b8613 | ||
|
|
a18895f42e | ||
|
|
24e58ce5b2 | ||
|
|
a0ab09fc0a | ||
|
|
290c4c0bbe | ||
|
|
25ba40f6fe | ||
|
|
578e4815b7 |
No files matched your search
+22
-1
@@ -200,6 +200,10 @@ void close(CloseStatus status = CloseStatus::Normal,
|
||||
const std::string &reason = "");
|
||||
bool is_open() const;
|
||||
|
||||
// Authentication - the same setters Client has (Basic wins over Bearer).
|
||||
void set_basic_auth(const std::string &username, const std::string &password);
|
||||
void set_bearer_token_auth(const std::string &token);
|
||||
|
||||
// Proxy - the same setters Client has. connect() opens a CONNECT tunnel
|
||||
// through the proxy for ws:// as well as wss:// (RFC 6455 4.1); the proxy
|
||||
// credentials are sent only on the CONNECT request, never through the tunnel.
|
||||
@@ -233,7 +237,8 @@ void set_address_family(int family);
|
||||
void set_ipv6_v6only(bool on);
|
||||
void set_socket_options(SocketOptions socket_options);
|
||||
void set_interface(const std::string &intf);
|
||||
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
void set_direct_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
void set_tunnel_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
|
||||
// SSL configuration (wss:// only, SSL builds only)
|
||||
void set_ca_cert_path(const std::string &ca_cert_file_path,
|
||||
@@ -403,6 +408,22 @@ svr.set_pre_request_handler([](const httplib::Request &req, httplib::Response &r
|
||||
|
||||
The server accepts a handshake from any `Origin`. A browser sends its cookies with a handshake another site starts, so if cookies authenticate the connection, also check `req.get_header_value("Origin")` against your own origins in one of these handlers (cross-site WebSocket hijacking).
|
||||
|
||||
### Authentication
|
||||
|
||||
```cpp
|
||||
httplib::ws::WebSocketClient ws("wss://example.com/ws");
|
||||
ws.set_bearer_token_auth("token123"); // Authorization: Bearer token123
|
||||
// or
|
||||
ws.set_basic_auth("user", "pass"); // Authorization: Basic dXNlcjpwYXNz
|
||||
|
||||
if (ws.connect()) { /* ... */ }
|
||||
```
|
||||
|
||||
The credentials are read on every `connect()`, so a new token takes effect on
|
||||
the next reconnect. An `Authorization` header passed to the constructor wins
|
||||
over them. Through a proxy they are sent only inside the tunnel, never on
|
||||
`CONNECT`.
|
||||
|
||||
### Custom Headers and Timeouts
|
||||
|
||||
```cpp
|
||||
|
||||
@@ -742,7 +742,7 @@ svr.Post("/multipart", [&](const Request& req, Response& res) {
|
||||
auto safe = httplib::sanitize_filename(file.filename);
|
||||
```
|
||||
|
||||
This function keeps only the part after the last `/` or `\`, removes null bytes, and trims leading and trailing spaces and tabs. It returns an empty string if what is left is empty, `.` or `..`.
|
||||
This function keeps only the part after the last `/` or `\` (or, on Windows, `:`, which starts a drive prefix such as `C:` or an NTFS stream name), removes null bytes, and trims leading and trailing spaces and tabs. It returns an empty string if what is left is empty, `.` or `..`.
|
||||
|
||||
> [!NOTE]
|
||||
> `name` and `filename` are read as quoted-strings (RFC 9110 §5.6.4), where `\"` and `\\` stand for `"` and `\`. Browsers send a backslash as it is, so from a browser, a value with `\\` in it arrives with a single `\`, and one that ends in `\` is not read correctly.
|
||||
@@ -1477,17 +1477,17 @@ The same method is available on `httplib::ws::WebSocketClient`.
|
||||
|
||||
### Override the connection target for a hostname
|
||||
|
||||
`set_hostname_addr_map` redirects where the socket connects, without changing
|
||||
the identity of the request. The hostname the client was constructed with keeps
|
||||
supplying the `Host` header, the SNI, and the name that the server certificate
|
||||
is verified against, so this is a connection-level override only, not a way to
|
||||
talk to a different origin.
|
||||
`set_direct_hostname_addr_map` redirects where the socket connects, without
|
||||
changing the identity of the request. The hostname the client was constructed
|
||||
with keeps supplying the `Host` header, the SNI, and the name that the server
|
||||
certificate is verified against, so this is a connection-level override only,
|
||||
not a way to talk to a different origin.
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://example.com");
|
||||
|
||||
// Connect to this IP address instead of resolving "example.com"
|
||||
cli.set_hostname_addr_map({{"example.com", "192.168.1.10"}});
|
||||
cli.set_direct_hostname_addr_map({{"example.com", "192.168.1.10"}});
|
||||
```
|
||||
|
||||
A mapped value may be an IP literal or another hostname. An IP literal is used
|
||||
@@ -1495,13 +1495,37 @@ as-is; anything else is resolved as a name, so a host that is only reachable
|
||||
under a different name works too:
|
||||
|
||||
```cpp
|
||||
cli.set_hostname_addr_map({{"example.com", "internal.example.lan"}});
|
||||
cli.set_direct_hostname_addr_map({{"example.com", "internal.example.lan"}});
|
||||
```
|
||||
|
||||
An empty value is ignored, leaving the original hostname as the connection
|
||||
target.
|
||||
|
||||
The same method is available on `httplib::ws::WebSocketClient`.
|
||||
The direct map applies only when the client connects to the origin itself.
|
||||
Through a proxy, use `set_tunnel_hostname_addr_map`: its value becomes the
|
||||
`CONNECT` target (and that request's `Host`) that the proxy is asked to connect
|
||||
to (HTTPS and WebSocket). The `Host` header, the SNI and the certificate check
|
||||
inside the tunnel keep the original hostname:
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://sni.example");
|
||||
|
||||
cli.set_tunnel_hostname_addr_map({{"sni.example", "server.example"}});
|
||||
cli.set_proxy("proxy.example", 3128);
|
||||
// CONNECT server.example:443, SNI sni.example
|
||||
```
|
||||
|
||||
The two maps are independent; to use the same target on both paths, set both.
|
||||
They correspond to curl's `--resolve` and `--connect-to`.
|
||||
|
||||
Neither map applies to the proxy's own address ("direct" means not through a
|
||||
proxy, as in a PAC file's `DIRECT`). To connect to the proxy at a fixed
|
||||
address, pass an IP literal to `set_proxy()`.
|
||||
|
||||
`set_hostname_addr_map` is deprecated and works the same as
|
||||
`set_direct_hostname_addr_map`.
|
||||
|
||||
These methods are also available on `httplib::ws::WebSocketClient`.
|
||||
|
||||
### Automatic Path Encoding
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ langs = ["en", "ja"]
|
||||
|
||||
[site]
|
||||
title = "cpp-httplib"
|
||||
version = "0.63.0"
|
||||
version = "0.65.0"
|
||||
hostname = "https://yhirose.github.io"
|
||||
base_path = "/cpp-httplib"
|
||||
footer_message = "© 2026 Yuji Hirose. All rights reserved."
|
||||
|
||||
@@ -214,7 +214,7 @@ svr.listen("/tmp/httplib.sock", 80); // The port is unused, but must not be 0
|
||||
// Client
|
||||
httplib::Client cli("http://localhost");
|
||||
cli.set_address_family(AF_UNIX);
|
||||
cli.set_hostname_addr_map({{"localhost", "/tmp/httplib.sock"}});
|
||||
cli.set_direct_hostname_addr_map({{"localhost", "/tmp/httplib.sock"}});
|
||||
|
||||
auto res = cli.Get("/");
|
||||
```
|
||||
|
||||
@@ -214,7 +214,7 @@ svr.listen("/tmp/httplib.sock", 80); // ポート番号は使われない(0
|
||||
// クライアント
|
||||
httplib::Client cli("http://localhost");
|
||||
cli.set_address_family(AF_UNIX);
|
||||
cli.set_hostname_addr_map({{"localhost", "/tmp/httplib.sock"}});
|
||||
cli.set_direct_hostname_addr_map({{"localhost", "/tmp/httplib.sock"}});
|
||||
|
||||
auto res = cli.Get("/");
|
||||
```
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#ifndef CPPHTTPLIB_HTTPLIB_H
|
||||
#define CPPHTTPLIB_HTTPLIB_H
|
||||
|
||||
#define CPPHTTPLIB_VERSION "0.63.0"
|
||||
#define CPPHTTPLIB_VERSION_NUM "0x003f00"
|
||||
#define CPPHTTPLIB_VERSION "0.65.0"
|
||||
#define CPPHTTPLIB_VERSION_NUM "0x004100"
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
|
||||
@@ -1029,18 +1029,27 @@ inline std::string resolve_relative_location(const std::string &location,
|
||||
// A ':' in the first segment means the value has a scheme.
|
||||
if (location.find(':') < location.find_first_of("/?#")) { return location; }
|
||||
|
||||
if (location[0] == '#') { return base.substr(0, base.find('#')) + location; }
|
||||
// A path starting with "//" would be re-parsed as an authority. Prefix "/."
|
||||
// (as the URL Standard does); remove_dot_segments strips it later.
|
||||
auto on_this_host = [](std::string path) -> std::string {
|
||||
if (path.compare(0, 2, "//") == 0) { path.insert(0, "/."); }
|
||||
return path;
|
||||
};
|
||||
|
||||
if (location[0] == '#') {
|
||||
return on_this_host(base.substr(0, base.find('#')) + location);
|
||||
}
|
||||
|
||||
auto base_path = base.substr(0, base.find_first_of("?#"));
|
||||
if (location[0] == '?') { return base_path + location; }
|
||||
if (location[0] == '?') { return on_this_host(base_path + location); }
|
||||
|
||||
if (base_path.empty() || base_path[0] != '/') { base_path = "/"; }
|
||||
auto merged = base_path.substr(0, base_path.rfind('/') + 1) + location;
|
||||
|
||||
// Remove "." and ".." segments from the merged path.
|
||||
auto path_end = (std::min)(merged.find_first_of("?#"), merged.size());
|
||||
return remove_dot_segments(merged.substr(0, path_end)) +
|
||||
merged.substr(path_end);
|
||||
return on_this_host(remove_dot_segments(merged.substr(0, path_end)) +
|
||||
merged.substr(path_end));
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
@@ -2927,7 +2936,12 @@ public:
|
||||
size_t is_socket_open() const;
|
||||
socket_t socket() const;
|
||||
|
||||
[[deprecated("Use set_direct_hostname_addr_map() instead.")]]
|
||||
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
void
|
||||
set_direct_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
void
|
||||
set_tunnel_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
|
||||
void set_default_headers(Headers headers);
|
||||
|
||||
@@ -3053,12 +3067,19 @@ protected:
|
||||
bool socket_should_be_closed_when_request_is_done_ = false;
|
||||
size_t socket_tls_settings_version_ = 0;
|
||||
|
||||
// Set by stop() while a request is in flight. The request's reads poll it,
|
||||
// because shutting the socket down does not wake a read on Windows.
|
||||
std::atomic<bool> stop_requested_{false};
|
||||
|
||||
std::atomic<size_t> tls_settings_version_{0};
|
||||
|
||||
// Hostname to connection target map. The value is an IP literal or another
|
||||
// hostname; only the connection target changes, never the identity.
|
||||
std::map<std::string, std::string> addr_map_;
|
||||
|
||||
// Hostname to CONNECT target map, used only through a proxy.
|
||||
std::map<std::string, std::string> tunnel_addr_map_;
|
||||
|
||||
// Default headers
|
||||
Headers default_headers_;
|
||||
|
||||
@@ -3348,7 +3369,12 @@ public:
|
||||
size_t is_socket_open() const;
|
||||
socket_t socket() const;
|
||||
|
||||
[[deprecated("Use set_direct_hostname_addr_map() instead.")]]
|
||||
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
void
|
||||
set_direct_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
void
|
||||
set_tunnel_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
|
||||
void set_default_headers(Headers headers);
|
||||
|
||||
@@ -4748,7 +4774,15 @@ public:
|
||||
set_connection_timeout(const std::chrono::duration<Rep, Period> &duration);
|
||||
|
||||
void set_interface(const std::string &intf);
|
||||
[[deprecated("Use set_direct_hostname_addr_map() instead.")]]
|
||||
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
void
|
||||
set_direct_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
void
|
||||
set_tunnel_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||
|
||||
void set_basic_auth(const std::string &username, const std::string &password);
|
||||
void set_bearer_token_auth(const std::string &token);
|
||||
|
||||
void set_proxy(const std::string &host, int port);
|
||||
void set_proxy_basic_auth(const std::string &username,
|
||||
@@ -4820,6 +4854,13 @@ private:
|
||||
// hostname; only the connection target changes, never the identity.
|
||||
std::map<std::string, std::string> addr_map_;
|
||||
|
||||
// Hostname to CONNECT target map, used only through a proxy.
|
||||
std::map<std::string, std::string> tunnel_addr_map_;
|
||||
|
||||
std::string basic_auth_username_;
|
||||
std::string basic_auth_password_;
|
||||
std::string bearer_token_auth_token_;
|
||||
|
||||
std::string proxy_host_;
|
||||
int proxy_port_ = -1;
|
||||
std::string proxy_basic_auth_username_;
|
||||
@@ -6812,10 +6853,14 @@ select_read_interruptible(socket_t sock, time_t sec, time_t usec,
|
||||
while (!interrupted()) {
|
||||
auto left = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
deadline - std::chrono::steady_clock::now());
|
||||
auto wait =
|
||||
(std::max)((std::min)(left, slice), std::chrono::microseconds(0));
|
||||
auto ret = select_impl(sock, POLLIN, 0, static_cast<time_t>(wait.count()));
|
||||
if (ret != 0 || wait < slice) { return ret; }
|
||||
auto last = left < slice;
|
||||
// poll() takes whole milliseconds, so round the last wait up to keep it
|
||||
// from ending before the deadline.
|
||||
auto wait_usec = last ? (std::max)((left.count() + 999) / 1000 * 1000,
|
||||
static_cast<decltype(left.count())>(0))
|
||||
: slice.count();
|
||||
auto ret = select_impl(sock, POLLIN, 0, static_cast<time_t>(wait_usec));
|
||||
if (ret != 0 || last) { return ret; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -9307,6 +9352,7 @@ inline bool redirect(T &cli, Request &req, Response &res,
|
||||
Error &error) {
|
||||
Request new_req = req;
|
||||
new_req.path = path;
|
||||
new_req.params.clear();
|
||||
new_req.redirect_count_ -= 1;
|
||||
|
||||
if (res.status == StatusCode::SeeOther_303 &&
|
||||
@@ -11975,8 +12021,13 @@ inline std::string decode_query_component(const std::string &component,
|
||||
}
|
||||
|
||||
inline std::string sanitize_filename(const std::string &filename) {
|
||||
// Extract basename: find the last path separator (/ or \)
|
||||
// Extract basename: find the last path separator (/ or \) and, on Windows,
|
||||
// ':', which ends a drive prefix ("C:..") or names an NTFS stream ("a:b")
|
||||
#ifdef _WIN32
|
||||
auto pos = filename.find_last_of("/\\:");
|
||||
#else
|
||||
auto pos = filename.find_last_of("/\\");
|
||||
#endif
|
||||
auto result =
|
||||
(pos != std::string::npos) ? filename.substr(pos + 1) : filename;
|
||||
|
||||
@@ -12826,7 +12877,8 @@ inline ssize_t SocketStream::read(char *ptr, size_t size) {
|
||||
inline ssize_t SocketStream::write(const char *ptr, size_t size) {
|
||||
if (!wait_writable()) { return -1; }
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
// Winsock's send() takes an int length, even on 64-bit Windows
|
||||
#ifdef _WIN32
|
||||
size =
|
||||
(std::min)(size, static_cast<size_t>((std::numeric_limits<int>::max)()));
|
||||
#endif
|
||||
@@ -13041,6 +13093,17 @@ make_host_and_port_string_always_port(const std::string &host, int port) {
|
||||
return prepare_host_string(host) + ":" + std::to_string(port);
|
||||
}
|
||||
|
||||
// Request target of a CONNECT for `host`, with `addr_map` applied.
|
||||
inline std::string
|
||||
make_connect_request_target(const std::map<std::string, std::string> &addr_map,
|
||||
const std::string &host, int port) {
|
||||
std::string connect_host;
|
||||
std::string ip;
|
||||
apply_addr_map(addr_map, host, connect_host, ip);
|
||||
return make_host_and_port_string_always_port(ip.empty() ? connect_host : ip,
|
||||
port);
|
||||
}
|
||||
|
||||
// Value for the Host header a client sends when the caller supplied none.
|
||||
// Only the value: callers decide where in their header list it goes.
|
||||
inline std::string make_default_host_header_value(const std::string &host,
|
||||
@@ -15230,7 +15293,10 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
|
||||
if ((req.method == "GET" || req.method == "HEAD") &&
|
||||
req.has_header("Range")) {
|
||||
const auto &range_header_value = req.get_header_value("Range");
|
||||
if (!detail::parse_range_header(range_header_value, req.ranges)) {
|
||||
// RFC 9110 14.2: a range unit other than bytes must be ignored
|
||||
auto unit = range_header_value.substr(0, range_header_value.find('='));
|
||||
if (detail::case_ignore::equal(unit, "bytes") &&
|
||||
!detail::parse_range_header(range_header_value, req.ranges)) {
|
||||
connection_closed = true;
|
||||
res.status = StatusCode::RangeNotSatisfiable_416;
|
||||
output_error_log(Error::InvalidRangeHeader, &req);
|
||||
@@ -15759,6 +15825,9 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
|
||||
// socket.
|
||||
socket_should_be_closed_when_request_is_done_ = false;
|
||||
|
||||
// A redirect or a retry sent inside a request keeps its stop()
|
||||
if (socket_requests_in_flight_ == 0) { stop_requested_ = false; }
|
||||
|
||||
if (!socket_reusable()) {
|
||||
if (!ensure_socket_connection(socket_, error)) { return false; }
|
||||
|
||||
@@ -15806,6 +15875,7 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
|
||||
});
|
||||
|
||||
ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {
|
||||
strm.set_read_interrupt([this] { return stop_requested_.load(); });
|
||||
return handle_request(strm, req, res, close_connection, error);
|
||||
});
|
||||
|
||||
@@ -16569,6 +16639,7 @@ inline void ClientImpl::setup_redirect_client(ClientType &client) {
|
||||
|
||||
// Copy network and socket settings
|
||||
client.addr_map_ = addr_map_;
|
||||
client.tunnel_addr_map_ = tunnel_addr_map_;
|
||||
client.set_address_family(address_family_);
|
||||
client.set_tcp_nodelay(tcp_nodelay_);
|
||||
client.set_ipv6_v6only(ipv6_v6only_);
|
||||
@@ -18023,6 +18094,9 @@ inline void ClientImpl::stop() {
|
||||
// are not thread-safe.
|
||||
if (socket_requests_in_flight_ > 0) {
|
||||
shutdown_socket(socket_);
|
||||
// shutdown() does not wake a read waiting in WSAPoll on Windows, so the
|
||||
// request's reads also give up once they see this flag.
|
||||
stop_requested_ = true;
|
||||
|
||||
// Aside from that, we set a flag for the socket to be closed when we're
|
||||
// done.
|
||||
@@ -18081,9 +18155,19 @@ inline void ClientImpl::set_path_encode(bool on) { path_encode_ = on; }
|
||||
|
||||
inline void
|
||||
ClientImpl::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
|
||||
set_direct_hostname_addr_map(std::move(addr_map));
|
||||
}
|
||||
|
||||
inline void ClientImpl::set_direct_hostname_addr_map(
|
||||
std::map<std::string, std::string> addr_map) {
|
||||
addr_map_ = std::move(addr_map);
|
||||
}
|
||||
|
||||
inline void ClientImpl::set_tunnel_hostname_addr_map(
|
||||
std::map<std::string, std::string> addr_map) {
|
||||
tunnel_addr_map_ = std::move(addr_map);
|
||||
}
|
||||
|
||||
inline void ClientImpl::set_default_headers(Headers headers) {
|
||||
default_headers_ = std::move(headers);
|
||||
}
|
||||
@@ -18792,7 +18876,17 @@ inline socket_t Client::socket() const { return cli_->socket(); }
|
||||
|
||||
inline void
|
||||
Client::set_hostname_addr_map(std::map<std::string, std::string> addr_map) {
|
||||
cli_->set_hostname_addr_map(std::move(addr_map));
|
||||
cli_->set_direct_hostname_addr_map(std::move(addr_map));
|
||||
}
|
||||
|
||||
inline void Client::set_direct_hostname_addr_map(
|
||||
std::map<std::string, std::string> addr_map) {
|
||||
cli_->set_direct_hostname_addr_map(std::move(addr_map));
|
||||
}
|
||||
|
||||
inline void Client::set_tunnel_hostname_addr_map(
|
||||
std::map<std::string, std::string> addr_map) {
|
||||
cli_->set_tunnel_hostname_addr_map(std::move(addr_map));
|
||||
}
|
||||
|
||||
inline void Client::set_default_headers(Headers headers) {
|
||||
@@ -19160,6 +19254,10 @@ inline bool SSLClient::connect_with_proxy(
|
||||
std::chrono::time_point<std::chrono::steady_clock> start_time,
|
||||
Response &res, bool &success, Error &error) {
|
||||
success = true;
|
||||
auto connect_target =
|
||||
detail::make_connect_request_target(tunnel_addr_map_, host_, port_);
|
||||
auto retargeted = connect_target !=
|
||||
detail::make_host_and_port_string_always_port(host_, port_);
|
||||
Response proxy_res;
|
||||
if (!detail::process_client_socket(
|
||||
socket.sock, read_timeout_sec_, read_timeout_usec_,
|
||||
@@ -19167,8 +19265,8 @@ inline bool SSLClient::connect_with_proxy(
|
||||
start_time, [&](Stream &strm) {
|
||||
Request req2;
|
||||
req2.method = "CONNECT";
|
||||
req2.path =
|
||||
detail::make_host_and_port_string_always_port(host_, port_);
|
||||
req2.path = connect_target;
|
||||
if (retargeted) { req2.set_header("Host", connect_target); }
|
||||
if (max_timeout_msec_ > 0) {
|
||||
req2.start_time_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
@@ -19207,8 +19305,8 @@ inline bool SSLClient::connect_with_proxy(
|
||||
start_time, [&](Stream &strm) {
|
||||
Request req3;
|
||||
req3.method = "CONNECT";
|
||||
req3.path = detail::make_host_and_port_string_always_port(
|
||||
host_, port_);
|
||||
req3.path = connect_target;
|
||||
if (retargeted) { req3.set_header("Host", connect_target); }
|
||||
req3.headers.insert(detail::make_digest_authentication_header(
|
||||
req3, make_request_target(req3), auth, 1,
|
||||
detail::random_string(10), proxy_digest_auth_username_,
|
||||
@@ -21040,6 +21138,11 @@ inline ErrorCode map_mbedtls_error(int ret, int &out_errno,
|
||||
out_errno = errno;
|
||||
return ErrorCode::SyscallError;
|
||||
}
|
||||
if (ret == MBEDTLS_ERR_SSL_CONN_EOF) {
|
||||
// EOF without close_notify
|
||||
out_errno = 0;
|
||||
return ErrorCode::SyscallError;
|
||||
}
|
||||
if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
|
||||
// Unlike OpenSSL/wolfSSL, Mbed TLS folds the CN/SAN identity check into
|
||||
// the handshake's chain verification (see set_sni()); a mismatch there
|
||||
@@ -21127,7 +21230,7 @@ inline int mbedtls_net_recv_cb(void *ctx, unsigned char *buf, size_t len) {
|
||||
return MBEDTLS_ERR_NET_RECV_FAILED;
|
||||
}
|
||||
#endif
|
||||
if (ret == 0) { return MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY; }
|
||||
// EOF is MBEDTLS_ERR_SSL_CONN_EOF; only a close_notify is a clean end
|
||||
return static_cast<int>(ret);
|
||||
}
|
||||
|
||||
@@ -21783,10 +21886,8 @@ inline ssize_t read(session_t session, void *buf, size_t len, TlsError &err) {
|
||||
return static_cast<ssize_t>(ret);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
err.code = ErrorCode::PeerClosed;
|
||||
return 0;
|
||||
}
|
||||
// 0 means EOF without close_notify, which is an error as on OpenSSL
|
||||
if (ret == 0) { ret = MBEDTLS_ERR_SSL_CONN_EOF; }
|
||||
|
||||
err.code = impl::map_mbedtls_error(ret, err.sys_errno, 0);
|
||||
err.backend_code = static_cast<uint64_t>(-ret);
|
||||
@@ -22393,6 +22494,8 @@ inline ErrorCode map_wolfssl_error(WOLFSSL *ssl, int ssl_error,
|
||||
case SSL_ERROR_WANT_WRITE: return ErrorCode::WantWrite;
|
||||
case SSL_ERROR_ZERO_RETURN: return ErrorCode::PeerClosed;
|
||||
case SSL_ERROR_SYSCALL: out_errno = errno; return ErrorCode::SyscallError;
|
||||
// EOF without close_notify
|
||||
case SOCKET_PEER_CLOSED_E: out_errno = 0; return ErrorCode::SyscallError;
|
||||
default:
|
||||
if (ssl) {
|
||||
// wolfSSL stores the low-level error code as a negative value.
|
||||
@@ -22956,6 +23059,9 @@ inline ssize_t read(session_t session, void *buf, size_t len, TlsError &err) {
|
||||
}
|
||||
|
||||
auto wsession = static_cast<impl::WolfSSLSession *>(session);
|
||||
constexpr auto max_len =
|
||||
static_cast<size_t>((std::numeric_limits<int>::max)());
|
||||
if (len > max_len) { len = max_len; }
|
||||
int ret = wolfSSL_read(wsession->ssl, buf, static_cast<int>(len));
|
||||
|
||||
if (ret > 0) {
|
||||
@@ -22963,13 +23069,10 @@ inline ssize_t read(session_t session, void *buf, size_t len, TlsError &err) {
|
||||
return static_cast<ssize_t>(ret);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
err.code = ErrorCode::PeerClosed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 0 is also returned for EOF without close_notify; get_error tells them apart
|
||||
int ssl_error = wolfSSL_get_error(wsession->ssl, ret);
|
||||
err.code = impl::map_wolfssl_error(wsession->ssl, ssl_error, err.sys_errno);
|
||||
if (err.code == ErrorCode::PeerClosed) { return 0; }
|
||||
err.backend_code = static_cast<uint64_t>(ssl_error);
|
||||
impl::wolfssl_last_error() = err.backend_code;
|
||||
return -1;
|
||||
@@ -23926,6 +24029,9 @@ inline void WebSocketClient::prepare_default_headers(Request &req) {
|
||||
}
|
||||
|
||||
detail::add_default_user_agent_header(req);
|
||||
detail::add_authorization_header(req, basic_auth_username_,
|
||||
basic_auth_password_,
|
||||
bearer_token_auth_token_, false);
|
||||
}
|
||||
|
||||
inline Result WebSocketClient::connect() {
|
||||
@@ -24051,9 +24157,19 @@ inline void WebSocketClient::set_interface(const std::string &intf) {
|
||||
|
||||
inline void WebSocketClient::set_hostname_addr_map(
|
||||
std::map<std::string, std::string> addr_map) {
|
||||
set_direct_hostname_addr_map(std::move(addr_map));
|
||||
}
|
||||
|
||||
inline void WebSocketClient::set_direct_hostname_addr_map(
|
||||
std::map<std::string, std::string> addr_map) {
|
||||
addr_map_ = std::move(addr_map);
|
||||
}
|
||||
|
||||
inline void WebSocketClient::set_tunnel_hostname_addr_map(
|
||||
std::map<std::string, std::string> addr_map) {
|
||||
tunnel_addr_map_ = std::move(addr_map);
|
||||
}
|
||||
|
||||
inline bool WebSocketClient::is_proxy_enabled() const {
|
||||
if (proxy_host_.empty() || proxy_port_ == -1) { return false; }
|
||||
if (no_proxy_entries_.empty()) { return true; }
|
||||
@@ -24103,7 +24219,8 @@ inline bool WebSocketClient::connect_with_proxy(Response &proxy_res,
|
||||
Error &error) {
|
||||
Request req;
|
||||
req.method = "CONNECT";
|
||||
req.path = detail::make_host_and_port_string_always_port(host_, port_);
|
||||
req.path =
|
||||
detail::make_connect_request_target(tunnel_addr_map_, host_, port_);
|
||||
req.headers.emplace("Host", req.path);
|
||||
detail::add_default_user_agent_header(req);
|
||||
detail::add_authorization_header(req, proxy_basic_auth_username_,
|
||||
@@ -24137,6 +24254,16 @@ inline bool WebSocketClient::connect_with_proxy(Response &proxy_res,
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void WebSocketClient::set_basic_auth(const std::string &username,
|
||||
const std::string &password) {
|
||||
basic_auth_username_ = username;
|
||||
basic_auth_password_ = password;
|
||||
}
|
||||
|
||||
inline void WebSocketClient::set_bearer_token_auth(const std::string &token) {
|
||||
bearer_token_auth_token_ = token;
|
||||
}
|
||||
|
||||
inline void WebSocketClient::set_proxy(const std::string &host, int port) {
|
||||
proxy_host_ = host;
|
||||
proxy_port_ = port;
|
||||
|
||||
+772
-20
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user