I'm writing an async HTTP server and client using cpp-netlib based on the given "Hello World" example. The server can process GET
request, but can't get the request body of POST
request. Then I saw the Issue-823 of the cpp-netlib and add an async connection handler. The struct as follows(Changed from the "file upload" example):
///
/// Custom exception type
///
struct file_uploader_exception : public std::runtime_error {
file_uploader_exception(const std::string err) :
std::runtime_error(err) {
}
};
///
/// Encapsulates request & connection
///
struct file_uploader : std::enable_shared_from_this<file_uploader> {
const server::request& req;
server::connection_ptr conn;
std::mutex mtx;
std::condition_variable condvar;
//FILE* fp = NULL;
std::string body;
public:
file_uploader(const server::request& req, const server::connection_ptr& 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 const& 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=value1&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 '&' with space
for (pos = 0; pos < query_string.size(); pos++) {
if (query_string[pos] == '&') {
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(&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();
}
};
When I use my client or curl to perform a POST
request, the server outputs as follows:
PS F:\CBPLibary\x64\Release> .\ServerTest.exe 0.0.0.0 40000
async_recv()
read_chunk()
wait_for_completion()
Then it stucks.The server does not respose to any other request, and the client stucks too. I want to know why this happened and how to solve it. My server code:
struct hello_world {
void operator()(server::request const& 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([&server_] { server_.run(); });
//server_.run();
t_server.detach();
char ch;
do
{
ch = getchar();
} while (ch != '0');
server_.stop();
}
catch (std::exception& 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::exception& 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)
Aucun commentaire:
Enregistrer un commentaire