summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortastytea2020-01-08 21:27:27 +0100
committertastytea2020-01-08 21:27:27 +0100
commitd2de78ff9e0ccfc39df8c7bdf2491773342eb11e (patch)
tree3eeab2f78d6ef6f645e59dc8f925cdd57d9073f7
parenta724006854f7364dbf305f84ee440d9664bb6307 (diff)
downloadmastodonpp-d2de78ff9e0ccfc39df8c7bdf2491773342eb11e.tar
mastodonpp-d2de78ff9e0ccfc39df8c7bdf2491773342eb11e.tar.gz
mastodonpp-d2de78ff9e0ccfc39df8c7bdf2491773342eb11e.zip
Add streaming support.
-rw-r--r--README.adoc2
-rw-r--r--include/connection.hpp7
-rw-r--r--include/curl_wrapper.hpp63
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/connection.cpp10
-rw-r--r--src/curl_wrapper.cpp40
6 files changed, 118 insertions, 6 deletions
diff --git a/README.adoc b/README.adoc
index 7b40e52..1177d2e 100644
--- a/README.adoc
+++ b/README.adoc
@@ -42,7 +42,7 @@ Have a look at the link:{uri-reference}[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-curl}[curl] (tested: 7.66 / 7.58)
+* link:{uri-curl}[curl] (at least: 7.32)
* Optional
** Documentation: link:{uri-doxygen}[Doxygen] (tested: 1.8)
** Tests: link:{uri-catch}[Catch] (tested: 2.5 / 1.2)
diff --git a/include/connection.hpp b/include/connection.hpp
index 5ffe969..4bcdfe9 100644
--- a/include/connection.hpp
+++ b/include/connection.hpp
@@ -106,6 +106,13 @@ public:
*/
void set_proxy(string_view proxy);
+ /*!
+ * @brief Get new stream contents and delete them.
+ *
+ * @since 0.1.0
+ */
+ string get_new_stream_contents();
+
private:
Instance &_instance;
const string_view _baseuri;
diff --git a/include/curl_wrapper.hpp b/include/curl_wrapper.hpp
index ddc74fb..1ff1d07 100644
--- a/include/curl_wrapper.hpp
+++ b/include/curl_wrapper.hpp
@@ -22,6 +22,7 @@
#include "curl/curl.h"
#include <map>
+#include <mutex>
#include <string>
#include <string_view>
#include <variant>
@@ -31,6 +32,7 @@ namespace mastodonpp
{
using std::map;
+using std::mutex;
using std::string;
using std::string_view;
using std::variant;
@@ -91,10 +93,10 @@ public:
CURLWrapper();
//! Copy constructor
- CURLWrapper(const CURLWrapper &other) = default;
+ CURLWrapper(const CURLWrapper &other) = delete;
//! Move constructor
- CURLWrapper(CURLWrapper &&other) noexcept = default;
+ CURLWrapper(CURLWrapper &&other) noexcept = delete;
/*!
* @brief Cleans up curl and connection.
@@ -108,10 +110,10 @@ public:
virtual ~CURLWrapper() noexcept;
//! Copy assignment operator
- CURLWrapper& operator=(const CURLWrapper &other) = default;
+ CURLWrapper& operator=(const CURLWrapper &other) = delete;
//! Move assignment operator
- CURLWrapper& operator=(CURLWrapper &&other) noexcept = default;
+ CURLWrapper& operator=(CURLWrapper &&other) noexcept = delete;
/*!
* @brief Returns pointer to the CURL easy handle.
@@ -139,8 +141,30 @@ 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.
+ *
+ * This mutex is locked in `writer_body()` and
+ * Connection::get_new_stream_contents before anything is read or written
+ * from/to _curl_buffer_body.
+ *
+ * @since 0.1.0
+ */
+ mutex buffer_mutex;
+
+ /*!
* @brief Make a HTTP request.
*
* @param method The HTTP method.
@@ -153,11 +177,23 @@ protected:
answer_type make_request(const http_method &method, string uri,
const parametermap &parameters);
+ /*!
+ * @brief Returns a reference to the buffer libcurl writes into.
+ *
+ * @since 0.1.0
+ */
+ [[nodiscard]]
+ string &get_buffer()
+ {
+ return _curl_buffer_body;
+ }
+
private:
CURL *_connection;
char _curl_buffer_error[CURL_ERROR_SIZE];
string _curl_buffer_headers;
string _curl_buffer_body;
+ bool _stream_cancelled;
/*!
* @brief libcurl write callback function.
@@ -191,6 +227,25 @@ private:
}
/*!
+ * @brief libcurl transfer info function.
+ *
+ * Used to cancel streams.
+ *
+ * @since 0.1.0
+ */
+ int progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
+ curl_off_t ultotal, curl_off_t ulnow);
+
+ //! @copydoc writer_body_wrapper
+ static inline int progress_wrapper(void *f, void *clientp,
+ curl_off_t dltotal, curl_off_t dlnow,
+ curl_off_t ultotal, curl_off_t ulnow)
+ {
+ return static_cast<CURLWrapper*>(f)->progress(clientp, dltotal, dlnow,
+ ultotal, ulnow);
+ }
+
+ /*!
* @brief Setup libcurl connection.
*
* @since 0.1.0
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cdb651b..b9ede8d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,6 +1,6 @@
include(GNUInstallDirs)
-find_package(CURL REQUIRED)
+find_package(CURL 7.32 REQUIRED)
# Write version in header.
configure_file ("version.hpp.in"
diff --git a/src/connection.cpp b/src/connection.cpp
index fad710a..3aaa236 100644
--- a/src/connection.cpp
+++ b/src/connection.cpp
@@ -48,4 +48,14 @@ void Connection::set_proxy(const string_view proxy)
_instance.set_proxy(proxy);
}
+string Connection::get_new_stream_contents()
+{
+ buffer_mutex.lock();
+ auto &buffer{get_buffer()};
+ auto buffer_copy{buffer};
+ buffer.clear();
+ buffer_mutex.unlock();
+ return buffer_copy;
+}
+
} // namespace mastodonpp
diff --git a/src/curl_wrapper.cpp b/src/curl_wrapper.cpp
index b72e9e9..b100d21 100644
--- a/src/curl_wrapper.cpp
+++ b/src/curl_wrapper.cpp
@@ -40,6 +40,7 @@ static atomic<uint16_t> curlwrapper_instances{0};
CURLWrapper::CURLWrapper()
: _curl_buffer_error{}
+ , _stream_cancelled(false)
{
if (curlwrapper_instances == 0)
{
@@ -73,9 +74,16 @@ 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 &parameters)
{
+ _stream_cancelled = false;
+
CURLcode code;
switch (method)
{
@@ -158,7 +166,9 @@ size_t CURLWrapper::writer_body(char *data, size_t size, size_t nmemb)
return 0;
}
+ buffer_mutex.lock();
_curl_buffer_body.append(data, size * nmemb);
+ buffer_mutex.unlock();
return size * nmemb;
}
@@ -175,6 +185,16 @@ size_t CURLWrapper::writer_header(char *data, size_t size, size_t nmemb)
return size * nmemb;
}
+int CURLWrapper::progress(void *, curl_off_t , curl_off_t ,
+ curl_off_t , curl_off_t )
+{
+ if (_stream_cancelled)
+ {
+ return 1;
+ }
+ return 0;
+}
+
void CURLWrapper::setup_curl()
{
if (_connection == nullptr)
@@ -225,6 +245,26 @@ void CURLWrapper::setup_curl()
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
+ code = curl_easy_setopt(_connection, CURLOPT_XFERINFOFUNCTION,
+ progress_wrapper);
+ if (code != CURLE_OK)
+ {
+ throw CURLException{code, "Failed to set transfer info function",
+ _curl_buffer_error};
+ }
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
+ curl_easy_setopt(_connection, CURLOPT_XFERINFODATA, this);
+ if (code != CURLE_OK)
+ {
+ throw CURLException{code, "Failed to set transfer info data",
+ _curl_buffer_error};
+ }
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
+ curl_easy_setopt(_connection, CURLOPT_NOPROGRESS, 0L);
+
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
code = curl_easy_setopt(_connection, CURLOPT_USERAGENT,
(string("mastorss/") += version).c_str());
if (code != CURLE_OK)