diff --git a/cmake/develop.cmake b/cmake/develop.cmake index e9ec2529..864fb2b5 100644 --- a/cmake/develop.cmake +++ b/cmake/develop.cmake @@ -39,11 +39,7 @@ int main() endmacro() # Enable address sanitizer -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - option(ENABLE_SANITIZER "Enable sanitizer(Debug+Gcc/Clang/AppleClang)" OFF) -else() - option(ENABLE_SANITIZER "Enable sanitizer(Debug+Gcc/Clang/AppleClang)" ON) -endif() +option(ENABLE_SANITIZER "Enable sanitizer(Debug+Gcc/Clang/AppleClang)" ON) if(ENABLE_SANITIZER AND NOT MSVC) if(CMAKE_BUILD_TYPE STREQUAL "Debug") check_asan(HAS_ASAN) diff --git a/example/benchmark.cpp b/example/benchmark.cpp index 45d55f53..ef0c6640 100644 --- a/example/benchmark.cpp +++ b/example/benchmark.cpp @@ -4,7 +4,8 @@ using namespace cinatra; using namespace std::chrono_literals; int main() { - coro_http_server server(std::thread::hardware_concurrency(), 8090, true); + coro_http_server server(std::thread::hardware_concurrency(), 8090, + "127.0.0.1", true); server.set_http_handler( "/plaintext", [](coro_http_request& req, coro_http_response& resp) { resp.get_conn()->set_multi_buf(false); diff --git a/include/cinatra/coro_http_server.hpp b/include/cinatra/coro_http_server.hpp index 86b08ed2..e9220be4 100644 --- a/include/cinatra/coro_http_server.hpp +++ b/include/cinatra/coro_http_server.hpp @@ -27,16 +27,37 @@ enum class file_resp_format_type { }; class coro_http_server { public: - coro_http_server(asio::io_context &ctx, unsigned short port) - : out_ctx_(&ctx), port_(port), acceptor_(ctx), check_timer_(ctx) {} + coro_http_server(asio::io_context &ctx, unsigned short port, + std::string address = "0.0.0.0") + : out_ctx_(&ctx), port_(port), acceptor_(ctx), check_timer_(ctx) { + init_address(std::move(address)); + } + + coro_http_server(asio::io_context &ctx, + std::string address /* = "0.0.0.0:9001" */) + : out_ctx_(&ctx), acceptor_(ctx), check_timer_(ctx) { + init_address(std::move(address)); + } coro_http_server(size_t thread_num, unsigned short port, - bool cpu_affinity = false) + std::string address = "0.0.0.0", bool cpu_affinity = false) : pool_(std::make_unique(thread_num, cpu_affinity)), port_(port), acceptor_(pool_->get_executor()->get_asio_executor()), - check_timer_(pool_->get_executor()->get_asio_executor()) {} + check_timer_(pool_->get_executor()->get_asio_executor()) { + init_address(std::move(address)); + } + + coro_http_server(size_t thread_num, + std::string address /* = "0.0.0.0:9001" */, + bool cpu_affinity = false) + : pool_(std::make_unique(thread_num, + cpu_affinity)), + acceptor_(pool_->get_executor()->get_asio_executor()), + check_timer_(pool_->get_executor()->get_asio_executor()) { + init_address(std::move(address)); + } ~coro_http_server() { CINATRA_LOG_INFO << "coro_http_server will quit"; @@ -64,21 +85,22 @@ class coro_http_server { // only call once, not thread safe. async_simple::Future async_start() { - auto ec = listen(); + errc_ = listen(); async_simple::Promise promise; auto future = promise.getFuture(); - if (ec == std::errc{}) { + if (errc_ == std::errc{}) { if (out_ctx_ == nullptr) { thd_ = std::thread([this] { pool_->run(); }); } - accept().start([p = std::move(promise)](auto &&res) mutable { + accept().start([p = std::move(promise), this](auto &&res) mutable { if (res.hasError()) { - p.setValue(std::errc::io_error); + errc_ = std::errc::io_error; + p.setValue(errc_); } else { p.setValue(res.value()); @@ -86,7 +108,7 @@ class coro_http_server { }); } else { - promise.setValue(ec); + promise.setValue(errc_); } return future; @@ -488,16 +510,31 @@ class coro_http_server { return connections_.size(); } + std::string_view address() { return address_; } + std::errc get_errc() { return errc_; } + private: std::errc listen() { CINATRA_LOG_INFO << "begin to listen"; using asio::ip::tcp; - auto endpoint = tcp::endpoint(tcp::v4(), port_); - acceptor_.open(endpoint.protocol()); + asio::error_code ec; + auto addr = asio::ip::address::from_string(address_, ec); + if (ec) { + CINATRA_LOG_ERROR << "bad address: " << address_ + << " error: " << ec.message(); + return std::errc::bad_address; + } + + auto endpoint = tcp::endpoint(addr, port_); + acceptor_.open(endpoint.protocol(), ec); + if (ec) { + CINATRA_LOG_ERROR << "acceptor open failed" + << " error: " << ec.message(); + return std::errc::io_error; + } #ifdef __GNUC__ - acceptor_.set_option(tcp::acceptor::reuse_address(true)); + acceptor_.set_option(tcp::acceptor::reuse_address(true), ec); #endif - asio::error_code ec; acceptor_.bind(endpoint, ec); if (ec) { CINATRA_LOG_ERROR << "bind port: " << port_ << " error: " << ec.message(); @@ -508,7 +545,12 @@ class coro_http_server { #ifdef _MSC_VER acceptor_.set_option(tcp::acceptor::reuse_address(true)); #endif - acceptor_.listen(); + acceptor_.listen(asio::socket_base::max_listen_connections, ec); + if (ec) { + CINATRA_LOG_ERROR << "get local endpoint port: " << port_ + << " listen error: " << ec.message(); + return std::errc::io_error; + } auto end_point = acceptor_.local_endpoint(ec); if (ec) { @@ -749,11 +791,36 @@ class coro_http_server { response.set_delay(true); } + void init_address(std::string address) { + if (size_t pos = address.find(':'); pos != std::string::npos) { + auto port_sv = std::string_view(address).substr(pos + 1); + + uint16_t port; + auto [ptr, ec] = std::from_chars( + port_sv.data(), port_sv.data() + port_sv.size(), port, 10); + if (ec != std::errc{}) { + address_ = std::move(address); + return; + } + + port_ = port; + address = address.substr(0, pos); + } + + if (iequal0(address, "localhost")) { + address = "127.0.0.1"; + } + + address_ = std::move(address); + } + private: std::unique_ptr pool_; asio::io_context *out_ctx_ = nullptr; std::unique_ptr> out_executor_ = nullptr; uint16_t port_; + std::string address_; + std::errc errc_ = {}; asio::ip::tcp::acceptor acceptor_; std::thread thd_; std::promise acceptor_close_waiter_; diff --git a/tests/test_cinatra.cpp b/tests/test_cinatra.cpp index a5689685..8571c696 100644 --- a/tests/test_cinatra.cpp +++ b/tests/test_cinatra.cpp @@ -317,6 +317,64 @@ TEST_CASE("test select coro channel") { CHECK(val == 42); } +TEST_CASE("test bad address") { + { + coro_http_server server(1, 9001, "127.0.0.1"); + server.async_start(); + auto ec = server.get_errc(); + CHECK(ec == std::errc{}); + } + { + coro_http_server server(1, 9001, "localhost"); + server.async_start(); + auto ec = server.get_errc(); + CHECK(ec == std::errc{}); + } + { + coro_http_server server(1, 9001, "0.0.0.0"); + server.async_start(); + auto ec = server.get_errc(); + CHECK(ec == std::errc{}); + } + { + coro_http_server server(1, 9001); + server.async_start(); + auto ec = server.get_errc(); + CHECK(ec == std::errc{}); + } + { + coro_http_server server(1, "0.0.0.0:9001"); + server.async_start(); + auto ec = server.get_errc(); + CHECK(ec == std::errc{}); + } + { + coro_http_server server(1, "127.0.0.1:9001"); + server.async_start(); + auto ec = server.get_errc(); + CHECK(ec == std::errc{}); + } + { + coro_http_server server(1, "localhost:9001"); + server.async_start(); + auto ec = server.get_errc(); + CHECK(ec == std::errc{}); + } + + { + coro_http_server server(1, 9001, "x.x.x"); + server.async_start(); + auto ec = server.get_errc(); + CHECK(ec == std::errc::bad_address); + } + { + coro_http_server server(1, "localhost:aaa"); + server.async_start(); + auto ec = server.get_errc(); + CHECK(ec == std::errc::bad_address); + } +} + async_simple::coro::Lazy test_collect_all() { asio::io_context ioc; std::thread thd([&] {