$include_dir="/home/hyper-archives/boost-users/include"; include("$include_dir/msg-header.inc") ?>
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_;