From 159cd05f5a1651cc3e9b2ac830f251828b8c6cd7 Mon Sep 17 00:00:00 2001 From: tastytea Date: Sun, 5 Jan 2020 19:00:24 +0100 Subject: [PATCH 01/11] Make _endpoint_map static. --- include/api.hpp | 10 +++++----- include/connection.hpp | 1 - src/api.cpp | 10 ++++++---- src/connection.cpp | 3 +-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/api.hpp b/include/api.hpp index 00a6dc3..e508fa1 100644 --- a/include/api.hpp +++ b/include/api.hpp @@ -302,7 +302,7 @@ public: * * @since 0.1.0 */ - explicit API(); + explicit API(const endpoint_type &endpoint); /*! * @brief Convert #endpoint_type to `std::string_view`. @@ -310,14 +310,14 @@ public: * @since 0.1.0 */ [[nodiscard]] - inline string_view endpoint_to_string_view(const endpoint_type &endpoint) - const + inline string_view to_string_view() const { - return _endpoint_map.at(endpoint).data(); + return _endpoint_map.at(_endpoint).data(); } private: - const map _endpoint_map; + const endpoint_type _endpoint; + static const map _endpoint_map; }; } // namespace mastodonpp diff --git a/include/connection.hpp b/include/connection.hpp index 1a6c11f..a26120c 100644 --- a/include/connection.hpp +++ b/include/connection.hpp @@ -74,7 +74,6 @@ public: private: Instance &_instance; const string_view _baseuri; - const API _api; }; } // namespace mastodonpp diff --git a/src/api.cpp b/src/api.cpp index 98a1d7f..fd7eede 100644 --- a/src/api.cpp +++ b/src/api.cpp @@ -19,8 +19,11 @@ namespace mastodonpp { -API::API() - : _endpoint_map +API::API(const endpoint_type &endpoint) + : _endpoint{endpoint} +{} + +const map API::_endpoint_map { {v1::apps, "/api/v1/apps"}, {v1::apps_verify_credentials, "/api/v1/apps/verify/credentials"}, @@ -238,7 +241,6 @@ API::API() "/api/pleroma/pleroma/notification_settings"}, {pleroma::pleroma_healthcheck, "/api/pleroma/pleroma/healthcheck"}, {pleroma::pleroma_change_email, "/api/pleroma/pleroma/change_email"}, -} -{} +}; } // namespace mastodonpp diff --git a/src/connection.cpp b/src/connection.cpp index cc956e0..e6bca95 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -22,14 +22,13 @@ namespace mastodonpp Connection::Connection(Instance &instance) : _instance{instance} , _baseuri{instance.get_baseuri()} - , _api{} {} answer_type Connection::get(const API::endpoint_type &endpoint) { return make_request( http_method::GET, - string(_baseuri).append(_api.endpoint_to_string_view(endpoint))); + string(_baseuri).append(API{endpoint}.to_string_view())); } answer_type Connection::get(const string_view &endpoint) From be2f00faaefa254bb069c4fac40e2fe59d851bb0 Mon Sep 17 00:00:00 2001 From: tastytea Date: Sun, 5 Jan 2020 19:07:07 +0100 Subject: [PATCH 02/11] Do global curl init / cleanup only once. --- include/curl_wrapper.hpp | 5 ++++- src/curl_wrapper.cpp | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index 07603b2..d8c3473 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -30,6 +30,8 @@ namespace mastodonpp using std::string; using std::string_view; +extern uint16_t curl_inits; + /*! * @brief The HTTP method. * @@ -59,7 +61,8 @@ public: /*! * @brief Initializes curl and sets up connection. * - * Calls `curl_global_init`, which is not thread-safe. For more information + * The first time an instance of CURLWrapper is created, it calls + * `curl_global_init`, which is not thread-safe. For more information * consult [curl_global_init(3)] * (https://curl.haxx.se/libcurl/c/curl_global_init.html). * diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index 130a27f..d4d44ba 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -26,17 +26,31 @@ namespace mastodonpp using std::uint8_t; using std::uint16_t; +uint16_t curlwrapper_instances{0}; + CURLWrapper::CURLWrapper() : _curl_buffer_error{} { - curl_global_init(CURL_GLOBAL_ALL); // NOLINT(hicpp-signed-bitwise) + if (curlwrapper_instances == 0) + { + curl_global_init(CURL_GLOBAL_ALL); // NOLINT(hicpp-signed-bitwise) + } + ++curlwrapper_instances; + debuglog << "CURLWrapper instances: " << curlwrapper_instances << " (+1)\n"; + _connection = curl_easy_init(); setup_curl(); } CURLWrapper::~CURLWrapper() noexcept { curl_easy_cleanup(_connection); - curl_global_cleanup(); + + --curlwrapper_instances; + debuglog << "CURLWrapper instances: " << curlwrapper_instances << " (-1)\n"; + if (curlwrapper_instances == 0) + { + curl_global_cleanup(); + } } answer_type CURLWrapper::make_request(const http_method &method, From b8802d36747280afd33a428afa758972c662ae29 Mon Sep 17 00:00:00 2001 From: tastytea Date: Sun, 5 Jan 2020 20:10:54 +0100 Subject: [PATCH 03/11] Make assignment to _max_chars clearer. --- src/instance.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/instance.cpp b/src/instance.cpp index fc56aa8..5d585d9 100644 --- a/src/instance.cpp +++ b/src/instance.cpp @@ -23,6 +23,7 @@ namespace mastodonpp { +using std::stoull; using std::move; Instance::Instance(string hostname, string access_token) @@ -38,19 +39,22 @@ Instance::Instance(string hostname, string access_token) if (answer) { debuglog << "Querying instance for max_toot_chars…\n"; - auto &body{answer.body}; - size_t pos_start{body.find("max_toot_chars")}; - if (pos_start == string::npos) + _max_chars = [&answer] { - debuglog << "max_toot_chars not found."; - return; - } - pos_start = body.find(':', pos_start) + 1; - const size_t pos_end{body.find(',', pos_start)}; + auto &body{answer.body}; + size_t pos_start{body.find("max_toot_chars")}; + if (pos_start == string::npos) + { + debuglog << "max_toot_chars not found.\n"; + return static_cast(500); + } + pos_start = body.find(':', pos_start) + 1; + const size_t pos_end{body.find(',', pos_start)}; - const auto max_toot_chars{body.substr(pos_start, - pos_end - pos_start)}; - _max_chars = std::stoull(max_toot_chars); + const auto max_toot_chars{body.substr(pos_start, + pos_end - pos_start)}; + return static_cast(stoull(max_toot_chars)); + }(); debuglog << "Set _max_chars to: " << _max_chars << '\n'; } } From b93fe57caec7d87764d60dc36b69e8f7aa01bcaa Mon Sep 17 00:00:00 2001 From: tastytea Date: Sun, 5 Jan 2020 20:11:42 +0100 Subject: [PATCH 04/11] Show filename in debuglog instead of function. --- src/log.hpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/log.hpp b/src/log.hpp index 5bdcc96..43d4fc4 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -18,14 +18,30 @@ #define MASTODONPP_LOG_HPP #include +#include namespace mastodonpp { using std::cerr; +using std::string_view; + +constexpr auto shorten_filename(const string_view &filename) +{ + for (const string_view &dir : {"/src/", "/include/"}) + { + auto pos{filename.rfind("/src/")}; + if (pos != string_view::npos) + { + return filename.substr(pos + dir.size()); + } + } + return filename; +} #ifndef NDEBUG - #define debuglog cerr << "[" << __func__ << "():" << __LINE__ << "] DEBUG: " +#define debuglog cerr << "[" << shorten_filename(__FILE__) \ + << ':' << __LINE__ << "] DEBUG: " #else #define debuglog false && cerr #endif From 3c6c56d97335d3ccc3cc8d01b3741c248662a598 Mon Sep 17 00:00:00 2001 From: tastytea Date: Sun, 5 Jan 2020 20:26:47 +0100 Subject: [PATCH 05/11] Make curlwrapper_instances static. --- include/curl_wrapper.hpp | 2 -- src/curl_wrapper.cpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp index d8c3473..90e010a 100644 --- a/include/curl_wrapper.hpp +++ b/include/curl_wrapper.hpp @@ -30,8 +30,6 @@ namespace mastodonpp using std::string; using std::string_view; -extern uint16_t curl_inits; - /*! * @brief The HTTP method. * diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp index d4d44ba..b4b9dde 100644 --- a/src/curl_wrapper.cpp +++ b/src/curl_wrapper.cpp @@ -26,7 +26,7 @@ namespace mastodonpp using std::uint8_t; using std::uint16_t; -uint16_t curlwrapper_instances{0}; +static uint16_t curlwrapper_instances{0}; CURLWrapper::CURLWrapper() : _curl_buffer_error{} From cb7dee1ab724bc9f5b8bdb311dbde08268a497a9 Mon Sep 17 00:00:00 2001 From: tastytea Date: Sun, 5 Jan 2020 20:39:05 +0100 Subject: [PATCH 06/11] Add errorlog. --- src/log.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/log.hpp b/src/log.hpp index 43d4fc4..ab8c033 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -39,12 +39,14 @@ constexpr auto shorten_filename(const string_view &filename) return filename; } +#define commonlog cerr << '[' << shorten_filename(__FILE__) \ + << ':' << __LINE__ << ']' #ifndef NDEBUG -#define debuglog cerr << "[" << shorten_filename(__FILE__) \ - << ':' << __LINE__ << "] DEBUG: " +#define debuglog commonlog << " DEBUG: " #else #define debuglog false && cerr #endif +#define errorlog commonlog << " ERROR: " } // namespace mastodonpp From a6bd0812809638b28445139e437885901b0666cf Mon Sep 17 00:00:00 2001 From: tastytea Date: Sun, 5 Jan 2020 20:47:34 +0100 Subject: [PATCH 07/11] Refactored constructor of Instance. --- src/instance.cpp | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/instance.cpp b/src/instance.cpp index 5d585d9..15783ad 100644 --- a/src/instance.cpp +++ b/src/instance.cpp @@ -34,29 +34,31 @@ Instance::Instance(string hostname, string access_token) { try { + debuglog << "Querying " << _hostname << " for max_toot_chars…\n"; const auto answer{make_request(http_method::GET, _baseuri + "/api/v1/instance")}; - if (answer) + if (!answer) { - debuglog << "Querying instance for max_toot_chars…\n"; - _max_chars = [&answer] - { - auto &body{answer.body}; - size_t pos_start{body.find("max_toot_chars")}; - if (pos_start == string::npos) - { - debuglog << "max_toot_chars not found.\n"; - return static_cast(500); - } - pos_start = body.find(':', pos_start) + 1; - const size_t pos_end{body.find(',', pos_start)}; - - const auto max_toot_chars{body.substr(pos_start, - pos_end - pos_start)}; - return static_cast(stoull(max_toot_chars)); - }(); - debuglog << "Set _max_chars to: " << _max_chars << '\n'; + return; } + + _max_chars = [&answer] + { + auto &body{answer.body}; + size_t pos_start{body.find("max_toot_chars")}; + if (pos_start == string::npos) + { + debuglog << "max_toot_chars not found.\n"; + return static_cast(500); + } + pos_start = body.find(':', pos_start) + 1; + const size_t pos_end{body.find(',', pos_start)}; + + const auto max_toot_chars{body.substr(pos_start, + pos_end - pos_start)}; + return static_cast(stoull(max_toot_chars)); + }(); + debuglog << "Set _max_chars to: " << _max_chars << '\n'; } catch (const std::exception &e) { From 6663cca653496eddb714aa0a32b134e93e74eb62 Mon Sep 17 00:00:00 2001 From: tastytea Date: Mon, 6 Jan 2020 09:11:32 +0100 Subject: [PATCH 08/11] Change signature of Instance. --- include/instance.hpp | 3 ++- src/instance.cpp | 9 +++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/include/instance.hpp b/include/instance.hpp index f80e201..17425cd 100644 --- a/include/instance.hpp +++ b/include/instance.hpp @@ -50,7 +50,8 @@ public: * * @since 0.1.0 */ - explicit Instance(string hostname, string access_token); + explicit Instance(const string_view &hostname, + const string_view &access_token); /*! * @brief Returns the hostname. diff --git a/src/instance.cpp b/src/instance.cpp index 15783ad..c765345 100644 --- a/src/instance.cpp +++ b/src/instance.cpp @@ -18,18 +18,15 @@ #include "log.hpp" #include "return_types.hpp" -#include - namespace mastodonpp { using std::stoull; -using std::move; -Instance::Instance(string hostname, string access_token) - : _hostname{move(hostname)} +Instance::Instance(const string_view &hostname, const string_view &access_token) + : _hostname{hostname} , _baseuri{"https://" + _hostname} - , _access_token{move(access_token)} + , _access_token{access_token} , _max_chars{500} { try From 0638f8fe9a8a1f0cd86e647d8452c9369ebc41de Mon Sep 17 00:00:00 2001 From: tastytea Date: Mon, 6 Jan 2020 09:31:05 +0100 Subject: [PATCH 09/11] Query max_toot_chars the first time get_max_chars() is called. --- include/instance.hpp | 5 +--- src/instance.cpp | 65 ++++++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/include/instance.hpp b/include/instance.hpp index 17425cd..914a470 100644 --- a/include/instance.hpp +++ b/include/instance.hpp @@ -94,10 +94,7 @@ public: * @since 0.1.0 */ [[nodiscard]] - inline uint64_t get_max_chars() const - { - return _max_chars; - } + uint64_t get_max_chars(); private: const string _hostname; diff --git a/src/instance.cpp b/src/instance.cpp index c765345..b367f9b 100644 --- a/src/instance.cpp +++ b/src/instance.cpp @@ -27,40 +27,51 @@ Instance::Instance(const string_view &hostname, const string_view &access_token) : _hostname{hostname} , _baseuri{"https://" + _hostname} , _access_token{access_token} - , _max_chars{500} + , _max_chars{0} +{} + +uint64_t Instance::get_max_chars() { - try - { - debuglog << "Querying " << _hostname << " for max_toot_chars…\n"; - const auto answer{make_request(http_method::GET, - _baseuri + "/api/v1/instance")}; - if (!answer) - { - return; - } + constexpr uint64_t default_max_chars{500}; - _max_chars = [&answer] + if (_max_chars == 0) + { + try { - auto &body{answer.body}; - size_t pos_start{body.find("max_toot_chars")}; - if (pos_start == string::npos) + debuglog << "Querying " << _hostname << " for max_toot_chars…\n"; + const auto answer{make_request(http_method::GET, + _baseuri + "/api/v1/instance")}; + if (!answer) { - debuglog << "max_toot_chars not found.\n"; - return static_cast(500); + debuglog << "Could not get instance info.\n"; + return default_max_chars; } - pos_start = body.find(':', pos_start) + 1; - const size_t pos_end{body.find(',', pos_start)}; - const auto max_toot_chars{body.substr(pos_start, - pos_end - pos_start)}; - return static_cast(stoull(max_toot_chars)); - }(); - debuglog << "Set _max_chars to: " << _max_chars << '\n'; - } - catch (const std::exception &e) - { - debuglog << "Unexpected exception: " << e.what() << '\n'; + _max_chars = [&answer] + { + auto &body{answer.body}; + size_t pos_start{body.find("max_toot_chars")}; + if (pos_start == string::npos) + { + debuglog << "max_toot_chars not found.\n"; + return default_max_chars; + } + pos_start = body.find(':', pos_start) + 1; + const size_t pos_end{body.find(',', pos_start)}; + + const auto max_toot_chars{body.substr(pos_start, + pos_end - pos_start)}; + return static_cast(stoull(max_toot_chars)); + }(); + debuglog << "Set _max_chars to: " << _max_chars << '\n'; + } + catch (const std::exception &e) + { + debuglog << "Unexpected exception: " << e.what() << '\n'; + } } + + return _max_chars; } } // namespace mastodonpp From 7563d083c0ec0828f05f897b8dd0aa4f8ebaeba9 Mon Sep 17 00:00:00 2001 From: tastytea Date: Mon, 6 Jan 2020 09:42:51 +0100 Subject: [PATCH 10/11] Improve exception documentation. --- include/mastodonpp.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/mastodonpp.hpp b/include/mastodonpp.hpp index 9cecc53..70d9bdc 100644 --- a/include/mastodonpp.hpp +++ b/include/mastodonpp.hpp @@ -70,7 +70,8 @@ * @section exceptions Exceptions * * Any unrecoverable libcurl error will be thrown as a - * mastodonpp::CURLException. + * mastodonpp::CURLException. Network errors will **not** be thrown, but + * reported via the return value. */ /*! From 5d6ad60d3bfefe42e102b52fecebd9c2fb873350 Mon Sep 17 00:00:00 2001 From: tastytea Date: Mon, 6 Jan 2020 09:43:08 +0100 Subject: [PATCH 11/11] Add simple example. --- examples/example01_instance_info.cpp | 83 ++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 examples/example01_instance_info.cpp diff --git a/examples/example01_instance_info.cpp b/examples/example01_instance_info.cpp new file mode 100644 index 0000000..6d371f3 --- /dev/null +++ b/examples/example01_instance_info.cpp @@ -0,0 +1,83 @@ +/* This file is part of mastodonpp. + * Copyright © 2020 tastytea + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +// Print information about an instance (/api/v1/instance). + +#include + +#include +#include +#include +#include + +namespace masto = mastodonpp; +using std::cout; +using std::cerr; +using std::endl; +using std::to_string; +using std::string_view; +using std::vector; + +int main(int argc, char *argv[]) +{ + const vector args(argv, argv + argc); + if (args.size() <= 1) + { + cerr << "Usage: " << args[0] << " \n"; + return 1; + } + + try + { + // Initialize an Instance. + masto::Instance instance{args[1], {}}; + + // Get maximum allowed characters per post. + const auto max_chars{instance.get_max_chars()}; + cout << "Maximum characters per post: " << max_chars << "\n\n"; + + // Initialize a Connection. + masto::Connection connection{instance}; + + // Get information about the instance. + masto::answer_type answer{connection.get(masto::API::v1::instance)}; + if (answer) + { + cout << answer << endl; + } + else + { + if (answer.curl_error_code == 0) + { + // If it is no libcurl error, it must be an HTTP error. + cerr << "HTTP status: " << answer.http_status << endl; + } + else + { + // Network errors like “Couldn't resolve host.”. + cerr << "libcurl error " << to_string(answer.curl_error_code) + << ": " << answer.error_message << endl; + } + } + } + catch (const masto::CURLException &e) + { + // Only libcurl errors that are not network errors will be thrown. + // There went probably something wrong with the initialization. + cerr << e.what() << endl; + } + + return 0; +}