From de61b7fffd7d024252a148eaca4d571a4fa63223 Mon Sep 17 00:00:00 2001 From: Adam Law Date: Sun, 8 Jan 2017 16:31:08 +0000 Subject: [PATCH 1/6] Added support for progress reporting when downloading/uploading --- include/restclient-cpp/connection.h | 10 ++++++++ source/connection.cc | 39 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index 087b6b4f..e97a8bfa 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -106,6 +106,8 @@ class Connection { int timeout; bool followRedirects; bool noSignal; + curl_progress_callback progressFn; + void* progressFnData; struct { std::string username; std::string password; @@ -132,6 +134,12 @@ class Connection { // set connection timeout to seconds void SetTimeout(int seconds); + // set file progress callback + void SetFileProgressCallback(curl_progress_callback progressFn); + + // set file progress callback data + void SetFileProgressCallbackData(void* data); + // set to not use signals void SetNoSignal(bool no); @@ -192,6 +200,8 @@ class Connection { int timeout; bool followRedirects; bool noSignal; + curl_progress_callback progressFn; + void* progressFnData; struct { std::string username; std::string password; diff --git a/source/connection.cc b/source/connection.cc index e005ed7a..83a97b65 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -35,6 +35,8 @@ RestClient::Connection::Connection(const std::string& baseUrl) this->timeout = 0; this->followRedirects = false; this->noSignal = false; + this->progressFn = nullptr; + this->progressFnData = nullptr; } RestClient::Connection::~Connection() { @@ -58,6 +60,8 @@ RestClient::Connection::GetInfo() { ret.timeout = this->timeout; ret.followRedirects = this->followRedirects; ret.noSignal = this->noSignal; + ret.progressFn = this->progressFn; + ret.progressFnData = this->progressFnData; ret.basicAuth.username = this->basicAuth.username; ret.basicAuth.password = this->basicAuth.password; ret.customUserAgent = this->customUserAgent; @@ -183,6 +187,28 @@ RestClient::Connection::SetNoSignal(bool no) { this->noSignal = no; } +/** + * @brief set file progress callback function + * + * @param callback function pointer + * + */ +void +RestClient::Connection::SetFileProgressCallback(curl_progress_callback progressFn) { + this->progressFn = progressFn; +} + +/** + * @brief set file progress callback data + * + * @param callback data pointer + * + */ +void +RestClient::Connection::SetFileProgressCallbackData(void* data) { + this->progressFnData = data; +} + /** * @brief set username and password for basic auth * @@ -338,6 +364,19 @@ RestClient::Connection::performCurlRequest(const std::string& uri) { curl_easy_setopt(this->curlHandle, CURLOPT_NOSIGNAL, 1); } + // set file progress callback + if (this->progressFn) + { + curl_easy_setopt(this->curlHandle, CURLOPT_NOPROGRESS, 0); + curl_easy_setopt(this->curlHandle, CURLOPT_PROGRESSFUNCTION, this->progressFn); + if (this->progressFnData) { + curl_easy_setopt(this->curlHandle, CURLOPT_PROGRESSDATA, this->progressFnData); + } + else { + curl_easy_setopt(this->curlHandle, CURLOPT_PROGRESSDATA, this); + } + } + // if provided, supply CA path if (!this->caInfoFilePath.empty()) { curl_easy_setopt(this->curlHandle, CURLOPT_CAINFO, From aef524791e8ec42194fb01e4a5e2c2bbcd906a7b Mon Sep 17 00:00:00 2001 From: Adam Law Date: Tue, 24 Jul 2018 21:53:05 +0100 Subject: [PATCH 2/6] * Fixed failing CI issues. * Added unit test for progress callback. * Updated `README.md` with documentation for the new progress callback additions. --- README.md | 14 ++++++++++++++ include/restclient-cpp/connection.h | 4 ++++ source/connection.cc | 25 +++++++++++++++---------- test/test_connection.cc | 20 ++++++++++++++++++++ 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f84ede91..ad2a2f69 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,20 @@ conn->SetProxy("37.187.100.23:3128"); RestClient::Response res = conn->get("/get"); ``` +## Progress callback + +Two simple wrapper functions are provided to setup progress callback for uploads/downloads; +Calling `conn->SetFileProgressCallback(callback)` with a callback parameter matching the prototype `int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)` will setup the progress callback. +Calling `conn->SetFileProgressCallbackData(data)` is optional. This will set the data pointer which is the first parameter fed back to the progress callback - `clientp`. If this isn't set then `clientp` will default to the connection object `conn`. + +```cpp +// set CURLOPT_NOPROGRESS +// set CURLOPT_PROGRESSFUNCTION +conn->SetFileProgressCallback(progressFunc); +// set CURLOPT_PROGRESSDATA +conn->SetFileProgressCallbackData(data); +``` + ## Dependencies - [libcurl][] diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index e97a8bfa..aa158721 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -79,6 +79,10 @@ class Connection { * Member 'timeout' contains the configured timeout * @var Info::followRedirects * Member 'followRedirects' contains whether or not to follow redirects + * @var Info::progressFn + * Member 'progressFn' file progress callback function + * @var Info::progressFnData + * Member 'progressFnData' file progress callback data * @var Info::basicAuth * Member 'basicAuth' contains information about basic auth * @var basicAuth::username diff --git a/source/connection.cc b/source/connection.cc index 83a97b65..77827b88 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -194,8 +194,9 @@ RestClient::Connection::SetNoSignal(bool no) { * */ void -RestClient::Connection::SetFileProgressCallback(curl_progress_callback progressFn) { - this->progressFn = progressFn; +RestClient::Connection::SetFileProgressCallback(curl_progress_callback + progressFn) { + this->progressFn = progressFn; } /** @@ -206,7 +207,7 @@ RestClient::Connection::SetFileProgressCallback(curl_progress_callback progressF */ void RestClient::Connection::SetFileProgressCallbackData(void* data) { - this->progressFnData = data; + this->progressFnData = data; } /** @@ -365,15 +366,19 @@ RestClient::Connection::performCurlRequest(const std::string& uri) { } // set file progress callback - if (this->progressFn) - { + if (this->progressFn) { curl_easy_setopt(this->curlHandle, CURLOPT_NOPROGRESS, 0); - curl_easy_setopt(this->curlHandle, CURLOPT_PROGRESSFUNCTION, this->progressFn); + curl_easy_setopt(this->curlHandle, + CURLOPT_PROGRESSFUNCTION, + this->progressFn); if (this->progressFnData) { - curl_easy_setopt(this->curlHandle, CURLOPT_PROGRESSDATA, this->progressFnData); - } - else { - curl_easy_setopt(this->curlHandle, CURLOPT_PROGRESSDATA, this); + curl_easy_setopt(this->curlHandle, + CURLOPT_PROGRESSDATA, + this->progressFnData); + } else { + curl_easy_setopt(this->curlHandle, + CURLOPT_PROGRESSDATA, + this); } } diff --git a/test/test_connection.cc b/test/test_connection.cc index adddbb9e..7a7ad8d1 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -217,6 +217,26 @@ TEST_F(ConnectionTest, TestNoSignal) EXPECT_EQ(200, res.code); } +TEST_F(ConnectionTest, TestSetProgress) +{ + static double totalToDownload = 0; + static double totalDownloaded = 0; + + auto progressCallback = [](void* pData, double downloadTotal, double downloaded, double uploadTotal, double uploaded) -> int { + totalToDownload = downloadTotal; + totalDownloaded = downloaded; + return 0; + }; + + conn->SetFileProgressCallback(progressCallback); + conn->SetFileProgressCallbackData(nullptr); + + RestClient::Response res = conn->get("/anything/{test_data}"); + EXPECT_EQ(200, res.code); + EXPECT_EQ(totalToDownload, 267); + EXPECT_EQ(totalDownloaded, 267); +} + TEST_F(ConnectionTest, TestProxy) { conn->SetProxy("127.0.0.1:3128"); From a57162a08c54623f38e90639769001de5ef6bf75 Mon Sep 17 00:00:00 2001 From: Adam Law Date: Tue, 24 Jul 2018 22:01:17 +0100 Subject: [PATCH 3/6] * Fixed merge conflicts --- include/restclient-cpp/connection.h | 7 +++++++ source/connection.cc | 23 ++++++++++++++++++----- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index aa158721..c1d197ca 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -83,6 +83,8 @@ class Connection { * Member 'progressFn' file progress callback function * @var Info::progressFnData * Member 'progressFnData' file progress callback data + * @var Info::maxRedirects + * Member 'maxRedirects' contains the maximum number of redirect to follow (-1 unlimited) * @var Info::basicAuth * Member 'basicAuth' contains information about basic auth * @var basicAuth::username @@ -109,6 +111,7 @@ class Connection { RestClient::HeaderFields headers; int timeout; bool followRedirects; + int maxRedirects; bool noSignal; curl_progress_callback progressFn; void* progressFnData; @@ -150,6 +153,9 @@ class Connection { // set whether to follow redirects void FollowRedirects(bool follow); + // set whether to follow redirects (-1 for unlimited) + void FollowRedirects(bool follow, int maxRedirects); + // set custom user agent // (this will result in the UA "foo/cool restclient-cpp/VERSION") void SetUserAgent(const std::string& userAgent); @@ -203,6 +209,7 @@ class Connection { RestClient::HeaderFields headerFields; int timeout; bool followRedirects; + int maxRedirects; bool noSignal; curl_progress_callback progressFn; void* progressFnData; diff --git a/source/connection.cc b/source/connection.cc index 77827b88..b144ca69 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -34,6 +34,7 @@ RestClient::Connection::Connection(const std::string& baseUrl) this->baseUrl = baseUrl; this->timeout = 0; this->followRedirects = false; + this->maxRedirects = -1l; this->noSignal = false; this->progressFn = nullptr; this->progressFnData = nullptr; @@ -59,6 +60,7 @@ RestClient::Connection::GetInfo() { ret.headers = this->GetHeaders(); ret.timeout = this->timeout; ret.followRedirects = this->followRedirects; + ret.maxRedirects = this->maxRedirects; ret.noSignal = this->noSignal; ret.progressFn = this->progressFn; ret.progressFnData = this->progressFnData; @@ -124,6 +126,19 @@ RestClient::Connection::GetHeaders() { void RestClient::Connection::FollowRedirects(bool follow) { this->followRedirects = follow; + this->maxRedirects = -1l; +} + +/** + * @brief configure whether to follow redirects on this connection + * + * @param follow - boolean whether to follow redirects + * @param maxRedirects - int indicating the maximum number of redirect to follow (-1 unlimited) + */ +void +RestClient::Connection::FollowRedirects(bool follow, int maxRedirects) { + this->followRedirects = follow; + this->maxRedirects = maxRedirects; } /** @@ -276,16 +291,12 @@ RestClient::Connection::SetKeyPassword(const std::string& keyPassword) { */ void RestClient::Connection::SetProxy(const std::string& uriProxy) { - if (uriProxy.empty()) { - return; - } - std::string uriProxyUpper = uriProxy; // check if the provided address is prefixed with "http" std::transform(uriProxyUpper.begin(), uriProxyUpper.end(), uriProxyUpper.begin(), ::toupper); - if (uriProxyUpper.compare(0, 4, "HTTP") != 0) { + if ((uriProxy.length() > 0) && (uriProxyUpper.compare(0, 4, "HTTP") != 0)) { this->uriProxy = "http://" + uriProxy; } else { this->uriProxy = uriProxy; @@ -358,6 +369,8 @@ RestClient::Connection::performCurlRequest(const std::string& uri) { // set follow redirect if (this->followRedirects == true) { curl_easy_setopt(this->curlHandle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(this->curlHandle, CURLOPT_MAXREDIRS, + static_cast(this->maxRedirects)); } if (this->noSignal) { From 7e3eaf08def8798afd9e6ddf1ad1f85653db2b02 Mon Sep 17 00:00:00 2001 From: Adam Law Date: Tue, 24 Jul 2018 22:12:35 +0100 Subject: [PATCH 4/6] * `nullptr` compilation errors on CI build. --- source/connection.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/connection.cc b/source/connection.cc index b144ca69..943ff154 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -36,8 +36,8 @@ RestClient::Connection::Connection(const std::string& baseUrl) this->followRedirects = false; this->maxRedirects = -1l; this->noSignal = false; - this->progressFn = nullptr; - this->progressFnData = nullptr; + this->progressFn = NULL; + this->progressFnData = NULL; } RestClient::Connection::~Connection() { From e0a4b4f9e039ee8bef2ee230ff21cfedca2cb868 Mon Sep 17 00:00:00 2001 From: Adam Law Date: Tue, 24 Jul 2018 22:17:55 +0100 Subject: [PATCH 5/6] * `nullptr` compilation error on unit test - these compile fine for me locally. --- test/test_connection.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_connection.cc b/test/test_connection.cc index a3d8cfcf..8249dbd5 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -244,7 +244,7 @@ TEST_F(ConnectionTest, TestSetProgress) }; conn->SetFileProgressCallback(progressCallback); - conn->SetFileProgressCallbackData(nullptr); + conn->SetFileProgressCallbackData(NULL); RestClient::Response res = conn->get("/anything/{test_data}"); EXPECT_EQ(200, res.code); From a3ff6d82da68cdfbc08a8facfc6b73f58a05d09c Mon Sep 17 00:00:00 2001 From: Adam Law Date: Wed, 25 Jul 2018 06:32:17 +0100 Subject: [PATCH 6/6] * Tweaked `TestSetProgress` expectations --- test/test_connection.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_connection.cc b/test/test_connection.cc index 8249dbd5..379aa35d 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -248,8 +248,8 @@ TEST_F(ConnectionTest, TestSetProgress) RestClient::Response res = conn->get("/anything/{test_data}"); EXPECT_EQ(200, res.code); - EXPECT_EQ(totalToDownload, 267); - EXPECT_EQ(totalDownloaded, 267); + EXPECT_GT(totalDownloaded, 0); + EXPECT_EQ(totalDownloaded, totalToDownload); } TEST_F(ConnectionTest, TestProxy)