Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 3 additions & 13 deletions lib/api/LogManagerImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,7 @@ namespace MAT_NS_BEGIN
if (m_httpClient == nullptr)
{
m_httpClient = HttpClientFactory::Create();
#ifdef HAVE_MAT_WININET_HTTP_CLIENT
HttpClient_WinInet* client = static_cast<HttpClient_WinInet*>(m_httpClient.get());
if (client != nullptr)
{
client->SetMsRootCheck(m_logConfiguration[CFG_MAP_HTTP][CFG_BOOL_HTTP_MS_ROOT_CHECK]);
}
#endif
m_httpClient->ApplySettings(m_logConfiguration);
}
else
{
Expand Down Expand Up @@ -366,14 +360,10 @@ namespace MAT_NS_BEGIN
/// </summary>
void LogManagerImpl::Configure()
{
// TODO: [maxgolov] - add other config params.
#ifdef HAVE_MAT_WININET_HTTP_CLIENT
HttpClient_WinInet* client = static_cast<HttpClient_WinInet*>(m_httpClient.get());
if (client != nullptr)
if (m_httpClient != nullptr)
{
client->SetMsRootCheck(m_logConfiguration[CFG_MAP_HTTP][CFG_BOOL_HTTP_MS_ROOT_CHECK]);
m_httpClient->ApplySettings(m_logConfiguration);
}
#endif
}

LogManagerImpl::~LogManagerImpl() noexcept
Expand Down
6 changes: 5 additions & 1 deletion lib/config/RuntimeConfig_Default.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ namespace MAT_NS_BEGIN
,
{"contentEncoding", "deflate"},
/* Optional parameter to require Microsoft Root CA */
{CFG_BOOL_HTTP_MS_ROOT_CHECK, false}}},
{CFG_BOOL_HTTP_MS_ROOT_CHECK, false},
/* Optional parameter for SSL certificate verification (curl) */
{CFG_BOOL_HTTP_SSL_VERIFY, true},
/* Optional CA bundle path for OpenSSL-backed curl */
{CFG_STR_HTTP_SSL_CAINFO, ""}}},
{CFG_MAP_TPM,
{
{CFG_INT_TPM_MAX_BLOB_BYTES, 2097152},
Expand Down
23 changes: 22 additions & 1 deletion lib/http/HttpClient_Curl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "utils/Utils.hpp"
#include "HttpClient_Curl.hpp"
#include "ILogConfiguration.hpp"

namespace MAT_NS_BEGIN {

Expand Down Expand Up @@ -74,7 +75,13 @@ namespace MAT_NS_BEGIN {
requestHeaders[header.first] = header.second;
}

auto curlOperation = std::make_shared<CurlHttpOperation>(curlRequest->m_method, curlRequest->m_url, callback, requestHeaders, curlRequest->m_body);
std::string sslCaInfo;
{
std::lock_guard<std::mutex> lock(m_requestsMtx);
sslCaInfo = m_sslCaInfo;
}

auto curlOperation = std::make_shared<CurlHttpOperation>(curlRequest->m_method, curlRequest->m_url, callback, requestHeaders, curlRequest->m_body, false, HTTP_CONN_TIMEOUT, m_sslVerify, sslCaInfo);
curlRequest->SetOperation(curlOperation);

// The lifetime of curlOperation is guarnteed by the call to result.wait() in the d'tor.
Expand Down Expand Up @@ -125,6 +132,20 @@ namespace MAT_NS_BEGIN {
}
}

void HttpClient_Curl::ApplySettings(ILogConfiguration& config)
{
SetSslVerification(
config[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY],
(const char *)config[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO]);
}

void HttpClient_Curl::SetSslVerification(bool sslVerify, const std::string& caInfo)
{
m_sslVerify = sslVerify;
std::lock_guard<std::mutex> lock(m_requestsMtx);
m_sslCaInfo = caInfo;
}

void HttpClient_Curl::EraseRequest(std::string const& id)
{
std::lock_guard<std::mutex> lock(m_requestsMtx);
Expand Down
18 changes: 14 additions & 4 deletions lib/http/HttpClient_Curl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,17 @@ class HttpClient_Curl : public IHttpClient {
virtual void SendRequestAsync(IHttpRequest* request, IHttpResponseCallback* callback) override;
virtual void CancelRequestAsync(std::string const& id) override;

virtual void ApplySettings(ILogConfiguration& config) override;
void SetSslVerification(bool sslVerify, const std::string& caInfo = "");

private:
void EraseRequest(std::string const& id);
void AddRequest(IHttpRequest* request);

std::mutex m_requestsMtx;
std::map<std::string, IHttpRequest*> m_requests;
std::atomic<bool> m_sslVerify { true };
std::string m_sslCaInfo;
};

class CurlHttpOperation {
Expand Down Expand Up @@ -91,7 +96,10 @@ class CurlHttpOperation {
const std::vector<uint8_t>& requestBody = std::vector<uint8_t>(),
// Default connectivity and response size options
bool rawResponse = false,
size_t httpConnTimeout = HTTP_CONN_TIMEOUT) :
size_t httpConnTimeout = HTTP_CONN_TIMEOUT,
// SSL certificate verification options
bool sslVerify = true,
const std::string& sslCaInfo = "") :

// Optional connection params
rawResponse(rawResponse),
Expand Down Expand Up @@ -129,9 +137,11 @@ class CurlHttpOperation {
// Specify target URL
curl_easy_setopt(curl, CURLOPT_URL, m_url.c_str());

// TODO: expose SSL cert verification opts via ILogConfiguration
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); // 1L
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); // 2L
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, sslVerify ? 1L : 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, sslVerify ? 2L : 0L);
if (!sslCaInfo.empty()) {
curl_easy_setopt(curl, CURLOPT_CAINFO, sslCaInfo.c_str());
}
// HTTP/2 please, fallback to HTTP/1.1 if not supported
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);

