Boost logo

Boost Users :

Subject: [Boost-users] [asio] SSL enabled http server3 example is blocking
From: Jean-Pierre Bergamin (james_at_[hidden])
Date: 2009-03-02 13:24:01


Dear boost-users

I changed the http server3 example (many thready run io_service::run())
to use SSL connections. I now face the problem that the async_accept
handler is only called when no other connections are alive and not
immediately by another thread as with the original non-ssl example.
My modified request_handler simply sleeps for 10 seconds. Now when a
request arrives while another one is being processed (sleeping), it is
only handled after the other one finished. By setting a breakpoint in
server::handle_accept() I can see that this handler for the second
request is only called after the first connection has been
destroyed/closed. This happens on Windows XP.

Do you may have an idea why the accept handler is not called immediately
or where the blocking could happen? I may help if you could test this on
another platform then XP.

To test this, apply the attached patch to the directory
boost_1_38_0/libs/asio/example/http/server3 and copy the *.pem files
from the asio ssl example to the working directory of the server3 example.

Regards

James

PS: I already posted this question to the asio-users list without
gettinga reply. I'm trying my luck now here on the boost list.

diff -ru server3.orig/connection.cpp server3/connection.cpp
--- server3.orig/connection.cpp 2008-03-12 10:12:08.000000000 +0100
+++ server3/connection.cpp 2009-02-17 15:52:12.867198900 +0100
@@ -17,20 +17,20 @@
 namespace server3 {
 
 connection::connection(boost::asio::io_service& io_service,
+ boost::asio::ssl::context &context,
     request_handler& handler)
   : strand_(io_service),
- socket_(io_service),
+ socket_(io_service, context),
     request_handler_(handler)
 {
 }
 
-boost::asio::ip::tcp::socket& connection::socket()
+connection::socket_type::lowest_layer_type& connection::socket()
 {
- return socket_;
+ return socket_.lowest_layer();
 }
 
-void connection::start()
-{
+void connection::handle_handshake(const boost::system::error_code& e) {
   socket_.async_read_some(boost::asio::buffer(buffer_),
       strand_.wrap(
         boost::bind(&connection::handle_read, shared_from_this(),
@@ -38,6 +38,14 @@
           boost::asio::placeholders::bytes_transferred)));
 }
 
+void connection::start()
+{
+ socket_.async_handshake(boost::asio::ssl::stream_base::server,
+ boost::bind(&connection::handle_handshake, shared_from_this(),
+ boost::asio::placeholders::error)
+ );
+}
+
 void connection::handle_read(const boost::system::error_code& e,
     std::size_t bytes_transferred)
 {
@@ -85,7 +93,7 @@
   {
     // Initiate graceful connection closure.
     boost::system::error_code ignored_ec;
- socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
+ socket_.shutdown(ignored_ec);
   }
 
   // No new asynchronous operations are started. This means that all shared_ptr
diff -ru server3.orig/connection.hpp server3/connection.hpp
--- server3.orig/connection.hpp 2008-03-12 10:12:08.000000000 +0100
+++ server3/connection.hpp 2009-02-26 08:51:42.187500000 +0100
@@ -12,6 +12,7 @@
 #define HTTP_SERVER3_CONNECTION_HPP
 
 #include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
 #include <boost/array.hpp>
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
@@ -30,17 +31,20 @@
     private boost::noncopyable
 {
 public:
+ typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_type;
   /// Construct a connection with the given io_service.
   explicit connection(boost::asio::io_service& io_service,
+ boost::asio::ssl::context &context,
       request_handler& handler);
 
   /// Get the socket associated with the connection.
- boost::asio::ip::tcp::socket& socket();
+ socket_type::lowest_layer_type& socket();
 
   /// Start the first asynchronous operation for the connection.
   void start();
-
+ ~connection() {}
 private:
+ void handle_handshake(const boost::system::error_code& e);
   /// Handle completion of a read operation.
   void handle_read(const boost::system::error_code& e,
       std::size_t bytes_transferred);
@@ -52,7 +56,7 @@
   boost::asio::io_service::strand strand_;
 
   /// Socket for the connection.
- boost::asio::ip::tcp::socket socket_;
+ socket_type socket_;
 
   /// The handler used to process the incoming request.
   request_handler& request_handler_;
diff -ru server3.orig/reply.cpp server3/reply.cpp
--- server3.orig/reply.cpp 2008-03-12 10:12:08.000000000 +0100
+++ server3/reply.cpp 2009-02-17 15:52:20.804800500 +0100
@@ -119,7 +119,7 @@
 
 namespace stock_replies {
 
-const char ok[] = "";
+const char ok[] = "ok\r\n";
 const char created[] =
   "<html>"
   "<head><title>Created</title></head>"
diff -ru server3.orig/request_handler.cpp server3/request_handler.cpp
--- server3.orig/request_handler.cpp 2008-03-12 10:12:08.000000000 +0100
+++ server3/request_handler.cpp 2009-02-26 08:23:56.250000000 +0100
@@ -8,6 +8,7 @@
 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 //
 
+#include <boost/thread.hpp>
 #include "request_handler.hpp"
 #include <fstream>
 #include <sstream>
@@ -27,56 +28,9 @@
 
 void request_handler::handle_request(const request& req, reply& rep)
 {
- // Decode url to path.
- std::string request_path;
- if (!url_decode(req.uri, request_path))
- {
- rep = reply::stock_reply(reply::bad_request);
- return;
- }
-
- // Request path must be absolute and not contain "..".
- if (request_path.empty() || request_path[0] != '/'
- || request_path.find("..") != std::string::npos)
- {
- rep = reply::stock_reply(reply::bad_request);
- return;
- }
-
- // If path ends in slash (i.e. is a directory) then add "index.html".
- if (request_path[request_path.size() - 1] == '/')
- {
- request_path += "index.html";
- }
-
- // Determine the file extension.
- std::size_t last_slash_pos = request_path.find_last_of("/");
- std::size_t last_dot_pos = request_path.find_last_of(".");
- std::string extension;
- if (last_dot_pos != std::string::npos && last_dot_pos > last_slash_pos)
- {
- extension = request_path.substr(last_dot_pos + 1);
- }
-
- // Open the file to send back.
- std::string full_path = doc_root_ + request_path;
- std::ifstream is(full_path.c_str(), std::ios::in | std::ios::binary);
- if (!is)
- {
- rep = reply::stock_reply(reply::not_found);
- return;
- }
-
- // Fill out the reply to be sent to the client.
- rep.status = reply::ok;
- char buf[512];
- while (is.read(buf, sizeof(buf)).gcount() > 0)
- rep.content.append(buf, is.gcount());
- rep.headers.resize(2);
- rep.headers[0].name = "Content-Length";
- rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
- rep.headers[1].name = "Content-Type";
- rep.headers[1].value = mime_types::extension_to_type(extension);
+ using namespace boost;
+ this_thread::sleep(posix_time::seconds(10));
+ rep = reply::stock_reply(reply::ok);
 }
 
 bool request_handler::url_decode(const std::string& in, std::string& out)
diff -ru server3.orig/server.cpp server3/server.cpp
--- server3.orig/server.cpp 2008-03-12 10:12:08.000000000 +0100
+++ server3/server.cpp 2009-02-17 16:24:16.173066900 +0100
@@ -21,9 +21,23 @@
     const std::string& doc_root, std::size_t thread_pool_size)
   : thread_pool_size_(thread_pool_size),
     acceptor_(io_service_),
- new_connection_(new connection(io_service_, request_handler_)),
+ context_(io_service_, boost::asio::ssl::context::sslv23),
+ new_connection_(new connection(io_service_, context_, request_handler_)),
     request_handler_(doc_root)
 {
+
+ context_.set_options(
+ boost::asio::ssl::context::default_workarounds
+ | boost::asio::ssl::context::no_sslv2
+ | boost::asio::ssl::context::single_dh_use);
+ context_.set_password_callback(boost::bind<const char*>(&server::get_password, this));
+
+ context_.use_certificate_chain_file("server.pem");
+ context_.use_private_key_file("server.pem", boost::asio::ssl::context::pem);
+ context_.use_tmp_dh_file("dh512.pem");
+ context_.set_verify_mode(boost::asio::ssl::context::verify_none);
+
+
   // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
   boost::asio::ip::tcp::resolver resolver(io_service_);
   boost::asio::ip::tcp::resolver::query query(address, port);
@@ -63,7 +77,7 @@
   if (!e)
   {
     new_connection_->start();
- new_connection_.reset(new connection(io_service_, request_handler_));
+ new_connection_.reset(new connection(io_service_, context_, request_handler_));
     acceptor_.async_accept(new_connection_->socket(),
         boost::bind(&server::handle_accept, this,
           boost::asio::placeholders::error));
diff -ru server3.orig/server.hpp server3/server.hpp
--- server3.orig/server.hpp 2008-03-12 10:12:08.000000000 +0100
+++ server3/server.hpp 2009-02-17 16:02:27.187562100 +0100
@@ -39,6 +39,10 @@
   void stop();
 
 private:
+ const char * get_password() const {
+ return "test";
+ }
+
   /// Handle completion of an asynchronous accept operation.
   void handle_accept(const boost::system::error_code& e);
 
@@ -51,6 +55,8 @@
   /// Acceptor used to listen for incoming connections.
   boost::asio::ip::tcp::acceptor acceptor_;
 
+ boost::asio::ssl::context context_;
+
   /// The next connection to be accepted.
   connection_ptr new_connection_;
 


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net