Subject: [Boost-users] [asio] SSL async_shutdown() never finishes if called when async_read_some() is in progress
From: Alexander D. (alexdems_at_[hidden])
Date: 2018-04-06 07:56:16


Hello,

I'm writing a middleware which basically adds an SSL layer to the connection between a client and a server. The connection is bidirectional, so I need to read from each side and write to the other. The client can also close the connection at any time, in which case async_shutdown() must be called on the server's stream.

The problem is, often the shutdown happens when the program is waiting on the server through async_read_some(), and the shutdown will never finish.

The following code illustrates this issue:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

int main(int argc, char* argv[])
{
  try
  {
    boost::asio::io_context io_context;
    boost::asio::streambuf buffer;

    boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12);
    ctx.set_verify_mode(boost::asio::ssl::verify_none);

    boost::asio::ssl::stream<boost::asio::ip::tcp::socket>
    sock(io_context, ctx);

    sock.next_layer().connect(boost::asio::ip::tcp::endpoint(
        boost::asio::ip::make_address("127.0.0.1"), 4433));
    sock.handshake(boost::asio::ssl::stream_base::client);

    /* "async_shutdown" won't appear unless async_read_some()
     * is commented out */
    sock.async_read_some(buffer.prepare(1024),
        [](boost::system::error_code, size_t)
        {std::cout << "async_read_some" << std::endl;});

    sock.async_shutdown([](boost::system::error_code)
        {std::cout << "async_shutdown" << std::endl;});

    io_context.run();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

To reproduce, start up an SSL server:
  openssl genrsa -out key.pem 2048
  openssl req -new -sha256 -key key.pem -out csr.csr
  openssl req -x509 -sha256 -days 365 -key key.pem -in csr.csr -out server.pem
  openssl s_server -key key.pem -cert server.pem
and run the program. Notice that "async_shutdown" does not finish even if the server is killed.

In practice, this leaks the memory of the shutdown handler and whatever is bound to it. The only solution I can think of would be to cancel the read operation, but there's no portable way in asio to do this.

Can someone point out how to solve this?

Cheers,

Alexander