Expand Down
5 changes: 5 additions & 0 deletions lib/http/HttpClient_WinInet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,11 @@ void HttpClient_WinInet::CancelAllRequests()
/// Enforces MS-root server certificate check.
/// </summary>
/// <param name="enforceMsRoot">if set to <c>true</c> [enforce verification that server cert is MS-Rooted].</param>
void HttpClient_WinInet::ApplySettings(ILogConfiguration& config)
{
SetMsRootCheck(config[CFG_MAP_HTTP][CFG_BOOL_HTTP_MS_ROOT_CHECK]);
}

void HttpClient_WinInet::SetMsRootCheck(bool enforceMsRoot)
{
m_msRootCheck = enforceMsRoot;
Expand Down
2 changes: 2 additions & 0 deletions lib/http/HttpClient_WinInet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class HttpClient_WinInet : public IHttpClient {
virtual void CancelRequestAsync(std::string const& id) final;
virtual void CancelAllRequests() final;

virtual void ApplySettings(ILogConfiguration& config) override;

// Methods unique to WinInet implementation.
void SetMsRootCheck(bool enforceMsRoot);
bool IsMsRootCheckRequired();
Expand Down
9 changes: 9 additions & 0 deletions lib/include/public/IHttpClient.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
///@cond INTERNAL_DOCS
namespace MAT_NS_BEGIN
{
class ILogConfiguration;
/// <summary>
/// The HttpHeaders class contains a set of HTTP headers.
/// </summary>
Expand Down Expand Up @@ -543,6 +544,14 @@ namespace MAT_NS_BEGIN
virtual void CancelRequestAsync(std::string const& id) = 0;

virtual void CancelAllRequests() {}

/// <summary>
/// Apply HTTP settings from the log configuration.
/// Subclasses override to handle platform-specific options.
/// Default implementation is a no-op.
/// </summary>
/// <param name="config">The log configuration to read settings from.</param>
virtual void ApplySettings(ILogConfiguration& /*config*/) {}
};

/// @endcond
Expand Down
10 changes: 10 additions & 0 deletions lib/include/public/ILogConfiguration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,16 @@ namespace MAT_NS_BEGIN
/// </summary>
static constexpr const char* const CFG_BOOL_HTTP_COMPRESSION = "compress";

/// <summary>
/// HTTP configuration: SSL certificate verification (peer + host)
/// </summary>
static constexpr const char* const CFG_BOOL_HTTP_SSL_VERIFY = "sslVerify";

/// <summary>
/// HTTP configuration: SSL CA bundle file path (for libcurl/OpenSSL)
/// </summary>
static constexpr const char* const CFG_STR_HTTP_SSL_CAINFO = "sslCaInfo";

/// <summary>
/// TPM configuration map
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions tests/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ set(SRCS
EventPropertiesTests.cpp
GuidTests.cpp
HttpClientCAPITests.cpp
HttpClientCurlTests.cpp
HttpClientManagerTests.cpp
HttpClientTests.cpp
HttpDeflateCompressionTests.cpp
Expand Down
126 changes: 126 additions & 0 deletions tests/unittests/HttpClientCurlTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//
#include "mat/config.h"

// These tests only apply to the curl HTTP client path (Linux, non-Apple, non-Android)
#if defined(MATSDK_PAL_CPP11) && !defined(_MSC_VER) && defined(HAVE_MAT_DEFAULT_HTTP_CLIENT) \
&& !defined(__APPLE__) && !defined(ANDROID)

#include "common/Common.hpp"
#include "http/HttpClient_Curl.hpp"
#include "config/RuntimeConfig_Default.hpp"

using namespace testing;
using namespace MAT;

class HttpClientCurlTests : public ::testing::Test
{
protected:
HttpClient_Curl m_client;
};

// --- SetSslVerification wiring ---

TEST_F(HttpClientCurlTests, SslVerification_DefaultsToTrue)
{
CurlHttpOperation op("GET", "https://example.com", nullptr);
ASSERT_NE(op.GetHandle(), nullptr);
}

TEST_F(HttpClientCurlTests, CurlHttpOperation_ConstructsWithVerifyTrue)
{
CurlHttpOperation op("GET", "https://example.com", nullptr,
std::map<std::string, std::string>(), std::vector<uint8_t>(),
false, 5, true, "");
ASSERT_NE(op.GetHandle(), nullptr);
}

TEST_F(HttpClientCurlTests, CurlHttpOperation_ConstructsWithVerifyFalse)
{
CurlHttpOperation op("GET", "https://example.com", nullptr,
std::map<std::string, std::string>(), std::vector<uint8_t>(),
false, 5, false, "");
ASSERT_NE(op.GetHandle(), nullptr);
}

TEST_F(HttpClientCurlTests, CurlHttpOperation_ConstructsWithCaInfo)
{
CurlHttpOperation op("GET", "https://example.com", nullptr,
std::map<std::string, std::string>(), std::vector<uint8_t>(),
false, 5, true, "/etc/ssl/certs/ca-certificates.crt");
ASSERT_NE(op.GetHandle(), nullptr);
}

// --- ILogConfiguration integration ---

TEST(HttpClientCurlConfigTests, LogConfiguration_SslVerify_DefaultIsTrue)
{
// defaultRuntimeConfig from RuntimeConfig_Default.hpp has the defaults
bool sslVerify = defaultRuntimeConfig[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY];
EXPECT_TRUE(sslVerify);
}

TEST(HttpClientCurlConfigTests, LogConfiguration_SslCaInfo_DefaultIsEmpty)
{
const char* caInfo = defaultRuntimeConfig[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO];
EXPECT_STREQ(caInfo, "");
}

TEST(HttpClientCurlConfigTests, LogConfiguration_SslVerify_CanBeDisabled)
{
ILogConfiguration config;
config[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY] = false;
bool sslVerify = config[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY];
EXPECT_FALSE(sslVerify);
}

TEST(HttpClientCurlConfigTests, LogConfiguration_SslCaInfo_CanBeSet)
{
ILogConfiguration config;
config[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO] = "/custom/ca-bundle.crt";
const char* caInfo = config[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO];
EXPECT_STREQ(caInfo, "/custom/ca-bundle.crt");
}

// --- ApplySettings integration ---

TEST_F(HttpClientCurlTests, ApplySettings_ReadsSslConfigFromLogConfiguration)
{
ILogConfiguration config;
config[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY] = false;
config[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO] = "/custom/ca.pem";
m_client.ApplySettings(config);
// Verify indirectly -- constructing an operation should not fail
SUCCEED();
}

TEST_F(HttpClientCurlTests, ApplySettings_DefaultConfigEnablesVerification)
{
ILogConfiguration config;
m_client.ApplySettings(config);
SUCCEED();
}

// --- Thread safety: SetSslVerification concurrent with reads ---

TEST_F(HttpClientCurlTests, SetSslVerification_ConcurrentCallsNoRace)
{
// Exercise the atomic + mutex path under contention.
// No assertions on output -- this is a sanitizer/TSAN target.
std::vector<std::future<void>> futures;
for (int i = 0; i < 10; ++i)
{
futures.push_back(std::async(std::launch::async, [this, i]() {
m_client.SetSslVerification(i % 2 == 0, (i % 2 == 0) ? "/some/path" : "");
}));
}
for (auto& f : futures)
{
f.get();
}
SUCCEED();
}

#endif // MATSDK_PAL_CPP11 && !_MSC_VER && HAVE_MAT_DEFAULT_HTTP_CLIENT
Loading