mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca5fe354fb | |||
| dd98d2a24d | |||
| 1f110b54d8 | |||
| 7b6867bcdf | |||
| 8b28577ec6 | |||
| 55b38907dc | |||
| 53ea9e8bb4 |
@@ -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).
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user