mirror of
https://github.com/BlackMATov/curly.hpp.git
synced 2025-12-16 14:11:17 +07:00
add progress information to request #13
This commit is contained in:
@@ -62,9 +62,18 @@ namespace curly_hpp
|
|||||||
virtual std::size_t write(const char* src, std::size_t size) = 0;
|
virtual std::size_t write(const char* src, std::size_t size) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class progress_handler {
|
||||||
|
public:
|
||||||
|
virtual ~progress_handler() {}
|
||||||
|
virtual float update(
|
||||||
|
std::size_t dnow, std::size_t dtotal,
|
||||||
|
std::size_t unow, std::size_t utotal) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
using callback_t = std::function<void(request)>;
|
using callback_t = std::function<void(request)>;
|
||||||
using uploader_uptr = std::unique_ptr<upload_handler>;
|
using uploader_uptr = std::unique_ptr<upload_handler>;
|
||||||
using downloader_uptr = std::unique_ptr<download_handler>;
|
using downloader_uptr = std::unique_ptr<download_handler>;
|
||||||
|
using progressor_uptr = std::unique_ptr<progress_handler>;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace curly_hpp
|
namespace curly_hpp
|
||||||
@@ -169,6 +178,7 @@ namespace curly_hpp
|
|||||||
headers_t headers;
|
headers_t headers;
|
||||||
uploader_uptr uploader;
|
uploader_uptr uploader;
|
||||||
downloader_uptr downloader;
|
downloader_uptr downloader;
|
||||||
|
progressor_uptr progressor;
|
||||||
private:
|
private:
|
||||||
http_code_t http_code_{0u};
|
http_code_t http_code_{0u};
|
||||||
};
|
};
|
||||||
@@ -193,6 +203,7 @@ namespace curly_hpp
|
|||||||
request(internal_state_ptr);
|
request(internal_state_ptr);
|
||||||
|
|
||||||
bool cancel() noexcept;
|
bool cancel() noexcept;
|
||||||
|
float progress() const noexcept;
|
||||||
req_status status() const noexcept;
|
req_status status() const noexcept;
|
||||||
|
|
||||||
bool is_done() const noexcept;
|
bool is_done() const noexcept;
|
||||||
@@ -246,6 +257,7 @@ namespace curly_hpp
|
|||||||
request_builder& callback(callback_t c) noexcept;
|
request_builder& callback(callback_t c) noexcept;
|
||||||
request_builder& uploader(uploader_uptr u) noexcept;
|
request_builder& uploader(uploader_uptr u) noexcept;
|
||||||
request_builder& downloader(downloader_uptr d) noexcept;
|
request_builder& downloader(downloader_uptr d) noexcept;
|
||||||
|
request_builder& progressor(progressor_uptr p) noexcept;
|
||||||
|
|
||||||
const std::string& url() const noexcept;
|
const std::string& url() const noexcept;
|
||||||
http_method method() const noexcept;
|
http_method method() const noexcept;
|
||||||
@@ -270,6 +282,9 @@ namespace curly_hpp
|
|||||||
downloader_uptr& downloader() noexcept;
|
downloader_uptr& downloader() noexcept;
|
||||||
const downloader_uptr& downloader() const noexcept;
|
const downloader_uptr& downloader() const noexcept;
|
||||||
|
|
||||||
|
progressor_uptr& progressor() noexcept;
|
||||||
|
const progressor_uptr& progressor() const noexcept;
|
||||||
|
|
||||||
request send();
|
request send();
|
||||||
|
|
||||||
template < typename Callback >
|
template < typename Callback >
|
||||||
@@ -289,6 +304,12 @@ namespace curly_hpp
|
|||||||
static_assert(std::is_base_of_v<download_handler, Downloader>);
|
static_assert(std::is_base_of_v<download_handler, Downloader>);
|
||||||
return downloader(std::make_unique<Downloader>(std::forward<Args>(args)...));
|
return downloader(std::make_unique<Downloader>(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template < typename Progressor, typename... Args >
|
||||||
|
request_builder& progressor(Args&&... args) {
|
||||||
|
static_assert(std::is_base_of_v<progress_handler, Progressor>);
|
||||||
|
return progressor(std::make_unique<Progressor>(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
std::string url_;
|
std::string url_;
|
||||||
http_method method_{http_method::GET};
|
http_method method_{http_method::GET};
|
||||||
@@ -304,6 +325,7 @@ namespace curly_hpp
|
|||||||
callback_t callback_;
|
callback_t callback_;
|
||||||
uploader_uptr uploader_;
|
uploader_uptr uploader_;
|
||||||
downloader_uptr downloader_;
|
downloader_uptr downloader_;
|
||||||
|
progressor_uptr progressor_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,23 @@ namespace
|
|||||||
private:
|
private:
|
||||||
data_t& data_;
|
data_t& data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class default_progressor final : public progress_handler {
|
||||||
|
public:
|
||||||
|
default_progressor() = default;
|
||||||
|
|
||||||
|
float update(
|
||||||
|
std::size_t dnow, std::size_t dtotal,
|
||||||
|
std::size_t unow, std::size_t utotal) override
|
||||||
|
{
|
||||||
|
double now_d = static_cast<double>(dnow + unow);
|
||||||
|
double total_d = static_cast<double>(dtotal + utotal);
|
||||||
|
double progress_d = total_d > 0.0
|
||||||
|
? static_cast<float>(now_d / total_d)
|
||||||
|
: 0.f;
|
||||||
|
return static_cast<float>(std::min(std::max(progress_d, 0.0), 1.0));
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -253,6 +270,10 @@ namespace curly_hpp
|
|||||||
if ( !breq_.downloader() ) {
|
if ( !breq_.downloader() ) {
|
||||||
breq_.downloader<default_downloader>(&response_content_);
|
breq_.downloader<default_downloader>(&response_content_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !breq_.progressor() ) {
|
||||||
|
breq_.progressor<default_progressor>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void enqueue(CURLM* curlm) {
|
void enqueue(CURLM* curlm) {
|
||||||
@@ -287,6 +308,10 @@ namespace curly_hpp
|
|||||||
curl_easy_setopt(curlh_.get(), CURLOPT_WRITEDATA, this);
|
curl_easy_setopt(curlh_.get(), CURLOPT_WRITEDATA, this);
|
||||||
curl_easy_setopt(curlh_.get(), CURLOPT_WRITEFUNCTION, &s_download_callback_);
|
curl_easy_setopt(curlh_.get(), CURLOPT_WRITEFUNCTION, &s_download_callback_);
|
||||||
|
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_NOPROGRESS, 0l);
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_XFERINFODATA, this);
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_XFERINFOFUNCTION, &s_progress_callback_);
|
||||||
|
|
||||||
curl_easy_setopt(curlh_.get(), CURLOPT_HEADERDATA, this);
|
curl_easy_setopt(curlh_.get(), CURLOPT_HEADERDATA, this);
|
||||||
curl_easy_setopt(curlh_.get(), CURLOPT_HEADERFUNCTION, &s_header_callback_);
|
curl_easy_setopt(curlh_.get(), CURLOPT_HEADERFUNCTION, &s_header_callback_);
|
||||||
|
|
||||||
@@ -364,6 +389,21 @@ namespace curly_hpp
|
|||||||
void dequeue(CURLM* curlm) noexcept {
|
void dequeue(CURLM* curlm) noexcept {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
if ( curlh_ ) {
|
if ( curlh_ ) {
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_PRIVATE, nullptr);
|
||||||
|
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_READDATA, nullptr);
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_READFUNCTION, nullptr);
|
||||||
|
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_WRITEDATA, nullptr);
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_WRITEFUNCTION, nullptr);
|
||||||
|
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_NOPROGRESS, 1l);
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_XFERINFODATA, nullptr);
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_XFERINFOFUNCTION, nullptr);
|
||||||
|
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_HEADERDATA, nullptr);
|
||||||
|
curl_easy_setopt(curlh_.get(), CURLOPT_HEADERFUNCTION, nullptr);
|
||||||
|
|
||||||
curl_multi_remove_handle(curlm, curlh_.get());
|
curl_multi_remove_handle(curlm, curlh_.get());
|
||||||
curlh_.reset();
|
curlh_.reset();
|
||||||
}
|
}
|
||||||
@@ -392,12 +432,14 @@ namespace curly_hpp
|
|||||||
response_.headers = std::move(response_headers_);
|
response_.headers = std::move(response_headers_);
|
||||||
response_.uploader = std::move(breq_.uploader());
|
response_.uploader = std::move(breq_.uploader());
|
||||||
response_.downloader = std::move(breq_.downloader());
|
response_.downloader = std::move(breq_.downloader());
|
||||||
|
response_.progressor = std::move(breq_.progressor());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
status_ = req_status::failed;
|
status_ = req_status::failed;
|
||||||
cvar_.notify_all();
|
cvar_.notify_all();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
progress_ = 1.f;
|
||||||
status_ = req_status::done;
|
status_ = req_status::done;
|
||||||
error_.clear();
|
error_.clear();
|
||||||
|
|
||||||
@@ -448,6 +490,11 @@ namespace curly_hpp
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float progress() const noexcept {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
return progress_;
|
||||||
|
}
|
||||||
|
|
||||||
req_status status() const noexcept {
|
req_status status() const noexcept {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
return status_;
|
return status_;
|
||||||
@@ -553,6 +600,13 @@ namespace curly_hpp
|
|||||||
return self->download_callback_(ptr, size * nmemb);
|
return self->download_callback_(ptr, size * nmemb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int s_progress_callback_(
|
||||||
|
void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) noexcept
|
||||||
|
{
|
||||||
|
auto* self = static_cast<internal_state*>(clientp);
|
||||||
|
return self->progress_callback(dlnow, dltotal, ulnow, ultotal);
|
||||||
|
}
|
||||||
|
|
||||||
static std::size_t s_header_callback_(
|
static std::size_t s_header_callback_(
|
||||||
char* buffer, std::size_t size, std::size_t nitems, void* userdata) noexcept
|
char* buffer, std::size_t size, std::size_t nitems, void* userdata) noexcept
|
||||||
{
|
{
|
||||||
@@ -589,6 +643,26 @@ namespace curly_hpp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int progress_callback(
|
||||||
|
curl_off_t dlnow, curl_off_t dltotal,
|
||||||
|
curl_off_t ulnow, curl_off_t ultotal) noexcept
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
|
||||||
|
std::size_t dnow_sz = dlnow > 0 ? static_cast<std::size_t>(dlnow) : 0u;
|
||||||
|
std::size_t dtotal_sz = dltotal > 0 ? static_cast<std::size_t>(dltotal) : 0u;
|
||||||
|
|
||||||
|
std::size_t unow_sz = ulnow > 0 ? static_cast<std::size_t>(ulnow) : 0u;
|
||||||
|
std::size_t utotal_sz = ultotal > 0 ? static_cast<std::size_t>(ultotal) : 0u;
|
||||||
|
|
||||||
|
progress_ = breq_.progressor()->update(dnow_sz, dtotal_sz, unow_sz, utotal_sz);
|
||||||
|
return 0;
|
||||||
|
} catch (...) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::size_t header_callback_(const char* src, std::size_t size) noexcept {
|
std::size_t header_callback_(const char* src, std::size_t size) noexcept {
|
||||||
try {
|
try {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
@@ -631,6 +705,7 @@ namespace curly_hpp
|
|||||||
bool callbacked_{false};
|
bool callbacked_{false};
|
||||||
std::exception_ptr callback_exception_{nullptr};
|
std::exception_ptr callback_exception_{nullptr};
|
||||||
private:
|
private:
|
||||||
|
float progress_{0.f};
|
||||||
req_status status_{req_status::pending};
|
req_status status_{req_status::pending};
|
||||||
std::string error_{"Unknown error"};
|
std::string error_{"Unknown error"};
|
||||||
private:
|
private:
|
||||||
@@ -650,6 +725,10 @@ namespace curly_hpp
|
|||||||
return state_->cancel();
|
return state_->cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float request::progress() const noexcept {
|
||||||
|
return state_->progress();
|
||||||
|
}
|
||||||
|
|
||||||
req_status request::status() const noexcept {
|
req_status request::status() const noexcept {
|
||||||
return state_->status();
|
return state_->status();
|
||||||
}
|
}
|
||||||
@@ -787,6 +866,11 @@ namespace curly_hpp
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request_builder& request_builder::progressor(progressor_uptr p) noexcept {
|
||||||
|
progressor_ = std::move(p);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& request_builder::url() const noexcept {
|
const std::string& request_builder::url() const noexcept {
|
||||||
return url_;
|
return url_;
|
||||||
}
|
}
|
||||||
@@ -855,6 +939,14 @@ namespace curly_hpp
|
|||||||
return downloader_;
|
return downloader_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
progressor_uptr& request_builder::progressor() noexcept {
|
||||||
|
return progressor_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressor_uptr& request_builder::progressor() const noexcept {
|
||||||
|
return progressor_;
|
||||||
|
}
|
||||||
|
|
||||||
request request_builder::send() {
|
request request_builder::send() {
|
||||||
auto sreq = std::make_shared<request::internal_state>(std::move(*this));
|
auto sreq = std::make_shared<request::internal_state>(std::move(*this));
|
||||||
new_handles.enqueue(sreq);
|
new_handles.enqueue(sreq);
|
||||||
|
|||||||
@@ -58,6 +58,22 @@ namespace
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class canceled_progressor : public net::progress_handler {
|
||||||
|
public:
|
||||||
|
canceled_progressor() = default;
|
||||||
|
|
||||||
|
float update(
|
||||||
|
std::size_t dnow, std::size_t dtotal,
|
||||||
|
std::size_t unow, std::size_t utotal) override
|
||||||
|
{
|
||||||
|
(void)dnow;
|
||||||
|
(void)dtotal;
|
||||||
|
(void)unow;
|
||||||
|
(void)utotal;
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
netex::promise<net::content_t> download(std::string url) {
|
netex::promise<net::content_t> download(std::string url) {
|
||||||
return netex::make_promise<net::content_t>([
|
return netex::make_promise<net::content_t>([
|
||||||
url = std::move(url)
|
url = std::move(url)
|
||||||
@@ -673,6 +689,14 @@ TEST_CASE("curly") {
|
|||||||
.send();
|
.send();
|
||||||
REQUIRE(req.wait() == net::req_status::canceled);
|
REQUIRE(req.wait() == net::req_status::canceled);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
auto req = net::request_builder("https://httpbin.org/anything")
|
||||||
|
.verbose(true)
|
||||||
|
.method(net::http_method::GET)
|
||||||
|
.progressor<canceled_progressor>()
|
||||||
|
.send();
|
||||||
|
REQUIRE(req.wait() == net::req_status::canceled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("callback") {
|
SECTION("callback") {
|
||||||
|
|||||||
Reference in New Issue
Block a user