From 92d7b5552f4030bd2db981b80db1423c5a9ebba2 Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Wed, 11 Sep 2024 08:57:43 +0200 Subject: [PATCH] WIP: CURL/request implementation. --- source/mijin/net/curl_wrappers.hpp | 134 +++++++++++++++++++++++++++++ source/mijin/net/request.cpp | 60 +++++++++++++ source/mijin/net/request.hpp | 15 ++++ 3 files changed, 209 insertions(+) create mode 100644 source/mijin/net/curl_wrappers.hpp create mode 100644 source/mijin/net/request.cpp create mode 100644 source/mijin/net/request.hpp diff --git a/source/mijin/net/curl_wrappers.hpp b/source/mijin/net/curl_wrappers.hpp new file mode 100644 index 0000000..a600da9 --- /dev/null +++ b/source/mijin/net/curl_wrappers.hpp @@ -0,0 +1,134 @@ + +#pragma once + +#if !defined(MIJIN_NET_CURL_WRAPPERS_HPP_INCLUDED) +#define MIJIN_NET_CURL_WRAPPERS_HPP_INCLUDED 1 + +#include + +#include "./url.hpp" +#include "../debug/assert.hpp" +#include "../internal/common.hpp" +#include "../types/result.hpp" + +namespace curl +{ +#if !MIJIN_WITH_EXCEPTIONS +struct [[nodiscard]] Error +{ + CURLcode code = CURLE_OK; + + [[nodiscard]] + bool isSuccess() const MIJIN_NOEXCEPT { return code == CURLE_OK; } +}; + +#define MIJIN_CURL_VERIFY_RESULT(curlResult) \ +do \ +{ \ + if ((curlResult) != CURLE_OK) \ + { \ + return Error{curlResult}; \ + } \ +} while (0) +#define MIJIN_CURL_RETURN_SUCCESS() return Error() +#else +using Error = void; + +[[nodiscard]] +std::string curlCodeMessage(CURLcode code) +{ + switch (code) + { + default: + return "Unknown CURL error."; + } +} + +class CurlException : public std::runtime_error +{ +private: + CURLcode code_; +public: + explicit CurlException(CURLcode code) MIJIN_NOEXCEPT: std::runtime_error(curlCodeMessage(code)), code_(code) + {} + + [[nodiscard]] + constexpr CURLcode getCode() const MIJIN_NOEXCEPT + { return code_; } +}; + +#define MIJIN_CURL_VERIFY_RESULT(curlResult) \ +do \ +{ \ + if ((curlResult) != CURLE_OK) \ + { \ + throw CurlException((curlResult)); \ + } \ +} while (0) +#define MIJIN_CURL_RETURN_SUCCESS() return +#endif + +class CurlEasy +{ +private: + CURL* handle_ = nullptr; +public: + CurlEasy() MIJIN_NOEXCEPT = default; + CurlEasy(const CurlEasy&) = delete; + CurlEasy(CurlEasy&& other) MIJIN_NOEXCEPT : handle_(std::exchange(other.handle_, nullptr)) {} + ~CurlEasy() MIJIN_NOEXCEPT + { + reset(); + } + + CurlEasy& operator=(const CurlEasy&) = delete; + CurlEasy& operator=(CurlEasy&& other) MIJIN_NOEXCEPT + { + if (this == &other) + { + return *this; + } + reset(); + handle_ = std::exchange(other.handle_, nullptr); + return *this; + } + + MIJIN_ERROR_BOOL init() MIJIN_THROWS + { + reset(); + handle_ = curl_easy_init(); + if (handle_ != nullptr) + { + MIJIN_THROW_OR_RETURN_STD(false, "Error initializing CURL easy."); + } + MIJIN_RETURN_SUCCESS(true); + } + void reset() MIJIN_NOEXCEPT + { + if (handle_) + { + curl_easy_cleanup(handle_); + handle_ = nullptr; + } + } + + Error setURL(const char* url) MIJIN_THROWS + { + const CURLcode result = curl_easy_setopt(handle_, CURLOPT_URL, url); + MIJIN_CURL_VERIFY_RESULT(result); + MIJIN_CURL_RETURN_SUCCESS(); + } + + Error setURL(const std::string& url) MIJIN_NOEXCEPT + { + return setURL(url.c_str()); + } + + Error setURL(const mijin::URL& url) MIJIN_NOEXCEPT + { + return setURL(url.getBase().c_str()); + } +}; +} // namespace curl + +#endif // !defined(MIJIN_NET_CURL_WRAPPERS_HPP_INCLUDED) diff --git a/source/mijin/net/request.cpp b/source/mijin/net/request.cpp new file mode 100644 index 0000000..84f6168 --- /dev/null +++ b/source/mijin/net/request.cpp @@ -0,0 +1,60 @@ + +#include "./request.hpp" + +#include + +#include "./curl_wrappers.hpp" + +namespace mijin +{ +namespace +{ +bool gCurlInited = false; +std::mutex gCurlInitMutex; + +class CURLGuard +{ +public: + ~CURLGuard() MIJIN_NOEXCEPT + { + if (gCurlInited) + { + curl_global_cleanup(); + } + } +} gCURLGuard [[maybe_unused]]; + +bool initCURL() MIJIN_NOEXCEPT +{ + if (gCurlInited) + { + return true; + } + const std::unique_lock initLock(gCurlInitMutex); + if (gCurlInited) + { + return true; + } + gCurlInited = (curl_global_init(0) == CURLE_OK); + return gCurlInited; +} +} + +Task> c_request(const URL& url, HTTPRequest request) MIJIN_NOEXCEPT +{ + (void) url; + (void) request; + if (!initCURL()) + { + co_return StreamError::UNKNOWN_ERROR; + } + curl::CurlEasy easy; + if (!easy.init()) + { + co_return StreamError::UNKNOWN_ERROR; + } + easy.setURL(url); + + co_return StreamError::UNKNOWN_ERROR; +} +} // namespace mijin diff --git a/source/mijin/net/request.hpp b/source/mijin/net/request.hpp new file mode 100644 index 0000000..fd76b4a --- /dev/null +++ b/source/mijin/net/request.hpp @@ -0,0 +1,15 @@ + +#pragma once + +#if !defined(MIJIN_NET_REQUST_HPP_INCLUDED) +#define MIJIN_NET_REQUST_HPP_INCLUDED 1 + +#include "./http.hpp" +#include "../internal/common.hpp" + +namespace mijin +{ +Task> c_request(const URL& url, HTTPRequest request = {}) MIJIN_NOEXCEPT; +} // namespace mijin + +#endif // !defined(MIJIN_NET_REQUST_HPP_INCLUDED)