264 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			264 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_NET_CURL_WRAPPERS_HPP_INCLUDED)
 | |
| #define MIJIN_NET_CURL_WRAPPERS_HPP_INCLUDED 1
 | |
| 
 | |
| #include <map>
 | |
| #include <string>
 | |
| #include <curl/curl.h>
 | |
| 
 | |
| #include "./url.hpp"
 | |
| #include "../debug/assert.hpp"
 | |
| #include "../internal/common.hpp"
 | |
| #include "../types/result.hpp"
 | |
| 
 | |
| namespace curl
 | |
| {
 | |
| struct [[nodiscard]] Error
 | |
| {
 | |
|     CURLcode code = CURLE_OK;
 | |
| 
 | |
|     [[nodiscard]]
 | |
|     bool isSuccess() const MIJIN_NOEXCEPT { return code == CURLE_OK; }
 | |
| };
 | |
| template<typename TSuccess>
 | |
| using Result = mijin::ResultBase<TSuccess, Error>;
 | |
| 
 | |
| #define MIJIN_CURL_VERIFY_RESULT(curlResult) \
 | |
| do                                           \
 | |
| {                                            \
 | |
|     if ((curlResult) != CURLE_OK)            \
 | |
|     {                                        \
 | |
|         return Error{curlResult};            \
 | |
|     }                                        \
 | |
| } while (0)
 | |
| 
 | |
| using curl_write_callback_t = size_t(*)(char*, size_t, size_t, void*);
 | |
| using curl_read_callback_t = curl_write_callback_t;
 | |
| 
 | |
| class SList
 | |
| {
 | |
| private:
 | |
|     curl_slist* next_ = nullptr;
 | |
| public:
 | |
|     SList() MIJIN_NOEXCEPT = default;
 | |
|     SList(const SList&) = delete;
 | |
|     SList(SList&& other) noexcept : next_(std::exchange(other.next_, nullptr)) {}
 | |
|     ~SList() MIJIN_NOEXCEPT
 | |
|     {
 | |
|         if (next_ != nullptr)
 | |
|         {
 | |
|             curl_slist_free_all(next_);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     SList& operator=(const SList&) = delete;
 | |
|     SList& operator=(SList&& other) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         if (&other == this)
 | |
|         {
 | |
|             return *this;
 | |
|         }
 | |
|         
 | |
|         if (next_ != nullptr)
 | |
|         {
 | |
|             curl_slist_free_all(next_);
 | |
|         }
 | |
|         next_ = std::exchange(other.next_, nullptr);
 | |
|         return *this;
 | |
|     }
 | |
|     auto operator<=>(const SList&) const noexcept = default;
 | |
|     
 | |
|     void append(const char* str) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         next_ = curl_slist_append(next_, str);
 | |
|     }
 | |
|     
 | |
|     friend class CurlEasy;
 | |
| };
 | |
| 
 | |
| class CurlEasy
 | |
| {
 | |
| private:
 | |
|     CURL* handle_ = nullptr;
 | |
|     SList headers_;
 | |
| public:
 | |
|     CurlEasy() MIJIN_NOEXCEPT = default;
 | |
|     CurlEasy(const CurlEasy&) = delete;
 | |
|     CurlEasy(CurlEasy&& other) MIJIN_NOEXCEPT : handle_(std::exchange(other.handle_, nullptr)), headers_(std::move(other.headers_)) {}
 | |
|     ~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);
 | |
|         headers_ = std::move(other.headers_);
 | |
|         return *this;
 | |
|     }
 | |
| 
 | |
|     bool init() MIJIN_NOEXCEPT
 | |
|     {
 | |
|         reset();
 | |
|         handle_ = curl_easy_init();
 | |
|         if (handle_ == nullptr)
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
|         return 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);
 | |
|         return {result};
 | |
|     }
 | |
| 
 | |
|     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());
 | |
|     }
 | |
| 
 | |
|     Error setSSLVerifyPeer(bool verify) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_SSL_VERIFYPEER, verify);
 | |
|         return {result};
 | |
|     }
 | |
| 
 | |
