Почему мой сервер cpp-netlib застрял в std::condition_variable::подождите, когда обработаете запрос POST?

#c #c 11 #boost #cpp-netlib

Вопрос:

Я пишу асинхронный HTTP-сервер и клиент с использованием cpp-netlib на основе приведенного примера «Привет, мир». Сервер может обработать GET запрос, но не может получить тело запроса POST запроса. Затем я увидел проблему-823 cpp-netlib и добавил обработчик асинхронного подключения. Структура выглядит следующим образом(изменена из примера «загрузка файла»):

 ///
/// Custom exception type
///
struct file_uploader_exception : public std::runtime_error {
    file_uploader_exception(const std::string err) :
        std::runtime_error(err) {
    }
};

///
/// Encapsulates request amp; connection
///
struct file_uploader : std::enable_shared_from_this<file_uploader> {
    const server::requestamp; req;
    server::connection_ptr  conn;

    std::mutex              mtx;
    std::condition_variable condvar;

    //FILE* fp = NULL;
    std::string body;

public:
    file_uploader(const server::requestamp; req, const server::connection_ptramp; conn)
        : req(req)
        , conn(conn) {
        /*const std::string dest = destination(req);

        if (dest.find("/upload") != std::string::npos) {
            auto queries = get_queries(dest);
            auto fname = queries.find("filename");
            if (fname != queries.end()) {
                fp = ::fopen(fname->second.c_str(), "wb");
                if (!fp) {
                    throw file_uploader_exception("Failed to open file to write");
                }
            }
            else {
                throw file_uploader_exception("'filename' cannot be empty");
            }
        }*/
    }

    ~file_uploader() {
        /*if (fp) {
            ::fflush(fp);
            ::fclose(fp);
        }*/
    }

    ///
    /// Non blocking call to initiate the data transfer
    ///
    void async_recv() {
        std::cout << "async_recv()" << std::endl;
        std::size_t content_length = 0;
        auto constamp; headers = req.headers;
        for (auto item : headers) {
            if (boost::to_lower_copy(item.name) == "content-length") {
                content_length = std::stoll(item.value);
                break;
            }
        }

        read_chunk(conn, content_length);
    }

    ///
    /// The client shall wait by calling this until the transfer is done by
    /// the IO threadpool
    ///
    void wait_for_completion() {
        std::cout << "wait_for_completion()" << std::endl;
        std::unique_lock<std::mutex> _(mtx);
        condvar.wait(_);
    }

private:
    ///
    /// Parses the string and gets the query as a key-value pair
    ///
    /// @param [in] dest String containing the path and the queries, without the fragment,
    ///                  of the form "/path?key1=value1amp;key2=value2"
    ///
    std::map<std::string, std::string> get_queries(const std::string dest) {
        std::cout << "get_queries()" << std::endl;

        std::size_t pos = dest.find_first_of("?");

        std::map<std::string, std::string> queries;
        if (pos != std::string::npos) {
            std::string query_string = dest.substr(pos   1);

            // Replace 'amp;' with space
            for (pos = 0; pos < query_string.size(); pos  ) {
                if (query_string[pos] == 'amp;') {
                    query_string[pos] = ' ';
                }
            }

            std::istringstream sin(query_string);
            while (sin >> query_string) {

                pos = query_string.find_first_of("=");

                if (pos != std::string::npos) {
                    const std::string key = query_string.substr(0, pos);
                    const std::string value = query_string.substr(pos   1);
                    queries[key] = value;
                }
            }
        }

        return queries;
    }

    ///
    /// Reads a chunk of data
    ///
    /// @param [in] conn        Connection to read from
    /// @param [in] left2read   Size to read
    ///
    void read_chunk(server::connection_ptr conn, std::size_t left2read) {
        std::cout << "read_chunk()" << std::endl;
        conn->read(boost::bind(amp;file_uploader::on_data_ready,
            file_uploader::shared_from_this(),
            _1, _2, _3, conn, left2read));
    }

    ///
    /// Callback that gets called when the data is ready to be consumed
    ///
    void on_data_ready(server::connection::input_range range,
        boost::system::error_code error,
        std::size_t size,
        server::connection_ptr conn,
        std::size_t left2read) {
        std::cout << "on_data_ready()" << std::endl;
        if (!error) {
            //::fwrite(boost::begin(range), size, 1, fp);
            body.append(boost::begin(range), boost::begin(range)   size);
            std::size_t left = left2read - size;
            if (left > 0)
                read_chunk(conn, left);
            else
                wakeup();
        }
    }

    ///
    /// Wakesup the waiting thread
    ///
    void wakeup() {
        std::cout << "wakeup()" << std::endl;
        std::unique_lock<std::mutex> _(mtx);
        condvar.notify_one();
    }
};

 

Когда я использую свой клиент или curl для выполнения POST запроса, сервер выводит следующие данные:

 PS F:CBPLibaryx64Release> .ServerTest.exe 0.0.0.0 40000
async_recv()
read_chunk()
wait_for_completion()
 

А потом все застревает.Сервер не отвечает ни на какие другие запросы, и клиент тоже зависает.
Я хочу знать, почему это произошло и как это решить.
Мой серверный код:

 struct hello_world {
    void operator()(server::request constamp; request, server::connection_ptr connection) {
        std::shared_ptr<file_uploader> uploader(new file_uploader(request, connection));
        uploader->async_recv();
        uploader->wait_for_completion();
        std::cout << uploader->body << std::endl;
        server::string_type ip = source(request);
        server::response_header headers[] = { {"Connection","close"} ,{"Content-Type", "text/plain"} };
        unsigned int port = request.source_port;
        std::ostringstream data;
        data << "Hello, " << ip << '[' << port << "]!";
        connection->set_headers(boost::make_iterator_range(headers, headers   2));
        connection->set_status(server::connection::ok);
        connection->write(data.str());
    }
};
int main(int argc, char* argv[]) {

    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " address port" << std::endl;
        return 1;
    }

    try {
        hello_world handler;
        server::options options(handler);
        options.thread_pool(std::make_shared<boost::network::utils::thread_pool>());
        server server_(options.address(argv[1]).port(argv[2]));
        std::thread t_server([amp;server_] { server_.run(); });
        //server_.run();
        t_server.detach();
        char ch;
        do
        {
            ch = getchar();
        } while (ch != '0');
        server_.stop();
    }
    catch (std::exceptionamp; e) {
        std::cerr << e.what() << std::endl;
        return 1;
    }

    return 0;
}
 

My client code:

 namespace http = boost::network::http;
using header = std::pair<std::string, std::string>;
int main(int argc, char* argv[]) {
    if (argc != 3) {
        std::cerr << "Usage: " << argv[0] << " address port" << std::endl;
        return 1;
    }

    try {
        std::string post_body = "test";
        http::client client;
        std::ostringstream url;
        url << "http://" << argv[1] << ":" << argv[2] << "/command";
        http::client::request request(url.str());
        request.add_header(header("Connection", "keep-alive"));
        request.add_header(header("Content-Type", "text/plain"));
        //http::client::response response = client.get(request);
        http::client::response response = client.post(request, post_body);
        std::cout << body(response) << std::endl;
    }
    catch (std::exceptionamp; e) {
        std::cerr << e.what() << std::endl;
        return 1;
    }
    return 0;
}
 

The curl command I use:

 curl.exe -d "test" -X POST http://127.0.0.1:40000/command
 

My develop environment:

 OS: Windows 11 build 22504
boost library version: 1.77.0
cpp-netlib version: 0.13.0
IDE: Visual Studio 2022 (MSVC17,Build tool v143)