diff --git a/conan.lock b/conan.lock index ff0ff718..9447dfe6 100644 --- a/conan.lock +++ b/conan.lock @@ -13,7 +13,7 @@ "miniz/3.0.2#bfbce07c6654293cce27ee24129d2df7%1743673472.805", "gtest/1.14.0#f8f0757a574a8dd747d16af62d6eb1b7%1743410807.169", "cryptopp/8.9.0#7a51e0038756b21bc3a6b82d681d5906%1758206597.119", - "cpp-httplib/0.16.3#7aa89fbb81ffd19539a49fc132502966%1748426320.106", + "cpp-httplib/0.47.0#add6673ff352c26898ed2650453e706e%1784539639.401", "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1762886692.465", "argon2/20190702-odr#965901884bc82ec8a7c0a1305d42c127%1784987057.981858" ], diff --git a/conanfile.py b/conanfile.py index 100abcc4..67687c06 100644 --- a/conanfile.py +++ b/conanfile.py @@ -54,7 +54,7 @@ def requirements(self): self.requires("uchardet/0.0.8") self.requires("utfcpp/4.0.9") if self.options.get_safe("with_http_server", False): - self.requires("cpp-httplib/0.16.3") + self.requires("cpp-httplib/0.47.0") self.requires("argon2/20190702-odr") if self.options.get_safe("with_python", False): self.requires("pybind11/2.13.6") diff --git a/src/odr/http_server.cpp b/src/odr/http_server.cpp index 7367b3f1..bdcc4705 100644 --- a/src/odr/http_server.cpp +++ b/src/odr/http_server.cpp @@ -215,6 +215,9 @@ class HttpServer::Impl : public std::enable_shared_from_this { ? static_cast(port) : -1); if (bound < 0) { + // a failed bind decommissions the server and only stop() clears that, so + // without this no later bind - a fallback to any port - could succeed + m_server->stop(); throw ServerBindFailed(host, port); } diff --git a/test/src/http_server_test.cpp b/test/src/http_server_test.cpp index 8b3c86a6..25c99cd7 100644 --- a/test/src/http_server_test.cpp +++ b/test/src/http_server_test.cpp @@ -53,6 +53,20 @@ TEST(HttpServer, bind_reports_a_port_in_use) { taken.stop(); } +TEST(HttpServer, bind_can_be_retried_after_it_failed) { + const HttpServer taken; + const std::uint32_t port = taken.bind("127.0.0.1", 0); + + // falling back to any port is the reason a failed bind may not be terminal: + // cpp-httplib decommissions the server on one, and only stop() undoes that + const HttpServer other; + EXPECT_THROW(other.bind("127.0.0.1", port), ServerBindFailed); + EXPECT_NE(other.bind("127.0.0.1", 0), 0); + + other.stop(); + taken.stop(); +} + TEST(HttpServer, listen_without_bind_is_refused) { const HttpServer server;