From 2f39723d08eab78aec1e8d167b24bf127ddd067c Mon Sep 17 00:00:00 2001
From: Florian Albrechtskirchinger <falbrechtskirchinger@gmail.com>
Date: Wed, 12 Mar 2025 17:12:03 +0100
Subject: [PATCH] Wrap poll()/WSAPoll() in a function and build compiled
 library on Windows (#2107)

* Wrap poll()/WSAPoll() in a function

Instead of using a macro for poll() on Windows, which breaks when the
implementation is compiled separately, add a detail::poll_wrapper()
function that dispatches to either ::poll() or ::WSAPoll().

* Build compiled library on Windows
---
 .github/workflows/test.yaml | 10 ++++++++++
 httplib.h                   | 19 ++++++++++++-------
 2 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 02f46be..7763d37 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -98,9 +98,17 @@ jobs:
       matrix:
         config:
         - with_ssl: false
+          compiled: false
+          run_tests: true
           name: without SSL
         - with_ssl: true
+          compiled: false
+          run_tests: true
           name: with SSL
+        - with_ssl: false
+          compiled: true
+          run_tests: false
+          name: compiled
     name: windows ${{ matrix.config.name }}
     steps:
     - name: Prepare Git for Checkout on Windows
@@ -128,12 +136,14 @@ jobs:
         -DCMAKE_BUILD_TYPE=Release
         -DCMAKE_TOOLCHAIN_FILE=${{ env.VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake
         -DHTTPLIB_TEST=ON
+        -DHTTPLIB_COMPILE=${{ matrix.config.compiled && 'ON' || 'OFF' }}
         -DHTTPLIB_REQUIRE_ZLIB=ON
         -DHTTPLIB_REQUIRE_BROTLI=ON
         -DHTTPLIB_REQUIRE_OPENSSL=${{ matrix.config.with_ssl && 'ON' || 'OFF' }}
     - name: Build ${{ matrix.config.name }}
       run: cmake --build build --config Release -- /v:m /clp:ShowCommandLine
     - name: Run tests ${{ matrix.config.name }}
+      if: ${{ matrix.config.run_tests }}
       run: ctest --output-on-failure --test-dir build -C Release
 
     env:
diff --git a/httplib.h b/httplib.h
index f6b7582..e009971 100644
--- a/httplib.h
+++ b/httplib.h
@@ -192,9 +192,9 @@ using ssize_t = long;
 #define WSA_FLAG_NO_HANDLE_INHERIT 0x80
 #endif
 
+using nfds_t = unsigned long;
 using socket_t = SOCKET;
 using socklen_t = int;
-#define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
 
 #else // not _WIN32
 
@@ -3240,6 +3240,14 @@ inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
   });
 }
 
+inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
+#ifdef _WIN32
+  return ::WSAPoll(fds, nfds, timeout);
+#else
+  return ::poll(fds, nfds, timeout);
+#endif
+}
+
 template <bool Read>
 inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
   struct pollfd pfd;
@@ -3248,7 +3256,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(&pfd, 1, timeout); });
+  return handle_EINTR([&]() { return poll_wrapper(&pfd, 1, timeout); });
 }
 
 inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
@@ -3267,7 +3275,8 @@ inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
 
   auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
 
-  auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
+  auto poll_res =
+      handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });
 
   if (poll_res == 0) { return Error::ConnectionTimeout; }
 
@@ -10367,8 +10376,4 @@ inline SSL_CTX *Client::ssl_context() const {
 
 } // namespace httplib
 
-#ifdef _WIN32
-#undef poll
-#endif
-
 #endif // CPPHTTPLIB_HTTPLIB_H