You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost Beast服务接收JSON负载时返回body limit exceeded问题

Boost Beast服务大JSON负载报错问题

我维护着一个前同事开发的Boost Beast服务,之前运行正常,但当JSON负载增大到约2.4MB时,服务返回"body limit exceeded"错误。查阅文档得知默认请求体限制是1MB,尝试修改限制后,发送负载又出现"Unknown HTTP request"错误,需要解决。

服务由ServerService、Listener、Session三个类实现,代码如下:

ServerService

void ServerService::startServer(const std::string& address, const unsigned short& port,
                                 const std::string& baseRessourceName, const unsigned short& threadNumber)
{
    try
    {
        const auto srvAddress = boost::asio::ip::make_address(address);

        // The io_context is required for all I/O
        auto const nbThreads = std::max<int>(1, threadNumber);
        boost::asio::io_context ioContext(nbThreads);

        // Create listener and launch a listening port
        std::shared_ptr<Listener> listener = std::make_shared<Listener>(ioContext, tcp::endpoint{ srvAddress, port }, baseRessourceName);
        listener->run();

        // Run the I/O service on the requested number of threads
        std::vector<std::thread> threads;
        threads.reserve(nbThreads - 1);
        for (auto i = nbThreads - 1; i > 0; --i)
        {
            threads.emplace_back([&ioContext] { ioContext.run(); });
        }
        ioContext.run();
    }
    catch (std::exception const& e)
    {
        LBC_ERROR("{}", e.what());
    }
}

Listener

// Used namespace
using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>


namespace Http
{
    class Listener : public std::enable_shared_from_this<Listener>
    {
    private:
        tcp::acceptor m_acceptor;
        tcp::socket m_socket;
        std::string const& m_baseResourceName;

        // Report a failure
        void logError(boost::system::error_code errorCode, char const* what)
        {
            LBC_ERROR("{}: {}", what, errorCode.message());
        }

    public:
        Listener(boost::asio::io_context& ioContext, tcp::endpoint endpoint, std::string const& docRoot)
            : m_acceptor(ioContext)
            , m_socket(ioContext)
            , m_baseResourceName(docRoot)
        {
            boost::system::error_code errorCode;

            // Open the acceptor
            m_acceptor.open(endpoint.protocol(), errorCode);
            if (errorCode)
            {
                logError(errorCode, "open");
                return;
            }

            // Allow address reuse
            m_acceptor.set_option(boost::asio::socket_base::reuse_address(true));
            if (errorCode)
            {
                logError(errorCode, "set_option");
                return;
            }

            // Bind to the server address
            m_acceptor.bind(endpoint, errorCode);
            if (errorCode)
            {
                logError(errorCode, "bind");
                return;
            }

            // Start listening for connections
            m_acceptor.listen(boost::asio::socket_base::max_listen_connections, errorCode);
            if (errorCode)
            {
                logError(errorCode, "listen");
                return;
            }
        }

        // Start accepting incoming connections
        void run()
        {
            if (!m_acceptor.is_open()) {
                return;
            }
            doAccept();
        }

        void doAccept()
        {
            m_acceptor.async_accept(m_socket,
                std::bind(
                    &Listener::onAccept,
                    shared_from_this(),
                    std::placeholders::_1));
        }

        void onAccept(boost::system::error_code errorCode)
        {
            if (errorCode)
            {
                logError(errorCode, "accept");
            }
            else
            {
                // Create the session and run it
                std::make_shared<Session>(
                    std::move(m_socket),
                    m_baseResourceName)->run();
            }

            // Accept another connection
            doAccept();
        }
    };
} // namespace Http

Session

// Used namespaces
using tcp = boost::asio::ip::tcp;           // from <boost/asio/ip/tcp.hpp>
namespace boostHttp = boost::beast::http;   // from <boost/beast/http.hpp>


namespace Http
{
    class Session : public std::enable_shared_from_this<Session>
    {
    private:
        // This is the C++11 equivalent of a generic lambda.
        // The function object is used to send an HTTP message.
        struct send_lambda
        {
            Session& self_;

            explicit send_lambda(Session& self) : self_(self) {}

            template<bool isRequest, class Body, class Fields>
            void operator()(boostHttp::message<isRequest, Body, Fields>&& msg) const
            {
                // The lifetime of the message has to extend
                // for the duration of the async operation so
                // we use a shared_ptr to manage it.
                auto sp = std::make_shared<boostHttp::message<isRequest, Body, Fields>>(std::move(msg));

                // Store a type-erased version of the shared
                // pointer in the class to keep it alive.
                self_.res_ = sp;

                // Write the response
                boostHttp::async_write(self_.socket_, *sp,
                    boost::asio::bind_executor(
                        self_.strand_, std::bind(
                            &Session::onWrite,
                            self_.shared_from_this(),
                            std::placeholders::_1,
                            std::placeholders::_2,
                            sp->need_eof())));
            }
        };

