Made the request stuff work.
This commit is contained in:
parent
77d46d986c
commit
0d00dec8c7
@ -6,6 +6,7 @@
|
||||
|
||||
#include <bit>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include "../debug/assert.hpp"
|
||||
@ -50,6 +51,12 @@ public:
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] std::span<const T> makeSpan() const;
|
||||
|
||||
template<typename TChar = char, typename TTraits = std::char_traits<TChar>>
|
||||
[[nodiscard]] std::basic_string_view<TChar, TTraits> makeStringView() const ;
|
||||
|
||||
template<typename T>
|
||||
void append(std::span<const T> data);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
@ -84,6 +91,12 @@ public:
|
||||
MIJIN_ASSERT(buffer_, "BufferView::resize(): cannot resize a view without a buffer.");
|
||||
buffer_->reserve(numElements * sizeof(T));
|
||||
}
|
||||
void append(std::span<const T> data)
|
||||
{
|
||||
MIJIN_ASSERT(buffer_, "BufferView::resize(): cannot append to a view without a buffer.");
|
||||
resize(size() + data.size());
|
||||
std::copy(data.begin(), data.end(), begin() + size() - data.size());
|
||||
}
|
||||
|
||||
[[nodiscard]] inline iterator begin() { return buffer_ ? static_cast<T*>(buffer_->data()) : nullptr; }
|
||||
[[nodiscard]] inline const_iterator begin() const { return buffer_ ? static_cast<T*>(buffer_->data()) : nullptr; }
|
||||
@ -127,6 +140,20 @@ std::span<const T> TypelessBuffer::makeSpan() const
|
||||
std::bit_cast<const T*>(bytes_.data() + bytes_.size())
|
||||
};
|
||||
}
|
||||
|
||||
template<typename TChar, typename TTraits>
|
||||
std::basic_string_view<TChar, TTraits> TypelessBuffer::makeStringView() const
|
||||
{
|
||||
MIJIN_ASSERT(bytes_.size() % sizeof(TChar) == 0, "Buffer cannot be divided into elements of this char type.");
|
||||
return {std::bit_cast<const TChar*>(bytes_.data()), bytes_.size() / sizeof(TChar)};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void TypelessBuffer::append(std::span<const T> data)
|
||||
{
|
||||
bytes_.resize(bytes_.size() + data.size_bytes());
|
||||
std::memcpy(bytes_.data() + bytes_.size() - data.size_bytes(), data.data(), data.size_bytes());
|
||||
}
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED)
|
||||
|
@ -132,6 +132,11 @@ public:
|
||||
return readRaw(&*range.begin(), bytes, {.partial = partial}, outBytesRead);
|
||||
}
|
||||
|
||||
StreamError readRaw(TypelessBuffer& buffer, const ReadOptions& options = {})
|
||||
{
|
||||
return readRaw(buffer.data(), buffer.byteSize(), options);
|
||||
}
|
||||
|
||||
template<std::ranges::contiguous_range TRange>
|
||||
mijin::Task<StreamError> c_readRaw(TRange& range, const ReadOptions& options = {}, std::size_t* outBytesRead = nullptr)
|
||||
{
|
||||
@ -139,6 +144,11 @@ public:
|
||||
return c_readRaw(&*range.begin(), bytes, options, outBytesRead);
|
||||
}
|
||||
|
||||
mijin::Task<StreamError> c_readRaw(TypelessBuffer& buffer, const ReadOptions& options = {})
|
||||
{
|
||||
return c_readRaw(buffer.data(), buffer.byteSize(), options);
|
||||
}
|
||||
|
||||
StreamError writeRaw(const void* data, std::size_t bytes)
|
||||
{
|
||||
const std::uint8_t* ptr = static_cast<const std::uint8_t*>(data);
|
||||
|
@ -4,6 +4,8 @@
|
||||
#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"
|
||||
@ -13,7 +15,6 @@
|
||||
|
||||
namespace curl
|
||||
{
|
||||
#if !MIJIN_WITH_EXCEPTIONS
|
||||
struct [[nodiscard]] Error
|
||||
{
|
||||
CURLcode code = CURLE_OK;
|
||||
@ -21,6 +22,8 @@ struct [[nodiscard]] Error
|
||||
[[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 \
|
||||
@ -30,52 +33,60 @@ do \
|
||||
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.";
|
||||
}
|
||||
}
|
||||
using curl_write_callback_t = size_t(*)(char*, size_t, size_t, void*);
|
||||
using curl_read_callback_t = curl_write_callback_t;
|
||||
|
||||
class CurlException : public std::runtime_error
|
||||
class SList
|
||||
{
|
||||
private:
|
||||
CURLcode code_;
|
||||
curl_slist* next_ = nullptr;
|
||||
public:
|
||||
explicit CurlException(CURLcode code) MIJIN_NOEXCEPT: std::runtime_error(curlCodeMessage(code)), code_(code)
|
||||
{}
|
||||
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_);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
constexpr CURLcode getCode() const MIJIN_NOEXCEPT
|
||||
{ return code_; }
|
||||
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;
|
||||
};
|
||||
|
||||
#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;
|
||||
SList headers_;
|
||||
public:
|
||||
CurlEasy() MIJIN_NOEXCEPT = default;
|
||||
CurlEasy(const CurlEasy&) = delete;
|
||||
CurlEasy(CurlEasy&& other) MIJIN_NOEXCEPT : handle_(std::exchange(other.handle_, nullptr)) {}
|
||||
CurlEasy(CurlEasy&& other) MIJIN_NOEXCEPT : handle_(std::exchange(other.handle_, nullptr)), headers_(std::move(other.headers_)) {}
|
||||
~CurlEasy() MIJIN_NOEXCEPT
|
||||
{
|
||||
reset();
|
||||
@ -90,19 +101,21 @@ public:
|
||||
}
|
||||
reset();
|
||||
handle_ = std::exchange(other.handle_, nullptr);
|
||||
headers_ = std::move(other.headers_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
MIJIN_ERROR_BOOL init() MIJIN_THROWS
|
||||
bool init() MIJIN_NOEXCEPT
|
||||
{
|
||||
reset();
|
||||
handle_ = curl_easy_init();
|
||||
if (handle_ != nullptr)
|
||||
if (handle_ == nullptr)
|
||||
{
|
||||
MIJIN_THROW_OR_RETURN_STD(false, "Error initializing CURL easy.");
|
||||
return false;
|
||||
}
|
||||
MIJIN_RETURN_SUCCESS(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() MIJIN_NOEXCEPT
|
||||
{
|
||||
if (handle_)
|
||||
@ -115,8 +128,7 @@ public:
|
||||
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();
|
||||
return {result};
|
||||
}
|
||||
|
||||
Error setURL(const std::string& url) MIJIN_NOEXCEPT
|
||||
@ -128,6 +140,123 @@ public:
|
||||
{
|
||||
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
|
||||
|
||||
|
@ -154,8 +154,8 @@ Task<StreamResult<HTTPResponse>> HTTPStream::c_readResponse() MIJIN_NOEXCEPT
|
||||
{
|
||||
co_return StreamError::PROTOCOL_ERROR;
|
||||
}
|
||||
response.content.resize(contentLength);
|
||||
MIJIN_HTTP_CHECKREAD(base_->c_readRaw(response.content));
|
||||
response.body.resize(contentLength);
|
||||
MIJIN_HTTP_CHECKREAD(base_->c_readRaw(response.body));
|
||||
}
|
||||
|
||||
co_return response;
|
||||
|
@ -10,11 +10,21 @@
|
||||
#include "./socket.hpp"
|
||||
#include "./url.hpp"
|
||||
#include "../container/boxed_object.hpp"
|
||||
#include "../container/typeless_buffer.hpp"
|
||||
#include "../internal/common.hpp"
|
||||
#include "../io/stream.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
namespace http_method
|
||||
{
|
||||
inline constexpr std::string GET = "GET";
|
||||
inline constexpr std::string POST = "POST";
|
||||
inline constexpr std::string HEAD = "HEAD";
|
||||
inline constexpr std::string PUT = "PUT";
|
||||
inline constexpr std::string DELETE = "DELETE";
|
||||
}
|
||||
|
||||
struct HTTPVersion
|
||||
{
|
||||
unsigned major;
|
||||
@ -30,13 +40,20 @@ struct HTTPRequest
|
||||
std::string body;
|
||||
};
|
||||
|
||||
struct HTTPRequestOptions
|
||||
{
|
||||
std::string method = "GET";
|
||||
std::multimap<std::string, std::string> headers;
|
||||
TypelessBuffer body;
|
||||
};
|
||||
|
||||
struct HTTPResponse
|
||||
{
|
||||
HTTPVersion version;
|
||||
unsigned status;
|
||||
std::string statusMessage;
|
||||
std::multimap<std::string, std::string> headers;
|
||||
std::string content;
|
||||
TypelessBuffer body;
|
||||
};
|
||||
|
||||
class HTTPStream
|
||||
|
@ -24,6 +24,12 @@ public:
|
||||
}
|
||||
} gCURLGuard [[maybe_unused]];
|
||||
|
||||
struct ReadData
|
||||
{
|
||||
mijin::TypelessBuffer* buffer;
|
||||
std::size_t pos;
|
||||
};
|
||||
|
||||
bool initCURL() MIJIN_NOEXCEPT
|
||||
{
|
||||
if (gCurlInited)
|
||||
@ -38,23 +44,139 @@ bool initCURL() MIJIN_NOEXCEPT
|
||||
gCurlInited = (curl_global_init(0) == CURLE_OK);
|
||||
return gCurlInited;
|
||||
}
|
||||
|
||||
std::size_t writeCallback(char* ptr, std::size_t /* size */, std::size_t nmemb, void* userdata) noexcept
|
||||
{
|
||||
TypelessBuffer& body = *static_cast<TypelessBuffer*>(userdata);
|
||||
body.append(std::span<const char>(ptr, nmemb));
|
||||
return nmemb;
|
||||
}
|
||||
|
||||
Task<StreamResult<HTTPResponse>> c_request(const URL& url, HTTPRequest request) MIJIN_NOEXCEPT
|
||||
std::size_t readCallback(char* ptr, std::size_t size, std::size_t nmemb, void* userdata) noexcept
|
||||
{
|
||||
(void) url;
|
||||
(void) request;
|
||||
ReadData& data = *static_cast<ReadData*>(userdata);
|
||||
const std::size_t bytesToRead = std::min(size * nmemb, data.buffer->byteSize() - data.pos);
|
||||
std::memcpy(ptr, static_cast<std::byte*>(data.buffer->data()) + data.pos, bytesToRead);
|
||||
data.pos += bytesToRead;
|
||||
|
||||
return bytesToRead;
|
||||
}
|
||||
}
|
||||
|
||||
Task<StreamResult<HTTPResponse>> c_request(const URL& url, HTTPRequestOptions options) MIJIN_NOEXCEPT
|
||||
{
|
||||
ReadData readData;
|
||||
|
||||
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;
|
||||
curl::SList requestHeaders;
|
||||
for (const auto& [name, value] : options.headers)
|
||||
{
|
||||
requestHeaders.append(std::format("{}: {}", name, value).c_str());
|
||||
}
|
||||
|
||||
#define HANDLE_CURL_RESULT(result) \
|
||||
if (const curl::Error error = (result); !error.isSuccess()) \
|
||||
{ \
|
||||
co_return StreamError::UNKNOWN_ERROR; \
|
||||
}
|
||||
HANDLE_CURL_RESULT(easy.setHeaders(std::move(requestHeaders)))
|
||||
|
||||
if (options.method == http_method::POST)
|
||||
{
|
||||
HANDLE_CURL_RESULT(easy.setPostFields(options.body.makeSpan<const char>().data()))
|
||||
}
|
||||
else if (options.method == http_method::PUT)
|
||||
{
|
||||
HANDLE_CURL_RESULT(easy.setUpload(true))
|
||||
HANDLE_CURL_RESULT(easy.setInFileSize(options.body.byteSize()));
|
||||
HANDLE_CURL_RESULT(easy.setReadFunction(&readCallback));
|
||||
readData.buffer = &options.body;
|
||||
readData.pos = 0;
|
||||
HANDLE_CURL_RESULT(easy.setReadData(&readData));
|
||||
}
|
||||
else if (options.method == http_method::HEAD)
|
||||
{
|
||||
HANDLE_CURL_RESULT(easy.setNoBody(true))
|
||||
}
|
||||
else if (options.method != http_method::GET)
|
||||
{
|
||||
// different option, let's do our best
|
||||
HANDLE_CURL_RESULT(easy.setCustomRequest(true))
|
||||
if (!options.body.empty())
|
||||
{
|
||||
HANDLE_CURL_RESULT(easy.setUpload(true))
|
||||
HANDLE_CURL_RESULT(easy.setInFileSize(options.body.byteSize()));
|
||||
HANDLE_CURL_RESULT(easy.setReadFunction(&readCallback));
|
||||
readData.buffer = &options.body;
|
||||
readData.pos = 0;
|
||||
HANDLE_CURL_RESULT(easy.setReadData(&readData));
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE_CURL_RESULT(easy.setURL(url))
|
||||
HANDLE_CURL_RESULT(easy.setWriteFunction(&writeCallback))
|
||||
TypelessBuffer body;
|
||||
HANDLE_CURL_RESULT(easy.setWriteData(&body))
|
||||
HANDLE_CURL_RESULT(easy.perform())
|
||||
#undef HANDLE_CURL_RESULT
|
||||
|
||||
HTTPResponse response = {
|
||||
.body = std::move(body)
|
||||
};
|
||||
if (const curl::Result<int> httpVersion = easy.getHTTPVersion(); httpVersion.isSuccess())
|
||||
{
|
||||
switch (*httpVersion)
|
||||
{
|
||||
case CURL_HTTP_VERSION_1_0:
|
||||
response.version = {1, 0};
|
||||
break;
|
||||
case CURL_HTTP_VERSION_1_1:
|
||||
response.version = {1, 1};
|
||||
break;
|
||||
case CURL_HTTP_VERSION_2_0:
|
||||
response.version = {2, 0};
|
||||
break;
|
||||
case CURL_HTTP_VERSION_3:
|
||||
response.version = {3, 0};
|
||||
break;
|
||||
default:
|
||||
MIJIN_ERROR("Unknown CURL http version returned.");
|
||||
response.version = {1, 0};
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
co_return StreamError::UNKNOWN_ERROR;
|
||||
}
|
||||
if (const curl::Result<int> status = easy.getStatus(); status.isSuccess())
|
||||
{
|
||||
response.status = *status;
|
||||
}
|
||||
else
|
||||
{
|
||||
co_return StreamError::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (curl::Result<std::multimap<std::string, std::string>> headers = easy.getResponseHeaders(); headers.isSuccess())
|
||||
{
|
||||
response.headers = std::move(*headers);
|
||||
}
|
||||
else
|
||||
{
|
||||
co_return StreamError::UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
co_return response;
|
||||
}
|
||||
} // namespace mijin
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
Task<StreamResult<HTTPResponse>> c_request(const URL& url, HTTPRequest request = {}) MIJIN_NOEXCEPT;
|
||||
Task<StreamResult<HTTPResponse>> c_request(const URL& url, HTTPRequestOptions options = {}) MIJIN_NOEXCEPT;
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_NET_REQUST_HPP_INCLUDED)
|
||||
|
Loading…
x
Reference in New Issue
Block a user