Boost SSL handshake(http_server_sync_ssl) : still reachable memory keeps increasing

Sandeep Bhardwaj via Boost-users <[email protected]>
Newsgroups gmane.comp.lib.boost.user
Message-ID <CAHdo=KuvCb_vPDmpo0dta1rKMHjyoa97Csq92OiTtVnfjN5FJw@mail.gmail.com>
Hi all,

  I am using boost 1.76.0 version. I have slightly modified the example
program http_server_sync_ssl.cpp to just dump the payload it receives and i
have used certificates with ssl context. When I run the program with
valgrind, the "still reachable" memory keeps increasing with time. This is
one example:

==17719== 38,880 bytes in 60 blocks are still reachable in loss record 740
of 740
==17719==    at 0x4C312EF: malloc (in
/usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==17719==    by 0x567F438: CRYPTO_zalloc (in /usr/lib64/libcrypto.so.1.1)
==17719==    by 0x52AA5CA: SSL_SESSION_new (in /usr/lib64/libssl.so.1.1)
==17719==    by 0x52AAE22: ??? (in /usr/lib64/libssl.so.1.1)
==17719==    by 0x52C8A06: ??? (in /usr/lib64/libssl.so.1.1)
==17719==    by 0x52B7FF6: ??? (in /usr/lib64/libssl.so.1.1)
==17719==    by 0x52A3F43: SSL_do_handshake (in /usr/lib64/libssl.so.1.1)
==17719==    by 0x419630:
boost::asio::ssl::detail::engine::do_accept(void*, unsigned long)
(engine.ipp:316)
==17719==    by 0x4193E3: boost::asio::ssl::detail::engine::perform(int
(boost::asio::ssl::detail::engine::*)(void*, unsigned long), void*,
unsigned long, boost::system::error_code&, unsigned long*) (engine.ipp:248)
==17719==    by 0x418F6D:
boost::asio::ssl::detail::engine::handshake(boost::asio::ssl::stream_base::handshake_type,
boost::system::error_code&) (engine.ipp:148)
==17719==    by 0x419778:
boost::asio::ssl::detail::handshake_op::operator()(boost::asio::ssl::detail::engine&,
boost::system::error_code&, unsigned long&) const (handshake_op.hpp:47)
==17719==    by 0x424DB8: unsigned long
boost::asio::ssl::detail::io<boost::asio::basic_stream_socket<boost::asio::ip::tcp,
boost::asio::any_io_executor>,
boost::asio::ssl::detail::handshake_op>(boost::asio::basic_stream_socket<boost::asio::ip::tcp,
boost::asio::any_io_executor>&, boost::asio::ssl::detail::stream_core&,
boost::asio::ssl::detail::handshake_op const&, boost::system::error_code&)
(io.hpp:38)
==17719==    by 0x421332:
boost::asio::ssl::stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp,
boost::asio::any_io_executor>&>::handshake(boost::asio::ssl::stream_base::handshake_type,
boost::system::error_code&) (stream.hpp:389)
==17719==    by 0x41D5FC:
boost::beast::ssl_stream<boost::asio::basic_stream_socket<boost::asio::ip::tcp,
boost::asio::any_io_executor>&>::handshake(boost::asio::ssl::stream_base::handshake_type,
boost::system::error_code&) (ssl_stream.hpp:344)
==17719==    by 0x406971:
do_session(boost::asio::basic_stream_socket<boost::asio::ip::tcp,
boost::asio::any_io_executor>&, boost::asio::ssl::context&,
std::shared_ptr<std::__cxx11::basic_string<char, std::char_traits<char>,
std::allocator<char> > const> const&) (boost_server.cpp:219)

I have attached the test program. Can anyone point out what needs to be
done here?

Thanks & Regards,
Sandeep

_______________________________________________
Boost-users mailing list
[email protected]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
boost_server.cpp (text/plain, 9.9 KB)
//
// 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 SSL server, synchronous
//
//------------------------------------------------------------------------------

//#include "example/common/server_certificate.hpp"

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/config.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <thread>

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>
namespace ssl = boost::asio::ssl;       // from <boost/asio/ssl.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(
    http::request<Body, http::basic_fields<Allocator>>&& req,
    Send&& send,
    int payloadProcessStatus)
{
    // Returns a bad request response
    auto const bad_request =
    [&req](beast::string_view why)
    {
        http::response<http::string_body> res;
        res.version(11);
        res.result(http::status::bad_request);
        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 server error response
    auto const server_error =
    [&req](beast::string_view what)
    {
        http::response<http::string_body> res;
        res.version(11);
        res.result(http::status::internal_server_error);
        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;
    };

    // Make sure we can handle the method
    if( req.method() != http::verb::post )
        return send(bad_request("Unknown HTTP-method"));

    if (payloadProcessStatus == -1)
        return send(server_error("Internal server error"));

    http::response<http::string_body> res;
    res.version(11);
    res.result(http::status::ok);
    res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
    res.set(http::field::content_type, "application/text");
    res.keep_alive(req.keep_alive());
    res.body() = "Collection request succeeded";
    res.prepare_payload(); 
    return send(std::move(res));
}

//------------------------------------------------------------------------------

// Report a failure
void
fail(beast::error_code ec, char const* what)
{
    std::cerr << what << ": " << ec.message() << "\n";
}

// This is the C++11 equivalent of a generic lambda.
// The function object is used to send an HTTP message.
template<class Stream>
struct send_lambda
{
    Stream& stream_;
    bool& close_;
    beast::error_code& ec_;

    explicit
    send_lambda(
        Stream& stream,
        bool& close,
        beast::error_code& ec)
        : stream_(stream)
        , close_(close)
        , ec_(ec)
    {
    }

    template<bool isRequest, class Body, class Fields>
    void
    operator()(http::message<isRequest, Body, Fields>&& msg) const
    {
        // Determine if we should close the connection after
        close_ = msg.need_eof();

        // We need the serializer here because the serializer requires
        // a non-const file_body, and the message oriented version of
        // http::write only works with const messages.
        http::serializer<isRequest, Body, Fields> sr{msg};
        http::write(stream_, sr, ec_);
    }
};

// Handles an HTTP server connection
void
do_session(
    tcp::socket& socket,
    ssl::context& ctx,
    std::shared_ptr<std::string const> const& doc_root)
{
    bool close = false;
    beast::error_code ec;

    // Construct the stream around the socket
    beast::ssl_stream<tcp::socket&> stream{socket, ctx};

    // Perform the SSL handshake
    stream.handshake(ssl::stream_base::server, ec);
    if(ec)
        return fail(ec, "handshake");

    // This buffer is required to persist across reads
    beast::flat_buffer buffer;

    // This lambda is used to send messages
    send_lambda<beast::ssl_stream<tcp::socket&>> lambda{stream, close, ec};

    for(;;)
    {
        // Read a request
        http::request<http::string_body> req;
        http::read(stream, buffer, req, ec);
        if(ec == http::error::end_of_stream)
            break;
        if(ec)
            return fail(ec, "read");

        std::string payload = req.body();
        std::cout<<"Payload : "<<payload<<std::endl;
        int r = 0;
        // Send the response
        handle_request(std::move(req), lambda, r);
        if(ec)
            return fail(ec, "write");
        if(close)
        {
            // This means we should close the connection, usually because
            // the response indicated the "Connection: close" semantic.
            break;
        }
    }

    // Perform the SSL shutdown
    std::cout<<"Shutting down stream"<<std::endl;
    stream.shutdown(ec);
    if(ec)
        return fail(ec, "shutdown");
    OPENSSL_thread_stop(); 
    // At this point the connection is closed gracefully
}

//------------------------------------------------------------------------------

int main(int argc, char* argv[])
{
    try
    {
        // Check command line arguments.
        if (argc != 4)
        {
            std::cerr <<
                "Usage: http-server-sync-ssl <address> <port> <doc_root>\n" <<
                "Example:\n" <<
                "    http-server-sync-ssl 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{1};

        // The SSL context is required, and holds certificates
        ssl::context ctx{ssl::context::tlsv12};

        ctx.load_verify_file("/tmp/ca.crt"); 
        ctx.use_certificate_file("/tmp/server.crt", ssl::context::file_format::pem);
        ctx.use_rsa_private_key_file("/tmp/server.key", ssl::context::file_format::pem);
        

        // The acceptor receives incoming connections
        tcp::acceptor acceptor{ioc, {address, port}};
        for(;;)
        {
            // This will receive the new connection
            tcp::socket socket{ioc};

            // Block until we get a connection
            acceptor.accept(socket);

            // Launch the session, transferring ownership of the socket
            std::thread{std::bind(
                &do_session,
                std::move(socket),
                std::ref(ctx),
                doc_root)}.detach();
        }
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.