diff --git a/core/include/userver/clients/http/client.hpp b/core/include/userver/clients/http/client.hpp index eefe720fcdd0..0be222dafd94 100644 --- a/core/include/userver/clients/http/client.hpp +++ b/core/include/userver/clients/http/client.hpp @@ -136,6 +136,7 @@ class Client final { friend class impl::EasyWrapper; void IncPending() noexcept { ++pending_tasks_; } void DecPending() noexcept { --pending_tasks_; } + void PushIdleEasy(std::shared_ptr&& easy) noexcept; std::shared_ptr TryDequeueIdle() noexcept; diff --git a/core/include/userver/clients/http/form.hpp b/core/include/userver/clients/http/form.hpp index 8ebc08491800..453d0d66b7da 100644 --- a/core/include/userver/clients/http/form.hpp +++ b/core/include/userver/clients/http/form.hpp @@ -25,12 +25,8 @@ class Form final { void AddContent(std::string_view key, std::string_view content, const std::string& content_type); void AddBuffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer); - void AddBuffer( - const std::string& key, - const std::string& file_name, - const std::shared_ptr& buffer, - const std::string& content_type - ); + + void AddBuffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer, const std::string& content_type); /// @cond // Call of this method will invalidate the form @@ -40,7 +36,6 @@ class Form final { private: std::unique_ptr impl_; }; - } // namespace clients::http USERVER_NAMESPACE_END diff --git a/core/include/userver/clients/http/mime.hpp b/core/include/userver/clients/http/mime.hpp new file mode 100644 index 000000000000..a2a6abd6208e --- /dev/null +++ b/core/include/userver/clients/http/mime.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +USERVER_NAMESPACE_BEGIN + +namespace curl { + class mime; +} + +namespace clients::http { + class Mime final { + public: + Mime(); + ~Mime(); + + Mime(const Mime&) = delete; + Mime(Mime&&) noexcept; + + Mime& operator= (const Mime&) = delete; + Mime& operator= (Mime&&) noexcept; + + public: + void AddContent(std::string_view key, std::string_view content); + void AddContent(std::string_view key, std::string_view content, std::string_view content_type); + + void AddBuffer(std::string_view key, std::string_view name, const std::shared_ptr& buffer); + void AddBuffer(std::string_view key, std::string_view name, std::string_view content_type, const std::shared_ptr& buffer); + + public: + std::unique_ptr GetNative() &&; + + private: + std::unique_ptr mime_; + + }; +} + +USERVER_NAMESPACE_END \ No newline at end of file diff --git a/core/include/userver/clients/http/request.hpp b/core/include/userver/clients/http/request.hpp index 0084c79585d3..95db4cf3f82d 100644 --- a/core/include/userver/clients/http/request.hpp +++ b/core/include/userver/clients/http/request.hpp @@ -31,6 +31,7 @@ class RequestState; class StreamedResponse; class ConnectTo; class Form; +class Mime; struct DeadlinePropagationConfig; class RequestStats; class DestinationStatistics; @@ -114,6 +115,9 @@ class Request final { /// POST request with url and multipart/form-data Request& post(std::string url, Form&& form) &; Request post(std::string url, Form&& form) &&; + /// POST request with url and mime + Request& post(std::string url, Mime&& mime) &; + Request post(std::string url, Mime&& mime) &&; /// PUT request Request& put() &; Request put() &&; @@ -148,9 +152,12 @@ class Request final { /// data for POST request Request& data(std::string data) &; Request data(std::string data) &&; - /// form for POST request + /// form POST request Request& form(Form&& form) &; Request form(Form&& form) &&; + // mime for POST request + Request& mime(Mime&& mime) &; + Request mime(Mime&& mime) &&; /// Headers for request as map Request& headers(const Headers& headers) &; Request headers(const Headers& headers) &&; diff --git a/core/src/clients/http/client.cpp b/core/src/clients/http/client.cpp index 4f6eae08f39d..65d836347c1b 100644 --- a/core/src/clients/http/client.cpp +++ b/core/src/clients/http/client.cpp @@ -129,7 +129,7 @@ Request Client::CreateRequest() { try { auto wrapper = engine::AsyncNoSpan(fs_task_processor_, [this, &multi] { - return impl::EasyWrapper{easy_.Get()->GetBoundBlocking(*multi), *this}; + return impl::EasyWrapper { easy_.Get()->GetBoundBlocking(*multi), *this }; }).Get(); return Request{ std::move(wrapper), diff --git a/core/src/clients/http/easy_wrapper.cpp b/core/src/clients/http/easy_wrapper.cpp index d4b92762304a..95dd3fefc3eb 100644 --- a/core/src/clients/http/easy_wrapper.cpp +++ b/core/src/clients/http/easy_wrapper.cpp @@ -20,10 +20,6 @@ EasyWrapper::~EasyWrapper() { } } -curl::easy& EasyWrapper::Easy() { return *easy_; } - -const curl::easy& EasyWrapper::Easy() const { return *easy_; } - } // namespace clients::http::impl USERVER_NAMESPACE_END diff --git a/core/src/clients/http/easy_wrapper.hpp b/core/src/clients/http/easy_wrapper.hpp index 9878722fd59b..414bfe650d23 100644 --- a/core/src/clients/http/easy_wrapper.hpp +++ b/core/src/clients/http/easy_wrapper.hpp @@ -23,10 +23,11 @@ class EasyWrapper final { ~EasyWrapper(); - curl::easy& Easy(); - const curl::easy& Easy() const; + inline curl::easy& Easy() { return *easy_; } + inline const curl::easy& Easy() const { return *easy_; }; private: + //std::shared_ptr easy_; std::shared_ptr easy_; Client& client_; }; diff --git a/core/src/clients/http/form.cpp b/core/src/clients/http/form.cpp index 896508a741a4..c48fa2d8ae8c 100644 --- a/core/src/clients/http/form.cpp +++ b/core/src/clients/http/form.cpp @@ -6,7 +6,7 @@ USERVER_NAMESPACE_BEGIN namespace clients::http { -Form::Form() : impl_(std::make_unique()) {} +Form::Form() : impl_(std::make_unique()) { } Form::~Form() = default; Form::Form(Form&&) noexcept = default; Form& Form::operator=(Form&&) noexcept = default; @@ -21,12 +21,8 @@ void Form::AddBuffer(const std::string& key, const std::string& file_name, const impl_->add_buffer(key, file_name, buffer); } -void Form::AddBuffer( - const std::string& key, - const std::string& file_name, - const std::shared_ptr& buffer, - const std::string& content_type -) { +void Form::AddBuffer(const std::string& key, const std::string& file_name, + const std::shared_ptr& buffer, const std::string& content_type) { impl_->add_buffer(key, file_name, buffer, content_type); } diff --git a/core/src/clients/http/mime.cpp b/core/src/clients/http/mime.cpp new file mode 100644 index 000000000000..a72c69ef5a26 --- /dev/null +++ b/core/src/clients/http/mime.cpp @@ -0,0 +1,37 @@ +#include + +USERVER_NAMESPACE_BEGIN + +namespace clients::http { +Mime::Mime() { + +} + +Mime::~Mime() { + +} + +Mime::Mime(Mime&&) noexcept = default; +Mime& Mime::operator= (Mime&&) noexcept = default; + +std::unique_ptr Mime::GetNative() && { return std::move(mime_); } + +void Mime::AddContent(std::string_view key, std::string_view content) { + mime_->add_content(key, content); +} + +void Mime::AddContent(std::string_view key, std::string_view content, std::string_view content_type) { + mime_->add_content(key, content, content_type); +} + +void Mime::AddBuffer(std::string_view key, std::string_view name, const std::shared_ptr& buffer) { + mime_->add_buffer(key, name, buffer); +} + +void Mime::AddBuffer(std::string_view key, std::string_view name, std::string_view content_type, const std::shared_ptr& buffer) { + mime_->add_buffer(key, name, content_type, buffer); +} + +} // namespace clients::http + +USERVER_NAMESPACE_END \ No newline at end of file diff --git a/core/src/clients/http/plugin.cpp b/core/src/clients/http/plugin.cpp index 7975945a93d2..f4d937e019fb 100644 --- a/core/src/clients/http/plugin.cpp +++ b/core/src/clients/http/plugin.cpp @@ -19,9 +19,13 @@ void PluginRequest::SetHeader(std::string_view name, std::string_view value) { void PluginRequest::AddQueryParams(std::string_view params) { const auto& url = state_.easy().get_original_url(); if (url.find('?') != std::string::npos) { - state_.easy().set_url(utils::StrCat(url, "&", params)); + std::error_code ec; + state_.easy().set_url(utils::StrCat(url, "&", params), ec); + UASSERT_MSG(!ec, "set_url filed!"); } else { - state_.easy().set_url(utils::StrCat(url, "?", params)); + std::error_code ec; + state_.easy().set_url(utils::StrCat(url, "?", params), ec); + UASSERT_MSG(!ec, "set_url filed!"); } } diff --git a/core/src/clients/http/request.cpp b/core/src/clients/http/request.cpp index 8b9fbecb9f3a..4cf0d82b3677 100644 --- a/core/src/clients/http/request.cpp +++ b/core/src/clients/http/request.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -75,21 +76,21 @@ constexpr utils::TrivialBiMap kAuthTypeMap = [](auto selector) { curl::easy::httpauth_t HttpAuthTypeToNative(HttpAuthType value) { switch (value) { case HttpAuthType::kBasic: - return curl::easy::auth_basic; + return curl::easy::httpauth_t::auth_basic; case HttpAuthType::kDigest: - return curl::easy::auth_digest; + return curl::easy::httpauth_t::auth_digest; case HttpAuthType::kDigestIE: - return curl::easy::auth_digest_ie; + return curl::easy::httpauth_t::auth_digest_ie; case HttpAuthType::kNegotiate: - return curl::easy::auth_negotiate; + return curl::easy::httpauth_t::auth_negotiate; case HttpAuthType::kNtlm: - return curl::easy::auth_ntlm; + return curl::easy::httpauth_t::auth_ntlm; case HttpAuthType::kNtlmWb: - return curl::easy::auth_ntlm_wb; + return curl::easy::httpauth_t::auth_ntlm_wb; case HttpAuthType::kAny: - return curl::easy::auth_any; + return curl::easy::httpauth_t::auth_any; case HttpAuthType::kAnySafe: - return curl::easy::auth_any_safe; + return curl::easy::httpauth_t::auth_any_safe; } UINVARIANT(false, "Unexpected http auth type"); @@ -98,23 +99,23 @@ curl::easy::httpauth_t HttpAuthTypeToNative(HttpAuthType value) { curl::easy::proxyauth_t ProxyAuthTypeToNative(ProxyAuthType value) { switch (value) { case ProxyAuthType::kBasic: - return curl::easy::proxy_auth_basic; + return curl::easy::proxyauth_t::proxy_auth_basic; case ProxyAuthType::kDigest: - return curl::easy::proxy_auth_digest; + return curl::easy::proxyauth_t::proxy_auth_digest; case ProxyAuthType::kDigestIE: - return curl::easy::proxy_auth_digest_ie; + return curl::easy::proxyauth_t::proxy_auth_digest_ie; case ProxyAuthType::kBearer: - return curl::easy::proxy_auth_bearer; + return curl::easy::proxyauth_t::proxy_auth_bearer; case ProxyAuthType::kNegotiate: - return curl::easy::proxy_auth_negotiate; + return curl::easy::proxyauth_t::proxy_auth_negotiate; case ProxyAuthType::kNtlm: - return curl::easy::proxy_auth_ntlm; + return curl::easy::proxyauth_t::proxy_auth_ntlm; case ProxyAuthType::kNtlmWb: - return curl::easy::proxy_auth_ntlm_wb; + return curl::easy::proxyauth_t::proxy_auth_ntlm_wb; case ProxyAuthType::kAny: - return curl::easy::proxy_auth_any; + return curl::easy::proxyauth_t::proxy_auth_any; case ProxyAuthType::kAnySafe: - return curl::easy::proxy_auth_anysafe; + return curl::easy::proxyauth_t::proxy_auth_anysafe; } UINVARIANT(false, "Unexpected proxy auth type"); @@ -124,9 +125,9 @@ bool IsUserAgentHeader(std::string_view header_name) { return utils::StrIcaseEqual{}(header_name, USERVER_NAMESPACE::http::headers::kUserAgent); } -void SetUserAgent(curl::easy& easy, const std::string& value) { easy.set_user_agent(value); } +void SetUserAgent(curl::easy& easy, const std::string& value) { easy.set_user_agent(std::string_view(value)); } -void SetUserAgent(curl::easy& easy, std::string_view value) { easy.set_user_agent(std::string{value}); } +void SetUserAgent(curl::easy& easy, std::string_view value) { easy.set_user_agent(value); } template void SetHeaders(curl::easy& easy, const Range& headers_range) { @@ -148,7 +149,7 @@ void SetCookies(curl::easy& easy, const Range& cookies_range) { cookie_str += '='; cookie_str += value; } - easy.set_cookie(cookie_str); + easy.set_cookie(std::string_view(cookie_str)); } template @@ -323,13 +324,13 @@ Request& Request::unix_socket_path(const std::string& path) & { Request Request::unix_socket_path(const std::string& path) && { return std::move(this->unix_socket_path(path)); } Request& Request::use_ipv4() & { - pimpl_->easy().set_ip_resolve(curl::easy::ip_resolve_v4); + pimpl_->easy().set_ip_resolve(curl::easy::ip_resolve_t::ip_resolve_v4); return *this; } Request Request::use_ipv4() && { return std::move(this->use_ipv4()); } Request& Request::use_ipv6() & { - pimpl_->easy().set_ip_resolve(curl::easy::ip_resolve_v6); + pimpl_->easy().set_ip_resolve(curl::easy::ip_resolve_t::ip_resolve_v6); return *this; } Request Request::use_ipv6() && { return std::move(this->use_ipv6()); } @@ -350,10 +351,19 @@ Request Request::data(std::string data) && { return std::move(this->data(std::mo Request& Request::form(Form&& form) & { pimpl_->easy().set_http_post(std::move(form).GetNative()); pimpl_->easy().add_header(kHeaderExpect, "", curl::easy::EmptyHeaderAction::kDoNotSend); - return *this; + return *this; } + Request Request::form(Form&& form) && { return std::move(this->form(std::move(form))); } +Request& Request::mime(Mime&& mime) & { + pimpl_->easy().set_mimepost(std::move(mime).GetNative()); + pimpl_->easy().add_header(kHeaderExpect, "", curl::easy::EmptyHeaderAction::kDoNotSend); + return *this; +} + +Request Request::mime(Mime&& mime) && { return std::move(this->mime(std::move(mime))); }; + Request& Request::headers(const Headers& headers) & { SetHeaders(pimpl_->easy(), headers); return *this; @@ -393,7 +403,7 @@ Request Request::proxy_headers(const std::initializer_listeasy().set_user_agent(value.c_str()); + pimpl_->easy().set_user_agent(std::string_view(value)); return *this; } Request Request::user_agent(const std::string& value) && { return std::move(this->user_agent(value)); } @@ -492,6 +502,9 @@ Request Request::post(std::string url, Form&& form) && { return std::move(this->post(std::move(url), std::move(form))); } +Request& Request::post(std::string url, Mime&& mime) & { return this->url(std::move(url)).mime(std::move(mime)); } +Request Request::post(std::string url, Mime&& mime) && { return std::move(this->post(std::move(url), std::move(mime))); } + Request& Request::post(std::string url, std::string data) & { return this->url(std::move(url)).data(std::move(data)).post(); } diff --git a/core/src/clients/http/request_state.cpp b/core/src/clients/http/request_state.cpp index d29cba16a30e..347bb185bb0b 100644 --- a/core/src/clients/http/request_state.cpp +++ b/core/src/clients/http/request_state.cpp @@ -113,7 +113,7 @@ void SetBaggageHeader(curl::easy& e) { LOG_DEBUG() << fmt::format("Send baggage: {}", baggage->ToString()); e.add_header( USERVER_NAMESPACE::http::headers::kXBaggage, - baggage->ToString(), + std::string_view(baggage->ToString()), curl::easy::EmptyHeaderAction::kDoNotSend, curl::easy::DuplicateHeaderAction::kReplace ); @@ -250,9 +250,7 @@ RequestState::RequestState( } RequestState::~RequestState() { - std::error_code ec; - easy().set_error_buffer(nullptr, ec); - UASSERT(!ec); + easy().set_error_buffer(nullptr); } void RequestState::follow_redirects(bool follow) { @@ -266,7 +264,7 @@ void RequestState::verify(bool verify) { easy().set_ssl_verify_peer(verify); } -void RequestState::ca_info(const std::string& file_path) { easy().set_ca_info(file_path.c_str()); } +void RequestState::ca_info(const std::string& file_path) { easy().set_ca_info(std::string_view(file_path)); } void RequestState::ca(crypto::Certificate cert) { UINVARIANT(cert, "No certificate"); @@ -275,12 +273,15 @@ void RequestState::ca(crypto::Certificate cert) { } else { // Legacy non-portable way, broken since 7.87.0 ca_ = std::move(cert); - easy().set_ssl_ctx_function(&RequestState::on_certificate_request); - easy().set_ssl_ctx_data(this); + std::error_code ec; + easy().set_ssl_ctx_function(&RequestState::on_certificate_request, ec); + UASSERT(!ec); + easy().set_ssl_ctx_data(this, ec); + UASSERT(!ec); } } -void RequestState::crl_file(const std::string& file_path) { easy().set_crl_file(file_path.c_str()); } +void RequestState::crl_file(const std::string& file_path) { easy().set_crl_file(std::string_view(file_path)); } void RequestState::client_key_cert(crypto::PrivateKey pkey, crypto::Certificate cert) { UINVARIANT(pkey, "No private key"); @@ -321,10 +322,16 @@ void RequestState::client_key_cert(crypto::PrivateKey pkey, crypto::Certificate cert_id ); cert_id.resize(kCertIdLength, '='); + +#if LIBCURL_VERSION_NUM <= 0x074700 easy().set_egd_socket(cert_id); +#endif - easy().set_ssl_ctx_function(&RequestState::on_certificate_request); - easy().set_ssl_ctx_data(this); + std::error_code ec; + easy().set_ssl_ctx_function(&RequestState::on_certificate_request, ec); + UASSERT(!ec); + easy().set_ssl_ctx_data(this, ec); + UASSERT(!ec); } } @@ -341,7 +348,7 @@ void RequestState::retry(short retries, bool on_fails) { retry_.on_fails = on_fails; } -void RequestState::unix_socket_path(const std::string& path) { easy().set_unix_socket_path(path); } +void RequestState::unix_socket_path(const std::string& path) { easy().set_unix_socket_path(std::string_view(path)); } void RequestState::connect_to(const ConnectTo& connect_to) { curl::native::curl_slist* ptr = connect_to.GetUnderlying(); @@ -352,7 +359,7 @@ void RequestState::connect_to(const ConnectTo& connect_to) { void RequestState::proxy(const std::string& value) { proxy_url_ = value; - easy().set_proxy(value); + easy().set_proxy(std::string_view(value)); } void RequestState::proxy_auth_type(curl::easy::proxyauth_t value) { easy().set_proxy_auth(value); } @@ -364,8 +371,8 @@ void RequestState::http_auth_type( std::string_view password ) { easy().set_http_auth(value, auth_only); - easy().set_user(std::string{user}.c_str()); - easy().set_password(std::string{password}.c_str()); + easy().set_user(user); + easy().set_password(password); } void RequestState::Cancel() { @@ -388,7 +395,7 @@ void RequestState::SetTestsuiteConfig(const std::shared_ptr& urls) { allowed_urls_extra_ = urls; } -void RequestState::DisableReplyDecoding() { easy().set_accept_encoding(nullptr); } +void RequestState::DisableReplyDecoding() { easy().set_accept_encoding(std::string_view{}); } void RequestState::SetCancellationPolicy(CancellationPolicy cp) { cancellation_policy_ = cp; } @@ -679,8 +686,12 @@ RequestState::async_perform_stream(const std::shared_ptr& queue, utils::i auto& span = span_storage_->Get(); span.AddTag("stream_api", 1); - easy().set_write_function(&RequestState::StreamWriteFunction); - easy().set_write_data(this); + std::error_code ec; + easy().set_write_function(&RequestState::StreamWriteFunction, ec); + UASSERT(!ec); + easy().set_write_data(this, ec); + UASSERT(!ec); + // Force no retries retry_.retries = 1; diff --git a/core/src/curl-ev/easy.cpp b/core/src/curl-ev/easy.cpp index 0b34249f0f06..d279859d7a1b 100644 --- a/core/src/curl-ev/easy.cpp +++ b/core/src/curl-ev/easy.cpp @@ -1,4 +1,4 @@ -/** +/** @file curl-ev/easy.hpp curl-ev: wrapper for integrating libcurl with libev applications Copyright (c) 2013 Oliver Kuckertz See COPYING for license information. @@ -10,19 +10,16 @@ #include #include +#include #include #include #include -#include #include #include #include #include -#include -#include - #include #include #include @@ -34,106 +31,197 @@ USERVER_NAMESPACE_BEGIN namespace curl { -namespace { +namespace detail { + bool is_header_matching_name(std::string_view header, std::string_view name) { + return header.size() > name.size() && utils::StrIcaseEqual()(header.substr(0, name.size()), name) && + (header[name.size()] == ':' || header[name.size()] == ';'); + } -bool IsHeaderMatchingName(std::string_view header, std::string_view name) { - return header.size() > name.size() && utils::StrIcaseEqual()(header.substr(0, name.size()), name) && - (header[name.size()] == ':' || header[name.size()] == ';'); -} + fmt::memory_buffer + create_header_buffer(std::string_view name, std::string_view value, easy::EmptyHeaderAction eha) { + fmt::memory_buffer fmt_mem_buf; + + if (eha == easy::EmptyHeaderAction::kSend && value.empty()) + fmt::format_to(std::back_inserter(fmt_mem_buf), FMT_COMPILE("{};"), name); + else + fmt::format_to(std::back_inserter(fmt_mem_buf), FMT_COMPILE("{}: {}"), name, value); -std::optional -FindHeaderByNameImpl(const std::shared_ptr& headers, std::string_view name) { - if (!headers) return std::nullopt; - auto result = headers->FindIf([name](std::string_view header) { return IsHeaderMatchingName(header, name); }); - if (result) { - result->remove_prefix(name.size() + 1); - while (!result->empty() && result->front() == ' ') result->remove_prefix(1); + fmt_mem_buf.push_back('\0'); + return fmt_mem_buf; } - return result; -} -fmt::memory_buffer CreateHeaderBuffer(std::string_view name, std::string_view value, easy::EmptyHeaderAction action) { - fmt::memory_buffer buf; + std::optional + find_header_by_name(const std::shared_ptr& headers, std::string_view name) { + if (!headers) + return std::nullopt; - if (action == easy::EmptyHeaderAction::kSend && value.empty()) { - fmt::format_to(std::back_inserter(buf), FMT_COMPILE("{};"), name); - } else { - fmt::format_to(std::back_inserter(buf), FMT_COMPILE("{}: {}"), name, value); + auto result = headers->FindIf([name](std::string_view header) { + return is_header_matching_name(header, name); + }); + + if (!result) { + result->remove_prefix(name.size() + 1); + while(!result->empty() && result->front() == ' ') { + result->remove_prefix(1); + } + } + + return result; } - buf.push_back('\0'); + bool add_header_do_skip(const std::shared_ptr& headers, std::string_view name, easy::DuplicateHeaderAction dha) { + if (dha == easy::DuplicateHeaderAction::kSkip && headers) + find_header_by_name(headers, name); + return false; + } - return buf; -} + bool add_header_do_replace(const std::shared_ptr& headers, const fmt::memory_buffer& mem_buf, + std::string_view name, easy::DuplicateHeaderAction dha) { + if (dha == easy::DuplicateHeaderAction::kReplace && headers) { + const bool replaced = headers->ReplaceFirstIf([name](std::string_view header) { + return is_header_matching_name(header, name); + }, mem_buf.data()); -bool AddHeaderDoSkip( - const std::shared_ptr& headers, - std::string_view name, - easy::DuplicateHeaderAction action -) { - if (action == easy::DuplicateHeaderAction::kSkip && headers) { - if (FindHeaderByNameImpl(headers, name)) return true; + if (replaced) + return true; + } + return false; } - return false; -} + template , _Et>> + inline constexpr unsigned long to_integral(_Et val) { + return static_cast>(val); + } -bool AddHeaderDoReplace( - const std::shared_ptr& headers, - const fmt::memory_buffer& buf, - std::string_view name, - easy::DuplicateHeaderAction action -) { - if (action == easy::DuplicateHeaderAction::kReplace && headers) { - const bool replaced = headers->ReplaceFirstIf( - [name](std::string_view header) { return IsHeaderMatchingName(header, name); }, buf.data() - ); + template + inline void set_curl_opt(_FuncName name, _Handle handle, native::CURLoption opt, bool state) { + std::error_code ec = std::error_code {static_cast( + native::curl_easy_setopt(handle, opt, state ? 1L : 0L) + )}; + throw_error(ec, name); + } + + template + inline void set_curl_opt(_FuncName name, _Handle handle, native::CURLoption opt, _OptType arg) { + std::error_code ec = std::error_code {static_cast( + native::curl_easy_setopt(handle, opt, arg) + )}; + throw_error(ec, name); + } - if (replaced) return true; + template + inline std::error_code set_curl_opt(_Handle handle, native::CURLoption opt, _OptType arg) { + std::error_code ec = std::error_code {static_cast( + native::curl_easy_setopt(handle, opt, arg) + )}; + return ec; } - return false; -} + template + inline void set_curl_opt_blob(_FuncName name, std::string_view sv, unsigned int flags, _Handle handle, native::CURLoption opt) { + native::curl_blob blob{}; + blob.data = const_cast(static_cast(sv.data())); + blob.len = sv.size(); + blob.flags = flags; + std::error_code ec = std::error_code {static_cast( + native::curl_easy_setopt(handle, opt, &blob) + )}; + throw_error(ec, name); + } + + template + inline std::error_code get_curl_info(_Handle handle, native::CURLINFO info, _Return value) { + std::error_code ec = std::error_code{static_cast( + native::curl_easy_getinfo(handle, info, value) + )}; + return ec; + } -} // namespace + template + inline std::string_view get_curl_info_string_view(_FuncName name, _Handle handle, native::CURLINFO info) { + char* result = nullptr; + std::error_code ec = get_curl_info(handle, info, &result); + throw_error(ec, name); + return result ? result : std::string_view{}; + } -using BusyMarker = utils::statistics::BusyMarker; + template + inline std::string_view get_curl_info_string_view(_Handle handle, native::CURLINFO info, std::error_code& ec) { + char* result = nullptr; + ec = get_curl_info(handle, info, &result); + return result ? result : std::string_view{}; + } -easy::easy(native::CURL* easy_handle, multi* multi_handle) - : handle_(easy_handle), multi_(multi_handle), construct_ts_(std::chrono::steady_clock::now()) { - UASSERT(handle_); + template + inline long get_curl_info_long(_FuncName name, _Handle handle, native::CURLINFO info) { + long result; + std::error_code ec = get_curl_info(handle, info, &result); + throw_error(ec, name); + return result; + } + + template + inline std::vector get_curl_info_list(_FuncName name, _Handle handle, native::CURLINFO info) { + std::vector results; + struct native::curl_slist* slist; + std::error_code ec = get_curl_info(handle, info, &slist); + throw_error(ec , name); + struct native::curl_slist* it = slist; + + while (it) { + results.emplace_back(it->data); + it = it->next; + } + + native::curl_slist_free_all(slist); + return results; + } + +} // namespace detail + +easy::easy(native::CURL* easy_handle, native::curl_mime* mime_ptr, multi* multi_ptr) : + easy_handle_(easy_handle), + mime_{std::make_shared(mime_ptr)}, + multi_handle_(multi_ptr), + construct_ts_(std::chrono::steady_clock::now()) +{ + UASSERT(easy_handle_); set_private(this); } easy::~easy() { cancel(); - if (handle_) { - native::curl_easy_cleanup(handle_); - handle_ = nullptr; + if (easy_handle_) { + native::curl_easy_cleanup(easy_handle_); + easy_handle_ = nullptr; } } std::shared_ptr easy::CreateBlocking() { impl::CurlGlobal::Init(); - + // Note: curl_easy_init() is blocking. - auto* handle = native::curl_easy_init(); - if (!handle) { - throw std::bad_alloc(); - } + auto* handle_ptr = native::curl_easy_init(); + if (!handle_ptr) { throw std::bad_alloc(); } - return std::make_shared(handle, nullptr); + auto* mime_ptr = native::curl_mime_init(handle_ptr); + if (!mime_ptr) { throw std::bad_alloc(); } + + return std::make_shared(handle_ptr, mime_ptr, nullptr); } std::shared_ptr easy::GetBoundBlocking(multi& multi_handle) const { - // Note: curl_easy_init() is blocking. - auto* cloned = native::curl_easy_duphandle(handle_); - if (!cloned) { + // Note: curl_easy_init() is blocking. + auto* cloned_easy = native::curl_easy_duphandle(easy_handle_); + if (!cloned_easy) throw std::bad_alloc(); - } - return std::make_shared(cloned, &multi_handle); + auto* cloned_mime = mime_->native_mime(); + if (!cloned_mime) + throw std::bad_alloc(); + + return std::make_shared(cloned_easy, cloned_mime, &multi_handle); } easy* easy::from_native(native::CURL* native_easy) { @@ -142,15 +230,18 @@ easy* easy::from_native(native::CURL* native_easy) { return easy_handle; } -engine::ev::ThreadControl& easy::GetThreadControl() { return multi_->GetThreadControl(); } +engine::ev::ThreadControl& easy::GetThreadControl() { + return multi_handle_->GetThreadControl(); +} void easy::async_perform(handler_type handler) { LOG_TRACE() << "easy::async_perform start " << this; size_t request_num = ++request_counter_; - if (multi_) { - multi_->GetThreadControl().RunInEvLoopAsync( + + if (multi_handle_) { + multi_handle_->GetThreadControl().RunInEvLoopAsync( [self = shared_from_this(), this, handler = std::move(handler), request_num]() mutable { - return do_ev_async_perform(std::move(handler), request_num); + return do_ev_async_perform(handler, request_num); } ); } else { @@ -159,604 +250,1378 @@ void easy::async_perform(handler_type handler) { LOG_TRACE() << "easy::async_perform finished " << this; } +void easy::reset() { + LOG_TRACE() << "easy::reset start " << this; + + orig_url_str_.clear(); + std::string{}.swap(post_fields_); + mime_.reset(); + + if (headers_) + headers_->clear(); + if (proxy_headers_) + proxy_headers_->clear(); + if (http200_aliases_) + http200_aliases_->clear(); + if (resolved_hosts_) + resolved_hosts_->clear(); + + share_.reset(); + + retries_counter_ = 0; + sockets_opened_ = 0; + rate_limit_error_.clear(); + + set_custom_request(nullptr); + set_no_body(false); + set_post(false); + + std::error_code ec; + set_ssl_ctx_data(nullptr, ec); + if (!ec) { + set_ssl_ctx_function(nullptr, ec); + } else if(ec != errc::EasyErrorCode::kNotBuiltIn) { + throw_error(ec, "set_ssl_ctx_data"); + } + + UASSERT(!multi_registered_); + native::curl_easy_reset(easy_handle_); + set_private(this); + + LOG_TRACE() << "easy::reset finished " << this; +} + +using BusyMarker = utils::statistics::BusyMarker; + void easy::do_ev_async_perform(handler_type handler, size_t request_num) { if (request_num <= cancelled_request_max_) { - LOG_DEBUG() << "already cancelled"; + LOG_DEBUG() << "async_perform requests allready canceled"; return; } LOG_TRACE() << "easy::do_ev_async_perform start " << this; mark_start_performing(); - if (!multi_) { - throw std::runtime_error("attempt to perform async. operation without assigning a multi object"); - } - BusyMarker busy(multi_->Statistics().get_busy_storage()); + if (!multi_handle_) + std::runtime_error("Attempt to perform async. Operation without assigning a multi object."); + + BusyMarker busy(multi_handle_->Statistics().get_busy_storage()); - // Cancel all previous async. operations cancel(request_num - 1); - // Keep track of all new sockets - set_opensocket_function(&easy::opensocket); + // open sock func & data + set_opensocket_function(&easy::open_socket); set_opensocket_data(this); - - // This one is tricky: Although sockets are opened in the context of an easy - // object, they can outlive the easy objects and be transferred into a multi - // object's connection pool. Why there is no connection pool interface in the - // multi interface to plug into to begin with is still a mystery to me. Either - // way, the close events have to be tracked by the multi object as sockets are - // usually closed when curl_multi_cleanup is invoked. - set_closesocket_function(&easy::closesocket); - set_closesocket_data(multi_); + + // close sock func & data + set_closesocket_function(&easy::close_socket); + set_closesocket_data(multi_handle_); handler_ = std::move(handler); multi_registered_ = true; - // Registering the easy handle with the multi handle might invoke a set of - // callbacks right away which cause the completion event to fire from within - // this function. LOG_TRACE() << "easy::do_ev_async_perform before multi_->add() " << this; - multi_->add(this); -} - -void easy::cancel() { cancel(request_counter_); } - -void easy::cancel(size_t request_num) { - if (multi_) { - multi_->GetThreadControl().RunInEvLoopSync([this, request_num] { do_ev_cancel(request_num); }); - } + multi_handle_->add(this); } void easy::do_ev_cancel(size_t request_num) { // RunInEvLoopAsync(do_ev_async_perform) and RunInEvLoopSync(do_ev_cancel) are // not synchronized. So we need to count last cancelled request to prevent its - // execution in do_ev_async_perform(). - if (cancelled_request_max_ < request_num) cancelled_request_max_ = request_num; + // execution in do_ev_async_perform() + if (cancelled_request_max_ < request_num) + cancelled_request_max_ = request_num; if (multi_registered_) { - BusyMarker busy(multi_->Statistics().get_busy_storage()); - + BusyMarker busy(multi_handle_->Statistics().get_busy_storage()); handle_completion(std::make_error_code(std::errc::operation_canceled)); - multi_->remove(this); + multi_handle_->remove(this); } } -void easy::reset() { - LOG_TRACE() << "easy::reset start " << this; - - orig_url_str_.clear(); - std::string{}.swap(post_fields_); // forced memory freeing - form_.reset(); - if (headers_) headers_->clear(); - if (proxy_headers_) proxy_headers_->clear(); - if (http200_aliases_) http200_aliases_->clear(); - if (resolved_hosts_) resolved_hosts_->clear(); - share_.reset(); - retries_count_ = 0; - sockets_opened_ = 0; - rate_limit_error_.clear(); +void easy::mark_start_performing() { + if (start_performing_ts_ == time_point()) + start_performing_ts_ = std::chrono::steady_clock::now(); +} - set_custom_request(nullptr); - set_no_body(false); - set_post(false); +void easy::mark_open_socket() { + ++sockets_opened_; +} - // MAC_COMPAT: Secure Transport does not provide these - std::error_code ec; - set_ssl_ctx_data(nullptr, ec); - if (!ec) { - set_ssl_ctx_function(nullptr); - } else if (ec != errc::EasyErrorCode::kNotBuiltIn) { - throw_error(ec, "set_ssl_ctx_data"); - } +void easy::mark_retry() { + ++retries_counter_; +} - UASSERT(!multi_registered_); - native::curl_easy_reset(handle_); - set_private(this); +void easy::handle_completion(const std::error_code& ec) { + LOG_TRACE() << "easy::handle_completion easy = " << this; + multi_registered_ = false; + auto handler = std::function([](std::error_code) {}); + std::swap(handler, handler_); - LOG_TRACE() << "easy::reset finished " << this; + /* It's OK to call handler in libev thread context as it is limited to + * Request::on_retry and Request::on_completed. All user code is executed in + * coro context. + */ + handler(ec); } -void easy::mark_start_performing() { - if (start_performing_ts_ == time_point{}) { - start_performing_ts_ = std::chrono::steady_clock::now(); - } +void easy::cancel() { + cancel(request_counter_); } -void easy::mark_open_socket() { ++sockets_opened_; } -void easy::set_source(std::shared_ptr source) { - std::error_code ec; - set_source(std::move(source), ec); - throw_error(ec, "set_source"); +void easy::cancel(size_t request_num) { + if(multi_handle_) + multi_handle_->GetThreadControl().RunInEvLoopSync([this, request_num] { do_ev_cancel(request_num); }); } -void easy::set_source(std::shared_ptr source, std::error_code& ec) { - source_ = std::move(source); - set_read_function(&easy::read_function, ec); - if (!ec) set_read_data(this, ec); - if (!ec) set_seek_function(&easy::seek_function, ec); - if (!ec) set_seek_data(this, ec); +std::error_code easy::rate_limit_error() const { + return rate_limit_error_; } -void easy::set_sink(std::string* sink) { - std::error_code ec; - set_sink(sink, ec); - throw_error(ec, "set_sink"); +easy::time_point::duration easy::time_to_start() const { + if (start_performing_ts_ != time_point{}) + return start_performing_ts_ - construct_ts_; + return {}; } -size_t easy::header_function(void*, size_t size, size_t nmemb, void*) { return size * nmemb; } +clients::http::LocalStats easy::get_local_stats() { + clients::http::LocalStats stats; -void easy::set_sink(std::string* sink, std::error_code& ec) { - sink_ = sink; - set_write_function(&easy::write_function); - if (!ec) set_write_data(this); -} + stats.open_socket_count = sockets_opened_; + stats.retries_count = retries_counter_; + stats.time_to_connect = std::chrono::microseconds(get_connect_time_usec()); + stats.time_to_process = std::chrono::microseconds(get_total_time_usec()); -void easy::unset_progress_callback() { - set_no_progress(true); - set_xferinfo_function(nullptr); - set_xferinfo_data(nullptr); + return stats; } -void easy::set_progress_callback(progress_callback_t progress_callback) { - progress_callback_ = std::move(progress_callback); - set_no_progress(false); - set_xferinfo_function(&easy::xferinfo_function); - set_xferinfo_data(this); +const std::string& easy::get_post_data() const { + return post_fields_; } -void easy::set_url(std::string url_str) { - std::error_code ec; - set_url(std::move(url_str), ec); - throw_error(ec, "set_url"); +std::string easy::extract_post_data() { + auto result = std::move(post_fields_); + set_post_fields({}); + return result; } -void easy::set_url(std::string&& url_str, std::error_code& ec) { - url_.SetAbsoluteUrl(url_str.c_str(), ec); - if (!ec) { - ec = static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_CURLU, url_.native_handle()) - ); - UASSERT(!ec); - } - - if (!ec) { - orig_url_str_ = std::move(url_str); - } else { - orig_url_str_.clear(); - } +bool easy::has_post_data() const { + return !post_fields_.empty() || mime_; } -const std::string& easy::get_original_url() const { return orig_url_str_; } - -const url& easy::get_easy_url() const { return url_; } +void easy::add_header(std::string_view name, std::string_view value, EmptyHeaderAction eha, DuplicateHeaderAction dha) { + if (detail::add_header_do_skip(headers_, name, dha)) { return; } + auto buffer = detail::create_header_buffer(name, value, eha); + if (detail::add_header_do_replace(headers_, buffer, name, dha)) { return; } + add_header(buffer.data()); +} -void easy::set_post_fields(std::string&& post_fields) { - std::error_code ec; - set_post_fields(std::move(post_fields), ec); - throw_error(ec, "set_post_fields"); +void easy::add_header(std::string_view name, std::string_view value, DuplicateHeaderAction dha) { + add_header(name, value, EmptyHeaderAction::kSend, dha); } -void easy::set_post_fields(std::string&& post_fields, std::error_code& ec) { - post_fields_ = std::move(post_fields); - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_POSTFIELDS, post_fields_.c_str()) - )}; +void easy::add_header(const char* header) { + if (!headers_) + headers_ = std::make_shared(); + + headers_->add(header); + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_HTTPHEADER, headers_->native_handle()); + throw_error(ec, "add_header"); +} - if (!ec) set_post_field_size_large(static_cast(post_fields_.length()), ec); +void easy::add_header(const std::string& header) { + add_header(header.c_str()); } -void easy::set_http_post(std::unique_ptr
form) { - std::error_code ec; - set_http_post(std::move(form), ec); - throw_error(ec, "set_http_post"); +void easy::add_proxy_header(std::string_view name, std::string_view value, EmptyHeaderAction eha, DuplicateHeaderAction dha) { + if (detail::add_header_do_skip(proxy_headers_, name, dha)) { return; } + auto buffer = detail::create_header_buffer(name, value, eha); + if (detail::add_header_do_replace(proxy_headers_, buffer, name, dha)) { return; } + add_proxy_header(buffer.data()); } -void easy::set_http_post(std::unique_ptr form, std::error_code& ec) { - form_ = std::move(form); +void easy::add_proxy_header(const char* header) { + if (!proxy_headers_) + proxy_headers_ = std::make_shared(); - if (form_) { - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_HTTPPOST, form_->native_handle()) - )}; - } else { - ec = std::error_code{ - static_cast(native::curl_easy_setopt(handle_, native::CURLOPT_HTTPPOST, NULL))}; - } + proxy_headers_->add(header); + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_PROXYHEADER, proxy_headers_->native_handle()); + throw_error(ec, "add_proxy_header"); } -void easy::add_header( - std::string_view name, - std::string_view value, - EmptyHeaderAction empty_header_action, - DuplicateHeaderAction duplicate_header_action -) { - std::error_code ec; - add_header(name, value, ec, empty_header_action, duplicate_header_action); - throw_error(ec, "add_header"); +void easy::add_proxy_header(const std::string& header) { + add_proxy_header(header.c_str()); } -void easy::add_header( - std::string_view name, - std::string_view value, - std::error_code& ec, - EmptyHeaderAction empty_header_action, - DuplicateHeaderAction duplicate_header_action -) { - if (AddHeaderDoSkip(headers_, name, duplicate_header_action)) { - return; - } +void easy::add_resolve(const std::string& host, const std::string& port, const std::string& addr) { + if (!resolved_hosts_) + resolved_hosts_ = std::make_shared(); - auto buf = CreateHeaderBuffer(name, value, empty_header_action); + auto host_port_addr = utils::StrCat(host, ":", port, ":", addr); + const std::string_view host_port_view {host_port_addr.data(), host_port_addr.size() - addr.size()}; - if (AddHeaderDoReplace(headers_, buf, name, duplicate_header_action)) { - return; + if (!resolved_hosts_->ReplaceFirstIf( + [host_port_view](const auto& entry) { + // host_port_addr, of which we hold a string_view, might be moved in + // ReplaceFirstIf, but it's guaranteed that this + // lambda is not called after that. + return std::string_view{entry}.substr(host_port_view.size()) == host_port_view; + }, + std::move(host_port_addr) + )) { + UASSERT_MSG(!host_port_addr.empty(), "ReplaceFirstIf moved the string out, when it shouldn't have done so."); + resolved_hosts_->add(std::move(host_port_addr)); } - add_header(buf.data(), ec); -} + set_resolves(resolved_hosts_); -void easy::add_header(std::string_view name, std::string_view value, DuplicateHeaderAction duplicate_header_action) { - std::error_code ec; - add_header(name, value, ec, duplicate_header_action); - throw_error(ec, "add_header"); + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_RESOLVE, resolved_hosts_->native_handle()); + + throw_error(ec, "add_resolve"); } -void easy::add_header( - std::string_view name, - std::string_view value, - std::error_code& ec, - DuplicateHeaderAction duplicate_header_action -) { - add_header(name, value, ec, EmptyHeaderAction::kSend, duplicate_header_action); +void easy::add_http200_alias(const std::string& http200_alias) { + if (!http200_aliases_) + http200_aliases_ = std::make_shared(); + + http200_aliases_->add(http200_alias); + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_HTTP200ALIASES, http200_aliases_->native_handle()); + throw_error(ec, "add_http200_alias"); } std::optional easy::FindHeaderByName(std::string_view name) const { - return FindHeaderByNameImpl(headers_, name); + return detail::find_header_by_name(headers_, name); } -void easy::add_header(const char* header) { - std::error_code ec; - add_header(header, ec); - throw_error(ec, "add_header"); -} +// setters -void easy::add_header(const char* header, std::error_code& ec) { - if (!headers_) { - headers_ = std::make_shared(); +void easy::set_share(std::shared_ptr share) { + share_ = std::move(share); + + if (share) { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_SHARE, share->native_handle()); + throw_error(ec, "set_share native_handle failed!"); + } else { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_SHARE, nullptr); + throw_error(ec, "set_share nullptr failed!"); } +} - headers_->add(header); - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_HTTPHEADER, headers_->native_handle()) - )}; +// cURL Opt setters +void easy::set_verbose(bool state) { + detail::set_curl_opt( + "set_verbose", easy_handle_, native::CURLOPT_VERBOSE, state); } -void easy::add_header(const std::string& header) { add_header(header.c_str()); } +void easy::set_header(bool state) { + detail::set_curl_opt( + "set_header", easy_handle_, native::CURLOPT_HEADER, state); +} -void easy::add_header(const std::string& header, std::error_code& ec) { add_header(header.c_str(), ec); } +void easy::set_no_progress(bool state) { + detail::set_curl_opt( + "set_no_progress", easy_handle_, native::CURLOPT_NOPROGRESS, state); +} -void easy::set_headers(std::shared_ptr headers) { - std::error_code ec; - set_headers(std::move(headers), ec); - throw_error(ec, "set_headers"); +void easy::set_no_signal(bool state) { + detail::set_curl_opt( + "set_no_signal", easy_handle_, native::CURLOPT_NOSIGNAL, state); } -void easy::set_headers(std::shared_ptr headers, std::error_code& ec) { - headers_ = std::move(headers); +void easy::set_wildcard_match(bool state) { + detail::set_curl_opt( + "set_wildcard_match", easy_handle_, native::CURLOPT_WILDCARDMATCH, state); +} - if (headers_) { - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_HTTPHEADER, headers_->native_handle()) - )}; - } else { - ec = std::error_code{ - static_cast(native::curl_easy_setopt(handle_, native::CURLOPT_HTTPHEADER, NULL))}; - } +void easy::set_post(bool state) { + detail::set_curl_opt( + "set_post", easy_handle_, native::CURLOPT_POST, state); } -void easy::add_proxy_header( - std::string_view name, - std::string_view value, - EmptyHeaderAction empty_header_action, - DuplicateHeaderAction duplicate_header_action -) { +void easy::set_source(std::shared_ptr source) { std::error_code ec; - add_proxy_header(name, value, ec, empty_header_action, duplicate_header_action); - throw_error(ec, "add_proxy_header"); + source_ = std::move(source); + set_read_function(&easy::read_runction, ec); + if (!ec) + set_read_data(this, ec); + if (!ec) + set_seek_function(&easy::seek_function, ec); + if (!ec) + set_seek_data(this, ec); + + throw_error(ec, "set_source"); } -void easy::add_proxy_header( - std::string_view name, - std::string_view value, - std::error_code& ec, - EmptyHeaderAction empty_header_action, - DuplicateHeaderAction duplicate_header_action -) { - if (AddHeaderDoSkip(proxy_headers_, name, duplicate_header_action)) { - return; - } +void easy::set_sink(std::string* sink) { + std::error_code ec; + sink_ = sink; + set_write_function(&easy::write_function, ec); + if (!ec) + set_write_data(this, ec); + throw_error(ec, "set_sink"); +} + +void easy::set_private(void* ptr) { + detail::set_curl_opt( + "set_private", easy_handle_, native::CURLOPT_PRIVATE, ptr); +} - auto buf = CreateHeaderBuffer(name, value, empty_header_action); +void easy::set_custom_request(const std::string& str) { + detail::set_curl_opt( + "set_custom_request", easy_handle_, native::CURLOPT_CUSTOMREQUEST, str.c_str()); +} - if (AddHeaderDoReplace(proxy_headers_, buf, name, duplicate_header_action)) { - return; - } +void easy::set_new_directory_perms(long perms) { + detail::set_curl_opt( + "set_new_directory_perms", easy_handle_, native::CURLOPT_NEW_DIRECTORY_PERMS, perms); +} - add_proxy_header(buf.data(), ec); +void easy::set_new_file_perms(long perms) { + detail::set_curl_opt( + "set_new_file_perms", easy_handle_, native::CURLOPT_NEW_FILE_PERMS, perms); } -void easy::add_proxy_header(const char* header, std::error_code& ec) { - if (!proxy_headers_) { - proxy_headers_ = std::make_shared(); +void easy::set_url(std::string&& url_str, std::error_code& ec) { + url_.SetAbsoluteUrl(url_str.c_str(), ec); + if (!ec) { + detail::set_curl_opt( + "set_url", easy_handle_, native::CURLOPT_URL, url_.native_handle()); + throw_error(ec, "set_url native_handle failed!"); } - proxy_headers_->add(header); - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_PROXYHEADER, proxy_headers_->native_handle()) - )}; + if (!ec) + orig_url_str_ = std::move(url_str); + else + orig_url_str_.clear(); } -void easy::add_http200_alias(const std::string& http200_alias) { - std::error_code ec; - add_http200_alias(http200_alias, ec); - throw_error(ec, "add_http200_alias"); +void easy::set_progress_data(void* ptr) { + detail::set_curl_opt( + "set_progress_data", easy_handle_, native::CURLOPT_XFERINFODATA, ptr); // CURLOPT_PROGRESSDATA } -void easy::add_http200_alias(const std::string& http200_alias, std::error_code& ec) { - if (!http200_aliases_) { - http200_aliases_ = std::make_shared(); - } - - http200_aliases_->add(http200_alias); - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_HTTP200ALIASES, http200_aliases_->native_handle()) - )}; +void easy::set_error_buffer(char* buffer) { + detail::set_curl_opt( + "set_error_buffer", easy_handle_, native::CURLOPT_ERRORBUFFER, buffer); } -void easy::set_http200_aliases(std::shared_ptr http200_aliases) { - std::error_code ec; - set_http200_aliases(std::move(http200_aliases), ec); - throw_error(ec, "set_http200_aliases"); +void easy::set_stderr(FILE* file) { + detail::set_curl_opt( + "set_stderr", easy_handle_, native::CURLOPT_STDERR, file); } -void easy::set_http200_aliases(std::shared_ptr http200_aliases, std::error_code& ec) { - http200_aliases_ = std::move(http200_aliases); +void easy::set_fail_on_error(bool state) { + detail::set_curl_opt( + "set_fail_on_error", easy_handle_, native::CURLOPT_FAILONERROR, state); +} - if (http200_aliases) { - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_HTTP200ALIASES, http200_aliases_->native_handle()) - )}; +void easy::set_mimepost(std::unique_ptr mime_ptr) { + mime_ = std::move(mime_ptr); + if (mime_) { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_MIMEPOST, mime_->native_mime()); + throw_error(ec, "set_mimepost native_mime failed!"); } else { - ec = std::error_code{ - static_cast(native::curl_easy_setopt(handle_, native::CURLOPT_HTTP200ALIASES, nullptr) - )}; + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_MIMEPOST, nullptr); + throw_error(ec, "set_mimepost nullptr failed!"); } } -void easy::add_resolve(const std::string& host, const std::string& port, const std::string& addr) { +void easy::set_http_proxy_tunnel(bool state) { + detail::set_curl_opt( + "set_http_proxy_tunnel", easy_handle_, native::CURLOPT_HTTPPROXYTUNNEL, state); +} + +void easy::set_socks5_gsapi_nec(bool state) { + detail::set_curl_opt( + "set_socks5_gsapi_nec", easy_handle_, native::CURLOPT_SOCKS5_GSSAPI_NEC, state); +} + +void easy::set_tcp_no_delay(bool state) { + detail::set_curl_opt( + "set_tcp_no_delay", easy_handle_, native::CURLOPT_TCP_NODELAY, state); +} + +void easy::set_tcp_keep_alive(bool state) { + detail::set_curl_opt( + "set_tcp_keep_alive", easy_handle_, native::CURLOPT_TCP_KEEPALIVE, state); +} + +void easy::set_auto_referrer(bool state) { + detail::set_curl_opt( + "set_auto_referrer", easy_handle_, native::CURLOPT_AUTOREFERER, state); +} + +void easy::set_follow_location(bool state) { + detail::set_curl_opt( + "set_follow_location", easy_handle_, native::CURLOPT_FOLLOWLOCATION, state); +} + +void easy::set_unrestricted_auth(bool state) { + detail::set_curl_opt( + "set_unrestricted_auth", easy_handle_, native::CURLOPT_UNRESTRICTED_AUTH, state); +} + +void easy::set_max_redirs(long value) { + detail::set_curl_opt( + "set_max_redirs", easy_handle_, native::CURLOPT_MAXREDIRS, value); +} +void easy::set_post_redir(long value) { + detail::set_curl_opt( + "set_post_redir", easy_handle_, native::CURLOPT_POSTREDIR, value); +} + +void easy::set_post_field_size(long value) { + detail::set_curl_opt( + "set_post_field_size", easy_handle_, native::CURLOPT_POSTFIELDSIZE, value); +} + +void easy::set_proxy_port(long value) { + detail::set_curl_opt( + "set_proxy_port", easy_handle_, native::CURLOPT_PROXYPORT, value); +} + +void easy::set_proxy_type(long value) { + detail::set_curl_opt( + "set_proxy_port", easy_handle_, native::CURLOPT_PROXYTYPE, value); +} + +void easy::set_local_port(long value) { + detail::set_curl_opt( + "set_local_port", easy_handle_, native::CURLOPT_LOCALPORT, value); +} + +void easy::set_local_port_range(long value) { + detail::set_curl_opt( + "set_local_port_range", easy_handle_, native::CURLOPT_LOCALPORTRANGE, value); +} + +void easy::set_dns_cache_timeout(long value) { + detail::set_curl_opt( + "set_dns_cache_timeout", easy_handle_, native::CURLOPT_DNS_CACHE_TIMEOUT, value); +} + +void easy::set_buffer_size(long value) { + detail::set_curl_opt( + "set_buffer_size", easy_handle_, native::CURLOPT_BUFFERSIZE, value); +} + +void easy::set_port(long value) { + detail::set_curl_opt( + "set_port", easy_handle_, native::CURLOPT_PORT, value); +} + +void easy::set_address_scope(long value) { + detail::set_curl_opt( + "set_address_scope", easy_handle_, native::CURLOPT_ADDRESS_SCOPE, value); +} + +void easy::set_tcp_keep_idle(long value) { + detail::set_curl_opt( + "set_tcp_keep_idle", easy_handle_, native::CURLOPT_TCP_KEEPIDLE, value); +} + +void easy::set_tcp_keep_intvl(long value) { + detail::set_curl_opt( + "set_tcp_keep_intvl", easy_handle_, native::CURLOPT_TCP_KEEPINTVL, value); +} + +void easy::set_connect_to(native::curl_slist* slist) { + detail::set_curl_opt( + "set_connect_to", easy_handle_, native::CURLOPT_CONNECT_TO, slist); +} + +void easy::set_post_field_size_large(native::curl_off_t value) { + detail::set_curl_opt( + "set_post_field_size_large", easy_handle_, native::CURLOPT_POSTFIELDSIZE_LARGE, value); +} + +void easy::set_post_fields(void* ptr) { + detail::set_curl_opt( + "set_post_fields", easy_handle_, native::CURLOPT_POSTFIELDS, ptr); +} + +void easy::set_post_fields(std::string&& post_fields) { + post_fields_ = std::move(post_fields); std::error_code ec; - add_resolve(host, port, addr, ec); - throw_error(ec, "add_resolve"); + if (!post_fields_.empty()) + ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_POSTFIELDS, post_fields_.data()); + + if (!ec) + set_post_field_size_large(static_cast(post_fields_.size())); + + throw_error(ec, "set_post_fields"); +} + +void easy::set_proxy(std::string_view sv) { + detail::set_curl_opt( + "set_proxy", easy_handle_, native::CURLOPT_PROXY, sv.data()); +} + +void easy::set_no_proxy(std::string_view sv) { + detail::set_curl_opt( + "set_no_proxy", easy_handle_, native::CURLOPT_NOPROXY, sv.data()); +} + +void easy::set_interface(std::string_view sv) { + detail::set_curl_opt( + "set_interface", easy_handle_, native::CURLOPT_INTERFACE, sv.data()); +} + +void easy::set_unix_socket_path(std::string_view sv) { + detail::set_curl_opt( + "set_unix_socket_path", easy_handle_, native::CURLOPT_UNIX_SOCKET_PATH, sv.data()); +} + +void easy::set_accept_encoding(std::string_view sv) { + detail::set_curl_opt( + "set_accept_encoding", easy_handle_, native::CURLOPT_ACCEPT_ENCODING, sv.data()); +} + +void easy::set_transfer_encoding(std::string_view sv) { + detail::set_curl_opt( + "set_transfer_encoding", easy_handle_, native::CURLOPT_TRANSFER_ENCODING, sv.data()); +} + +void easy::set_referer(std::string_view sv) { + detail::set_curl_opt( + "set_referer", easy_handle_, native::CURLOPT_REFERER, sv.data()); +} + +void easy::set_user_agent(std::string_view sv) { + detail::set_curl_opt( + "set_user_agent", easy_handle_, native::CURLOPT_USERAGENT, sv.data()); +} + +void easy::set_protocols_str(std::string_view sv) { + detail::set_curl_opt( + "set_protocols", easy_handle_, native::CURLOPT_PROTOCOLS_STR, sv.data()); +} + +void easy::set_redir_protocols_str(std::string_view sv) { + detail::set_curl_opt( + "set_redir_protocols_str", easy_handle_, native::CURLOPT_REDIR_PROTOCOLS_STR, sv.data()); +} + +void easy::set_headers(std::shared_ptr headers) { + headers_ = std::move(headers); + + if (headers_) { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_HTTPHEADER, headers_->native_handle()); + throw_error(ec, "set_headers native_handle failed!"); + } else { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_HTTPHEADER, nullptr); + throw_error(ec, "set_headers nullptr failed!"); + } +} + +void easy::set_http200_aliases(std::shared_ptr http200_aliases) { + http200_aliases_ = std::move(http200_aliases); + + if (http200_aliases_) { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_HTTP200ALIASES, http200_aliases_->native_handle()); + throw_error(ec, "set_http200_aliases native_handle failed!"); + } else { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_HTTP200ALIASES, nullptr); + throw_error(ec, "set_http200_aliases nullptr failed!"); + } +} + +void easy::set_tls_auth_type(long value) { + detail::set_curl_opt( + "set_tls_auth_type", easy_handle_, native::CURLOPT_TLSAUTH_TYPE, value); +} + +void easy::set_tls_auth_user(std::string_view sv) { + detail::set_curl_opt( + "set_tls_auth_user", easy_handle_, native::CURLOPT_TLSAUTH_USERNAME, sv.data()); +} + +void easy::set_tls_auth_password(std::string_view sv) { + detail::set_curl_opt( + "set_tls_auth_password", easy_handle_, native::CURLOPT_TLSAUTH_PASSWORD, sv.data()); +} + +void easy::set_netrc_file(std::string_view sv) { + detail::set_curl_opt( + "set_netrc_file", easy_handle_, native::CURLOPT_NETRC_FILE, sv.data()); +} + +void easy::set_user(std::string_view sv) { + detail::set_curl_opt( + "set_user", easy_handle_, native::CURLOPT_USERNAME, sv.data()); +} + +void easy::set_password(std::string_view sv) { + detail::set_curl_opt( + "set_password", easy_handle_, native::CURLOPT_PASSWORD, sv.data()); +} + +void easy::set_proxy_user(std::string_view sv) { + detail::set_curl_opt( + "set_proxy_user", easy_handle_, native::CURLOPT_PROXYUSERNAME, sv.data()); +} + +void easy::set_proxy_password(std::string_view sv) { + detail::set_curl_opt( + "set_proxy_password", easy_handle_, native::CURLOPT_PROXYPASSWORD, sv.data()); +} + +void easy::set_netrc(netrc_t netrc) { + detail::set_curl_opt( + "set_netrc", easy_handle_, native::CURLOPT_NETRC, detail::to_integral(netrc)); +} + +void easy::set_http_auth(httpauth_t httpauth, bool auth_only) { + auto result = detail::to_integral(httpauth) | (auth_only ? CURLAUTH_ONLY : 0UL); + detail::set_curl_opt( + "set_http_auth", easy_handle_, native::CURLOPT_HTTPAUTH, result); +} + +void easy::set_proxy_auth(proxyauth_t proxyauth) { + detail::set_curl_opt( + "set_proxy_auth", easy_handle_, native::CURLOPT_PROXYAUTH, detail::to_integral(proxyauth)); +} + +void easy::set_transfer_text(bool state) { + detail::set_curl_opt( + "set_transfer_text", easy_handle_, native::CURLOPT_TRANSFERTEXT, state); +} + +void easy::set_transfer_mode(bool state) { + detail::set_curl_opt( + "set_transfer_mode", easy_handle_, native::CURLOPT_PROXY_TRANSFER_MODE, state); +} + +void easy::set_crlf(bool state) { + detail::set_curl_opt( + "set_crlf", easy_handle_, native::CURLOPT_CRLF, state); +} + +void easy::set_file_time(bool state) { + detail::set_curl_opt( + "set_file_time", easy_handle_, native::CURLOPT_FILETIME, state); +} + +void easy::set_upload(bool state) { + detail::set_curl_opt( + "set_upload", easy_handle_, native::CURLOPT_UPLOAD, state); +} + +void easy::set_no_body(bool state) { + detail::set_curl_opt( + "set_no_body", easy_handle_, native::CURLOPT_NOBODY, state); +} + +void easy::set_ignore_content_length(bool state) { + detail::set_curl_opt( + "set_ignore_content_length", easy_handle_, native::CURLOPT_IGNORE_CONTENT_LENGTH, state); +} + +void easy::set_http_content_decoding(bool state) { + detail::set_curl_opt( + "set_http_content_decoding", easy_handle_, native::CURLOPT_HTTP_CONTENT_DECODING, state); +} + +void easy::set_http_transfer_decoding(bool state) { + detail::set_curl_opt( + "set_http_transfer_decoding", easy_handle_, native::CURLOPT_HTTP_TRANSFER_DECODING, state); +} + +void easy::set_http_get(bool state) { + detail::set_curl_opt( + "set_http_get", easy_handle_, native::CURLOPT_HTTPGET, state); +} + +void easy::set_cookie_session(bool state) { + detail::set_curl_opt( + "set_cookie_session", easy_handle_, native::CURLOPT_COOKIESESSION, state); +} + +void easy::set_resume_from(long value) { + detail::set_curl_opt( + "set_resume_from", easy_handle_, native::CURLOPT_RESUME_FROM, value); +} + +void easy::set_in_file_size(long value) { + detail::set_curl_opt( + "set_in_file_size", easy_handle_, native::CURLOPT_INFILESIZE, value); +} + +void easy::set_max_file_size(long value) { + detail::set_curl_opt( + "set_max_file_size", easy_handle_, native::CURLOPT_MAXFILESIZE, value); +} + +void easy::set_time_value(long value) { + detail::set_curl_opt( + "set_time_value", easy_handle_, native::CURLOPT_TIMEVALUE, value); +} + +void easy::set_resume_from_large(native::curl_off_t value) { + detail::set_curl_opt( + "set_resume_from_large", easy_handle_, native::CURLOPT_RESUME_FROM_LARGE, value); +} + +void easy::set_in_file_size_large(native::curl_off_t value) { + detail::set_curl_opt( + "set_in_file_size_large", easy_handle_, native::CURLOPT_INFILESIZE_LARGE, value); +} + +void easy::set_max_file_size_large(native::curl_off_t value) { + detail::set_curl_opt( + "set_max_file_size_large", easy_handle_, native::CURLOPT_MAXFILESIZE_LARGE, value); +} + +void easy::set_range(std::string_view sv) { + detail::set_curl_opt( + "set_range", easy_handle_, native::CURLOPT_RANGE, sv.data()); +} + +void easy::set_cookie_list(std::string_view sv) { + detail::set_curl_opt( + "set_cookie_list", easy_handle_, native::CURLOPT_COOKIELIST, sv.data()); +} + +void easy::set_cookie(std::string_view sv) { + detail::set_curl_opt( + "set_cookie", easy_handle_, native::CURLOPT_COOKIE, sv.data()); +} + +void easy::set_cookie_file(std::string_view sv) { + detail::set_curl_opt( + "set_cookie_file", easy_handle_, native::CURLOPT_COOKIEFILE, sv.data()); +} + +void easy::set_cookie_jar(std::string_view sv) { + detail::set_curl_opt( + "set_cookie_jar", easy_handle_, native::CURLOPT_COOKIEJAR, sv.data()); +} + +void easy::set_http_version(http_version_t version) { + detail::set_curl_opt( + "set_http_version", easy_handle_, native::CURLOPT_HTTP_VERSION, detail::to_integral(version)); +} + +void easy::set_time_condition(time_condition_t condition) { + detail::set_curl_opt( + "set_time_condition", easy_handle_, native::CURLOPT_TIMECONDITION, detail::to_integral(condition)); +} + +void easy::set_fresh_connect(bool state) { + detail::set_curl_opt( + "set_fresh_connect", easy_handle_, native::CURLOPT_FRESH_CONNECT, state); +} + +void easy::set_forbit_reuse(bool state) { + detail::set_curl_opt( + "set_forbit_reuse", easy_handle_, native::CURLOPT_FORBID_REUSE, state); +} + +void easy::set_connect_only(bool state) { + detail::set_curl_opt( + "set_connect_only", easy_handle_, native::CURLOPT_CONNECT_ONLY, state); +} + +void easy::set_timeout(long timeout) { + detail::set_curl_opt( + "set_timeout", easy_handle_, native::CURLOPT_TIMEOUT, timeout); +} + +void easy::set_timeout_ms(long timeout) { + detail::set_curl_opt( + "set_timeout_ms", easy_handle_, native::CURLOPT_TIMEOUT_MS, timeout); +} + +void easy::set_low_speed_limit(long limit) { + detail::set_curl_opt( + "set_low_speed", easy_handle_, native::CURLOPT_LOW_SPEED_LIMIT, limit); +} + +void easy::set_low_speed_time(long time) { + detail::set_curl_opt( + "set_low_speed_time", easy_handle_, native::CURLOPT_LOW_SPEED_TIME, time); +} + +void easy::set_max_connects(long connects) { + detail::set_curl_opt( + "set_max_connects", easy_handle_, native::CURLOPT_MAXCONNECTS, connects); +} + +void easy::set_connect_timeout(long timeout) { + detail::set_curl_opt( + "set_connect_timeout", easy_handle_, native::CURLOPT_CONNECTTIMEOUT, timeout); +} + +void easy::set_connect_timeout_ms(long timeout) { + detail::set_curl_opt( + "set_connect_timeout_ms", easy_handle_, native::CURLOPT_CONNECTTIMEOUT_MS, timeout); +} + +void easy::set_accept_timeout_ms(long timeout) { + detail::set_curl_opt( + "set_accept_timeout_ms", easy_handle_, native::CURLOPT_ACCEPTTIMEOUT_MS, timeout); +} + +void easy::set_max_send_speed_large(native::curl_off_t large) { + detail::set_curl_opt( + "set_max_send_speed_large", easy_handle_, native::CURLOPT_MAX_SEND_SPEED_LARGE, large); +} + +void easy::set_max_recv_speed_large(native::curl_off_t large) { + detail::set_curl_opt( + "set_max_recv_speed_large", easy_handle_, native::CURLOPT_MAX_RECV_SPEED_LARGE, large); +} + +void easy::set_dns_servers(std::string_view sv) { + detail::set_curl_opt( + "set_dns_servers", easy_handle_, native::CURLOPT_DNS_SERVERS, sv.data()); +} + +void easy::set_ip_resolve(ip_resolve_t resolve) { + detail::set_curl_opt( + "set_ip_resolve", easy_handle_, native::CURLOPT_IPRESOLVE, detail::to_integral(resolve)); +} + +void easy::set_resolves(std::shared_ptr resolved_hosts) { + resolved_hosts_ = std::move(resolved_hosts); + if (resolved_hosts_) { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_RESOLVE, resolved_hosts_->native_handle()); + throw_error(ec, "set_resolves native_handle failed!"); + } else { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_RESOLVE, nullptr); + throw_error(ec, "set_resolves nullptr failed!"); + } +} + +void easy::set_ssh_auth_types(long types) { + detail::set_curl_opt( + "set_ssh_auth_types", easy_handle_, native::CURLOPT_SSH_AUTH_TYPES, types); +} + +void easy::set_ssh_host_public_key_md5(std::string_view sv) { + detail::set_curl_opt( + "set_ssh_host_public_key_md5", easy_handle_, native::CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, sv.data()); +} + +void easy::set_ssh_public_key_file(std::string_view sv) { + detail::set_curl_opt( + "set_ssh_public_key_file", easy_handle_, native::CURLOPT_SSH_PUBLIC_KEYFILE, sv.data()); +} + +void easy::set_ssh_private_key_file(std::string_view sv) { + detail::set_curl_opt( + "set_ssh_private_key_file", easy_handle_, native::CURLOPT_SSH_PRIVATE_KEYFILE, sv.data()); +} + +void easy::set_ssh_known_hosts(std::string_view sv) { + detail::set_curl_opt( + "set_ssh_known_hosts", easy_handle_, native::CURLOPT_SSH_KNOWNHOSTS, sv.data()); +} + +void easy::set_ssh_key_function(void* ptr) { + detail::set_curl_opt( + "set_ssh_key_function", easy_handle_, native::CURLOPT_SSH_KEYFUNCTION, ptr); +} + +void easy::set_ssh_key_data(void* ptr) { + detail::set_curl_opt( + "set_ssh_key_data", easy_handle_, native::CURLOPT_SSH_KEYDATA, ptr); +} + +void easy::set_ssl_cert(std::string_view sv) { + detail::set_curl_opt( + "set_ssl_cert", easy_handle_, native::CURLOPT_SSLCERT, sv.data()); +} + +void easy::set_ssl_cert_type(std::string_view sv) { + detail::set_curl_opt( + "set_ssl_cert_type", easy_handle_, native::CURLOPT_SSLCERTTYPE, sv.data()); +} + +void easy::set_ssl_key(std::string_view sv) { + detail::set_curl_opt( + "set_ssl_key", easy_handle_, native::CURLOPT_SSLKEY, sv.data()); +} + +void easy::set_ssl_key_type(std::string_view sv) { + detail::set_curl_opt( + "set_ssl_key_type", easy_handle_, native::CURLOPT_SSLKEYTYPE, sv.data()); +} + +void easy::set_ssl_key_passwd(std::string_view sv) { + detail::set_curl_opt( + "set_ssl_key_passwd", easy_handle_, native::CURLOPT_KEYPASSWD, sv.data()); +} + +void easy::set_ssl_engine(std::string_view sv) { + detail::set_curl_opt( + "set_ssl_engine", easy_handle_, native::CURLOPT_SSLENGINE, sv.data()); +} + +void easy::set_ssl_engine_default(std::string_view sv) { + detail::set_curl_opt( + "set_ssl_engine_default", easy_handle_, native::CURLOPT_SSLENGINE_DEFAULT, sv.data()); +} + +void easy::set_ca_info(std::string_view sv) { + detail::set_curl_opt( + "set_ca_info", easy_handle_, native::CURLOPT_CAINFO, sv.data()); +} + +void easy::set_ca_file(std::string_view sv) { + detail::set_curl_opt( + "set_ca_file", easy_handle_, native::CURLOPT_CAPATH, sv.data()); +} + +void easy::set_crl_file(std::string_view sv) { + detail::set_curl_opt( + "set_crl_file", easy_handle_, native::CURLOPT_CRLFILE, sv.data()); +} + +void easy::set_issuer_cert(std::string_view sv) { + detail::set_curl_opt( + "set_issuer_cert", easy_handle_, native::CURLOPT_ISSUERCERT, sv.data()); +} + +void easy::set_ssl_cipher_list(std::string_view sv) { + detail::set_curl_opt( + "set_ssl_cipher_list", easy_handle_, native::CURLOPT_SSL_CIPHER_LIST, sv.data()); +} + +void easy::set_krb_level(std::string_view sv) { + detail::set_curl_opt( + "set_krb_level", easy_handle_, native::CURLOPT_KRBLEVEL, sv.data()); +} + +void easy::set_ssl_options(long options) { + detail::set_curl_opt( + "set_ssl_options", easy_handle_, native::CURLOPT_SSL_OPTIONS, options); +} + +void easy::set_gssapi_delegation(long arg) { + detail::set_curl_opt( + "set_gssapi_delegation", easy_handle_, native::CURLOPT_GSSAPI_DELEGATION, arg); +} + +void easy::set_cert_info(bool state) { + detail::set_curl_opt( + "set_cert_info", easy_handle_, native::CURLOPT_CERTINFO, state); +} + +void easy::set_ssl_session_id_cache(bool state) { + detail::set_curl_opt( + "set_ssl_session_id_cache", easy_handle_, native::CURLOPT_SSL_SESSIONID_CACHE, state); +} + +void easy::set_ssl_verify_peer(bool state) { + detail::set_curl_opt("set_ssl_verify_peer", easy_handle_, native::CURLOPT_SSL_VERIFYPEER, state); +} + +void easy::set_ssl_verify_host(bool state) { + std::error_code ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_SSL_VERIFYHOST, state ? 2L : 0L); + throw_error(ec, "set_ssl_verify_host"); +} + +void easy::set_ssl_version(ssl_version_t version) { + detail::set_curl_opt( + "set_ssl_version", easy_handle_, native::CURLOPT_SSLVERSION, detail::to_integral(version)); +} + +void easy::set_use_ssl(use_ssl_t ssl) { + detail::set_curl_opt( + "set_use_ssl", easy_handle_, native::CURLOPT_USE_SSL, detail::to_integral(ssl)); +} + +void easy::set_ssl_cert_blob_copy(std::string_view sv) { + detail::set_curl_opt_blob( + "set_ssl_cert_blob_copy", sv, CURL_BLOB_COPY, easy_handle_, native::CURLOPT_SSLCERT_BLOB); +} + +void easy::set_ssl_cert_blob_no_copy(std::string_view sv) { + detail::set_curl_opt_blob( + "set_ssl_cert_blob_no_copy", sv, CURL_BLOB_NOCOPY, easy_handle_, native::CURLOPT_SSLCERT_BLOB); +} + +void easy::set_ssl_key_blob_copy(std::string_view sv) { + detail::set_curl_opt_blob( + "set_ssl_key_blob_copy", sv, CURL_BLOB_COPY, easy_handle_, native::CURLOPT_SSLKEY_BLOB); +} + +void easy::set_ssl_key_blob_no_copy(std::string_view sv) { + detail::set_curl_opt_blob( + "set_ssl_key_blob_no_copy", sv, CURL_BLOB_NOCOPY, easy_handle_, native::CURLOPT_SSLKEY_BLOB); +} + +void easy::set_ca_info_blob_copy(std::string_view sv) { + detail::set_curl_opt_blob( + "set_ca_info_blob_copy", sv, CURL_BLOB_COPY, easy_handle_, native::CURLOPT_CAINFO_BLOB); +} + +void easy::set_ca_info_blob_no_copy(std::string_view sv) { + detail::set_curl_opt_blob( + "set_ca_info_blob_no_copy", sv, CURL_BLOB_NOCOPY, easy_handle_, native::CURLOPT_CAINFO_BLOB); +} + +// * puplic callback function @fn set_* @return none +void easy::set_progress_callback(progress_function_t func) { + progress_function_ = std::move(func); + set_no_progress(false); + set_xferinfo_function(&easy::xfer_function); + set_xferinfo_data(this); +} + +void easy::unset_progress_callback() { + set_no_progress(true); + set_xferinfo_function(nullptr); + set_xferinfo_data(nullptr); +} + +void easy::set_header_function(header_function_t func) { + detail::set_curl_opt( + "set_header_function", easy_handle_, native::CURLOPT_HEADERFUNCTION, func); } -void easy::add_resolve(const std::string& host, const std::string& port, const std::string& addr, std::error_code& ec) { - if (!resolved_hosts_) { - resolved_hosts_ = std::make_shared(); - } - auto host_port_addr = utils::StrCat(host, ":", port, ":", addr); - const std::string_view host_port_view{host_port_addr.data(), host_port_addr.size() - addr.size()}; +void easy::set_header_data(void* ptr) { + detail::set_curl_opt( + "set_header_data", easy_handle_, native::CURLOPT_HEADERDATA, ptr); +} - if (!resolved_hosts_->ReplaceFirstIf( - [host_port_view](const auto& entry) { - // host_port_addr, of which we hold a string_view, might be moved in - // ReplaceFirstIf, but it's guaranteed that this - // lambda is not called after that. - return std::string_view{entry}.substr(host_port_view.size()) == host_port_view; - }, - std::move(host_port_addr) - )) { - UASSERT_MSG(!host_port_addr.empty(), "ReplaceFirstIf moved the string out, when it shouldn't have done so."); - resolved_hosts_->add(std::move(host_port_addr)); - } +void easy::set_debug_function(debug_function_t func) { + detail::set_curl_opt( + "set_debug_function", easy_handle_, native::CURLOPT_DEBUGFUNCTION, func); +} - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_RESOLVE, resolved_hosts_->native_handle()) - )}; +void easy::set_debug_data(void* ptr) { + detail::set_curl_opt( + "set_debug_data", easy_handle_, native::CURLOPT_DEBUGDATA, ptr); } -void easy::set_resolves(std::shared_ptr resolved_hosts) { - std::error_code ec; - set_resolves(std::move(resolved_hosts), ec); - throw_error(ec, "set_resolves"); +void easy::set_interleave_function(interleave_function_t func) { + detail::set_curl_opt( + "set_interleave_function", easy_handle_, native::CURLOPT_INTERLEAVEFUNCTION, func); } -void easy::set_resolves(std::shared_ptr resolved_hosts, std::error_code& ec) { - resolved_hosts_ = std::move(resolved_hosts); +void easy::set_interleave_data(void* ptr) { + detail::set_curl_opt( + "set_interleave_data", easy_handle_, native::CURLOPT_INTERLEAVEDATA, ptr); +} - if (resolved_hosts_) { - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_RESOLVE, resolved_hosts_->native_handle()) - )}; - } else { - ec = std::error_code{ - static_cast(native::curl_easy_setopt(handle_, native::CURLOPT_RESOLVE, NULL))}; - } +void easy::set_write_function(write_function_t func, std::error_code& ec) { + ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_WRITEFUNCTION, func); } -void easy::set_share(std::shared_ptr share) { +void easy::set_write_data(void* ptr, std::error_code& ec) { + ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_WRITEDATA, ptr); +} + +void easy::set_read_function(read_function_t func, std::error_code& ec) { + ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_READFUNCTION, func); +} + +void easy::set_read_data(void* ptr, std::error_code& ec) { + ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_READDATA, ptr); +} + +void easy::set_seek_function(seek_function_t func, std::error_code& ec) { + ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_SEEKFUNCTION, func); +} + +void easy::set_seek_data(void* ptr, std::error_code& ec) { + ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_SEEKDATA, ptr); +} + +void easy::set_sockopt_function(sockopt_function_t func) { + detail::set_curl_opt( + "set_sockopt_function", easy_handle_, native::CURLOPT_SOCKOPTFUNCTION, func); +} + +void easy::set_sockopt_data(void* ptr) { + detail::set_curl_opt( + "set_sockopt_data", easy_handle_, native::CURLOPT_SOCKOPTDATA, ptr); +} + +void easy::set_xferinfo_function(xfer_function_t func) { + detail::set_curl_opt( + "set_xferinfo_function", easy_handle_, native::CURLOPT_XFERINFOFUNCTION, func); +} + +void easy::set_xferinfo_data(void* ptr) { + detail::set_curl_opt( + "set_xferinfo_data", easy_handle_, native::CURLOPT_XFERINFODATA, ptr); +} + +void easy::set_opensocket_function(opensocket_function_t func) { + detail::set_curl_opt( + "set_opensocket_function", easy_handle_, native::CURLOPT_OPENSOCKETFUNCTION, func); +} + +void easy::set_opensocket_data(void* ptr) { + detail::set_curl_opt( + "set_opensocket_data", easy_handle_, native::CURLOPT_OPENSOCKETDATA, ptr); +} + +void easy::set_closesocket_function(closesocket_function_t func) { + detail::set_curl_opt( + "set_closesocket_function", easy_handle_, native::CURLOPT_CLOSESOCKETFUNCTION, func); +} + +void easy::set_closesocket_data(void* ptr) { + detail::set_curl_opt( + "set_closesocket_data", easy_handle_, native::CURLOPT_CLOSESOCKETDATA, ptr); +} + +void easy::set_ssl_ctx_function(ssl_ctx_function_t func, std::error_code& ec) { + ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_SSL_CTX_FUNCTION, func); +} + +void easy::set_ssl_ctx_data(void* ptr, std::error_code& ec) { + ec = detail::set_curl_opt(easy_handle_, native::CURLOPT_SSL_CTX_DATA, ptr); +} + +// public getters +std::string_view easy::get_effective_url() { std::error_code ec; - set_share(std::move(share), ec); - throw_error(ec, "set_share"); + std::string_view sv = get_effective_url(ec); + throw_error(ec, "get_effective_url"); + return sv; +} +std::string_view easy::get_effective_url(std::error_code& ec) { + return detail::get_curl_info_string_view(easy_handle_, native::CURLINFO_EFFECTIVE_URL, ec); } -void easy::set_share(std::shared_ptr share, std::error_code& ec) { - share_ = std::move(share); +std::vector easy::get_ssl_engines() { + return detail::get_curl_info_list( + "get_ssl_engines", easy_handle_, native::CURLINFO_SSL_ENGINES); +} - if (share) { - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_SHARE, share_->native_handle()) - )}; - } else { - ec = std::error_code{ - static_cast(native::curl_easy_setopt(handle_, native::CURLOPT_SHARE, NULL))}; - } +std::vector easy::get_cookielist() { + return detail::get_curl_info_list( + "get_cookielist", easy_handle_, native::CURLINFO_COOKIELIST); } -bool easy::has_post_data() const { return !post_fields_.empty() || form_; } +std::string_view easy::get_scheme() { + return detail::get_curl_info_string_view( + "get_scheme", easy_handle_, native::CURLINFO_SCHEME); +} -const std::string& easy::get_post_data() const { return post_fields_; } +std::string_view easy::get_local_ip() { + return detail::get_curl_info_string_view( + "get_local_ip", easy_handle_, native::CURLINFO_LOCAL_IP); +} -std::string easy::extract_post_data() { - auto data = std::move(post_fields_); - set_post_fields({}); - return data; +std::string_view easy::get_rtsp_session_id() { + return detail::get_curl_info_string_view( + "get_rtsp_session_id", easy_handle_, native::CURLINFO_RTSP_SESSION_ID); } -void easy::handle_completion(const std::error_code& err) { - LOG_TRACE() << "easy::handle_completion easy=" << this; +std::string_view easy::get_primary_ip() { + return detail::get_curl_info_string_view( + "get_primary_ip", easy_handle_, native::CURLINFO_PRIMARY_IP); +} - multi_registered_ = false; +std::string_view easy::get_redirect_url() { + return detail::get_curl_info_string_view( + "get_redirect_url", easy_handle_, native::CURLINFO_REDIRECT_URL); +} - auto handler = std::function([](std::error_code) {}); - swap(handler, handler_); +std::string_view easy::get_ftp_entry_path() { + return detail::get_curl_info_string_view( + "get_ftp_entry_path", easy_handle_, native::CURLINFO_FTP_ENTRY_PATH); +} - /* It's OK to call handler in libev thread context as it is limited to - * Request::on_retry and Request::on_completed. All user code is executed in - * coro context. - */ - handler(err); +std::string_view easy::get_content_type() { + return detail::get_curl_info_string_view( + "get_content_type", easy_handle_, native::CURLINFO_CONTENT_TYPE); } -void easy::mark_retry() { ++retries_count_; } +long easy::get_http_version() { + return detail::get_curl_info_long( + "get_http_version", easy_handle_, native::CURLINFO_HTTP_VERSION); +} -clients::http::LocalStats easy::get_local_stats() { - clients::http::LocalStats stats; +long easy::get_proxy_ssl_verifyresult() { + return detail::get_curl_info_long( + "get_proxy_ssl_verifyresult", easy_handle_, native::CURLINFO_PROXY_SSL_VERIFYRESULT); +} - stats.open_socket_count = sockets_opened_; - stats.retries_count = retries_count_; - stats.time_to_connect = std::chrono::microseconds(get_connect_time_usec()); - stats.time_to_process = std::chrono::microseconds(get_total_time_usec()); +long easy::get_local_port() { + return detail::get_curl_info_long( + "get_local_port", easy_handle_, native::CURLINFO_LOCAL_PORT); +} - return stats; +long easy::get_primary_port() { + return detail::get_curl_info_long( + "get_primary_port", easy_handle_, native::CURLINFO_PRIMARY_PORT); } -std::error_code easy::rate_limit_error() const { return rate_limit_error_; } +long easy::get_rtsp_cseq_recv() { + return detail::get_curl_info_long( + "get_rtsp_cseq_recv", easy_handle_, native::CURLINFO_RTSP_CSEQ_RECV); +} -easy::time_point::duration easy::time_to_start() const { - if (start_performing_ts_ != time_point{}) { - return start_performing_ts_ - construct_ts_; - } - return {}; +long easy::get_rtsp_server_cseq() { + return detail::get_curl_info_long( + "get_rtsp_server_cseq", easy_handle_, native::CURLINFO_RTSP_SERVER_CSEQ); } -native::curl_socket_t easy::open_tcp_socket(native::curl_sockaddr* address) { - std::error_code ec; +long easy::get_rtsp_client_cseq() { + return detail::get_curl_info_long( + "get_rtsp_client_cseq", easy_handle_, native::CURLINFO_RTSP_CLIENT_CSEQ); +} - LOG_TRACE() << "open_tcp_socket family=" << address->family; +long easy::get_condition_unmet() { + return detail::get_curl_info_long( + "get_condition_unmet", easy_handle_, native::CURLINFO_CONDITION_UNMET); +} - int fd = socket(address->family, address->socktype, address->protocol); - if (fd == -1) { - const auto old_errno = errno; - LOG_ERROR() << "socket(2) failed with error: " << utils::strerror(old_errno); - return CURL_SOCKET_BAD; - } - if (multi_) multi_->BindEasySocket(*this, fd); - return fd; +long easy::get_num_connects() { + return detail::get_curl_info_long( + "get_num_connects", easy_handle_, native::CURLINFO_NUM_CONNECTS); } -size_t easy::write_function(char* ptr, size_t size, size_t nmemb, void* userdata) noexcept { - easy* self = static_cast(userdata); - size_t actual_size = size * nmemb; +long easy::get_os_errno() { + return detail::get_curl_info_long( + "get_os_errno", easy_handle_, native::CURLINFO_OS_ERRNO); +} - if (!actual_size) { - return 0; - } +long easy::get_proxyauth_avail() { + return detail::get_curl_info_long( + "get_proxyauth_avail", easy_handle_, native::CURLINFO_PROXYAUTH_AVAIL); +} - try { - self->sink_->append(ptr, actual_size); - } catch (const std::exception&) { - // out of memory - return 0; - } +long easy::get_httpauth_avail() { + return detail::get_curl_info_long( + "get_httpauth_avail", easy_handle_, native::CURLINFO_HTTPAUTH_AVAIL); +} - return actual_size; +long easy::get_http_connectcode() { + return detail::get_curl_info_long( + "get_http_connectcode", easy_handle_, native::CURLINFO_HTTP_CONNECTCODE); } -size_t easy::read_function(void* ptr, size_t size, size_t nmemb, void* userdata) noexcept { - // FIXME readsome doesn't work with TFTP (see cURL docs) +long easy::get_redirect_count() { + return detail::get_curl_info_long( + "get_redirect_count", easy_handle_, native::CURLINFO_REDIRECT_COUNT); +} - easy* self = static_cast(userdata); - size_t actual_size = size * nmemb; +long easy::get_redirect_time() { + return detail::get_curl_info_long( + "get_redirect_time", easy_handle_, native::CURLINFO_REDIRECT_TIME); +} - if (!self->source_) return CURL_READFUNC_ABORT; +long easy::get_header_size() { + return detail::get_curl_info_long( + "get_header_size", easy_handle_, native::CURLINFO_HEADER_SIZE); +} - if (self->source_->eof()) { - return 0; - } +long easy::get_request_size() { + return detail::get_curl_info_long( + "get_request_size", easy_handle_, native::CURLINFO_REQUEST_SIZE); +} - std::streamsize chars_stored = self->source_->readsome(static_cast(ptr), actual_size); +long easy::get_ssl_verifyresult() { + return detail::get_curl_info_long( + "get_ssl_verifyresult", easy_handle_, native::CURLINFO_SSL_VERIFYRESULT); +} - if (!*self->source_) { - return CURL_READFUNC_ABORT; - } else { - return static_cast(chars_stored); - } +long easy::get_response_code() { + return detail::get_curl_info_long( + "get_response_code", easy_handle_, native::CURLINFO_RESPONSE_CODE); } -int easy::seek_function(void* instream, native::curl_off_t offset, int origin) noexcept { - // TODO we could allow the user to define an offset which this library - // should consider as position zero for uploading chunks of the file - // alternatively do tellg() on the source stream when it is first passed to - // use_source() and use that as origin +long easy::get_content_length_upload() { + return detail::get_curl_info_long( + "get_content_length_upload", easy_handle_, native::CURLINFO_CONTENT_LENGTH_UPLOAD_T); +} - easy* self = static_cast(instream); +long easy::get_content_length_download() { + return detail::get_curl_info_long( + "get_content_length_download", easy_handle_, native::CURLINFO_CONTENT_LENGTH_DOWNLOAD_T); +} - std::ios::seekdir dir = std::ios::beg; +long easy::get_filetime_sec() { + return detail::get_curl_info_long( + "get_filetime_sec", easy_handle_, native::CURLINFO_FILETIME_T); +} - switch (origin) { - case SEEK_SET: - dir = std::ios::beg; - break; +long easy::get_speed_upload_bps() { + return detail::get_curl_info_long( + "get_speed_upload_bps", easy_handle_, native::CURLINFO_SPEED_UPLOAD_T); +} - case SEEK_CUR: - dir = std::ios::cur; - break; +long easy::get_speed_download_bps() { + return detail::get_curl_info_long( + "get_speed_dowload_bps", easy_handle_, native::CURLINFO_SPEED_DOWNLOAD_T); +} - case SEEK_END: - dir = std::ios::end; - break; +long easy::get_size_download() { + return detail::get_curl_info_long( + "get_size_download", easy_handle_, native::CURLINFO_SIZE_DOWNLOAD_T); +} - default: - return CURL_SEEKFUNC_FAIL; - } +long easy::get_size_upload() { + return detail::get_curl_info_long( + "get_size_upload", easy_handle_, native::CURLINFO_SIZE_UPLOAD_T); +} - if (!self->source_->seekg(offset, dir)) { - return CURL_SEEKFUNC_FAIL; - } else { - return CURL_SEEKFUNC_OK; - } +// stats getters +long easy::get_total_time_usec() { + return detail::get_curl_info_long( + "get_total_time_usec", easy_handle_, native::CURLINFO_TOTAL_TIME_T); } -int easy::xferinfo_function( - void* clientp, - native::curl_off_t dltotal, - native::curl_off_t dlnow, - native::curl_off_t ultotal, - native::curl_off_t ulnow -) noexcept { - easy* self = static_cast(clientp); - try { - return self->progress_callback_(dltotal, dlnow, ultotal, ulnow) ? 0 : 1; - } catch (const std::exception& ex) { - LOG_LIMITED_WARNING() << "Progress callback failed: " << ex; - return 1; - } +long easy::get_namelookup_time_usec() { + return detail::get_curl_info_long( + "get_namelookup_time_usec", easy_handle_, native::CURLINFO_NAMELOOKUP_TIME_T); +} + +long easy::get_connect_time_usec() { + return detail::get_curl_info_long( + "get_connect_time_usec", easy_handle_, native::CURLINFO_CONNECT_TIME_T); } -native::curl_socket_t -easy::opensocket(void* clientp, native::curlsocktype purpose, struct native::curl_sockaddr* address) noexcept { +long easy::get_pretransfer_time_usec() { + return detail::get_curl_info_long( + "get_pretransfer_time_usec", easy_handle_, native::CURLINFO_PRETRANSFER_TIME_T); +} + +long easy::get_starttransfer_time_usec() { + return detail::get_curl_info_long( + "get_starttransfer_time_usec", easy_handle_, native::CURLINFO_STARTTRANSFER_TIME_T); +} + +long easy::get_redirect_time_usec() { + return detail::get_curl_info_long( + "get_redirect_time_usec", easy_handle_, native::CURLINFO_REDIRECT_TIME_T); +} + +long easy::get_appconnect_time_usec() { + return detail::get_curl_info_long( + "get_appconnect_time_usec", easy_handle_, native::CURLINFO_APPCONNECT_TIME_T); +} + +long easy::get_retry_after_sec() { + return detail::get_curl_info_long("get_retry_after_sec", easy_handle_, native::CURLINFO_RETRY_AFTER); +} + +// private static +native::curl_socket_t easy::open_socket(void* clientp, native::curlsocktype purpose, struct native::curl_sockaddr* address) noexcept { easy* self = static_cast(clientp); - multi* multi_handle = self->multi_; - native::curl_socket_t s = -1; + multi* multi_handle = self->multi_handle_; + + native::curl_socket_t socket = -1; if (multi_handle) { std::error_code ec; @@ -772,31 +1637,30 @@ easy::opensocket(void* clientp, native::curlsocktype purpose, struct native::cur return CURL_SOCKET_BAD; } else { LOG_TRACE() << "not throttled"; - } + } } else { LOG_TRACE() << "skip throttle check"; } if (purpose == native::CURLSOCKTYPE_IPCXN && address->socktype == SOCK_STREAM) { // Note to self: Why is address->protocol always set to zero? - s = self->open_tcp_socket(address); - if (s != -1 && multi_handle) { + socket = self->open_tcp_socket(address); + if (socket != -1 && multi_handle) { multi_handle->Statistics().mark_open_socket(); self->mark_open_socket(); } - return s; + return socket; } - // unknown or invalid socket type return CURL_SOCKET_BAD; } -int easy::closesocket(void* clientp, native::curl_socket_t item) noexcept { +int easy::close_socket(void* clientp, native::curl_socket_t item) noexcept { auto* multi_handle = static_cast(clientp); multi_handle->UnbindEasySocket(item); - int ret = close(item); - if (ret == -1) { + int result = close(item); + if (result == -1) { const auto old_errno = errno; LOG_ERROR() << "close(2) failed with error: " << utils::strerror(old_errno); } @@ -805,6 +1669,125 @@ int easy::closesocket(void* clientp, native::curl_socket_t item) noexcept { return 0; } -} // namespace curl +int easy::seek_function(void* instream, native::curl_off_t offset, int origin) noexcept { + // TODO we could allow the user to define an offset which this library + // should consider as position zero for uploading chunks of the file + // alternatively do tellg() on the source stream when it is first passed to + // use_source() and use that as origin + easy* self = static_cast(instream); + + std::ios::seekdir dir = std::ios::beg; + + switch (origin) { + case SEEK_SET: + dir = std::ios::beg; + break; + case SEEK_CUR: + dir = std::ios::cur; + break; + case SEEK_END: + dir = std::ios::end; + break; + default: + return CURL_SEEKFUNC_FAIL; + } + + if (!self->source_->seekg(offset, dir)) + return CURL_SEEKFUNC_FAIL; + + return CURL_SEEKFUNC_OK; +} + +size_t easy::header_function(void*, size_t size, size_t nmemb, void*) { + return size * nmemb; +} + +size_t easy::read_runction(void* ptr, size_t size, size_t nmemb, void* userdata) noexcept { + // FIXME readsome doesn't work with TFTP (see cURL docs) + easy* self = static_cast(userdata); + std::streamsize actual_size = static_cast(size * nmemb); + + if (!self->source_) + return CURL_READFUNC_ABORT; + + if (!self->source_->eof()) + return 0; + + std::streamsize result = self->source_->readsome(static_cast(ptr), actual_size); + + if (!*self->source_) + return CURL_READFUNC_ABORT; + + return static_cast(result); +} + +size_t easy::write_function(char* ptr, size_t size, size_t nmemb, void* userdata) noexcept { + easy* self = static_cast(userdata); + size_t actual_size = size * nmemb; + + if(!actual_size) + return 0; + + try { + self->sink_->append(ptr, actual_size); + } catch (const std::exception& ex) { + LOG_LIMITED_WARNING() << "write function failed: " << ex; + return 0; + } + + return actual_size; +} + +int easy::xfer_function(void* clinetp, native::curl_off_t dltotal, native::curl_off_t dlnow, + native::curl_off_t ultotal, native::curl_off_t ulnow) noexcept { + easy* self = static_cast(clinetp); + try { + return self->progress_function_(dltotal, dlnow, ultotal, ulnow) ? 0 : 1; + } catch (const std::exception& ex) { + LOG_LIMITED_WARNING() << "progress callback failed: " << ex; + return 1; + } +} + +native::curl_socket_t easy::open_tcp_socket(struct native::curl_sockaddr* address) noexcept { + std::error_code ec; + + LOG_TRACE() << "easy::open_tcp_socket family = " << address->family; + int fd = socket(address->family, address->socktype, address->protocol); + if (fd == -1) { + const auto old_errno = errno; + LOG_ERROR() << "socket(2) failed with error: " << utils::strerror(old_errno); + return CURL_SOCKET_BAD; + } + + if (multi_handle_) + multi_handle_->BindEasySocket(*this, fd); + + return fd; +} + +void easy::set_http_post(std::unique_ptr form_ptr) { + form_ = std::move(form_ptr); + if (form_) { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_HTTPPOST, form_->native_handle()); + throw_error(ec, "set_http_post native_handle failed!"); + } else { + std::error_code ec = detail::set_curl_opt( + easy_handle_, native::CURLOPT_HTTPPOST, nullptr); + throw_error(ec, "set_http_post nullptr failed!"); + } +} + +void easy::set_egd_socket(std::string_view sv) { + detail::set_curl_opt("set_egd_socket", easy_handle_, native::CURLOPT_EGDSOCKET, sv.data()); +} + +long easy::get_protocol() { + return detail::get_curl_info_long( + "get_protocol", easy_handle_, native::CURLINFO_PROTOCOL); +} + +} // namespace curl -USERVER_NAMESPACE_END +USERVER_NAMESPACE_END \ No newline at end of file diff --git a/core/src/curl-ev/easy.hpp b/core/src/curl-ev/easy.hpp index f7a02a78a5b8..a792752da9cd 100644 --- a/core/src/curl-ev/easy.hpp +++ b/core/src/curl-ev/easy.hpp @@ -1,3 +1,5 @@ +#pragma once + /** @file curl-ev/easy.hpp curl-ev: wrapper for integrating libcurl with libev applications Copyright (c) 2013 Oliver Kuckertz @@ -6,8 +8,6 @@ C++ wrapper for libcurl's easy interface */ -#pragma once - #include #include #include @@ -16,171 +16,25 @@ #include #include #include +#include #include #include +#include #include #include +#include +#include + #include USERVER_NAMESPACE_BEGIN namespace engine::ev { - -class ThreadControl; - + class ThreadControl; } // namespace engine::ev -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define IMPLEMENT_CURL_OPTION(FUNCTION_NAME, OPTION_NAME, OPTION_TYPE) \ - inline void FUNCTION_NAME(OPTION_TYPE arg) { \ - std::error_code ec; \ - FUNCTION_NAME(arg, ec); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME)); \ - } \ - inline void FUNCTION_NAME(OPTION_TYPE arg, std::error_code& ec) { \ - ec = std::error_code(static_cast(native::curl_easy_setopt(handle_, OPTION_NAME, arg))); \ - } - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define IMPLEMENT_CURL_OPTION_BOOLEAN(FUNCTION_NAME, OPTION_NAME) \ - inline void FUNCTION_NAME(bool enabled) { \ - std::error_code ec; \ - FUNCTION_NAME(enabled, ec); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME)); \ - } \ - inline void FUNCTION_NAME(bool enabled, std::error_code& ec) { \ - ec = std::error_code( \ - static_cast(native::curl_easy_setopt(handle_, OPTION_NAME, enabled ? 1L : 0L)) \ - ); \ - } - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define IMPLEMENT_CURL_OPTION_ENUM(FUNCTION_NAME, OPTION_NAME, ENUM_TYPE, OPTION_TYPE) \ - inline void FUNCTION_NAME(ENUM_TYPE arg) { \ - std::error_code ec; \ - FUNCTION_NAME(arg, ec); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME)); \ - } \ - inline void FUNCTION_NAME(ENUM_TYPE arg, std::error_code& ec) { \ - ec = std::error_code(static_cast( \ - native::curl_easy_setopt(handle_, OPTION_NAME, static_cast(arg)) \ - )); \ - } - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define IMPLEMENT_CURL_OPTION_STRING(FUNCTION_NAME, OPTION_NAME) \ - inline void FUNCTION_NAME(const char* str) { \ - std::error_code ec; \ - FUNCTION_NAME(str, ec); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME)); \ - } \ - inline void FUNCTION_NAME(const char* str, std::error_code& ec) { \ - ec = std::error_code(static_cast(native::curl_easy_setopt(handle_, OPTION_NAME, str))); \ - } \ - inline void FUNCTION_NAME(const std::string& str) { \ - std::error_code ec; \ - FUNCTION_NAME(str, ec); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME)); \ - } \ - inline void FUNCTION_NAME(const std::string& str, std::error_code& ec) { \ - ec = std::error_code( \ - static_cast(native::curl_easy_setopt(handle_, OPTION_NAME, str.c_str())) \ - ); \ - } - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define IMPLEMENT_CURL_OPTION_BLOB(FUNCTION_NAME, OPTION_NAME) \ -private: \ - inline void FUNCTION_NAME##_impl(std::string_view sv, unsigned int flags, std::error_code& ec) { \ - native::curl_blob blob{}; \ - /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) */ \ - blob.data = const_cast(static_cast(sv.data())); \ - blob.len = sv.size(); \ - blob.flags = flags; \ - ec = std::error_code(static_cast(native::curl_easy_setopt(handle_, OPTION_NAME, &blob))); \ - } \ - \ -public: \ - static constexpr bool is_##FUNCTION_NAME##_available = true; \ - inline void FUNCTION_NAME##_copy(std::string_view sv) { \ - std::error_code ec; \ - FUNCTION_NAME##_copy(sv, ec); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME##_copy)); \ - } \ - inline void FUNCTION_NAME##_no_copy(std::string_view sv) { \ - std::error_code ec; \ - FUNCTION_NAME##_no_copy(sv, ec); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME##_no_copy)); \ - } \ - inline void FUNCTION_NAME##_copy(std::string_view sv, std::error_code& ec) { \ - FUNCTION_NAME##_impl(sv, CURL_BLOB_COPY, ec); \ - } \ - inline void FUNCTION_NAME##_no_copy(std::string_view sv, std::error_code& ec) { \ - FUNCTION_NAME##_impl(sv, CURL_BLOB_NOCOPY, ec); \ - } - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define DELETE_CURL_OPTION_BLOB(FUNCTION_NAME) \ - static constexpr bool is_##FUNCTION_NAME##_available = false; \ - inline void FUNCTION_NAME##_copy(std::string_view) = delete; \ - inline void FUNCTION_NAME##_no_copy(std::string_view) = delete; \ - inline void FUNCTION_NAME##_copy(std::string_view, std::error_code&) = delete; \ - inline void FUNCTION_NAME##_no_copy(std::string_view, std::error_code&) = delete - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define IMPLEMENT_CURL_OPTION_GET_STRING_VIEW(FUNCTION_NAME, OPTION_NAME) \ - inline std::string_view FUNCTION_NAME() { \ - std::error_code ec; \ - auto info = FUNCTION_NAME(ec); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME)); \ - return info; \ - } \ - inline std::string_view FUNCTION_NAME(std::error_code& ec) { \ - char* info = nullptr; \ - ec = \ - std::error_code(static_cast(native::curl_easy_getinfo(handle_, OPTION_NAME, &info))); \ - return info ? info : std::string_view{}; \ - } - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define IMPLEMENT_CURL_OPTION_GET_LONG(FUNCTION_NAME, OPTION_NAME) \ - inline long FUNCTION_NAME() { \ - long info; \ - std::error_code ec = \ - std::error_code(static_cast(native::curl_easy_getinfo(handle_, OPTION_NAME, &info))); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME)); \ - return info; \ - } - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(FUNCTION_NAME, OPTION_NAME) \ - inline long FUNCTION_NAME() { \ - native::curl_off_t info; \ - std::error_code ec = \ - std::error_code(static_cast(native::curl_easy_getinfo(handle_, OPTION_NAME, &info))); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME)); \ - return info; \ - } - -// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define IMPLEMENT_CURL_OPTION_GET_LIST(FUNCTION_NAME, OPTION_NAME) \ - inline std::vector FUNCTION_NAME() { \ - struct native::curl_slist* info; \ - std::vector results; \ - std::error_code ec = \ - std::error_code(static_cast(native::curl_easy_getinfo(handle_, OPTION_NAME, &info))); \ - throw_error(ec, PP_STRINGIZE(FUNCTION_NAME)); \ - struct native::curl_slist* it = info; \ - while (it) { \ - results.emplace_back(it->data); \ - it = it->next; \ - } \ - native::curl_slist_free_all(info); \ - return results; \ - } - #ifdef CURL_SSLVERSION_DEFAULT // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define CURL_SSLVERSION_NAMESPACE @@ -190,512 +44,444 @@ public: #endif namespace curl { -// class form; class multi; class share; class string_list; class easy final : public std::enable_shared_from_this { public: - using handler_type = std::function; - using time_point = std::chrono::steady_clock::time_point; + explicit easy(native::CURL* easy_handle, native::curl_mime* mime_ptr, multi* multi_ptr); - static easy* from_native(native::CURL* native_easy); + easy(const easy& rhs) = delete; + easy(easy&& rhs) = delete; - // Creates an initialized but unbound easy, use GetBound() for usable - // instances. May block on resolver initialization. - static std::shared_ptr CreateBlocking(); + easy& operator= (const easy& rhs) = delete; + easy& operator= (easy&& rhs) = delete; - easy(native::CURL*, multi*); - easy(const easy&) = delete; ~easy(); - // Makes a clone of an initialized easy, hopefully non-blocking (skips full - // resolver initialization). - std::shared_ptr GetBoundBlocking(multi&) const; - - const multi* GetMulti() const { return multi_; } +public: + enum class EmptyHeaderAction { kSend, kDoNotSend }; + enum class DuplicateHeaderAction { kAdd, kSkip, kReplace }; + enum class http_version_t : unsigned long { + http_version_none = native::CURL_HTTP_VERSION_NONE, + http_version_1_0 = native::CURL_HTTP_VERSION_1_0, + http_version_1_1 = native::CURL_HTTP_VERSION_1_1, + http_version_2_0 = native::CURL_HTTP_VERSION_2_0, + http_version_2tls = native::CURL_HTTP_VERSION_2TLS, + http_version_2_prior_knowledge = native::CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE + // !http_version_3_0 = native::CURL_HTTP_VERSION_3, // important, uncomments next feature + // !http_version_3_0_only = native::CURL_HTTP_VERSION_3ONLY // version userver supports HTTP/3 + }; + enum class ssl_version_t : unsigned long { + ssl_version_default = native::CURL_SSLVERSION_DEFAULT, + ssl_version_tls_v1 = native::CURL_SSLVERSION_TLSv1, + ssl_version_ssl_v2 = native::CURL_SSLVERSION_SSLv2, + ssl_version_ssl_v3 = native::CURL_SSLVERSION_SSLv3 + }; + enum class use_ssl_t : unsigned long { + use_ssl_none = native::CURLUSESSL_NONE, + use_ssl_try = native::CURLUSESSL_TRY, + use_ssl_control = native::CURLUSESSL_CONTROL, + use_ssl_all = native::CURLUSESSL_ALL + }; + enum class ip_resolve_t : unsigned long { + ip_resolve_whatever = CURL_IPRESOLVE_WHATEVER, + ip_resolve_v4 = CURL_IPRESOLVE_V4, + ip_resolve_v6 = CURL_IPRESOLVE_V6 + }; + enum class time_condition_t : unsigned long { + if_modified_since = native::CURL_TIMECOND_IFMODSINCE, + if_unmodified_since = native::CURL_TIMECOND_IFUNMODSINCE + }; + enum class netrc_t : unsigned long { + netrc_optional = native::CURL_NETRC_OPTIONAL, + netrc_ignored = native::CURL_NETRC_IGNORED, + netrc_required = native::CURL_NETRC_REQUIRED + }; + enum class httpauth_t : unsigned long { + auth_basic = CURLAUTH_BASIC, + auth_digest = CURLAUTH_DIGEST, + auth_digest_ie = CURLAUTH_DIGEST_IE, + auth_negotiate = CURLAUTH_NEGOTIATE, + auth_ntlm = CURLAUTH_NTLM, + auth_ntlm_wb = CURLAUTH_NTLM_WB, + auth_any = CURLAUTH_ANY, + auth_any_safe = CURLAUTH_ANYSAFE + }; + enum class proxyauth_t : unsigned long { + proxy_auth_basic = CURLAUTH_BASIC, + proxy_auth_digest = CURLAUTH_DIGEST, + proxy_auth_digest_ie = CURLAUTH_DIGEST_IE, + proxy_auth_bearer = CURLAUTH_BEARER, + proxy_auth_negotiate = CURLAUTH_NEGOTIATE, + proxy_auth_ntlm = CURLAUTH_NTLM, + proxy_auth_ntlm_wb = CURLAUTH_NTLM_WB, + proxy_auth_any = CURLAUTH_ANY, + proxy_auth_anysafe = CURLAUTH_ANYSAFE + }; - inline native::CURL* native_handle() { return handle_; } - engine::ev::ThreadControl& GetThreadControl(); + using time_point = std::chrono::steady_clock::time_point; + using handler_type = std::function; + + // * this function unused, use async_perform() + [[maybe_unused]] void perform(){} - void perform(); - void perform(std::error_code& ec); void async_perform(handler_type handler); void cancel(); void reset(); - void set_source(std::shared_ptr source); - void set_source(std::shared_ptr source, std::error_code& ec); - void set_sink(std::string* sink); - void set_sink(std::string* sink, std::error_code& ec); - - using progress_callback_t = std::function; - void unset_progress_callback(); - void set_progress_callback(progress_callback_t progress_callback); + void mark_retry(); - // behavior options + void handle_completion(const std::error_code& ec); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_verbose, native::CURLOPT_VERBOSE); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_header, native::CURLOPT_HEADER); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_no_progress, native::CURLOPT_NOPROGRESS); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_no_signal, native::CURLOPT_NOSIGNAL); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_wildcard_match, native::CURLOPT_WILDCARDMATCH); + void set_share(std::shared_ptr share); - // callback options + static easy* from_native(native::CURL* native_easy); + [[nodiscard]] static std::shared_ptr CreateBlocking(); - using write_function_t = size_t (*)(char* ptr, size_t size, size_t nmemb, void* userdata); - IMPLEMENT_CURL_OPTION(set_write_function, native::CURLOPT_WRITEFUNCTION, write_function_t); - IMPLEMENT_CURL_OPTION(set_write_data, native::CURLOPT_WRITEDATA, void*); - using read_function_t = size_t (*)(void* ptr, size_t size, size_t nmemb, void* userdata); - IMPLEMENT_CURL_OPTION(set_read_function, native::CURLOPT_READFUNCTION, read_function_t); - IMPLEMENT_CURL_OPTION(set_read_data, native::CURLOPT_READDATA, void*); - using seek_function_t = int (*)(void* instream, native::curl_off_t offset, int origin); - IMPLEMENT_CURL_OPTION(set_seek_function, native::CURLOPT_SEEKFUNCTION, seek_function_t); - IMPLEMENT_CURL_OPTION(set_seek_data, native::CURLOPT_SEEKDATA, void*); - using sockopt_function_t = int (*)(void* clientp, native::curl_socket_t curlfd, native::curlsocktype purpose); - IMPLEMENT_CURL_OPTION(set_sockopt_function, native::CURLOPT_SOCKOPTFUNCTION, sockopt_function_t); - IMPLEMENT_CURL_OPTION(set_sockopt_data, native::CURLOPT_SOCKOPTDATA, void*); - using opensocket_function_t = - native::curl_socket_t (*)(void* clientp, native::curlsocktype purpose, struct native::curl_sockaddr* address); - IMPLEMENT_CURL_OPTION(set_opensocket_function, native::CURLOPT_OPENSOCKETFUNCTION, opensocket_function_t); - IMPLEMENT_CURL_OPTION(set_opensocket_data, native::CURLOPT_OPENSOCKETDATA, void*); - using closesocket_function_t = int (*)(void* clientp, native::curl_socket_t item); - IMPLEMENT_CURL_OPTION(set_closesocket_function, native::CURLOPT_CLOSESOCKETFUNCTION, closesocket_function_t); - IMPLEMENT_CURL_OPTION(set_closesocket_data, native::CURLOPT_CLOSESOCKETDATA, void*); - IMPLEMENT_CURL_OPTION(set_progress_data, native::CURLOPT_PROGRESSDATA, void*); - using xferinfo_function_t = int (*)( - void* clientp, - native::curl_off_t dltotal, - native::curl_off_t dlnow, - native::curl_off_t ultotal, - native::curl_off_t ulnow - ); - IMPLEMENT_CURL_OPTION(set_xferinfo_function, native::CURLOPT_XFERINFOFUNCTION, xferinfo_function_t); - IMPLEMENT_CURL_OPTION(set_xferinfo_data, native::CURLOPT_XFERINFODATA, void*); - using header_function_t = size_t (*)(void* ptr, size_t size, size_t nmemb, void* userdata); - IMPLEMENT_CURL_OPTION(set_header_function, native::CURLOPT_HEADERFUNCTION, header_function_t); - IMPLEMENT_CURL_OPTION(set_header_data, native::CURLOPT_HEADERDATA, void*); - using debug_callback_t = int (*)(native::CURL*, native::curl_infotype, char*, size_t, void*); - IMPLEMENT_CURL_OPTION(set_debug_callback, native::CURLOPT_DEBUGFUNCTION, debug_callback_t); - IMPLEMENT_CURL_OPTION(set_debug_data, native::CURLOPT_DEBUGDATA, void*); - using ssl_ctx_function_t = native::CURLcode (*)(native::CURL* curl, void* sslctx, void* parm); - IMPLEMENT_CURL_OPTION(set_ssl_ctx_function, native::CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_function_t); - IMPLEMENT_CURL_OPTION(set_ssl_ctx_data, native::CURLOPT_SSL_CTX_DATA, void*); - using interleave_function_t = size_t (*)(void* ptr, size_t size, size_t nmemb, void* userdata); - IMPLEMENT_CURL_OPTION(set_interleave_function, native::CURLOPT_INTERLEAVEFUNCTION, interleave_function_t); - IMPLEMENT_CURL_OPTION(set_interleave_data, native::CURLOPT_INTERLEAVEDATA, void*); + [[nodiscard]] std::shared_ptr GetBoundBlocking(multi& multi_handle) const; + engine::ev::ThreadControl& GetThreadControl(); - // error options + std::error_code rate_limit_error() const; + time_point::duration time_to_start() const; + clients::http::LocalStats get_local_stats(); + const std::string& get_post_data() const; - IMPLEMENT_CURL_OPTION(set_error_buffer, native::CURLOPT_ERRORBUFFER, char*); - IMPLEMENT_CURL_OPTION(set_stderr, native::CURLOPT_STDERR, FILE*); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_fail_on_error, native::CURLOPT_FAILONERROR); + std::string extract_post_data(); - // network options + bool has_post_data() const; - void set_url(std::string url_str); + inline native::CURL* native_handle() { return easy_handle_; } + inline const multi* GetMulti() const { return multi_handle_; } - /// @overload `set_url` - /// @note Doesn't perform a move from `url_str`, if error occurs. - void set_url(std::string&& url_str, std::error_code& ec); + inline bool operator<(const easy& other) const { return (this < &other); } - const std::string& get_original_url() const; - const url& get_easy_url() const; - - IMPLEMENT_CURL_OPTION(set_protocols, native::CURLOPT_PROTOCOLS, long); - IMPLEMENT_CURL_OPTION(set_redir_protocols, native::CURLOPT_REDIR_PROTOCOLS, long); - IMPLEMENT_CURL_OPTION_STRING(set_proxy, native::CURLOPT_PROXY); - IMPLEMENT_CURL_OPTION(set_proxy_port, native::CURLOPT_PROXYPORT, long); - IMPLEMENT_CURL_OPTION(set_proxy_type, native::CURLOPT_PROXYTYPE, long); - IMPLEMENT_CURL_OPTION_STRING(set_no_proxy, native::CURLOPT_NOPROXY); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_http_proxy_tunnel, native::CURLOPT_HTTPPROXYTUNNEL); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_socks5_gsapi_nec, native::CURLOPT_SOCKS5_GSSAPI_NEC); - IMPLEMENT_CURL_OPTION_STRING(set_interface, native::CURLOPT_INTERFACE); - IMPLEMENT_CURL_OPTION(set_local_port, native::CURLOPT_LOCALPORT, long); - IMPLEMENT_CURL_OPTION(set_local_port_range, native::CURLOPT_LOCALPORTRANGE, long); - IMPLEMENT_CURL_OPTION(set_dns_cache_timeout, native::CURLOPT_DNS_CACHE_TIMEOUT, long); - IMPLEMENT_CURL_OPTION(set_buffer_size, native::CURLOPT_BUFFERSIZE, long); - IMPLEMENT_CURL_OPTION(set_port, native::CURLOPT_PORT, long); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_tcp_no_delay, native::CURLOPT_TCP_NODELAY); - IMPLEMENT_CURL_OPTION(set_address_scope, native::CURLOPT_ADDRESS_SCOPE, long); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_tcp_keep_alive, native::CURLOPT_TCP_KEEPALIVE); - IMPLEMENT_CURL_OPTION(set_tcp_keep_idle, native::CURLOPT_TCP_KEEPIDLE, long); - IMPLEMENT_CURL_OPTION(set_tcp_keep_intvl, native::CURLOPT_TCP_KEEPINTVL, long); - IMPLEMENT_CURL_OPTION_STRING(set_unix_socket_path, native::CURLOPT_UNIX_SOCKET_PATH); - IMPLEMENT_CURL_OPTION(set_connect_to, native::CURLOPT_CONNECT_TO, native::curl_slist*); - // authentication options - - enum netrc_t { - netrc_optional = native::CURL_NETRC_OPTIONAL, - netrc_ignored = native::CURL_NETRC_IGNORED, - netrc_required = native::CURL_NETRC_REQUIRED - }; - IMPLEMENT_CURL_OPTION_ENUM(set_netrc, native::CURLOPT_NETRC, netrc_t, long); - IMPLEMENT_CURL_OPTION_STRING(set_netrc_file, native::CURLOPT_NETRC_FILE); - IMPLEMENT_CURL_OPTION_STRING(set_user, native::CURLOPT_USERNAME); - IMPLEMENT_CURL_OPTION_STRING(set_password, native::CURLOPT_PASSWORD); - IMPLEMENT_CURL_OPTION_STRING(set_proxy_user, native::CURLOPT_PROXYUSERNAME); - IMPLEMENT_CURL_OPTION_STRING(set_proxy_password, native::CURLOPT_PROXYPASSWORD); - enum httpauth_t { - auth_basic = CURLAUTH_BASIC, - auth_digest = CURLAUTH_DIGEST, - auth_digest_ie = CURLAUTH_DIGEST_IE, - auth_negotiate = CURLAUTH_NEGOTIATE, - auth_ntlm = CURLAUTH_NTLM, - auth_ntlm_wb = CURLAUTH_NTLM_WB, - auth_any = CURLAUTH_ANY, - auth_any_safe = CURLAUTH_ANYSAFE - }; - inline void set_http_auth(httpauth_t auth, bool auth_only) { - std::error_code ec; - set_http_auth(auth, auth_only, ec); - throw_error(ec, "set_http_auth failed"); - } - inline void set_http_auth(httpauth_t auth, bool auth_only, std::error_code& ec) { - auto l = static_cast(auth | (auth_only ? CURLAUTH_ONLY : 0UL)); - ec = std::error_code( - static_cast(native::curl_easy_setopt(handle_, native::CURLOPT_HTTPAUTH, l)) - ); - } - IMPLEMENT_CURL_OPTION(set_tls_auth_type, native::CURLOPT_TLSAUTH_TYPE, long); - IMPLEMENT_CURL_OPTION_STRING(set_tls_auth_user, native::CURLOPT_TLSAUTH_USERNAME); - IMPLEMENT_CURL_OPTION_STRING(set_tls_auth_password, native::CURLOPT_TLSAUTH_PASSWORD); - enum proxyauth_t { - proxy_auth_basic = CURLAUTH_BASIC, - proxy_auth_digest = CURLAUTH_DIGEST, - proxy_auth_digest_ie = CURLAUTH_DIGEST_IE, - proxy_auth_bearer = CURLAUTH_BEARER, - proxy_auth_negotiate = CURLAUTH_NEGOTIATE, - proxy_auth_ntlm = CURLAUTH_NTLM, - proxy_auth_ntlm_wb = CURLAUTH_NTLM_WB, - proxy_auth_any = CURLAUTH_ANY, - proxy_auth_anysafe = CURLAUTH_ANYSAFE - }; - IMPLEMENT_CURL_OPTION(set_proxy_auth, native::CURLOPT_PROXYAUTH, long); - - // HTTP options - - IMPLEMENT_CURL_OPTION_BOOLEAN(set_auto_referrer, native::CURLOPT_AUTOREFERER); - IMPLEMENT_CURL_OPTION_STRING(set_accept_encoding, native::CURLOPT_ACCEPT_ENCODING); - IMPLEMENT_CURL_OPTION_STRING(set_transfer_encoding, native::CURLOPT_TRANSFER_ENCODING); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_follow_location, native::CURLOPT_FOLLOWLOCATION); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_unrestricted_auth, native::CURLOPT_UNRESTRICTED_AUTH); - IMPLEMENT_CURL_OPTION(set_max_redirs, native::CURLOPT_MAXREDIRS, long); - IMPLEMENT_CURL_OPTION(set_post_redir, native::CURLOPT_POSTREDIR, long); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_post, native::CURLOPT_POST); - void set_post_fields(std::string&& post_fields); - void set_post_fields(std::string&& post_fields, std::error_code& ec); - IMPLEMENT_CURL_OPTION(set_post_fields, native::CURLOPT_POSTFIELDS, void*); - IMPLEMENT_CURL_OPTION(set_post_field_size, native::CURLOPT_POSTFIELDSIZE, long); - IMPLEMENT_CURL_OPTION(set_post_field_size_large, native::CURLOPT_POSTFIELDSIZE_LARGE, native::curl_off_t); - - void set_http_post(std::unique_ptr form); - void set_http_post(std::unique_ptr form, std::error_code& ec); - - IMPLEMENT_CURL_OPTION_STRING(set_referer, native::CURLOPT_REFERER); - IMPLEMENT_CURL_OPTION_STRING(set_user_agent, native::CURLOPT_USERAGENT); - enum class EmptyHeaderAction { kSend, kDoNotSend }; - enum class DuplicateHeaderAction { kAdd, kSkip, kReplace }; - void add_header( - std::string_view name, - std::string_view value, - EmptyHeaderAction empty_header_action = EmptyHeaderAction::kSend, - DuplicateHeaderAction duplicate_header_action = DuplicateHeaderAction::kAdd - ); - void add_header( - std::string_view name, - std::string_view value, - std::error_code& ec, - EmptyHeaderAction empty_header_action = EmptyHeaderAction::kSend, - DuplicateHeaderAction duplicate_header_action = DuplicateHeaderAction::kAdd - ); - void add_header(std::string_view name, std::string_view value, DuplicateHeaderAction duplicate_header_action); - void add_header( - std::string_view name, - std::string_view value, - std::error_code& ec, - DuplicateHeaderAction duplicate_header_action - ); +public: + void add_header(std::string_view name, std::string_view value, + EmptyHeaderAction eha = EmptyHeaderAction::kSend, DuplicateHeaderAction dha = DuplicateHeaderAction::kAdd); + void add_header(std::string_view name, std::string_view value, DuplicateHeaderAction dha); void add_header(const char* header); - void add_header(const char* header, std::error_code& ec); void add_header(const std::string& header); - void add_header(const std::string& header, std::error_code& ec); - void set_headers(std::shared_ptr headers); - void set_headers(std::shared_ptr headers, std::error_code& ec); - std::optional FindHeaderByName(std::string_view name) const; - void add_proxy_header( - std::string_view name, - std::string_view value, - EmptyHeaderAction empty_header_action = EmptyHeaderAction::kSend, - DuplicateHeaderAction duplicate_header_action = DuplicateHeaderAction::kAdd - ); - void add_proxy_header( - std::string_view name, - std::string_view value, - std::error_code& ec, - EmptyHeaderAction empty_header_action = EmptyHeaderAction::kSend, - DuplicateHeaderAction duplicate_header_action = DuplicateHeaderAction::kAdd - ); - void add_proxy_header(const char* header, std::error_code& ec); - void add_http200_alias(const std::string& http200_alias); - void add_http200_alias(const std::string& http200_alias, std::error_code& ec); - void set_http200_aliases(std::shared_ptr http200_aliases); - void set_http200_aliases(std::shared_ptr http200_aliases, std::error_code& ec); - IMPLEMENT_CURL_OPTION_STRING(set_cookie, native::CURLOPT_COOKIE); - IMPLEMENT_CURL_OPTION_STRING(set_cookie_file, native::CURLOPT_COOKIEFILE); - IMPLEMENT_CURL_OPTION_STRING(set_cookie_jar, native::CURLOPT_COOKIEJAR); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_cookie_session, native::CURLOPT_COOKIESESSION); - IMPLEMENT_CURL_OPTION_STRING(set_cookie_list, native::CURLOPT_COOKIELIST); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_http_get, native::CURLOPT_HTTPGET); - enum http_version_t { - http_version_none = native::CURL_HTTP_VERSION_NONE, - http_version_1_0 = native::CURL_HTTP_VERSION_1_0, - http_version_1_1 = native::CURL_HTTP_VERSION_1_1, - http_version_2_0 = native::CURL_HTTP_VERSION_2_0, - http_version_2tls = native::CURL_HTTP_VERSION_2TLS, - http_version_2_prior_knowledge = native::CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, - }; - IMPLEMENT_CURL_OPTION_ENUM(set_http_version, native::CURLOPT_HTTP_VERSION, http_version_t, long); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_ignore_content_length, native::CURLOPT_IGNORE_CONTENT_LENGTH); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_http_content_decoding, native::CURLOPT_HTTP_CONTENT_DECODING); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_http_transfer_decoding, native::CURLOPT_HTTP_TRANSFER_DECODING); - - // protocol options - - IMPLEMENT_CURL_OPTION_BOOLEAN(set_transfer_text, native::CURLOPT_TRANSFERTEXT); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_transfer_mode, native::CURLOPT_PROXY_TRANSFER_MODE); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_crlf, native::CURLOPT_CRLF); - IMPLEMENT_CURL_OPTION_STRING(set_range, native::CURLOPT_RANGE); - IMPLEMENT_CURL_OPTION(set_resume_from, native::CURLOPT_RESUME_FROM, long); - IMPLEMENT_CURL_OPTION(set_resume_from_large, native::CURLOPT_RESUME_FROM_LARGE, native::curl_off_t); - IMPLEMENT_CURL_OPTION_STRING(set_custom_request, native::CURLOPT_CUSTOMREQUEST); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_file_time, native::CURLOPT_FILETIME); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_no_body, native::CURLOPT_NOBODY); - IMPLEMENT_CURL_OPTION(set_in_file_size, native::CURLOPT_INFILESIZE, long); - IMPLEMENT_CURL_OPTION(set_in_file_size_large, native::CURLOPT_INFILESIZE_LARGE, native::curl_off_t); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_upload, native::CURLOPT_UPLOAD); - IMPLEMENT_CURL_OPTION(set_max_file_size, native::CURLOPT_MAXFILESIZE, long); - IMPLEMENT_CURL_OPTION(set_max_file_size_large, native::CURLOPT_MAXFILESIZE_LARGE, native::curl_off_t); - enum time_condition_t { - if_modified_since = native::CURL_TIMECOND_IFMODSINCE, - if_unmodified_since = native::CURL_TIMECOND_IFUNMODSINCE - }; - IMPLEMENT_CURL_OPTION_ENUM(set_time_condition, native::CURLOPT_TIMECONDITION, time_condition_t, long); - IMPLEMENT_CURL_OPTION(set_time_value, native::CURLOPT_TIMEVALUE, long); - - // connection options - - IMPLEMENT_CURL_OPTION(set_timeout, native::CURLOPT_TIMEOUT, long); - IMPLEMENT_CURL_OPTION(set_timeout_ms, native::CURLOPT_TIMEOUT_MS, long); - IMPLEMENT_CURL_OPTION(set_low_speed_limit, native::CURLOPT_LOW_SPEED_LIMIT, long); - IMPLEMENT_CURL_OPTION(set_low_speed_time, native::CURLOPT_LOW_SPEED_TIME, long); - IMPLEMENT_CURL_OPTION(set_max_send_speed_large, native::CURLOPT_MAX_SEND_SPEED_LARGE, native::curl_off_t); - IMPLEMENT_CURL_OPTION(set_max_recv_speed_large, native::CURLOPT_MAX_RECV_SPEED_LARGE, native::curl_off_t); - IMPLEMENT_CURL_OPTION(set_max_connects, native::CURLOPT_MAXCONNECTS, long); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_fresh_connect, native::CURLOPT_FRESH_CONNECT); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_forbot_reuse, native::CURLOPT_FORBID_REUSE); - IMPLEMENT_CURL_OPTION(set_connect_timeout, native::CURLOPT_CONNECTTIMEOUT, long); - IMPLEMENT_CURL_OPTION(set_connect_timeout_ms, native::CURLOPT_CONNECTTIMEOUT_MS, long); - enum ip_resolve_t { - ip_resolve_whatever = CURL_IPRESOLVE_WHATEVER, - ip_resolve_v4 = CURL_IPRESOLVE_V4, - ip_resolve_v6 = CURL_IPRESOLVE_V6 - }; - IMPLEMENT_CURL_OPTION_ENUM(set_ip_resolve, native::CURLOPT_IPRESOLVE, ip_resolve_t, long); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_connect_only, native::CURLOPT_CONNECT_ONLY); - enum use_ssl_t { - use_ssl_none = native::CURLUSESSL_NONE, - use_ssl_try = native::CURLUSESSL_TRY, - use_ssl_control = native::CURLUSESSL_CONTROL, - use_ssl_all = native::CURLUSESSL_ALL - }; - IMPLEMENT_CURL_OPTION_ENUM(set_use_ssl, native::CURLOPT_USE_SSL, use_ssl_t, long); + void add_proxy_header(std::string_view name, std::string_view value, + EmptyHeaderAction = EmptyHeaderAction::kSend, DuplicateHeaderAction = DuplicateHeaderAction::kAdd); + void add_proxy_header(const char* header); + void add_proxy_header(const std::string& header); void add_resolve(const std::string& host, const std::string& port, const std::string& addr); - void add_resolve(const std::string& host, const std::string& port, const std::string& addr, std::error_code& ec); + void add_http200_alias(const std::string& http200_alias); + + std::optional FindHeaderByName(std::string_view name) const; + +public: +/// cURL Opt setters + void set_verbose(bool state); + void set_header(bool state); + void set_no_progress(bool state); + void set_no_signal(bool state); + [[maybe_unused]] void set_wildcard_match(bool state); + void set_post(bool state); + + [[maybe_unused]] void set_source(std::shared_ptr source); + void set_sink(std::string* sink); + void set_private(void* ptr); + void set_custom_request(const std::string& str); + [[maybe_unused]] void set_new_directory_perms(long perms); + [[maybe_unused]] void set_new_file_perms(long perms); + + void set_url(std::string&& url_str, std::error_code& ec); + [[maybe_unused]] void set_progress_data(void* ptr); + + void set_error_buffer(char* buffer); + void set_stderr(FILE* file); + void set_fail_on_error(bool state); + + void set_mimepost(std::unique_ptr mime_ptr); + +// * puplic http setters @fn set_* @return none + [[maybe_unused]] void set_http_proxy_tunnel(bool state); + [[maybe_unused]] void set_socks5_gsapi_nec(bool state); + [[maybe_unused]] void set_tcp_no_delay(bool state); + [[maybe_unused]] void set_tcp_keep_alive(bool state); + [[maybe_unused]] void set_auto_referrer(bool state); + void set_follow_location(bool state); + [[maybe_unused]] void set_unrestricted_auth(bool state); + void set_max_redirs(long value); + void set_post_redir(long value); + [[maybe_unused]] void set_post_field_size(long value); + [[maybe_unused]] void set_proxy_port(long value); + [[maybe_unused]] void set_proxy_type(long value); + [[maybe_unused]] void set_local_port(long value); + [[maybe_unused]] void set_local_port_range(long value); + [[maybe_unused]] void set_dns_cache_timeout(long value); + [[maybe_unused]] void set_buffer_size(long value); + [[maybe_unused]] void set_port(long value); + [[maybe_unused]] void set_address_scope(long value); + [[maybe_unused]] void set_tcp_keep_idle(long value); + [[maybe_unused]] void set_tcp_keep_intvl(long value); + void set_connect_to(native::curl_slist* slist); + void set_post_field_size_large(native::curl_off_t value); + void set_post_fields(void* ptr); + void set_post_fields(std::string&& post_fields); + void set_proxy(std::string_view sv); + [[maybe_unused]] void set_interface(std::string_view sv); + void set_unix_socket_path(std::string_view sv); + [[maybe_unused]] void set_no_proxy(std::string_view sv); + void set_accept_encoding(std::string_view sv); + [[maybe_unused]] void set_transfer_encoding(std::string_view sv); + [[maybe_unused]] void set_referer(std::string_view sv); + void set_user_agent(std::string_view sv); + [[maybe_unused]] void set_protocols_str(std::string_view sv); + [[maybe_unused]] void set_redir_protocols_str(std::string_view sv); + [[maybe_unused]] void set_headers(std::shared_ptr headers); + [[maybe_unused]] void set_http200_aliases(std::shared_ptr http200_aliases); + +// * puplic auth setters @fn set_* @return none + [[maybe_unused]] void set_tls_auth_type(long value); + [[maybe_unused]] void set_tls_auth_user(std::string_view sv); + [[maybe_unused]] void set_tls_auth_password(std::string_view sv); + [[maybe_unused]] void set_netrc_file(std::string_view sv); + void set_user(std::string_view sv); + void set_password(std::string_view sv); + [[maybe_unused]] void set_proxy_user(std::string_view sv); + [[maybe_unused]] void set_proxy_password(std::string_view sv); + [[maybe_unused]] void set_netrc(netrc_t netrc); + void set_http_auth(httpauth_t httpauth, bool auth_only); + void set_proxy_auth(proxyauth_t proxyauth); + +// * puplic protocol setters @fn set_* @return none + [[maybe_unused]] void set_transfer_text(bool state); + [[maybe_unused]] void set_transfer_mode(bool state); + [[maybe_unused]] void set_crlf(bool state); + [[maybe_unused]] void set_file_time(bool state); + [[maybe_unused]] void set_upload(bool state); + void set_no_body(bool state); + [[maybe_unused]] void set_ignore_content_length(bool state); + [[maybe_unused]] void set_http_content_decoding(bool state); + [[maybe_unused]] void set_http_transfer_decoding(bool state); + void set_http_get(bool state); + [[maybe_unused]] void set_cookie_session(bool state); + [[maybe_unused]] void set_resume_from(long value); + [[maybe_unused]] void set_in_file_size(long value); + [[maybe_unused]] void set_max_file_size(long value); + [[maybe_unused]] void set_time_value(long value); + [[maybe_unused]] void set_resume_from_large(native::curl_off_t value); + [[maybe_unused]] void set_in_file_size_large(native::curl_off_t value); + [[maybe_unused]] void set_max_file_size_large(native::curl_off_t value); + [[maybe_unused]] void set_range(std::string_view sv); + [[maybe_unused]] void set_cookie_list(std::string_view sv); + void set_cookie(std::string_view sv); + [[maybe_unused]] void set_cookie_file(std::string_view sv); + [[maybe_unused]] void set_cookie_jar(std::string_view sv); + void set_http_version(http_version_t version); + [[maybe_unused]] void set_time_condition(time_condition_t condition); + +// * puplic connection setters @fn set_* @return none + [[maybe_unused]] void set_fresh_connect(bool state); + [[maybe_unused]] void set_forbit_reuse(bool state); + [[maybe_unused]] void set_connect_only(bool state); + [[maybe_unused]] void set_timeout(long timeout); + void set_timeout_ms(long timeout); + [[maybe_unused]] void set_low_speed_limit(long limit); + [[maybe_unused]] void set_low_speed_time(long time); + [[maybe_unused]] void set_max_connects(long connects); + [[maybe_unused]] void set_connect_timeout(long timeout); + void set_connect_timeout_ms(long timeout); + [[maybe_unused]] void set_accept_timeout_ms(long timeout); + [[maybe_unused]] void set_max_send_speed_large(native::curl_off_t large); + [[maybe_unused]] void set_max_recv_speed_large(native::curl_off_t large); + [[maybe_unused]] void set_dns_servers(std::string_view sv); + void set_ip_resolve(ip_resolve_t resolve); void set_resolves(std::shared_ptr resolved_hosts); - void set_resolves(std::shared_ptr resolved_hosts, std::error_code& ec); - IMPLEMENT_CURL_OPTION_STRING(set_dns_servers, native::CURLOPT_DNS_SERVERS); - IMPLEMENT_CURL_OPTION(set_accept_timeout_ms, native::CURLOPT_ACCEPTTIMEOUT_MS, long); - - // SSL and security options - - IMPLEMENT_CURL_OPTION_STRING(set_ssl_cert, native::CURLOPT_SSLCERT); - IMPLEMENT_CURL_OPTION_STRING(set_ssl_cert_type, native::CURLOPT_SSLCERTTYPE); - IMPLEMENT_CURL_OPTION_STRING(set_ssl_key, native::CURLOPT_SSLKEY); - IMPLEMENT_CURL_OPTION_STRING(set_ssl_key_type, native::CURLOPT_SSLKEYTYPE); - IMPLEMENT_CURL_OPTION_STRING(set_ssl_key_passwd, native::CURLOPT_KEYPASSWD); - IMPLEMENT_CURL_OPTION_STRING(set_ssl_engine, native::CURLOPT_SSLENGINE); - IMPLEMENT_CURL_OPTION_STRING(set_ssl_engine_default, native::CURLOPT_SSLENGINE_DEFAULT); -#if LIBCURL_VERSION_NUM >= 0x074700 - IMPLEMENT_CURL_OPTION_BLOB(set_ssl_cert_blob, native::CURLOPT_SSLCERT_BLOB); - IMPLEMENT_CURL_OPTION_BLOB(set_ssl_key_blob, native::CURLOPT_SSLKEY_BLOB); -#else - DELETE_CURL_OPTION_BLOB(set_ssl_cert_blob); - DELETE_CURL_OPTION_BLOB(set_ssl_key_blob); + +// * puplic ssh setters @fn set_* @return none + [[maybe_unused]] void set_ssh_auth_types(long types); + [[maybe_unused]] void set_ssh_host_public_key_md5(std::string_view sv); + [[maybe_unused]] void set_ssh_public_key_file(std::string_view sv); + [[maybe_unused]] void set_ssh_private_key_file(std::string_view sv); + [[maybe_unused]] void set_ssh_known_hosts(std::string_view sv); + [[maybe_unused]] void set_ssh_key_function(void* ptr); // !TODO ssh key callback ? + [[maybe_unused]] void set_ssh_key_data(void* ptr); + +// * ssl and security setters @fn set_* @return none + void set_ssl_cert(std::string_view sv); + void set_ssl_cert_type(std::string_view sv); + void set_ssl_key(std::string_view sv); + void set_ssl_key_type(std::string_view sv); + void set_ssl_key_passwd(std::string_view sv); + void set_ssl_engine(std::string_view sv); + void set_ssl_engine_default(std::string_view sv); + void set_ca_info(std::string_view sv); + void set_ca_file(std::string_view sv); + void set_crl_file(std::string_view sv); + void set_issuer_cert(std::string_view sv); + void set_ssl_cipher_list(std::string_view sv); + void set_krb_level(std::string_view sv); + void set_ssl_options(long options); + void set_gssapi_delegation(long arg); + void set_cert_info(bool state); + void set_ssl_session_id_cache(bool state); + void set_ssl_verify_peer(bool state); + void set_ssl_verify_host(bool state); + void set_ssl_version(ssl_version_t version); + void set_use_ssl(use_ssl_t ssl); + +#if LIBCURL_VERSION_NUM >= 0x074700 + void set_ssl_cert_blob_copy(std::string_view sv); + void set_ssl_cert_blob_no_copy(std::string_view sv); + void set_ssl_key_blob_copy(std::string_view sv); + void set_ssl_key_blob_no_copy(std::string_view sv); + + static constexpr bool is_set_ssl_cert_blob_available = true; + static constexpr bool is_set_ssl_key_blob_available = true; #endif - enum ssl_version_t { - ssl_version_default = CURL_SSLVERSION_NAMESPACE CURL_SSLVERSION_DEFAULT, - ssl_version_tls_v1 = CURL_SSLVERSION_NAMESPACE CURL_SSLVERSION_TLSv1, - ssl_version_ssl_v2 = CURL_SSLVERSION_NAMESPACE CURL_SSLVERSION_SSLv2, - ssl_version_ssl_v3 = CURL_SSLVERSION_NAMESPACE CURL_SSLVERSION_SSLv3, - }; - IMPLEMENT_CURL_OPTION_ENUM(set_ssl_version, native::CURLOPT_SSLVERSION, ssl_version_t, long); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_ssl_verify_peer, native::CURLOPT_SSL_VERIFYPEER); - IMPLEMENT_CURL_OPTION_STRING(set_ca_info, native::CURLOPT_CAINFO); + #if LIBCURL_VERSION_NUM >= 0x074D00 - IMPLEMENT_CURL_OPTION_BLOB(set_ca_info_blob, native::CURLOPT_CAINFO_BLOB); -#else - DELETE_CURL_OPTION_BLOB(set_ca_info_blob); + void set_ca_info_blob_copy(std::string_view sv); + void set_ca_info_blob_no_copy(std::string_view sv); + + static constexpr bool is_set_ca_info_blob_available = true; #endif - IMPLEMENT_CURL_OPTION_STRING(set_issuer_cert, native::CURLOPT_ISSUERCERT); - IMPLEMENT_CURL_OPTION_STRING(set_ca_file, native::CURLOPT_CAPATH); - IMPLEMENT_CURL_OPTION_STRING(set_crl_file, native::CURLOPT_CRLFILE); - inline void set_ssl_verify_host(bool verify_host) { - std::error_code ec; - set_ssl_verify_host(verify_host, ec); - throw_error(ec, "set_ssl_verify_host failed"); - } - inline void set_ssl_verify_host(bool verify_host, std::error_code& ec) { - ec = std::error_code{static_cast( - native::curl_easy_setopt(handle_, native::CURLOPT_SSL_VERIFYHOST, verify_host ? 2L : 0L) - )}; - } - IMPLEMENT_CURL_OPTION_BOOLEAN(set_cert_info, native::CURLOPT_CERTINFO); - IMPLEMENT_CURL_OPTION_STRING(set_random_file, native::CURLOPT_RANDOM_FILE); - IMPLEMENT_CURL_OPTION_STRING(set_egd_socket, native::CURLOPT_EGDSOCKET); - IMPLEMENT_CURL_OPTION_STRING(set_ssl_cipher_list, native::CURLOPT_SSL_CIPHER_LIST); - IMPLEMENT_CURL_OPTION_BOOLEAN(set_ssl_session_id_cache, native::CURLOPT_SSL_SESSIONID_CACHE); - IMPLEMENT_CURL_OPTION(set_ssl_options, native::CURLOPT_SSL_OPTIONS, long); - IMPLEMENT_CURL_OPTION_STRING(set_krb_level, native::CURLOPT_KRBLEVEL); - IMPLEMENT_CURL_OPTION(set_gssapi_delegation, native::CURLOPT_GSSAPI_DELEGATION, long); - - // SSH options - - IMPLEMENT_CURL_OPTION(set_ssh_auth_types, native::CURLOPT_SSH_AUTH_TYPES, long); - IMPLEMENT_CURL_OPTION_STRING(set_ssh_host_public_key_md5, native::CURLOPT_SSH_HOST_PUBLIC_KEY_MD5); - IMPLEMENT_CURL_OPTION_STRING(set_ssh_public_key_file, native::CURLOPT_SSH_PUBLIC_KEYFILE); - IMPLEMENT_CURL_OPTION_STRING(set_ssh_private_key_file, native::CURLOPT_SSH_PRIVATE_KEYFILE); - IMPLEMENT_CURL_OPTION_STRING(set_ssh_known_hosts, native::CURLOPT_SSH_KNOWNHOSTS); - IMPLEMENT_CURL_OPTION(set_ssh_key_function, native::CURLOPT_SSH_KEYFUNCTION, - void*); // TODO curl_sshkeycallback? - IMPLEMENT_CURL_OPTION(set_ssh_key_data, native::CURLOPT_SSH_KEYDATA, void*); - - // other options - - IMPLEMENT_CURL_OPTION(set_private, native::CURLOPT_PRIVATE, void*); - void set_share(std::shared_ptr share); - void set_share(std::shared_ptr share, std::error_code& ec); - IMPLEMENT_CURL_OPTION(set_new_file_perms, native::CURLOPT_NEW_FILE_PERMS, long); - IMPLEMENT_CURL_OPTION(set_new_directory_perms, native::CURLOPT_NEW_DIRECTORY_PERMS, long); - - // getters - - IMPLEMENT_CURL_OPTION_GET_STRING_VIEW(get_effective_url, native::CURLINFO_EFFECTIVE_URL); - IMPLEMENT_CURL_OPTION_GET_LONG(get_response_code, native::CURLINFO_RESPONSE_CODE); - // CURLINFO_TOTAL_TIME - // CURLINFO_NAMELOOKUP_TIME - // CURLINFO_CONNECT_TIME - // CURLINFO_PRETRANSFER_TIME - // CURLINFO_SIZE_UPLOAD - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_size_upload, native::CURLINFO_SIZE_UPLOAD_T); - // CURLINFO_SIZE_DOWNLOAD - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_size_download, native::CURLINFO_SIZE_DOWNLOAD_T); - // CURLINFO_SPEED_DOWNLOAD - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_speed_download_bps, native::CURLINFO_SPEED_DOWNLOAD_T); - // CURLINFO_SPEED_UPLOAD - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_speed_upload_bps, native::CURLINFO_SPEED_UPLOAD_T); - IMPLEMENT_CURL_OPTION_GET_LONG(get_header_size, native::CURLINFO_HEADER_SIZE); - IMPLEMENT_CURL_OPTION_GET_LONG(get_request_size, native::CURLINFO_REQUEST_SIZE); - IMPLEMENT_CURL_OPTION_GET_LONG(get_ssl_verifyresult, native::CURLINFO_SSL_VERIFYRESULT); - // CURLINFO_FILETIME - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_filetime_sec, native::CURLINFO_FILETIME_T); - // CURLINFO_CONTENT_LENGTH_DOWNLOAD - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_content_length_download, native::CURLINFO_CONTENT_LENGTH_DOWNLOAD_T); - // CURLINFO_CONTENT_LENGTH_UPLOAD - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_content_length_upload, native::CURLINFO_CONTENT_LENGTH_UPLOAD_T); - // CURLINFO_STARTTRANSFER_TIME - IMPLEMENT_CURL_OPTION_GET_STRING_VIEW(get_content_type, native::CURLINFO_CONTENT_TYPE); - // CURLINFO_REDIRECT_TIME - IMPLEMENT_CURL_OPTION_GET_LONG(get_redirect_count, native::CURLINFO_REDIRECT_COUNT); - // CURLINFO_PRIVATE - IMPLEMENT_CURL_OPTION_GET_LONG(get_http_connectcode, native::CURLINFO_HTTP_CONNECTCODE); - IMPLEMENT_CURL_OPTION_GET_LONG(get_httpauth_avail, native::CURLINFO_HTTPAUTH_AVAIL); - IMPLEMENT_CURL_OPTION_GET_LONG(get_proxyauth_avail, native::CURLINFO_PROXYAUTH_AVAIL); - IMPLEMENT_CURL_OPTION_GET_LONG(get_os_errno, native::CURLINFO_OS_ERRNO); - IMPLEMENT_CURL_OPTION_GET_LONG(get_num_connects, native::CURLINFO_NUM_CONNECTS); - IMPLEMENT_CURL_OPTION_GET_LIST(get_ssl_engines, native::CURLINFO_SSL_ENGINES); - IMPLEMENT_CURL_OPTION_GET_LIST(get_cookielist, native::CURLINFO_COOKIELIST); - IMPLEMENT_CURL_OPTION_GET_STRING_VIEW(get_ftp_entry_path, native::CURLINFO_FTP_ENTRY_PATH); - IMPLEMENT_CURL_OPTION_GET_STRING_VIEW(get_redirect_url, native::CURLINFO_REDIRECT_URL); - IMPLEMENT_CURL_OPTION_GET_STRING_VIEW(get_primary_ip, native::CURLINFO_PRIMARY_IP); - // CURLINFO_APPCONNECT_TIME - // CURLINFO_CERTINFO - IMPLEMENT_CURL_OPTION_GET_LONG(get_condition_unmet, native::CURLINFO_CONDITION_UNMET); - IMPLEMENT_CURL_OPTION_GET_STRING_VIEW(get_rtsp_session_id, native::CURLINFO_RTSP_SESSION_ID); - IMPLEMENT_CURL_OPTION_GET_LONG(get_rtsp_client_cseq, native::CURLINFO_RTSP_CLIENT_CSEQ); - IMPLEMENT_CURL_OPTION_GET_LONG(get_rtsp_server_cseq, native::CURLINFO_RTSP_SERVER_CSEQ); - IMPLEMENT_CURL_OPTION_GET_LONG(get_rtsp_cseq_recv, native::CURLINFO_RTSP_CSEQ_RECV); - IMPLEMENT_CURL_OPTION_GET_LONG(get_primary_port, native::CURLINFO_PRIMARY_PORT); - IMPLEMENT_CURL_OPTION_GET_STRING_VIEW(get_local_ip, native::CURLINFO_LOCAL_IP); - IMPLEMENT_CURL_OPTION_GET_LONG(get_local_port, native::CURLINFO_LOCAL_PORT); - // CURLINFO_TLS_SESSION - // CURLINFO_ACTIVESOCKET - // CURLINFO_TLS_SSL_PTR - IMPLEMENT_CURL_OPTION_GET_LONG(get_http_version, native::CURLINFO_HTTP_VERSION); - IMPLEMENT_CURL_OPTION_GET_LONG(get_proxy_ssl_verifyresult, native::CURLINFO_PROXY_SSL_VERIFYRESULT); - IMPLEMENT_CURL_OPTION_GET_LONG(get_protocol, native::CURLINFO_PROTOCOL); - IMPLEMENT_CURL_OPTION_GET_STRING_VIEW(get_scheme, native::CURLINFO_SCHEME); - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_total_time_usec, native::CURLINFO_TOTAL_TIME_T); - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_namelookup_time_usec, native::CURLINFO_NAMELOOKUP_TIME_T); - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_connect_time_usec, native::CURLINFO_CONNECT_TIME_T); - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_pretransfer_time_usec, native::CURLINFO_PRETRANSFER_TIME_T); - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_starttransfer_time_usec, native::CURLINFO_STARTTRANSFER_TIME_T); - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_redirect_time_usec, native::CURLINFO_REDIRECT_TIME_T); - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_appconnect_time_usec, native::CURLINFO_APPCONNECT_TIME_T); - IMPLEMENT_CURL_OPTION_GET_CURL_OFF_T(get_retry_after_sec, native::CURLINFO_RETRY_AFTER); - bool has_post_data() const; +// * puplic callback function @fn set_* @return none + using progress_function_t = std::function; + void set_progress_callback(progress_function_t func); + void unset_progress_callback(); - const std::string& get_post_data() const; + using header_function_t = size_t (*)(void* ptr, size_t size, size_t nmemb, void* userdata); + void set_header_function(header_function_t func); + void set_header_data(void* ptr); - std::string extract_post_data(); + using debug_function_t = int (*)(native::CURL*, native::curl_infotype, char*, size_t, void*); + void set_debug_function(debug_function_t func); + void set_debug_data(void* ptr); - inline bool operator<(const easy& other) const { return (this < &other); } + using interleave_function_t = size_t (*)(void* ptr, size_t size, size_t nmemb, void* userdata); + [[maybe_unused]] void set_interleave_function(interleave_function_t func); + [[maybe_unused]] void set_interleave_data(void* ptr); + + using write_function_t = size_t (*)(char* ptr, size_t size, size_t nmemb, void* userdata); + void set_write_function(write_function_t func, std::error_code& ec); + void set_write_data(void* ptr, std::error_code& ec); - void handle_completion(const std::error_code& err); + using read_function_t = std::size_t (*)(void* ptr, size_t size, size_t nmemb, void* userdata); + void set_read_function(read_function_t func, std::error_code& ec); + void set_read_data(void* ptr, std::error_code& ec); - void mark_retry(); + using seek_function_t = int (*)(void* instream, native::curl_off_t offset, int origin); + void set_seek_function(seek_function_t func, std::error_code& ec); + void set_seek_data(void* ptr, std::error_code& ec); - clients::http::LocalStats get_local_stats(); + using sockopt_function_t = int (*)(void* clientp, native::curl_socket_t curlfd, native::curlsocktype purpose); + void set_sockopt_function(sockopt_function_t func); + void set_sockopt_data(void* ptr); - std::error_code rate_limit_error() const; + using xfer_function_t = int (*)(void* clinetp, native::curl_off_t dltotal, native::curl_off_t dlnow, + native::curl_off_t ultotal, native::curl_off_t ulnow); + void set_xferinfo_function(xfer_function_t func); + void set_xferinfo_data(void* ptr); - time_point::duration time_to_start() const; + using opensocket_function_t = native::curl_socket_t (*)(void* clientp, + native::curlsocktype purpose, struct native::curl_sockaddr* address); + void set_opensocket_function(opensocket_function_t func); + void set_opensocket_data(void* ptr); + using closesocket_function_t = int (*)(void* clientp, native::curl_socket_t item); + void set_closesocket_function(closesocket_function_t func); + void set_closesocket_data(void* ptr); + + using ssl_ctx_function_t = native::CURLcode (*)(native::CURL* curl, void* sslctx, void* parm); + void set_ssl_ctx_function(ssl_ctx_function_t func, std::error_code& ec); + void set_ssl_ctx_data(void* ptr, std::error_code& ec); + +// * getters @fn get_* @return std::shared_ptr + [[nodiscard]] inline const std::string& get_original_url() const { return orig_url_str_; } + [[nodiscard]] inline const url& get_easy_url() const { return url_; } + +// * getters @fn get_* @return std::vector + [[nodiscard]] std::vector get_ssl_engines(); + [[nodiscard]] std::vector get_cookielist(); + +// * getters @fn get_* @return std::string_view + [[nodiscard]] std::string_view get_effective_url(); + [[nodiscard]] std::string_view get_effective_url(std::error_code& ec); + [[nodiscard]] std::string_view get_scheme(); + [[nodiscard]] std::string_view get_local_ip(); + [[nodiscard]] std::string_view get_rtsp_session_id(); + [[nodiscard]] std::string_view get_primary_ip(); + [[nodiscard]] std::string_view get_redirect_url(); + [[nodiscard]] std::string_view get_ftp_entry_path(); + [[nodiscard]] std::string_view get_content_type(); + +// * getters @fn get_* @return long + [[nodiscard]] long get_http_version(); + [[nodiscard]] long get_proxy_ssl_verifyresult(); + [[nodiscard]] long get_local_port(); + [[nodiscard]] long get_primary_port(); + [[nodiscard]] long get_rtsp_cseq_recv(); + [[nodiscard]] long get_rtsp_server_cseq(); + [[nodiscard]] long get_rtsp_client_cseq(); + [[nodiscard]] long get_condition_unmet(); + [[nodiscard]] long get_num_connects(); + [[nodiscard]] long get_os_errno(); + [[nodiscard]] long get_proxyauth_avail(); + [[nodiscard]] long get_httpauth_avail(); + [[nodiscard]] long get_http_connectcode(); + [[nodiscard]] long get_redirect_count(); + [[nodiscard]] long get_redirect_time(); + [[nodiscard]] long get_header_size(); + [[nodiscard]] long get_request_size(); + [[nodiscard]] long get_ssl_verifyresult(); + [[nodiscard]] long get_response_code(); + +// * getters stats @fn get_* @return long + [[nodiscard]] long get_content_length_upload(); // return native::curl_off_t ? + [[nodiscard]] long get_content_length_download(); // return native::curl_off_t ? + [[nodiscard]] long get_filetime_sec(); // return native::curl_off_t ? + [[nodiscard]] long get_speed_upload_bps(); // return native::curl_off_t ? + [[nodiscard]] long get_speed_download_bps(); // return native::curl_off_t ? + [[nodiscard]] long get_size_download(); // return native::curl_off_t ? + [[nodiscard]] long get_size_upload(); // return native::curl_off_t ? + +// * getters stats @fn get_* @return long + [[nodiscard]] long get_total_time_usec(); // return native::curl_off_t ? + [[nodiscard]] long get_namelookup_time_usec(); // return native::curl_off_t ? + [[nodiscard]] long get_connect_time_usec(); // return native::curl_off_t ? + [[nodiscard]] long get_pretransfer_time_usec(); // return native::curl_off_t ? + [[nodiscard]] long get_starttransfer_time_usec(); // return native::curl_off_t ? + [[nodiscard]] long get_redirect_time_usec(); // return native::curl_off_t ? + [[nodiscard]] long get_appconnect_time_usec(); // return native::curl_off_t ? + [[nodiscard]] long get_retry_after_sec(); // return native::curl_off_t ? + + // this deprecated function use set_mimepost() + [[deprecated("Use set_mimepost())")]] + void set_http_post(std::unique_ptr form_ptr); + // this deprecated function + [[deprecated("Serves no purpose anymore")]] [[noreturn]] + void set_random_file(std::string_view); // CURLOPT_RANDOM_FILE + // this deprecated function + [[deprecated("Serves no purpose anymore")]] + void set_egd_socket(std::string_view sv); // CURLOPT_EGDSOCKET + // this deprecated function + [[deprecated("Use set_redir_protocols_str()")]] [[noreturn]] + void set_redir_protocols(long); // CURLOPT_REDIR_PROTOCOLS + [[deprecated("Use set_protocols_str()")]] [[noreturn]] + void set_protocols(long); // CURLOPT_PROTOCOLS + // this deprecated function + [[deprecated("Use get_scheme()")]] + long get_protocol(); // CURLINFO_PROTOCOL + private: +// private static + static native::curl_socket_t open_socket(void* clientp, native::curlsocktype purpose, struct native::curl_sockaddr* address) noexcept; + static int close_socket(void* clientp, native::curl_socket_t item) noexcept; + static int seek_function(void* instream, native::curl_off_t offset, int origin) noexcept; static size_t header_function(void* ptr, size_t size, size_t nmemb, void* userdata); - - native::curl_socket_t open_tcp_socket(native::curl_sockaddr* address); - void cancel(size_t request_num); - + static size_t read_runction(void* ptr, size_t size, size_t nmemb, void* userdata) noexcept; static size_t write_function(char* ptr, size_t size, size_t nmemb, void* userdata) noexcept; - static size_t read_function(void* ptr, size_t size, size_t nmemb, void* userdata) noexcept; - static int seek_function(void* instream, native::curl_off_t offset, int origin) noexcept; - static int xferinfo_function( - void* clientp, - native::curl_off_t dltotal, - native::curl_off_t dlnow, - native::curl_off_t ultotal, - native::curl_off_t ulnow - ) noexcept; - static native::curl_socket_t - opensocket(void* clientp, native::curlsocktype purpose, struct native::curl_sockaddr* address) noexcept; - static int closesocket(void* clientp, native::curl_socket_t item) noexcept; + static int xfer_function(void* clinetp, native::curl_off_t dltotal, native::curl_off_t dlnow, + native::curl_off_t ultotal, native::curl_off_t ulnow) noexcept; + + native::curl_socket_t open_tcp_socket(struct native::curl_sockaddr* address) noexcept; // do_ev_* methods run in libev thread void do_ev_async_perform(handler_type handler, size_t request_num); @@ -704,39 +490,41 @@ class easy final : public std::enable_shared_from_this { void mark_start_performing(); void mark_open_socket(); - native::CURL* handle_{nullptr}; - multi* multi_; - size_t request_counter_{0}; - size_t cancelled_request_max_{0}; - bool multi_registered_{false}; - std::string orig_url_str_; - url url_; - handler_type handler_; - std::shared_ptr source_; - std::string* sink_{nullptr}; - std::string post_fields_; - std::shared_ptr form_; - std::shared_ptr headers_; - std::shared_ptr proxy_headers_; - std::shared_ptr http200_aliases_; - std::shared_ptr resolved_hosts_; - std::shared_ptr share_; - progress_callback_t progress_callback_; - std::size_t retries_count_{0}; - std::size_t sockets_opened_{0}; - std::error_code rate_limit_error_; + void cancel(size_t request_num); + +protected: + native::CURL* easy_handle_; + std::shared_ptr mime_; + multi* multi_handle_; + url url_; + + size_t request_counter_ { 0 }; + size_t retries_counter_ { 0 }; + size_t cancelled_request_max_ { 0 }; + size_t sockets_opened_ { 0 }; + + bool multi_registered_ { false }; + + std::string* sink_ { nullptr }; + std::string orig_url_str_ {}; + std::string post_fields_ {}; + + std::shared_ptr form_ { nullptr }; + std::shared_ptr share_ { nullptr }; + std::shared_ptr source_ { nullptr }; + std::shared_ptr headers_ { nullptr }; + std::shared_ptr proxy_headers_ { nullptr }; + std::shared_ptr http200_aliases_ { nullptr }; + std::shared_ptr resolved_hosts_ { nullptr }; + + handler_type handler_ {}; + progress_function_t progress_function_ {}; + + std::error_code rate_limit_error_ {}; time_point start_performing_ts_{}; const time_point construct_ts_; }; -} // namespace curl - -#undef IMPLEMENT_CURL_OPTION -#undef IMPLEMENT_CURL_OPTION_BOOLEAN -#undef IMPLEMENT_CURL_OPTION_ENUM -#undef IMPLEMENT_CURL_OPTION_STRING -#undef IMPLEMENT_CURL_OPTION_GET_STRING_VIEW -#undef IMPLEMENT_CURL_OPTION_GET_LONG -#undef IMPLEMENT_CURL_OPTION_GET_LIST +} // namespace curl -USERVER_NAMESPACE_END +USERVER_NAMESPACE_END \ No newline at end of file diff --git a/core/src/curl-ev/error_code.cpp b/core/src/curl-ev/error_code.cpp index c22dfd493daf..163acfda9290 100644 --- a/core/src/curl-ev/error_code.cpp +++ b/core/src/curl-ev/error_code.cpp @@ -74,7 +74,7 @@ class FormErrorCategory final : public std::error_category { default: return "unknown CURLFORMcode"; } - } + } }; class UrlErrorCategory final : public std::error_category { diff --git a/core/src/curl-ev/form.cpp b/core/src/curl-ev/form.cpp index a6cbb6894643..2ba8ea7af2d7 100644 --- a/core/src/curl-ev/form.cpp +++ b/core/src/curl-ev/form.cpp @@ -52,12 +52,7 @@ void form::add_content(std::string_view key, std::string_view content, const std throw_error(ec, "add_content"); } -void form::add_content( - std::string_view key, - std::string_view content, - const std::string& content_type, - std::error_code& ec -) { +void form::add_content(std::string_view key, std::string_view content, const std::string& content_type, std::error_code& ec) { ec = std::error_code{static_cast(native::curl_formadd( &post_, &last_, @@ -75,13 +70,7 @@ void form::add_content( ))}; } -void form::add_buffer( - const std::string& key, - const std::string& file_name, - const char* buffer, - size_t buffer_len, - std::error_code& ec -) { +void form::add_buffer(const std::string& key, const std::string& file_name, const char* buffer, size_t buffer_len, std::error_code& ec) { ec = std::error_code{static_cast(native::curl_formadd( &post_, &last_, @@ -99,44 +88,24 @@ void form::add_buffer( ))}; } -void form::add_buffer( - const std::string& key, - const std::string& file_name, - const std::shared_ptr& buffer -) { +void form::add_buffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer) { std::error_code ec; add_buffer(key, file_name, buffer, ec); throw_error(ec, "add_buffer"); } -void form::add_buffer( - const std::string& key, - const std::string& file_name, - const std::shared_ptr& buffer, - std::error_code& ec -) { +void form::add_buffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer, std::error_code& ec) { buffers_.push_back(buffer); add_buffer(key, file_name, buffers_.back()->c_str(), buffers_.back()->size(), ec); } -void form::add_buffer( - const std::string& key, - const std::string& file_name, - const std::shared_ptr& buffer, - const std::string& content_type -) { +void form::add_buffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer, const std::string& content_type) { std::error_code ec; add_buffer(key, file_name, buffer, content_type, ec); throw_error(ec, "add_buffer"); } -void form::add_buffer( - const std::string& key, - const std::string& file_name, - const std::shared_ptr& buffer, - const std::string& content_type, - std::error_code& ec -) { +void form::add_buffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer, const std::string& content_type, std::error_code& ec) { buffers_.push_back(buffer); ec = std::error_code{static_cast(native::curl_formadd( &post_, @@ -163,7 +132,7 @@ void form::add_file(const std::string& key, const std::string& file_path) { throw_error(ec, "add_file"); } -void form::add_file(const std::string& key, const std::string& file_path, std::error_code& ec) { +void form::add_file([[maybe_unused]] const std::string& key, [[maybe_unused]] const std::string& file_path, [[maybe_unused]] std::error_code& ec) { ec = std::error_code{static_cast(native::curl_formadd( &post_, &last_, @@ -183,12 +152,7 @@ void form::add_file(const std::string& key, const std::string& file_path, const throw_error(ec, "add_file"); } -void form::add_file( - const std::string& key, - const std::string& file_path, - const std::string& content_type, - std::error_code& ec -) { +void form::add_file(const std::string& key, const std::string& file_path, const std::string& content_type, std::error_code& ec) { ec = std::error_code{static_cast(native::curl_formadd( &post_, &last_, @@ -210,12 +174,7 @@ void form::add_file_using_name(const std::string& key, const std::string& file_p throw_error(ec, "add_file_using_name"); } -void form::add_file_using_name( - const std::string& key, - const std::string& file_path, - const std::string& file_name, - std::error_code& ec -) { +void form::add_file_using_name(const std::string& key, const std::string& file_path, const std::string& file_name, std::error_code& ec) { ec = std::error_code{static_cast(native::curl_formadd( &post_, &last_, @@ -231,24 +190,13 @@ void form::add_file_using_name( ))}; } -void form::add_file_using_name( - const std::string& key, - const std::string& file_path, - const std::string& file_name, - const std::string& content_type -) { +void form::add_file_using_name(const std::string& key, const std::string& file_path, const std::string& file_name, const std::string& content_type) { std::error_code ec; add_file_using_name(key, file_path, file_name, content_type, ec); throw_error(ec, "add_file_using_name"); } -void form::add_file_using_name( - const std::string& key, - const std::string& file_path, - const std::string& file_name, - const std::string& content_type, - std::error_code& ec -) { +void form::add_file_using_name(const std::string& key, const std::string& file_path, const std::string& file_name, const std::string& content_type, std::error_code& ec) { ec = std::error_code{static_cast(native::curl_formadd( &post_, &last_, @@ -292,12 +240,7 @@ void form::add_file_content(const std::string& key, const std::string& file_path throw_error(ec, "add_file_content"); } -void form::add_file_content( - const std::string& key, - const std::string& file_path, - const std::string& content_type, - std::error_code& ec -) { +void form::add_file_content(const std::string& key, const std::string& file_path, const std::string& content_type, std::error_code& ec) { ec = std::error_code{static_cast(native::curl_formadd( &post_, &last_, diff --git a/core/src/curl-ev/form.hpp b/core/src/curl-ev/form.hpp index fcacae37c98c..00a93fd40599 100644 --- a/core/src/curl-ev/form.hpp +++ b/core/src/curl-ev/form.hpp @@ -32,77 +32,30 @@ class form { void add_content(std::string_view key, std::string_view content); void add_content(std::string_view key, std::string_view content, std::error_code& ec); void add_content(std::string_view key, std::string_view content, const std::string& content_type); - void - add_content(std::string_view key, std::string_view content, const std::string& content_type, std::error_code& ec); + void add_content(std::string_view key, std::string_view content, const std::string& content_type, std::error_code& ec); void add_buffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer); - void add_buffer( - const std::string& key, - const std::string& file_name, - const std::shared_ptr& buffer, - std::error_code& ec - ); - void add_buffer( - const std::string& key, - const std::string& file_name, - const std::shared_ptr& buffer, - const std::string& content_type - ); - void add_buffer( - const std::string& key, - const std::string& file_name, - const std::shared_ptr& buffer, - const std::string& content_type, - std::error_code& ec - ); + void add_buffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer, std::error_code& ec); + void add_buffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer, const std::string& content_type); + void add_buffer(const std::string& key, const std::string& file_name, const std::shared_ptr& buffer, const std::string& content_type, std::error_code& ec); private: void add_file(const std::string& key, const std::string& file_path); void add_file(const std::string& key, const std::string& file_path, std::error_code& ec); void add_file(const std::string& key, const std::string& file_path, const std::string& content_type); - void add_file( - const std::string& key, - const std::string& file_path, - const std::string& content_type, - std::error_code& ec - ); + void add_file(const std::string& key, const std::string& file_path, const std::string& content_type, std::error_code& ec); + void add_file_using_name(const std::string& key, const std::string& file_path, const std::string& file_name); - void add_file_using_name( - const std::string& key, - const std::string& file_path, - const std::string& file_name, - std::error_code& ec - ); - void add_file_using_name( - const std::string& key, - const std::string& file_path, - const std::string& file_name, - const std::string& content_type - ); - void add_file_using_name( - const std::string& key, - const std::string& file_path, - const std::string& file_name, - const std::string& content_type, - std::error_code& ec - ); + void add_file_using_name(const std::string& key, const std::string& file_path, const std::string& file_name, std::error_code& ec); + void add_file_using_name(const std::string& key, const std::string& file_path, const std::string& file_name, const std::string& content_type); + void add_file_using_name(const std::string& key, const std::string& file_path, const std::string& file_name, const std::string& content_type, std::error_code& ec); + void add_file_content(const std::string& key, const std::string& file_path); void add_file_content(const std::string& key, const std::string& file_path, std::error_code& ec); void add_file_content(const std::string& key, const std::string& file_path, const std::string& content_type); - void add_file_content( - const std::string& key, - const std::string& file_path, - const std::string& content_type, - std::error_code& ec - ); - - void add_buffer( - const std::string& key, - const std::string& file_name, - const char* buffer, - size_t buffer_len, - std::error_code& ec - ); + void add_file_content(const std::string& key, const std::string& file_path, const std::string& content_type, std::error_code& ec); + + void add_buffer(const std::string& key, const std::string& file_name, const char* buffer, size_t buffer_len, std::error_code& ec); native::curl_httppost* post_{nullptr}; native::curl_httppost* last_{nullptr}; diff --git a/core/src/curl-ev/mime.cpp b/core/src/curl-ev/mime.cpp new file mode 100644 index 000000000000..80497669d50e --- /dev/null +++ b/core/src/curl-ev/mime.cpp @@ -0,0 +1,120 @@ +/** @file curl-ev/mime.cpp */ + +#include +#include +#include + +#include + +USERVER_NAMESPACE_BEGIN +namespace curl { + +mime::mime(native::curl_mime* mime_ptr) : + mime_(mime_ptr) { + +} + +mime::~mime() { + if (mime_) { + native::curl_mime_free(mime_); + mime_ = nullptr; + } +} + +void mime::add_content(std::string_view key, std::string_view content) { + LOG_TRACE() << "mime::add_content start " << this; + + if(!mime_) { + LOG_ERROR() << "mime failed!"; + return; + } + // content part + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_data(part_, content.data(), content.length()); + // key part + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_data(part_, key.data(), key.length()); + native::curl_mime_name(part_, key.data()); + // + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_subparts(part_, mime_); + native::curl_mime_type(part_, "multipart/form-data"); + + + LOG_TRACE() << "mime::add_content finished " << this; +} + +void mime::add_content(std::string_view key, std::string_view content, std::string_view content_type) { + LOG_TRACE() << "mime::add_content start " << this; + + if(!mime_) { + LOG_ERROR() << "mime failed!"; + return; + } + // content part + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_data(part_, content.data(), content.length()); + native::curl_mime_type(part_, content_type.data()); + // key part + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_data(part_, key.data(), key.length()); + native::curl_mime_name(part_, key.data()); + // + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_subparts(part_, mime_); + native::curl_mime_type(part_, "multipart/from-data"); + + LOG_TRACE() << "mime::add_content finished " << this; +} + +void mime::add_buffer(std::string_view key, std::string_view name, const std::shared_ptr& buffer) { + LOG_TRACE() << "mime::add_buffer start " << this; + + if(!mime_) { + LOG_ERROR() << "mime failed!"; + return; + } + + buffers_.push_back(buffer); + + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_data(part_, buffer->c_str(), buffer->length()); + + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_data(part_, key.data(), key.length()); + native::curl_mime_name(part_, name.data()); + + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_subparts(part_, mime_); + native::curl_mime_type(part_, "multipart/from-data"); + + LOG_TRACE() << "mime::add_buffer finished " << this; +} + +void mime::add_buffer(std::string_view key, std::string_view name, std::string_view content_type, const std::shared_ptr& buffer) { + LOG_TRACE() << "mime::add_buffer start " << this; + + if(!mime_) { + LOG_ERROR() << "mime failed!"; + return; + } + + buffers_.push_back(buffer); + + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_data(part_, buffer->c_str(), buffer->length()); + native::curl_mime_type(part_, content_type.data()); + + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_data(part_, key.data(), key.length()); + native::curl_mime_name(part_, name.data()); + + part_ = native::curl_mime_addpart(mime_); + native::curl_mime_subparts(part_, mime_); + native::curl_mime_type(part_, "multipart/from-data"); + + LOG_TRACE() << "mime::add_buffer finished " << this; +} + +} // curl namespace +USERVER_NAMESPACE_END \ No newline at end of file diff --git a/core/src/curl-ev/mime.hpp b/core/src/curl-ev/mime.hpp new file mode 100644 index 000000000000..c925e0b9a5bb --- /dev/null +++ b/core/src/curl-ev/mime.hpp @@ -0,0 +1,43 @@ +#pragma once + +/** @file curl-ev/mime.cpp */ + +#include +#include +#include + +#include + +USERVER_NAMESPACE_BEGIN + +namespace curl { + +class mime final { +public: + mime(native::curl_mime* mime_ptr); + ~mime(); + + mime(const mime& rhs) = delete; + mime(mime&& rhs) = delete; + + mime& operator= (const mime& rhs) = delete; + mime& operator= (mime&& rhs) = delete; + + inline native::curl_mime* native_mime() { return mime_; } + inline native::curl_mimepart* native_part() { return part_; } + +public: + void add_content(std::string_view key, std::string_view content); + void add_content(std::string_view key, std::string_view content, std::string_view content_type); + + void add_buffer(std::string_view key, std::string_view name, const std::shared_ptr& buffer); + void add_buffer(std::string_view key, std::string_view name, std::string_view content_type, const std::shared_ptr& buffer); +private: + native::curl_mime* mime_ { nullptr }; + native::curl_mimepart* part_ { nullptr }; + std::vector> buffers_ { nullptr }; +}; + +} // namespace curl + +USERVER_NAMESPACE_END \ No newline at end of file