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