mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-08-11 21:01:24 +00:00
* Close the listening socket in stop() even when not serving Server::stop() released svr_sock_ only under `if (is_running_)`, and is_running_ is set inside listen_internal(). A server that bound with bind_to_port() / bind_to_any_port() and never reached listen_after_bind() therefore kept its listening descriptor for the life of the process: ~Server() is defaulted and svr_sock_ is a bare atomic<socket_t>, so nothing else closes it. The port stayed held too. Close whenever the socket is open instead. The exchange already made this safe against a concurrent accept loop, so dropping the is_running_ gate also removes a TOCTOU between the check and the exchange. * Fail listen_after_bind() when stop() already closed the socket With stop() now releasing a bound-but-not-serving socket, a stop() that lands between bind and listen used to slip through listen_internal(): the accept loop saw INVALID_SOCKET, never iterated, and returned success without ever serving, pulsing is_running_ just long enough that a wait_until_ready() caller could miss it and spin forever. Return false instead and mark the server decommissioned the way any failed listen does, so waiters wake up. Also drop the assert() in stop(). It read is_running_ and svr_sock_ separately, which is exactly the race the exchange removes: a second stop() while the accept loop is still unwinding sees is_running_ true and svr_sock_ already INVALID_SOCKET, aborting debug builds. The regression test checks the released port with a raw connect() instead of a Client request: against the old code the connection is accepted into the backlog and never answered, which would hang the test rather than fail it.