Skip to content

Commit 6d47202

Browse files
committed
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.
1 parent 7484b50 commit 6d47202

9 files changed

Lines changed: 689 additions & 1 deletion

README.md

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,6 +1494,297 @@ To test the above example, you can run the following command from a terminal:
14941494

14951495
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/deferred_with_accumulator.cpp).
14961496

1497+
#### Example of an empty response (204 No Content)
1498+
```cpp
1499+
#include <httpserver.hpp>
1500+
1501+
using namespace httpserver;
1502+
1503+
class no_content_resource : public http_resource {
1504+
public:
1505+
std::shared_ptr<http_response> render_DELETE(const http_request&) {
1506+
// Return a 204 No Content response with no body
1507+
return std::make_shared<empty_response>(
1508+
http::http_utils::http_no_content);
1509+
}
1510+
1511+
std::shared_ptr<http_response> render_HEAD(const http_request&) {
1512+
// Return a HEAD-only response with headers but no body
1513+
auto response = std::make_shared<empty_response>(
1514+
http::http_utils::http_ok,
1515+
empty_response::HEAD_ONLY);
1516+
response->with_header("X-Total-Count", "42");
1517+
return response;
1518+
}
1519+
};
1520+
1521+
int main() {
1522+
webserver ws = create_webserver(8080);
1523+
1524+
no_content_resource ncr;
1525+
ws.register_resource("/items", &ncr);
1526+
ws.start(true);
1527+
1528+
return 0;
1529+
}
1530+
```
1531+
To test the above example, you can run the following commands from a terminal:
1532+
1533+
curl -XDELETE -v localhost:8080/items
1534+
curl -I -v localhost:8080/items
1535+
1536+
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/empty_response_example.cpp).
1537+
1538+
#### Example of a scatter-gather (iovec) response
1539+
```cpp
1540+
#include <httpserver.hpp>
1541+
1542+
using namespace httpserver;
1543+
1544+
class iovec_resource : public http_resource {
1545+
public:
1546+
std::shared_ptr<http_response> render_GET(const http_request&) {
1547+
// Build a response from multiple separate buffers without copying
1548+
std::vector<std::string> parts;
1549+
parts.push_back("{\"header\": \"value\", ");
1550+
parts.push_back("\"items\": [1, 2, 3], ");
1551+
parts.push_back("\"footer\": \"end\"}");
1552+
1553+
return std::make_shared<iovec_response>(
1554+
std::move(parts), 200, "application/json");
1555+
}
1556+
};
1557+
1558+
int main() {
1559+
webserver ws = create_webserver(8080);
1560+
1561+
iovec_resource ir;
1562+
ws.register_resource("/data", &ir);
1563+
ws.start(true);
1564+
1565+
return 0;
1566+
}
1567+
```
1568+
To test the above example, you can run the following command from a terminal:
1569+
1570+
curl -XGET -v localhost:8080/data
1571+
1572+
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/iovec_response_example.cpp).
1573+
1574+
#### Example of a pipe-based streaming response
1575+
```cpp
1576+
#include <cstring>
1577+
#include <thread>
1578+
#include <unistd.h>
1579+
#include <httpserver.hpp>
1580+
1581+
using namespace httpserver;
1582+
1583+
class pipe_resource : public http_resource {
1584+
public:
1585+
std::shared_ptr<http_response> render_GET(const http_request&) {
1586+
int pipefd[2];
1587+
if (pipe(pipefd) == -1) {
1588+
return std::make_shared<string_response>("pipe failed", 500);
1589+
}
1590+
1591+
// Spawn a thread to write data into the pipe
1592+
std::thread writer([fd = pipefd[1]]() {
1593+
const char* messages[] = {"Hello ", "from ", "a pipe!\n"};
1594+
for (const char* msg : messages) {
1595+
ssize_t ret = write(fd, msg, strlen(msg));
1596+
(void)ret;
1597+
}
1598+
close(fd);
1599+
});
1600+
writer.detach();
1601+
1602+
// Return the read end of the pipe as the response
1603+
return std::make_shared<pipe_response>(pipefd[0], 200, "text/plain");
1604+
}
1605+
};
1606+
1607+
int main() {
1608+
webserver ws = create_webserver(8080);
1609+
1610+
pipe_resource pr;
1611+
ws.register_resource("/stream", &pr);
1612+
ws.start(true);
1613+
1614+
return 0;
1615+
}
1616+
```
1617+
To test the above example, you can run the following command from a terminal:
1618+
1619+
curl -XGET -v localhost:8080/stream
1620+
1621+
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/pipe_response_example.cpp).
1622+
1623+
#### Example of a WebSocket echo server
1624+
```cpp
1625+
#include <iostream>
1626+
#include <httpserver.hpp>
1627+
1628+
using namespace httpserver;
1629+
1630+
class echo_handler : public websocket_handler {
1631+
public:
1632+
void on_open(websocket_session& session) override {
1633+
std::cout << "WebSocket connection opened" << std::endl;
1634+
session.send_text("Welcome to the echo server!");
1635+
}
1636+
1637+
void on_message(websocket_session& session, const std::string& msg) override {
1638+
std::cout << "Received: " << msg << std::endl;
1639+
session.send_text("Echo: " + msg);
1640+
}
1641+
1642+
void on_close(websocket_session& session, uint16_t code, const std::string& reason) override {
1643+
std::cout << "WebSocket closed (code=" << code << ", reason=" << reason << ")" << std::endl;
1644+
}
1645+
};
1646+
1647+
int main() {
1648+
webserver ws = create_webserver(8080);
1649+
1650+
echo_handler handler;
1651+
ws.register_ws_resource("/ws", &handler);
1652+
ws.start(true);
1653+
1654+
return 0;
1655+
}
1656+
```
1657+
Note: WebSocket support requires libmicrohttpd 1.0.0 built with WebSocket support. You can test this with any WebSocket client library or browser JavaScript: `new WebSocket("ws://localhost:8080/ws")`.
1658+
1659+
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/websocket_echo.cpp).
1660+
1661+
#### Example of daemon introspection
1662+
```cpp
1663+
#include <iostream>
1664+
#include <httpserver.hpp>
1665+
1666+
using namespace httpserver;
1667+
1668+
class hello_resource : public http_resource {
1669+
public:
1670+
std::shared_ptr<http_response> render_GET(const http_request&) {
1671+
return std::make_shared<string_response>("Hello, World!");
1672+
}
1673+
};
1674+
1675+
int main() {
1676+
// Use port 0 to let the OS assign an ephemeral port
1677+
webserver ws = create_webserver(0);
1678+
1679+
hello_resource hr;
1680+
ws.register_resource("/hello", &hr);
1681+
ws.start(false);
1682+
1683+
// Query daemon information
1684+
std::cout << "libmicrohttpd version: "
1685+
<< http::http_utils::get_mhd_version() << std::endl;
1686+
std::cout << "Bound port: " << ws.get_bound_port() << std::endl;
1687+
std::cout << "Listen FD: " << ws.get_listen_fd() << std::endl;
1688+
std::cout << "Active connections: " << ws.get_active_connections() << std::endl;
1689+
std::cout << "HTTP 200 reason: "
1690+
<< http::http_utils::reason_phrase(200) << std::endl;
1691+
std::cout << "HTTP 404 reason: "
1692+
<< http::http_utils::reason_phrase(404) << std::endl;
1693+
1694+
ws.sweet_kill();
1695+
return 0;
1696+
}
1697+
```
1698+
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/daemon_info.cpp).
1699+
1700+
#### Example of an external event loop
1701+
```cpp
1702+
#include <csignal>
1703+
#include <iostream>
1704+
#include <httpserver.hpp>
1705+
1706+
using namespace httpserver;
1707+
1708+
static volatile bool running = true;
1709+
1710+
void signal_handler(int) { running = false; }
1711+
1712+
class hello_resource : public http_resource {
1713+
public:
1714+
std::shared_ptr<http_response> render_GET(const http_request&) {
1715+
return std::make_shared<string_response>("Hello from external event loop!");
1716+
}
1717+
};
1718+
1719+
int main() {
1720+
signal(SIGINT, signal_handler);
1721+
1722+
webserver ws = create_webserver(8080);
1723+
1724+
hello_resource hr;
1725+
ws.register_resource("/hello", &hr);
1726+
ws.start(false);
1727+
1728+
std::cout << "Server running on port " << ws.get_bound_port() << std::endl;
1729+
1730+
// Drive the event loop externally using run_wait
1731+
while (running) {
1732+
// Block for up to 1000ms waiting for HTTP activity
1733+
ws.run_wait(1000);
1734+
1735+
// You can do other work here between iterations
1736+
}
1737+
1738+
// Graceful shutdown: stop accepting new connections first
1739+
ws.quiesce();
1740+
ws.stop();
1741+
1742+
return 0;
1743+
}
1744+
```
1745+
To test the above example, you can run the following command from a terminal:
1746+
1747+
curl -XGET -v localhost:8080/hello
1748+
1749+
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/external_event_loop.cpp).
1750+
1751+
#### Example of turbo mode with performance options
1752+
```cpp
1753+
#include <httpserver.hpp>
1754+
1755+
using namespace httpserver;
1756+
1757+
class hello_resource : public http_resource {
1758+
public:
1759+
std::shared_ptr<http_response> render_GET(const http_request&) {
1760+
return std::make_shared<string_response>("Hello, turbo world!");
1761+
}
1762+
};
1763+
1764+
int main() {
1765+
// Create a high-performance server with turbo mode,
1766+
// suppressed date headers, and a thread pool.
1767+
webserver ws = create_webserver(8080)
1768+
.start_method(http::http_utils::INTERNAL_SELECT)
1769+
.max_threads(4)
1770+
.turbo()
1771+
.suppress_date_header()
1772+
.tcp_fastopen_queue_size(16)
1773+
.listen_backlog(128);
1774+
1775+
hello_resource hr;
1776+
ws.register_resource("/hello", &hr);
1777+
ws.start(true);
1778+
1779+
return 0;
1780+
}
1781+
```
1782+
To test the above example, you can run the following command from a terminal:
1783+
1784+
curl -XGET -v localhost:8080/hello
1785+
1786+
You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/turbo_mode.cpp).
1787+
14971788
[Back to TOC](#table-of-contents)
14981789

14991790
## Copying

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
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
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

4652
if HAVE_BAUTH
4753
noinst_PROGRAMS += basic_authentication centralized_authentication
@@ -59,3 +65,9 @@ if HAVE_DAUTH
5965
noinst_PROGRAMS += digest_authentication
6066
digest_authentication_SOURCES = digest_authentication.cpp
6167
endif
68+
69+
if HAVE_WEBSOCKET
70+
noinst_PROGRAMS += websocket_echo
71+
websocket_echo_SOURCES = websocket_echo.cpp
72+
websocket_echo_LDADD = $(LDADD) -lmicrohttpd_ws
73+
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+
}

0 commit comments

Comments
 (0)