Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View file

@ -0,0 +1,67 @@
cmake_minimum_required(VERSION 3.15)
target_include_directories(cpr PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/cpr_generated_includes/>)
target_sources(cpr PRIVATE
# Header files (useful in IDEs)
cpr/accept_encoding.h
cpr/api.h
cpr/async.h
cpr/auth.h
cpr/bearer.h
cpr/body.h
cpr/buffer.h
cpr/cert_info.h
cpr/cookies.h
cpr/cpr.h
cpr/cprtypes.h
cpr/curlholder.h
cpr/curlholder.h
cpr/error.h
cpr/file.h
cpr/limit_rate.h
cpr/local_port.h
cpr/local_port_range.h
cpr/multipart.h
cpr/parameters.h
cpr/payload.h
cpr/proxies.h
cpr/proxyauth.h
cpr/response.h
cpr/session.h
cpr/singleton.h
cpr/ssl_ctx.h
cpr/ssl_options.h
cpr/threadpool.h
cpr/timeout.h
cpr/unix_socket.h
cpr/util.h
cpr/verbose.h
cpr/interface.h
cpr/redirect.h
cpr/http_version.h
cpr/interceptor.h
cpr/filesystem.h
cpr/curlmultiholder.h
cpr/multiperform.h
cpr/resolve.h
${PROJECT_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h
)
# Filesystem
if(CPR_USE_BOOST_FILESYSTEM)
find_package(Boost 1.44 REQUIRED COMPONENTS filesystem)
if(Boost_FOUND)
target_link_libraries(cpr PUBLIC Boost::filesystem)
endif()
endif()
if (((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND NOT CPR_USE_BOOST_FILESYSTEM)
target_link_libraries(cpr PUBLIC stdc++fs)
endif()
install(DIRECTORY cpr DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(DIRECTORY ${PROJECT_BINARY_DIR}/cpr_generated_includes/cpr DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

View file

@ -0,0 +1,36 @@
#ifndef CPR_ACCEPT_ENCODING_H
#define CPR_ACCEPT_ENCODING_H
#include <curl/curlver.h>
#include <initializer_list>
#include <map>
#include <string>
#include <vector>
namespace cpr {
enum class AcceptEncodingMethods {
identity,
deflate,
zlib,
gzip,
};
static const std::map<AcceptEncodingMethods, std::string> AcceptEncodingMethodsStringMap{{AcceptEncodingMethods::identity, "identity"}, {AcceptEncodingMethods::deflate, "deflate"}, {AcceptEncodingMethods::zlib, "zlib"}, {AcceptEncodingMethods::gzip, "gzip"}};
class AcceptEncoding {
public:
AcceptEncoding() = default;
AcceptEncoding(const std::initializer_list<AcceptEncodingMethods>& methods);
AcceptEncoding(const std::initializer_list<std::string>& methods);
bool empty() const noexcept;
const std::string getString() const;
private:
std::vector<std::string> methods_;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,321 @@
#ifndef CPR_API_H
#define CPR_API_H
#include <fstream>
#include <functional>
#include <future>
#include <string>
#include <utility>
#include "cpr/async.h"
#include "cpr/auth.h"
#include "cpr/bearer.h"
#include "cpr/cprtypes.h"
#include "cpr/multipart.h"
#include "cpr/multiperform.h"
#include "cpr/payload.h"
#include "cpr/response.h"
#include "cpr/session.h"
#include <cpr/filesystem.h>
#include <utility>
namespace cpr {
using AsyncResponse = std::future<Response>;
namespace priv {
template <bool processed_header, typename CurrentType>
void set_option_internal(Session& session, CurrentType&& current_option) {
session.SetOption(std::forward<CurrentType>(current_option));
}
template <>
inline void set_option_internal<true, Header>(Session& session, Header&& current_option) {
// Header option was already provided -> Update previous header
session.UpdateHeader(std::forward<Header>(current_option));
}
template <bool processed_header, typename CurrentType, typename... Ts>
void set_option_internal(Session& session, CurrentType&& current_option, Ts&&... ts) {
set_option_internal<processed_header, CurrentType>(session, std::forward<CurrentType>(current_option));
if (std::is_same<CurrentType, Header>::value) {
set_option_internal<true, Ts...>(session, std::forward<Ts>(ts)...);
} else {
set_option_internal<processed_header, Ts...>(session, std::forward<Ts>(ts)...);
}
}
template <typename... Ts>
void set_option(Session& session, Ts&&... ts) {
set_option_internal<false, Ts...>(session, std::forward<Ts>(ts)...);
}
// Idea: https://stackoverflow.com/a/19060157
template <typename Tuple, std::size_t... I>
void apply_set_option_internal(Session& session, Tuple&& t, std::index_sequence<I...>) {
set_option(session, std::get<I>(std::forward<Tuple>(t))...);
}
// Idea: https://stackoverflow.com/a/19060157
template <typename Tuple>
void apply_set_option(Session& session, Tuple&& t) {
using Indices = std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
apply_set_option_internal(session, std::forward<Tuple>(t), Indices());
}
template <typename T>
void setup_multiperform_internal(MultiPerform& multiperform, T&& t) {
std::shared_ptr<Session> session = std::make_shared<Session>();
apply_set_option(*session, t);
multiperform.AddSession(session);
}
template <typename T, typename... Ts>
void setup_multiperform_internal(MultiPerform& multiperform, T&& t, Ts&&... ts) {
std::shared_ptr<Session> session = std::make_shared<Session>();
apply_set_option(*session, t);
multiperform.AddSession(session);
setup_multiperform_internal<Ts...>(multiperform, std::forward<Ts>(ts)...);
}
template <typename... Ts>
void setup_multiperform(MultiPerform& multiperform, Ts&&... ts) {
setup_multiperform_internal<Ts...>(multiperform, std::forward<Ts>(ts)...);
}
} // namespace priv
// Get methods
template <typename... Ts>
Response Get(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Get();
}
// Get async methods
template <typename... Ts>
AsyncResponse GetAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Get(std::move(ts_inner)...); }, std::move(ts)...);
}
// Get callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto GetCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Get(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Post methods
template <typename... Ts>
Response Post(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Post();
}
// Post async methods
template <typename... Ts>
AsyncResponse PostAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Post(std::move(ts_inner)...); }, std::move(ts)...);
}
// Post callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto PostCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Post(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Put methods
template <typename... Ts>
Response Put(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Put();
}
// Put async methods
template <typename... Ts>
AsyncResponse PutAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Put(std::move(ts_inner)...); }, std::move(ts)...);
}
// Put callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto PutCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Put(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Head methods
template <typename... Ts>
Response Head(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Head();
}
// Head async methods
template <typename... Ts>
AsyncResponse HeadAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Head(std::move(ts_inner)...); }, std::move(ts)...);
}
// Head callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto HeadCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Head(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Delete methods
template <typename... Ts>
Response Delete(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Delete();
}
// Delete async methods
template <typename... Ts>
AsyncResponse DeleteAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Delete(std::move(ts_inner)...); }, std::move(ts)...);
}
// Delete callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto DeleteCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Delete(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Options methods
template <typename... Ts>
Response Options(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Options();
}
// Options async methods
template <typename... Ts>
AsyncResponse OptionsAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Options(std::move(ts_inner)...); }, std::move(ts)...);
}
// Options callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto OptionsCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Options(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Patch methods
template <typename... Ts>
Response Patch(Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Patch();
}
// Patch async methods
template <typename... Ts>
AsyncResponse PatchAsync(Ts... ts) {
return cpr::async([](Ts... ts_inner) { return Patch(std::move(ts_inner)...); }, std::move(ts)...);
}
// Patch callback methods
template <typename Then, typename... Ts>
// NOLINTNEXTLINE(fuchsia-trailing-return)
auto PatchCallback(Then then, Ts... ts) {
return cpr::async([](Then then_inner, Ts... ts_inner) { return then_inner(Patch(std::move(ts_inner)...)); }, std::move(then), std::move(ts)...);
}
// Download methods
template <typename... Ts>
Response Download(std::ofstream& file, Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Download(file);
}
// Download async method
template <typename... Ts>
AsyncResponse DownloadAsync(fs::path local_path, Ts... ts) {
return std::async(
std::launch::async,
[](fs::path local_path_, Ts... ts_) {
#ifdef CPR_USE_BOOST_FILESYSTEM
std::ofstream f(local_path_.string());
#else
std::ofstream f(local_path_);
#endif
return Download(f, std::move(ts_)...);
},
std::move(local_path), std::move(ts)...);
}
// Download with user callback
template <typename... Ts>
Response Download(const WriteCallback& write, Ts&&... ts) {
Session session;
priv::set_option(session, std::forward<Ts>(ts)...);
return session.Download(write);
}
// Multi requests
template <typename... Ts>
std::vector<Response> MultiGet(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Get();
}
template <typename... Ts>
std::vector<Response> MultiDelete(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Delete();
}
template <typename... Ts>
std::vector<Response> MultiPut(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Put();
}
template <typename... Ts>
std::vector<Response> MultiHead(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Head();
}
template <typename... Ts>
std::vector<Response> MultiOptions(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Options();
}
template <typename... Ts>
std::vector<Response> MultiPatch(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Patch();
}
template <typename... Ts>
std::vector<Response> MultiPost(Ts&&... ts) {
MultiPerform multiperform;
priv::setup_multiperform<Ts...>(multiperform, std::forward<Ts>(ts)...);
return multiperform.Post();
}
} // namespace cpr
#endif

View file

@ -0,0 +1,49 @@
#ifndef CPR_ASYNC_H
#define CPR_ASYNC_H
#include "singleton.h"
#include "threadpool.h"
namespace cpr {
class GlobalThreadPool : public ThreadPool {
CPR_SINGLETON_DECL(GlobalThreadPool)
protected:
GlobalThreadPool() = default;
public:
~GlobalThreadPool() override = default;
};
/**
* Return a future, calling future.get() will wait task done and return RetType.
* async(fn, args...)
* async(std::bind(&Class::mem_fn, &obj))
* async(std::mem_fn(&Class::mem_fn, &obj))
**/
template <class Fn, class... Args>
auto async(Fn&& fn, Args&&... args) {
return GlobalThreadPool::GetInstance()->Submit(std::forward<Fn>(fn), std::forward<Args>(args)...);
}
class async {
public:
static void startup(size_t min_threads = CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM, size_t max_threads = CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM, std::chrono::milliseconds max_idle_ms = CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME) {
GlobalThreadPool* gtp = GlobalThreadPool::GetInstance();
if (gtp->IsStarted()) {
return;
}
gtp->SetMinThreadNum(min_threads);
gtp->SetMaxThreadNum(max_threads);
gtp->SetMaxIdleTime(max_idle_ms);
gtp->Start();
}
static void cleanup() {
GlobalThreadPool::ExitInstance();
}
};
} // namespace cpr
#endif

View file

