]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1661] HTTP code half done
authorFrancis Dupont <fdupont@isc.org>
Mon, 15 Feb 2021 15:02:31 +0000 (16:02 +0100)
committerFrancis Dupont <fdupont@isc.org>
Wed, 24 Mar 2021 08:09:02 +0000 (09:09 +0100)
14 files changed:
src/lib/http/Makefile.am
src/lib/http/client.cc
src/lib/http/client.h
src/lib/http/connection.cc
src/lib/http/connection.h
src/lib/http/http_acceptor.h
src/lib/http/http_messages.mes
src/lib/http/listener.cc
src/lib/http/listener.h
src/lib/http/listener_impl.cc
src/lib/http/listener_impl.h
src/lib/http/tests/Makefile.am
src/lib/http/tests/connection_pool_unittests.cc
src/lib/http/tests/server_client_unittests.cc

index ab3f83f90c5c149cd3176279fb786a0cb008304f..0562b7dc7da6ccd83f9dadf428848fb8cc241729 100644 (file)
@@ -1,7 +1,7 @@
 SUBDIRS = . tests
 
 AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
 AM_CXXFLAGS  = $(KEA_CXXFLAGS)
 
 EXTRA_DIST = http.dox
@@ -54,7 +54,7 @@ libkea_http_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
 libkea_http_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
 libkea_http_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
 libkea_http_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
-libkea_http_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS)
+libkea_http_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(CRYPTO_LIBS)
 
 # If we want to get rid of all generated messages files, we need to use
 # make maintainer-clean. The proper way to introduce custom commands for
index 3958ab6959f83b0e32dbc48d395de492b8fd2113..affcb985f114637020750a6e7c04f050adf845b0 100644 (file)
@@ -8,7 +8,7 @@
 
 #include <asiolink/asio_wrapper.h>
 #include <asiolink/interval_timer.h>
-#include <asiolink/tcp_socket.h>
+#include <asiolink/tls_socket.h>
 #include <http/client.h>
 #include <http/http_log.h>
 #include <http/http_messages.h>
@@ -41,11 +41,11 @@ namespace {
 /// The part of the HTTP message beyond this value is truncated.
 constexpr size_t MAX_LOGGED_MESSAGE_SIZE = 1024;
 
-/// @brief TCP socket callback function type.
+/// @brief TCP / TLS socket callback function type.
 typedef std::function<void(boost::system::error_code ec, size_t length)>
 SocketCallbackFunction;
 
-/// @brief Socket callback class required by the TCPSocket API.
+/// @brief Socket callback class required by the TCPSocket and TLSSocket APIs.
 ///
 /// Its function call operator ignores callbacks invoked with "operation aborted"
 /// error codes. Such status codes are generated when the posted IO operations
@@ -99,7 +99,7 @@ typedef boost::shared_ptr<ConnectionPool> ConnectionPoolPtr;
 /// the new request is stored in the FIFO queue. The queued requests to the
 /// particular URL are sent to the server when the current transaction ends.
 ///
-/// The communication over the TCP socket is asynchronous. The caller is
+/// The communication over the transport socket is asynchronous. The caller is
 /// notified about the completion of the transaction via a callback that the
 /// caller supplies when initiating the transaction.
 class Connection : public boost::enable_shared_from_this<Connection> {
@@ -108,10 +108,12 @@ public:
     /// @brief Constructor.
     ///
     /// @param io_service IO service to be used for the connection.
+    /// @param context TLS context to be used for the connection.
     /// @param conn_pool Back pointer to the connection pool to which this
     /// connection belongs.
     /// @param url URL associated with this connection.
     explicit Connection(IOService& io_service,
+                        const TlsContextPtr& context,
                         const ConnectionPoolPtr& conn_pool,
                         const Url& url);
 
@@ -132,6 +134,8 @@ public:
     /// transaction completes.
     /// @param connect_callback Pointer to the callback function to be invoked
     /// when the client connects to the server.
+    /// @param handshake_callback Optional callback invoked when the client
+    /// performs the TLS handshake with the server.
     /// @param close_callback Pointer to the callback function to be invoked
     /// when the client closes the socket to the server.
     void doTransaction(const HttpRequestPtr& request,
@@ -139,6 +143,7 @@ public:
                        const long request_timeout,
                        const HttpClient::RequestHandler& callback,
                        const HttpClient::ConnectHandler& connect_callback,
+                       const HttpClient::HandshakeHandler& handshake_callback,
                        const HttpClient::CloseHandler& close_callback);
 
     /// @brief Closes the socket and cancels the request timer.
@@ -191,6 +196,8 @@ private:
     /// transaction completes.
     /// @param connect_callback Pointer to the callback function to be invoked
     /// when the client connects to the server.
+    /// @param handshake_callback Optional callback invoked when the client
+    /// performs the TLS handshake with the server.
     /// @param close_callback Pointer to the callback function to be invoked
     /// when the client closes the socket to the server.
     void doTransactionInternal(const HttpRequestPtr& request,
@@ -198,6 +205,7 @@ private:
                                const long request_timeout,
                                const HttpClient::RequestHandler& callback,
                                const HttpClient::ConnectHandler& connect_callback,
+                               const HttpClient::HandshakeHandler& handshake_callback,
                                const HttpClient::CloseHandler& close_callback);
 
     /// @brief Closes the socket and cancels the request timer.
@@ -281,6 +289,11 @@ private:
     /// @param request_timeout New timer interval in milliseconds.
     void scheduleTimer(const long request_timeout);
 
+    /// @brief Asynchronously performs the TLS handshake.
+    ///
+    /// @param transid Current transaction id.
+    void doHandshake(const uint64_t transid);
+
     /// @brief Asynchronously sends data over the socket.
     ///
     /// The data sent over the socket are stored in the @c buf_.
@@ -298,7 +311,8 @@ private:
     /// @brief Local callback invoked when the connection is established.
     ///
     /// If the connection is successfully established, this callback will start
-    /// to asynchronously send the request over the socket.
+    /// to asynchronously send the request over the socket or perform the
+    /// TLS handshake with the server.
     ///
     /// @param Pointer to the callback to be invoked when client connects to
     /// the server.
@@ -308,6 +322,19 @@ private:
                          const uint64_t transid,
                          const boost::system::error_code& ec);
 
+    /// @brief Local callback invoked when the handshake is performed.
+    ///
+    /// If the handshake is successfully performed, this callback will start
+    /// to asynchronously send the request over the socket.
+    ///
+    /// @param Pointer to the callback to be invoked when client performs
+    /// the TLS handshake with the server.
+    /// @param transid Current transaction id.
+    /// @param ec Error code being a result of the connection attempt.
+    void handshakeCallback(HttpClient::HandshakeHandler handshake_callback,
+                           const uint64_t transid,
+                           const boost::system::error_code& ec);
+
     /// @brief Local callback invoked when an attempt to send a portion of data
     /// over the socket has ended.
     ///
@@ -355,6 +382,7 @@ private:
 
     /// @brief Socket to be used for this connection.
     TCPSocket<SocketCallback> socket_;
+    ////// change this
 
     /// @brief Interval timer used for detecting request timeouts.
     IntervalTimer timer_;
@@ -380,7 +408,10 @@ private:
     /// @brief Identifier of the current transaction.
     uint64_t current_transid_;
 
-    /// @brief User supplied callback.
+    /// @brief User supplied handshake callback.
+    HttpClient::HandshakeHandler handshake_callback_;
+
+    /// @brief User supplied close callback.
     HttpClient::CloseHandler close_callback_;
 
     /// @brief Flag to indicate that a transaction is running.
@@ -436,6 +467,7 @@ public:
     /// in progress for the given URL. Otherwise, the request is queued.
     ///
     /// @param url Destination where the request should be sent.
+    /// @param context TLS context to be used for the connection.
     /// @param request Pointer to the request to be sent to the server.
     /// @param response Pointer to the object into which the response should be
     /// stored.
@@ -445,24 +477,30 @@ public:
     /// transaction ends.
     /// @param connect_callback Pointer to the user callback to be invoked when the
     /// client connects to the server.
+    /// @param handshake_callback Optional callback invoked when the client
+    /// performs the TLS handshake with the server.
     /// @param close_callback Pointer to the user callback to be invoked when the
     /// client closes the connection to the server.
     void queueRequest(const Url& url,
+                      const TlsContextPtr& context,
                       const HttpRequestPtr& request,
                       const HttpResponsePtr& response,
                       const long request_timeout,
                       const HttpClient::RequestHandler& request_callback,
                       const HttpClient::ConnectHandler& connect_callback,
+                      const HttpClient::HandshakeHandler& handshake_callback,
                       const HttpClient::CloseHandler& close_callback) {
         if (MultiThreadingMgr::instance().getMode()) {
             std::lock_guard<std::mutex> lk(mutex_);
-            return (queueRequestInternal(url, request, response,
+            return (queueRequestInternal(url, context, request, response,
                                          request_timeout, request_callback,
-                                         connect_callback, close_callback));
+                                         connect_callback, handshake_callback,
+                                         close_callback));
         } else {
-            return (queueRequestInternal(url, request, response,
+            return (queueRequestInternal(url, context, request, response,
                                          request_timeout, request_callback,
-                                         connect_callback, close_callback));
+                                         connect_callback, handshake_callback,
+                                         close_callback));
         }
     }
 
@@ -528,8 +566,10 @@ private:
                 RequestDescriptor desc = it->second.front();
                 it->second.pop();
                 desc.conn_->doTransaction(desc.request_, desc.response_,
-                                          desc.request_timeout_, desc.callback_,
+                                          desc.request_timeout_,
+                                          desc.callback_,
                                           desc.connect_callback_,
+                                          desc.handshake_callback_,
                                           desc.close_callback_);
             }
         }
