Compare commits

...

7 Commits

Author SHA1 Message Date
yhirose ca5fe354fb Release v0.23.1 2025-07-16 17:59:52 -04:00
Benjamin Gilbert dd98d2a24d build(meson): warn/fail on 32-bit machines (#2181)
On 32-bit Windows, meson setup fails with an unclear error:

    meson.build:25:16: ERROR: Could not get define 'CPPHTTPLIB_VERSION'

The actual problem is that httplib.h #errors out.

Have the Meson logic explicitly check for a 32-bit host and warn or error,
matching the check in httplib.h.  Phrase the Windows error in a way that
triggers WrapDB CI's unsupported architecture check.
2025-07-16 12:47:51 -04:00
yhirose 1f110b54d8 Chang #error to #warning for the 32-bit environment check except 32-bit Windows 2025-07-11 22:44:29 -04:00
yhirose 7b6867bcdf Fix #2021 (#2180) 2025-07-10 22:01:41 -04:00
yhirose 8b28577ec6 Resolve #366 2025-07-10 01:07:44 -04:00
yhirose 55b38907dc Resolve #1264 2025-07-10 00:58:52 -04:00
yhirose 53ea9e8bb4 Fix #2111 (#2179) 2025-07-10 00:47:45 -04:00
4 changed files with 158 additions and 5 deletions
+40 -1
View File
@@ -871,7 +871,7 @@ auto res = cli.Post(
httplib::Client cli(url, port);
// prints: 0 / 000 bytes => 50% complete
auto res = cli.Get("/", [](uint64_t len, uint64_t total) {
auto res = cli.Get("/", [](size_t len, size_t total) {
printf("%lld / %lld bytes => %d%% complete\n",
len, total,
(int)(len*100/total));
@@ -990,6 +990,25 @@ auto res = cli.Get("/already%20encoded/path"); // Use pre-encoded paths
- `true` (default): Automatically encodes spaces, plus signs, newlines, and other special characters
- `false`: Sends paths as-is without encoding (useful for pre-encoded URLs)
### Performance Note for Local Connections
> [!WARNING]
> On Windows systems with improperly configured IPv6 settings, using "localhost" as the hostname may cause significant connection delays (up to 2 seconds per request) due to DNS resolution issues. This affects both client and server operations. For better performance when connecting to local services, use "127.0.0.1" instead of "localhost".
>
> See: https://github.com/yhirose/cpp-httplib/issues/366#issuecomment-593004264
```cpp
// May be slower on Windows due to DNS resolution delays
httplib::Client cli("localhost", 8080);
httplib::Server svr;
svr.listen("localhost", 8080);
// Faster alternative for local connections
httplib::Client cli("127.0.0.1", 8080);
httplib::Server svr;
svr.listen("127.0.0.1", 8080);
```
Compression
-----------
@@ -1065,6 +1084,8 @@ cli.set_address_family(AF_UNIX);
"my-socket.sock" can be a relative path or an absolute path. Your application must have the appropriate permissions for the path. You can also use an abstract socket address on Linux. To use an abstract socket address, prepend a null byte ('\x00') to the path.
This library automatically sets the Host header to "localhost" for Unix socket connections, similar to curl's behavior:
URI Encoding/Decoding Utilities
-------------------------------
@@ -1141,6 +1162,24 @@ Serving HTTP on 0.0.0.0 port 80 ...
NOTE
----
### Regular Expression Stack Overflow
> [!CAUTION]
> When using complex regex patterns in route handlers, be aware that certain patterns may cause stack overflow during pattern matching. This is a known issue with `std::regex` implementations and affects the `dispatch_request()` method.
>
> ```cpp
> // This pattern can cause stack overflow with large input
> svr.Get(".*", handler);
> ```
>
> Consider using simpler patterns or path parameters to avoid this issue:
>
> ```cpp
> // Safer alternatives
> svr.Get("/users/:id", handler); // Path parameters
> svr.Get(R"(/api/v\d+/.*)", handler); // More specific patterns
> ```
### g++
g++ 4.8 and below cannot build this library since `<regex>` in the versions are [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
+68 -4
View File
@@ -8,7 +8,8 @@
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_VERSION "0.23.0"
#define CPPHTTPLIB_VERSION "0.23.1"
#define CPPHTTPLIB_VERSION_NUM "0x001701"
/*
* Platform compatibility check
@@ -18,10 +19,10 @@
#error \
"cpp-httplib doesn't support 32-bit Windows. Please use a 64-bit compiler."
#elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ < 8
#error \
#warning \
"cpp-httplib doesn't support 32-bit platforms. Please use a 64-bit compiler."
#elif defined(__SIZEOF_SIZE_T__) && __SIZEOF_SIZE_T__ < 8
#error \
#warning \
"cpp-httplib doesn't support platforms where size_t is less than 64 bits."
#endif
@@ -3215,6 +3216,23 @@ inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
template <bool Read>
inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
#ifdef __APPLE__
if (sock >= FD_SETSIZE) { return -1; }
fd_set fds, *rfds, *wfds;
FD_ZERO(&fds);
FD_SET(sock, &fds);
rfds = (Read ? &fds : nullptr);
wfds = (Read ? nullptr : &fds);
timeval tv;
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
return handle_EINTR([&]() {
return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);
});
#else
struct pollfd pfd;
pfd.fd = sock;
pfd.events = (Read ? POLLIN : POLLOUT);
@@ -3222,6 +3240,7 @@ inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
return handle_EINTR([&]() { return poll_wrapper(&pfd, 1, timeout); });
#endif
}
inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
@@ -3234,6 +3253,36 @@ inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
time_t usec) {
#ifdef __APPLE__
if (sock >= FD_SETSIZE) { return Error::Connection; }
fd_set fdsr, fdsw;
FD_ZERO(&fdsr);
FD_ZERO(&fdsw);
FD_SET(sock, &fdsr);
FD_SET(sock, &fdsw);
timeval tv;
tv.tv_sec = static_cast<long>(sec);
tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
auto ret = handle_EINTR([&]() {
return select(static_cast<int>(sock + 1), &fdsr, &fdsw, nullptr, &tv);
});
if (ret == 0) { return Error::ConnectionTimeout; }
if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
auto error = 0;
socklen_t len = sizeof(error);
auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
reinterpret_cast<char *>(&error), &len);
auto successful = res >= 0 && !error;
return successful ? Error::Success : Error::Connection;
}
return Error::Connection;
#else
struct pollfd pfd_read;
pfd_read.fd = sock;
pfd_read.events = POLLIN | POLLOUT;
@@ -3255,6 +3304,7 @@ inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
}
return Error::Connection;
#endif
}
inline bool is_socket_alive(socket_t sock) {
@@ -8001,6 +8051,16 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
res.version = "HTTP/1.1";
res.headers = default_headers_;
#ifdef __APPLE__
// Socket file descriptor exceeded FD_SETSIZE...
if (strm.socket() >= FD_SETSIZE) {
Headers dummy;
detail::read_headers(strm, dummy);
res.status = StatusCode::InternalServerError_500;
return write_response(strm, close_connection, req, res);
}
#endif
// Request line and headers
if (!parse_request_line(line_reader.ptr(), req) ||
!detail::read_headers(strm, req.headers)) {
@@ -8785,7 +8845,11 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
}
if (!req.has_header("Host")) {
if (is_ssl()) {
// For Unix socket connections, use "localhost" as Host header (similar to
// curl behavior)
if (address_family_ == AF_UNIX) {
req.set_header("Host", "localhost");
} else if (is_ssl()) {
if (port_ == 443) {
req.set_header("Host", host_);
} else {
+8
View File
@@ -18,6 +18,14 @@ project(
cxx = meson.get_compiler('cpp')
if cxx.sizeof('void *') != 8
if host_machine.system() == 'windows'
error('unsupported architecture: cpp-httplib doesn\'t support 32-bit Windows. Please use a 64-bit compiler.')
else
warning('cpp-httplib doesn\'t support 32-bit platforms. Please use a 64-bit compiler.')
endif
endif
# Check just in case downstream decides to edit the source
# and add a project version
version = meson.project_version()
+42
View File
@@ -173,6 +173,48 @@ TEST_F(UnixSocketTest, abstract) {
}
#endif
TEST_F(UnixSocketTest, HostHeaderAutoSet) {
httplib::Server svr;
std::string received_host_header;
svr.Get(pattern_, [&](const httplib::Request &req, httplib::Response &res) {
// Capture the Host header sent by the client
auto host_iter = req.headers.find("Host");
if (host_iter != req.headers.end()) {
received_host_header = host_iter->second;
}
res.set_content(content_, "text/plain");
});
std::thread t{[&] {
ASSERT_TRUE(svr.set_address_family(AF_UNIX).listen(pathname_, 80));
}};
auto se = detail::scope_exit([&] {
svr.stop();
t.join();
ASSERT_FALSE(svr.is_running());
});
svr.wait_until_ready();
ASSERT_TRUE(svr.is_running());
// Test that Host header is automatically set to "localhost" for Unix socket
// connections
httplib::Client cli{pathname_};
cli.set_address_family(AF_UNIX);
ASSERT_TRUE(cli.is_valid());
const auto &result = cli.Get(pattern_);
ASSERT_TRUE(result) << "error: " << result.error();
const auto &resp = result.value();
EXPECT_EQ(resp.status, StatusCode::OK_200);
EXPECT_EQ(resp.body, content_);
// Verify that Host header was automatically set to "localhost"
EXPECT_EQ(received_host_header, "localhost");
}
#ifndef _WIN32
TEST(SocketStream, wait_writable_UNIX) {
int fds[2];