summaryrefslogtreecommitdiff
path: root/http/http_connection.hpp
diff options
context:
space:
mode:
authorEd Tanous <edtanous@google.com>2023-09-02 00:20:50 +0300
committerEd Tanous <ed@tanous.net>2023-12-05 21:54:28 +0300
commit4fa45dffd1ece21a468ed32850428b3b41bc8093 (patch)
treeda14af66150c4b652c8aeb5c42d1e5789ac04cdd /http/http_connection.hpp
parent7164bc62dd26ec92b01985aaae97ecc48276dea5 (diff)
downloadbmcweb-4fa45dffd1ece21a468ed32850428b3b41bc8093.tar.xz
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 <edtanous@google.com>
Diffstat (limited to 'http/http_connection.hpp')
-rw-r--r--http/http_connection.hpp155
1 files changed, 88 insertions, 67 deletions
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 <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/steady_timer.hpp>
+#include <boost/beast/_experimental/test/stream.hpp>
#include <boost/beast/core/buffers_generator.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/http/error.hpp>
@@ -44,6 +45,14 @@ constexpr uint64_t loggedOutPostBodyLimit = 4096;
constexpr uint32_t httpHeaderLimit = 8192;
+template <typename>
+struct IsTls : std::false_type
+{};
+
+template <typename T>
+struct IsTls<boost::beast::ssl_stream<T>> : std::true_type
+{};
+
template <typename Adaptor, typename Handler>
class Connection :
public std::enable_shared_from_this<Connection<Adaptor, Handler>>
@@ -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<Adaptor>::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<const unsigned char*>(cStr);
- int ret = SSL_set_session_id_context(
- adaptor.native_handle(), idC,
- static_cast<unsigned int>(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<const unsigned char*>(cStr);
+ int ret = SSL_set_session_id_context(
+ adaptor.native_handle(), idC,
+ static_cast<unsigned int>(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<Adaptor,
- boost::beast::ssl_stream<
- boost::asio::ip::tcp::socket>>)
+ if constexpr (IsTls<Adaptor>::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<Adaptor, boost::beast::test::stream>)
{
- 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::AsyncResp>();
BMCWEB_LOG_DEBUG("Setting completion handler");
asyncResp->res.setCompleteRequestHandler(
@@ -309,12 +322,14 @@ class Connection :
bool isAlive()
{
- if constexpr (std::is_same_v<Adaptor,
- boost::beast::ssl_stream<
- boost::asio::ip::tcp::socket>>)
+ if constexpr (IsTls<Adaptor>::value)
{
return adaptor.next_layer().is_open();
}
+ else if constexpr (std::is_same_v<Adaptor, boost::beast::test::stream>)
+ {
+ return true;
+ }
else
{
return adaptor.is_open();
@@ -322,9 +337,7 @@ class Connection :
}
void close()
{
- if constexpr (std::is_same_v<Adaptor,
- boost::beast::ssl_stream<
- boost::asio::ip::tcp::socket>>)
+ if constexpr (IsTls<Adaptor>::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<Adaptor, boost::beast::test::stream>)
{
- // 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<Adaptor, boost::beast::test::stream>)
+ {
#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<uint64_t> 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<uint64_t> 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())
{