MINIFICPP-2209 Small HTTPClient refactor: preconditions and exception safety
Signed-off-by: Ferenc Gerlits <fgerlits@gmail.com>
This closes #1648
diff --git a/extensions/http-curl/client/HTTPClient.cpp b/extensions/http-curl/client/HTTPClient.cpp
index 6e8d411..8d73f62 100644
--- a/extensions/http-curl/client/HTTPClient.cpp
+++ b/extensions/http-curl/client/HTTPClient.cpp
@@ -16,18 +16,15 @@
*/
#include "HTTPClient.h"
-#include <algorithm>
#include <cinttypes>
#include <memory>
#include <string>
#include <utility>
#include <vector>
-#include "Exception.h"
#include "utils/gsl.h"
#include "utils/StringUtils.h"
#include "core/Resource.h"
-#include "utils/RegexUtils.h"
#include "range/v3/algorithm/all_of.hpp"
#include "range/v3/action/transform.hpp"
#include "utils/HTTPUtils.h"
@@ -328,18 +325,16 @@
}
namespace {
-struct CurlSListFreeAll {
- void operator()(struct curl_slist* slist) const {
- curl_slist_free_all(slist);
- }
-};
+using CurlSlistDeleter = decltype([](struct curl_slist* slist) { curl_slist_free_all(slist); });
-std::unique_ptr<struct curl_slist, CurlSListFreeAll> getCurlSList(const std::unordered_map<std::string, std::string>& request_headers) {
- curl_slist* new_list = nullptr;
+std::unique_ptr<struct curl_slist, CurlSlistDeleter> toCurlSlist(const std::unordered_map<std::string, std::string>& request_headers) {
+ gsl::owner<curl_slist*> new_list = nullptr;
+ const auto guard = gsl::finally([&new_list]() { curl_slist_free_all(new_list); });
for (const auto& [header_key, header_value] : request_headers)
- new_list = curl_slist_append(new_list, utils::StringUtils::join_pack(header_key, ": ", header_value).c_str());
+ new_list = (utils::optional_from_ptr(curl_slist_append(new_list, utils::StringUtils::join_pack(header_key, ": ", header_value).c_str()))
+ | utils::orElse([]() { throw std::runtime_error{"curl_slist_append failed"}; })).value();
- return {new_list, {}};
+ return {std::exchange(new_list, nullptr), {}};
}
} // namespace
@@ -366,7 +361,7 @@
curl_easy_setopt(http_session_.get(), CURLOPT_NOPROGRESS, 1);
}
- auto headers = getCurlSList(request_headers_);
+ const auto headers = toCurlSlist(request_headers_);
if (headers) {
curl_slist_append(headers.get(), "Expect:");
curl_easy_setopt(http_session_.get(), CURLOPT_HTTPHEADER, headers.get());
@@ -452,7 +447,8 @@
}
int HTTPClient::onProgress(void *clientp, curl_off_t /*dltotal*/, curl_off_t dlnow, curl_off_t /*ultotal*/, curl_off_t ulnow) {
- HTTPClient& client = *reinterpret_cast<HTTPClient*>(clientp);
+ gsl_Expects(clientp);
+ HTTPClient& client = *static_cast<HTTPClient*>(clientp);
auto now = std::chrono::steady_clock::now();
auto elapsed = now - client.progress_.last_transferred_;
if (dlnow != client.progress_.downloaded_data_ || ulnow != client.progress_.uploaded_data_) {
@@ -522,12 +518,10 @@
return "X-MiNiFi-Empty-Attribute-Name";
}
- std::string result;
- result.reserve(field_name.size());
// RFC822 3.1.2: The field-name must be composed of printable ASCII characters
// (i.e., characters that have values between 33. and 126., decimal, except colon).
- ranges::actions::transform(field_name, [](char ch) {
- return (ch >= 33 && ch <= 126 && ch != ':') ? ch : '-';
+ ranges::actions::transform(field_name, [](char ch) { // NOLINT: false positive: Add #include <algorithm> for transform [build/include_what_you_use] [4]
+ return (ch >= 33 && ch <= 126 && ch != ':') ? ch : '-';
});
return field_name;
}
diff --git a/extensions/http-curl/client/HTTPClient.h b/extensions/http-curl/client/HTTPClient.h
index d6a1894..144edb3 100644
--- a/extensions/http-curl/client/HTTPClient.h
+++ b/extensions/http-curl/client/HTTPClient.h
@@ -207,8 +207,10 @@
protected:
static CURLcode configure_ssl_context(CURL* /*curl*/, void *ctx, void *param) {
#ifdef OPENSSL_SUPPORT
- auto* ssl_context_service = static_cast<minifi::controllers::SSLContextService*>(param);
- if (!ssl_context_service->configure_ssl_context(static_cast<SSL_CTX*>(ctx))) {
+ gsl_Expects(ctx);
+ gsl_Expects(param);
+ auto& ssl_context_service = *static_cast<minifi::controllers::SSLContextService*>(param);
+ if (!ssl_context_service.configure_ssl_context(static_cast<SSL_CTX*>(ctx))) {
return CURLE_FAILED_INIT;
}
return CURLE_OK;