@ -0,0 +1,33 @@
#ifndef CPR_AUTH_H
#define CPR_AUTH_H
#include <string>
#include <utility>
namespace cpr {
enum class AuthMode { BASIC, DIGEST, NTLM };
class Authentication {
public:
Authentication(const std::string& username, const std::string& password, const AuthMode& auth_mode) : auth_string_{username + ":" + password}, auth_mode_{auth_mode} {}
Authentication(std::string&& username, std::string&& password, AuthMode&& auth_mode) : auth_string_{std::move(username) + ":" + std::move(password)}, auth_mode_{std::move(auth_mode)} {}
Authentication(const Authentication& other) = default;
Authentication(Authentication&& old) noexcept = default;
~Authentication() noexcept;
Authentication& operator=(Authentication&& old) noexcept = default;
Authentication& operator=(const Authentication& other) = default;
const char* GetAuthString() const noexcept;
AuthMode GetAuthMode() const noexcept;
private:
std::string auth_string_;
AuthMode auth_mode_;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,36 @@
#ifndef CPR_BEARER_H
#define CPR_BEARER_H
#include <curl/curlver.h>
#include <string>
#include <utility>
namespace cpr {
// Only supported with libcurl >= 7.61.0.
// As an alternative use SetHeader and add the token manually.
#if LIBCURL_VERSION_NUM >= 0x073D00
class Bearer {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Bearer(const std::string& token) : token_string_{token} {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Bearer(std::string&& token) : token_string_{std::move(token)} {}
Bearer(const Bearer& other) = default;
Bearer(Bearer&& old) noexcept = default;
virtual ~Bearer() noexcept;
Bearer& operator=(Bearer&& old) noexcept = default;
Bearer& operator=(const Bearer& other) = default;
virtual const char* GetToken() const noexcept;
protected:
std::string token_string_;
};
#endif
} // namespace cpr
#endif

View file

@ -0,0 +1,54 @@
#ifndef CPR_BODY_H
#define CPR_BODY_H
#include <exception>
#include <initializer_list>
#include <string>
#include <vector>
#include <fstream>
#include "cpr/buffer.h"
#include "cpr/cprtypes.h"
#include "cpr/file.h"
namespace cpr {
class Body : public StringHolder<Body> {
public:
Body() : StringHolder<Body>() {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Body(const std::string& body) : StringHolder<Body>(body) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Body(std::string&& body) : StringHolder<Body>(std::move(body)) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Body(std::string_view body) : StringHolder<Body>(body) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Body(const char* body) : StringHolder<Body>(body) {}
Body(const char* str, size_t len) : StringHolder<Body>(str, len) {}
Body(const std::initializer_list<std::string> args) : StringHolder<Body>(args) {}
Body(const Buffer& buffer) : StringHolder<Body>(reinterpret_cast<const char*>(buffer.data), static_cast<size_t>(buffer.datalen)) {}
Body(const File& file) {
std::ifstream is(file.filepath, std::ifstream::binary);
if (!is) {
throw std::invalid_argument("Can't open the file for HTTP request body!");
}
is.seekg(0, std::ios::end);
const std::streampos length = is.tellg();
is.seekg(0, std::ios::beg);
std::string buffer;
buffer.resize(static_cast<size_t>(length));
is.read(&buffer[0], length);
str_ = std::move(buffer);
}
Body(const Body& other) = default;
Body(Body&& old) noexcept = default;
~Body() override = default;
Body& operator=(Body&& old) noexcept = default;
Body& operator=(const Body& other) = default;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,35 @@
#ifndef CPR_BUFFER_H
#define CPR_BUFFER_H
#include <string>
#include <cpr/filesystem.h>
namespace cpr {
struct Buffer {
using data_t = const unsigned char*;
template <typename Iterator>
Buffer(Iterator begin, Iterator end, fs::path&& p_filename)
// Ignored here since libcurl reqires a long.
// There is also no way around the reinterpret_cast.
// NOLINTNEXTLINE(google-runtime-int, cppcoreguidelines-pro-type-reinterpret-cast)
: data{reinterpret_cast<data_t>(&(*begin))}, datalen{static_cast<long>(std::distance(begin, end))}, filename(std::move(p_filename)) {
is_random_access_iterator(begin, end);
static_assert(sizeof(*begin) == 1, "Only byte buffers can be used");
}
template <typename Iterator>
typename std::enable_if<std::is_same<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>::value>::type is_random_access_iterator(Iterator /* begin */, Iterator /* end */) {}
data_t data;
// Ignored here since libcurl reqires a long:
// NOLINTNEXTLINE(google-runtime-int)
long datalen;
const fs::path filename;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,89 @@
#ifndef CPR_CALLBACK_H
#define CPR_CALLBACK_H
#include "cprtypes.h"
#include <functional>
#include <utility>
namespace cpr {
class ReadCallback {
public:
ReadCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ReadCallback(std::function<bool(char* buffer, size_t& size, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{-1}, callback{std::move(p_callback)} {}
ReadCallback(cpr_off_t p_size, std::function<bool(char* buffer, size_t& size, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), size{p_size}, callback{std::move(p_callback)} {}
bool operator()(char* buffer, size_t& buffer_size) const {
return callback(buffer, buffer_size, userdata);
}
intptr_t userdata;
cpr_off_t size{};
std::function<bool(char* buffer, size_t& size, intptr_t userdata)> callback;
};
class HeaderCallback {
public:
HeaderCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
HeaderCallback(std::function<bool(std::string header, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}
bool operator()(std::string header) const {
return callback(std::move(header), userdata);
}
intptr_t userdata;
std::function<bool(std::string header, intptr_t userdata)> callback;
};
class WriteCallback {
public:
WriteCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
WriteCallback(std::function<bool(std::string data, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}
bool operator()(std::string data) const {
return callback(std::move(data), userdata);
}
intptr_t userdata;
std::function<bool(std::string data, intptr_t userdata)> callback;
};
class ProgressCallback {
public:
ProgressCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ProgressCallback(std::function<bool(cpr_off_t downloadTotal, cpr_off_t downloadNow, cpr_off_t uploadTotal, cpr_off_t uploadNow, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}
bool operator()(cpr_off_t downloadTotal, cpr_off_t downloadNow, cpr_off_t uploadTotal, cpr_off_t uploadNow) const {
return callback(downloadTotal, downloadNow, uploadTotal, uploadNow, userdata);
}
intptr_t userdata;
std::function<bool(cpr_off_t downloadTotal, cpr_off_t downloadNow, cpr_off_t uploadTotal, cpr_off_t uploadNow, intptr_t userdata)> callback;
};
class DebugCallback {
public:
enum class InfoType {
TEXT = 0,
HEADER_IN = 1,
HEADER_OUT = 2,
DATA_IN = 3,
DATA_OUT = 4,
SSL_DATA_IN = 5,
SSL_DATA_OUT = 6,
};
DebugCallback() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
DebugCallback(std::function<void(InfoType type, std::string data, intptr_t userdata)> p_callback, intptr_t p_userdata = 0) : userdata(p_userdata), callback(std::move(p_callback)) {}
void operator()(InfoType type, std::string data) const {
callback(type, std::move(data), userdata);
}
intptr_t userdata;
std::function<void(InfoType type, std::string data, intptr_t userdata)> callback;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,35 @@
#ifndef CPR_CERT_INFO_H
#define CPR_CERT_INFO_H
#include <initializer_list>
#include <string>
#include <vector>
namespace cpr {
class CertInfo {
private:
std::vector<std::string> cert_info_;
public:
CertInfo() = default;
CertInfo(const std::initializer_list<std::string>& entry) : cert_info_{entry} {};
~CertInfo() noexcept = default;
using iterator = std::vector<std::string>::iterator;
using const_iterator = std::vector<std::string>::const_iterator;
std::string& operator[](const size_t& pos);
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
void emplace_back(const std::string& str);
void push_back(const std::string& str);
void pop_back();
};
} // namespace cpr
#endif

View file

@ -0,0 +1,18 @@
#ifndef CPR_CONNECT_TIMEOUT_H
#define CPR_CONNECT_TIMEOUT_H
#include "cpr/timeout.h"
namespace cpr {
class ConnectTimeout : public Timeout {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ConnectTimeout(const std::chrono::milliseconds& duration) : Timeout{duration} {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ConnectTimeout(const std::int32_t& milliseconds) : Timeout{milliseconds} {}
};
} // namespace cpr
#endif

View file

@ -0,0 +1,92 @@
#ifndef CPR_COOKIES_H
#define CPR_COOKIES_H
#include "cpr/curlholder.h"
#include <chrono>
#include <initializer_list>
#include <sstream>
#include <string>
#include <vector>
namespace cpr {
/**
* EXPIRES_STRING_SIZE is an explicitly static and const variable that could be only accessed within the same namespace and is immutable.
* To be used for "std::array", the expression must have a constant value, so EXPIRES_STRING_SIZE must be a const value.
**/
static const std::size_t EXPIRES_STRING_SIZE = 100;
class Cookie {
public:
Cookie() = default;
/**
* Some notes for the default value used by expires:
* std::chrono::system_clock::time_point::min() won't work on Windows due to the min, max clash there.
* So we fall back to std::chrono::system_clock::from_time_t(0) for the minimum value here.
**/
Cookie(const std::string& name, const std::string& value, const std::string& domain = "", bool p_isIncludingSubdomains = false, const std::string& path = "/", bool p_isHttpsOnly = false, std::chrono::system_clock::time_point expires = std::chrono::system_clock::from_time_t(0)) : name_{name}, value_{value}, domain_{domain}, includeSubdomains_{p_isIncludingSubdomains}, path_{path}, httpsOnly_{p_isHttpsOnly}, expires_{expires} {};
const std::string GetDomain() const;
bool IsIncludingSubdomains() const;
const std::string GetPath() const;
bool IsHttpsOnly() const;
const std::chrono::system_clock::time_point GetExpires() const;
const std::string GetExpiresString() const;
const std::string GetName() const;
const std::string GetValue() const;
private:
std::string name_;
std::string value_;
std::string domain_;
bool includeSubdomains_{};
std::string path_;
bool httpsOnly_{};
/**
* TODO: Update the implementation using `std::chrono::utc_clock` of C++20
**/
std::chrono::system_clock::time_point expires_{};
};
class Cookies {
public:
/**
* Should we URL-encode cookies when making a request.
* Based on RFC6265, it is recommended but not mandatory to encode cookies.
*
* -------
* To maximize compatibility with user agents, servers that wish to
* store arbitrary data in a cookie-value SHOULD encode that data, for
* example, using Base64 [RFC4648].
* -------
* Source: RFC6265 (https://www.ietf.org/rfc/rfc6265.txt)
**/
bool encode{true};
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Cookies(bool p_encode = true) : encode{p_encode} {};
Cookies(const std::initializer_list<cpr::Cookie>& cookies, bool p_encode = true) : encode{p_encode}, cookies_{cookies} {};
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Cookies(const cpr::Cookie& cookie, bool p_encode = true) : encode{p_encode}, cookies_{cookie} {};
cpr::Cookie& operator[](size_t pos);
const std::string GetEncoded(const CurlHolder& holder) const;
using iterator = std::vector<cpr::Cookie>::iterator;
using const_iterator = std::vector<cpr::Cookie>::const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
void emplace_back(const Cookie& str);
void push_back(const Cookie& str);
void pop_back();
private:
std::vector<cpr::Cookie> cookies_;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,46 @@
#ifndef CPR_CPR_H
#define CPR_CPR_H
#include "cpr/api.h"
#include "cpr/auth.h"
#include "cpr/bearer.h"
#include "cpr/callback.h"
#include "cpr/cert_info.h"
#include "cpr/connect_timeout.h"
#include "cpr/cookies.h"
#include "cpr/cprtypes.h"
#include "cpr/cprver.h"
#include "cpr/curl_container.h"
#include "cpr/curlholder.h"
#include "cpr/error.h"
#include "cpr/http_version.h"
#include "cpr/interceptor.h"
#include "cpr/interface.h"
#include "cpr/limit_rate.h"
#include "cpr/local_port.h"
#include "cpr/local_port_range.h"
#include "cpr/low_speed.h"
#include "cpr/multipart.h"
#include "cpr/multiperform.h"
#include "cpr/parameters.h"
#include "cpr/payload.h"
#include "cpr/proxies.h"
#include "cpr/proxyauth.h"
#include "cpr/range.h"
#include "cpr/redirect.h"
#include "cpr/reserve_size.h"
#include "cpr/resolve.h"
#include "cpr/response.h"
#include "cpr/session.h"
#include "cpr/ssl_ctx.h"
#include "cpr/ssl_options.h"
#include "cpr/status_codes.h"
#include "cpr/timeout.h"
#include "cpr/unix_socket.h"
#include "cpr/user_agent.h"
#include "cpr/util.h"
#include "cpr/verbose.h"
#define CPR_LIBCURL_VERSION_NUM LIBCURL_VERSION_NUM
#endif

View file

@ -0,0 +1,137 @@
#ifndef CPR_CPR_TYPES_H
#define CPR_CPR_TYPES_H
#include <curl/system.h>
#include <initializer_list>
#include <map>
#include <memory>
#include <numeric>
#include <string>
#include <string_view>
namespace cpr {
/**
* Wrapper around "curl_off_t" to prevent applications from having to link against libcurl.
**/
using cpr_off_t = curl_off_t;
template <class T>
class StringHolder {
public:
StringHolder() = default;
explicit StringHolder(const std::string& str) : str_(str) {}
explicit StringHolder(std::string&& str) : str_(std::move(str)) {}
explicit StringHolder(std::string_view str) : str_(str) {}
explicit StringHolder(const char* str) : str_(str) {}
StringHolder(const char* str, size_t len) : str_(str, len) {}
StringHolder(const std::initializer_list<std::string> args) {
str_ = std::accumulate(args.begin(), args.end(), str_);
}
StringHolder(const StringHolder& other) = default;
StringHolder(StringHolder&& old) noexcept = default;
virtual ~StringHolder() = default;
StringHolder& operator=(StringHolder&& old) noexcept = default;
StringHolder& operator=(const StringHolder& other) = default;
explicit operator std::string() const {
return str_;
}
T operator+(const char* rhs) const {
return T(str_ + rhs);
}
T operator+(const std::string& rhs) const {
return T(str_ + rhs);
}
T operator+(const StringHolder<T>& rhs) const {
return T(str_ + rhs.str_);
}
void operator+=(const char* rhs) {
str_ += rhs;
}
void operator+=(const std::string& rhs) {
str_ += rhs;
}
void operator+=(const StringHolder<T>& rhs) {
str_ += rhs;
}
bool operator==(const char* rhs) const {
return str_ == rhs;
}
bool operator==(const std::string& rhs) const {
return str_ == rhs;
}
bool operator==(const StringHolder<T>& rhs) const {
return str_ == rhs.str_;
}
bool operator!=(const char* rhs) const {
return str_.c_str() != rhs;
}
bool operator!=(const std::string& rhs) const {
return str_ != rhs;
}
bool operator!=(const StringHolder<T>& rhs) const {
return str_ != rhs.str_;
}
const std::string& str() {
return str_;
}
const std::string& str() const {
return str_;
}
const char* c_str() const {
return str_.c_str();
}
const char* data() const {
return str_.data();
}
protected:
std::string str_{};
};
template <class T>
std::ostream& operator<<(std::ostream& os, const StringHolder<T>& s) {
os << s.str();
return os;
}
class Url : public StringHolder<Url> {
public:
Url() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Url(const std::string& url) : StringHolder<Url>(url) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Url(std::string&& url) : StringHolder<Url>(std::move(url)) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Url(std::string_view url) : StringHolder<Url>(url) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Url(const char* url) : StringHolder<Url>(url) {}
Url(const char* str, size_t len) : StringHolder<Url>(std::string(str, len)) {}
Url(const std::initializer_list<std::string> args) : StringHolder<Url>(args) {}
Url(const Url& other) = default;
Url(Url&& old) noexcept = default;
~Url() override = default;
Url& operator=(Url&& old) noexcept = default;
Url& operator=(const Url& other) = default;
};
struct CaseInsensitiveCompare {
bool operator()(const std::string& a, const std::string& b) const noexcept;
};
using Header = std::map<std::string, std::string, CaseInsensitiveCompare>;
} // namespace cpr
#endif

View file

@ -0,0 +1,53 @@
#ifndef CURL_CONTAINER_H
#define CURL_CONTAINER_H
#include <initializer_list>
#include <memory>
#include <string>
#include <vector>
#include "cpr/curlholder.h"
namespace cpr {
struct Parameter {
Parameter(const std::string& p_key, const std::string& p_value) : key{p_key}, value{p_value} {}
Parameter(std::string&& p_key, std::string&& p_value) : key{std::move(p_key)}, value{std::move(p_value)} {}
std::string key;
std::string value;
};
struct Pair {
Pair(const std::string& p_key, const std::string& p_value) : key(p_key), value(p_value) {}
Pair(std::string&& p_key, std::string&& p_value) : key(std::move(p_key)), value(std::move(p_value)) {}
std::string key;
std::string value;
};
template <class T>
class CurlContainer {
public:
/**
* Enables or disables URL encoding for keys and values when calling GetContent(...).
**/
bool encode = true;
CurlContainer() = default;
CurlContainer(const std::initializer_list<T>&);
void Add(const std::initializer_list<T>&);
void Add(const T&);
const std::string GetContent(const CurlHolder&) const;
protected:
std::vector<T> containerList_;
};
} // namespace cpr
#endif //

View file

@ -0,0 +1,54 @@
#ifndef CPR_CURL_HOLDER_H
#define CPR_CURL_HOLDER_H
#include <array>
#include <mutex>
#include <string>
#include <curl/curl.h>
namespace cpr {
struct CurlHolder {
private:
/**
* Mutex for curl_easy_init().
* curl_easy_init() is not thread save.
* References:
* https://curl.haxx.se/libcurl/c/curl_easy_init.html
* https://curl.haxx.se/libcurl/c/threadsafe.html
**/
// Avoids initalization order problems in a static build
static std::mutex& curl_easy_init_mutex_() {
static std::mutex curl_easy_init_mutex_;
return curl_easy_init_mutex_;
}
public:
CURL* handle{nullptr};
struct curl_slist* chunk{nullptr};
struct curl_slist* resolveCurlList{nullptr};
struct curl_httppost* formpost{nullptr};
std::array<char, CURL_ERROR_SIZE> error{};
CurlHolder();
CurlHolder(const CurlHolder& other) = default;
CurlHolder(CurlHolder&& old) noexcept = default;
~CurlHolder();
CurlHolder& operator=(CurlHolder&& old) noexcept = default;
CurlHolder& operator=(const CurlHolder& other) = default;
/**
* Uses curl_easy_escape(...) for escaping the given string.
**/
std::string urlEncode(const std::string& s) const;
/**
* Uses curl_easy_unescape(...) for unescaping the given string.
**/
std::string urlDecode(const std::string& s) const;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,18 @@
#ifndef CPR_CURLMULTIHOLDER_H
#define CPR_CURLMULTIHOLDER_H
#include <curl/curl.h>
namespace cpr {
class CurlMultiHolder {
public:
CurlMultiHolder();
~CurlMultiHolder();
CURLM* handle{nullptr};
};
} // namespace cpr
#endif

View file

@ -0,0 +1,53 @@
#ifndef CPR_ERROR_H
#define CPR_ERROR_H
#include <cstdint>
#include <string>
#include "cpr/cprtypes.h"
#include <utility>
namespace cpr {
enum class ErrorCode {
OK = 0,
CONNECTION_FAILURE,
EMPTY_RESPONSE,
HOST_RESOLUTION_FAILURE,
INTERNAL_ERROR,
INVALID_URL_FORMAT,
NETWORK_RECEIVE_ERROR,
NETWORK_SEND_FAILURE,
OPERATION_TIMEDOUT,
PROXY_RESOLUTION_FAILURE,
SSL_CONNECT_ERROR,
SSL_LOCAL_CERTIFICATE_ERROR,
SSL_REMOTE_CERTIFICATE_ERROR,
SSL_CACERT_ERROR,
GENERIC_SSL_ERROR,
UNSUPPORTED_PROTOCOL,
REQUEST_CANCELLED,
TOO_MANY_REDIRECTS,
UNKNOWN_ERROR = 1000,
};
class Error {
public:
ErrorCode code = ErrorCode::OK;
std::string message{};
Error() = default;
Error(const std::int32_t& curl_code, std::string&& p_error_message) : code{getErrorCodeForCurlError(curl_code)}, message(std::move(p_error_message)) {}
explicit operator bool() const {
return code != ErrorCode::OK;
}
private:
static ErrorCode getErrorCodeForCurlError(std::int32_t curl_code);
};
} // namespace cpr
#endif

View file

@ -0,0 +1,55 @@
#ifndef CPR_FILE_H
#define CPR_FILE_H
#include <initializer_list>
#include <string>
#include <vector>
#include <cpr/filesystem.h>
namespace cpr {
struct File {
explicit File(std::string&& p_filepath, const std::string& p_overrided_filename = {}) : filepath(std::move(p_filepath)), overrided_filename(p_overrided_filename) {}
explicit File(const std::string& p_filepath, const std::string& p_overrided_filename = {}) : filepath(p_filepath), overrided_filename(p_overrided_filename) {}
const std::string filepath;
const std::string overrided_filename;
bool hasOverridedFilename() const noexcept {
return !overrided_filename.empty();
};
};
class Files {
public:
Files() = default;
Files(const File& p_file) : files{p_file} {};
Files(const std::initializer_list<File>& p_files) : files{p_files} {};
Files(const std::initializer_list<std::string>& p_filepaths) {
for (const std::string& filepath : p_filepaths) {
files.emplace_back(File(filepath));
}
};
~Files() noexcept = default;
using iterator = std::vector<File>::iterator;
using const_iterator = std::vector<File>::const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
void emplace_back(const File& file);
void push_back(const File& file);
void pop_back();
private:
std::vector<File> files;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,19 @@
#ifndef CPR_FILESYSTEM_H
#define CPR_FILESYSTEM_H
// Include filesystem into the namespace "fs" from either "filesystem" or "experimental/filesystem" or "boost/filesystem"
#if __has_include(<filesystem>)
#include <filesystem>
namespace fs = std::filesystem;
#elif __has_include("experimental/filesystem")
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
//cppcheck-suppress preprocessorErrorDirective
#elif defined(CPR_USE_BOOST_FILESYSTEM) && __has_include(<boost/filesystem.hpp>)
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
#error "Failed to include <filesystem> header!"
#endif
#endif

View file

@ -0,0 +1,67 @@
#ifndef CPR_HTTP_VERSION_H
#define CPR_HTTP_VERSION_H
#include <curl/curlver.h>
namespace cpr {
enum class HttpVersionCode {
/**
* Let libcurl decide which version is the best.
**/
VERSION_NONE,
/**
* Enforce HTTP 1.0 requests.
**/
VERSION_1_0,
/**
* Enforce HTTP 1.1 requests.
**/
VERSION_1_1,
#if LIBCURL_VERSION_NUM >= 0x072100 // 7.33.0
/**
* Attempt HTTP 2.0 requests.
* Fallback to HTTP 1.1 if negotiation fails.
**/
VERSION_2_0,
#endif
#if LIBCURL_VERSION_NUM >= 0x072F00 // 7.47.0
/**
* Attempt HTTP 2.0 for HTTPS requests only.
* Fallback to HTTP 1.1 if negotiation fails.
* HTTP 1.1 will be used for HTTP connections.
**/
VERSION_2_0_TLS,
#endif
#if LIBCURL_VERSION_NUM >= 0x073100 // 7.49.0
/**
* Start HTTP 2.0 for HTTP requests.
* Requires prior knowledge that the server supports HTTP 2.0.
* For HTTPS requests we will negotiate the protocol version in the TLS handshake.
**/
VERSION_2_0_PRIOR_KNOWLEDGE,
#endif
#if LIBCURL_VERSION_NUM >= 0x074200 // 7.66.0
/**
* Attempt HTTP 3.0 requests.
* Requires prior knowledge that the server supports HTTP 3.0 since there is no gracefully downgrade.
* Fallback to HTTP 1.1 if negotiation fails.
**/
VERSION_3_0
#endif
};
class HttpVersion {
public:
/**
* The HTTP version that should be used by libcurl when initiating a HTTP(S) connection.
* Default: HttpVersionCode::VERSION_NONE
**/
HttpVersionCode code = HttpVersionCode::VERSION_NONE;
HttpVersion() = default;
explicit HttpVersion(HttpVersionCode _code) : code(_code) {}
};
} // namespace cpr
#endif

View file

@ -0,0 +1,36 @@
#ifndef CPR_INTERCEPTOR_H
#define CPR_INTERCEPTOR_H
#include "cpr/response.h"
#include "cpr/session.h"
namespace cpr {
class Interceptor {
public:
enum class ProceedHttpMethod {
GET_REQUEST = 0,
POST_REQUEST,
PUT_REQUEST,
DELETE_REQUEST,
PATCH_REQUEST,
HEAD_REQUEST,
OPTIONS_REQUEST,
DOWNLOAD_CALLBACK_REQUEST,
DOWNLOAD_FILE_REQUEST,
};
virtual ~Interceptor() = default;
virtual Response intercept(Session& session) = 0;
protected:
static Response proceed(Session& session);
static Response proceed(Session& session, ProceedHttpMethod httpMethod);
static Response proceed(Session& session, ProceedHttpMethod httpMethod, std::ofstream& file);
static Response proceed(Session& session, ProceedHttpMethod httpMethod, const WriteCallback& write);
};
} // namespace cpr
#endif

View file

@ -0,0 +1,34 @@
#ifndef CPR_INTERFACE_H
#define CPR_INTERFACE_H
#include <initializer_list>
#include <string>
#include "cpr/cprtypes.h"
namespace cpr {
class Interface : public StringHolder<Interface> {
public:
Interface() : StringHolder<Interface>() {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Interface(const std::string& iface) : StringHolder<Interface>(iface) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Interface(std::string&& iface) : StringHolder<Interface>(std::move(iface)) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Interface(std::string_view iface) : StringHolder<Interface>(iface) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Interface(const char* iface) : StringHolder<Interface>(iface) {}
Interface(const char* str, size_t len) : StringHolder<Interface>(str, len) {}
Interface(const std::initializer_list<std::string> args) : StringHolder<Interface>(args) {}
Interface(const Interface& other) = default;
Interface(Interface&& old) noexcept = default;
~Interface() override = default;
Interface& operator=(Interface&& old) noexcept = default;
Interface& operator=(const Interface& other) = default;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,18 @@
#ifndef CPR_SPEED_LIMIT_H
#define CPR_SPEED_LIMIT_H
#include <cstdint>
namespace cpr {
class LimitRate {
public:
LimitRate(const std::int64_t p_downrate, const std::int64_t p_uprate) : downrate(p_downrate), uprate(p_uprate) {}
std::int64_t downrate = 0;
std::int64_t uprate = 0;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,23 @@
#ifndef CPR_LOCAL_PORT_H
#define CPR_LOCAL_PORT_H
#include <cstdint>
namespace cpr {
class LocalPort {
public:
// NOLINTNEXTLINE(google-explicit-constructor)
LocalPort(const std::uint16_t p_localport) : localport_(p_localport) {}
operator std::uint16_t() const {
return localport_;
}
private:
std::uint16_t localport_ = 0;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,23 @@
#ifndef CPR_LOCAL_PORT_RANGE_H
#define CPR_LOCAL_PORT_RANGE_H
#include <cstdint>
namespace cpr {
class LocalPortRange {
public:
// NOLINTNEXTLINE(google-explicit-constructor)
LocalPortRange(const std::uint16_t p_localportrange) : localportrange_(p_localportrange) {}
operator std::uint16_t() const {
return localportrange_;
}
private:
std::uint16_t localportrange_ = 0;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,18 @@
#ifndef CPR_LOW_SPEED_H
#define CPR_LOW_SPEED_H
#include <cstdint>
namespace cpr {
class LowSpeed {
public:
LowSpeed(const std::int32_t p_limit, const std::int32_t p_time) : limit(p_limit), time(p_time) {}
std::int32_t limit;
std::int32_t time;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,45 @@
#ifndef CPR_MULTIPART_H
#define CPR_MULTIPART_H
#include <cstdint>
#include <initializer_list>
#include <string>
#include <type_traits>
#include <vector>
#include "buffer.h"
#include "file.h"
namespace cpr {
struct Part {
Part(const std::string& p_name, const std::string& p_value, const std::string& p_content_type = {}) : name{p_name}, value{p_value}, content_type{p_content_type}, is_file{false}, is_buffer{false} {}
Part(const std::string& p_name, const std::int32_t& p_value, const std::string& p_content_type = {}) : name{p_name}, value{std::to_string(p_value)}, content_type{p_content_type}, is_file{false}, is_buffer{false} {}
Part(const std::string& p_name, const Files& p_files, const std::string& p_content_type = {}) : name{p_name}, value{}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{p_files} {}
Part(const std::string& p_name, Files&& p_files, const std::string& p_content_type = {}) : name{p_name}, value{}, content_type{p_content_type}, is_file{true}, is_buffer{false}, files{std::move(p_files)} {}
Part(const std::string& p_name, const Buffer& buffer, const std::string& p_content_type = {}) : name{p_name}, value{buffer.filename.string()}, content_type{p_content_type}, data{buffer.data}, datalen{buffer.datalen}, is_file{false}, is_buffer{true} {}
std::string name;
// We don't use fs::path here, as this leads to problems using windows
std::string value;
std::string content_type;
Buffer::data_t data{nullptr};
// Ignored here since libcurl reqires a long:
// NOLINTNEXTLINE(google-runtime-int)
long datalen{0};
bool is_file;
bool is_buffer;
Files files;
};
class Multipart {
public:
Multipart(const std::initializer_list<Part>& parts);
std::vector<Part> parts;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,119 @@
#ifndef CPR_MULTIPERFORM_H
#define CPR_MULTIPERFORM_H
#include <functional>
#include <memory>
#include <stdexcept>
#include <vector>
#include "cpr/curlmultiholder.h"
#include "cpr/response.h"
#include "cpr/session.h"
namespace cpr {
class MultiPerform {
public:
enum class HttpMethod {
UNDEFINED = 0,
GET_REQUEST,
POST_REQUEST,
PUT_REQUEST,
DELETE_REQUEST,
PATCH_REQUEST,
HEAD_REQUEST,
OPTIONS_REQUEST,
DOWNLOAD_REQUEST,
};
MultiPerform();
~MultiPerform();
std::vector<Response> Get();
std::vector<Response> Delete();
template <typename... DownloadArgTypes>
std::vector<Response> Download(DownloadArgTypes... args);
std::vector<Response> Put();
std::vector<Response> Head();
std::vector<Response> Options();
std::vector<Response> Patch();
std::vector<Response> Post();
std::vector<Response> Perform();
template <typename... DownloadArgTypes>
std::vector<Response> PerformDownload(DownloadArgTypes... args);
void AddSession(std::shared_ptr<Session>& session, HttpMethod method = HttpMethod::UNDEFINED);
void RemoveSession(const std::shared_ptr<Session>& session);
private:
void SetHttpMethod(HttpMethod method);
void PrepareSessions();
template <typename CurrentDownloadArgType, typename... DownloadArgTypes>
void PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args);
template <typename CurrentDownloadArgType>
void PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg);
void PrepareDownloadSession(size_t sessions_index, std::ofstream& file);
void PrepareDownloadSession(size_t sessions_index, const WriteCallback& write);
void PrepareGet();
void PrepareDelete();
void PreparePut();
void PreparePatch();
void PrepareHead();
void PrepareOptions();
void PreparePost();
template <typename... DownloadArgTypes>
void PrepareDownload(DownloadArgTypes... args);
std::vector<Response> MakeRequest();
std::vector<Response> MakeDownloadRequest();
void DoMultiPerform();
std::vector<Response> ReadMultiInfo(std::function<Response(Session&, CURLcode)>&& complete_function);
std::vector<std::pair<std::shared_ptr<Session>, HttpMethod>> sessions_;
std::unique_ptr<CurlMultiHolder> multicurl_;
bool is_download_multi_perform{false};
};
template <typename CurrentDownloadArgType>
void MultiPerform::PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg) {
PrepareDownloadSession(sessions_index, current_arg);
}
template <typename CurrentDownloadArgType, typename... DownloadArgTypes>
void MultiPerform::PrepareDownloadSessions(size_t sessions_index, CurrentDownloadArgType current_arg, DownloadArgTypes... args) {
PrepareDownloadSession(sessions_index, current_arg);
PrepareDownloadSessions<DownloadArgTypes...>(sessions_index + 1, args...);
}
template <typename... DownloadArgTypes>
void MultiPerform::PrepareDownload(DownloadArgTypes... args) {
SetHttpMethod(HttpMethod::DOWNLOAD_REQUEST);
PrepareDownloadSessions<DownloadArgTypes...>(0, args...);
}
template <typename... DownloadArgTypes>
std::vector<Response> MultiPerform::Download(DownloadArgTypes... args) {
if (sizeof...(args) != sessions_.size()) {
throw std::invalid_argument("Number of download arguments has to match the number of sessions added to the multiperform!");
}
PrepareDownload(args...);
return MakeDownloadRequest();
}
template <typename... DownloadArgTypes>
std::vector<Response> MultiPerform::PerformDownload(DownloadArgTypes... args) {
if (sizeof...(args) != sessions_.size()) {
throw std::invalid_argument("Number of download arguments has to match the number of sessions added to the multiperform!");
}
PrepareDownloadSessions<DownloadArgTypes...>(0, args...);
return MakeDownloadRequest();
}
} // namespace cpr
#endif

View file

@ -0,0 +1,18 @@
#ifndef CPR_PARAMETERS_H
#define CPR_PARAMETERS_H
#include <initializer_list>
#include "cpr/curl_container.h"
namespace cpr {
class Parameters : public CurlContainer<Parameter> {
public:
Parameters() = default;
Parameters(const std::initializer_list<Parameter>& parameters);
};
} // namespace cpr
#endif

View file

@ -0,0 +1,23 @@
#ifndef CPR_PAYLOAD_H
#define CPR_PAYLOAD_H
#include <initializer_list>
#include "cpr/curl_container.h"
namespace cpr {
class Payload : public CurlContainer<Pair> {
public:
template <class It>
Payload(const It begin, const It end) {
for (It pair = begin; pair != end; ++pair) {
Add(*pair);
}
}
Payload(const std::initializer_list<Pair>& pairs);
};
} // namespace cpr
#endif

View file

@ -0,0 +1,23 @@
#ifndef CPR_PROXIES_H
#define CPR_PROXIES_H
#include <initializer_list>
#include <map>
#include <string>
namespace cpr {
class Proxies {
public:
Proxies() = default;
Proxies(const std::initializer_list<std::pair<const std::string, std::string>>& hosts);
Proxies(const std::map<std::string, std::string>& hosts);
bool has(const std::string& protocol) const;
const std::string& operator[](const std::string& protocol);
private:
std::map<std::string, std::string> hosts_;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,44 @@
#ifndef CPR_PROXYAUTH_H
#define CPR_PROXYAUTH_H
#include <initializer_list>
#include <map>
#include "cpr/auth.h"
#include "cpr/util.h"
namespace cpr {
class EncodedAuthentication {
public:
EncodedAuthentication() : auth_string_{""} {}
EncodedAuthentication(const std::string& username, const std::string& password) : auth_string_{cpr::util::urlEncode(username) + ":" + cpr::util::urlEncode(password)} {}
EncodedAuthentication(std::string&& username, std::string&& password) : auth_string_{cpr::util::urlEncode(std::move(username)) + ":" + cpr::util::urlEncode(std::move(password))} {}
EncodedAuthentication(const EncodedAuthentication& other) = default;
EncodedAuthentication(EncodedAuthentication&& old) noexcept = default;
virtual ~EncodedAuthentication() noexcept;
EncodedAuthentication& operator=(EncodedAuthentication&& old) noexcept = default;
EncodedAuthentication& operator=(const EncodedAuthentication& other) = default;
const char* GetAuthString() const noexcept;
protected:
std::string auth_string_;
};
class ProxyAuthentication {
public:
ProxyAuthentication() = default;
ProxyAuthentication(const std::initializer_list<std::pair<const std::string, EncodedAuthentication>>& auths) : proxyAuth_{auths} {}
ProxyAuthentication(const std::map<std::string, EncodedAuthentication>& auths) : proxyAuth_{auths} {}
bool has(const std::string& protocol) const;
const char* operator[](const std::string& protocol);
private:
std::map<std::string, EncodedAuthentication> proxyAuth_;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,44 @@
#ifndef CPR_RANGE_H
#define CPR_RANGE_H
#include <cstdint>
#include <optional>
namespace cpr {
class Range {
public:
Range(const std::optional<std::int64_t> p_resume_from = std::nullopt, const std::optional<std::int64_t> p_finish_at = std::nullopt) {
resume_from = p_resume_from.value_or(0);
finish_at = p_finish_at.value_or(-1);
}
std::int64_t resume_from;
std::int64_t finish_at;
const std::string str() const {
std::string from_str = (resume_from < 0U) ? "" : std::to_string(resume_from);
std::string to_str = (finish_at < 0U) ? "" : std::to_string(finish_at);
return from_str + "-" + to_str;
}
};
class MultiRange {
public:
MultiRange(std::initializer_list<Range> rs) : ranges{rs} {}
const std::string str() const {
std::string multi_range_string{};
for (Range range : ranges) {
multi_range_string += ((multi_range_string.empty()) ? "" : ", ") + range.str();
}
return multi_range_string;
}
private:
std::vector<Range> ranges;
}; // namespace cpr
} // namespace cpr
#endif

View file

@ -0,0 +1,84 @@
#ifndef CPR_REDIRECT_H
#define CPR_REDIRECT_H
#include <cstdint>
namespace cpr {
enum class PostRedirectFlags : uint8_t {
/**
* Respect RFC 7231 (section 6.4.2 to 6.4.4).
* Same as CURL_REDIR_POST_301 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).
**/
POST_301 = 0x1 << 0,
/**
* Maintain the request method after a 302 redirect.
* Same as CURL_REDIR_POST_302 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).
**/
POST_302 = 0x1 << 1,
/**
* Maintain the request method after a 303 redirect.
* Same as CURL_REDIR_POST_303 (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).
**/
POST_303 = 0x1 << 2,
/**
* Default value.
* Convenience option to enable all flags.
* Same as CURL_REDIR_POST_ALL (https://curl.se/libcurl/c/CURLOPT_POSTREDIR.html).
**/
POST_ALL = POST_301 | POST_302 | POST_303,
/**
* * Convenience option to disable all flags.
**/
NONE = 0x0
};
PostRedirectFlags operator|(PostRedirectFlags lhs, PostRedirectFlags rhs);
PostRedirectFlags operator&(PostRedirectFlags lhs, PostRedirectFlags rhs);
PostRedirectFlags operator^(PostRedirectFlags lhs, PostRedirectFlags rhs);
PostRedirectFlags operator~(PostRedirectFlags flag);
PostRedirectFlags& operator|=(PostRedirectFlags& lhs, PostRedirectFlags rhs);
PostRedirectFlags& operator&=(PostRedirectFlags& lhs, PostRedirectFlags rhs);
PostRedirectFlags& operator^=(PostRedirectFlags& lhs, PostRedirectFlags rhs);
bool any(PostRedirectFlags flag);
class Redirect {
public:
/**
* The maximum number of redirects to follow.
* 0: Refuse any redirects.
* -1: Infinite number of redirects.
* Default: 50
* https://curl.se/libcurl/c/CURLOPT_MAXREDIRS.html
**/
// NOLINTNEXTLINE (google-runtime-int)
long maximum{50L};
/**
* Follow 3xx redirects.
* Default: true
* https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html
**/
bool follow{true};
/**
* Continue to send authentication (user+password) credentials when following locations, even when hostname changed.
* Default: false
* https://curl.se/libcurl/c/CURLOPT_UNRESTRICTED_AUTH.html
**/
bool cont_send_cred{false};
/**
* Flags to control how to act after a redirect for a post request.
* Default: POST_ALL
**/
PostRedirectFlags post_flags{PostRedirectFlags::POST_ALL};
Redirect() = default;
// NOLINTNEXTLINE (google-runtime-int)
Redirect(long p_maximum, bool p_follow, bool p_cont_send_cred, PostRedirectFlags p_post_flags) : maximum(p_maximum), follow(p_follow), cont_send_cred(p_cont_send_cred), post_flags(p_post_flags){};
// NOLINTNEXTLINE (google-runtime-int)
explicit Redirect(long p_maximum) : maximum(p_maximum){};
explicit Redirect(bool p_follow) : follow(p_follow){};
Redirect(bool p_follow, bool p_cont_send_cred) : follow(p_follow), cont_send_cred(p_cont_send_cred){};
explicit Redirect(PostRedirectFlags p_post_flags) : post_flags(p_post_flags){};
};
} // namespace cpr
#endif

View file

@ -0,0 +1,17 @@
#ifndef CPR_RESERVE_SIZE_H
#define CPR_RESERVE_SIZE_H
#include <cstdint>
namespace cpr {
class ReserveSize {
public:
ReserveSize(const size_t _size) : size(_size) {}
size_t size = 0;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,23 @@
#ifndef CPR_RESOLVE_H
#define CPR_RESOLVE_H
#include <string>
#include <set>
namespace cpr {
class Resolve {
public:
std::string host;
std::string addr;
std::set<uint16_t> ports;
Resolve(const std::string& host_param, const std::string& addr_param, const std::set<uint16_t>& ports_param = std::set<uint16_t>{80U, 443U}): host(host_param), addr(addr_param), ports(ports_param) {
if (this->ports.empty()) {
this->ports.insert(80U);
this->ports.insert(443U);
}
}
};
} // namespace cpr
#endif

View file

@ -0,0 +1,58 @@
#ifndef CPR_RESPONSE_H
#define CPR_RESPONSE_H
#include <cassert>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "cpr/cert_info.h"
#include "cpr/cookies.h"
#include "cpr/cprtypes.h"
#include "cpr/error.h"
#include "cpr/ssl_options.h"
#include "cpr/util.h"
namespace cpr {
class MultiPerform;
class Response {
private:
friend MultiPerform;
std::shared_ptr<CurlHolder> curl_{nullptr};
public:
// Ignored here since libcurl uses a long for this.
// NOLINTNEXTLINE(google-runtime-int)
long status_code{};
std::string text{};
Header header{};
Url url{};
double elapsed{};
Cookies cookies{};
Error error{};
std::string raw_header{};
std::string status_line{};
std::string reason{};
cpr_off_t uploaded_bytes{};
cpr_off_t downloaded_bytes{};
// Ignored here since libcurl uses a long for this.
// NOLINTNEXTLINE(google-runtime-int)
long redirect_count{};
Response() = default;
Response(std::shared_ptr<CurlHolder> curl, std::string&& p_text, std::string&& p_header_string, Cookies&& p_cookies, Error&& p_error);
std::vector<CertInfo> GetCertInfos();
Response(const Response& other) = default;
Response(Response&& old) noexcept = default;
~Response() noexcept = default;
Response& operator=(Response&& old) noexcept = default;
Response& operator=(const Response& other) = default;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,297 @@
#ifndef CPR_SESSION_H
#define CPR_SESSION_H
#include <cstdint>
#include <fstream>
#include <future>
#include <memory>
#include <queue>
#include "cpr/accept_encoding.h"
#include "cpr/auth.h"
#include "cpr/bearer.h"
#include "cpr/body.h"
#include "cpr/callback.h"
#include "cpr/connect_timeout.h"
#include "cpr/cookies.h"
#include "cpr/cprtypes.h"
#include "cpr/curlholder.h"
#include "cpr/http_version.h"
#include "cpr/interface.h"
#include "cpr/limit_rate.h"
#include "cpr/local_port.h"
#include "cpr/local_port_range.h"
#include "cpr/low_speed.h"
#include "cpr/multipart.h"
#include "cpr/parameters.h"
#include "cpr/payload.h"
#include "cpr/proxies.h"
#include "cpr/proxyauth.h"
#include "cpr/range.h"
#include "cpr/redirect.h"
#include "cpr/reserve_size.h"
#include "cpr/resolve.h"
#include "cpr/response.h"
#include "cpr/ssl_options.h"
#include "cpr/timeout.h"
#include "cpr/unix_socket.h"
#include "cpr/user_agent.h"
#include "cpr/verbose.h"
namespace cpr {
using AsyncResponse = std::future<Response>;
class Interceptor;
class MultiPerform;
class Session : public std::enable_shared_from_this<Session> {
public:
Session();
Session(const Session& other) = delete;
Session(Session&& old) = default;
~Session() = default;
Session& operator=(Session&& old) noexcept = default;
Session& operator=(const Session& other) = delete;
void SetUrl(const Url& url);
void SetParameters(const Parameters& parameters);
void SetParameters(Parameters&& parameters);
void SetHeader(const Header& header);
void UpdateHeader(const Header& header);
void SetTimeout(const Timeout& timeout);
void SetConnectTimeout(const ConnectTimeout& timeout);
void SetAuth(const Authentication& auth);
// Only supported with libcurl >= 7.61.0.
// As an alternative use SetHeader and add the token manually.
#if LIBCURL_VERSION_NUM >= 0x073D00
void SetBearer(const Bearer& token);
#endif
void SetUserAgent(const UserAgent& ua);
void SetPayload(Payload&& payload);
void SetPayload(const Payload& payload);
void SetProxies(Proxies&& proxies);
void SetProxies(const Proxies& proxies);
void SetProxyAuth(ProxyAuthentication&& proxy_auth);
void SetProxyAuth(const ProxyAuthentication& proxy_auth);
void SetMultipart(Multipart&& multipart);
void SetMultipart(const Multipart& multipart);
void SetRedirect(const Redirect& redirect);
void SetCookies(const Cookies& cookies);
void SetBody(Body&& body);
void SetBody(const Body& body);
void SetLowSpeed(const LowSpeed& low_speed);
void SetVerifySsl(const VerifySsl& verify);
void SetUnixSocket(const UnixSocket& unix_socket);
void SetSslOptions(const SslOptions& options);
void SetReadCallback(const ReadCallback& read);
void SetHeaderCallback(const HeaderCallback& header);
void SetWriteCallback(const WriteCallback& write);
void SetProgressCallback(const ProgressCallback& progress);
void SetDebugCallback(const DebugCallback& debug);
void SetVerbose(const Verbose& verbose);
void SetInterface(const Interface& iface);
void SetLocalPort(const LocalPort& local_port);
void SetLocalPortRange(const LocalPortRange& local_port_range);
void SetHttpVersion(const HttpVersion& version);
void SetRange(const Range& range);
void SetResolve(const Resolve& resolve);
void SetResolves(const std::vector<Resolve>& resolves);
void SetMultiRange(const MultiRange& multi_range);
void SetReserveSize(const ReserveSize& reserve_size);
void SetAcceptEncoding(const AcceptEncoding& accept_encoding);
void SetAcceptEncoding(AcceptEncoding&& accept_encoding);
void SetLimitRate(const LimitRate& limit_rate);
// Used in templated functions
void SetOption(const Url& url);
void SetOption(const Parameters& parameters);
void SetOption(Parameters&& parameters);
void SetOption(const Header& header);
void SetOption(const Timeout& timeout);
void SetOption(const ConnectTimeout& timeout);
void SetOption(const Authentication& auth);
// Only supported with libcurl >= 7.61.0.
// As an alternative use SetHeader and add the token manually.
#if LIBCURL_VERSION_NUM >= 0x073D00
void SetOption(const Bearer& auth);
#endif
void SetOption(const UserAgent& ua);
void SetOption(Payload&& payload);
void SetOption(const Payload& payload);
void SetOption(const LimitRate& limit_rate);
void SetOption(Proxies&& proxies);
void SetOption(const Proxies& proxies);
void SetOption(ProxyAuthentication&& proxy_auth);
void SetOption(const ProxyAuthentication& proxy_auth);
void SetOption(Multipart&& multipart);
void SetOption(const Multipart& multipart);
void SetOption(const Redirect& redirect);
void SetOption(const Cookies& cookies);
void SetOption(Body&& body);
void SetOption(const Body& body);
void SetOption(const ReadCallback& read);
void SetOption(const HeaderCallback& header);
void SetOption(const WriteCallback& write);
void SetOption(const ProgressCallback& progress);
void SetOption(const DebugCallback& debug);
void SetOption(const LowSpeed& low_speed);
void SetOption(const VerifySsl& verify);
void SetOption(const Verbose& verbose);
void SetOption(const UnixSocket& unix_socket);
void SetOption(const SslOptions& options);
void SetOption(const Interface& iface);
void SetOption(const LocalPort& local_port);
void SetOption(const LocalPortRange& local_port_range);
void SetOption(const HttpVersion& version);
void SetOption(const Range& range);
void SetOption(const MultiRange& multi_range);
void SetOption(const ReserveSize& reserve_size);
void SetOption(const AcceptEncoding& accept_encoding);
void SetOption(AcceptEncoding&& accept_encoding);
void SetOption(const Resolve& resolve);
void SetOption(const std::vector<Resolve>& resolves);
cpr_off_t GetDownloadFileLength();
/**
* Attempt to preallocate enough memory for specified number of characters in the response string.
* Pass 0 to disable this behavior and let the response string be allocated dynamically on demand.
*
* Example:
* cpr::Session session;
* session.SetUrl(cpr::Url{"http://xxx/file"});
* session.ResponseStringReserve(1024 * 512); // Reserve space for at least 1024 * 512 characters
* cpr::Response r = session.Get();
**/
void ResponseStringReserve(size_t size);
Response Delete();
Response Download(const WriteCallback& write);
Response Download(std::ofstream& file);
Response Get();
Response Head();
Response Options();
Response Patch();
Response Post();
Response Put();
AsyncResponse GetAsync();
AsyncResponse DeleteAsync();
AsyncResponse DownloadAsync(const WriteCallback& write);
AsyncResponse DownloadAsync(std::ofstream& file);
AsyncResponse HeadAsync();
AsyncResponse OptionsAsync();
AsyncResponse PatchAsync();
AsyncResponse PostAsync();
AsyncResponse PutAsync();
template <typename Then>
auto GetCallback(Then then);
template <typename Then>
auto PostCallback(Then then);
template <typename Then>
auto PutCallback(Then then);
template <typename Then>
auto HeadCallback(Then then);
template <typename Then>
auto DeleteCallback(Then then);
template <typename Then>
auto OptionsCallback(Then then);
template <typename Then>
auto PatchCallback(Then then);
std::shared_ptr<CurlHolder> GetCurlHolder();
std::string GetFullRequestUrl();
void PrepareDelete();
void PrepareGet();
void PrepareHead();
void PrepareOptions();
void PreparePatch();
void PreparePost();
void PreparePut();
void PrepareDownload(const WriteCallback& write);
void PrepareDownload(std::ofstream& file);
Response Complete(CURLcode curl_error);
Response CompleteDownload(CURLcode curl_error);
void AddInterceptor(const std::shared_ptr<Interceptor>& pinterceptor);
private:
// Interceptors should be able to call the private procceed() function
friend Interceptor;
friend MultiPerform;
bool hasBodyOrPayload_{false};
bool chunkedTransferEncoding_{false};
std::shared_ptr<CurlHolder> curl_;
Url url_;
Parameters parameters_;
Proxies proxies_;
ProxyAuthentication proxyAuth_;
Header header_;
AcceptEncoding acceptEncoding_;
/**
* Will be set by the read callback.
* Ensures that the "Transfer-Encoding" is set to "chunked", if not overriden in header_.
**/
ReadCallback readcb_;
HeaderCallback headercb_;
WriteCallback writecb_;
ProgressCallback progresscb_;
DebugCallback debugcb_;
size_t response_string_reserve_size_{0};
std::string response_string_;
std::string header_string_;
std::queue<std::shared_ptr<Interceptor>> interceptors_;
bool isUsedInMultiPerform{false};
Response makeDownloadRequest();
Response makeRequest();
Response proceed();
void prepareCommon();
void prepareCommonDownload();
void SetHeaderInternal();
std::shared_ptr<Session> GetSharedPtrFromThis();
CURLcode DoEasyPerform();
};
template <typename Then>
auto Session::GetCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Get()); }, std::move(then));
}
template <typename Then>
auto Session::PostCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Post()); }, std::move(then));
}
template <typename Then>
auto Session::PutCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Put()); }, std::move(then));
}
template <typename Then>
auto Session::HeadCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Head()); }, std::move(then));
}
template <typename Then>
auto Session::DeleteCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Delete()); }, std::move(then));
}
template <typename Then>
auto Session::OptionsCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Options()); }, std::move(then));
}
template <typename Then>
auto Session::PatchCallback(Then then) {
return async([shared_this = GetSharedPtrFromThis()](Then then_inner) { return then_inner(shared_this->Patch()); }, std::move(then));
}
} // namespace cpr
#endif

View file

@ -0,0 +1,47 @@
#ifndef CPR_SINGLETON_H
#define CPR_SINGLETON_H
#include <mutex>
#ifndef CPR_DISABLE_COPY
#define CPR_DISABLE_COPY(Class) \
Class(const Class&) = delete; \
Class& operator=(const Class&) = delete;
#endif
#ifndef CPR_SINGLETON_DECL
#define CPR_SINGLETON_DECL(Class) \
public: \
static Class* GetInstance(); \
static void ExitInstance(); \
private: \
CPR_DISABLE_COPY(Class) \
static Class* s_pInstance; \
static std::mutex s_mutex;
#endif
#ifndef CPR_SINGLETON_IMPL
#define CPR_SINGLETON_IMPL(Class) \
Class* Class::s_pInstance = nullptr; \
std::mutex Class::s_mutex; \
Class* Class::GetInstance() { \
if (s_pInstance == nullptr) { \
s_mutex.lock(); \
if (s_pInstance == nullptr) { \
s_pInstance = new Class; \
} \
s_mutex.unlock(); \
} \
return s_pInstance; \
} \
void Class::ExitInstance() { \
s_mutex.lock(); \
if (s_pInstance) { \
delete s_pInstance; \
s_pInstance = nullptr; \
} \
s_mutex.unlock(); \
}
#endif
#endif

View file

@ -0,0 +1,26 @@
#ifndef CPR_SSL_CTX_H
#define CPR_SSL_CTX_H
#include "cpr/ssl_options.h"
#include <curl/curl.h>
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
namespace cpr {
/**
* This callback function loads a CA certificate from raw_cert_buf and gets called by libcurl
* just before the initialization of an SSL connection.
* The raw_cert_buf argument is set with the CURLOPT_SSL_CTX_DATA option and has to be a nul
* terminated buffer.
*
* Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html
* https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html
*/
CURLcode sslctx_function_load_ca_cert_from_buffer(CURL* curl, void* sslctx, void* raw_cert_buf);
} // Namespace cpr
#endif
#endif

View file

@ -0,0 +1,622 @@
#ifndef CPR_SSLOPTIONS_H
#define CPR_SSLOPTIONS_H
#include <memory>
#include <string>
#include <vector>
#include <cpr/filesystem.h>
#include <curl/curl.h>
#include "cpr/util.h"
#include <utility>
#define __LIBCURL_VERSION_GTE(major, minor) ((LIBCURL_VERSION_MAJOR > (major)) || ((LIBCURL_VERSION_MAJOR == (major)) && (LIBCURL_VERSION_MINOR >= (minor))))
#define __LIBCURL_VERSION_LT(major, minor) ((LIBCURL_VERSION_MAJOR < (major)) || ((LIBCURL_VERSION_MAJOR == (major)) && (LIBCURL_VERSION_MINOR < (minor))))
#ifndef SUPPORT_ALPN
#define SUPPORT_ALPN __LIBCURL_VERSION_GTE(7, 36)
#endif
#ifndef SUPPORT_NPN
#define SUPPORT_NPN __LIBCURL_VERSION_GTE(7, 36)
#endif
#ifndef SUPPORT_SSLv2
#define SUPPORT_SSLv2 __LIBCURL_VERSION_LT(7, 19)
#endif
#ifndef SUPPORT_SSLv3
#define SUPPORT_SSLv3 __LIBCURL_VERSION_LT(7, 39)
#endif
#ifndef SUPPORT_TLSv1_0
#define SUPPORT_TLSv1_0 __LIBCURL_VERSION_GTE(7, 34)
#endif
#ifndef SUPPORT_TLSv1_1
#define SUPPORT_TLSv1_1 __LIBCURL_VERSION_GTE(7, 34)
#endif
#ifndef SUPPORT_TLSv1_2
#define SUPPORT_TLSv1_2 __LIBCURL_VERSION_GTE(7, 34)
#endif
#ifndef SUPPORT_TLSv1_3
#define SUPPORT_TLSv1_3 __LIBCURL_VERSION_GTE(7, 52)
#endif
#ifndef SUPPORT_MAX_TLS_VERSION
#define SUPPORT_MAX_TLS_VERSION __LIBCURL_VERSION_GTE(7, 54)
#endif
#ifndef SUPPORT_MAX_TLSv1_1
#define SUPPORT_MAX_TLSv1_1 __LIBCURL_VERSION_GTE(7, 54)
#endif
#ifndef SUPPORT_MAX_TLSv1_2
#define SUPPORT_MAX_TLSv1_2 __LIBCURL_VERSION_GTE(7, 54)
#endif
#ifndef SUPPORT_MAX_TLSv1_3
#define SUPPORT_MAX_TLSv1_3 __LIBCURL_VERSION_GTE(7, 54)
#endif
#ifndef SUPPORT_TLSv13_CIPHERS
#define SUPPORT_TLSv13_CIPHERS __LIBCURL_VERSION_GTE(7, 61)
#endif
#ifndef SUPPORT_SESSIONID_CACHE
#define SUPPORT_SESSIONID_CACHE __LIBCURL_VERSION_GTE(7, 16)
#endif
#ifndef SUPPORT_SSL_FALSESTART
#define SUPPORT_SSL_FALSESTART __LIBCURL_VERSION_GTE(7, 42)
#endif
#ifndef SUPPORT_SSL_NO_REVOKE
#define SUPPORT_SSL_NO_REVOKE __LIBCURL_VERSION_GTE(7, 44)
#endif
#ifndef SUPPORT_CURLOPT_SSLKEY_BLOB
#define SUPPORT_CURLOPT_SSLKEY_BLOB __LIBCURL_VERSION_GTE(7, 71)
#endif
#ifndef SUPPORT_CURLOPT_SSL_CTX_FUNCTION
#define SUPPORT_CURLOPT_SSL_CTX_FUNCTION __LIBCURL_VERSION_GTE(7, 11)
#endif
namespace cpr {
class VerifySsl {
public:
VerifySsl() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
VerifySsl(bool p_verify) : verify(p_verify) {}
explicit operator bool() const {
return verify;
}
bool verify = true;
};
namespace ssl {
// set SSL client certificate
class CertFile {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
CertFile(fs::path&& p_filename) : filename(std::move(p_filename)) {}
virtual ~CertFile() = default;
const fs::path filename;
virtual const char* GetCertType() const {
return "PEM";
}
};
using PemCert = CertFile;
class DerCert : public CertFile {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
DerCert(fs::path&& p_filename) : CertFile(std::move(p_filename)) {}
virtual ~DerCert() = default;
const char* GetCertType() const override {
return "DER";
}
};
// specify private keyfile for TLS and SSL client cert
class KeyFile {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
KeyFile(fs::path&& p_filename) : filename(std::move(p_filename)) {}
template <typename FileType, typename PassType>
KeyFile(FileType&& p_filename, PassType p_password) : filename(std::forward<FileType>(p_filename)), password(std::move(p_password)) {}
virtual ~KeyFile() {
util::secureStringClear(password);
}
fs::path filename;
std::string password;
virtual const char* GetKeyType() const {
return "PEM";
}
};
#if SUPPORT_CURLOPT_SSLKEY_BLOB
class KeyBlob {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
KeyBlob(std::string&& p_blob) : blob(std::move(p_blob)) {}
template <typename BlobType, typename PassType>
KeyBlob(BlobType&& p_blob, PassType p_password) : blob(std::forward<BlobType>(p_blob)), password(std::move(p_password)) {}
virtual ~KeyBlob() {
util::secureStringClear(password);
}
std::string blob;
std::string password;
virtual const char* GetKeyType() const {
return "PEM";
}
};
#endif
using PemKey = KeyFile;
class DerKey : public KeyFile {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
DerKey(fs::path&& p_filename) : KeyFile(std::move(p_filename)) {}
template <typename FileType, typename PassType>
DerKey(FileType&& p_filename, PassType p_password) : KeyFile(std::forward<FileType>(p_filename), std::move(p_password)) {}
virtual ~DerKey() = default;
const char* GetKeyType() const override {
return "DER";
}
};
class PinnedPublicKey {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
PinnedPublicKey(std::string&& p_pinned_public_key) : pinned_public_key(std::move(p_pinned_public_key)) {}
const std::string pinned_public_key;
};
#if SUPPORT_ALPN
// This option enables/disables ALPN in the SSL handshake (if the SSL backend libcurl is built to
// use supports it), which can be used to negotiate http2.
class ALPN {
public:
ALPN() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
ALPN(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
#endif // SUPPORT_ALPN
#if SUPPORT_NPN
// This option enables/disables NPN in the SSL handshake (if the SSL backend libcurl is built to
// use supports it), which can be used to negotiate http2.
class NPN {
public:
NPN() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
NPN(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
#endif // SUPPORT_NPN
// This option determines whether libcurl verifies that the server cert is for the server it is
// known as.
class VerifyHost {
public:
VerifyHost() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
VerifyHost(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
// This option determines whether libcurl verifies the authenticity of the peer's certificate.
class VerifyPeer {
public:
VerifyPeer() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
VerifyPeer(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
// This option determines whether libcurl verifies the status of the server cert using the
// "Certificate Status Request" TLS extension (aka. OCSP stapling).
class VerifyStatus {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
VerifyStatus(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = false;
};
// TLS v1.0 or later
struct TLSv1 {};
#if SUPPORT_SSLv2
// SSL v2 (but not SSLv3)
struct SSLv2 {};
#endif
#if SUPPORT_SSLv3
// SSL v3 (but not SSLv2)
struct SSLv3 {};
#endif
#if SUPPORT_TLSv1_0
// TLS v1.0 or later (Added in 7.34.0)
struct TLSv1_0 {};
#endif
#if SUPPORT_TLSv1_1
// TLS v1.1 or later (Added in 7.34.0)
struct TLSv1_1 {};
#endif
#if SUPPORT_TLSv1_2
// TLS v1.2 or later (Added in 7.34.0)
struct TLSv1_2 {};
#endif
#if SUPPORT_TLSv1_3
// TLS v1.3 or later (Added in 7.52.0)
struct TLSv1_3 {};
#endif
#if SUPPORT_MAX_TLS_VERSION
// The flag defines the maximum supported TLS version by libcurl, or the default value from the SSL
// library is used.
struct MaxTLSVersion {};
#endif
#if SUPPORT_MAX_TLSv1_0
// The flag defines maximum supported TLS version as TLSv1.0. (Added in 7.54.0)
struct MaxTLSv1_0 {};
#endif
#if SUPPORT_MAX_TLSv1_1
// The flag defines maximum supported TLS version as TLSv1.1. (Added in 7.54.0)
struct MaxTLSv1_1 {};
#endif
#if SUPPORT_MAX_TLSv1_2
// The flag defines maximum supported TLS version as TLSv1.2. (Added in 7.54.0)
struct MaxTLSv1_2 {};
#endif
#if SUPPORT_MAX_TLSv1_3
// The flag defines maximum supported TLS version as TLSv1.3. (Added in 7.54.0)
struct MaxTLSv1_3 {};
#endif
// path to Certificate Authority (CA) bundle
class CaInfo {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
CaInfo(fs::path&& p_filename) : filename(std::move(p_filename)) {}
fs::path filename;
};
// specify directory holding CA certificates
class CaPath {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
CaPath(fs::path&& p_filename) : filename(std::move(p_filename)) {}
fs::path filename;
};
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
class CaBuffer {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
CaBuffer(std::string&& p_buffer) : buffer(std::move(p_buffer)) {}
const std::string buffer;
};
#endif
// specify a Certificate Revocation List file
class Crl {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Crl(fs::path&& p_filename) : filename(std::move(p_filename)) {}
fs::path filename;
};
// specify ciphers to use for TLS
class Ciphers {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {}
std::string ciphers;
};
#if SUPPORT_TLSv13_CIPHERS
// specify ciphers suites to use for TLS 1.3
class TLS13_Ciphers {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
TLS13_Ciphers(std::string&& p_ciphers) : ciphers(std::move(p_ciphers)) {}
std::string ciphers;
};
#endif
#if SUPPORT_SESSIONID_CACHE
// enable/disable use of the SSL session-ID cache
class SessionIdCache {
public:
SessionIdCache() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
SessionIdCache(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = true;
};
#endif
#if SUPPORT_SSL_FALSESTART
class SslFastStart {
public:
SslFastStart() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
SslFastStart(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = false;
};
#endif
class NoRevoke {
public:
NoRevoke() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
NoRevoke(bool p_enabled) : enabled(p_enabled) {}
explicit operator bool() const {
return enabled;
}
bool enabled = false;
};
} // namespace ssl
struct SslOptions {
// We don't use fs::path here, as this leads to problems using windows
std::string cert_file;
std::string cert_type;
// We don't use fs::path here, as this leads to problems using windows
std::string key_file;
#if SUPPORT_CURLOPT_SSLKEY_BLOB
std::string key_blob;
#endif
std::string key_type;
std::string key_pass;
std::string pinned_public_key;
#if SUPPORT_ALPN
bool enable_alpn = true;
#endif // SUPPORT_ALPN
#if SUPPORT_NPN
bool enable_npn = true;
#endif // SUPPORT_ALPN
bool verify_host = true;
bool verify_peer = true;
bool verify_status = false;
int ssl_version = CURL_SSLVERSION_DEFAULT;
#if SUPPORT_SSL_NO_REVOKE
bool ssl_no_revoke = false;
#endif
#if SUPPORT_MAX_TLS_VERSION
int max_version = CURL_SSLVERSION_MAX_DEFAULT;
#endif
// We don't use fs::path here, as this leads to problems using windows
std::string ca_info;
// We don't use fs::path here, as this leads to problems using windows
std::string ca_path;
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
std::string ca_buffer;
#endif
// We don't use fs::path here, as this leads to problems using windows
std::string crl_file;
std::string ciphers;
#if SUPPORT_TLSv13_CIPHERS
std::string tls13_ciphers;
#endif
#if SUPPORT_SESSIONID_CACHE
bool session_id_cache = true;
#endif
~SslOptions() noexcept {
#if SUPPORT_CURLOPT_SSLKEY_BLOB
util::secureStringClear(key_blob);
#endif
util::secureStringClear(key_pass);
}
void SetOption(const ssl::CertFile& opt) {
cert_file = opt.filename.string();
cert_type = opt.GetCertType();
}
void SetOption(const ssl::KeyFile& opt) {
key_file = opt.filename.string();
key_type = opt.GetKeyType();
key_pass = opt.password;
}
#if SUPPORT_CURLOPT_SSLKEY_BLOB
void SetOption(const ssl::KeyBlob& opt) {
key_blob = opt.blob;
key_type = opt.GetKeyType();
key_pass = opt.password;
}
#endif
void SetOption(const ssl::PinnedPublicKey& opt) {
pinned_public_key = opt.pinned_public_key;
}
#if SUPPORT_ALPN
void SetOption(const ssl::ALPN& opt) {
enable_alpn = opt.enabled;
}
#endif // SUPPORT_ALPN
#if SUPPORT_NPN
void SetOption(const ssl::NPN& opt) {
enable_npn = opt.enabled;
}
#endif // SUPPORT_NPN
void SetOption(const ssl::VerifyHost& opt) {
verify_host = opt.enabled;
}
void SetOption(const ssl::VerifyPeer& opt) {
verify_peer = opt.enabled;
}
void SetOption(const ssl::VerifyStatus& opt) {
verify_status = opt.enabled;
}
void SetOption(const ssl::TLSv1& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1;
}
#if SUPPORT_SSL_NO_REVOKE
void SetOption(const ssl::NoRevoke& opt) {
ssl_no_revoke = opt.enabled;
}
#endif
#if SUPPORT_SSLv2
void SetOption(const ssl::SSLv2& /*opt*/) {
ssl_version = CURL_SSLVERSION_SSLv2;
}
#endif
#if SUPPORT_SSLv3
void SetOption(const ssl::SSLv3& /*opt*/) {
ssl_version = CURL_SSLVERSION_SSLv3;
}
#endif
#if SUPPORT_TLSv1_0
void SetOption(const ssl::TLSv1_0& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1_0;
}
#endif
#if SUPPORT_TLSv1_1
void SetOption(const ssl::TLSv1_1& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1_1;
}
#endif
#if SUPPORT_TLSv1_2
void SetOption(const ssl::TLSv1_2& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1_2;
}
#endif
#if SUPPORT_TLSv1_3
void SetOption(const ssl::TLSv1_3& /*opt*/) {
ssl_version = CURL_SSLVERSION_TLSv1_3;
}
#endif
#if SUPPORT_MAX_TLS_VERSION
void SetOption(const ssl::MaxTLSVersion& /*opt*/) {
max_version = CURL_SSLVERSION_DEFAULT;
}
#endif
#if SUPPORT_MAX_TLSv1_0
void SetOption(const ssl::MaxTLSv1_0& opt) {
max_version = CURL_SSLVERSION_MAX_TLSv1_0;
}
#endif
#if SUPPORT_MAX_TLSv1_1
void SetOption(const ssl::MaxTLSv1_1& /*opt*/) {
max_version = CURL_SSLVERSION_MAX_TLSv1_1;
}
#endif
#if SUPPORT_MAX_TLSv1_2
void SetOption(const ssl::MaxTLSv1_2& /*opt*/) {
max_version = CURL_SSLVERSION_MAX_TLSv1_2;
}
#endif
#if SUPPORT_MAX_TLSv1_3
void SetOption(const ssl::MaxTLSv1_3& /*opt*/) {
max_version = CURL_SSLVERSION_MAX_TLSv1_3;
}
#endif
void SetOption(const ssl::CaInfo& opt) {
ca_info = opt.filename.string();
}
void SetOption(const ssl::CaPath& opt) {
ca_path = opt.filename.string();
}
#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION
void SetOption(const ssl::CaBuffer& opt) {
ca_buffer = opt.buffer;
}
#endif
void SetOption(const ssl::Crl& opt) {
crl_file = opt.filename.string();
}
void SetOption(const ssl::Ciphers& opt) {
ciphers = opt.ciphers;
}
#if SUPPORT_TLSv13_CIPHERS
void SetOption(const ssl::TLS13_Ciphers& opt) {
tls13_ciphers = opt.ciphers;
}
#endif
#if SUPPORT_SESSIONID_CACHE
void SetOption(const ssl::SessionIdCache& opt) {
session_id_cache = opt.enabled;
}
#endif
};
namespace priv {
template <typename T>
void set_ssl_option(SslOptions& opts, T&& t) {
opts.SetOption(std::forward<T>(t));
}
template <typename T, typename... Ts>
void set_ssl_option(SslOptions& opts, T&& t, Ts&&... ts) {
set_ssl_option(opts, std::forward<T>(t));
set_ssl_option(opts, std::move(ts)...);
}
} // namespace priv
template <typename... Ts>
SslOptions Ssl(Ts&&... ts) {
SslOptions opts;
priv::set_ssl_option(opts, std::move(ts)...);
return opts;
}
} // namespace cpr
#endif

View file

@ -0,0 +1,100 @@
#ifndef _CPR_STATUS_CODES
#define _CPR_STATUS_CODES
#include <cstdint>
namespace cpr {
namespace status {
// Information responses
constexpr std::int32_t HTTP_CONTINUE = 100;
constexpr std::int32_t HTTP_SWITCHING_PROTOCOL = 101;
constexpr std::int32_t HTTP_PROCESSING = 102;
constexpr std::int32_t HTTP_EARLY_HINTS = 103;
// Successful responses
constexpr std::int32_t HTTP_OK = 200;
constexpr std::int32_t HTTP_CREATED = 201;
constexpr std::int32_t HTTP_ACCEPTED = 202;
constexpr std::int32_t HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
constexpr std::int32_t HTTP_NO_CONTENT = 204;
constexpr std::int32_t HTTP_RESET_CONTENT = 205;
constexpr std::int32_t HTTP_PARTIAL_CONTENT = 206;
constexpr std::int32_t HTTP_MULTI_STATUS = 207;
constexpr std::int32_t HTTP_ALREADY_REPORTED = 208;
constexpr std::int32_t HTTP_IM_USED = 226;
// Redirection messages
constexpr std::int32_t HTTP_MULTIPLE_CHOICE = 300;
constexpr std::int32_t HTTP_MOVED_PERMANENTLY = 301;
constexpr std::int32_t HTTP_FOUND = 302;
constexpr std::int32_t HTTP_SEE_OTHER = 303;
constexpr std::int32_t HTTP_NOT_MODIFIED = 304;
constexpr std::int32_t HTTP_USE_PROXY = 305;
constexpr std::int32_t HTTP_UNUSED = 306;
constexpr std::int32_t HTTP_TEMPORARY_REDIRECT = 307;
constexpr std::int32_t HTTP_PERMANENT_REDIRECT = 308;
// Client error responses
constexpr std::int32_t HTTP_BAD_REQUEST = 400;
constexpr std::int32_t HTTP_UNAUTHORIZED = 401;
constexpr std::int32_t HTTP_PAYMENT_REQUIRED = 402;
constexpr std::int32_t HTTP_FORBIDDEN = 403;
constexpr std::int32_t HTTP_NOT_FOUND = 404;
constexpr std::int32_t HTTP_METHOD_NOT_ALLOWED = 405;
constexpr std::int32_t HTTP_NOT_ACCEPTABLE = 406;
constexpr std::int32_t HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
constexpr std::int32_t HTTP_REQUEST_TIMEOUT = 408;
constexpr std::int32_t HTTP_CONFLICT = 409;
constexpr std::int32_t HTTP_GONE = 410;
constexpr std::int32_t HTTP_LENGTH_REQUIRED = 411;
constexpr std::int32_t HTTP_PRECONDITION_FAILED = 412;
constexpr std::int32_t HTTP_PAYLOAD_TOO_LARGE = 413;
constexpr std::int32_t HTTP_URI_TOO_LONG = 414;
constexpr std::int32_t HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
constexpr std::int32_t HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
constexpr std::int32_t HTTP_EXPECTATION_FAILED = 417;
constexpr std::int32_t HTTP_IM_A_TEAPOT = 418;
constexpr std::int32_t HTTP_MISDIRECTED_REQUEST = 421;
constexpr std::int32_t HTTP_UNPROCESSABLE_ENTITY = 422;
constexpr std::int32_t HTTP_LOCKED = 423;
constexpr std::int32_t HTTP_FAILED_DEPENDENCY = 424;
constexpr std::int32_t HTTP_TOO_EARLY = 425;
constexpr std::int32_t HTTP_UPGRADE_REQUIRED = 426;
constexpr std::int32_t HTTP_PRECONDITION_REQUIRED = 428;
constexpr std::int32_t HTTP_TOO_MANY_REQUESTS = 429;
constexpr std::int32_t HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431;
constexpr std::int32_t HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
// Server response errors
constexpr std::int32_t HTTP_INTERNAL_SERVER_ERROR = 500;
constexpr std::int32_t HTTP_NOT_IMPLEMENTED = 501;
constexpr std::int32_t HTTP_BAD_GATEWAY = 502;
constexpr std::int32_t HTTP_SERVICE_UNAVAILABLE = 503;
constexpr std::int32_t HTTP_GATEWAY_TIMEOUT = 504;
constexpr std::int32_t HTTP_HTTP_VERSION_NOT_SUPPORTED = 505;
constexpr std::int32_t HTTP_VARIANT_ALSO_NEGOTIATES = 506;
constexpr std::int32_t HTTP_INSUFFICIENT_STORAGE = 507;
constexpr std::int32_t HTTP_LOOP_DETECTED = 508;
constexpr std::int32_t HTTP_NOT_EXTENDED = 510;
constexpr std::int32_t HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511;
constexpr std::int32_t INFO_CODE_OFFSET = 100;
constexpr std::int32_t SUCCESS_CODE_OFFSET = 200;
constexpr std::int32_t REDIRECT_CODE_OFFSET = 300;
constexpr std::int32_t CLIENT_ERROR_CODE_OFFSET = 400;
constexpr std::int32_t SERVER_ERROR_CODE_OFFSET = 500;
constexpr std::int32_t MISC_CODE_OFFSET = 600;
constexpr bool is_informational(const std::int32_t code) {
return (code >= INFO_CODE_OFFSET && code < SUCCESS_CODE_OFFSET);
}
constexpr bool is_success(const std::int32_t code) {
return (code >= SUCCESS_CODE_OFFSET && code < REDIRECT_CODE_OFFSET);
}
constexpr bool is_redirect(const std::int32_t code) {
return (code >= REDIRECT_CODE_OFFSET && code < CLIENT_ERROR_CODE_OFFSET);
}
constexpr bool is_client_error(const std::int32_t code) {
return (code >= CLIENT_ERROR_CODE_OFFSET && code < SERVER_ERROR_CODE_OFFSET);
}
constexpr bool is_server_error(const std::int32_t code) {
return (code >= SERVER_ERROR_CODE_OFFSET && code < MISC_CODE_OFFSET);
}
} // namespace status
} // namespace cpr
#endif

View file

@ -0,0 +1,122 @@
#ifndef CPR_THREAD_POOL_H
#define CPR_THREAD_POOL_H
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <future>
#include <list>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
#define CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM std::thread::hardware_concurrency()
constexpr size_t CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM = 1;
constexpr std::chrono::milliseconds CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME{60000};
namespace cpr {
class ThreadPool {
public:
using Task = std::function<void()>;
explicit ThreadPool(size_t min_threads = CPR_DEFAULT_THREAD_POOL_MIN_THREAD_NUM, size_t max_threads = CPR_DEFAULT_THREAD_POOL_MAX_THREAD_NUM, std::chrono::milliseconds max_idle_ms = CPR_DEFAULT_THREAD_POOL_MAX_IDLE_TIME);
virtual ~ThreadPool();
void SetMinThreadNum(size_t min_threads) {
min_thread_num = min_threads;
}
void SetMaxThreadNum(size_t max_threads) {
max_thread_num = max_threads;
}
void SetMaxIdleTime(std::chrono::milliseconds ms) {
max_idle_time = ms;
}
size_t GetCurrentThreadNum() {
return cur_thread_num;
}
size_t GetIdleThreadNum() {
return idle_thread_num;
}
bool IsStarted() {
return status != STOP;
}
bool IsStopped() {
return status == STOP;
}
int Start(size_t start_threads = 0);
int Stop();
int Pause();
int Resume();
int Wait();
/**
* Return a future, calling future.get() will wait task done and return RetType.
* Submit(fn, args...)
* Submit(std::bind(&Class::mem_fn, &obj))
* Submit(std::mem_fn(&Class::mem_fn, &obj))
**/
template <class Fn, class... Args>
auto Submit(Fn&& fn, Args&&... args) {
if (status == STOP) {
Start();
}
if (idle_thread_num <= 0 && cur_thread_num < max_thread_num) {
CreateThread();
}
using RetType = decltype(fn(args...));
auto task = std::make_shared<std::packaged_task<RetType()> >(std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...));
std::future<RetType> future = task->get_future();
{
std::lock_guard<std::mutex> locker(task_mutex);
tasks.emplace([task] { (*task)(); });
}
task_cond.notify_one();
return future;
}
private:
bool CreateThread();
void AddThread(std::thread* thread);
void DelThread(std::thread::id id);
public:
size_t min_thread_num;
size_t max_thread_num;
std::chrono::milliseconds max_idle_time;
private:
enum Status {
STOP,
RUNNING,
PAUSE,
};
struct ThreadData {
std::shared_ptr<std::thread> thread;
std::thread::id id;
Status status;
time_t start_time;
time_t stop_time;
};
std::atomic<Status> status;
std::atomic<size_t> cur_thread_num;
std::atomic<size_t> idle_thread_num;
std::list<ThreadData> threads;
std::mutex thread_mutex;
std::queue<Task> tasks;
std::mutex task_mutex;
std::condition_variable task_cond;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,27 @@
#ifndef CPR_TIMEOUT_H
#define CPR_TIMEOUT_H
#include <chrono>
#include <cstdint>
namespace cpr {
class Timeout {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Timeout(const std::chrono::milliseconds& duration) : ms{duration} {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Timeout(const std::int32_t& milliseconds) : Timeout{std::chrono::milliseconds(milliseconds)} {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Timeout(const std::chrono::seconds& duration) : ms{1000 * duration.count()} {}
// No way around since curl uses a long here.
// NOLINTNEXTLINE(google-runtime-int)
long Milliseconds() const;
std::chrono::milliseconds ms;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,21 @@
#ifndef CPR_UNIX_SOCKET_H
#define CPR_UNIX_SOCKET_H
#include <string>
namespace cpr {
class UnixSocket {
public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
UnixSocket(std::string&& unix_socket) : unix_socket_(std::move(unix_socket)) {}
const char* GetUnixSocketString() const noexcept;
private:
const std::string unix_socket_;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,33 @@
#ifndef CPR_USERAGENT_H
#define CPR_USERAGENT_H
#include <initializer_list>
#include <string>
#include "cpr/cprtypes.h"
namespace cpr {
class UserAgent : public StringHolder<UserAgent> {
public:
UserAgent() : StringHolder<UserAgent>() {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
UserAgent(const std::string& useragent) : StringHolder<UserAgent>(useragent) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
UserAgent(std::string&& useragent) : StringHolder<UserAgent>(std::move(useragent)) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
UserAgent(std::string_view useragent) : StringHolder<UserAgent>(useragent) {}
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
UserAgent(const char* useragent) : StringHolder<UserAgent>(useragent) {}
UserAgent(const char* str, size_t len) : StringHolder<UserAgent>(str, len) {}
UserAgent(const std::initializer_list<std::string> args) : StringHolder<UserAgent>(args) {}
UserAgent(const UserAgent& other) = default;
UserAgent(UserAgent&& old) noexcept = default;
~UserAgent() override = default;
UserAgent& operator=(UserAgent&& old) noexcept = default;
UserAgent& operator=(const UserAgent& other) = default;
};
} // namespace cpr
#endif

View file

@ -0,0 +1,45 @@
#ifndef CPR_UTIL_H
#define CPR_UTIL_H
#include <fstream>
#include <string>
#include <vector>
#include "cpr/callback.h"
#include "cpr/cookies.h"
#include "cpr/cprtypes.h"
#include "cpr/curlholder.h"
namespace cpr {
namespace util {
Header parseHeader(const std::string& headers, std::string* status_line = nullptr, std::string* reason = nullptr);
Cookies parseCookies(curl_slist* raw_cookies);
size_t readUserFunction(char* ptr, size_t size, size_t nitems, const ReadCallback* read);
size_t headerUserFunction(char* ptr, size_t size, size_t nmemb, const HeaderCallback* header);
size_t writeFunction(char* ptr, size_t size, size_t nmemb, std::string* data);
size_t writeFileFunction(char* ptr, size_t size, size_t nmemb, std::ofstream* file);
size_t writeUserFunction(char* ptr, size_t size, size_t nmemb, const WriteCallback* write);
#if LIBCURL_VERSION_NUM < 0x072000
int progressUserFunction(const ProgressCallback* progress, double dltotal, double dlnow, double ultotal, double ulnow);
#else
int progressUserFunction(const ProgressCallback* progress, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
#endif
int debugUserFunction(CURL* handle, curl_infotype type, char* data, size_t size, const DebugCallback* debug);
std::vector<std::string> split(const std::string& to_split, char delimiter);
std::string urlEncode(const std::string& s);
std::string urlDecode(const std::string& s);
/**
* Override the content of the provided string to hide sensitive data. The
* string content after invocation is undefined. The string size is reset to zero.
* impl. based on:
* https://github.com/ojeda/secure_clear/blob/master/example-implementation/secure_clear.h
**/
void secureStringClear(std::string& s);
bool isTrue(const std::string& s);
} // namespace util
} // namespace cpr
#endif

View file

@ -0,0 +1,18 @@
#ifndef CPR_VERBOSE_H_
#define CPR_VERBOSE_H_
namespace cpr {
class Verbose {
public:
Verbose() = default;
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
Verbose(const bool p_verbose) : verbose{p_verbose} {}
bool verbose = true;
};
} // namespace cpr
#endif /* CPR_VERBOSE_H_ */