From bde8d1170632d9f31f8c8f721330b12d3f872b93 Mon Sep 17 00:00:00 2001 From: tastytea Date: Wed, 10 Apr 2019 02:25:55 +0200 Subject: [PATCH] Revamped streams. --- CMakeLists.txt | 6 +- examples/example02_stream.cpp | 107 ++++++++++++++++++++++++++++++++++ src/api/get_stream.cpp | 24 ++++---- src/easy/easy.cpp | 2 +- src/easy/types_easy.hpp | 6 +- src/http.cpp | 38 +++++++++++- src/mastodon-cpp.hpp | 47 ++++++++++----- 7 files changed, 199 insertions(+), 31 deletions(-) create mode 100644 examples/example02_stream.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e2f0999..d494dce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,9 +74,11 @@ set_target_properties(mastodon-cpp PROPERTIES ) if(WITH_EASY) - target_link_libraries(mastodon-cpp ${CURLPP_LIBRARIES} ${JSONCPP_LIBRARIES}) + target_link_libraries(mastodon-cpp + ${CURLPP_LIBRARIES} pthread ${JSONCPP_LIBRARIES}) else() - target_link_libraries(mastodon-cpp ${CURLPP_LIBRARIES}) + target_link_libraries(mastodon-cpp + ${CURLPP_LIBRARIES} pthread) endif() # Compile examples diff --git a/examples/example02_stream.cpp b/examples/example02_stream.cpp new file mode 100644 index 0000000..3ada2d1 --- /dev/null +++ b/examples/example02_stream.cpp @@ -0,0 +1,107 @@ +/* This file is part of mastodon-cpp. + */ + +// Don't compile this if the Easy-interface is turned off +#ifndef WITHOUT_EASY + +#include +#include +#include +#include +#include +#include +#ifdef MASTODON_CPP + #include "mastodon-cpp.hpp" + #include "easy/all.hpp" +#else + #include + #include +#endif + +using std::cout; +using std::cerr; +using std::endl; +using std::string; +using std::uint8_t; +using namespace Mastodon; + +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + std::cerr << "usage: " << argv[0] << " \n"; + return 1; + } + + // Construct a Mastodon::Easy object without authorization token. + Easy::API masto(argv[1], ""); + // Prepare a pointer to the http object, to cancel the stream later. + std::unique_ptr ptr; + // This variable is filled with the stream data. + string stream; + + // Get the public timeline, the pointer is set here. The error detection is + // not very reliable at the moment, don't count on it. + uint8_t ret = masto.get_stream(API::v1::streaming_public, ptr, stream); + cout << "Return code: " << std::to_string(ret) << endl; + + // Listen to the stream for 120 seconds. + for (uint8_t counter = 0; counter < 120; ++counter) + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Acquire lock for the stream variable to avoid simultaneous access. + std::lock_guard lock(ptr->get_mutex()); + // Parse event stream into a vector. + std::vector events = Easy::parse_stream(stream); + // Clear the stream buffer. + stream.clear(); + + for (const Easy::stream_event &event : events) + { + // Print out some information about the events. + switch (event.type) + { + case Easy::event_type::Update: + { + Easy::Status status(event.data); + cout << "[" << status.created_at().strtime("%T") << "] "; + cout << "Status from: " << status.account().acct() + << " (" << status.url() << ")\n"; + break; + } + case Easy::event_type::Notification: + { + Easy::Notification notification(event.data); + cout << "Notification involving: " + << notification.account().acct() + << " (" << notification.id() << ")\n"; + break; + } + case Easy::event_type::Delete: + { + cout << "Deleted: " << event.data << '\n'; + break; + } + default: + { + cout << "Something undefined happened. 😱\n"; + } + } + } + } + + // Close connection. + ptr->cancel_stream(); + + return 0; +} + +#else +#include +int main() +{ + std::printf("mastodon-cpp was compiled without Easy support.\n"); + return 255; +} +#endif // WITHOUT_EASY diff --git a/src/api/get_stream.cpp b/src/api/get_stream.cpp index 73f2a63..8d93026 100644 --- a/src/api/get_stream.cpp +++ b/src/api/get_stream.cpp @@ -21,9 +21,10 @@ using namespace Mastodon; using std::cerr; -return_call API::get_stream(const Mastodon::API::v1 &call, - const parametermap ¶meters, - std::unique_ptr &ptr) +uint8_t API::get_stream(const Mastodon::API::v1 &call, + const parametermap ¶meters, + std::unique_ptr &ptr, + string &stream) { string strcall = ""; @@ -46,7 +47,7 @@ return_call API::get_stream(const Mastodon::API::v1 &call, break; default: ttdebug << "ERROR: Invalid call.\n"; - return { 22, "Invalid argument", 0, "" }; + return 22; break; } @@ -55,18 +56,19 @@ return_call API::get_stream(const Mastodon::API::v1 &call, strcall += maptostr(parameters); } - return get_stream(strcall, ptr); + return get_stream(strcall, ptr, stream); } -return_call API::get_stream(const Mastodon::API::v1 &call, - std::unique_ptr &ptr) +uint8_t API::get_stream(const Mastodon::API::v1 &call, + std::unique_ptr &ptr, + string &stream) { - parametermap p = {}; - return get_stream(call, p, ptr); + return get_stream(call, {}, ptr, stream); } -return_call API::get_stream(const std::string &call, std::unique_ptr &ptr) +uint8_t API::get_stream(const std::string &call, std::unique_ptr &ptr, + string &stream) { ptr = std::make_unique(*this, _instance, _access_token); - return ptr->request(http_method::GET_STREAM, call); + return ptr->request_stream(call, stream); } diff --git a/src/easy/easy.cpp b/src/easy/easy.cpp index 828115c..c992ee5 100644 --- a/src/easy/easy.cpp +++ b/src/easy/easy.cpp @@ -70,7 +70,7 @@ const vector Easy::parse_stream( else if (event.compare("delete") == 0) type = event_type::Delete; - vec.push_back(stream_event(type, data)); + vec.push_back({ type, data }); stream = match.suffix().str(); } diff --git a/src/easy/types_easy.hpp b/src/easy/types_easy.hpp index 92de9c2..bca46a3 100644 --- a/src/easy/types_easy.hpp +++ b/src/easy/types_easy.hpp @@ -103,7 +103,11 @@ namespace Easy * * @since before 0.11.0 */ - typedef std::pair stream_event; + typedef struct stream_event + { + event_type type = event_type::Undefined; + string data; + } stream_event; /*! * @brief Map of 'notification type' and 'push is requested or not'. diff --git a/src/http.cpp b/src/http.cpp index 33c2236..dc934c5 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -19,6 +19,7 @@ #include #include // std::strncmp #include +#include #include #include #include @@ -49,13 +50,43 @@ return_call API::http::request(const http_method &meth, const string &path) return request(meth, path, curlpp::Forms()); } -return_call API::http::request(const http_method &meth, - const string &path, +return_call API::http::request(const http_method &meth, const string &path, const curlpp::Forms &formdata) +{ + string answer; + return request_common(meth, path, formdata, answer); +} + +uint8_t API::http::request_stream(const string &path, string &stream) +{ + static return_call ret; + _streamthread = std::thread( + [&] + { + ret = request_common(http_method::GET_STREAM, path, + curlpp::Forms(), stream); + }); + + // FIXME: Build reliable error detection. + std::this_thread::sleep_for(std::chrono::seconds(1)); + if (!ret) + { + cancel_stream(); + return ret.error_code; + } + else + { + return 0; + } +} + +return_call API::http::request_common(const http_method &meth, + const string &path, + const curlpp::Forms &formdata, + string &answer) { using namespace std::placeholders; // _1, _2, _3 - string answer; ttdebug << "Path is: " << path << '\n'; try @@ -220,6 +251,7 @@ double API::http::callback_progress(double /* dltotal */, double /* dlnow */, void API::http::cancel_stream() { _cancel_stream = true; + _streamthread.join(); } std::mutex &API::http::get_mutex() diff --git a/src/mastodon-cpp.hpp b/src/mastodon-cpp.hpp index 59600ec..3f717a7 100644 --- a/src/mastodon-cpp.hpp +++ b/src/mastodon-cpp.hpp @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include @@ -36,6 +38,7 @@ #endif using std::string; +using std::uint8_t; /*! * @example example01_get_public_timeline.cpp @@ -92,8 +95,8 @@ namespace Mastodon /*! * @brief HTTP Request. * - * @param meth A method defined in http::method - * @param path The api call as string + * @param meth A method defined in http::method. + * @param path The API call as string. * @param formdata The form data for PATCH and POST requests. * * @return @ref error "Error code". If the URL has permanently @@ -105,6 +108,16 @@ namespace Mastodon const string &path, const curlpp::Forms &formdata); + /*! + * @brief HTTP Request for streams. + * + * @param path The API call as string. + * @param stream The stream of data that is returned. + * + * @since 0.100.0 + */ + uint8_t request_stream(const string &path, string &stream); + /*! * @brief Get all headers in a string */ @@ -140,7 +153,12 @@ namespace Mastodon string _headers; bool _cancel_stream; std::mutex _mutex; + std::thread _streamthread; + return_call request_common(const http_method &meth, + const string &path, + const curlpp::Forms &formdata, + string &answer); size_t callback_write(char* data, size_t size, size_t nmemb, string *oss); double callback_progress(double /* dltotal */, double /* dlnow */, @@ -467,7 +485,7 @@ namespace Mastodon * * @param call A call defined in Mastodon::API::v1 * - * @return @ref error "Error code". + * @return return_call * * @since 0.100.0 */ @@ -480,7 +498,7 @@ namespace Mastodon * @param parameters A Mastodon::parametermap containing * parameters * - * @return @ref error "Error code". + * @return return_call */ const return_call get(const Mastodon::API::v1 &call, const parametermap ¶meters); @@ -494,7 +512,7 @@ namespace Mastodon * @param parameters A Mastodon::parametermap containing * parameters * - * @return @ref error "Error code". + * @return return_call */ const return_call get(const Mastodon::API::v2 &call, const parametermap ¶meters); @@ -504,7 +522,7 @@ namespace Mastodon * * @param call String in the form `/api/v1/example` * - * @return @ref error "Error code". + * @return return_call * * @since 0.100.0 */ @@ -523,9 +541,10 @@ namespace Mastodon * * @since 0.100.0 */ - return_call get_stream(const Mastodon::API::v1 &call, - const parametermap ¶meters, - std::unique_ptr &ptr); + uint8_t get_stream(const Mastodon::API::v1 &call, + const parametermap ¶meters, + std::unique_ptr &ptr, + string &stream); /*! * @brief Make a streaming GET request. @@ -538,8 +557,9 @@ namespace Mastodon * * @since 0.100.0 */ - return_call get_stream(const Mastodon::API::v1 &call, - std::unique_ptr &ptr); + uint8_t get_stream(const Mastodon::API::v1 &call, + std::unique_ptr &ptr, + string &stream); /*! * @brief Make a streaming GET request. @@ -552,8 +572,9 @@ namespace Mastodon * * @since 0.100.0 */ - return_call get_stream(const string &call, - std::unique_ptr &ptr); + uint8_t get_stream(const string &call, + std::unique_ptr &ptr, + string &stream); /*! * @brief Make a PATCH request.