From 4fa45dffd1ece21a468ed32850428b3b41bc8093 Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Fri, 1 Sep 2023 14:20:50 -0700 Subject: Unit test Connection Boost asio provides a test stream object that we can use to begin unit testing the connection object. This patchset uses it to re-enable some simple http1.1 tests. There's some features that have snuck into the connection class that aren't compatible with a stream (like ip address getting), so unfortunately we do need the connection class to be aware if it's in test mode, but that tradeoff seems worthwhile. Tested: Unit test pass. Change-Id: Id8b1f8866582b58502dbafe6139f841bf64b8ef3 Signed-off-by: Ed Tanous --- http/http_connection.hpp | 155 +++++++++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 67 deletions(-) (limited to 'http/http_connection.hpp') diff --git a/http/http_connection.hpp b/http/http_connection.hpp index 1ec80ae77a..c64d511925 100644 --- a/http/http_connection.hpp +++ b/http/http_connection.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,14 @@ constexpr uint64_t loggedOutPostBodyLimit = 4096; constexpr uint32_t httpHeaderLimit = 8192; +template +struct IsTls : std::false_type +{}; + +template +struct IsTls> : std::true_type +{}; + template class Connection : public std::enable_shared_from_this> @@ -112,31 +121,34 @@ class Connection : void prepareMutualTls() { - std::error_code error; - std::filesystem::path caPath(ensuressl::trustStorePath); - auto caAvailable = !std::filesystem::is_empty(caPath, error); - caAvailable = caAvailable && !error; - if (caAvailable && persistent_data::SessionStore::getInstance() - .getAuthMethodsConfig() - .tls) + if constexpr (IsTls::value) { - adaptor.set_verify_mode(boost::asio::ssl::verify_peer); - std::string id = "bmcweb"; - - const char* cStr = id.c_str(); - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - const auto* idC = reinterpret_cast(cStr); - int ret = SSL_set_session_id_context( - adaptor.native_handle(), idC, - static_cast(id.length())); - if (ret == 0) + std::error_code error; + std::filesystem::path caPath(ensuressl::trustStorePath); + auto caAvailable = !std::filesystem::is_empty(caPath, error); + caAvailable = caAvailable && !error; + if (caAvailable && persistent_data::SessionStore::getInstance() + .getAuthMethodsConfig() + .tls) { - BMCWEB_LOG_ERROR("{} failed to set SSL id", logPtr(this)); + adaptor.set_verify_mode(boost::asio::ssl::verify_peer); + std::string id = "bmcweb"; + + const char* cStr = id.c_str(); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + const auto* idC = reinterpret_cast(cStr); + int ret = SSL_set_session_id_context( + adaptor.native_handle(), idC, + static_cast(id.length())); + if (ret == 0) + { + BMCWEB_LOG_ERROR("{} failed to set SSL id", logPtr(this)); + } } - } - adaptor.set_verify_callback( - std::bind_front(&self_type::tlsVerifyCallback, this)); + adaptor.set_verify_callback( + std::bind_front(&self_type::tlsVerifyCallback, this)); + } } Adaptor& socket() @@ -157,9 +169,7 @@ class Connection : // TODO(ed) Abstract this to a more clever class with the idea of an // asynchronous "start" - if constexpr (std::is_same_v>) + if constexpr (IsTls::value) { adaptor.async_handshake(boost::asio::ssl::stream_base::server, [this, self(shared_from_this())]( @@ -252,20 +262,23 @@ class Connection : return; } keepAlive = thisReq.keepAlive(); -#ifndef BMCWEB_INSECURE_DISABLE_AUTHX - if (!crow::authentication::isOnAllowlist(req->url().path(), - req->method()) && - thisReq.session == nullptr) + if constexpr (!std::is_same_v) { - BMCWEB_LOG_WARNING("Authentication failed"); - forward_unauthorized::sendUnauthorized( - req->url().encoded_path(), - req->getHeaderValue("X-Requested-With"), - req->getHeaderValue("Accept"), res); - completeRequest(res); - return; - } +#ifndef BMCWEB_INSECURE_DISABLE_AUTHX + if (!crow::authentication::isOnAllowlist(req->url().path(), + req->method()) && + thisReq.session == nullptr) + { + BMCWEB_LOG_WARNING("Authentication failed"); + forward_unauthorized::sendUnauthorized( + req->url().encoded_path(), + req->getHeaderValue("X-Requested-With"), + req->getHeaderValue("Accept"), res); + completeRequest(res); + return; + } #endif // BMCWEB_INSECURE_DISABLE_AUTHX + } auto asyncResp = std::make_shared(); BMCWEB_LOG_DEBUG("Setting completion handler"); asyncResp->res.setCompleteRequestHandler( @@ -309,12 +322,14 @@ class Connection : bool isAlive() { - if constexpr (std::is_same_v>) + if constexpr (IsTls::value) { return adaptor.next_layer().is_open(); } + else if constexpr (std::is_same_v) + { + return true; + } else { return adaptor.is_open(); @@ -322,9 +337,7 @@ class Connection : } void close() { - if constexpr (std::is_same_v>) + if constexpr (IsTls::value) { adaptor.next_layer().close(); if (mtlsSession != nullptr) @@ -384,18 +397,22 @@ class Connection : { boost::system::error_code ec; BMCWEB_LOG_DEBUG("Fetch the client IP address"); - boost::asio::ip::tcp::endpoint endpoint = - boost::beast::get_lowest_layer(adaptor).remote_endpoint(ec); - if (ec) + if constexpr (!std::is_same_v) { - // If remote endpoint fails keep going. "ClientOriginIPAddress" - // will be empty. - BMCWEB_LOG_ERROR("Failed to get the client's IP Address. ec : {}", - ec); - return ec; + boost::asio::ip::tcp::endpoint endpoint = + boost::beast::get_lowest_layer(adaptor).remote_endpoint(ec); + + if (ec) + { + // If remote endpoint fails keep going. "ClientOriginIPAddress" + // will be empty. + BMCWEB_LOG_ERROR( + "Failed to get the client's IP Address. ec : {}", ec); + return ec; + } + ip = endpoint.address(); } - ip = endpoint.address(); return ec; } @@ -457,27 +474,31 @@ class Connection : { BMCWEB_LOG_DEBUG("Unable to get client IP"); } + if constexpr (!std::is_same_v) + { #ifndef BMCWEB_INSECURE_DISABLE_AUTHX - boost::beast::http::verb method = parser->get().method(); - userSession = crow::authentication::authenticate( - ip, res, method, parser->get().base(), mtlsSession); + boost::beast::http::verb method = parser->get().method(); + userSession = crow::authentication::authenticate( + ip, res, method, parser->get().base(), mtlsSession); - bool loggedIn = userSession != nullptr; - if (!loggedIn) - { - const boost::optional contentLength = - parser->content_length(); - if (contentLength && *contentLength > loggedOutPostBodyLimit) + bool loggedIn = userSession != nullptr; + if (!loggedIn) { - BMCWEB_LOG_DEBUG("Content length greater than limit {}", - *contentLength); - close(); - return; + const boost::optional contentLength = + parser->content_length(); + if (contentLength && + *contentLength > loggedOutPostBodyLimit) + { + BMCWEB_LOG_DEBUG("Content length greater than limit {}", + *contentLength); + close(); + return; + } + + BMCWEB_LOG_DEBUG("Starting quick deadline"); } - - BMCWEB_LOG_DEBUG("Starting quick deadline"); - } #endif // BMCWEB_INSECURE_DISABLE_AUTHX + } if (parser->is_done()) { -- cgit v1.2.3