diff options
| author | Roland Reichwein <mail@reichwein.it> | 2020-04-10 16:58:49 +0200 | 
|---|---|---|
| committer | Roland Reichwein <mail@reichwein.it> | 2020-04-10 16:58:49 +0200 | 
| commit | 0f55f61ee745f38c312a53009b883feb5aa86b49 (patch) | |
| tree | 46b8b9f0847e0dcbb1ea8e25fff69de8c5337850 | |
| parent | c0ccf16c69d43a89674640c61d13ec2c02b128d6 (diff) | |
Simplify http and https units
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | http.cpp | 55 | ||||
| -rw-r--r-- | http_debian10.cpp | 529 | ||||
| -rw-r--r-- | https.cpp | 62 | ||||
| -rw-r--r-- | response.cpp | 57 | ||||
| -rw-r--r-- | response.h | 3 | 
6 files changed, 65 insertions, 642 deletions
@@ -62,7 +62,6 @@ PROGSRC=\      file.cpp \      http.cpp \      https.cpp \ -    http_debian10.cpp \      plugin.cpp \      privileges.cpp \      response.cpp \ @@ -26,65 +26,18 @@ using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>  namespace { -// 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"; -} -  // 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> +template<class Send>  void  handle_request(      ::Server& server, -    http::request<Body, http::basic_fields<Allocator>>&& req, +    request_type&& req,      Send&& send)  { -    http::response<http::string_body> res{http::status::ok, req.version()}; -    res.set(http::field::server, VersionString); -    res.set(http::field::content_type, mime_type(extend_index_html(std::string(req.target())))); -    res.keep_alive(req.keep_alive()); -    std::string res_data = generate_response(req, res, server); -    if (req.method() != http::verb::head) { -     res.body() = res_data; -     res.prepare_payload(); -    } -    return send(std::move(res)); +    return send(std::move(generate_response(req, server)));  }  //------------------------------------------------------------------------------ @@ -200,7 +153,7 @@ public:              return fail(ec, "read");          // Send the response -        handle_request(m_server , std::move(req_), lambda_); +        handle_request(m_server, std::move(req_), lambda_);      }      void diff --git a/http_debian10.cpp b/http_debian10.cpp deleted file mode 100644 index 252cf56..0000000 --- a/http_debian10.cpp +++ /dev/null @@ -1,529 +0,0 @@ -#include <boost/beast/version.hpp> - -#if BOOST_VERSION < 107100 - -#include "server_certificate.h" - -#include <boost/beast/core.hpp> -#include <boost/beast/http.hpp> -#include <boost/asio/bind_executor.hpp> -#include <boost/asio/ip/tcp.hpp> -#include <boost/asio/ssl/stream.hpp> -#include <boost/asio/strand.hpp> -#include <boost/config.hpp> -#include <algorithm> -#include <cstdlib> -#include <functional> -#include <iostream> -#include <memory> -#include <string> -#include <thread> -#include <vector> - -using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp> -namespace ssl = boost::asio::ssl;       // from <boost/asio/ssl.hpp> -namespace http = boost::beast::http;    // from <boost/beast/http.hpp> - -// Return a reasonable mime type based on the extension of a file. -boost::beast::string_view -mime_type(boost::beast::string_view path) -{ -    using boost::beast::iequals; -    auto const ext = [&path] -    { -        auto const pos = path.rfind("."); -        if(pos == boost::beast::string_view::npos) -            return boost::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( -    boost::beast::string_view base, -    boost::beast::string_view path) -{ -    if(base.empty()) -        return path.to_string(); -    std::string result = base.to_string(); -#if 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( -    boost::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](boost::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() = why.to_string(); -        res.prepare_payload(); -        return res; -    }; - -    // Returns a not found response -    auto const not_found = -    [&req](boost::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 '" + target.to_string() + "' was not found."; -        res.prepare_payload(); -        return res; -    }; - -    // Returns a server error response -    auto const server_error = -    [&req](boost::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: '" + what.to_string() + "'"; -        res.prepare_payload(); -        return res; -    }; - -    // 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("..") != boost::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 -    boost::beast::error_code ec; -    http::file_body::value_type body; -    body.open(path.c_str(), boost::beast::file_mode::scan, ec); - -    // Handle the case where the file doesn't exist -    if(ec == boost::system::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(boost::system::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, -                boost::asio::bind_executor( -                    self_.strand_, -                    std::bind( -                        &session::on_write, -                        self_.shared_from_this(), -                        std::placeholders::_1, -                        std::placeholders::_2, -                        sp->need_eof()))); -        } -    }; - -    tcp::socket socket_; -    ssl::stream<tcp::socket&> stream_; -    boost::asio::strand< -        boost::asio::io_context::executor_type> strand_; -    boost::beast::flat_buffer buffer_; -    std::string const& doc_root_; -    http::request<http::string_body> req_; -    std::shared_ptr<void> res_; -    send_lambda lambda_; - -public: -    // Take ownership of the socket -    explicit -    session( -        tcp::socket socket, -        ssl::context& ctx, -        std::string const& doc_root) -        : socket_(std::move(socket)) -        , stream_(socket_, ctx) -        , strand_(socket_.get_executor()) -        , doc_root_(doc_root) -        , lambda_(*this) -    { -    } - -    // Start the asynchronous operation -    void -    run() -    { -        // Perform the SSL handshake -        stream_.async_handshake( -            ssl::stream_base::server, -            boost::asio::bind_executor( -                strand_, -                std::bind( -                    &session::on_handshake, -                    shared_from_this(), -                    std::placeholders::_1))); -    } - -    void -    on_handshake(boost::system::error_code ec) -    { -        if(ec) -            return fail(ec, "handshake"); - -        do_read(); -    } - -    void -    do_read() -    { -        // Make the request empty before reading, -        // otherwise the operation behavior is undefined. -        req_ = {}; - -        // Read a request -        http::async_read(stream_, buffer_, req_, -            boost::asio::bind_executor( -                strand_, -                std::bind( -                    &session::on_read, -                    shared_from_this(), -                    std::placeholders::_1, -                    std::placeholders::_2))); -    } - -    void -    on_read( -        boost::system::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( -        boost::system::error_code ec, -        std::size_t bytes_transferred, -        bool close) -    { -        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() -    { -        // Perform the SSL shutdown -        stream_.async_shutdown( -            boost::asio::bind_executor( -                strand_, -                std::bind( -                    &session::on_shutdown, -                    shared_from_this(), -                    std::placeholders::_1))); -    } - -    void -    on_shutdown(boost::system::error_code ec) -    { -        if(ec) -            return fail(ec, "shutdown"); - -        // At this point the connection is closed gracefully -    } -}; - -//------------------------------------------------------------------------------ - -// Accepts incoming connections and launches the sessions -class listener : public std::enable_shared_from_this<listener> -{ -    ssl::context& ctx_; -    tcp::acceptor acceptor_; -    tcp::socket socket_; -    std::string const& doc_root_; - -public: -    listener( -        boost::asio::io_context& ioc, -        ssl::context& ctx, -        tcp::endpoint endpoint, -        std::string const& doc_root) -        : ctx_(ctx) -        , acceptor_(ioc) -        , socket_(ioc) -        , doc_root_(doc_root) -    { -        boost::system::error_code ec; - -        // Open the acceptor -        acceptor_.open(endpoint.protocol(), ec); -        if(ec) -        { -            fail(ec, "open"); -            return; -        } - -        // Allow address reuse -        acceptor_.set_option(boost::asio::socket_base::reuse_address(true)); -        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( -            boost::asio::socket_base::max_listen_connections, ec); -        if(ec) -        { -            fail(ec, "listen"); -            return; -        } -    } - -    // Start accepting incoming connections -    void -    run() -    { -        if(! acceptor_.is_open()) -            return; -        do_accept(); -    } - -    void -    do_accept() -    { -        acceptor_.async_accept( -            socket_, -            std::bind( -                &listener::on_accept, -                shared_from_this(), -                std::placeholders::_1)); -    } - -    void -    on_accept(boost::system::error_code ec) -    { -        if(ec) -        { -            fail(ec, "accept"); -        } -        else -        { -            // Create the session and run it -            std::make_shared<session>( -                std::move(socket_), -                ctx_, -                doc_root_)->run(); -        } - -        // Accept another connection -        do_accept(); -    } -}; - -//------------------------------------------------------------------------------ - -int http_server(int argc, char* argv[]) -{ -    // Check command line arguments. -    if (argc != 5) -    { -        std::cerr << -            "Usage: http-server-async-ssl <address> <port> <doc_root> <threads>\n" << -            "Example:\n" << -            "    http-server-async-ssl 0.0.0.0 8080 . 1\n"; -        return EXIT_FAILURE; -    } -    auto const address = boost::asio::ip::make_address(argv[1]); -    auto const port = static_cast<unsigned short>(std::atoi(argv[2])); -    std::string const doc_root = argv[3]; -    auto const threads = std::max<int>(1, std::atoi(argv[4])); - -    // The io_context is required for all I/O -    boost::asio::io_context ioc{threads}; - -    // The SSL context is required, and holds certificates -    ssl::context ctx{ssl::context::sslv23}; - -    // This holds the self-signed certificate used by the server -    load_server_certificate(ctx); - -    // Create and launch a listening port -    std::make_shared<listener>( -        ioc, -        ctx, -        tcp::endpoint{address, port}, -        doc_root)->run(); - -    // Run the I/O service on the requested number of threads -    std::vector<std::thread> v; -    v.reserve(threads - 1); -    for(auto i = threads - 1; i > 0; --i) -        v.emplace_back( -        [&ioc] -        { -            ioc.run(); -        }); -    ioc.run(); - -    return EXIT_SUCCESS; -} - -#endif @@ -5,10 +5,6 @@  #include "server.h"  #include "response.h" -#include <boost/beast/version.hpp> - -#if BOOST_VERSION == 107100 -  #include <openssl/ssl.h>  #include <boost/asio/buffer.hpp> @@ -39,65 +35,18 @@ using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>  namespace { -// 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"; -} -  // 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> +template<class Send>  void  handle_request(      ::Server& server, -    http::request<Body, http::basic_fields<Allocator>>&& req, +    request_type&& req,      Send&& send)  { -    http::response<http::string_body> res{http::status::ok, req.version()}; -    res.set(http::field::server, VersionString); -    res.set(http::field::content_type, mime_type(extend_index_html(std::string(req.target())))); -    res.keep_alive(req.keep_alive()); -    std::string res_data = generate_response(req, res, server); -    if (req.method() != http::verb::head) { -     res.body() = res_data; -     res.prepare_payload(); -    } -    return send(std::move(res)); +    return send(std::move(generate_response(req, server)));  }  //------------------------------------------------------------------------------ @@ -448,6 +397,8 @@ void load_server_certificate(boost::asio::ssl::context& ctx, fs::path cert_path,      std::string cert;      if (cert_path == "") { +     // generate dummy self signed certificate. Will be replaced by real +     // certificate if configured upon respective session       cert =        "-----BEGIN CERTIFICATE-----\n"        "MIIDnTCCAoWgAwIBAgIULkYtO+2Ddeg+qLZ+aDQpmA5b4L0wDQYJKoZIhvcNAQEL\n" @@ -481,6 +432,8 @@ void load_server_certificate(boost::asio::ssl::context& ctx, fs::path cert_path,      std::string key;      if (key_path == "") { +     // generate dummy self signed certificate. Will be replaced by real +     // certificate if configured upon respective session       key =        "-----BEGIN PRIVATE KEY-----\n"        "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyTW9Fi28/sT/m\n" @@ -588,5 +541,4 @@ int Server::start()  }  } // namespace HTTPS -#endif diff --git a/response.cpp b/response.cpp index 6b7ae6a..70c93b1 100644 --- a/response.cpp +++ b/response.cpp @@ -7,6 +7,8 @@  using namespace std::placeholders; +namespace { +  std::string extend_index_html(std::string path)  {   if (path.size() && path.back() == '/') @@ -14,8 +16,6 @@ std::string extend_index_html(std::string path)   return path;  } -namespace { -  std::string GetServerParam(const std::string& key, Server& server)  {   // following are the supported fields: @@ -73,10 +73,51 @@ void SetResponseHeader(const std::string& key, const std::string& value, respons    throw std::runtime_error("Unsupported response field: "s + key);  } +// 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"; +} +  } // anonymous namespace -std::string generate_response(request_type& req, response_type& res, Server& server) +response_type generate_response(request_type& req, Server& server)  { + response_type res{http::status::ok, req.version()}; + res.set(http::field::server, VersionString); + res.set(http::field::content_type, mime_type(extend_index_html(std::string(req.target())))); + res.keep_alive(req.keep_alive()); +   std::string host{req["host"]};   std::string target{req.target()};   std::string plugin_name { server.GetConfig().GetPlugin(server.GetSocket(), host, target)}; @@ -86,6 +127,14 @@ std::string generate_response(request_type& req, response_type& res, Server& ser   auto GetRequestParamFunction {std::function<std::string(const std::string& key)>(std::bind(GetRequestParam, _1, std::ref(req), std::ref(server)))};   auto SetResponseHeaderFunction{std::function<void(const std::string& key, const std::string& value)>(std::bind(SetResponseHeader, _1, _2, std::ref(res)))}; - return plugin->generate_page(GetServerParamFunction, GetRequestParamFunction, SetResponseHeaderFunction); + std::string res_data { plugin->generate_page(GetServerParamFunction, GetRequestParamFunction, SetResponseHeaderFunction)}; + if (req.method() == http::verb::head) { +  res.content_length(res_data.size()); + } else { +  res.body() = res_data; +  res.prepare_payload(); + } + + return res;  } @@ -13,5 +13,4 @@ namespace http = beast::http;           // from <boost/beast/http.hpp>  typedef http::request<http::string_body> request_type;  typedef http::response<http::string_body> response_type; -std::string extend_index_html(std::string path); -std::string generate_response(request_type& req, response_type& res, Server& server); +response_type generate_response(request_type& req, Server& server);  | 
