SIGSEGV при отправке http::response через http::async_write в Boost.Beast

Ловлю SIGSEGV после отправки http::response через http::async_write. Клиент получает ответ, но на сервере до хэндлера http::async_write управление не доходит.

Ответ формируется следующим образом (по примеру из документации):

void func(std::shared_ptr<tcp::socket> socket)
{
    http::response<http::string_body> response(http::status::ok, 11);

    response.set(http::field::content_type, "json/application");
    response.body() = "{}";
    response.prepare_payload();

    async_write(socket, response);
}

Функция, куда передаётся ответ для последующей отправки:

void async_write(const std::shared_ptr<tcp::socket> &socket, http::response<http::string_body> &response)
{
    http::async_write(
        *socket,
        response,
        [this, socket](const system::error_code&, std::size_t)
        {
            socket->close()
        }
    );
}

Стэктрейс ошибки:

stackstrace

А строка, где программа прерывается, выглядит так:

template<class Allocator>
inline
boost::asio::const_buffer
basic_fields<Allocator>::
value_type::
buffer() const
{
    return boost::asio::const_buffer{
        reinterpret_cast<char const*>(this + 1),
        static_cast<std::size_t>(off_) + len_ + 2}; // <-
}

Важно отметить, что, если не определять тело ответа, то сигнал не генерируется, однако, хэндлер по прежнему не вызывается.


Ответы (1 шт):

Автор решения: Alexey Nikolaev

Как уже было отмечено в комментарии, проблема в том, что вы передаете в функцию async_write переменную response по константной ссылке. Так как операция асинхронная, она откладывает свое выполнение, а тем временем программа выходит из функции async_write, а затем и из функции func, в которой был создан объект response. Естественно, при выходе из функции func он разрушается, но не факт, что он успел отправиться асинхронно, в результате чего и получаем ошибку. Для решения проблемы, рекомендую вам создавать в функции func shared_ptr объекта response и передавать его в async_write, а также передать в лямбду по значению, чтобы этот объект не разрушился до вызова лямбды. Вы точно также поступаете с объектом socket. В результате функция async_write будет выглядеть так:

void async_write(std::shared_ptr<tcp::socket> socket, std::shared_ptr<http::response<http::string_body>> response)
{
    http::async_write(
        *socket,
        *response,
        [this, socket, response](const system::error_code&, std::size_t)
        {
            socket->close()
        }
    );
}
→ Ссылка