Skip to content

Commit d8b055e

Browse files
etrclaude
andauthored
Migrate to libmicrohttpd 1.0.0 API with new features (#370)
* Migrate to libmicrohttpd 1.0.0 API with new features Raise minimum libmicrohttpd requirement to 1.0.0 and migrate all deprecated APIs to their v3 replacements: - Basic Auth: MHD_basic_auth_get_username_password3, MHD_queue_basic_auth_required_response3 with UTF-8 support - Digest Auth: MHD_digest_auth_check3, MHD_digest_auth_check_digest3, MHD_queue_auth_required_response3 with SHA-512/256, userhash, nonce binding, and structured digest_auth_result enum Add new response types: empty_response, pipe_response, iovec_response. Add external event loop integration (run, run_wait, get_fdset, get_timeout, add_connection), daemon management (quiesce, get_listen_fd, get_active_connections, get_bound_port), and numerous new daemon options (listen_backlog, address_reuse, tcp_fastopen_queue_size, turbo, etc.). Add conditional WebSocket support via libmicrohttpd_ws. Add utility functions: reason_phrase, is_feature_supported, get_mhd_version. * Update README with all new MHD 1.0 features and options Document WebSocket support, new response types (empty, iovec, pipe), external event loop integration, daemon introspection methods, new server configuration options (turbo, no_listen_socket, suppress_date_header, etc.), enhanced TLS options, updated digest authentication API with SHA-256/SHA-512/256 and fine-grained result codes, and new http_utils utility methods. * Add examples for new MHD 1.0 features and document them in README New example files: - empty_response_example: 204 No Content and HEAD-only responses - iovec_response_example: scatter-gather response from multiple buffers - pipe_response_example: streaming response from a pipe fd - websocket_echo: WebSocket echo server with on_open/on_message/on_close - daemon_info: daemon introspection (bound port, listen fd, http_utils) - external_event_loop: driving the server with run_wait() and quiesce() - turbo_mode: high-performance config with turbo, suppressed date header, TCP Fast Open, and listen backlog All examples are added to the Other Examples section of the README with inline code, test commands, and links to the source files. * Cleaned-up validation issues * fix codacy warnings * Add documentation and example for serving binary data from memory The existing string_response already supports binary content (std::string can hold arbitrary bytes), but this was not documented or demonstrated anywhere. This gap caused users to believe a new response type was needed (see PR #368). - Add a note to the README's string_response description clarifying binary data support - Add a new "Serving binary data from memory" section with inline example - Add examples/binary_buffer_response.cpp as a complete, buildable example that serves a PNG image from an in-memory buffer - Register the new example in examples/Makefile.am https://claude.ai/code/session_01S3BvBrSoNvUhpYTyhPYCjJ * Fix CI: add ChangeLog entry and missing include <utility> Add ChangeLog entry for the binary buffer example to satisfy the ChangeLog Check workflow. Add missing #include <utility> for std::move to fix cpplint warning. https://claude.ai/code/session_01S3BvBrSoNvUhpYTyhPYCjJ * Migrate to libmicrohttpd 1.0.0 API with new features Raise minimum libmicrohttpd requirement to 1.0.0 and migrate all deprecated APIs to their v3 replacements: - Basic Auth: MHD_basic_auth_get_username_password3, MHD_queue_basic_auth_required_response3 with UTF-8 support - Digest Auth: MHD_digest_auth_check3, MHD_digest_auth_check_digest3, MHD_queue_auth_required_response3 with SHA-512/256, userhash, nonce binding, and structured digest_auth_result enum Add new response types: empty_response, pipe_response, iovec_response. Add external event loop integration (run, run_wait, get_fdset, get_timeout, add_connection), daemon management (quiesce, get_listen_fd, get_active_connections, get_bound_port), and numerous new daemon options (listen_backlog, address_reuse, tcp_fastopen_queue_size, turbo, etc.). Add conditional WebSocket support via libmicrohttpd_ws. Add utility functions: reason_phrase, is_feature_supported, get_mhd_version. * Fix Codacy CWE-126: replace strlen with std::char_traits::length Replace strlen(s) with std::char_traits<char>::length(s) in unescaper_func to silence Codacy static analysis warning about potential over-read on non-null-terminated strings. The MHD API guarantees s is null-terminated, but using the C++ equivalent avoids false positives from C-focused analyzers. * libmicro to v1.0.3 * libmicrohttpd to 1.0.3 in codeql checks * Fix CI: cpplint errors, external event loop test, and Windows pipe build - Add missing #include <utility> and <string> in headers and examples - Reorder includes in webserver.cpp to satisfy cpplint include order - Add NOLINT(runtime/int) for curl API's long parameters - Add EXTERNAL_SELECT start method (MHD_USE_AUTO without internal threading) for proper external event loop support - Fix daemon_info test and external_event_loop example to use EXTERNAL_SELECT instead of INTERNAL_SELECT with run_wait() - Fix pipe_response_example to compile on Windows (_pipe + <io.h>) * Fix Windows build for new_response_types test Test file was missed in d4808b4 which fixed the same pattern in pipe_response_example.cpp. MinGW64 has _pipe, not pipe. * Silence cpplint readability/braces on Windows-guarded class cpplint's brace check gets confused by the #if/#else/#endif inside pipe_resource::render_GET, matching the existing NOLINT in examples/pipe_response_example.cpp. * Fix Windows external_event_loop test: drop no_thread_safety() On Windows/MSYS2, MHD_start_daemon rejects the combination of MHD_USE_AUTO (EXTERNAL_SELECT) and MHD_USE_NO_THREAD_SAFETY, causing daemon_info.exe test #6 to fail with "Unable to connect daemon to port: 8080". Drop no_thread_safety() from both the test and the matching example; the default thread-safe daemon is still drivable from a single thread via MHD_run_wait. * Fix Windows port leak in quiesce_does_not_crash test MHD_quiesce_daemon transfers the listen socket to the caller. On Windows, MHD sockets are Winsock SOCKETs and POSIX close() from <unistd.h> does not close them, so the prior version leaked the socket and left port 8080 bound. The next test in the same binary (external_event_loop) then failed MHD_start_daemon with "Unable to connect daemon to port: 8080". Use closesocket() on Windows and close() elsewhere. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent a752404 commit d8b055e

38 files changed

Lines changed: 2809 additions & 349 deletions

ChangeLog

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
Version 0.20.0
22

3+
Raised minimum libmicrohttpd requirement to 1.0.0.
4+
Migrated Basic Auth to v3 API (MHD_basic_auth_get_username_password3,
5+
MHD_queue_basic_auth_required_response3) with UTF-8 support.
6+
Migrated Digest Auth to v3 API (MHD_digest_auth_check3,
7+
MHD_digest_auth_check_digest3, MHD_queue_auth_required_response3)
8+
with SHA-512/256 support, userhash, nonce binding, and structured
9+
digest_auth_result enum. Default algorithm changed to SHA-256.
10+
Added new response types: empty_response, pipe_response, iovec_response.
11+
Added external event loop integration: webserver::run(), run_wait(),
12+
get_fdset(), get_timeout(), add_connection().
13+
Added daemon management: quiesce(), get_listen_fd(),
14+
get_active_connections(), get_bound_port().
15+
Added daemon options: listen_backlog, address_reuse,
16+
connection_memory_increment, tcp_fastopen_queue_size,
17+
sigpipe_handled_by_app, https_mem_dhparams, https_key_password,
18+
https_priorities_append, no_alpn, client_discipline_level.
19+
Added startup flags: no_listen_socket, no_thread_safety, turbo,
20+
suppress_date_header.
21+
Added WebSocket support (conditional on HAVE_WEBSOCKET):
22+
websocket_handler, websocket_session, register_ws_resource().
23+
Added utility functions: reason_phrase(), is_feature_supported(),
24+
get_mhd_version().
325
Added example and documentation for serving binary data from memory
426
using string_response (addresses PR #368).
527
Added conditional compilation for basic auth (HAVE_BAUTH), mirroring

README.md

Lines changed: 470 additions & 16 deletions
Large diffs are not rendered by default.

configure.ac

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,18 @@ AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutl
108108
# Checks for libmicrohttpd
109109
if test x"$host" = x"$build"; then
110110
AC_CHECK_HEADER([microhttpd.h],
111-
AC_CHECK_LIB([microhttpd], [MHD_get_fdset2],
112-
[AC_MSG_CHECKING([for libmicrohttpd >= 0.9.64])
111+
AC_CHECK_LIB([microhttpd], [MHD_start_daemon],
112+
[AC_MSG_CHECKING([for libmicrohttpd >= 1.0.0])
113113
AC_COMPILE_IFELSE(
114114
[AC_LANG_SOURCE([
115115
#include <microhttpd.h>
116-
#if (MHD_VERSION < 0x00096400)
117-
#error needs at least version 0.9.64
116+
#if (MHD_VERSION < 0x01000000)
117+
#error needs at least version 1.0.0
118118
#endif
119119
int main () { return 0; }
120120
])],
121121
[],
122-
[AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.64")]
122+
[AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 1.0.0")]
123123
)
124124
],
125125
[AC_MSG_ERROR(["libmicrohttpd not found"])]
@@ -133,7 +133,7 @@ if test x"$host" = x"$build"; then
133133
cond_cross_compile="no"
134134
else
135135
AC_CHECK_HEADER([microhttpd.h],
136-
AC_CHECK_LIB([microhttpd], [MHD_get_fdset2],
136+
AC_CHECK_LIB([microhttpd], [MHD_start_daemon],
137137
[],
138138
[AC_MSG_ERROR(["libmicrohttpd not found"])]
139139
),
@@ -149,15 +149,22 @@ fi
149149
AM_CONDITIONAL([COND_CROSS_COMPILE],[test x"$cond_cross_compile" = x"yes"])
150150
AC_SUBST(COND_CROSS_COMPILE)
151151

152-
# Check for basic auth support in libmicrohttpd
153-
AC_CHECK_LIB([microhttpd], [MHD_queue_basic_auth_fail_response],
152+
# Check for basic auth v3 support in libmicrohttpd
153+
AC_CHECK_LIB([microhttpd], [MHD_basic_auth_get_username_password3],
154154
[have_bauth="yes"],
155-
[have_bauth="no"; AC_MSG_WARN("libmicrohttpd basic auth support not found. Basic auth will be disabled")])
155+
[have_bauth="no"; AC_MSG_WARN("libmicrohttpd basic auth v3 support not found. Basic auth will be disabled")])
156156

157-
# Check for digest auth support in libmicrohttpd
158-
AC_CHECK_LIB([microhttpd], [MHD_queue_auth_fail_response],
157+
# Check for digest auth v3 support in libmicrohttpd
158+
AC_CHECK_LIB([microhttpd], [MHD_digest_auth_check3],
159159
[have_dauth="yes"],
160-
[have_dauth="no"; AC_MSG_WARN("libmicrohttpd digest auth support not found. Digest auth will be disabled")])
160+
[have_dauth="no"; AC_MSG_WARN("libmicrohttpd digest auth v3 support not found. Digest auth will be disabled")])
161+
162+
# Check for WebSocket support in libmicrohttpd_ws
163+
AC_CHECK_HEADER([microhttpd_ws.h],
164+
[AC_CHECK_LIB([microhttpd_ws], [MHD_websocket_stream_init],
165+
[have_websocket="yes"],
166+
[have_websocket="no"; AC_MSG_WARN("libmicrohttpd_ws not found. WebSocket support will be disabled")])],
167+
[have_websocket="no"; AC_MSG_WARN("microhttpd_ws.h not found. WebSocket support will be disabled")])
161168

162169
AC_MSG_CHECKING([whether to build with TCP_FASTOPEN support])
163170
AC_ARG_ENABLE([fastopen],
@@ -283,6 +290,14 @@ fi
283290

284291
AM_CONDITIONAL([HAVE_DAUTH],[test x"$have_dauth" = x"yes"])
285292

293+
if test x"$have_websocket" = x"yes"; then
294+
AM_CXXFLAGS="$AM_CXXFLAGS -DHAVE_WEBSOCKET"
295+
AM_CFLAGS="$AM_CXXFLAGS -DHAVE_WEBSOCKET"
296+
LHT_LIBDEPS="$LHT_LIBDEPS -lmicrohttpd_ws"
297+
fi
298+
299+
AM_CONDITIONAL([HAVE_WEBSOCKET],[test x"$have_websocket" = x"yes"])
300+
286301
DX_HTML_FEATURE(ON)
287302
DX_CHM_FEATURE(OFF)
288303
DX_CHI_FEATURE(OFF)
@@ -341,6 +356,7 @@ AC_MSG_NOTICE([Configuration Summary:
341356
TLS Enabled : ${have_gnutls}
342357
Basic Auth : ${have_bauth}
343358
Digest Auth : ${have_dauth}
359+
WebSocket : ${have_websocket}
344360
TCP_FASTOPEN : ${is_fastopen_supported}
345361
Static : ${static}
346362
Windows build : ${is_windows}

examples/Makefile.am

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
LDADD = $(top_builddir)/src/libhttpserver.la
2020
AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/
2121
METASOURCES = AUTO
22-
noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg args_processing setting_headers custom_access_log minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator file_upload file_upload_with_callback binary_buffer_response
22+
noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg args_processing setting_headers custom_access_log minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator file_upload file_upload_with_callback empty_response_example iovec_response_example pipe_response_example daemon_info external_event_loop turbo_mode binary_buffer_response
2323

2424
hello_world_SOURCES = hello_world.cpp
2525
service_SOURCES = service.cpp
@@ -42,6 +42,12 @@ benchmark_threads_SOURCES = benchmark_threads.cpp
4242
benchmark_nodelay_SOURCES = benchmark_nodelay.cpp
4343
file_upload_SOURCES = file_upload.cpp
4444
file_upload_with_callback_SOURCES = file_upload_with_callback.cpp
45+
empty_response_example_SOURCES = empty_response_example.cpp
46+
iovec_response_example_SOURCES = iovec_response_example.cpp
47+
pipe_response_example_SOURCES = pipe_response_example.cpp
48+
daemon_info_SOURCES = daemon_info.cpp
49+
external_event_loop_SOURCES = external_event_loop.cpp
50+
turbo_mode_SOURCES = turbo_mode.cpp
4551
binary_buffer_response_SOURCES = binary_buffer_response.cpp
4652

4753
if HAVE_BAUTH
@@ -60,3 +66,9 @@ if HAVE_DAUTH
6066
noinst_PROGRAMS += digest_authentication
6167
digest_authentication_SOURCES = digest_authentication.cpp
6268
endif
69+
70+
if HAVE_WEBSOCKET
71+
noinst_PROGRAMS += websocket_echo
72+
websocket_echo_SOURCES = websocket_echo.cpp
73+
websocket_echo_LDADD = $(LDADD) -lmicrohttpd_ws
74+
endif

examples/daemon_info.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
This file is part of libhttpserver
3+
Copyright (C) 2011-2019 Sebastiano Merlino
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
USA
19+
*/
20+
21+
#include <iostream>
22+
#include <memory>
23+
24+
#include <httpserver.hpp>
25+
26+
class hello_resource : public httpserver::http_resource {
27+
public:
28+
std::shared_ptr<httpserver::http_response> render_GET(const httpserver::http_request&) {
29+
return std::make_shared<httpserver::string_response>("Hello, World!");
30+
}
31+
};
32+
33+
int main() {
34+
// Use port 0 to let the OS assign an ephemeral port
35+
httpserver::webserver ws = httpserver::create_webserver(0);
36+
37+
hello_resource hr;
38+
ws.register_resource("/hello", &hr);
39+
ws.start(false);
40+
41+
// Query daemon information
42+
std::cout << "libmicrohttpd version: "
43+
<< httpserver::http::http_utils::get_mhd_version() << std::endl;
44+
std::cout << "Bound port: " << ws.get_bound_port() << std::endl;
45+
std::cout << "Listen FD: " << ws.get_listen_fd() << std::endl;
46+
std::cout << "Active connections: " << ws.get_active_connections() << std::endl;
47+
std::cout << "HTTP 200 reason: "
48+
<< httpserver::http::http_utils::reason_phrase(200) << std::endl;
49+
std::cout << "HTTP 404 reason: "
50+
<< httpserver::http::http_utils::reason_phrase(404) << std::endl;
51+
52+
std::cout << "\nServer running on port " << ws.get_bound_port()
53+
<< ". Press Ctrl+C to stop." << std::endl;
54+
55+
// Block until interrupted
56+
ws.sweet_kill();
57+
58+
return 0;
59+
}

examples/digest_authentication.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,21 @@
2727
class digest_resource : public httpserver::http_resource {
2828
public:
2929
std::shared_ptr<httpserver::http_response> render_GET(const httpserver::http_request& req) {
30+
using httpserver::http::http_utils;
3031
if (req.get_digested_user() == "") {
31-
return std::shared_ptr<httpserver::digest_auth_fail_response>(new httpserver::digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true));
32+
return std::make_shared<httpserver::digest_auth_fail_response>("FAIL", "test@example.com", MY_OPAQUE, true,
33+
http_utils::http_ok, http_utils::text_plain, http_utils::digest_algorithm::MD5);
3234
} else {
33-
bool reload_nonce = false;
34-
if (!req.check_digest_auth("test@example.com", "mypass", 300, &reload_nonce)) {
35-
return std::shared_ptr<httpserver::digest_auth_fail_response>(new httpserver::digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce));
35+
auto result = req.check_digest_auth("test@example.com", "mypass", 300, 0, http_utils::digest_algorithm::MD5);
36+
if (result == http_utils::digest_auth_result::NONCE_STALE) {
37+
return std::make_shared<httpserver::digest_auth_fail_response>("FAIL", "test@example.com", MY_OPAQUE, true,
38+
http_utils::http_ok, http_utils::text_plain, http_utils::digest_algorithm::MD5);
39+
} else if (result != http_utils::digest_auth_result::OK) {
40+
return std::make_shared<httpserver::digest_auth_fail_response>("FAIL", "test@example.com", MY_OPAQUE, false,
41+
http_utils::http_ok, http_utils::text_plain, http_utils::digest_algorithm::MD5);
3642
}
3743
}
38-
return std::shared_ptr<httpserver::string_response>(new httpserver::string_response("SUCCESS", 200, "text/plain"));
44+
return std::make_shared<httpserver::string_response>("SUCCESS", 200, "text/plain");
3945
}
4046
};
4147

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
This file is part of libhttpserver
3+
Copyright (C) 2011-2019 Sebastiano Merlino
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
USA
19+
*/
20+
21+
#include <memory>
22+
23+
#include <httpserver.hpp>
24+
25+
class no_content_resource : public httpserver::http_resource {
26+
public:
27+
std::shared_ptr<httpserver::http_response> render_DELETE(const httpserver::http_request&) {
28+
// Return a 204 No Content response with no body
29+
return std::make_shared<httpserver::empty_response>(
30+
httpserver::http::http_utils::http_no_content);
31+
}
32+
33+
std::shared_ptr<httpserver::http_response> render_HEAD(const httpserver::http_request&) {
34+
// Return a HEAD-only response with headers but no body
35+
auto response = std::make_shared<httpserver::empty_response>(
36+
httpserver::http::http_utils::http_ok,
37+
httpserver::empty_response::HEAD_ONLY);
38+
response->with_header("X-Total-Count", "42");
39+
return response;
40+
}
41+
};
42+
43+
int main() {
44+
httpserver::webserver ws = httpserver::create_webserver(8080);
45+
46+
no_content_resource ncr;
47+
ws.register_resource("/items", &ncr);
48+
ws.start(true);
49+
50+
return 0;
51+
}

examples/external_event_loop.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
This file is part of libhttpserver
3+
Copyright (C) 2011-2019 Sebastiano Merlino
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
18+
USA
19+
*/
20+
21+
#include <csignal>
22+
#include <cstdint>
23+
#include <iostream>
24+
#include <memory>
25+
26+
#include <httpserver.hpp>
27+
28+
static volatile bool running = true;
29+
30+
void signal_handler(int) {
31+
running = false;
32+
}
33+
34+
class hello_resource : public httpserver::http_resource {
35+
public:
36+
std::shared_ptr<httpserver::http_response> render_GET(const httpserver::http_request&) {
37+
return std::make_shared<httpserver::string_response>("Hello from external event loop!");
38+
}
39+
};
40+
41+
int main() {
42+
signal(SIGINT, signal_handler);
43+
44+
// EXTERNAL_SELECT runs MHD without an internal polling thread; the
45+
// application drives it via run_wait() below. no_thread_safety() can be
46+
// added for a small perf gain when the daemon is only ever touched from
47+
// a single thread, but it is omitted here for portability (some MHD
48+
// builds, notably Windows/MSYS2, reject that combination at start).
49+
httpserver::webserver ws = httpserver::create_webserver(8080)
50+
.start_method(httpserver::http::http_utils::EXTERNAL_SELECT);
51+
52+
hello_resource hr;
53+
ws.register_resource("/hello", &hr);
54+
ws.start(false);
55+
56+
std::cout << "Server running on port " << ws.get_bound_port() << std::endl;
57+
58+
// Drive the event loop externally using run_wait
59+
while (running) {
60+
// Block for up to 1000ms waiting for HTTP activity
61+
ws.run_wait(1000);
62+
63+
// You can do other work here between iterations
64+
}
65+
66+
std::cout << "\nShutting down..." << std::endl;
67+
68+
// Graceful shutdown: stop accepting new connections first
69+
ws.quiesce();
70+
ws.stop();
71+
72+
return 0;
73+
}

0 commit comments

Comments
 (0)