title: How to create a boost ssl iostream?
body: <p>I'm adding HTTPS support to code that does input and output using boost tcp::iostream (acting as an HTTP server).</p>
  
  <p>I've found examples (and have a working toy HTTPS server) that do SSL input/output using boost::asio::read/boost::asio::write, but none that use iostreams and the &lt;&lt; >> operators.  How do I turn an ssl::stream into an iostream?</p>
  
  <p>Working code:</p>
  
  <pre><code>#include &lt;boost/asio.hpp&gt; 
  #include &lt;boost/asio/ssl.hpp&gt; 
  #include &lt;boost/foreach.hpp&gt;
  #include &lt;iostream&gt; 
  #include &lt;sstream&gt;
  #include &lt;string&gt;
  
  using namespace std;
  using namespace boost;
  using boost::asio::ip::tcp;
  
  typedef boost::asio::ssl::stream&lt;boost::asio::ip::tcp::socket&gt; ssl_stream;
  
  string HTTPReply(int nStatus, const string&amp; strMsg)
  {
      string strStatus;
      if (nStatus == 200) strStatus = "OK";
      else if (nStatus == 400) strStatus = "Bad Request";
      else if (nStatus == 404) strStatus = "Not Found";
      else if (nStatus == 500) strStatus = "Internal Server Error";
      ostringstream s;
      s &lt;&lt; "HTTP/1.1 " &lt;&lt; nStatus &lt;&lt; " " &lt;&lt; strStatus &lt;&lt; "\r\n"
        &lt;&lt; "Connection: close\r\n"
        &lt;&lt; "Content-Length: " &lt;&lt; strMsg.size() &lt;&lt; "\r\n"
        &lt;&lt; "Content-Type: application/json\r\n"
        &lt;&lt; "Date: Sat, 09 Jul 2009 12:04:08 GMT\r\n"
        &lt;&lt; "Server: json-rpc/1.0\r\n"
        &lt;&lt; "\r\n"
        &lt;&lt; strMsg;
      return s.str();
  }
  
  int main() 
  { 
      // Bind to loopback 127.0.0.1 so the socket can only be accessed locally                                            
      boost::asio::io_service io_service;
      tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 1111);
      tcp::acceptor acceptor(io_service, endpoint);
  
      boost::asio::ssl::context context(io_service, boost::asio::ssl::context::sslv23);
      context.set_options(
          boost::asio::ssl::context::default_workarounds
          | boost::asio::ssl::context::no_sslv2);
      context.use_certificate_chain_file("server.cert");
      context.use_private_key_file("server.pem", boost::asio::ssl::context::pem);
  
      for(;;)
      {
          // Accept connection                                                                                            
          ssl_stream stream(io_service, context);
          tcp::endpoint peer_endpoint;
          acceptor.accept(stream.lowest_layer(), peer_endpoint);
          boost::system::error_code ec;
          stream.handshake(boost::asio::ssl::stream_base::server, ec);
  
          if (!ec) {
              boost::asio::write(stream, boost::asio::buffer(HTTPReply(200, "Okely-Dokely\n")));
              // I really want to write:
              // iostream_object &lt;&lt; HTTPReply(200, "Okely-Dokely\n") &lt;&lt; std::flush;
          }
      }
  }
  </code></pre>
  
  <p>It seems like the ssl::stream_service would be the answer, but that is a dead end.</p>
  
  <p>Using boost::iostreams (as suggested by accepted answer) is the right approach; here's the working code I've ended up with:</p>
  
  <pre><code>#include &lt;boost/asio.hpp&gt; 
  #include &lt;boost/asio/ssl.hpp&gt; 
  #include &lt;boost/iostreams/concepts.hpp&gt;
  #include &lt;boost/iostreams/stream.hpp&gt;
  #include &lt;sstream&gt;
  #include &lt;string&gt;
  #include &lt;iostream&gt;
  
  using namespace boost::asio;
  
  typedef ssl::stream&lt;ip::tcp::socket&gt; ssl_stream;
  
  
  //
  // IOStream device that speaks SSL but can also speak non-SSL
  //
  class ssl_iostream_device : public boost::iostreams::device&lt;boost::iostreams::bidirectional&gt; {
  public:
      ssl_iostream_device(ssl_stream &amp;_stream, bool _use_ssl ) : stream(_stream)
      {
          use_ssl = _use_ssl;
          need_handshake = _use_ssl;
      }
  
      void handshake(ssl::stream_base::handshake_type role)
      {
          if (!need_handshake) return;
          need_handshake = false;
          stream.handshake(role);
      }
      std::streamsize read(char* s, std::streamsize n)
      {
          handshake(ssl::stream_base::server); // HTTPS servers read first
          if (use_ssl) return stream.read_some(boost::asio::buffer(s, n));
          return stream.next_layer().read_some(boost::asio::buffer(s, n));
      }
      std::streamsize write(const char* s, std::streamsize n)
      {
          handshake(ssl::stream_base::client); // HTTPS clients write first
          if (use_ssl) return boost::asio::write(stream, boost::asio::buffer(s, n));
          return boost::asio::write(stream.next_layer(), boost::asio::buffer(s, n));
      }
  
  private:
      bool need_handshake;
      bool use_ssl;
      ssl_stream&amp; stream;
  };
  
  std::string HTTPReply(int nStatus, const std::string&amp; strMsg)
  {
      std::string strStatus;
      if (nStatus == 200) strStatus = "OK";
      else if (nStatus == 400) strStatus = "Bad Request";
      else if (nStatus == 404) strStatus = "Not Found";
      else if (nStatus == 500) strStatus = "Internal Server Error";
      std::ostringstream s;
      s &lt;&lt; "HTTP/1.1 " &lt;&lt; nStatus &lt;&lt; " " &lt;&lt; strStatus &lt;&lt; "\r\n"
        &lt;&lt; "Connection: close\r\n"
        &lt;&lt; "Content-Length: " &lt;&lt; strMsg.size() &lt;&lt; "\r\n"
        &lt;&lt; "Content-Type: application/json\r\n"
        &lt;&lt; "Date: Sat, 09 Jul 2009 12:04:08 GMT\r\n"
        &lt;&lt; "Server: json-rpc/1.0\r\n"
        &lt;&lt; "\r\n"
        &lt;&lt; strMsg;
      return s.str();
  }
  
  
  void handle_request(std::iostream&amp; s)
  {
      s &lt;&lt; HTTPReply(200, "Okely-Dokely\n") &lt;&lt; std::flush;
  }
  
  int main(int argc, char* argv[])
  { 
      bool use_ssl = (argc &lt;= 1);
  
      // Bind to loopback 127.0.0.1 so the socket can only be accessed locally                                            
      io_service io_service;
      ip::tcp::endpoint endpoint(ip::address_v4::loopback(), 1111);
      ip::tcp::acceptor acceptor(io_service, endpoint);
  
      ssl::context context(io_service, ssl::context::sslv23);
      context.set_options(
          ssl::context::default_workarounds
          | ssl::context::no_sslv2);
      context.use_certificate_chain_file("server.cert");
      context.use_private_key_file("server.pem", ssl::context::pem);
  
      for(;;)
      {
          ip::tcp::endpoint peer_endpoint;
          ssl_stream _ssl_stream(io_service, context);
          ssl_iostream_device d(_ssl_stream, use_ssl);
          boost::iostreams::stream&lt;ssl_iostream_device&gt; ssl_iostream(d);
  
          // Accept connection                                                                                            
          acceptor.accept(_ssl_stream.lowest_layer(), peer_endpoint);
          std::string method;
          std::string path;
          ssl_iostream &gt;&gt; method &gt;&gt; path;
  
          handle_request(ssl_iostream);
      }
  }
  </code></pre>