mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-11-11 16:40:21 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdcd2ac6e6 | ||
|
|
bd23a7b179 | ||
|
|
6d8dec3c3e |
No files matched your search
@@ -1716,6 +1716,8 @@ cli.set_address_family(AF_UNIX);
|
||||
|
||||
This library automatically sets the Host header to "localhost" for Unix socket connections, similar to curl's behavior.
|
||||
|
||||
With `set_follow_location(true)`, a redirect is followed over the same socket, whatever host the `Location` names, as curl's `--unix-socket` does. A host mapped with `set_direct_hostname_addr_map` goes to its mapped socket instead. Only a `Location` naming `localhost` counts as the same host; any other host is treated as cross-host, so `Authorization` and `Cookie` are not sent to it.
|
||||
|
||||
|
||||
URI Encoding/Decoding Utilities
|
||||
-------------------------------
|
||||
|
||||
@@ -4,7 +4,7 @@ langs = ["en", "ja"]
|
||||
|
||||
[site]
|
||||
title = "cpp-httplib"
|
||||
version = "0.65.0"
|
||||
version = "0.66.0"
|
||||
hostname = "https://yhirose.github.io"
|
||||
base_path = "/cpp-httplib"
|
||||
footer_message = "© 2026 Yuji Hirose. All rights reserved."
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#ifndef CPPHTTPLIB_HTTPLIB_H
|
||||
#define CPPHTTPLIB_HTTPLIB_H
|
||||
|
||||
#define CPPHTTPLIB_VERSION "0.65.0"
|
||||
#define CPPHTTPLIB_VERSION_NUM "0x004100"
|
||||
#define CPPHTTPLIB_VERSION "0.66.0"
|
||||
#define CPPHTTPLIB_VERSION_NUM "0x004200"
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
|
||||
@@ -12638,7 +12638,16 @@ inline bool ThreadPool::enqueue(std::function<void()> fn) {
|
||||
if (jobs_.size() > idle_thread_count_ &&
|
||||
threads_.size() + dynamic_threads_.size() < max_thread_count_) {
|
||||
cleanup_finished_threads();
|
||||
dynamic_threads_.emplace_back(std::thread([this]() { worker(true); }));
|
||||
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
dynamic_threads_.emplace_back(std::thread([this]() { worker(true); }));
|
||||
#ifndef CPPHTTPLIB_NO_EXCEPTIONS
|
||||
} catch (const std::system_error &) {
|
||||
// No thread budget left: keep the job queued for an existing worker
|
||||
// instead of letting the exception escape the accept loop.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13093,14 +13102,22 @@ make_host_and_port_string_always_port(const std::string &host, int port) {
|
||||
return prepare_host_string(host) + ":" + std::to_string(port);
|
||||
}
|
||||
|
||||
// Where a client connects for `host`, as one string: the mapped address or
|
||||
// name, or `host` itself.
|
||||
inline std::string
|
||||
addr_map_target(const std::map<std::string, std::string> &addr_map,
|
||||
const std::string &host) {
|
||||
std::string connect_host;
|
||||
std::string ip;
|
||||
apply_addr_map(addr_map, host, connect_host, ip);
|
||||
return ip.empty() ? connect_host : ip;
|
||||
}
|
||||
|
||||
// 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,
|
||||
return make_host_and_port_string_always_port(addr_map_target(addr_map, host),
|
||||
port);
|
||||
}
|
||||
|
||||
@@ -16488,8 +16505,13 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
|
||||
|
||||
auto path = std::move(next_path) + next_query;
|
||||
|
||||
// A Unix socket client sends "Host: localhost", so a Location naming
|
||||
// localhost names this client too.
|
||||
auto is_same_host = next_host == host_ ||
|
||||
(address_family_ == AF_UNIX && next_host == "localhost");
|
||||
|
||||
// Same host redirect - use current client
|
||||
if (next_scheme == scheme && next_host == host_ && next_port == port_) {
|
||||
if (next_scheme == scheme && is_same_host && next_port == port_) {
|
||||
return detail::redirect(*this, req, res, path, location, error);
|
||||
}
|
||||
|
||||
@@ -16640,6 +16662,14 @@ 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_;
|
||||
|
||||
// A Unix socket client reaches every host through its own socket, as curl's
|
||||
// --unix-socket does, unless the caller mapped that host elsewhere. The
|
||||
// Location's host is never taken as a socket path.
|
||||
if (address_family_ == AF_UNIX) {
|
||||
auto &target = client.addr_map_[client.host()];
|
||||
if (target.empty()) { target = detail::addr_map_target(addr_map_, host_); }
|
||||
}
|
||||
client.set_address_family(address_family_);
|
||||
client.set_tcp_nodelay(tcp_nodelay_);
|
||||
client.set_ipv6_v6only(ipv6_v6only_);
|
||||
|
||||
@@ -291,6 +291,76 @@ TEST_F(UnixSocketTest, HostHeaderAutoSet) {
|
||||
EXPECT_EQ(received_host_header, "localhost");
|
||||
}
|
||||
|
||||
TEST_F(UnixSocketTest, RedirectStaysOnSocket) {
|
||||
// Another socket in the working directory, which a Location can name as
|
||||
// its host
|
||||
const auto other_path = "other-" + pathname_.substr(2);
|
||||
|
||||
httplib::Server svr;
|
||||
svr.Get("/redirect", [&](const httplib::Request &, httplib::Response &res) {
|
||||
// An absolute Location naming the Host this client sends
|
||||
res.set_redirect("http://localhost" + pattern_);
|
||||
});
|
||||
svr.Get("/cross", [&](const httplib::Request &, httplib::Response &res) {
|
||||
res.set_redirect("http://" + other_path + pattern_);
|
||||
});
|
||||
svr.Get("/mapped", [&](const httplib::Request &, httplib::Response &res) {
|
||||
res.set_redirect("http://mapped.test" + pattern_);
|
||||
});
|
||||
svr.Get(pattern_, [&](const httplib::Request &req, httplib::Response &res) {
|
||||
res.set_header("X-Auth", req.get_header_value("Authorization"));
|
||||
res.set_content(content_, "text/plain");
|
||||
});
|
||||
|
||||
httplib::Server other;
|
||||
other.Get(pattern_, [&](const httplib::Request &, httplib::Response &res) {
|
||||
res.set_content("other", "text/plain");
|
||||
});
|
||||
|
||||
std::thread t{[&] {
|
||||
ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(pathname_, 80));
|
||||
}};
|
||||
std::thread t2{[&] {
|
||||
ASSERT_TRUE(other.set_address_family(AF_UNIX).listen(other_path, 80));
|
||||
}};
|
||||
auto se = detail::scope_exit([&] {
|
||||
svr.stop();
|
||||
other.stop();
|
||||
t.join();
|
||||
t2.join();
|
||||
std::remove(other_path.c_str());
|
||||
});
|
||||
|
||||
svr.wait_until_ready();
|
||||
other.wait_until_ready();
|
||||
|
||||
httplib::Client cli{pathname_};
|
||||
cli.set_address_family(AF_UNIX);
|
||||
cli.set_follow_location(true);
|
||||
cli.set_bearer_token_auth("token");
|
||||
|
||||
// Another host: the same socket, without the credentials
|
||||
auto res = cli.Get("/cross");
|
||||
ASSERT_TRUE(res) << "error: " << res.error();
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
EXPECT_EQ(content_, res->body);
|
||||
EXPECT_EQ("", res->get_header_value("X-Auth"));
|
||||
|
||||
// localhost is this client's own host
|
||||
res = cli.Get("/redirect");
|
||||
ASSERT_TRUE(res) << "error: " << res.error();
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
EXPECT_EQ(content_, res->body);
|
||||
EXPECT_EQ("Bearer token", res->get_header_value("X-Auth"));
|
||||
|
||||
// A host the caller mapped to a socket goes there
|
||||
cli.set_direct_hostname_addr_map({{"mapped.test", other_path}});
|
||||
res = cli.Get("/mapped");
|
||||
ASSERT_TRUE(res) << "error: " << res.error();
|
||||
EXPECT_EQ(StatusCode::OK_200, res->status);
|
||||
EXPECT_EQ("other", res->body);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
TEST(SocketStream, wait_writable_UNIX) {
|
||||
int fds[2];
|
||||
|
||||
Reference in new issue
Block a user