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)
 |