Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to create a boost ssl iostream?
    primarykey
    data
    text
    <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>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload