Skip to content

Commit 76afe97

Browse files
authored
Documentation improvements (issues #334, #324, #343) (#358)
* Documentation improvements (issues #334, #324, #343) - Remove obsolete INSTALL file (issue #334): The generic autoconf INSTALL file incorrectly mentioned `make -f Makefile.cvs` instead of `./bootstrap`. README.md already contains accurate build instructions. - Add args_processing.cpp example (issue #324): Demonstrates how to use get_args() and get_args_flat() methods to iterate over all request arguments, including handling parameters with multiple values. - Add bind_address(string) overload (issue #343): Allows binding to a specific IP address using a string (e.g., "127.0.0.1") instead of requiring manual sockaddr construction. Supports both IPv4 and IPv6. IPv6 mode is automatically enabled when an IPv6 address is provided. - Document bind_address() methods in README.md (issue #343): Added documentation for both the sockaddr* and string overloads. * Fix macro redefinition warning in create_webserver.cpp Remove explicit #define HTTPSERVER_COMPILATION as it is already defined by the build system via command line flags. * Fix memory leak: copy bind_address_storage to webserver The webserver was only copying the raw bind_address pointer but not the shared_ptr that owns the storage. This caused the memory to be freed when the temporary create_webserver was destroyed, leaving webserver with a dangling pointer and causing valgrind to report a leak. * CI: Print valgrind memcheck log on success or failure Move the memcheck log printing to a separate step that runs with always() condition, so the log is printed regardless of whether the valgrind check passes or fails. This helps debug valgrind failures in CI.
1 parent 1ac17bb commit 76afe97

File tree

11 files changed

+214
-370
lines changed

11 files changed

+214
-370
lines changed

.github/workflows/verify-build.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,9 +624,17 @@ jobs:
624624
run: |
625625
cd build ;
626626
make check-valgrind ;
627-
cat test/test-suite-memcheck.log ;
628627
if: ${{ matrix.build-type == 'valgrind' }}
629628

629+
- name: Print Valgrind memcheck results
630+
shell: bash
631+
run: |
632+
cd build ;
633+
if [ -f test/test-suite-memcheck.log ]; then
634+
cat test/test-suite-memcheck.log ;
635+
fi
636+
if: ${{ always() && matrix.build-type == 'valgrind' }}
637+
630638
- name: Run cppcheck
631639
run: |
632640
cd src/ ;

INSTALL

Lines changed: 0 additions & 367 deletions
This file was deleted.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ For example, if your connection limit is “1”, a browser may open a first con
254254
* _.connection_timeout(**int** timeout):_ Determines after how many seconds of inactivity a connection should be timed out automatically. The default timeout is `180 seconds`.
255255
* _.memory_limit(**int** memory_limit):_ Maximum memory size per connection (followed by a `size_t`). The default is 32 kB (32*1024 bytes). Values above 128k are unlikely to result in much benefit, as half of the memory will be typically used for IO, and TCP buffers are unlikely to support window sizes above 64k on most systems.
256256
* _.per_IP_connection_limit(**int** connection_limit):_ Limit on the number of (concurrent) connections made to the server from the same IP address. Can be used to prevent one IP from taking over all of the allowed connections. If the same IP tries to establish more than the specified number of connections, they will be immediately rejected. The default is `0`, which means no limit on the number of connections from the same IP address.
257+
* _.bind_address(**const struct sockaddr*** address):_ Bind the server to a specific network interface by passing a pre-constructed `sockaddr` structure. This gives full control over the address configuration but requires manual socket address setup.
258+
* _.bind_address(**const std::string&** ip):_ Bind the server to a specific network interface by IP address string (e.g., `"127.0.0.1"` for localhost only, or `"192.168.1.100"` for a specific interface). Supports both IPv4 and IPv6 addresses. When an IPv6 address is provided, IPv6 mode is automatically enabled. Example: `create_webserver(8080).bind_address("127.0.0.1")`.
257259
* _.bind_socket(**int** socket_fd):_ Listen socket to use. Pass a listen socket for the daemon to use (systemd-style). If this option is used, the daemon will not open its own listen socket(s). The argument passed must be of type "int" and refer to an existing socket that has been bound to a port and is listening.
258260
* _.max_thread_stack_size(**int** stack_size):_ Maximum stack size for threads created by the library. Not specifying this option or using a value of zero means using the system default (which is likely to differ based on your platform). Default is `0 (system default)`.
259261
* _.use_ipv6() and .no_ipv6():_ Enable or disable the IPv6 protocol support (by default, libhttpserver will just support IPv4). If you specify this and the local platform does not support it, starting up the server will throw an exception. `off` by default.

examples/Makefile.am

Lines changed: 2 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 setting_headers custom_access_log basic_authentication 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 basic_authentication 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
2323

2424
hello_world_SOURCES = hello_world.cpp
2525
service_SOURCES = service.cpp
@@ -28,6 +28,7 @@ custom_error_SOURCES = custom_error.cpp
2828
allowing_disallowing_methods_SOURCES = allowing_disallowing_methods.cpp
2929
handlers_SOURCES = handlers.cpp
3030
hello_with_get_arg_SOURCES = hello_with_get_arg.cpp
31+
args_processing_SOURCES = args_processing.cpp
3132
setting_headers_SOURCES = setting_headers.cpp
3233
custom_access_log_SOURCES = custom_access_log.cpp
3334
basic_authentication_SOURCES = basic_authentication.cpp

examples/args_processing.cpp

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
This file is part of libhttpserver
3+
Copyright (C) 2011, 2012, 2013, 2014, 2015 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+
#include <sstream>
24+
#include <string>
25+
26+
#include <httpserver.hpp>
27+
28+
// This example demonstrates how to use get_args() and get_args_flat() to
29+
// process all query string and body arguments from an HTTP request.
30+
//
31+
// Try these URLs:
32+
// http://localhost:8080/args?name=john&age=30
33+
// http://localhost:8080/args?id=1&id=2&id=3 (multiple values for same key)
34+
// http://localhost:8080/args?colors=red&colors=green&colors=blue
35+
36+
class args_resource : public httpserver::http_resource {
37+
public:
38+
std::shared_ptr<httpserver::http_response> render(const httpserver::http_request& req) {
39+
std::stringstream response_body;
40+
41+
response_body << "=== Using get_args() (supports multiple values per key) ===\n\n";
42+
43+
// get_args() returns a map where each key maps to an http_arg_value.
44+
// http_arg_value contains a vector of values for parameters like "?id=1&id=2&id=3"
45+
auto args = req.get_args();
46+
for (const auto& [key, arg_value] : args) {
47+
response_body << "Key: " << key << "\n";
48+
// Use get_all_values() to get all values for this key
49+
auto all_values = arg_value.get_all_values();
50+
if (all_values.size() > 1) {
51+
response_body << " Values (" << all_values.size() << "):\n";
52+
for (const auto& v : all_values) {
53+
response_body << " - " << v << "\n";
54+
}
55+
} else {
56+
// For single values, http_arg_value converts to string_view
57+
response_body << " Value: " << std::string_view(arg_value) << "\n";
58+
}
59+
}
60+
61+
response_body << "\n=== Using get_args_flat() (one value per key) ===\n\n";
62+
63+
// get_args_flat() returns a simple map with one value per key.
64+
// If a key has multiple values, only the first value is returned.
65+
auto args_flat = req.get_args_flat();
66+
for (const auto& [key, value] : args_flat) {
67+
response_body << key << " = " << value << "\n";
68+
}
69+
70+
response_body << "\n=== Accessing individual arguments ===\n\n";
71+
72+
// You can also access individual arguments directly
73+
auto name = req.get_arg("name"); // Returns http_arg_value (may have multiple values)
74+
auto name_flat = req.get_arg_flat("name"); // Returns string_view (first value only)
75+
76+
if (!name.get_flat_value().empty()) {
77+
response_body << "name (via get_arg): " << std::string_view(name) << "\n";
78+
}
79+
if (!name_flat.empty()) {
80+
response_body << "name (via get_arg_flat): " << name_flat << "\n";
81+
}
82+
83+
return std::make_shared<httpserver::string_response>(response_body.str(), 200, "text/plain");
84+
}
85+
};
86+
87+
int main() {
88+
httpserver::webserver ws = httpserver::create_webserver(8080);
89+
90+
args_resource ar;
91+
ws.register_resource("/args", &ar);
92+
93+
std::cout << "Server running on http://localhost:8080/args\n";
94+
std::cout << "Try: http://localhost:8080/args?name=john&age=30\n";
95+
std::cout << "Or: http://localhost:8080/args?id=1&id=2&id=3\n";
96+
97+
ws.start(true);
98+
99+
return 0;
100+
}

src/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/
2020
METASOURCES = AUTO
2121
lib_LTLIBRARIES = libhttpserver.la
22-
libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp file_info.cpp http_request.cpp http_response.cpp string_response.cpp basic_auth_fail_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp details/http_endpoint.cpp
22+
libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp file_info.cpp http_request.cpp http_response.cpp string_response.cpp basic_auth_fail_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp create_webserver.cpp details/http_endpoint.cpp
2323
noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp gettext.h
2424
nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/file_info.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/basic_auth_fail_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp httpserver/http_arg_value.hpp
2525

src/create_webserver.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
#if defined(_WIN32) && !defined(__CYGWIN__)
22+
#define _WINDOWS
23+
#undef _WIN32_WINNT
24+
#define _WIN32_WINNT 0x600
25+
#include <winsock2.h>
26+
#include <ws2tcpip.h>
27+
#else
28+
#include <arpa/inet.h>
29+
#include <netinet/in.h>
30+
#include <sys/socket.h>
31+
#endif
32+
33+
#include <cstring>
34+
#include <memory>
35+
#include <stdexcept>
36+
#include <string>
37+
38+
#include "httpserver/create_webserver.hpp"
39+
40+
namespace httpserver {
41+
42+
create_webserver& create_webserver::bind_address(const std::string& ip) {
43+
_bind_address_storage = std::make_shared<struct sockaddr_storage>();
44+
std::memset(_bind_address_storage.get(), 0, sizeof(struct sockaddr_storage));
45+
46+
// Try IPv4 first
47+
auto* addr4 = reinterpret_cast<struct sockaddr_in*>(_bind_address_storage.get());
48+
if (inet_pton(AF_INET, ip.c_str(), &(addr4->sin_addr)) == 1) {
49+
addr4->sin_family = AF_INET;
50+
addr4->sin_port = htons(_port);
51+
_bind_address = reinterpret_cast<const struct sockaddr*>(_bind_address_storage.get());
52+
return *this;
53+
}
54+
55+
// Try IPv6
56+
auto* addr6 = reinterpret_cast<struct sockaddr_in6*>(_bind_address_storage.get());
57+
if (inet_pton(AF_INET6, ip.c_str(), &(addr6->sin6_addr)) == 1) {
58+
addr6->sin6_family = AF_INET6;
59+
addr6->sin6_port = htons(_port);
60+
_bind_address = reinterpret_cast<const struct sockaddr*>(_bind_address_storage.get());
61+
_use_ipv6 = true;
62+
return *this;
63+
}
64+
65+
throw std::invalid_argument("Invalid IP address: " + ip);
66+
}
67+
68+
} // namespace httpserver

src/httpserver/create_webserver.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <functional>
3131
#include <limits>
3232
#include <string>
33+
#include <variant>
3334

3435
#include "httpserver/http_response.hpp"
3536
#include "httpserver/http_utils.hpp"
@@ -128,6 +129,8 @@ class create_webserver {
128129
return *this;
129130
}
130131

132+
create_webserver& bind_address(const std::string& ip);
133+
131134
create_webserver& bind_socket(int bind_socket) {
132135
_bind_socket = bind_socket;
133136
return *this;
@@ -387,6 +390,7 @@ class create_webserver {
387390
validator_ptr _validator = nullptr;
388391
unescaper_ptr _unescaper = nullptr;
389392
const struct sockaddr* _bind_address = nullptr;
393+
std::shared_ptr<struct sockaddr_storage> _bind_address_storage;
390394
int _bind_socket = 0;
391395
int _max_thread_stack_size = 0;
392396
bool _use_ssl = false;

src/httpserver/webserver.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class webserver {
144144
validator_ptr validator;
145145
unescaper_ptr unescaper;
146146
const struct sockaddr* bind_address;
147+
std::shared_ptr<struct sockaddr_storage> bind_address_storage;
147148
/* Changed type to MHD_socket because this type will always reflect the
148149
platform's actual socket type (e.g. SOCKET on windows, int on unixes)*/
149150
MHD_socket bind_socket;

src/webserver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ webserver::webserver(const create_webserver& params):
134134
validator(params._validator),
135135
unescaper(params._unescaper),
136136
bind_address(params._bind_address),
137+
bind_address_storage(params._bind_address_storage),
137138
bind_socket(params._bind_socket),
138139
max_thread_stack_size(params._max_thread_stack_size),
139140
use_ssl(params._use_ssl),

0 commit comments

Comments
 (0)