@@ -543,6 +583,7 @@ private:
     /// This method should be called in a thread safe context.
     ///
     /// @param url Destination where the request should be sent.
+    /// @param context TLS context to be used for the connection.
     /// @param request Pointer to the request to be sent to the server.
     /// @param response Pointer to the object into which the response should be
     /// stored.
@@ -552,14 +593,18 @@ private:
     /// transaction ends.
     /// @param connect_callback Pointer to the user callback to be invoked when the
     /// client connects to the server.
+    /// @param handshake_callback Optional callback invoked when the client
+    /// performs the TLS handshake with the server.
     /// @param close_callback Pointer to the user callback to be invoked when the
     /// client closes the connection to the server.
     void queueRequestInternal(const Url& url,
+                              const TlsContextPtr& context,
                               const HttpRequestPtr& request,
                               const HttpResponsePtr& response,
                               const long request_timeout,
                               const HttpClient::RequestHandler& request_callback,
                               const HttpClient::ConnectHandler& connect_callback,
+                              const HttpClient::HandshakeHandler& handshake_callback,
                               const HttpClient::CloseHandler& close_callback) {
         auto it = conns_.find(url);
         if (it != conns_.end()) {
@@ -571,20 +616,24 @@ private:
                                                    request_timeout,
                                                    request_callback,
                                                    connect_callback,
+                                                   handshake_callback,
                                                    close_callback));
             } else {
                 // Connection is idle, so we can start the transaction.
                 conn->doTransaction(request, response, request_timeout,
                                     request_callback, connect_callback,
-                                    close_callback);
+                                    handshake_callback, close_callback);
             }
         } else {
             // There is no connection with this destination yet. Let's create
             // it and start the transaction.
-            ConnectionPtr conn(new Connection(io_service_, shared_from_this(),
+            ConnectionPtr conn(new Connection(io_service_,
+                                              context,
+                                              shared_from_this(),
                                               url));
-            conn->doTransaction(request, response, request_timeout, request_callback,
-                                connect_callback, close_callback);
+            conn->doTransaction(request, response, request_timeout,
+                                request_callback, connect_callback,
+                                handshake_callback, close_callback);
             conns_[url] = conn;
         }
     }
@@ -683,6 +732,8 @@ private:
         /// @param callback Pointer to the user callback.
         /// @param connect_callback pointer to the user callback to be invoked
         /// when the client connects to the server.
+        /// @param handshake_callback Optional callback invoked when the client
+        /// performs the TLS handshake with the server.
         /// @param close_callback pointer to the user callback to be invoked
         /// when the client closes the connection to the server.
         RequestDescriptor(const ConnectionPtr& conn,
@@ -691,10 +742,12 @@ private:
                           const long& request_timeout,
                           const HttpClient::RequestHandler& callback,
                           const HttpClient::ConnectHandler& connect_callback,
+                          const HttpClient::HandshakeHandler& handshake_callback,
                           const HttpClient::CloseHandler& close_callback)
             : conn_(conn), request_(request), response_(response),
               request_timeout_(request_timeout), callback_(callback),
               connect_callback_(connect_callback),
+              handshake_callback_(handshake_callback),
               close_callback_(close_callback) {
         }
 
@@ -716,6 +769,9 @@ private:
         /// @brief Holds pointer to the user callback for connect.
         HttpClient::ConnectHandler connect_callback_;
 
+        /// @brief Holds pointer to the user callback for handshake.
+        HttpClient::HandshakeHandler handshake_callback_;
+
         /// @brief Holds pointer to the user callback for close.
         HttpClient::CloseHandler close_callback_;
     };
@@ -728,12 +784,14 @@ private:
 };
 
 Connection::Connection(IOService& io_service,
+                       const TlsContextPtr& context,
                        const ConnectionPoolPtr& conn_pool,
                        const Url& url)
     : conn_pool_(conn_pool), url_(url), socket_(io_service), timer_(io_service),
       current_request_(), current_response_(), parser_(), current_callback_(),
       buf_(), input_buf_(), current_transid_(0), close_callback_(),
       started_(false) {
+    ////// to finish
 }
 
 Connection::~Connection() {
@@ -770,14 +828,17 @@ Connection::doTransaction(const HttpRequestPtr& request,
                           const long request_timeout,
                           const HttpClient::RequestHandler& callback,
                           const HttpClient::ConnectHandler& connect_callback,
+                          const HttpClient::HandshakeHandler& handshake_callback,
                           const HttpClient::CloseHandler& close_callback) {
     if (MultiThreadingMgr::instance().getMode()) {
         std::lock_guard<std::mutex> lk(mutex_);
         doTransactionInternal(request, response, request_timeout,
-                              callback, connect_callback, close_callback);
+                              callback, connect_callback, handshake_callback,
+                              close_callback);
     } else {
         doTransactionInternal(request, response, request_timeout,
-                              callback, connect_callback, close_callback);
+                              callback, connect_callback, handshake_callback,
+                              close_callback);
     }
 }
 
