Skip to content

Commit ec82d90

Browse files
etrclaude
andcommitted
TASK-029: collapse IP-control and stop verbs to canonical names
Public API rename (no aliases, per v2.0 breaking-changes policy): - webserver::sweet_kill -> webserver::stop_and_wait - webserver::ban_ip / unban_ip / allow_ip / disallow_ip collapsed to webserver::block_ip(std::string_view) / webserver::unblock_ip(std::string_view) Per PRD-NAM-REQ-005 and OQ-004 the public surface keeps only deny-list manipulation. The internal allowances set and the is_allowed branch in policy_callback remain in place so default_policy(REJECT) keeps working at the daemon level, but they are no longer reachable from the public API. Coverage note: five ban_system tests that drove the allow-list through the (now-removed) public API are deleted; a follow-up unit test that pokes the PIMPL could restore that coverage at the cost of breaking the PIMPL boundary, which is deliberately out of scope here. webserver::stop() is unchanged ("stop without waiting" verb). shoutCAST is preserved per OQ-005. Action items: - src/httpserver/webserver.hpp: new <string_view> include; old four ban/allow declarations replaced with block_ip / unblock_ip; new doxygen; sweet_kill renamed to stop_and_wait. - src/webserver.cpp: stop_and_wait body unchanged; block_ip / unblock_ip materialize std::string once at the boundary to call ip_representation(const std::string&) (no string_view ctor added to ip_representation). - test/integ/ws_start_stop.cpp: sweet_kill test renamed. - test/integ/ban_system.cpp: 4 retained tests renamed inline; 5 allow_ip/disallow_ip tests removed. - examples/minimal_ip_ban.cpp: rewritten to demonstrate block_ip under the default ACCEPT policy. - examples/daemon_info.cpp: sweet_kill -> stop_and_wait. - README.md: bullets + code samples updated. Acceptance criteria verified: - grep '\\bsweet_kill\\b' src/httpserver/*.hpp src/*.cpp -> empty - grep '\\b(ban_ip|unban_ip|allow_ip|disallow_ip)\\b' src/httpserver/*.hpp -> empty - camelCase grep on src/httpserver/*.hpp returns pre-existing matches only (comments, MHD/GnuTLS types, generateFilenameException, shoutCAST). No method names introduced or touched by this task use camelCase. - make check (release + --enable-debug): 40/40 pass. - cpplint: clean on all touched files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent cf31239 commit ec82d90

7 files changed

Lines changed: 89 additions & 317 deletions

File tree

README.md

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ Once a webserver is created, you can manage its execution through the following
544544
* _**void** webserver::start(**bool** blocking):_ Allows to start a server. If the `blocking` flag is passed as `true`, it will block the execution of the current thread until a call to stop on the same webserver object is performed.
545545
* _**void** webserver::stop():_ Allows to stop a server. It immediately stops it.
546546
* _**bool** webserver::is_running():_ Checks if a server is running
547-
* _**void** webserver::sweet_kill():_ Allows to stop a server. It doesn't guarantee an immediate halt to allow for thread termination and connection closure.
547+
* _**void** webserver::stop_and_wait():_ Stop the webserver and wait for in-flight handlers to complete before returning. Use `stop()` when no such guarantee is required.
548548
* _**int** webserver::quiesce():_ Quiesce the daemon: stop accepting new connections while letting in-flight requests complete. Returns the listen socket file descriptor (the caller can close it), or `-1` on error.
549549
* _**bool** webserver::run():_ Run the webserver's event loop once (non-blocking). For use with external event loops when the server is started without internal threading. Returns `true` on success.
550550
* _**bool** webserver::run_wait(**int32_t** millisec):_ Run the webserver's event loop, blocking until there is activity or the timeout expires. Pass `-1` for indefinite wait. Returns `true` on success.
@@ -946,10 +946,8 @@ libhttpserver provides natively a system to blacklist and whitelist IP addresses
946946
The system supports both IPV4 and IPV6 and manages them transparently. The only requirement is for ipv6 to be enabled on your server - you'll have to enable this by using the `use_ipv6` method on `create_webserver`.
947947
948948
You can explicitly ban or allow an IP address using the following methods on the `webserver` class:
949-
* _**void** ban_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to ban. To use when the `default_policy` is `ACCEPT`.
950-
* _**void** allow_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to allow. To use when the `default_policy` is `REJECT`.
951-
* _**void** unban_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `ACCEPT`.
952-
* _**void** disallow_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `REJECT`.
949+
* _**void** block_ip(**std::string_view** ip):_ Add one IP (or a range, e.g. `"127.0.0.*"`) to the block list. Connections from a matching address are refused at the policy callback. Intended for use under the default `ACCEPT` policy.
950+
* _**void** unblock_ip(**std::string_view** ip):_ Remove one IP (or a range) from the block list. Idempotent: removing an entry that is not currently blocked is a no-op.
953951
954952
### IP String Format
955953
The IP string format can represent both IPV4 and IPV6. Addresses will be normalized by the webserver to operate in the same sapce. Any valid IPV4 or IPV6 textual representation works.
@@ -977,10 +975,11 @@ Examples of valid IPs include:
977975
};
978976
979977
int main(int argc, char** argv) {
980-
webserver ws = create_webserver(8080)
981-
.default_policy(http::http_utils::REJECT);
978+
webserver ws = create_webserver(8080);
982979
983-
ws.allow_ip("127.0.0.1");
980+
// Refuse connections from this address; everything else is accepted
981+
// by default. Use a range like "127.0.0.*" to block a wildcard.
982+
ws.block_ip("10.0.0.1");
984983
985984
hello_world_resource hwr;
986985
ws.register_resource("/hello", &hwr);
@@ -1728,7 +1727,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver
17281727
std::cout << "HTTP 404 reason: "
17291728
<< http::http_utils::reason_phrase(404) << std::endl;
17301729

1731-
ws.sweet_kill();
1730+
ws.stop_and_wait();
17321731
return 0;
17331732
}
17341733
```

examples/daemon_info.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ int main() {
5353
<< ". Press Ctrl+C to stop." << std::endl;
5454

5555
// Block until interrupted
56-
ws.sweet_kill();
56+
ws.stop_and_wait();
5757

5858
return 0;
5959
}

examples/minimal_ip_ban.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ class hello_world_resource : public httpserver::http_resource {
2929
}
3030
};
3131

32+
// TASK-029: The v2.0 public IP-control API is the pair block_ip / unblock_ip,
33+
// usable under the default ACCEPT policy. The historical allow_ip /
34+
// disallow_ip pair (under REJECT) was dropped from the public surface.
3235
int main() {
33-
httpserver::webserver ws = httpserver::create_webserver(8080).default_policy(httpserver::http::http_utils::REJECT);
36+
httpserver::webserver ws = httpserver::create_webserver(8080);
3437

35-
ws.allow_ip("127.0.0.1");
38+
ws.block_ip("10.0.0.1");
3639

3740
auto hwr = std::make_shared<hello_world_resource>();
3841
ws.register_path("/hello", hwr);

src/httpserver/webserver.hpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <functional>
3333
#include <memory>
3434
#include <string>
35+
#include <string_view>
3536
#include <type_traits>
3637
#include <utility>
3738
#include <vector>
@@ -298,10 +299,21 @@ class webserver {
298299
* and unregister_prefix; idempotent.
299300
**/
300301
void unregister_resource(const std::string& path);
301-
void ban_ip(const std::string& ip);
302-
void allow_ip(const std::string& ip);
303-
void unban_ip(const std::string& ip);
304-
void disallow_ip(const std::string& ip);
302+
303+
/**
304+
* Add @p ip (or a range, e.g. "127.0.0.*") to the IP block list.
305+
* Connections from a matching address are refused at the policy
306+
* callback. Intended for use under the default ACCEPT policy.
307+
* No-op semantics are preserved when the same IP is added twice;
308+
* a more specific entry replaces a previously-recorded wildcard.
309+
**/
310+
void block_ip(std::string_view ip);
311+
312+
/**
313+
* Remove @p ip from the IP block list. Idempotent: removing an IP
314+
* that is not currently blocked is a no-op.
315+
**/
316+
void unblock_ip(std::string_view ip);
305317

