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;
}
}