diff --git a/src/io/ssl_server.cc b/src/io/ssl_server.cc index d68a3755d91..8d3769367b9 100644 --- a/src/io/ssl_server.cc +++ b/src/io/ssl_server.cc @@ -2,10 +2,15 @@ * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. */ +#include +#include + #include "ssl_server.h" #include "ssl_session.h" #include "io/event_manager.h" +#include "io/io_utils.h" +#include "io/io_log.h" SslServer::SslServer(EventManager *evm, boost::asio::ssl::context::method m, bool ssl_enabled, bool ssl_handshake_delayed) @@ -44,6 +49,83 @@ TcpSession *SslServer::AllocSession(bool server_session) { return session; } +void SslServer::AcceptHandlerComplete(TcpSessionPtr &session) { + SslSession *ssl= static_cast(session.get()); + if (ssl->IsSslDisabled() || ssl->IsSslHandShakeDelayed()) { + TcpServer::AcceptHandlerComplete(session); + } else { + // trigger ssl server handshake + std::srand(std::time(0)); + ssl->ssl_handshake_in_progress_ = true; + ssl->ssl_socket_->async_handshake + (boost::asio::ssl::stream_base::server, + boost::bind(&SslServer::AcceptHandShakeHandler, + TcpServerPtr(this), TcpSessionPtr(ssl), + boost::asio::placeholders::error)); + } +} + +void SslServer::AcceptHandShakeHandler(TcpServerPtr server, + TcpSessionPtr session, + const boost::system::error_code& error) { + SslServer *ssl_server = static_cast(server.get()); + SslSession *ssl_session = static_cast(session.get()); + ssl_session->ssl_handshake_in_progress_ = false; + if (!error) { + // on successful handshake continue with tcp server state machine. + ssl_session->SetSslHandShakeSuccess(); + ssl_server->TcpServer::AcceptHandlerComplete(session); + } else { + // close session on failure + ssl_session->SetSslHandShakeFailure(); + TCP_SESSION_LOG_ERROR(ssl_session, TCP_DIR_OUT, + "SSL Handshake failed due to error: " + << error.value() << " category: " + << error.category().name() + << " message: " << error.message()); + ssl_session->CloseInternal(error, false); + } +} + +void SslServer::ConnectHandlerComplete(TcpSessionPtr &session) { + SslSession *ssl= static_cast(session.get()); + if (ssl->IsSslDisabled() || ssl->IsSslHandShakeDelayed()) { + TcpServer::ConnectHandlerComplete(session); + } else { + // trigger ssl client handshake + std::srand(std::time(0)); + ssl->ssl_handshake_in_progress_ = true; + ssl->ssl_socket_->async_handshake + (boost::asio::ssl::stream_base::client, + boost::bind(&SslServer::ConnectHandShakeHandler, + TcpServerPtr(this), TcpSessionPtr(ssl), + boost::asio::placeholders::error)); + } +} + +void SslServer::ConnectHandShakeHandler(TcpServerPtr server, + TcpSessionPtr session, + const boost::system::error_code& error) { + SslServer *ssl_server = static_cast(server.get()); + SslSession *ssl_session = static_cast(session.get()); + ssl_session->ssl_handshake_in_progress_ = false; + if (!error) { + // on successful handshake continue with tcp server state machine. + ssl_session->SetSslHandShakeSuccess(); + ssl_server->TcpServer::ConnectHandlerComplete(session); + } else { + // report connect failure and close the session + ssl_session->SetSslHandShakeFailure(); + ssl_session->CloseInternal(error, false); + TCP_SESSION_LOG_ERROR(ssl_session, TCP_DIR_OUT, + "SSL Handshake failed due to error: " + << error.value() << " category: " + << error.category().name() + << " message: " << error.message()); + ssl_session->ConnectFailed(); + } +} + TcpServer::Socket *SslServer::accept_socket() const { // return tcp socket return &(so_ssl_accept_->next_layer()); diff --git a/src/io/ssl_server.h b/src/io/ssl_server.h index 8bd356b43b3..82e666ebe2f 100644 --- a/src/io/ssl_server.h +++ b/src/io/ssl_server.h @@ -30,12 +30,25 @@ class SslServer : public TcpServer { private: friend class SslSession; + static void AcceptHandShakeHandler(TcpServerPtr server, + TcpSessionPtr session, + const boost::system::error_code& error); + static void ConnectHandShakeHandler(TcpServerPtr server, + TcpSessionPtr session, + const boost::system::error_code& error); + // suppress AllocSession method using tcp socket, not valid for // ssl server. TcpSession *AllocSession(Socket *socket) { return NULL; } TcpSession *AllocSession(bool server_session); + // override accept complete handler to trigger handshake + virtual void AcceptHandlerComplete(TcpSessionPtr &session); + + // override connect complete handler to trigger handshake + void ConnectHandlerComplete(TcpSessionPtr &session); + Socket *accept_socket() const; void set_accept_socket(); diff --git a/src/io/ssl_session.cc b/src/io/ssl_session.cc index afa4bce0154..f3b63733bf1 100644 --- a/src/io/ssl_session.cc +++ b/src/io/ssl_session.cc @@ -70,85 +70,6 @@ TcpSession::Socket *SslSession::socket() const { return &ssl_socket_->next_layer(); } -bool SslSession::Connected(Endpoint remote) { - if (IsClosed()) { - return false; - } - - if (IsSslDisabled() || IsSslHandShakeDelayed()) { - return (TcpSession::Connected(remote)); - } else { - // trigger ssl client handshake - std::srand(std::time(0)); - ssl_handshake_in_progress_ = true; - ssl_socket_->async_handshake - (boost::asio::ssl::stream_base::client, - boost::bind(&SslSession::ConnectHandShakeHandler, TcpSessionPtr(this), - remote, boost::asio::placeholders::error)); - return true; - } -} - -void SslSession::Accepted() { - - if (IsSslDisabled() || IsSslHandShakeDelayed()) { - return (TcpSession::Accepted()); - } else { - // trigger ssl server handshake - std::srand(std::time(0)); - ssl_handshake_in_progress_ = true; - ssl_socket_->async_handshake - (boost::asio::ssl::stream_base::server, - boost::bind(&SslSession::AcceptHandShakeHandler, TcpSessionPtr(this), - boost::asio::placeholders::error)); - } -} - -void SslSession::AcceptHandShakeHandler(TcpSessionPtr session, - const boost::system::error_code& error) { - - SslSession *ssl_session = static_cast(session.get()); - ssl_session->ssl_handshake_in_progress_ = false; - if (!error) { - // on successful handshake continue with tcp session state machine. - ssl_session->SetSslHandShakeSuccess(); - ssl_session->TcpSession::Accepted(); - } else { - // close session on failure - ssl_session->SetSslHandShakeFailure(); - TCP_SESSION_LOG_ERROR(ssl_session, TCP_DIR_OUT, - "SSL Handshake failed due to error: " - << error.value() << " category: " - << error.category().name() - << " message: " << error.message()); - ssl_session->CloseInternal(error, false); - } -} - -void SslSession::ConnectHandShakeHandler(TcpSessionPtr session, Endpoint remote, - const boost::system::error_code& error) { - - SslSession *ssl_session = static_cast(session.get()); - ssl_session->ssl_handshake_in_progress_ = false; - bool ret = false; - if (!error) { - // on successful handshake continue with tcp session state machine. - ssl_session->SetSslHandShakeSuccess(); - ret = ssl_session->TcpSession::Connected(remote); - } - if (ret == false) { - // report connect failure and close the session - ssl_session->SetSslHandShakeFailure(); - TCP_SESSION_LOG_ERROR(ssl_session, TCP_DIR_OUT, - "SSL Handshake failed due to error: " - << error.value() << " category: " - << error.category().name() - << " message: " << error.message()); - ssl_session->ConnectFailed(); - ssl_session->CloseInternal(error, false); - } -} - bool SslSession::AsyncReadHandlerProcess(boost::asio::mutable_buffer buffer, size_t &bytes_transferred, boost::system::error_code &error) { diff --git a/src/io/ssl_session.h b/src/io/ssl_session.h index ab70e453824..2fcece1e849 100644 --- a/src/io/ssl_session.h +++ b/src/io/ssl_session.h @@ -23,12 +23,6 @@ class SslSession : public TcpSession { virtual Socket *socket() const; - // Override to trigger handshake - virtual bool Connected(Endpoint remote); - - // Override to trigger handshake - virtual void Accepted(); - // Trigger delayed SslHandShake void TriggerSslHandShake(SslHandShakeCallbackHandler); @@ -65,11 +59,7 @@ class SslSession : public TcpSession { private: class SslReader; - friend class Sslserver; - static void AcceptHandShakeHandler(TcpSessionPtr session, - const boost::system::error_code& error); - static void ConnectHandShakeHandler(TcpSessionPtr session, Endpoint remote, - const boost::system::error_code& error); + friend class SslServer; // SslSession do actual ssl socket read for data in this context with // session mutex held, to avoid concurrent read and write operations diff --git a/src/io/tcp_server.cc b/src/io/tcp_server.cc index e85bb20b2f1..62c41e82969 100644 --- a/src/io/tcp_server.cc +++ b/src/io/tcp_server.cc @@ -343,6 +343,17 @@ void TcpServer::AcceptHandlerInternal(TcpServerPtr server, } session->SessionEstablished(remote, TcpSession::PASSIVE); + AcceptHandlerComplete(session); + +done: + if (need_close) { + session->CloseInternal(ec, false, false); + } + AsyncAccept(); +} + +void TcpServer::AcceptHandlerComplete(TcpSessionPtr &session) { + tcp::endpoint remote = session->remote_endpoint(); { tbb::mutex::scoped_lock lock(mutex_); if (AcceptSession(session.get())) { @@ -357,18 +368,13 @@ void TcpServer::AcceptHandlerInternal(TcpServerPtr server, "Rejected session from " << remote.address().to_string() << ":" << remote.port()); - need_close = true; - goto done; + boost::system::error_code ec; + session->CloseInternal(ec, false, false); + return; } } session->Accepted(); - -done: - if (need_close) { - session->CloseInternal(ec, false, false); - } - AsyncAccept(); } TcpSession *TcpServer::GetSession(Endpoint remote) { @@ -389,10 +395,14 @@ void TcpServer::ConnectHandler(TcpServerPtr server, TcpSessionPtr session, return; } + ConnectHandlerComplete(session); +} + +void TcpServer::ConnectHandlerComplete(TcpSessionPtr &session) { boost::system::error_code ec; Endpoint remote = session->socket()->remote_endpoint(ec); if (ec) { - TCP_SERVER_LOG_INFO(server, TCP_DIR_OUT, + TCP_SERVER_LOG_INFO(this, TCP_DIR_OUT, "Connect getsockaddr: " << ec.message()); session->ConnectFailed(); return; @@ -408,7 +418,6 @@ void TcpServer::ConnectHandler(TcpServerPtr server, TcpSessionPtr session, if (!session->Connected(remote)) { tbb::mutex::scoped_lock lock(mutex_); RemoveSessionFromMap(remote, session.get()); - return; } } diff --git a/src/io/tcp_server.h b/src/io/tcp_server.h index 74e88ae18e2..cfdc6bd2d8f 100644 --- a/src/io/tcp_server.h +++ b/src/io/tcp_server.h @@ -85,6 +85,7 @@ class TcpServer { const std::string &md5_password); protected: + typedef boost::intrusive_ptr TcpServerPtr; typedef boost::intrusive_ptr TcpSessionPtr; // Create a session object. @@ -112,6 +113,10 @@ class TcpServer { Endpoint LocalEndpoint() const; + virtual void AcceptHandlerComplete(TcpSessionPtr &session); + virtual void ConnectHandlerComplete(TcpSessionPtr &session); + + private: friend class TcpSession; friend class TcpMessageWriter; @@ -119,7 +124,6 @@ class TcpServer { friend void intrusive_ptr_add_ref(TcpServer *server); friend void intrusive_ptr_release(TcpServer *server); - typedef boost::intrusive_ptr TcpServerPtr; struct TcpSessionPtrCmp { bool operator()(const TcpSessionPtr &lhs, const TcpSessionPtr &rhs) const { diff --git a/src/io/test/ssl_client_cert.pem b/src/io/test/ssl_client_cert.pem new file mode 100644 index 00000000000..3bb69bd48b5 --- /dev/null +++ b/src/io/test/ssl_client_cert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDwjCCAqoCCQCorP94MWXbMjANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlQYWxvIEFsdG8xFTATBgNVBAoTDE9w +ZW4gdlN3aXRjaDEfMB0GA1UECxMWT3BlbiB2U3dpdGNoIGNlcnRpZmllcjE6MDgG +A1UEAxMxdG9yLWFnZW50IGlkOmNhMzE4ZjI2LTAxMmItNDE3MC1iYmJkLTA5YmE2 +NjFmZDU1NzAeFw0xNTAyMDkwODE2MTBaFw0yNTAyMDYwODE2MTBaMIGiMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEVMBMGA1UE +ChMMT3BlbiB2U3dpdGNoMR8wHQYDVQQLExZPcGVuIHZTd2l0Y2ggY2VydGlmaWVy +MTowOAYDVQQDEzF0b3ItYWdlbnQgaWQ6Y2EzMThmMjYtMDEyYi00MTcwLWJiYmQt +MDliYTY2MWZkNTU3MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6sn0 +a5F0mIb3pbUoIRIRGx7rBebYqH5IxiHBXBSjr8cRyZFQk+iQhJ3bCLYKhsrXrrMG +0MZjlmGwhqm94LbkU4ZvMoT1yTfHyfgcFbLnBUY4hrfdYb8lfnB9naghk6jxY7Gv +BPrd2Rr/Xb50uY09/kW/BycKQ9WxAObpJn98lJstavUkd7RBVxQTYQJt/kfukfQM +MgbHEYzJrIjPmTInrZ1P4+KH8C4/GcT4+BolIsy089GI5Pw0Io/dB0NF/e1N/daq +egAuvSlgLfZwaPuCp/H/eMaslyvolgr2N7TishZi0hNgfu783Oxu9W2kCp6cJN1E +IRTyf1yUUd25ezpK6wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQDQ+UvwBMv5vGFu +9qTmhQiEfGz2GSANs+0RgDuqh71Tr1CBNAHAXXbZApOrHXG5GYcdkWpAMc6yDWrB +CArCKLQ8pMZse/cGh2wqDmgTLvNxP/cWc3y6AldNgVXCSeaBllL8Q8oq1IEYGbSS +sElyRKMnZthSi6ulkLBs12jzKr+dbuuk+N0URriW+x8L2AmG8F0jMSjcytVrlbCg +i6CdRC3Gj/F+ZfDfSjxlOO/vMCly2ul14zg6PuuZsyqHBTgvbY8NZml74EOgjvQr +wUOJ0zmgaMLL5KeNl1ZZeKE3JXSU3MSo0/Zc6MZRjdm3TMtbnzqr9R9Z8nm3DzKu +IMjDCqFr +-----END CERTIFICATE----- diff --git a/src/io/test/ssl_client_privkey.pem b/src/io/test/ssl_client_privkey.pem new file mode 100644 index 00000000000..174a2b838a0 --- /dev/null +++ b/src/io/test/ssl_client_privkey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA6sn0a5F0mIb3pbUoIRIRGx7rBebYqH5IxiHBXBSjr8cRyZFQ +k+iQhJ3bCLYKhsrXrrMG0MZjlmGwhqm94LbkU4ZvMoT1yTfHyfgcFbLnBUY4hrfd +Yb8lfnB9naghk6jxY7GvBPrd2Rr/Xb50uY09/kW/BycKQ9WxAObpJn98lJstavUk +d7RBVxQTYQJt/kfukfQMMgbHEYzJrIjPmTInrZ1P4+KH8C4/GcT4+BolIsy089GI +5Pw0Io/dB0NF/e1N/daqegAuvSlgLfZwaPuCp/H/eMaslyvolgr2N7TishZi0hNg +fu783Oxu9W2kCp6cJN1EIRTyf1yUUd25ezpK6wIDAQABAoIBAQCxR0PyZKWw9VfQ +GvTt44JfA/1ZO3Cj6JZyY9JoAH2Hn7vigoJg690TBU35Sdqw5D/ufObMhL91MNXl +GA1yuyWzm3IipxoekKud6GTsTWT6KodL0VCrGXTp/24ZuHWB1LJPID6SeAlCgIwI +8GGaKPeCIo+WivfJOHSpxbCNjP04BnDAhRvtAuNnKUW8mK1DjkjyvwM0kGXierkG +CREdCzu7mkNAWiU3JUW+dodyWFgc1E8uObACSGLlodCZJ/IBrRvO5/4mPV/76QKC +wqrCLmhdUed+2FQDtmPbiBunHrFKS0MrnO3OakxTm8C53Bd5+MEdb/I9QwavjGnO +AkZRy3pBAoGBAP889ZciTX8DPg7xwBeZvIcCbpEJAqVCZI2CxUXbDRKx20eiXqMA +TUAwFWyXvNtxkBIeJ45HS5pznBk0Usr8qykTvi0nwS84RLTHE3xTmfCRLxUCF7L9 +LVRN4y7en7fIDfyfsrmpHB9wac0XFZ4Swn7EPWROt9EeivLzBDY5AbsZAoGBAOt9 +Xn3n6v0gwsSF8o3e+E/nDiwyH5Rqetik1pnqCLpPbq89SVgXCfbiliB0esi+pOPh +zn3Vf5I5KSnZOjdJhlz5JBKNOaXOTxAzj3fAkqnOHYIPjDc6C/MRsKu54AD2l0fY +XCyvQBeQGlB48PEZMinNjMacWNjaypZpPMcssLqjAoGBALmFga19DX9IyA7swYdm +A5bOubdKKVYd0CDb8LA57GKuTIjhCJDKY1xIlwn7sRaAkQvaRz4vrFBzv/7B/Xv1 +9CNDanQ/9TdxWt9b5Fn2Gmq13NcUUk2ToSMqCfvDbayUCTaajbpNpVdkykJ8iQYA +9MZXtZf3b7zcynqVEXaoMQ3hAoGAerHNY8hMFSrWj9JCeEyuD+OQ7NIIxilcbDgm +X/ciKQBt6PwDlNQcKvgPxEUsHB/IhbsG/WUZnLQPkHBP9rJmQVbFxqyyVoNRil1y +6K/7OmHb2gIDylqCE1kqNa8Y2Seh1psSG24L9LruGvOIhfXcvw8LxAd9+y6z0v5t +3lCYnAUCgYEAi9uR3Zq2lUaS42E69mqiUYd/E1U9tqUOxPmxnB+qNPRh1t+eNay8 +g1mmKQT4RPrb1aWghcNvLAxmKp1XEtXggttFQM/lANNp3RGW+f9MxriDvduk4m5t +dgPiji/iMlOwEKx4m3MU0Fns0+RKjB0du0m5ALJGcXlU9fTuaGlG6Ts= +-----END RSA PRIVATE KEY----- diff --git a/src/io/test/ssl_server_test.cc b/src/io/test/ssl_server_test.cc index 8bae6a5dab2..70ac683d1e9 100644 --- a/src/io/test/ssl_server_test.cc +++ b/src/io/test/ssl_server_test.cc @@ -68,17 +68,28 @@ class EchoServer : public SslServer { ctx->use_certificate_chain_file ("controller/src/ifmap/client/test/newcert.pem", ec); assert(ec.value() == 0); - ctx->use_private_key_file("controller/src/ifmap/client/test/server.pem", + ctx->use_private_key_file("controller/src/ifmap/client/test/privkey.pem", boost::asio::ssl::context::pem, ec); assert(ec.value() == 0); - ctx->add_verify_path("controller/src/ifmap/client/test/", ec); - assert(ec.value() == 0); - ctx->load_verify_file("controller/src/ifmap/client/test/newcert.pem", + ctx->load_verify_file("controller/src/io/test/ssl_client_cert.pem", ec); assert(ec.value() == 0); } + ~EchoServer() { } + + void set_verify_fail_certs() { + boost::asio::ssl::context *ctx = context(); + boost::system::error_code ec; + ctx->set_verify_mode((boost::asio::ssl::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert), ec); + assert(ec.value() == 0); + ctx->load_verify_file("controller/src/ifmap/client/test/newcert.pem", + ec); + assert(ec.value() == 0); + } + virtual SslSession *AllocSession(SslSocket *socket) { session_ = new EchoSession(this, socket, ssl_handshake_delayed_); return session_; @@ -168,13 +179,11 @@ class SslClient : public SslServer { ctx->set_verify_mode(boost::asio::ssl::context::verify_none, ec); assert(ec.value() == 0); ctx->use_certificate_chain_file - ("controller/src/ifmap/client/test/newcert.pem", ec); + ("controller/src/io/test/ssl_client_cert.pem", ec); assert(ec.value() == 0); - ctx->use_private_key_file("controller/src/ifmap/client/test/server.pem", + ctx->use_private_key_file("controller/src/io/test/ssl_client_privkey.pem", boost::asio::ssl::context::pem, ec); assert(ec.value() == 0); - ctx->add_verify_path("controller/src/ifmap/client/test/", ec); - assert(ec.value() == 0); ctx->load_verify_file("controller/src/ifmap/client/test/newcert.pem", ec); assert(ec.value() == 0); @@ -186,6 +195,20 @@ class SslClient : public SslServer { return session_; } + void set_verify_fail_certs() { + boost::asio::ssl::context *ctx = context(); + boost::system::error_code ec; + ctx->set_verify_mode((boost::asio::ssl::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert), ec); + assert(ec.value() == 0); + ctx->use_certificate_chain_file + ("controller/src/ifmap/client/test/newcert.pem", ec); + assert(ec.value() == 0); + ctx->load_verify_file("controller/src/io/test/ssl_client_cert.pem", + ec); + assert(ec.value() == 0); + } + TcpSession *CreateSession() { TcpSession *session = SslServer::CreateSession(); Socket *socket = session->socket(); @@ -225,7 +248,7 @@ class SslClient : public SslServer { ClientSession::ClientSession(SslClient *server, SslSocket *socket, bool ssl_handshake_delayed) - : SslSession(server, socket) { + : SslSession(server, socket), len_(0) { if (!ssl_handshake_delayed) { set_observer(boost::bind(&ClientSession::OnEvent, this, _1, _2)); } @@ -266,9 +289,6 @@ class SslEchoServerTest : public ::testing::Test { virtual void TearDown() { - if (server_->GetSession()) { - server_->GetSession()->Close(); - } if (session_) session_->Close(); task_util::WaitForIdle(); server_->Shutdown(); @@ -349,7 +369,47 @@ TEST_F(SslEchoServerTest, msg_send_recv) { client = NULL; } -TEST_F(SslEchoServerTest, test_delayed_ssl_handshake) { +TEST_F(SslEchoServerTest, HandshakeFailure) { + + SetUpImmedidate(); + SslClient *client = new SslClient(evm_.get()); + + // set context to verify certs to fail handshake + client->set_verify_fail_certs(); + server_->set_verify_fail_certs(); + + task_util::WaitForIdle(); + server_->Initialize(0); + task_util::WaitForIdle(); + thread_->Start(); // Must be called after initialization + + connect_success_ = connect_fail_ = connect_abort_ = 0; + ClientSession *session = static_cast(client->CreateSession()); + session->set_observer(boost::bind(&SslEchoServerTest::OnEvent, this, _1, _2)); + boost::asio::ip::tcp::endpoint endpoint; + boost::system::error_code ec; + endpoint.address(boost::asio::ip::address::from_string("127.0.0.1", ec)); + endpoint.port(server_->GetPort()); + client->Connect(session, endpoint); + task_util::WaitForIdle(); + TASK_UTIL_EXPECT_FALSE(session->IsClosed()); + TASK_UTIL_EXPECT_EQ(0, connect_success_); + TASK_UTIL_EXPECT_EQ(connect_fail_, 1); + TASK_UTIL_EXPECT_EQ(connect_abort_, 0); + + session->Close(); + client->DeleteSession(session); + + TASK_UTIL_EXPECT_EQ(0, server_->GetSessionCount()); + TASK_UTIL_EXPECT_FALSE(server_->HasSessions()); + + client->Shutdown(); + task_util::WaitForIdle(); + TcpServerManager::DeleteServer(client); + client = NULL; +} + +TEST_F(SslEchoServerTest, DISABLED_test_delayed_ssl_handshake) { SetUpDelayedHandShake();