[asio] SSL enabled http server3 example is blocking

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_;
participants (1)
-
Jean-Pierre Bergamin