diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/utils/cpp/TARGETS | 13 | ||||
-rw-r--r-- | src/utils/cpp/curl_easy_handle.cpp | 166 | ||||
-rw-r--r-- | src/utils/cpp/curl_easy_handle.hpp | 86 |
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 |