Skip to content

Commit

Permalink
Merge pull request #918 from CrowCpp/885-there-are-no-exceptions-with…
Browse files Browse the repository at this point in the history
…-run_async

added wait timeout to server start
  • Loading branch information
gittiver authored Jan 15, 2025
2 parents 08f2b36 + 44bc055 commit b57abf2
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 27 deletions.
40 changes: 30 additions & 10 deletions include/crow/app.h
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ namespace crow
}

/// \brief Get the number of threads that server is using
std::uint16_t concurrency()
std::uint16_t concurrency() const
{
return concurrency_;
}
Expand Down Expand Up @@ -506,11 +506,18 @@ namespace crow
#endif
validate();

error_code ec;
asio::ip::address addr = asio::ip::make_address(bindaddr_,ec);
if (ec){
CROW_LOG_ERROR << ec.message() << " - Can not create valid ip address from string: \"" << bindaddr_ << "\"";
return;
}
tcp::endpoint endpoint(addr, port_);
#ifdef CROW_ENABLE_SSL
if (ssl_used_)
{
router_.using_ssl = true;
ssl_server_ = std::move(std::unique_ptr<ssl_server_t>(new ssl_server_t(this, bindaddr_, port_, server_name_, &middlewares_, concurrency_, timeout_, &ssl_context_)));
ssl_server_ = std::move(std::unique_ptr<ssl_server_t>(new ssl_server_t(this, endpoint, server_name_, &middlewares_, concurrency_, timeout_, &ssl_context_)));
ssl_server_->set_tick_function(tick_interval_, tick_function_);
ssl_server_->signal_clear();
for (auto snum : signals_)
Expand All @@ -523,7 +530,7 @@ namespace crow
else
#endif
{
server_ = std::move(std::unique_ptr<server_t>(new server_t(this, bindaddr_, port_, server_name_, &middlewares_, concurrency_, timeout_, nullptr)));
server_ = std::move(std::unique_ptr<server_t>(new server_t(this, endpoint, server_name_, &middlewares_, concurrency_, timeout_, nullptr)));
server_->set_tick_function(tick_interval_, tick_function_);
for (auto snum : signals_)
{
Expand Down Expand Up @@ -695,19 +702,32 @@ namespace crow
}

/// \brief Wait until the server has properly started
void wait_for_server_start()
std::cv_status wait_for_server_start(std::chrono::milliseconds wait_timeout = std::chrono::milliseconds(3000))
{
std::cv_status status = std::cv_status::no_timeout;
auto wait_until = std::chrono::steady_clock::now() + wait_timeout;
{
std::unique_lock<std::mutex> lock(start_mutex_);
while (!server_started_)
cv_started_.wait(lock);
while (!server_started_ && (status == std::cv_status::no_timeout))
{
status = cv_started_.wait_until(lock, wait_until);
}
}
if (server_)
server_->wait_for_start();

if (status == std::cv_status::no_timeout)
{
if (server_)
{
status = server_->wait_for_start(wait_until);
}
#ifdef CROW_ENABLE_SSL
else if (ssl_server_)
ssl_server_->wait_for_start();
else if (ssl_server_)
{
status = ssl_server_->wait_for_start(wait_until);
}
#endif
}
return status;
}

private:
Expand Down
34 changes: 20 additions & 14 deletions include/crow/http_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,20 @@ namespace crow // NOTE: Already documented in "crow/app.h"
class Server
{
public:
Server(Handler* handler, std::string bindaddr, uint16_t port, std::string server_name = std::string("Crow/") + VERSION, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, uint8_t timeout = 5, typename Adaptor::context* adaptor_ctx = nullptr):
acceptor_(io_context_, tcp::endpoint(asio::ip::make_address(bindaddr), port)),
Server(Handler* handler,
const tcp::endpoint& endpoint,
std::string server_name = std::string("Crow/") + VERSION,
std::tuple<Middlewares...>* middlewares = nullptr,
uint16_t concurrency = 1,
uint8_t timeout = 5,
typename Adaptor::context* adaptor_ctx = nullptr):
acceptor_(io_context_,endpoint),
signals_(io_context_),
tick_timer_(io_context_),
handler_(handler),
concurrency_(concurrency),
timeout_(timeout),
server_name_(server_name),
port_(port),
bindaddr_(bindaddr),
task_queue_length_pool_(concurrency_ - 1),
middlewares_(middlewares),
adaptor_ctx_(adaptor_ctx)
Expand Down Expand Up @@ -150,11 +154,12 @@ namespace crow // NOTE: Already documented in "crow/app.h"
});
}

