summaryrefslogtreecommitdiff
path: root/src/utils/cpp
diff options
context:
space:
mode:
authorPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2022-09-28 17:00:02 +0200
committerPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2022-12-21 14:59:04 +0100
commitc5e383b2955eec34aac6501f48c07976c5c06d60 (patch)
treeb7b34324ebef386f8c13667e0bb3a8726b6d526d /src/utils/cpp
parent4e2ce048cc3da45602b3a366d9b7377125164074 (diff)
downloadjustbuild-c5e383b2955eec34aac6501f48c07976c5c06d60.tar.gz
Utils: Add curl easy handle utility class
Diffstat (limited to 'src/utils/cpp')
-rw-r--r--src/utils/cpp/TARGETS13
-rw-r--r--src/utils/cpp/curl_easy_handle.cpp166
-rw-r--r--src/utils/cpp/curl_easy_handle.hpp86
3 files changed, 265 insertions, 0 deletions
diff --git a/src/utils/cpp/TARGETS b/src/utils/cpp/TARGETS
index 82177208..e278210e 100644
--- a/src/utils/cpp/TARGETS
+++ b/src/utils/cpp/TARGETS
@@ -66,4 +66,17 @@
, "stage": ["src", "utils", "cpp"]
, "private-deps": [["src/buildtool/logging", "logging"], ["", "libcurl"]]
}
+, "curl_easy_handle":
+ { "type": ["@", "rules", "CC", "library"]
+ , "name": ["curl_easy_handle"]
+ , "hdrs": ["curl_easy_handle.hpp"]
+ , "srcs": ["curl_easy_handle.cpp"]
+ , "deps": ["curl_context", ["@", "gsl-lite", "", "gsl-lite"]]
+ , "stage": ["src", "utils", "cpp"]
+ , "private-deps":
+ [ ["src/buildtool/logging", "logging"]
+ , ["src/buildtool/file_system", "file_system_manager"]
+ , ["", "libcurl"]
+ ]
+ }
}
diff --git a/src/utils/cpp/curl_easy_handle.cpp b/src/utils/cpp/curl_easy_handle.cpp
new file mode 100644
index 00000000..c823890d
--- /dev/null
+++ b/src/utils/cpp/curl_easy_handle.cpp
@@ -0,0 +1,166 @@
+// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/utils/cpp/curl_easy_handle.hpp"
+
+#include <fstream>
+
+#include "src/buildtool/file_system/file_system_manager.hpp"
+#include "src/buildtool/logging/logger.hpp"
+
+#ifndef BOOTSTRAP_BUILD_TOOL
+extern "C" {
+#include "curl/curl.h"
+}
+#endif // BOOTSTRAP_BUILD_TOOL
+
+void curl_easy_closer(gsl::owner<CURL*> curl) {
+#ifndef BOOTSTRAP_BUILD_TOOL
+ curl_easy_cleanup(curl);
+#endif // BOOTSTRAP_BUILD_TOOL
+}
+
+auto CurlEasyHandle::Create() noexcept -> std::shared_ptr<CurlEasyHandle> {
+#ifdef BOOTSTRAP_BUILD_TOOL
+ return nullptr;
+#else
+ try {
+ auto curl = std::make_shared<CurlEasyHandle>();
+ auto* handle = curl_easy_init();
+ if (handle == nullptr) {
+ return nullptr;
+ }
+ curl->handle_.reset(handle);
+ return curl;
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "create curl easy handle failed with:\n{}",
+ ex.what());
+ return nullptr;
+ }
+#endif // BOOTSTRAP_BUILD_TOOL
+}
+
+auto CurlEasyHandle::EasyWriteToFile(gsl::owner<char*> data,
+ size_t size,
+ size_t nmemb,
+ gsl::owner<void*> userptr)
+ -> std::streamsize {
+#ifdef BOOTSTRAP_BUILD_TOOL
+ return 0;
+#else
+ auto actual_size = static_cast<std::streamsize>(size * nmemb);
+ auto* file = static_cast<std::ofstream*>(userptr);
+ file->write(data, actual_size); // append chunk
+ return actual_size;
+#endif // BOOTSTRAP_BUILD_TOOL
+}
+
+auto CurlEasyHandle::EasyWriteToString(gsl::owner<char*> data,
+ size_t size,
+ size_t nmemb,
+ gsl::owner<void*> userptr)
+ -> std::streamsize {
+#ifdef BOOTSTRAP_BUILD_TOOL
+ return 0;
+#else
+ (static_cast<std::string*>(userptr))->append(data, size * nmemb);
+ return static_cast<std::streamsize>(size * nmemb);
+#endif // BOOTSTRAP_BUILD_TOOL
+}
+
+auto CurlEasyHandle::DownloadToFile(
+ std::string const& url,
+ std::filesystem::path const& file_path) noexcept -> int {
+#ifdef BOOTSTRAP_BUILD_TOOL
+ return 1;
+#else
+ try {
+ // set URL
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
+ curl_easy_setopt(handle_.get(), CURLOPT_URL, url.c_str());
+
+ // set callback for writing to file
+ std::ofstream file(file_path.c_str(), std::ios::binary);
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
+ curl_easy_setopt(handle_.get(), CURLOPT_WRITEFUNCTION, EasyWriteToFile);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
+ curl_easy_setopt(
+ handle_.get(), CURLOPT_WRITEDATA, static_cast<void*>(&file));
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
+ curl_easy_setopt(handle_.get(), CURLOPT_VERBOSE, 1);
+
+ // perform download
+ auto res = curl_easy_perform(handle_.get());
+
+ // close file
+ file.close();
+
+ // check result
+ if (res != CURLE_OK) {
+ // cleanup failed downloaded file, if created
+ [[maybe_unused]] auto tmp_res =
+ FileSystemManager::RemoveFile(file_path);
+ }
+ return res;
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "curl download to file failed with:\n{}",
+ ex.what());
+ return 1;
+ }
+#endif // BOOTSTRAP_BUILD_TOOL
+}
+
+auto CurlEasyHandle::DownloadToString(std::string const& url) noexcept
+ -> std::optional<std::string> {
+#ifdef BOOTSTRAP_BUILD_TOOL
+ return std::nullopt;
+#else
+ try {
+ // set URL
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
+ curl_easy_setopt(handle_.get(), CURLOPT_URL, url.c_str());
+
+ // set callback for writing to string
+ std::string content{};
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
+ curl_easy_setopt(
+ handle_.get(), CURLOPT_WRITEFUNCTION, EasyWriteToString);
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
+ curl_easy_setopt(
+ handle_.get(), CURLOPT_WRITEDATA, static_cast<void*>(&content));
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
+ curl_easy_setopt(handle_.get(), CURLOPT_VERBOSE, 1);
+
+ // perform download
+ auto res = curl_easy_perform(handle_.get());
+
+ // check result
+ if (res != CURLE_OK) {
+ return std::nullopt;
+ }
+ return content;
+ } catch (std::exception const& ex) {
+ Logger::Log(LogLevel::Error,
+ "curl download to string failed with:\n{}",
+ ex.what());
+ return std::nullopt;
+ }
+#endif // BOOTSTRAP_BUILD_TOOL
+}
diff --git a/src/utils/cpp/curl_easy_handle.hpp b/src/utils/cpp/curl_easy_handle.hpp
new file mode 100644
index 00000000..5f318887
--- /dev/null
+++ b/src/utils/cpp/curl_easy_handle.hpp
@@ -0,0 +1,86 @@
+// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef INCLUDED_SRC_OTHER_TOOLS_CURL_EASY_HANDLE_HPP
+#define INCLUDED_SRC_OTHER_TOOLS_CURL_EASY_HANDLE_HPP
+
+#include <filesystem>
+#include <functional>
+#include <memory>
+#include <optional>
+
+#include "gsl-lite/gsl-lite.hpp"
+#include "src/utils/cpp/curl_context.hpp"
+
+extern "C" {
+#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER)
+using CURL = struct Curl_easy;
+#else
+using CURL = void;
+#endif
+}
+
+void curl_easy_closer(gsl::owner<CURL*> curl);
+
+class CurlEasyHandle {
+ public:
+ CurlEasyHandle() noexcept = default;
+ ~CurlEasyHandle() noexcept = default;
+
+ // prohibit moves and copies
+ CurlEasyHandle(CurlEasyHandle const&) = delete;
+ CurlEasyHandle(CurlEasyHandle&& other) = delete;
+ auto operator=(CurlEasyHandle const&) = delete;
+ auto operator=(CurlEasyHandle&& other) = delete;
+
+ /// \brief Create a CurlEasyHandle object
+ [[nodiscard]] auto static Create() noexcept
+ -> std::shared_ptr<CurlEasyHandle>;
+
+ /// \brief Download file from URL into given file_path.
+ /// Will perform cleanup (i.e., remove empty file) in case download fails.
+ /// Returns 0 if successful.
+ [[nodiscard]] auto DownloadToFile(
+ std::string const& url,
+ std::filesystem::path const& file_path) noexcept -> int;
+
+ /// \brief Download file from URL into string as binary.
+ /// Returns the content or nullopt if download failure.
+ [[nodiscard]] auto DownloadToString(std::string const& url) noexcept
+ -> std::optional<std::string>;
+
+ private:
+ // IMPORTANT: the CurlContext must to be initialized before any curl object!
+ CurlContext curl_context_{};
+ std::unique_ptr<CURL, decltype(&curl_easy_closer)> handle_{
+ nullptr,
+ curl_easy_closer};
+
+ /// \brief Overwrites write_callback to redirrect to file instead of stdout.
+ [[nodiscard]] auto static EasyWriteToFile(gsl::owner<char*> data,
+ size_t size,
+ size_t nmemb,
+ gsl::owner<void*> userptr)
+ -> std::streamsize;
+
+ /// \brief Overwrites write_callback to redirect to string instead of
+ /// stdout.
+ [[nodiscard]] auto static EasyWriteToString(gsl::owner<char*> data,
+ size_t size,
+ size_t nmemb,
+ gsl::owner<void*> userptr)
+ -> std::streamsize;
+};
+
+#endif // INCLUDED_SRC_OTHER_TOOLS_CURL_EASY_HANDLE_HPP \ No newline at end of file