306318
log_access_ptr get_access_logger() const {
307319
return log_access;
@@ -320,9 +332,10 @@ class webserver {
320332
}
321333

322334
/**
323-
* Method used to kill the webserver waiting for it to terminate
335+
* Stop the webserver and wait for in-flight handlers to complete
336+
* before returning. Use stop() when no such guarantee is required.
324337
**/
325-
void sweet_kill();
338+
void stop_and_wait();
326339

327340
/**
328341
* Run the webserver's event loop once (non-blocking).

src/webserver.cpp

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ webserver::~webserver() {
243243
// when the unique_ptr is destroyed, after this body finishes.
244244
}
245245

246-
void webserver::sweet_kill() {
246+
void webserver::stop_and_wait() {
247247
stop();
248248
}
249249

@@ -1088,38 +1088,27 @@ void webserver::unregister_resource(const string& resource) {
10881088
impl_->route_cache_v2.clear();
10891089
}
10901090

1091-
void webserver::ban_ip(const string& ip) {
1091+
// TASK-029: The v2.0 public IP-control API is the pair block_ip / unblock_ip.
1092+
// The historical ban_ip / unban_ip / allow_ip / disallow_ip quartet was
1093+
// collapsed to a single name pair operating on the deny list. The internal
1094+
// allowances set and the allow-list branch in policy_callback remain in
1095+
// place so default_policy(REJECT) keeps working at the daemon level, but
1096+
// they are no longer reachable from the public API.
1097+
void webserver::block_ip(std::string_view ip) {
10921098
std::unique_lock bans_lock(impl_->bans_mutex);
1093-
ip_representation t_ip(ip);
1094-
set<ip_representation>::iterator it = impl_->bans.find(t_ip);
1095-
if (it != impl_->bans.end() && (t_ip.weight() < (*it).weight())) {
1099+
ip_representation t_ip{std::string{ip}};
1100+
auto it = impl_->bans.find(t_ip);
1101+
if (it != impl_->bans.end() && (t_ip.weight() < it->weight())) {
10961102
impl_->bans.erase(it);
10971103
impl_->bans.insert(t_ip);
10981104
} else {
10991105
impl_->bans.insert(t_ip);
11001106
}
11011107
}
11021108

1103-
void webserver::allow_ip(const string& ip) {
1104-
std::unique_lock allowances_lock(impl_->allowances_mutex);
1105-
ip_representation t_ip(ip);
1106-
set<ip_representation>::iterator it = impl_->allowances.find(t_ip);
1107-
if (it != impl_->allowances.end() && (t_ip.weight() < (*it).weight())) {
1108-
impl_->allowances.erase(it);
1109-
impl_->allowances.insert(t_ip);
1110-
} else {
1111-
impl_->allowances.insert(t_ip);
1112-
}
1113-
}
1114-
1115-
void webserver::unban_ip(const string& ip) {
1109+
void webserver::unblock_ip(std::string_view ip) {
11161110
std::unique_lock bans_lock(impl_->bans_mutex);
1117-
impl_->bans.erase(ip_representation(ip));
1118-
}
1119-
1120-
void webserver::disallow_ip(const string& ip) {
1121-
std::unique_lock allowances_lock(impl_->allowances_mutex);
1122-
impl_->allowances.erase(ip_representation(ip));
1111+
impl_->bans.erase(ip_representation{std::string{ip}});
11231112
}
11241113

11251114
namespace detail {

0 commit comments

Comments
 (0)