@@ -787,6 +848,7 @@ Connection::doTransactionInternal(const HttpRequestPtr& request,
                                   const long request_timeout,
                                   const HttpClient::RequestHandler& callback,
                                   const HttpClient::ConnectHandler& connect_callback,
+                                  const HttpClient::HandshakeHandler& handshake_callback,
                                   const HttpClient::CloseHandler& close_callback) {
     try {
         started_ = true;
@@ -795,6 +857,7 @@ Connection::doTransactionInternal(const HttpRequestPtr& request,
         parser_.reset(new HttpResponseParser(*current_response_));
         parser_->initModel();
         current_callback_ = callback;
+        handshake_callback_ = handshake_callback;
         close_callback_ = close_callback;
 
         // Starting new transaction. Generate new transaction id.
@@ -998,6 +1061,21 @@ Connection::scheduleTimer(const long request_timeout) {
     }
 }
 
+void
+Connection::doHandshake(const uint64_t transid) {
+    SocketCallback socket_cb(std::bind(&Connection::handshakeCallback,
+                                       shared_from_this(),
+                                       handshake_callback_,
+                                       transid,
+                                       ph::_1));
+    try {
+        ////////socket_.handshake(false, socket_cb);
+
+    } catch (...) {
+        terminate(boost::asio::error::not_connected);
+    }
+}
+
 void
 Connection::doSend(const uint64_t transid) {
     SocketCallback socket_cb(std::bind(&Connection::sendCallback,
@@ -1059,6 +1137,36 @@ Connection::connectCallback(HttpClient::ConnectHandler connect_callback,
         (ec.value() != boost::asio::error::already_connected)) {
         terminate(ec);
 
+    } else {
+        /////////// insert doHandshake(transid); here
+
+        // Start sending the request asynchronously.
+        doSend(transid);
+    }
+}
+
+void
+Connection::handshakeCallback(HttpClient::ConnectHandler connect_callback,
+                              const uint64_t transid,
+                              const boost::system::error_code& ec) {
+    if (checkPrematureTimeout(transid)) {
+        return;
+    }
+
+    // Run user defined handshake callback if specified.
+    if (handshake_callback_) {
+        // If the user defined callback indicates that the connection
+        // should not be continued.
+        if (!handshake_callback_(ec, socket_.getNative())) {
+            return;
+        }
+    }
+
+    if (ec && (ec.value() == boost::asio::error::operation_aborted)) {
+        return;
+    } else if (ec) {
+        terminate(ec);
+
     } else {
         // Start sending the request asynchronously.
         doSend(transid);
@@ -1219,11 +1327,14 @@ HttpClient::HttpClient(IOService& io_service)
 }
 
 void
-HttpClient::asyncSendRequest(const Url& url, const HttpRequestPtr& request,
+HttpClient::asyncSendRequest(const Url& url,
+                             const TlsContextPtr& context,
+                             const HttpRequestPtr& request,
                              const HttpResponsePtr& response,
                              const HttpClient::RequestHandler& request_callback,
                              const HttpClient::RequestTimeout& request_timeout,
                              const HttpClient::ConnectHandler& connect_callback,
+                             const HttpClient::HandshakeHandler& handshake_callback,
                              const HttpClient::CloseHandler& close_callback) {
     if (!url.isValid()) {
         isc_throw(HttpClientError, "invalid URL specified for the HTTP client");
@@ -1241,8 +1352,10 @@ HttpClient::asyncSendRequest(const Url& url, const HttpRequestPtr& request,
         isc_throw(HttpClientError, "callback for HTTP transaction must not be null");
     }
 
-    impl_->conn_pool_->queueRequest(url, request, response, request_timeout.value_,
-                                    request_callback, connect_callback, close_callback);
+    impl_->conn_pool_->queueRequest(url, context, request, response,
+                                    request_timeout.value_,
+                                    request_callback, connect_callback,
+                                    handshake_callback, close_callback);
 }
 
 void
index 9662b796bcb6a66c4eacdcb0d4a7dc2b74736410..bb4463c4f3b4b2d266f7067317090b9fbaa33605 100644 (file)
@@ -59,11 +59,12 @@ class HttpClientImpl;
 /// a request by trying to read from the socket (with message peeking). If
 /// the socket is usable the client uses it to transmit the request.
 ///
-/// This classes exposes the underlying TCP socket's descriptor for each
-/// connection via connect and close callbacks.  This is done to permit the
-/// sockets to be monitored for IO readiness by external code that's something
-/// other than boost::asio (e.g.select() or epoll()), and would thus otherwise
-/// starve the client's IOService and cause a backlog of ready event handlers.
+/// This classes exposes the underlying transport socket's descriptor for
+/// each connection via connect, handshake and close callbacks.
+/// This is done to permit the sockets to be monitored for IO readiness
+/// by external code that's something other than boost::asio
+/// (e.g.select() or epoll()), and would thus otherwise starve the
+/// client's IOService and cause a backlog of ready event handlers.
 ///
 /// All errors are reported to the caller via the callback function supplied
 /// to the @ref HttpClient::asyncSendRequest. The IO errors are communicated
@@ -100,6 +101,14 @@ public:
     /// so a not null error code does not always mean the connect failed.
     typedef std::function<bool(const boost::system::error_code&, const int)> ConnectHandler;
 
+    /// @brief Optional handler invoked when client performs the TLS handshake
+    /// with the server.
+    ///
+    /// Returned boolean value indicates whether the client should continue
+    /// connecting to the server (if true) or not (false).
+    /// @note The second argument is not used.
+    typedef std::function<bool(const boost::system::error_code&, const int)> HandshakeHandler;
+
     /// @brief Optional handler invoked when client closes the connection to the server.
     ///
     /// It is passed the native socket handler of the connection's TCP socket.
@@ -121,12 +130,13 @@ public:
     /// transaction is started immediately.
     ///
     /// The existing connection is tested before it is used for the new
-    /// transaction by attempting to read (with message peeking) from the open
-    /// TCP socket. If the read attempt is successful, the client will transmit
-    /// the HTTP request to the server using this connection. It is possible
-    /// that the server closes the connection between the connection test and
-    /// sending the request. In such case, an error will be returned and the
-    /// caller will need to try re-sending the request.
+    /// transaction by attempting to read (with message peeking) from
+    /// the open transport socket. If the read attempt is successful,
+    /// the client will transmit the HTTP request to the server using
+    /// this connection. It is possible that the server closes the
+    /// connection between the connection test and sending the request.
+    /// In such case, an error will be returned and the caller will
+    /// need to try re-sending the request.
     ///
     /// If the connection test fails, the client will close the socket and
     /// reconnect to the server prior to sending the request.
@@ -166,6 +176,7 @@ public:
     /// callback can be used to recognize this condition.
     ///
     /// @param url URL where the request should be send.
+    /// @param context TLS context.
     /// @param request Pointer to the object holding a request.
     /// @param response Pointer to the object where response should be stored.
     /// @param request_callback Pointer to the user callback function invoked
@@ -173,11 +184,14 @@ public:
     /// @param request_timeout Timeout for the transaction in milliseconds.
     /// @param connect_callback Optional callback invoked when the client
     /// connects to the server.
+    /// @param handshake_callback Optional callback invoked when the client
+    /// performs the TLS handshake with the server.
     /// @param close_callback Optional callback invoked when the client
     /// closes the connection to the server.
     ///
     /// @throw HttpClientError If invalid arguments were provided.
     void asyncSendRequest(const Url& url,
+                          const asiolink::TlsContextPtr& context,
                           const HttpRequestPtr& request,
                           const HttpResponsePtr& response,
                           const RequestHandler& request_callback,
@@ -185,6 +199,8 @@ public:
                           RequestTimeout(10000),
                           const ConnectHandler& connect_callback =
                           ConnectHandler(),
+                          const HandshakeHandler& handshake_callback =
+                          HandshakeHandler(),
                           const CloseHandler& close_callback =
                           CloseHandler());
 
index 7b425e2bf9159c98d8d62f35c0f9e3e393bff40a..db47dce69c31631550452da57640451e44db40de 100644 (file)
@@ -63,10 +63,12 @@ SocketCallback::operator()(boost::system::error_code ec, size_t length) {
 }
 
 HttpConnection::HttpConnection(asiolink::IOService& io_service,
-                               HttpAcceptor& acceptor,
+                               const HttpAcceptorPtr& acceptor,
+                               const TlsContextPtr& context,
                                HttpConnectionPool& connection_pool,
                                const HttpResponseCreatorPtr& response_creator,
-                               const HttpAcceptorCallback& callback,
+                               const HttpAcceptorCallback& acceptor_callback,
+                               const HttpAcceptorCallback& handshake_callback,
                                const long request_timeout,
                                const long idle_timeout)
     : request_timer_(io_service),
@@ -76,7 +78,8 @@ HttpConnection::HttpConnection(asiolink::IOService& io_service,
       acceptor_(acceptor),
       connection_pool_(connection_pool),
       response_creator_(response_creator),
-      acceptor_callback_(callback) {
+      acceptor_callback_(acceptor_callback),
+      handshake_callback_(handshake_callback) {
 }
 
 HttpConnection::~HttpConnection() {
@@ -110,7 +113,7 @@ HttpConnection::asyncAccept() {
                                         shared_from_this(),
                                         ph::_1); // error
     try {
-        acceptor_.asyncAccept(socket_, cb);
+        acceptor_->asyncAccept(socket_, cb);
 
     } catch (const std::exception& ex) {
         isc_throw(HttpConnectionError, "unable to start accepting TCP "
@@ -118,6 +121,23 @@ HttpConnection::asyncAccept() {
     }
 }
 
+void
+HttpConnection::doHandshake() {
+    // Create instance of the callback. It is safe to pass the local instance
+    // of the callback, because the underlying boost functions make copies
+    // as needed.
+    SocketCallback cb(std::bind(&HttpConnection::handshakeCallback,
+                                shared_from_this(),
+                                ph::_1)); // error
+    try {
+        ////////// socket_.handshake(true, cb);
+
+    } catch (const std::exception& ex) {
+        isc_throw(HttpConnectionError, "unable to perform TLS handshake: "
+                  << ex.what());
+    }
+}
+
 void
 HttpConnection::doRead(TransactionPtr transaction) {
     try {
@@ -193,7 +213,7 @@ HttpConnection::asyncSendResponse(const ConstHttpResponsePtr& response,
 
 void
 HttpConnection::acceptorCallback(const boost::system::error_code& ec) {
-    if (!acceptor_.isOpen()) {
+    if (!acceptor_->isOpen()) {
         return;
     }
 
@@ -206,11 +226,30 @@ HttpConnection::acceptorCallback(const boost::system::error_code& ec) {
     if (!ec) {
         LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_DETAIL,
                   HTTP_REQUEST_RECEIVE_START)
+            //////////// change the message
             .arg(getRemoteEndpointAddressAsText())
             .arg(static_cast<unsigned>(request_timeout_/1000));
 
         setupRequestTimer();
         doRead();
+        ////////// or doHandshake();
+    }
+}
+
+void
+HttpConnection::handshakeCallback(const boost::system::error_code& ec) {
+    if (ec) {
+        stopThisConnection();
+    }
+
+    handshake_callback_(ec);
+
+    if (!ec) {
+        LOG_DEBUG(http_logger, isc::log::DBGLVL_TRACE_DETAIL,
+                  HTTP_REQUEST_RECEIVE_START)
+            .arg(getRemoteEndpointAddressAsText());
+
+        doRead();
     }
 }
 
@@ -427,7 +466,5 @@ HttpConnection::getRemoteEndpointAddressAsText() const {
     return ("(unknown address)");
 }
 
-
 } // end of namespace isc::http
 } // end of namespace isc
-
index fa735064c7ff33206887e7009872c8c8c44ec594..12f752c13ef1e53b503078e1bcd10e9f7fde87f6 100644 (file)
@@ -230,21 +230,26 @@ public:
     /// @brief Constructor.
     ///
     /// @param io_service IO service to be used by the connection.
-    /// @param acceptor Reference to the TCP acceptor object used to listen for
+    /// @param acceptor Pointer to the TCP acceptor object used to listen for
     /// new HTTP connections.
+    /////// add a TLS acceptor
+    /// @param context TLS context.
     /// @param connection_pool Connection pool in which this connection is
     /// stored.
     /// @param response_creator Pointer to the response creator object used to
     /// create HTTP response from the HTTP request received.
-    /// @param callback Callback invoked when new connection is accepted.
+    /// @param acceptor_callback Callback invoked when new connection is accepted.
+    /// @param handshake_callback Callback invoked when TLS handshake is performed.
     /// @param request_timeout Configured timeout for a HTTP request.
     /// @param idle_timeout Timeout after which persistent HTTP connection is
     /// closed by the server.
     HttpConnection(asiolink::IOService& io_service,
-                   HttpAcceptor& acceptor,
+                   const HttpAcceptorPtr& acceptor,
+                   const asiolink::TlsContextPtr& context,
                    HttpConnectionPool& connection_pool,
                    const HttpResponseCreatorPtr& response_creator,
-                   const HttpAcceptorCallback& callback,
+                   const HttpAcceptorCallback& acceptor_callback,
+                   const HttpAcceptorCallback& handshake_callback,
                    const long request_timeout,
                    const long idle_timeout);
 
@@ -256,12 +261,19 @@ public:
     /// @brief Asynchronously accepts new connection.
     ///
     /// When the connection is established successfully, the timeout timer is
-    /// setup and the asynchronous read from the socket is started.
+    /// setup and the asynchronous read from the socket or handshake with
+    /// the client is started.
     void asyncAccept();
 
     /// @brief Closes the socket.
     void close();
 
+    /// @brief Asynchronously performs TLS handshake.
+    ///
+    /// When the handshake is performed successfully, the asynchronous read
+    /// from the socket is started.
+    void doHandshake();
+
     /// @brief Starts asynchronous read from the socket.
     ///
     /// The data received over the socket are supplied to the HTTP parser until
@@ -301,11 +313,20 @@ protected:
     ///
     /// It invokes external (supplied via constructor) acceptor callback. If
     /// the acceptor is not opened it returns immediately. If the connection
-    /// is accepted successfully the @ref HttpConnection::doRead is called.
+    /// is accepted successfully the @ref HttpConnection::doRead or
+    /// @ref HttpConnection::doHandshake is called.
     ///
     /// @param ec Error code.
     void acceptorCallback(const boost::system::error_code& ec);
 
+    /// @brief Local callback invoked when TLS handshake is performed.
+    ///
+    /// If the handshake is performed successfully the @ref
+    /// HttpConnection::doRead is called.
+    ///
+    /// @param ec Error code.
+    void handshakeCallback(const boost::system::error_code& ec);
+
     /// @brief Callback invoked when new data is received over the socket.
     ///
     /// This callback supplies the data to the HTTP parser and continues
@@ -360,6 +381,9 @@ protected:
     /// @brief Configured Request Timeout in milliseconds.
     long request_timeout_;
 
+    /// @brief TLS context.
+    asiolink::TlsContextPtr context_;
+
     /// @brief Timeout after which the persistent HTTP connection is closed
     /// by the server.
     long idle_timeout_;
@@ -367,8 +391,9 @@ protected:
     /// @brief Socket used by this connection.
     asiolink::TCPSocket<SocketCallback> socket_;
 
-    /// @brief Reference to the TCP acceptor used to accept new connections.
-    HttpAcceptor& acceptor_;
+    /// @brief Pointer to the TCP acceptor used to accept new connections.
+    HttpAcceptorPtr acceptor_;
+    /////////////// Add a TLS acceptor.
 
     /// @brief Connection pool holding this connection.
     HttpConnectionPool& connection_pool_;
@@ -379,6 +404,9 @@ protected:
 
     /// @brief External TCP acceptor callback.
     HttpAcceptorCallback acceptor_callback_;
+
+    /// @brief External TLS handshake callback.
+    HttpAcceptorCallback handshake_callback_;
 };
 
 } // end of namespace isc::http
index 9ef70123b497ab6296ba5a8d7dcdac96bc566db1..26fa0236e9b02c7933423959aab27c6675c42707 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -7,7 +7,8 @@
 #ifndef HTTP_ACCEPTOR_H
 #define HTTP_ACCEPTOR_H
 
-#include <asiolink/tcp_acceptor.h>
+#include <asiolink/tls_acceptor.h>
+#include <boost/shared_ptr.hpp>
 #include <boost/system/system_error.hpp>
 #include <functional>
 
@@ -21,6 +22,9 @@ HttpAcceptorCallback;
 /// @brief Type of the TCP acceptor used in this library.
 typedef asiolink::TCPAcceptor<HttpAcceptorCallback> HttpAcceptor;
 
+/// @brief Type of shared pointer to TCP acceptors.
+typedef boost::shared_ptr<HttpAcceptor> HttpAcceptorPtr;
+
 } // end of namespace isc::http
 } // end of namespace isc
 
index 87e7a69e4f448d3dd729337f4b34da5f57f016ff..f3af6b9fe491685a713e1c57f11343ac7c6221ba 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2016-2020 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2016-2021 Internet Systems Consortium, Inc. ("ISC")
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -33,6 +33,12 @@ from the server. The first argument specifies an URL of the server. The
 second argument provides a response in the textual format. The request is
 truncated by the logger if it is too large to be printed.
 
+% HTTP_CLIENT_HANDSHAKE_START start TLS handshake with %1 with timeout %2
+This debug message is issued when the server starts the TLS handshake
+with the remote endpoint. The first argument specifies the address
+of the remote endpoint. The second argument specifies request timeout in
+seconds.
+
 % HTTP_CLIENT_REQUEST_AUTHORIZED received HTTP request authorized for '%1'
 This information message is issued when the server receives with a matching
 authentication header. The argument provides the user id.
@@ -115,7 +121,7 @@ will be interrupted. New transactions should be conducted normally.
 This debug message is issued when the server starts receiving new request
 over the established connection. The first argument specifies the address
 of the remote endpoint. The second argument specifies request timeout in
-seconds.
+seconds.TTTTTTTTTOOOOOOOOOOOODDDDDDDDDDOOOOOOOOO
 
 % HTTP_SERVER_RESPONSE_RECEIVED received HTTP response from %1
 This debug message is issued when the client finished receiving an HTTP
index 720570a0469399b93d663de6f72e478a1b6addc7..63455f88f5f9bcc01b2d348fbbecbe189c29f7fa 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -18,11 +18,13 @@ namespace http {
 HttpListener::HttpListener(IOService& io_service,
                            const asiolink::IOAddress& server_address,
                            const unsigned short server_port,
+                           const TlsContextPtr& context,
                            const HttpResponseCreatorFactoryPtr& creator_factory,
                            const HttpListener::RequestTimeout& request_timeout,
                            const HttpListener::IdleTimeout& idle_timeout)
     : impl_(new HttpListenerImpl(io_service, server_address, server_port,
-                                 creator_factory, request_timeout.value_,
+                                 context, creator_factory,
+                                 request_timeout.value_,
                                  idle_timeout.value_)) {
 }
 
index f64d3edd99f3f353df24a65765fdc86c26de1b5f..6379e632d7344a42625a10747c7d77b6743b582c 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2017-2019 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -9,6 +9,9 @@
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_service.h>
+#include <asiolink/crypto_tls.h>
+#include <asiolink/openssl_tls.h>
+#include <asiolink/botan_tls.h>
 #include <exceptions/exceptions.h>
 #include <http/response_creator_factory.h>
 #include <boost/shared_ptr.hpp>
@@ -30,7 +33,7 @@ class HttpListenerImpl;
 /// @brief HTTP listener.
 ///
 /// This class is an entry point to the use of HTTP services in Kea.
-/// It creates a TCP acceptor service on the specified address and
+/// It creates a transport acceptor service on the specified address and
 /// port and listens to the incoming HTTP connections. The constructor
 /// receives a pointer to the implementation of the
 /// @ref HttpResponseCreatorFactory, which is used by the @ref HttpListener
@@ -84,6 +87,7 @@ public:
     /// @param io_service IO service to be used by the listener.
     /// @param server_address Address on which the HTTP service should run.
     /// @param server_port Port number on which the HTTP service should run.
+    /// @param context TLS context.
     /// @param creator_factory Pointer to the caller-defined
     /// @ref HttpResponseCreatorFactory derivation which should be used to
     /// create @ref HttpResponseCreator instances.
@@ -97,13 +101,14 @@ public:
     HttpListener(asiolink::IOService& io_service,
                  const asiolink::IOAddress& server_address,
                  const unsigned short server_port,
+                 const asiolink::TlsContextPtr& context,
                  const HttpResponseCreatorFactoryPtr& creator_factory,
                  const RequestTimeout& request_timeout,
                  const IdleTimeout& idle_timeout);
 
     /// @brief Destructor.
     ///
-    /// Stops all active connections and closes TCP acceptor service.
+    /// Stops all active connections and closes transport acceptor service.
     ~HttpListener();
 
     /// @brief Returns local address on which server is listening.
index 1a80f6817ff205b9b52315aeb7c609aa055404b5..d401d4dac2e717e17bcb5a1f6c3455f29bbbf9b6 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -19,10 +19,13 @@ namespace http {
 HttpListenerImpl::HttpListenerImpl(IOService& io_service,
                                    const asiolink::IOAddress& server_address,
                                    const unsigned short server_port,
+                                   const TlsContextPtr& context,
                                    const HttpResponseCreatorFactoryPtr& creator_factory,
                                    const long request_timeout,
                                    const long idle_timeout)
-    : io_service_(io_service), acceptor_(io_service),
+    : io_service_(io_service), context_(context),
+      ///////// move acceptor init below
+      acceptor_(new HttpAcceptor(io_service)),
       endpoint_(), connections_(),
       creator_factory_(creator_factory),
       request_timeout_(request_timeout), idle_timeout_(idle_timeout) {
@@ -62,10 +65,10 @@ HttpListenerImpl::getEndpoint() const {
 void
 HttpListenerImpl::start() {
     try {
-        acceptor_.open(*endpoint_);
-        acceptor_.setOption(HttpAcceptor::ReuseAddress(true));
-        acceptor_.bind(*endpoint_);
-        acceptor_.listen();
+        acceptor_->open(*endpoint_);
+        acceptor_->setOption(HttpAcceptor::ReuseAddress(true));
+        acceptor_->bind(*endpoint_);
+        acceptor_->listen();
 
     } catch (const boost::system::system_error& ex) {
         stop();
@@ -79,7 +82,7 @@ HttpListenerImpl::start() {
 void
 HttpListenerImpl::stop() {
     connections_.stopAll();
-    acceptor_.close();
+    acceptor_->close();
 }
 
 void
@@ -90,8 +93,11 @@ HttpListenerImpl::accept() {
     HttpResponseCreatorPtr response_creator = creator_factory_->create();
     HttpAcceptorCallback acceptor_callback =
         std::bind(&HttpListenerImpl::acceptHandler, this, ph::_1);
+    HttpAcceptorCallback handshake_callback =
+        std::bind(&HttpListenerImpl::handshakeHandler, this, ph::_1);
     HttpConnectionPtr conn = createConnection(response_creator,
-                                              acceptor_callback);
+                                              acceptor_callback,
+                                              handshake_callback);
     // Add this new connection to the pool.
     connections_.start(conn);
 }
@@ -103,12 +109,20 @@ HttpListenerImpl::acceptHandler(const boost::system::error_code&) {
     accept();
 }
 
+void
+HttpListenerImpl::handshakeHandler(const boost::system::error_code&) {
+    // The TLS handshake has been performed.
+    ///////////// DO MORE!!!!!!!!
+}
+
 HttpConnectionPtr
 HttpListenerImpl::createConnection(const HttpResponseCreatorPtr& response_creator,
-                                   const HttpAcceptorCallback& callback) {
+                                   const HttpAcceptorCallback& acceptor_callback,
+                                   const HttpAcceptorCallback& handshake_callback) {
     HttpConnectionPtr
-        conn(new HttpConnection(io_service_, acceptor_, connections_,
-                                response_creator, callback,
+        conn(new HttpConnection(io_service_, acceptor_, context_,
+                                connections_, response_creator,
+                                acceptor_callback, handshake_callback,
                                 request_timeout_, idle_timeout_));
     return (conn);
 }
index 2ae81037e1528b18ed6ec8551c8a1461e766a3bf..26e54fbb3c30a574ded14a0e046cd3118650686f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2019,2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2019-2021 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -33,6 +33,7 @@ public:
     /// @param io_service IO service to be used by the listener.
     /// @param server_address Address on which the HTTP service should run.
     /// @param server_port Port number on which the HTTP service should run.
+    /// @param context TLS context.
     /// @param creator_factory Pointer to the caller-defined
     /// @ref HttpResponseCreatorFactory derivation which should be used to
     /// create @ref HttpResponseCreator instances.
@@ -46,6 +47,7 @@ public:
     HttpListenerImpl(asiolink::IOService& io_service,
                      const asiolink::IOAddress& server_address,
                      const unsigned short server_port,
+                     const asiolink::TlsContextPtr& context,
                      const HttpResponseCreatorFactoryPtr& creator_factory,
                      const long request_timeout,
                      const long idle_timeout);
@@ -87,6 +89,11 @@ protected:
     /// @param ec Error code passed to the handler. This is currently ignored.
     void acceptHandler(const boost::system::error_code& ec);
 
+    /// @brief Callback invoked when the TLS handshake is performed.
+    ///
+    /// @param ec Error code passed to the handler. This is currently ignored.
+    void handshakeHandler(const boost::system::error_code& ec);
+
     /// @brief Creates an instance of the @c HttpConnection.
     ///
     /// This method is virtual so as it can be overridden when customized
@@ -94,17 +101,23 @@ protected:
     ///
     /// @param response_creator Pointer to the response creator object used to
     /// create HTTP response from the HTTP request received.
-    /// @param callback Callback invoked when new connection is accepted.
+    /// @param acceptor_callback Callback invoked when new connection is accepted.
+    /// @param handshake_callback Callback invoked when TLS handshake is performed.
     ///
     /// @return Pointer to the created connection.
     virtual HttpConnectionPtr createConnection(const HttpResponseCreatorPtr& response_creator,
-                                               const HttpAcceptorCallback& callback);
+                                               const HttpAcceptorCallback& acceptor_callback,
+                                               const HttpAcceptorCallback& handshake_callback);
 
     /// @brief Reference to the IO service.
     asiolink::IOService& io_service_;
 
+    /// @brief TLS context.
+    asiolink::TlsContextPtr context_;
+
     /// @brief Acceptor instance.
-    HttpAcceptor acceptor_;
+    HttpAcceptorPtr acceptor_;
+    /////////// Fork it?
 
     /// @brief Pointer to the endpoint representing IP address and port on
     /// which the service is running.
index 2dc175f2373172fbd9a2efe372c97becbbc41404..2e97f269fb467ddbcb114cde0f3f755f8aa8d063 100644 (file)
@@ -1,7 +1,8 @@
 SUBDIRS = .
 
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
-AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
+AM_CPPFLAGS += -DTEST_CA_DIR=\"$(srcdir)/../../asiolink/tests/ca\"
 AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/http/tests\"
 AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
 
@@ -42,7 +43,7 @@ libhttp_unittests_SOURCES += test_http_client.h
 
 libhttp_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 libhttp_unittests_CXXFLAGS = $(AM_CXXFLAGS)
-libhttp_unittests_LDFLAGS  = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+libhttp_unittests_LDFLAGS  = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
 
 libhttp_unittests_LDADD  = $(top_builddir)/src/lib/http/libkea-http.la
 libhttp_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
@@ -53,7 +54,7 @@ libhttp_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
 libhttp_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
 libhttp_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 libhttp_unittests_LDADD += $(LOG4CPLUS_LIBS)
-libhttp_unittests_LDADD += $(BOOST_LIBS) $(GTEST_LDADD)
+libhttp_unittests_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(GTEST_LDADD)
 endif
 
 noinst_PROGRAMS = $(TESTS)
index 0635f2a9bd92870e047eb3b99b49677942091068..18da6401c4b3fcd27169286c2cb491744684c06d 100644 (file)
@@ -105,7 +105,9 @@ public:
 
     /// @brief Constructor.
     HttpConnectionPoolTest()
-        : io_service_(), acceptor_(io_service_), connection_pool_(),
+        : io_service_(),
+          acceptor_(new HttpAcceptor(io_service_)),
+          connection_pool_(),
           response_creator_(new TestHttpResponseCreator()) {
         MultiThreadingMgr::instance().setMode(false);
     }
@@ -119,16 +121,20 @@ public:
     void startStopTest() {
         // Create two distinct connections.
         HttpConnectionPtr conn1(new HttpConnection(io_service_, acceptor_,
+                                                   TlsContextPtr(),
                                                    connection_pool_,
                                                    response_creator_,
                                                    HttpAcceptorCallback(),
+                                                   HttpAcceptorCallback(),
                                                    CONN_REQUEST_TIMEOUT,
                                                    CONN_IDLE_TIMEOUT));
 
         HttpConnectionPtr conn2(new HttpConnection(io_service_, acceptor_,
+                                                   TlsContextPtr(),
                                                    connection_pool_,
                                                    response_creator_,
                                                    HttpAcceptorCallback(),
+                                                   HttpAcceptorCallback(),
                                                    CONN_REQUEST_TIMEOUT,
                                                    CONN_IDLE_TIMEOUT));
         // The pool should be initially empty.
@@ -162,16 +168,20 @@ public:
     void stopAllTest() {
         // Create two distinct connections.
         HttpConnectionPtr conn1(new HttpConnection(io_service_, acceptor_,
+                                                   TlsContextPtr(),
                                                    connection_pool_,
                                                    response_creator_,
                                                    HttpAcceptorCallback(),
+                                                   HttpAcceptorCallback(),
                                                    CONN_REQUEST_TIMEOUT,
                                                    CONN_IDLE_TIMEOUT));
 
         HttpConnectionPtr conn2(new HttpConnection(io_service_, acceptor_,
+                                                   TlsContextPtr(),
                                                    connection_pool_,
                                                    response_creator_,
                                                    HttpAcceptorCallback(),
+                                                   HttpAcceptorCallback(),
                                                    CONN_REQUEST_TIMEOUT,
                                                    CONN_IDLE_TIMEOUT));
         TestHttpConnectionPool pool;
@@ -189,16 +199,20 @@ public:
     /// @brief Verifies that stopping a non-existing connection is no-op.
     void stopInvalidTest() {
         HttpConnectionPtr conn1(new HttpConnection(io_service_, acceptor_,
+                                                   TlsContextPtr(),
                                                    connection_pool_,
                                                    response_creator_,
                                                    HttpAcceptorCallback(),
+                                                   HttpAcceptorCallback(),
                                                    CONN_REQUEST_TIMEOUT,
                                                    CONN_IDLE_TIMEOUT));
 
         HttpConnectionPtr conn2(new HttpConnection(io_service_, acceptor_,
+                                                   TlsContextPtr(),
                                                    connection_pool_,
                                                    response_creator_,
                                                    HttpAcceptorCallback(),
+                                                   HttpAcceptorCallback(),
                                                    CONN_REQUEST_TIMEOUT,
                                                    CONN_IDLE_TIMEOUT));
         TestHttpConnectionPool pool;
@@ -209,7 +223,7 @@ public:
     }
 
     IOService io_service_;                      ///< IO service.
-    HttpAcceptor acceptor_;                     ///< Test acceptor.
+    HttpAcceptorPtr acceptor_;                  ///< Test acceptor.
     HttpConnectionPool connection_pool_;        ///< Test connection pool.
     HttpResponseCreatorPtr response_creator_;   ///< Test response creator.
 
index f6cf94869b049952929b2a8ec2d31e1fd0a5fa56..48316b9c36ad53fdd4689a007816247c879ba11c 100644 (file)
@@ -7,6 +7,7 @@
 #include <config.h>
 #include <asiolink/asio_wrapper.h>
 #include <asiolink/interval_timer.h>
+#include <asiolink/tls_acceptor.h>
 #include <cc/data.h>
 #include <test_http_client.h>
 #include <http/client.h>
@@ -210,11 +211,12 @@ public:
     HttpListenerImplCustom(IOService& io_service,
                            const IOAddress& server_address,
                            const unsigned short server_port,
+                           const TlsContextPtr& context,
                            const HttpResponseCreatorFactoryPtr& creator_factory,
                            const long request_timeout,
                            const long idle_timeout)
         : HttpListenerImpl(io_service, server_address, server_port,
-                           creator_factory, request_timeout,
+                           context, creator_factory, request_timeout,
                            idle_timeout) {
     }
 
@@ -226,23 +228,28 @@ protected:
     /// connections are to be used, e.g. in case of unit testing.
     ///
     /// @param io_service IO service to be used by the connection.
-    /// @param acceptor Reference to the TCP acceptor object used to listen for
+    /// @param acceptor Pointer to the TCP acceptor object used to listen for
     /// new HTTP connections.
+    /// @param context TLS context.
     /// @param connection_pool Connection pool in which this connection is
     /// stored.
     /// @param response_creator Pointer to the response creator object used to
     /// create HTTP response from the HTTP request received.
-    /// @param callback Callback invoked when new connection is accepted.
+    /// @param acceptor_callback Callback invoked when new connection is accepted.
+    /// @param handshake_callback Callback invoked when TLS handshake is performed.
     /// @param request_timeout Configured timeout for a HTTP request.
     /// @param idle_timeout Timeout after which persistent HTTP connection is
     /// closed by the server.
     ///
     /// @return Pointer to the created connection.
     virtual HttpConnectionPtr createConnection(const HttpResponseCreatorPtr& response_creator,
-                                               const HttpAcceptorCallback& callback) {
+                                               const HttpAcceptorCallback& acceptor_callback,
+                                               const HttpAcceptorCallback& handshake_callback) {
         HttpConnectionPtr
-            conn(new HttpConnectionType(io_service_, acceptor_, connections_,
-                                        response_creator, callback,
+            conn(new HttpConnectionType(io_service_, acceptor_,
+                                        context_, connections_,
+                                        response_creator,
+                                        acceptor_callback, handshake_callback,
                                         request_timeout_, idle_timeout_));
         return (conn);
     }
@@ -264,6 +271,7 @@ public:
     /// @param io_service IO service to be used by the listener.
     /// @param server_address Address on which the HTTP service should run.
     /// @param server_port Port number on which the HTTP service should run.
+    /// @param context TLS context.
     /// @param creator_factory Pointer to the caller-defined
     /// @ref HttpResponseCreatorFactory derivation which should be used to
     /// create @ref HttpResponseCreator instances.
@@ -277,16 +285,18 @@ public:
     HttpListenerCustom(IOService& io_service,
                        const IOAddress& server_address,
                        const unsigned short server_port,
+                       const TlsContextPtr& context,
                        const HttpResponseCreatorFactoryPtr& creator_factory,
                        const HttpListener::RequestTimeout& request_timeout,
                        const HttpListener::IdleTimeout& idle_timeout)
-        : HttpListener(io_service, server_address, server_port, creator_factory,
+        : HttpListener(io_service, server_address, server_port,
+                       context, creator_factory,
                        request_timeout, idle_timeout) {
         // Replace the default implementation with the customized version
         // using the custom derivation of the HttpConnection.
         impl_.reset(new HttpListenerImplCustom<HttpConnectionType>
                     (io_service, server_address, server_port,
-                     creator_factory, request_timeout.value_,
+                     context, creator_factory, request_timeout.value_,
                      idle_timeout.value_));
     }
 };
@@ -299,25 +309,30 @@ public:
     /// @brief Constructor.
     ///
     /// @param io_service IO service to be used by the connection.
-    /// @param acceptor Reference to the TCP acceptor object used to listen for
+    /// @param acceptor Pointer to the TCP acceptor object used to listen for
     /// new HTTP connections.
+    /// @param context TLS context.
     /// @param connection_pool Connection pool in which this connection is
     /// stored.
     /// @param response_creator Pointer to the response creator object used to
     /// create HTTP response from the HTTP request received.
-    /// @param callback Callback invoked when new connection is accepted.
+    /// @param acceptor_callback Callback invoked when new connection is accepted.
+    /// @param handshake_callback Callback invoked when TLS handshake is performed.
     /// @param request_timeout Configured timeout for a HTTP request.
     /// @param idle_timeout Timeout after which persistent HTTP connection is
     /// closed by the server.
     HttpConnectionLongWriteBuffer(IOService& io_service,
-                                  HttpAcceptor& acceptor,
+                                  const HttpAcceptorPtr& acceptor,
+                                  const TlsContextPtr& context,
                                   HttpConnectionPool& connection_pool,
                                   const HttpResponseCreatorPtr& response_creator,
-                                  const HttpAcceptorCallback& callback,
+                                  const HttpAcceptorCallback& acceptor_callback,
+                                  const HttpAcceptorCallback& handshake_callback,
                                   const long request_timeout,
                                   const long idle_timeout)
-        : HttpConnection(io_service, acceptor, connection_pool,
-                         response_creator, callback, request_timeout,
+        : HttpConnection(io_service, acceptor, context, connection_pool,
+                         response_creator, acceptor_callback,
+                         handshake_callback, request_timeout,
                          idle_timeout) {
     }
 
@@ -344,25 +359,30 @@ public:
     /// @brief Constructor.
     ///
     /// @param io_service IO service to be used by the connection.
-    /// @param acceptor Reference to the TCP acceptor object used to listen for
+    /// @param acceptor Pointer to the TCP acceptor object used to listen for
     /// new HTTP connections.
+    /// @param context TLS context.
     /// @param connection_pool Connection pool in which this connection is
     /// stored.
     /// @param response_creator Pointer to the response creator object used to
     /// create HTTP response from the HTTP request received.
-    /// @param callback Callback invoked when new connection is accepted.
+    /// @param acceptor_callback Callback invoked when new connection is accepted.
+    /// @param handshake_callback Callback invoked when TLS handshake is performed.
     /// @param request_timeout Configured timeout for a HTTP request.
     /// @param idle_timeout Timeout after which persistent HTTP connection is
     /// closed by the server.
     HttpConnectionTransactionChange(IOService& io_service,
-                                    HttpAcceptor& acceptor,
+                                    const HttpAcceptorPtr& acceptor,
+                                    const TlsContextPtr& context,
                                     HttpConnectionPool& connection_pool,
                                     const HttpResponseCreatorPtr& response_creator,
-                                    const HttpAcceptorCallback& callback,
+                                    const HttpAcceptorCallback& acceptor_callback,
+                                    const HttpAcceptorCallback& handshake_callback,
                                     const long request_timeout,
                                     const long idle_timeout)
-        : HttpConnection(io_service, acceptor, connection_pool,
-                         response_creator, callback, request_timeout,
+        : HttpConnection(io_service, acceptor, context, connection_pool,
+                         response_creator, acceptor_callback,
+                         handshake_callback, request_timeout,
                          idle_timeout) {
     }
 
@@ -480,7 +500,9 @@ public:
                             const HttpVersion& expected_version) {
         // Open the listener with the Request Timeout of 1 sec and post the
         // partial request.
-        HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
+        TlsContextPtr context;
+        HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS),
+                              SERVER_PORT, context,
                               factory_, HttpListener::RequestTimeout(1000),
                               HttpListener::IdleTimeout(IDLE_TIMEOUT));
         ASSERT_NO_THROW(listener.start());
@@ -531,9 +553,11 @@ public:
             "{ }";
 
         // Use custom listener and the specialized connection object.
+        TlsContextPtr context;
         HttpListenerCustom<HttpConnectionType>
             listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                     factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                     context, factory_,
+                     HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                      HttpListener::IdleTimeout(IDLE_TIMEOUT));
 
         ASSERT_NO_THROW(listener.start());
@@ -575,8 +599,10 @@ TEST_F(HttpListenerTest, listen) {
         "Content-Length: 3\r\n\r\n"
         "{ }";
 
+    TlsContextPtr context;
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                          factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                          context, factory_,
+                          HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                           HttpListener::IdleTimeout(IDLE_TIMEOUT));
     ASSERT_NO_THROW(listener.start());
     ASSERT_EQ(SERVER_ADDRESS, listener.getLocalAddress().toText());
@@ -605,8 +631,10 @@ TEST_F(HttpListenerTest, keepAlive) {
         "Connection: Keep-Alive\r\n\r\n"
         "{ }";
 
+    TlsContextPtr context;
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                          factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                          context, factory_,
+                          HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                           HttpListener::IdleTimeout(IDLE_TIMEOUT));
 
     ASSERT_NO_THROW(listener.start());
@@ -653,8 +681,10 @@ TEST_F(HttpListenerTest, persistentConnection) {
         "Content-Length: 3\r\n\r\n"
         "{ }";
 
+    TlsContextPtr context;
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                          factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                          context, factory_,
+                          HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                           HttpListener::IdleTimeout(IDLE_TIMEOUT));
 
     ASSERT_NO_THROW(listener.start());
@@ -703,9 +733,11 @@ TEST_F(HttpListenerTest, keepAliveTimeout) {
         "Connection: Keep-Alive\r\n\r\n"
         "{ }";
 
+    TlsContextPtr context;
     // Specify the idle timeout of 500ms.
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                          factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                          context, factory_,
+                          HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                           HttpListener::IdleTimeout(500));
 
     ASSERT_NO_THROW(listener.start());
@@ -759,9 +791,11 @@ TEST_F(HttpListenerTest, persistentConnectionTimeout) {
         "Content-Length: 3\r\n\r\n"
         "{ }";
 
+    TlsContextPtr context;
     // Specify the idle timeout of 500ms.
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                          factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                          context, factory_,
+                          HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                           HttpListener::IdleTimeout(500));
 
     ASSERT_NO_THROW(listener.start());
@@ -815,8 +849,10 @@ TEST_F(HttpListenerTest, persistentConnectionBadBody) {
         "Content-Length: 12\r\n\r\n"
         "{ \"a\": abc }";
 
+    TlsContextPtr context;
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                          factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                          context, factory_,
+                          HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                           HttpListener::IdleTimeout(IDLE_TIMEOUT));
 
     ASSERT_NO_THROW(listener.start());
@@ -859,8 +895,10 @@ TEST_F(HttpListenerTest, persistentConnectionBadBody) {
 
 // This test verifies that the HTTP listener can't be started twice.
 TEST_F(HttpListenerTest, startTwice) {
+    TlsContextPtr context;
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                          factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                          context, factory_,
+                          HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                           HttpListener::IdleTimeout(IDLE_TIMEOUT));
     ASSERT_NO_THROW(listener.start());
     EXPECT_THROW(listener.start(), HttpListenerError);
@@ -875,8 +913,10 @@ TEST_F(HttpListenerTest, badRequest) {
         "Content-Length: 3\r\n\r\n"
         "{ }";
 
+    TlsContextPtr context;
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                          factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                          context, factory_,
+                          HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                           HttpListener::IdleTimeout(IDLE_TIMEOUT));
     ASSERT_NO_THROW(listener.start());
     ASSERT_NO_THROW(startRequest(request));
@@ -896,8 +936,10 @@ TEST_F(HttpListenerTest, badRequest) {
 // This test verifies that NULL pointer can't be specified for the
 // HttpResponseCreatorFactory.
 TEST_F(HttpListenerTest, invalidFactory) {
+    TlsContextPtr context;
     EXPECT_THROW(HttpListener(io_service_, IOAddress(SERVER_ADDRESS),
-                              SERVER_PORT, HttpResponseCreatorFactoryPtr(),
+                              SERVER_PORT, context,
+                              HttpResponseCreatorFactoryPtr(),
                               HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                               HttpListener::IdleTimeout(IDLE_TIMEOUT)),
                  HttpListenerError);
@@ -906,8 +948,10 @@ TEST_F(HttpListenerTest, invalidFactory) {
 // This test verifies that the timeout of 0 can't be specified for the
 // Request Timeout.
 TEST_F(HttpListenerTest, invalidRequestTimeout) {
+    TlsContextPtr context;
     EXPECT_THROW(HttpListener(io_service_, IOAddress(SERVER_ADDRESS),
-                              SERVER_PORT, factory_, HttpListener::RequestTimeout(0),
+                              SERVER_PORT, context, factory_,
+                              HttpListener::RequestTimeout(0),
                               HttpListener::IdleTimeout(IDLE_TIMEOUT)),
                  HttpListenerError);
 }
@@ -915,8 +959,9 @@ TEST_F(HttpListenerTest, invalidRequestTimeout) {
 // This test verifies that the timeout of 0 can't be specified for the
 // idle persistent connection timeout.
 TEST_F(HttpListenerTest, invalidIdleTimeout) {
+    TlsContextPtr context;
     EXPECT_THROW(HttpListener(io_service_, IOAddress(SERVER_ADDRESS),
-                              SERVER_PORT, factory_,
+                              SERVER_PORT, context, factory_,
                               HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                               HttpListener::IdleTimeout(0)),
                  HttpListenerError);
@@ -933,10 +978,11 @@ TEST_F(HttpListenerTest, addressInUse) {
     acceptor.open(endpoint.protocol());
     acceptor.bind(endpoint);
 
+    TlsContextPtr context;
     // Listener should report an error when we try to start it because another
     // acceptor is bound to that port and address.
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS),
-                          SERVER_PORT + 1, factory_,
+                          SERVER_PORT + 1, context, factory_,
                           HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                           HttpListener::IdleTimeout(IDLE_TIMEOUT));
     EXPECT_THROW(listener.start(), HttpListenerError);
@@ -989,13 +1035,16 @@ public:
     HttpClientTest()
         : HttpListenerTest(),
           listener_(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
-                    factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                    TlsContextPtr(), factory_,
+                    HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                     HttpListener::IdleTimeout(IDLE_TIMEOUT)),
           listener2_(io_service_, IOAddress(IPV6_SERVER_ADDRESS), SERVER_PORT + 1,
-                     factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                     TlsContextPtr(), factory_,
+                     HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                      HttpListener::IdleTimeout(IDLE_TIMEOUT)),
           listener3_(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT + 2,
-                     factory_, HttpListener::RequestTimeout(REQUEST_TIMEOUT),
+                     TlsContextPtr(), factory_,
+                     HttpListener::RequestTimeout(REQUEST_TIMEOUT),
                      HttpListener::IdleTimeout(SHORT_IDLE_TIMEOUT)) {
         MultiThreadingMgr::instance().setMode(false);
     }
@@ -1050,10 +1099,12 @@ public:
         Url url("http://127.0.0.1:18123");
 
         // Initiate request to the server.
+        TlsContextPtr context1;
         PostHttpRequestJsonPtr request1 = createRequest("sequence", 1, version);
         HttpResponseJsonPtr response1(new HttpResponseJson());
         unsigned resp_num = 0;
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context1,
+                                                request1, response1,
             [this, &resp_num](const boost::system::error_code& ec,
                               const HttpResponsePtr&,
                               const std::string&) {
@@ -1064,9 +1115,11 @@ public:
         }));
 
         // Initiate another request to the destination.
+        TlsContextPtr context2;
         PostHttpRequestJsonPtr request2 = createRequest("sequence", 2, version);
         HttpResponseJsonPtr response2(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context2,
+                                                request2, response2,
             [this, &resp_num](const boost::system::error_code& ec,
                               const HttpResponsePtr&,
                               const std::string&) {
@@ -1109,10 +1162,12 @@ public:
         Url url2("http://[::1]:18124");
 
         // Create a request to the first server.
+        TlsContextPtr context1;
         PostHttpRequestJsonPtr request1 = createRequest("sequence", 1);
         HttpResponseJsonPtr response1(new HttpResponseJson());
         unsigned resp_num = 0;
-        ASSERT_NO_THROW(client.asyncSendRequest(url1, request1, response1,
+        ASSERT_NO_THROW(client.asyncSendRequest(url1, context1,
+                                                request1, response1,
             [this, &resp_num](const boost::system::error_code& ec,
                               const HttpResponsePtr&,
                               const std::string&) {
@@ -1123,9 +1178,11 @@ public:
         }));
 
         // Create a request to the second server.
+        TlsContextPtr context2;
         PostHttpRequestJsonPtr request2 = createRequest("sequence", 2);
         HttpResponseJsonPtr response2(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url2, request2, response2,
+        ASSERT_NO_THROW(client.asyncSendRequest(url2, context2,
+                                                request2, response2,
             [this, &resp_num](const boost::system::error_code& ec,
                               const HttpResponsePtr&,
                               const std::string&) {
@@ -1163,9 +1220,11 @@ public:
         Url url("http://127.0.0.1:18125");
 
         // Create the first request.
+        TlsContextPtr context1;
         PostHttpRequestJsonPtr request1 = createRequest("sequence", 1);
         HttpResponseJsonPtr response1(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context1,
+                                                request1, response1,
             [this](const boost::system::error_code& ec, const HttpResponsePtr&,
                    const std::string&) {
             io_service_.stop();
@@ -1185,9 +1244,11 @@ public:
         ASSERT_NO_THROW(runIOService(SHORT_IDLE_TIMEOUT * 2));
 
         // Create another request.
+        TlsContextPtr context2;
         PostHttpRequestJsonPtr request2 = createRequest("sequence", 2);
         HttpResponseJsonPtr response2(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context2,
+                                                request2, response2,
             [this](const boost::system::error_code& ec, const HttpResponsePtr&,
                    const std::string&) {
             io_service_.stop();
@@ -1216,9 +1277,11 @@ public:
         Url url("http://127.0.0.1:18123");
 
         // Create the request.
+        TlsContextPtr context;
         PostHttpRequestJsonPtr request = createRequest("sequence", 1);
         HttpResponseJsonPtr response(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request, response,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context,
+                                                request, response,
             [this](const boost::system::error_code& ec,
                    const HttpResponsePtr&,
                    const std::string&) {
@@ -1243,6 +1306,7 @@ public:
         // Specify the URL of the server.
         Url url("http://127.0.0.1:18123");
 
+        TlsContextPtr context;
         // The response is going to be malformed in such a way that it holds
         // an invalid content type. We affect the content type by creating
         // a request that holds a JSON parameter requesting a specific
@@ -1250,7 +1314,8 @@ public:
         PostHttpRequestJsonPtr request = createRequest("requested-content-type",
                                                        "text/html");
         HttpResponseJsonPtr response(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request, response,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context,
+                                                request, response,
             [this](const boost::system::error_code& ec,
                    const HttpResponsePtr& response,
                    const std::string& parsing_error) {
@@ -1281,6 +1346,7 @@ public:
 
         unsigned cb_num = 0;
 
+        TlsContextPtr context1;
         // Create the request which asks the server to generate a partial
         // (although well formed) response. The client will be waiting for the
         // rest of the response to be provided and will eventually time out.
@@ -1289,7 +1355,8 @@ public:
         // This value will be set to true if the connection close callback is
         // invoked upon time out.
         auto connection_closed = false;
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context1,
+                                                request1, response1,
             [this, &cb_num](const boost::system::error_code& ec,
                             const HttpResponsePtr& response,
                             const std::string&) {
@@ -1302,7 +1369,10 @@ public:
                 EXPECT_TRUE(ec.value() == boost::asio::error::timed_out);
                 // There should be no response returned.
                 EXPECT_FALSE(response);
-            }, HttpClient::RequestTimeout(100), HttpClient::ConnectHandler(),
+            },
+            HttpClient::RequestTimeout(100),
+            HttpClient::ConnectHandler(),
+            HttpClient::HandshakeHandler(),
             [&connection_closed](const int) {
                 // This callback is called when the connection gets closed
                 // by the client.
@@ -1311,16 +1381,18 @@ public:
         );
 
         // Create another request after the timeout. It should be handled ok.
+        TlsContextPtr context2;
         PostHttpRequestJsonPtr request2 = createRequest("sequence", 1);
         HttpResponseJsonPtr response2(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2,
-                        [this, &cb_num](const boost::system::error_code& /*ec*/,
-                                        const HttpResponsePtr&,
-                                        const std::string&) {
-            if (++cb_num > 1) {
-                io_service_.stop();
-            }
-        }));
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context2,
+                                                request2, response2,
+            [this, &cb_num](const boost::system::error_code& /*ec*/,
+                            const HttpResponsePtr&,
+                            const std::string&) {
+                if (++cb_num > 1) {
+                    io_service_.stop();
+                }
+            }));
 
         // Actually trigger the requests.
         ASSERT_NO_THROW(runIOService());
@@ -1341,9 +1413,11 @@ public:
 
         unsigned cb_num = 0;
 
+        TlsContextPtr context;
         PostHttpRequestJsonPtr request = createRequest("sequence", 1);
         HttpResponseJsonPtr response(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request, response,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context,
+                                                request, response,
             [this, &cb_num](const boost::system::error_code& ec,
                             const HttpResponsePtr& response,
                             const std::string&) {
@@ -1369,14 +1443,15 @@ public:
         }));
 
         // Create another request after the timeout. It should be handled ok.
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request, response,
-                        [this, &cb_num](const boost::system::error_code& /*ec*/,
-                                        const HttpResponsePtr&,
-                                        const std::string&) {
-            if (++cb_num > 1) {
-                io_service_.stop();
-            }
-        }));
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context,
+                                                request, response,
+            [this, &cb_num](const boost::system::error_code& /*ec*/,
+                            const HttpResponsePtr&,
+                            const std::string&) {
+                if (++cb_num > 1) {
+                    io_service_.stop();
+                }
+            }));
 
         // Actually trigger the requests.
         ASSERT_NO_THROW(runIOService());
@@ -1412,12 +1487,14 @@ public:
         Url url("http://127.0.0.1:18123");
 
         // Generate first request.
+        TlsContextPtr context1;
         PostHttpRequestJsonPtr request1 = createRequest("sequence", 1);
         HttpResponseJsonPtr response1(new HttpResponseJson());
 
         // Use very short timeout to make sure that it occurs before we actually
         // run the transaction.
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context1,
+                                                request1, response1,
             [](const boost::system::error_code& ec,
                const HttpResponsePtr& response,
                const std::string&) {
@@ -1431,9 +1508,11 @@ public:
         }, HttpClient::RequestTimeout(1)));
 
         if (queue_two_requests) {
+            TlsContextPtr context2;
             PostHttpRequestJsonPtr request2 = createRequest("sequence", 2);
             HttpResponseJsonPtr response2(new HttpResponseJson());
-            ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2,
+            ASSERT_NO_THROW(client.asyncSendRequest(url, context2,
+                                                    request2, response2,
                 [](const boost::system::error_code& ec,
                    const HttpResponsePtr& response,
                    const std::string&) {
@@ -1456,9 +1535,11 @@ public:
 
         // Now try to send another request to make sure that the client
         // is healthy.
+        TlsContextPtr context3;
         PostHttpRequestJsonPtr request3 = createRequest("sequence", 3);
         HttpResponseJsonPtr response3(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request3, response3,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context3,
+                                                request3, response3,
                          [this](const boost::system::error_code& ec,
                                 const HttpResponsePtr&,
                                 const std::string&) {
@@ -1487,12 +1568,14 @@ public:
         Url url("http://127.0.0.1:18123");
 
         // Initiate request to the server.
+        TlsContextPtr context1;
         PostHttpRequestJsonPtr request1 = createRequest("sequence", 1, version);
         HttpResponseJsonPtr response1(new HttpResponseJson());
         unsigned resp_num = 0;
         ExternalMonitor monitor;
 
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context1,
+                                                request1, response1,
             [this, &resp_num](const boost::system::error_code& ec,
                               const HttpResponsePtr&,
                               const std::string&) {
@@ -1504,13 +1587,16 @@ public:
         },
             HttpClient::RequestTimeout(10000),
             std::bind(&ExternalMonitor::connectHandler, &monitor, ph::_1, ph::_2),
+            std::bind(&ExternalMonitor::handshakeHandler, &monitor, ph::_1, ph::_2),
             std::bind(&ExternalMonitor::closeHandler, &monitor, ph::_1)
         ));
 
         // Initiate another request to the destination.
+        TlsContextPtr context2;
         PostHttpRequestJsonPtr request2 = createRequest("sequence", 2, version);
         HttpResponseJsonPtr response2(new HttpResponseJson());
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context2,
+                                                request2, response2,
             [this, &resp_num](const boost::system::error_code& ec,
                               const HttpResponsePtr&,
                               const std::string&) {
@@ -1521,6 +1607,7 @@ public:
         },
             HttpClient::RequestTimeout(10000),
             std::bind(&ExternalMonitor::connectHandler, &monitor, ph::_1, ph::_2),
+            std::bind(&ExternalMonitor::handshakeHandler, &monitor, ph::_1, ph::_2),
             std::bind(&ExternalMonitor::closeHandler, &monitor, ph::_1)
         ));
 
@@ -1578,12 +1665,14 @@ public:
         Url url("http://127.0.0.1:18123");
 
         // Initiate request to the server.
+        TlsContextPtr context1;
         PostHttpRequestJsonPtr request1 = createRequest("sequence", 1, version);
         HttpResponseJsonPtr response1(new HttpResponseJson());
         unsigned resp_num = 0;
         ExternalMonitor monitor;
 
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context1,
+                                                request1, response1,
             [this, &client, &resp_num, &monitor](const boost::system::error_code& ec,
                               const HttpResponsePtr&,
                               const std::string&) {
@@ -1608,6 +1697,7 @@ public:
         },
             HttpClient::RequestTimeout(10000),
             std::bind(&ExternalMonitor::connectHandler, &monitor, ph::_1, ph::_2),
+            std::bind(&ExternalMonitor::handshakeHandler, &monitor, ph::_1, ph::_2),
             std::bind(&ExternalMonitor::closeHandler, &monitor, ph::_1)
         ));
 
@@ -1638,10 +1728,12 @@ public:
 
         // Now let's do another request to the destination to verify that
         // we'll reopen the connection without issue.
+        TlsContextPtr context2;
         PostHttpRequestJsonPtr request2 = createRequest("sequence", 2, version);
         HttpResponseJsonPtr response2(new HttpResponseJson());
         resp_num = 0;
-        ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2,
+        ASSERT_NO_THROW(client.asyncSendRequest(url, context2,
+                                                request2, response2,
             [this, &client, &resp_num, &monitor](const boost::system::error_code& ec,
                               const HttpResponsePtr&,
                               const std::string&) {
@@ -1666,6 +1758,7 @@ public:
         },
             HttpClient::RequestTimeout(10000),
             std::bind(&ExternalMonitor::connectHandler, &monitor, ph::_1, ph::_2),
+            std::bind(&ExternalMonitor::handshakeHandler, &monitor, ph::_1, ph::_2),
             std::bind(&ExternalMonitor::closeHandler, &monitor, ph::_1)
         ));
 
@@ -1698,7 +1791,9 @@ public:
     public:
         /// @brief Constructor
         ExternalMonitor()
-            : registered_fd_(-1), connect_cnt_(0), close_cnt_(0) {};
+            : registered_fd_(-1), connect_cnt_(0), handshake_cnt_(0),
+              close_cnt_(0) {
+        }
 
         /// @brief Connect callback handler
         /// @param ec Error status of the ASIO connect
@@ -1719,6 +1814,15 @@ public:
             return (true);
         }
 
+        /// @brief Handshake callback handler
+        /// @param ec Error status of the ASIO connect
+        bool handshakeHandler(const boost::system::error_code& ec, int) {
+            ++handshake_cnt_;
+            // ec indicates an error, return true, so that error can be handled
+            // by Connection logic.
+            return (true);
+        }
+
         /// @brief Close callback handler
         ///
         /// @param tcp_native_fd socket descriptor to register
@@ -1736,6 +1840,9 @@ public:
         /// @brief Tracks how many times the connect callback is invoked.
         int connect_cnt_;
 
+        /// @brief Tracks how many times the handshake callback is invoked.
+        int handshake_cnt_;
+
         /// @brief Tracks how many times the close callback is invoked.
         int close_cnt_;
     };