We were testing Crow. We noticed when sending DDoS requests, it hits the fd soft limit, causing new fd open()s fail with errno 24 "Too many open files".
We discovered that during each handling in do_accept() in http_server.h, the shared pointer of Connection "p" is copied to the lambda in asio::io_service's post(), causing it to be enqueued and handled later. If we hammer with for example 2000 requests, each request takes several hundred milliseconds, Crow keeps creating and holding new Connections until hitting the 1024 fd limit.
void do_accept()
{
if (!shutting_down_)
{
uint16_t service_idx = pick_io_service_idx();
asio::io_service& is = *io_service_pool_[service_idx];
task_queue_length_pool_[service_idx]++;
CROW_LOG_DEBUG << &is << " {" << service_idx << "} queue length: " << task_queue_length_pool_[service_idx];
auto p = std::make_shared<Connection<Adaptor, Handler, Middlewares...>>(
is, handler_, server_name_, middlewares_,
get_cached_date_str_pool_[service_idx], *task_timer_pool_[service_idx], adaptor_ctx_, task_queue_length_pool_[service_idx]);
acceptor_.async_accept(
p->socket(),
[this, p, &is, service_idx](error_code ec) {
if (!ec)
{
is.post(
[p] {
p->start();
});
}
else
{
task_queue_length_pool_[service_idx]--;
CROW_LOG_DEBUG << &is << " {" << service_idx << "} queue length: " << task_queue_length_pool_[service_idx];
}
do_accept();
});
}
}
We did a temporary workaround:
We added a blocking check before is.post(...) -- If the task queue is larger than max_task_queue_length_, the new Connection won't be forwarded to p->start().
acceptor_.async_accept(
p->socket(),
[this, p, &is, service_idx](error_code ec) {
// New change
if(max_task_queue_length_.has_value() && task_queue_length_pool_[service_idx] > max_task_queue_length_.value())
{
CROW_LOG_DEBUG << "Too many queued tasks for io_service " << &is << " {" << service_idx << "}, rejecting connection. Queue length: " << task_queue_length_pool_[service_idx];
}
else if (!ec)
{
is.post(
[p] {
p->start();
});
}
This works for now, but we would like to know is there a better solution on the fd level in Crow? Thanks!
We were testing Crow. We noticed when sending DDoS requests, it hits the fd soft limit, causing new fd open()s fail with errno 24 "Too many open files".
We discovered that during each handling in
do_accept()inhttp_server.h, the shared pointer ofConnection"p" is copied to the lambda inasio::io_service'spost(), causing it to be enqueued and handled later. If we hammer with for example 2000 requests, each request takes several hundred milliseconds, Crow keeps creating and holding newConnections until hitting the 1024 fd limit.We did a temporary workaround:
We added a blocking check before
is.post(...)-- If the task queue is larger thanmax_task_queue_length_, the new Connection won't be forwarded top->start().This works for now, but we would like to know is there a better solution on the fd level in Crow? Thanks!