|     Error setSSLVerifyHost(bool verify) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_SSL_VERIFYHOST, verify);
 | |
|         return {result};
 | |
|     }
 | |
|     
 | |
|     Error setWriteFunction(curl_write_callback_t callback) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_WRITEFUNCTION, callback);
 | |
|         return {result};
 | |
|     }
 | |
|     
 | |
|     Error setWriteData(void* data) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_WRITEDATA, data);
 | |
|         return {result};
 | |
|     }
 | |
| 
 | |
|     Error setPostFields(const char* data, bool copy = false) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, copy ? CURLOPT_COPYPOSTFIELDS : CURLOPT_POSTFIELDS, data);
 | |
|         return {result};
 | |
|     }
 | |
| 
 | |
|     Error setUpload(bool upload) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_UPLOAD, upload ? 1 : 0);
 | |
|         return {result};
 | |
|     }
 | |
| 
 | |
|     Error setInFileSize(curl_off_t size) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_INFILESIZE_LARGE, size);
 | |
|         return {result};
 | |
|     }
 | |
| 
 | |
|     Error setReadFunction(curl_read_callback_t callback) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_READFUNCTION, callback);
 | |
|         return {result};
 | |
|     }
 | |
| 
 | |
|     Error setReadData(const void* data) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_READDATA, data);
 | |
|         return {result};
 | |
|     }
 | |
|     
 | |
|     Error setNoBody(bool nobody) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_NOBODY, nobody ? 1 : 0);
 | |
|         return {result};
 | |
|     }
 | |
|     
 | |
|     Error setCustomRequest(bool customRequest) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_CUSTOMREQUEST, customRequest ? 1 : 0);
 | |
|         return {result};
 | |
|     }
 | |
|     
 | |
|     Error setHeaders(SList headers) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         headers_ = std::move(headers);
 | |
|         const CURLcode result = curl_easy_setopt(handle_, CURLOPT_HTTPHEADER, headers_.next_);
 | |
|         return {result};
 | |
|     }
 | |
|     
 | |
|     Error perform() MIJIN_NOEXCEPT
 | |
|     {
 | |
|         const CURLcode result = curl_easy_perform(handle_);
 | |
|         return {result};
 | |
|     }
 | |
|     
 | |
|     Result<int> getHTTPVersion() MIJIN_NOEXCEPT
 | |
|     {
 | |
|         return getInfo<int, long>(CURLINFO_HTTP_VERSION);
 | |
|     }
 | |
|     
 | |
|     Result<int> getStatus() MIJIN_NOEXCEPT
 | |
|     {
 | |
|         return getInfo<int, long>(CURLINFO_RESPONSE_CODE);
 | |
|     }
 | |
|     
 | |
|     Result<std::multimap<std::string, std::string>> getResponseHeaders() MIJIN_NOEXCEPT
 | |
|     {
 | |
|         // TODO: how to detect errors?
 | |
|         std::multimap<std::string, std::string> result;
 | |
|         curl_header* header = nullptr;
 | |
|         curl_header* prevHeader = nullptr;
 | |
|         
 | |
|         while ((header = curl_easy_nextheader(handle_, CURLH_HEADER, 0, prevHeader)) != nullptr)
 | |
|         {
 | |
|             result.emplace(header->name, header->value);
 | |
|             prevHeader = header;
 | |
|         }
 | |
|         
 | |
|         return result;
 | |
|     }
 | |
| private:
 | |
|     template<typename TResult, typename TInfo = TResult>
 | |
|     Result<TResult> getInfo(CURLINFO info) MIJIN_NOEXCEPT
 | |
|     {
 | |
|         TInfo value = 0;
 | |
|         const CURLcode result = curl_easy_getinfo(handle_, info, &value);
 | |
|         if (result != CURLE_OK)
 | |
|         {
 | |
|             return Error{result};
 | |
|         }
 | |
|         return static_cast<TResult>(value);
 | |
|     }
 | |
| };
 | |
| } // namespace curl
 | |
| 
 | |
| #endif // !defined(MIJIN_NET_CURL_WRAPPERS_HPP_INCLUDED)
 |