port_ = acceptor_.local_endpoint().port();
handler_->port(port_);
handler_->port(acceptor_.local_endpoint().port());


CROW_LOG_INFO << server_name_ << " server is running at " << (handler_->ssl_used() ? "https://" : "http://") << bindaddr_ << ":" << acceptor_.local_endpoint().port() << " using " << concurrency_ << " threads";
CROW_LOG_INFO << server_name_
<< " server is running at " << (handler_->ssl_used() ? "https://" : "http://")
<< acceptor_.local_endpoint().address() << ":" << acceptor_.local_endpoint().port() << " using " << concurrency_ << " threads";
CROW_LOG_INFO << "Call `app.loglevel(crow::LogLevel::Warning)` to hide Info level logs.";

signals_.async_wait(
Expand Down Expand Up @@ -192,16 +197,19 @@ namespace crow // NOTE: Already documented in "crow/app.h"
io_context_.stop(); // Close main io_service
}

uint16_t port(){
uint16_t port() const {
return acceptor_.local_endpoint().port();
}

/// Wait until the server has properly started
void wait_for_start()
/// Wait until the server has properly started or until timeout
std::cv_status wait_for_start(std::chrono::steady_clock::time_point wait_until)
{
std::unique_lock<std::mutex> lock(start_mutex_);
while (!server_started_)
cv_started_.wait(lock);

std::cv_status status = std::cv_status::no_timeout;
while (!server_started_ && ( status==std::cv_status::no_timeout ))
status = cv_started_.wait_until(lock,wait_until);
return status;
}

void signal_clear()
Expand Down Expand Up @@ -290,8 +298,6 @@ namespace crow // NOTE: Already documented in "crow/app.h"
uint16_t concurrency_{2};
std::uint8_t timeout_;
std::string server_name_;
uint16_t port_;
std::string bindaddr_;
std::vector<std::atomic<unsigned int>> task_queue_length_pool_;

std::chrono::milliseconds tick_interval_;
Expand Down
23 changes: 20 additions & 3 deletions tests/unittest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,22 @@ TEST_CASE("server_handling_error_request")
app.stop();
} // server_handling_error_request

TEST_CASE("server_dynamic_port_allication")
TEST_CASE("server_invalid_ip_address")
{
SimpleApp app;
CROW_ROUTE(app, "/")
([] {
return "A";
});
auto _ = app.bindaddr("192.").port(45451).run_async();
auto state = app.wait_for_server_start();

// we should run into a timeout as the server will not started
CHECK(state==cv_status::timeout);
} // server_invalid_ip_address


TEST_CASE("server_dynamic_port_allocation")
{
SimpleApp app;
CROW_ROUTE(app, "/")
Expand All @@ -659,7 +674,7 @@ TEST_CASE("server_dynamic_port_allication")
asio::ip::make_address(LOCALHOST_ADDRESS), app.port()));
}
app.stop();
} // server_dynamic_port_allication
} // server_dynamic_port_allocation

TEST_CASE("server_handling_error_request_http_version")
{
Expand Down Expand Up @@ -1520,7 +1535,9 @@ struct NullSimpleMiddleware
TEST_CASE("middleware_simple")
{
App<NullMiddleware, NullSimpleMiddleware> app;
decltype(app)::server_t server(&app, LOCALHOST_ADDRESS, 45451);
asio::ip::address adr = asio::ip::make_address(LOCALHOST_ADDRESS);
tcp::endpoint ep(adr,45451);
decltype(app)::server_t server(&app, ep);
CROW_ROUTE(app, "/")
([&](const crow::request& req) {
app.get_context<NullMiddleware>(req);
Expand Down

0 comments on commit b57abf2

Please sign in to comment.