[Boost-bugs] [Boost C++ Libraries] #13587: ssl::stream::async_shutdown() never completes when async_read is active

Subject: [Boost-bugs] [Boost C++ Libraries] #13587: ssl::stream::async_shutdown() never completes when async_read is active
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2018-06-06 07:46:10


#13587: ssl::stream::async_shutdown() never completes when async_read is active
-------------------------------------------+----------------------------
 Reporter: anonymous | Owner: chris_kohlhoff
     Type: Bugs | Status: new
Milestone: To Be Determined | Component: asio
  Version: Boost 1.67.0 | Severity: Problem
 Keywords: async_shutdown ssl async_read |
-------------------------------------------+----------------------------
 I have a connected ssl::stream. If I do an asio::async_read followed by an
 async_shutdown on the stream, the read operation will complete with
 stream_truncated error (as expected) but the async_shutdown operation
 never completes (i.e. handler never gets called). See a minimal reproduce
 below.

 Environment: Debian stretch, gcc 6.3.0, Boost 1.67, BoringSSL.

 My questions:
 1. Is it allowed to call async_shutdown on an ssl::stream when there is an
 async_read pending?
 2. If yes, is the above behavior expected?
 3. If not, how do I gracefully shutdown an SSL stream when an async_read
 is pending? And is this restriction documented anywhere?

 Code:
 {{{
 #include <boost/asio.hpp>
 #include <boost/asio/connect.hpp>
 #include <boost/asio/ip/tcp.hpp>
 #include <boost/asio/ssl/error.hpp>
 #include <boost/asio/ssl/stream.hpp>
 #include <cstdlib>
 #include <functional>
 #include <iostream>
 #include <memory>
 #include <string>

 using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
 namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>

 void
 fail(boost::system::error_code ec, char const* what)
 {
     std::cerr << what << ": " << ec.message() << "\n";
 }

 class session : public std::enable_shared_from_this<session>
 {
     tcp::resolver resolver_;
     ssl::stream<tcp::socket> stream_;
     std::string buffer_;

 public:
     explicit
     session(boost::asio::io_context& ioc, ssl::context& ctx)
         : resolver_(ioc)
         , stream_(ioc, ctx)
     {
     }

     void
     run(
         char const* host,
         char const* port)
     {
         // Set SNI Hostname (many hosts need this to handshake
 successfully)
         if(! SSL_set_tlsext_host_name(stream_.native_handle(), host))
         {
             boost::system::error_code
 ec{static_cast<int>(::ERR_get_error()),
 boost::asio::error::get_ssl_category()};
             std::cerr << ec.message() << "\n";
             return;
         }

         resolver_.async_resolve(
             host,
             port,
             std::bind(
                 &session::on_resolve,
                 shared_from_this(),
                 std::placeholders::_1,
                 std::placeholders::_2));
     }

     void
     on_resolve(
         boost::system::error_code ec,
         tcp::resolver::results_type results)
     {
         if(ec)
             return fail(ec, "resolve");

         boost::asio::async_connect(
             stream_.next_layer(),
             results.begin(),
             results.end(),
             std::bind(
                 &session::on_connect,
                 shared_from_this(),
                 std::placeholders::_1));
     }

     void
     on_connect(boost::system::error_code ec)
     {
         if(ec)
             return fail(ec, "connect");

         stream_.async_handshake(
             ssl::stream_base::client,
             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");
         std::cout << "Connected" << std::endl;

         boost::asio::async_read(stream_,
 boost::asio::dynamic_buffer(buffer_),
             std::bind(
                 &session::on_read,
                 shared_from_this(),
                 std::placeholders::_1,
                 std::placeholders::_2));

         stream_.async_shutdown(
             std::bind(
                 &session::on_shutdown,
                 shared_from_this(),
                 std::placeholders::_1));
     }

     void
     on_read(
         boost::system::error_code ec,
         std::size_t)
     {
         if(ec)
             return fail(ec, "read");

         std::cout << "Message received" << std::endl;
     }

     void
     on_shutdown(boost::system::error_code ec)
     {
         std::cout << "Closed" << std::endl;
     }
 };

 int main(int argc, char** argv)
 {
     boost::asio::io_context ioc;
     ssl::context ctx{ssl::context::sslv23_client};
     std::make_shared<session>(ioc, ctx)->run("www.google.com", "443");
     ioc.run();
     return EXIT_SUCCESS;
 }
 }}}
 The program will output
 {{{
 Connected
 read: stream truncated
 }}}
 and hangs.

-- 
Ticket URL: <https://svn.boost.org/trac10/ticket/13587>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2018-06-06 07:51:15 UTC