Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
-----------

Expand Down Expand Up @@ -1141,6 +1160,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).
Expand Down
61 changes: 60 additions & 1 deletion httplib.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#ifndef CPPHTTPLIB_HTTPLIB_H
#define CPPHTTPLIB_HTTPLIB_H

#define CPPHTTPLIB_VERSION "0.22.0"
#define CPPHTTPLIB_VERSION "0.23.0"

/*
* Platform compatibility check
Expand Down Expand Up @@ -3215,13 +3215,31 @@ 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);

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) {
Expand All @@ -3234,6 +3252,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;
Expand All @@ -3255,6 +3303,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) {
Expand Down Expand Up @@ -8001,6 +8050,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)) {
Expand Down
Loading