From f1cc8cfb2dea9e05f83a1f2856b27e926189a859 Mon Sep 17 00:00:00 2001 From: tastytea Date: Thu, 9 Jan 2020 19:01:52 +0100 Subject: [PATCH 01/24] =?UTF-8?q?Typo:=20typo=20=E2=86=92=20type.=20?= =?UTF-8?q?=F0=9F=98=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/example02_streaming.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example02_streaming.cpp b/examples/example02_streaming.cpp index 389b16b..b3984f2 100644 --- a/examples/example02_streaming.cpp +++ b/examples/example02_streaming.cpp @@ -68,7 +68,7 @@ int main(int argc, char *argv[]) sleep_for(2s); for (const auto &event : connection.get_new_events()) { - // Print typo of event and the beginning of the data. + // Print type of event and the beginning of the data. cout << event.type << ": " << event.data.substr(0, 70) << " …" << endl; } From 9bc1be8481473dcad18c2c420d3f388a8cb80c78 Mon Sep 17 00:00:00 2001 From: tastytea Date: Thu, 9 Jan 2020 19:05:37 +0100 Subject: [PATCH 02/24] Shorten streaming example. --- examples/example02_streaming.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/example02_streaming.cpp b/examples/example02_streaming.cpp index b3984f2..1ac1b8f 100644 --- a/examples/example02_streaming.cpp +++ b/examples/example02_streaming.cpp @@ -76,8 +76,7 @@ int main(int argc, char *argv[]) // Cancel the stream, … connection.cancel_stream(); - // … and get the rest of the data. - cout << connection.get_new_stream_contents() << endl; + // … and wait for the thread. stream_thread.join(); } else From a395d0e7bf8f5dcfe4d23473e52b83b3dded969b Mon Sep 17 00:00:00 2001 From: tastytea Date: Thu, 9 Jan 2020 19:18:45 +0100 Subject: [PATCH 03/24] Shorten the pkg-config file a bit. --- pkg-config/mastodonpp.pc.in | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg-config/mastodonpp.pc.in b/pkg-config/mastodonpp.pc.in index 49a4fde..d5aa215 100644 --- a/pkg-config/mastodonpp.pc.in +++ b/pkg-config/mastodonpp.pc.in @@ -1,10 +1,8 @@ -name=@PROJECT_NAME@ prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ -Name: ${name} +Name: @PROJECT_NAME@ Description: @PROJECT_DESCRIPTION@ Version: @PROJECT_VERSION@ Cflags: -I${includedir} From 23ab1d89976b6234c5451d712164fc8aeb9e41c1 Mon Sep 17 00:00:00 2001 From: tastytea Date: Thu, 9 Jan 2020 19:23:49 +0100 Subject: [PATCH 04/24] Add curl version to CMake config. --- cmake/mastodonppConfig.cmake.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/mastodonppConfig.cmake.in b/cmake/mastodonppConfig.cmake.in index 8c1180a..4dbde82 100644 --- a/cmake/mastodonppConfig.cmake.in +++ b/cmake/mastodonppConfig.cmake.in @@ -1,5 +1,5 @@ include(CMakeFindDependencyMacro) -find_dependency(CURL REQUIRED) +find_dependency(CURL 7.32 REQUIRED) include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") From a9e918ba6af8bbff427838d56968c66d82327214 Mon Sep 17 00:00:00 2001 From: tastytea Date: Thu, 9 Jan 2020 19:25:27 +0100 Subject: [PATCH 05/24] Document package generation switches. --- README.adoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.adoc b/README.adoc index cadfe1a..3f6301d 100644 --- a/README.adoc +++ b/README.adoc @@ -117,8 +117,8 @@ cmake --build . -- -j$(nproc --ignore=1) * `-DCMAKE_BUILD_TYPE=Debug` for a debug build. * `-DWITH_TESTS=YES` if you want to compile the tests. * `-DWITH_EXAMPLES=YES` if you want to compile the examples. -// * One of: -// ** `-DWITH_DEB=YES` if you want to be able to generate a deb-package. -// ** `-DWITH_RPM=YES` if you want to be able to generate an rpm-package. +* One of: + ** `-DWITH_DEB=YES` if you want to be able to generate a deb-package. + ** `-DWITH_RPM=YES` if you want to be able to generate an rpm-package. include::{uri-base}/raw/branch/main/CONTRIBUTING.adoc[] From 1263b03999bb0f6f38f725cf25b33c6da96e0053 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 12:18:04 +0100 Subject: [PATCH 06/24] Add a message only constructor to CURLException. --- include/exceptions.hpp | 7 +++++++ src/exceptions.cpp | 13 +++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/exceptions.hpp b/include/exceptions.hpp index 9e51d97..5cbad42 100644 --- a/include/exceptions.hpp +++ b/include/exceptions.hpp @@ -55,6 +55,13 @@ public: explicit CURLException(const CURLcode &error, string message, string error_buffer); + /*! + * @brief Constructor with message. + * + * @since 0.1.0 + */ + explicit CURLException(string message); + /*! * @brief The error code returned by libcurl. * diff --git a/src/exceptions.cpp b/src/exceptions.cpp index 7dd0e0f..f4630a4 100644 --- a/src/exceptions.cpp +++ b/src/exceptions.cpp @@ -36,10 +36,19 @@ CURLException::CURLException(const CURLcode &error, string message, , _error_buffer{move(error_buffer)} {} +CURLException::CURLException(string message) + : error_code{CURLE_OK} + , _message{move(message)} +{} + const char *CURLException::what() const noexcept { - static string error_string{"libCURL error: " + to_string(error_code) - + " - " + _message}; + static string error_string{"libCURL error: "}; + if (error_code != CURLE_OK) + { + error_string += to_string(error_code) + " - "; + } + error_string += _message; if (!_error_buffer.empty()) { error_string += " [" + _error_buffer + "]"; From 529e4bca6dcc7a5478c8247e2917f4905d8835b9 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 12:18:46 +0100 Subject: [PATCH 07/24] Add parameterpair. --- include/curl_wrapper.hpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index fc3bf3f..f4da6ff 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,7 @@ using std::map; using std::mutex; using std::string; using std::string_view; +using std::pair; using std::variant; using std::vector; @@ -53,7 +55,7 @@ enum class http_method }; /*! - * @brief std::map of parameters for %API calls. + * @brief `std::map` of parameters for %API calls. * * Example: * @code @@ -66,7 +68,16 @@ enum class http_method * * @since 0.1.0 */ -using parametermap = map>>; +using parametermap = + map>>; + +/*! + * @brief A single parameter of a parametermap. + * + * @since 0.1.0 + */ +using parameterpair = + pair>>; /*! * @brief Handles the details of network connections. From f2222151169419fe3afbcdfa3c4ec53627ab1021 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 13:08:34 +0100 Subject: [PATCH 08/24] Implement HTTP POST in CURLWrapper. --- cmake/mastodonppConfig.cmake.in | 2 +- include/curl_wrapper.hpp | 29 +++++++++ src/CMakeLists.txt | 2 +- src/curl_wrapper.cpp | 106 +++++++++++++++++++++++++++----- 4 files changed, 121 insertions(+), 18 deletions(-) diff --git a/cmake/mastodonppConfig.cmake.in b/cmake/mastodonppConfig.cmake.in index 4dbde82..e703c6a 100644 --- a/cmake/mastodonppConfig.cmake.in +++ b/cmake/mastodonppConfig.cmake.in @@ -1,5 +1,5 @@ include(CMakeFindDependencyMacro) -find_dependency(CURL 7.32 REQUIRED) +find_dependency(CURL 7.56 REQUIRED) include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index f4da6ff..0daf771 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -263,6 +263,18 @@ private: */ void setup_curl(); + /*! + * @brief Replace parameter in URI. + * + * @param uri Reference to the URI. + * @param parameter The parameter. + * + * @return true if parameter was replaced. + * + * @since 0.1.0 + */ + bool replace_parameter_in_uri(string &uri, const parameterpair ¶meter); + /*! * @brief Add parameters to URI. * @@ -272,6 +284,23 @@ private: * @since 0.1.0 */ void add_parameters_to_uri(string &uri, const parametermap ¶meters); + + /*! + * @brief Convert parametermap to `*curl_mime`. + * + * For more information consult [curl_mime_init(3)] + * (https://curl.haxx.se/libcurl/c/curl_mime_init.html). Calls + * replace_parameter_in_uri(). + * + * @param uri Reference to the URI. + * @param parameters The parametermap. + * + * @return `*curl_mime`. + * + * @since 0.1.0 + */ + curl_mime *parameters_to_curl_mime(string &uri, + const parametermap ¶meters); }; } // namespace mastodonpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b9ede8d..a7c16ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,6 @@ include(GNUInstallDirs) -find_package(CURL 7.32 REQUIRED) +find_package(CURL 7.56 REQUIRED) # Write version in header. configure_file ("version.hpp.in" diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index f675cca..6746fe1 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -68,7 +68,7 @@ CURLWrapper::~CURLWrapper() noexcept void CURLWrapper::set_proxy(const string_view proxy) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - CURLcode code = curl_easy_setopt(_connection, CURLOPT_PROXY, proxy); + CURLcode code{curl_easy_setopt(_connection, CURLOPT_PROXY, proxy)}; if (code != CURLE_OK) { throw CURLException{code, "Failed to set proxy", _curl_buffer_error}; @@ -101,8 +101,11 @@ answer_type CURLWrapper::make_request(const http_method &method, string uri, } case http_method::POST: { + curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - code = curl_easy_setopt(_connection, CURLOPT_POST, 1L); + code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); + break; } case http_method::PATCH: @@ -247,27 +250,38 @@ void CURLWrapper::setup_curl() curl_easy_setopt(_connection, CURLOPT_MAXREDIRS, 10L); } +bool CURLWrapper::replace_parameter_in_uri(string &uri, + const parameterpair ¶meter) +{ + static constexpr array replace + { + "id", "nickname", "nickname_or_id", + "hashtag", "permission_group" + }; + if (any_of(replace.begin(), replace.end(), + [¶meter](const auto &s) { return s == parameter.first; })) + { + const auto pos{uri.find('<')}; + if (pos != string::npos) + { + uri.replace(pos, parameter.first.size() + 2, + get(parameter.second)); + return true; + } + } + + return false; +} + void CURLWrapper::add_parameters_to_uri(string &uri, const parametermap ¶meters) { // Replace with the value of parameter “id” and so on. for (const auto ¶m : parameters) { - static constexpr array replace_in_uri - { - "id", "nickname", "nickname_or_id", - "hashtag", "permission_group" - }; - if (any_of(replace_in_uri.begin(), replace_in_uri.end(), - [¶m](const auto &s) { return s == param.first; })) + if (replace_parameter_in_uri(uri, param)) { - const auto pos{uri.find('<')}; - if (pos != string::npos) - { - uri.replace(pos, param.first.size() + 2, - get(param.second)); - continue; - } + continue; } static bool first{true}; @@ -298,4 +312,64 @@ void CURLWrapper::add_parameters_to_uri(string &uri, } } +curl_mime *CURLWrapper::parameters_to_curl_mime(string &uri, + const parametermap ¶meters) +{ + debuglog << "Building HTTP form.\n"; + + curl_mime *mime{curl_mime_init(_connection)}; + for (const auto ¶m : parameters) + { + if (replace_parameter_in_uri(uri, param)) + { + continue; + } + + curl_mimepart *part{curl_mime_addpart(mime)}; + if (part == nullptr) + { + throw CURLException{"Could not build HTTP form."}; + } + + CURLcode code; + if (holds_alternative(param.second)) + { + code = curl_mime_name(part, param.first.data()); + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + + code = curl_mime_data(part, get(param.second).data(), + CURL_ZERO_TERMINATED); + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + debuglog << "Set form part: " << param.first << " = " + << get(param.second) << '\n'; + } + else + { + for (const auto &arg : get>(param.second)) + { + const string name{string(param.first) += "[]"}; + code = curl_mime_name(part, name.c_str()); + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + code = curl_mime_data(part, arg.data(), CURL_ZERO_TERMINATED); + if (code != CURLE_OK) + { + throw CURLException{code, "Could not build HTTP form."}; + } + debuglog << "Set form part: " << name << " = " << arg << '\n'; + } + } + } + + return mime; +} + } // namespace mastodonpp From a6139b43900c7d580561f93f72be2e51ded5bd42 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 13:08:55 +0100 Subject: [PATCH 09/24] Implement HTTP POST in Connection. --- include/connection.hpp | 39 +++++++++++++++++++++++++++++++++++++++ src/connection.cpp | 28 ++++++++++++++++++---------- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/include/connection.hpp b/include/connection.hpp index 6885931..91a06d7 100644 --- a/include/connection.hpp +++ b/include/connection.hpp @@ -124,6 +124,43 @@ public: return get(endpoint, {}); } + /*! + * @brief Make a HTTP POST call with parameters. + * + * Example: + * @code + * auto answer{connection.post( + * mastodonpp::API::v1::statuses, + * { + * {"status", "How is the wheather?"}, + * {"poll[options]", vector{"Nice", "not nice"}}, + * {"poll[expires_in]", to_string(poll_seconds)} + * })}; + * @endcode + * + * @param endpoint Endpoint as API::endpoint_type or `std::string_view`. + * @param parameters A map of parameters. + * + * + * @since 0.1.0 + */ + [[nodiscard]] + answer_type post(const endpoint_variant &endpoint, + const parametermap ¶meters); + + /*! + * @brief Make a HTTP POST call. + * + * @param endpoint Endpoint as API::endpoint_type or `std::string_view`. + * + * @since 0.1.0 + */ + [[nodiscard]] + inline answer_type post(const endpoint_variant &endpoint) + { + return post(endpoint, {}); + } + /*! * @brief Copy new stream contents and delete the “original”. * @@ -147,6 +184,8 @@ public: private: Instance &_instance; const string_view _baseuri; + + string endpoint_to_uri(const endpoint_variant &endpoint) const; }; } // namespace mastodonpp diff --git a/src/connection.cpp b/src/connection.cpp index 662c333..498384d 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -32,20 +32,28 @@ Connection::Connection(Instance &instance) } } +string Connection::endpoint_to_uri(const endpoint_variant &endpoint) const +{ + if (holds_alternative(endpoint)) + { + return string(_baseuri) + += API{std::get(endpoint)}.to_string_view(); + } + return string(_baseuri) += std::get(endpoint); +} + answer_type Connection::get(const endpoint_variant &endpoint, const parametermap ¶meters) { - const string uri{[&] - { - if (holds_alternative(endpoint)) - { - return string(_baseuri) - += API{std::get(endpoint)}.to_string_view(); - } - return string(_baseuri) += std::get(endpoint); - }()}; + return make_request(http_method::GET, + endpoint_to_uri(endpoint), parameters); +} - return make_request(http_method::GET, uri, parameters); +answer_type Connection::post(const endpoint_variant &endpoint, + const parametermap ¶meters) +{ + return make_request(http_method::POST, + endpoint_to_uri(endpoint), parameters); } string Connection::get_new_stream_contents() From 89c50951b3b17159f3267373edba73eb9849b78d Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 13:41:38 +0100 Subject: [PATCH 10/24] Fix User-Agent. --- src/curl_wrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index 6746fe1..ae7562c 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -232,7 +232,7 @@ void CURLWrapper::setup_curl() // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) CURLcode code{curl_easy_setopt(_connection, CURLOPT_USERAGENT, - (string("mastorss/") += version).c_str())}; + (string("mastodonpp/") += version).c_str())}; if (code != CURLE_OK) { throw CURLException{code, "Failed to set User-Agent", From 1910e780b0fd3b3a57444537ab21cc263532ff86 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 14:24:35 +0100 Subject: [PATCH 11/24] Add vector fields to HTTP forms. --- src/curl_wrapper.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index ae7562c..86d3073 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -325,15 +325,15 @@ curl_mime *CURLWrapper::parameters_to_curl_mime(string &uri, continue; } - curl_mimepart *part{curl_mime_addpart(mime)}; - if (part == nullptr) - { - throw CURLException{"Could not build HTTP form."}; - } - CURLcode code; if (holds_alternative(param.second)) { + curl_mimepart *part{curl_mime_addpart(mime)}; + if (part == nullptr) + { + throw CURLException{"Could not build HTTP form."}; + } + code = curl_mime_name(part, param.first.data()); if (code != CURLE_OK) { @@ -353,6 +353,12 @@ curl_mime *CURLWrapper::parameters_to_curl_mime(string &uri, { for (const auto &arg : get>(param.second)) { + curl_mimepart *part{curl_mime_addpart(mime)}; + if (part == nullptr) + { + throw CURLException{"Could not build HTTP form."}; + } + const string name{string(param.first) += "[]"}; code = curl_mime_name(part, name.c_str()); if (code != CURLE_OK) From c3bb9a20c4c630f65120902e77a8b2fd6c711967 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 14:26:50 +0100 Subject: [PATCH 12/24] Send the access token. --- include/curl_wrapper.hpp | 7 +++++++ src/connection.cpp | 5 +++++ src/curl_wrapper.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index 0daf771..73c748a 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -199,6 +199,13 @@ protected: return _curl_buffer_body; } + /*! + * @brief Set OAuth 2.0 Bearer Access Token. + * + * @since 0.1.0 + */ + void set_access_token(const string_view access_token); + private: CURL *_connection; char _curl_buffer_error[CURL_ERROR_SIZE]; diff --git a/src/connection.cpp b/src/connection.cpp index 498384d..f74a816 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -30,6 +30,11 @@ Connection::Connection(Instance &instance) { CURLWrapper::set_proxy(proxy); } + + if (!_instance.get_access_token().empty()) + { + CURLWrapper::set_access_token(_instance.get_access_token()); + } } string Connection::endpoint_to_uri(const endpoint_variant &endpoint) const diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index 86d3073..d7e152f 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -166,6 +166,33 @@ answer_type CURLWrapper::make_request(const http_method &method, string uri, return answer; } +void CURLWrapper::set_access_token(const string_view access_token) +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + CURLcode code{curl_easy_setopt(_connection, CURLOPT_XOAUTH2_BEARER, + access_token.data())}; + if (code != CURLE_OK) + { + throw CURLException{code, "Could not set authorization token.", + _curl_buffer_error}; + } + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); + if (code == CURLE_NOT_BUILT_IN) // libcurl < 7.61.0. + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + } + if (code != CURLE_OK) + { + throw CURLException{code, "Could not set authorization token.", + _curl_buffer_error}; + } + + debuglog << "Set authorization token.\n"; +} + size_t CURLWrapper::writer_body(char *data, size_t size, size_t nmemb) { if(data == nullptr) From f7191d1f3ba841d768f2cc2f7794aab4388732cf Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 14:27:00 +0100 Subject: [PATCH 13/24] Add support for setting the access token. --- include/instance.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/instance.hpp b/include/instance.hpp index 5af177c..1aa76be 100644 --- a/include/instance.hpp +++ b/include/instance.hpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace mastodonpp { @@ -29,6 +30,7 @@ namespace mastodonpp using std::uint64_t; using std::string; using std::string_view; +using std::move; /*! * @brief Holds the access data of an instance. @@ -87,6 +89,19 @@ public: return _access_token; } + /*! + * @brief Set OAuth 2.0 Bearer Access Token. + * + * Sets also the access token for all Connection%s that are initialized + * with this Instance afterwards. + * + * @since 0.1.0 + */ + inline void set_access_token(string access_token) + { + _access_token = move(access_token); + } + /*! * @brief Returns the maximum number of characters per post. * @@ -113,6 +128,7 @@ public: * * @since 0.1.0 */ + [[nodiscard]] string_view get_proxy() const { return _proxy; From 108aeea771b350db33627139130f9bdaf90033c1 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 14:27:15 +0100 Subject: [PATCH 14/24] Update readme. --- README.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 3f6301d..37a157b 100644 --- a/README.adoc +++ b/README.adoc @@ -32,7 +32,7 @@ This is still a work in progress; here is a rough overview of the features: * [ ] Requests ** [x] `GET` requests. ** [x] Streaming `GET` requests. - ** [ ] `POST` requests. + ** [x] `POST` requests. ** [ ] `PATCH` requests. ** [ ] `PUT` requests. ** [ ] `DELETE` requests. @@ -84,7 +84,7 @@ link:{uri-reference}/examples.html[More examples] are included in the reference. * Tested OS: Linux * C++ compiler (tested: link:{uri-gcc}[GCC] 7/8/9, link:{uri-lang}[clang] 6/7) * link:{uri-cmake}[CMake] (at least: 3.9) -* link:{uri-libcurl}[libcurl] (at least: 7.32) +* link:{uri-libcurl}[libcurl] (at least: 7.56) * Optional ** Documentation: link:{uri-doxygen}[Doxygen] (tested: 1.8) ** Tests: link:{uri-catch}[Catch] (tested: 2.5 / 1.2) From cb7019a0fdc1ee861a5a2ab65d49845d7b194bb8 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 14:33:33 +0100 Subject: [PATCH 15/24] Allow HTTP POST without parameters. --- src/curl_wrapper.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index d7e152f..b4ba74e 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -101,10 +101,17 @@ answer_type CURLWrapper::make_request(const http_method &method, string uri, } case http_method::POST: { - curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; - - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); + if (parameters.empty()) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_POST, 1L); + } + else + { + curl_mime *mime{parameters_to_curl_mime(uri, parameters)}; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + code = curl_easy_setopt(_connection, CURLOPT_MIMEPOST, mime); + } break; } From da033c3ffc10f84240ca09effd1c1fe0d32ced9c Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 14:35:44 +0100 Subject: [PATCH 16/24] Silence clang-tidy warnings I can't do anything about. --- src/curl_wrapper.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index b4ba74e..18ab7a0 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -175,7 +175,7 @@ answer_type CURLWrapper::make_request(const http_method &method, string uri, void CURLWrapper::set_access_token(const string_view access_token) { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise) CURLcode code{curl_easy_setopt(_connection, CURLOPT_XOAUTH2_BEARER, access_token.data())}; if (code != CURLE_OK) @@ -184,11 +184,11 @@ void CURLWrapper::set_access_token(const string_view access_token) _curl_buffer_error}; } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise) code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); if (code == CURLE_NOT_BUILT_IN) // libcurl < 7.61.0. { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise) code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_ANY); } if (code != CURLE_OK) From c47a62969f1733ec32f503b335738f4e438ee7b3 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 16:18:59 +0100 Subject: [PATCH 17/24] Only use CURLAUTH_BEARER if libcurl supports it. --- src/curl_wrapper.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index 18ab7a0..0c9a3f6 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -23,7 +23,6 @@ #include #include #include -#include namespace mastodonpp { @@ -184,11 +183,14 @@ void CURLWrapper::set_access_token(const string_view access_token) _curl_buffer_error}; } - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise) - code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); - if (code == CURLE_NOT_BUILT_IN) // libcurl < 7.61.0. + if constexpr (LIBCURL_VERSION_NUM >= 0x073d00) // libcurl >= 7.61.0. { - // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise) + //NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-signed-bitwise) + code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); + } + else + { + //NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-signed-bitwise) code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_ANY); } if (code != CURLE_OK) From 20167fdce3e9412440131632ed06b84db0a976b5 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 17:15:23 +0100 Subject: [PATCH 18/24] Replace if constexpr with preprocessor if. --- src/curl_wrapper.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index 0c9a3f6..97894e0 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -183,16 +183,17 @@ void CURLWrapper::set_access_token(const string_view access_token) _curl_buffer_error}; } - if constexpr (LIBCURL_VERSION_NUM >= 0x073d00) // libcurl >= 7.61.0. + #if LIBCURL_VERSION_NUM >= 0x073d00 // libcurl >= 7.61.0. { //NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-signed-bitwise) code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); } - else + #else { //NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-signed-bitwise) code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_ANY); } + #endif if (code != CURLE_OK) { throw CURLException{code, "Could not set authorization token.", From f601e770ac2f74aef0fcead977ff0bd95ce57f99 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 17:43:50 +0100 Subject: [PATCH 19/24] Define CURLAUTH_BEARER as CURLAUTH_ANY with libcurl < 7.61.0 --- src/curl_wrapper.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index 97894e0..cf6adfe 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -183,17 +183,12 @@ void CURLWrapper::set_access_token(const string_view access_token) _curl_buffer_error}; } - #if LIBCURL_VERSION_NUM >= 0x073d00 // libcurl >= 7.61.0. - { - //NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-signed-bitwise) - code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); - } - #else - { - //NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-signed-bitwise) - code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - } - #endif +#if (LIBCURL_VERSION_NUM < 0x073d00) // libcurl < 7.61.0. +#define CURLAUTH_BEARER CURLAUTH_ANY +#endif + + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-signed-bitwise) + code = curl_easy_setopt(_connection, CURLOPT_HTTPAUTH, CURLAUTH_BEARER); if (code != CURLE_OK) { throw CURLException{code, "Could not set authorization token.", From 796e159cdc628631cd19eacfb1796fd117105c65 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 18:50:53 +0100 Subject: [PATCH 20/24] Actually set the proxy to the string in CURLWrappper::set_proxy(). --- src/curl_wrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index cf6adfe..5af9298 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -67,7 +67,7 @@ CURLWrapper::~CURLWrapper() noexcept void CURLWrapper::set_proxy(const string_view proxy) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) - CURLcode code{curl_easy_setopt(_connection, CURLOPT_PROXY, proxy)}; + CURLcode code{curl_easy_setopt(_connection, CURLOPT_PROXY, proxy.data())}; if (code != CURLE_OK) { throw CURLException{code, "Failed to set proxy", _curl_buffer_error}; From 7ac1bc13775c457182df714bfb1d0b83100cd9e7 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 18:57:42 +0100 Subject: [PATCH 21/24] Hide CURLWrapper::cancel_stream(). --- include/connection.hpp | 6 ++++++ include/curl_wrapper.hpp | 25 ++++++++++++++----------- src/curl_wrapper.cpp | 5 ----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/include/connection.hpp b/include/connection.hpp index 91a06d7..0ed09eb 100644 --- a/include/connection.hpp +++ b/include/connection.hpp @@ -181,10 +181,16 @@ public: */ vector get_new_events(); + //! @copydoc CURLWrapper::cancel_stream + inline void cancel_stream() + { + CURLWrapper::cancel_stream(); + } private: Instance &_instance; const string_view _baseuri; + [[nodiscard]] string endpoint_to_uri(const endpoint_variant &endpoint) const; }; diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index 73c748a..967295f 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -152,17 +152,6 @@ public: */ void set_proxy(string_view proxy); - /*! - * @brief Cancel the stream. - * - * The stream will be cancelled, usually whithin a second. The @link - * answer_type::curl_error_code curl_error_code @endlink of the answer will - * be set to 42 (`CURLE_ABORTED_BY_CALLBACK`). - * - * @since 0.1.0 - */ - void cancel_stream(); - protected: /*! * @brief Mutex for #get_buffer a.k.a. _curl_buffer_body. @@ -199,6 +188,20 @@ protected: return _curl_buffer_body; } + /*! + * @brief Cancel the stream. + * + * The stream will be cancelled, usually whithin a second. The @link + * answer_type::curl_error_code curl_error_code @endlink of the answer will + * be set to 42 (`CURLE_ABORTED_BY_CALLBACK`). + * + * @since 0.1.0 + */ + inline void cancel_stream() + { + _stream_cancelled = true; + } + /*! * @brief Set OAuth 2.0 Bearer Access Token. * diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index 5af9298..91fb39e 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -74,11 +74,6 @@ void CURLWrapper::set_proxy(const string_view proxy) } } -void CURLWrapper::cancel_stream() -{ - _stream_cancelled = true; -} - answer_type CURLWrapper::make_request(const http_method &method, string uri, const parametermap ¶meters) { From fbcded6e1ebb5f2ffffa012588fe31c4368f9e91 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 19:03:35 +0100 Subject: [PATCH 22/24] Simplified buffer_mutex documentation. --- include/curl_wrapper.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index 967295f..97df5b7 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -156,9 +156,8 @@ protected: /*! * @brief Mutex for #get_buffer a.k.a. _curl_buffer_body. * - * This mutex is locked in `writer_body()` and - * Connection::get_new_stream_contents before anything is read or written - * from/to _curl_buffer_body. + * This mutex is locked before anything is read or written from/to + * _curl_buffer_body. * * @since 0.1.0 */ From 67bded42feb2518eb0f40a1b29893e46fafe76d7 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 19:04:18 +0100 Subject: [PATCH 23/24] Add tests for Instance. --- ...st_instanciation.cpp => test_instance.cpp} | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) rename tests/{test_instanciation.cpp => test_instance.cpp} (60%) diff --git a/tests/test_instanciation.cpp b/tests/test_instance.cpp similarity index 60% rename from tests/test_instanciation.cpp rename to tests/test_instance.cpp index bde806a..8e38275 100644 --- a/tests/test_instanciation.cpp +++ b/tests/test_instance.cpp @@ -15,7 +15,6 @@ */ #include "instance.hpp" -#include "connection.hpp" #include @@ -27,7 +26,7 @@ namespace mastodonpp using std::string; -SCENARIO ("Instantiations.") +SCENARIO ("mastopp::Instance") { bool exception = false; @@ -35,7 +34,7 @@ SCENARIO ("Instantiations.") { try { - Instance instance{"example.com", ""}; + Instance instance{"example.com", {}}; } catch (const std::exception &e) { @@ -48,12 +47,17 @@ SCENARIO ("Instantiations.") } } - WHEN ("Connection is instantiated.") + WHEN ("Variables are set.") { + constexpr auto hostname{"likeable.space"}; + constexpr auto proxy{"socks4a://[::1]:9050"}; + constexpr auto access_token{"abc123"}; + Instance instance{hostname, {}}; + try { - Instance instance{"example.com", ""}; - Connection connection{instance}; + instance.set_proxy(proxy); + instance.set_access_token(access_token); } catch (const std::exception &e) { @@ -61,8 +65,16 @@ SCENARIO ("Instantiations.") } THEN ("No exception is thrown") + AND_THEN ("get_proxy() returns the set value.") + AND_THEN ("get_access_token() returns the set value.") + AND_THEN ("get_hostname() returns the set value.") + AND_THEN ("get_baseuri() returns the expected value.") { REQUIRE_FALSE(exception); + REQUIRE(instance.get_proxy() == proxy); + REQUIRE(instance.get_access_token() == access_token); + REQUIRE(instance.get_hostname() == hostname); + REQUIRE(instance.get_baseuri() == (string("https://") += hostname)); } } } From 628130e21ae65b854fb844d0080bbd59ee0721c6 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 10 Jan 2020 19:09:25 +0100 Subject: [PATCH 24/24] Add test for Connection. --- tests/test_connection.cpp | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/test_connection.cpp diff --git a/tests/test_connection.cpp b/tests/test_connection.cpp new file mode 100644 index 0000000..db8f223 --- /dev/null +++ b/tests/test_connection.cpp @@ -0,0 +1,51 @@ +/* This file is part of mastodonpp. + * Copyright © 2020 tastytea + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "instance.hpp" +#include "connection.hpp" + +#include + +#include + +namespace mastodonpp +{ + + +SCENARIO ("mastodonpp::Connection.") +{ + bool exception = false; + + WHEN ("Connection is instantiated.") + { + try + { + Instance instance{"example.com", {}}; + Connection connection{instance}; + } + catch (const std::exception &e) + { + exception = true; + } + + THEN ("No exception is thrown") + { + REQUIRE_FALSE(exception); + } + } +} + +} // namespace mastodonpp