Boost logo

Boost Users :

From: Klebsch, Mario (Mario.Klebsch_at_[hidden])
Date: 2021-04-26 14:48:13


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.




Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net