        // Report a failure
        void logError(boost::system::error_code errorCode, char const* what)
        {
            LBC_ERROR("{}: {}", what, errorCode.message());
        }

        tcp::socket socket_;
        boost::asio::strand<boost::asio::any_io_executor> strand_;
        boost::beast::flat_buffer buffer_;
        std::string const& baseResourceName_;
        boostHttp::request<boostHttp::string_body> req_;
        std::shared_ptr<void> res_;
        send_lambda lambda_;

    public:
        // Take ownership of the socket
        explicit Session(tcp::socket socket, std::string const& docRoot)
            : socket_(std::move(socket))
            , strand_(socket_.get_executor())
            , baseResourceName_(docRoot)
            , lambda_(*this)
        {}

        // Start the asynchronous operation
        void run()
        {
            doRead();
        }

        void doRead()
        {
            // Make the request empty before reading,
            // otherwise the operation behavior is undefined.
            req_ = {};

            // Read a request
            boostHttp::async_read(socket_, buffer_, req_,
                boost::asio::bind_executor(
                    strand_, std::bind(
                        &Session::onRead,
                        shared_from_this(),
                        std::placeholders::_1,
                        std::placeholders::_2)));
        }

        void onRead(boost::system::error_code errorCode, std::size_t transferredBytes)
        {
            boost::ignore_unused(transferredBytes);

            // This means they closed the connection
            if (errorCode == boostHttp::error::end_of_stream)
            {
                return doClose();
            }

            if (errorCode) {
                return logError(errorCode, "*** read");  // Error is here
            }

            // Some stuff here to manage request

        }

        void onWrite(boost::system::error_code ec, std::size_t transferredBytes, bool close)
        {
            boost::ignore_unused(transferredBytes);

            if (ec)
            {
                return logError(ec, "write");
            }

            if (close)
            {
                // This means we should close the connection, usually because
                // the response indicated the "Connection: close" semantic.
                return doClose();
            }

            // We're done with the response so delete it
            res_ = nullptr;

            // Read another request
            doRead();
        }

        void doClose()
        {
            // Send a TCP shutdown
            boost::system::error_code ec;
            socket_.shutdown(tcp::socket::shutdown_send, ec);

            // At this point the connection is closed gracefully
        }
    };
} // namespace Http

服务启动代码

Service::ServerService serverService;
serverService.startServer("127.0.0.1", 8080, "service_name", 5);

解决方案

"Unknown HTTP request"错误是因为修改请求体限制的方式不正确。Boost Beast默认async_read使用的是默认配置的解析器,直接修改请求对象无法生效,正确做法是显式创建request_parser并设置body_limit:

  1. 修改Session类私有成员
    替换原有的boostHttp::request<boostHttp::string_body> req_;为解析器对象:
boostHttp::request_parser<boostHttp::string_body> parser_;
  1. 更新doRead方法
    初始化解析器并设置允许的最大请求体大小:
void doRead()
{
    // 重置解析器状态
    parser_.reset();
    // 设置3MB的请求体限制(可根据实际需求调整)
    parser_.body_limit(3 * 1024 * 1024);

    // 使用解析器异步读取请求
    boostHttp::async_read(socket_, buffer_, parser_,
        boost::asio::bind_executor(
            strand_, std::bind(
                &Session::onRead,
                shared_from_this(),
                std::placeholders::_1,
                std::placeholders::_2)));
}
  1. 调整onRead方法获取请求
    通过解析器获取解析后的完整请求对象:
void onRead(boost::system::error_code errorCode, std::size_t transferredBytes)
{
    boost::ignore_unused(transferredBytes);

    if (errorCode == boostHttp::error::end_of_stream)
    {
        return doClose();
    }

    if (errorCode) {
        return logError(errorCode, "*** read");
    }

    // 获取解析后的请求对象
    boostHttp::request<boostHttp::string_body> const& req = parser_.get();
    // 后续请求处理逻辑使用req替代原有的req_
    // Some stuff here to manage request

}

注意事项

  • 设置的body_limit需大于实际负载大小(比如2.4MB负载可设为3MB或更大)
  • 若负载可能持续增大,可考虑使用file_body或dynamic_body处理超大请求,避免内存占用过高

内容的提问来源于stack exchange,提问作者user19334409

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:03:20