Hello,
Would you mind sharing your code (perhaps in a git repo) so that I can reproduce, debug and advise?
No problem, the code is just a slightly modified version of the example: // // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/beast // //------------------------------------------------------------------------------ // // Example: HTTP server, asynchronous // //------------------------------------------------------------------------------ #include <boost/beast/core.hpp> #include <boost/beast/http.hpp> #include <boost/beast/version.hpp> #include <boost/asio/dispatch.hpp> #include <boost/asio/strand.hpp> #include <boost/asio/buffers_iterator.hpp> #include <boost/config.hpp> #include <algorithm> #include <cstdlib> #include <functional> #include <iostream> #include <memory> #include <string> #include <thread> #include <vector> namespace beast = boost::beast; // from <boost/beast.hpp> namespace http = beast::http; // from <boost/beast/http.hpp> namespace net = boost::asio; // from <boost/asio.hpp> using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> // Return a reasonable mime type based on the extension of a file. beast::string_view mime_type(beast::string_view path) { using beast::iequals; auto const ext = [&path] { auto const pos = path.rfind("."); if(pos == beast::string_view::npos) return beast::string_view{}; return path.substr(pos); }(); if(iequals(ext, ".htm")) return "text/html"; if(iequals(ext, ".html")) return "text/html"; if(iequals(ext, ".php")) return "text/html"; if(iequals(ext, ".css")) return "text/css"; if(iequals(ext, ".txt")) return "text/plain"; if(iequals(ext, ".js")) return "application/javascript"; if(iequals(ext, ".json")) return "application/json"; if(iequals(ext, ".xml")) return "application/xml"; if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; if(iequals(ext, ".flv")) return "video/x-flv"; if(iequals(ext, ".png")) return "image/png"; if(iequals(ext, ".jpe")) return "image/jpeg"; if(iequals(ext, ".jpeg")) return "image/jpeg"; if(iequals(ext, ".jpg")) return "image/jpeg"; if(iequals(ext, ".gif")) return "image/gif"; if(iequals(ext, ".bmp")) return "image/bmp"; if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; if(iequals(ext, ".tiff")) return "image/tiff"; if(iequals(ext, ".tif")) return "image/tiff"; if(iequals(ext, ".svg")) return "image/svg+xml"; if(iequals(ext, ".svgz")) return "image/svg+xml"; return "application/text"; } // Append an HTTP rel-path to a local filesystem path. // The returned path is normalized for the platform. std::string path_cat( beast::string_view base, beast::string_view path) { if(base.empty()) return std::string(path); std::string result(base); #ifdef BOOST_MSVC char constexpr path_separator = '\\'; if(result.back() == path_separator) result.resize(result.size() - 1); result.append(path.data(), path.size()); for(auto& c : result) if(c == '/') c = path_separator; #else char constexpr path_separator = '/'; if(result.back() == path_separator) result.resize(result.size() - 1); result.append(path.data(), path.size()); #endif return result; } // This function produces an HTTP response for the given // request. The type of the response object depends on the // contents of the request, so the interface requires the // caller to pass a generic lambda for receiving the response. template< class Body, class Allocator, class Send> void handle_request( beast::string_view doc_root, http::request<Body, http::basic_fields<Allocator>>&& req, Send&& send) { // Returns a bad request response auto const bad_request = [&req](beast::string_view why) { http::response<http::string_body> res{http::status::bad_request, req.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = std::string(why); res.prepare_payload(); return res; }; // Returns a not found response auto const not_found = [&req](beast::string_view target) { http::response<http::string_body> res{http::status::not_found, req.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = "The resource '" + std::string(target) + "' was not found."; res.prepare_payload(); return res; }; // Returns a server error response auto const server_error = [&req](beast::string_view what) { http::response<http::string_body> res{http::status::internal_server_error, req.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = "An error occurred: '" + std::string(what) + "'"; res.prepare_payload(); return res; }; if (req.method() == http::verb::post && req.target()=="/xxx" ) { for (const auto &h:req) printf("heder: %s=%s\n", std::string(h.name_string()).c_str(), std::string(h.value()).c_str()); printf("body as string:\n"); std::string body{ boost::asio::buffers_begin(req.body().data()), boost::asio::buffers_end(req.body().data()) }; printf("%s", body.c_str()); printf("end of body as string\n"); printf("body with iterator:\n"); const auto begin = boost::asio::buffers_begin(req.body().data()); const auto end = boost::asio::buffers_end(req.body().data()); unsigned x=0; for (auto it = begin; it != end; it++) putchar(*it); printf("end of body with iterator\n"); } // Make sure we can handle the method if( req.method() != http::verb::get && req.method() != http::verb::head) return send(bad_request("Unknown HTTP-method")); // Request path must be absolute and not contain "..". if( req.target().empty() || req.target()[0] != '/' || req.target().find("..") != beast::string_view::npos) return send(bad_request("Illegal request-target")); // Build the path to the requested file std::string path = path_cat(doc_root, req.target()); if(req.target().back() == '/') path.append("index.html"); // Attempt to open the file beast::error_code ec; http::file_body::value_type body; body.open(path.c_str(), beast::file_mode::scan, ec); // Handle the case where the file doesn't exist if(ec == beast::errc::no_such_file_or_directory) return send(not_found(req.target())); // Handle an unknown error if(ec) return send(server_error(ec.message())); // Cache the size since we need it after the move auto const size = body.size(); // Respond to HEAD request if(req.method() == http::verb::head) { http::response<http::empty_body> res{http::status::ok, req.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, mime_type(path)); res.content_length(size); res.keep_alive(req.keep_alive()); return send(std::move(res)); } // Respond to GET request http::response<http::file_body> res{ std::piecewise_construct, std::make_tuple(std::move(body)), std::make_tuple(http::status::ok, req.version())}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, mime_type(path)); res.content_length(size); res.keep_alive(req.keep_alive()); return send(std::move(res)); } //------------------------------------------------------------------------------ // Report a failure void fail(beast::error_code ec, char const* what) { std::cerr << what << ": " << ec.message() << "\n"; } // Handles an HTTP server connection class session : public std::enable_shared_from_this<session> { // 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()(http::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< http::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 http::async_write( self_.stream_, *sp, beast::bind_front_handler( &session::on_write, self_.shared_from_this(), sp->need_eof())); } }; beast::tcp_stream stream_; beast::flat_buffer buffer_; std::shared_ptr<std::string const> doc_root_; // http::request<http::string_body> req_; http::request<http::dynamic_body> req_; std::shared_ptr<void> res_; send_lambda lambda_; public: // Take ownership of the stream session( tcp::socket&& socket, std::shared_ptr<std::string const> const& doc_root) : stream_(std::move(socket)) , doc_root_(doc_root) , lambda_(*this) { } // Start the asynchronous operation void run() { // We need to be executing within a strand to perform async operations // on the I/O objects in this session. Although not strictly necessary // for single-threaded contexts, this example code is written to be // thread-safe by default. net::dispatch(stream_.get_executor(), beast::bind_front_handler( &session::do_read, shared_from_this())); } void do_read() { // Make the request empty before reading, // otherwise the operation behavior is undefined. req_ = {}; // Set the timeout. stream_.expires_after(std::chrono::seconds(30)); // Read a request http::async_read(stream_, buffer_, req_, beast::bind_front_handler( &session::on_read, shared_from_this())); } void on_read( beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); // This means they closed the connection if(ec == http::error::end_of_stream) return do_close(); if(ec) return fail(ec, "read"); // Send the response handle_request(*doc_root_, std::move(req_), lambda_); } void on_write( bool close, beast::error_code ec, std::size_t bytes_transferred) { boost::ignore_unused(bytes_transferred); if(ec) return fail(ec, "write"); if(close) { // This means we should close the connection, usually because // the response indicated the "Connection: close" semantic. return do_close(); } // We're done with the response so delete it res_ = nullptr; // Read another request do_read(); } void do_close() { // Send a TCP shutdown beast::error_code ec; stream_.socket().shutdown(tcp::socket::shutdown_send, ec); // At this point the connection is closed gracefully } }; //------------------------------------------------------------------------------ // Accepts incoming connections and launches the sessions class listener : public std::enable_shared_from_this<listener> { net::io_context& ioc_; tcp::acceptor acceptor_; std::shared_ptr<std::string const> doc_root_; public: listener( net::io_context& ioc, tcp::endpoint endpoint, std::shared_ptr<std::string const> const& doc_root) : ioc_(ioc) , acceptor_(net::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; // Open the acceptor acceptor_.open(endpoint.protocol(), ec); if(ec) { fail(ec, "open"); return; } // Allow address reuse acceptor_.set_option(net::socket_base::reuse_address(true), ec); if(ec) { fail(ec, "set_option"); return; } // Bind to the server address acceptor_.bind(endpoint, ec); if(ec) { fail(ec, "bind"); return; } // Start listening for connections acceptor_.listen( net::socket_base::max_listen_connections, ec); if(ec) { fail(ec, "listen"); return; } } // Start accepting incoming connections void run() { do_accept(); } private: void do_accept() { // The new connection gets its own strand acceptor_.async_accept( net::make_strand(ioc_), beast::bind_front_handler( &listener::on_accept, shared_from_this())); } void on_accept(beast::error_code ec, tcp::socket socket) { if(ec) { fail(ec, "accept"); } else { // Create the session and run it std::make_shared<session>( std::move(socket), doc_root_)->run(); } // Accept another connection do_accept(); } }; //------------------------------------------------------------------------------ int main(int argc, char* argv[]) { // Check command line arguments. if (argc != 4) { std::cerr << "Usage: http-server-async <address> <port> <doc_root>\n" << "Example:\n" << " http-server-async 0.0.0.0 8080 .\n"; return EXIT_FAILURE; } auto const address = net::ip::make_address(argv[1]); auto const port = static_cast<unsigned short>(std::atoi(argv[2])); auto const doc_root = std::make_shared<std::string>(argv[3]); // The io_context is required for all I/O net::io_context ioc; // Create and launch a listening port std::make_shared<listener>( ioc, tcp::endpoint{address, port}, doc_root)->run(); // Run the I/O service printf("running\n"); ioc.run(); return EXIT_SUCCESS; } And this is my index.html: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd> <html xmlns=http://www.w3.org/1999/xhtml> <body> <form action="xxx" method="post" accept-charset="utf-8" enctype="multipart/form-data"> <label for="field0">field 0:</label><input type="text" id="field0" name="field0"/><br/><br/> <label for="field1">field 1:</label><input type="password" id="field1" name="field1"/><br/><br/> <label for="field2">field 2:</label><input type="text" id="field2" name="field2"/><br/><br/> <label for="field3">field 3:</label><input type="text" id="field3" name="field3"/><br/><br/> <label for="field4">field 4:</label><input type="file" id="field4" name="field4"/><br/><br/> <input type="submit" value="Submit"/> </form> </body> </html> Klebsch Mario Funktion | R&D Tel: +49 (0) 531 38 701 718 Raum: Braunschweig, E20 Diese E-Mail und die an sie angehängten Dateien sind ausschließlich für Personen oder Institutionen bestimmt, deren Namen oben aufgeführt sind. Sie können Informationen enthalten, die durch das Berufsgeheimnis geschützt sind und deren Weitergabe strengstens untersagt ist. Jede elektronische Nachricht kann geändert werden. ACTIA lehnt jede Verantwortung für diese Nachricht ab. Der Inhalt dieser Nachricht stellt keine Verpflichtung seitens unseres Unternehmens dar. Wenn Sie kein Empfänger sind, weisen wir Sie darauf hin, dass das Lesen, Vervielfältigen oder Verteilen strengstens untersagt ist. Wir bitten Sie daher, uns umgehend über diesen Brief zu informieren und diese Nachricht sowie eventuell beigefügte Unterlagen aus Ihrem Postfach zu löschen. Danke. This e-mail and the files attached to it are intended exclusively for persons or institutions whose names are listed above. They may contain information that is protected by professional secrecy and the disclosure of which is strictly prohibited. Any electronic message can be modified. ACTIA declines all responsibility for this message. The content of this message does not represent a commitment on the part of our company. If you are not a recipient, we would like to point out that reading, copying or distribution is strictly prohibited. We therefore ask you to inform us immediately about this letter and to delete this message and any accompanying documents from your mailbox. Thank you.