mirror of
https://github.com/BlackMATov/curly.hpp.git
synced 2025-12-13 03:29:37 +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;
|
||||
};
|
||||
|
||||
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 uploader_uptr = std::unique_ptr<upload_handler>;
|
||||
using downloader_uptr = std::unique_ptr<download_handler>;
|
||||
using progressor_uptr = std::unique_ptr<progress_handler>;
|
||||
}
|
||||
|
||||
namespace curly_hpp
|
||||
@@ -169,6 +178,7 @@ namespace curly_hpp
|
||||
headers_t headers;
|
||||
uploader_uptr uploader;
|
||||
downloader_uptr downloader;
|
||||
progressor_uptr progressor;
|
||||
private:
|
||||
http_code_t http_code_{0u};
|
||||
};
|
||||
@@ -193,6 +203,7 @@ namespace curly_hpp
|
||||
request(internal_state_ptr);
|
||||
|
||||
bool cancel() noexcept;
|
||||
float progress() const noexcept;
|
||||
req_status status() const noexcept;
|
||||
|
||||
bool is_done() const noexcept;
|
||||
@@ -246,6 +257,7 @@ namespace curly_hpp
|
||||
request_builder& callback(callback_t c) noexcept;
|
||||
request_builder& uploader(uploader_uptr u) noexcept;
|
||||
request_builder& downloader(downloader_uptr d) noexcept;
|
||||
request_builder& progressor(progressor_uptr p) noexcept;
|
||||
|
||||
const std::string& url() const noexcept;
|
||||
http_method method() const noexcept;
|
||||
@@ -270,6 +282,9 @@ namespace curly_hpp
|
||||
downloader_uptr& downloader() noexcept;
|
||||
const downloader_uptr& downloader() const noexcept;
|
||||
|
||||
progressor_uptr& progressor() noexcept;
|
||||
const progressor_uptr& progressor() const noexcept;
|
||||
|
||||
request send();
|
||||
|
||||
template < typename Callback >
|
||||
@@ -289,6 +304,12 @@ namespace curly_hpp
|
||||
static_assert(std::is_base_of_v<download_handler, Downloader>);
|
||||
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:
|
||||
std::string url_;
|
||||
http_method method_{http_method::GET};
|
||||
@@ -304,6 +325,7 @@ namespace curly_hpp
|
||||
callback_t callback_;
|
||||
uploader_uptr uploader_;
|
||||
downloader_uptr downloader_;
|
||||
progressor_uptr progressor_;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,23 @@ namespace
|
||||
private:
|
||||
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() ) {
|
||||
breq_.downloader<default_downloader>(&response_content_);
|
||||
}
|
||||
|
||||
if ( !breq_.progressor() ) {
|
||||
breq_.progressor<default_progressor>();
|
||||
}
|
||||
}
|
||||
|
||||
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_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_HEADERFUNCTION, &s_header_callback_);
|
||||
|
||||
@@ -364,6 +389,21 @@ namespace curly_hpp
|
||||
void dequeue(CURLM* curlm) noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
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());
|
||||
curlh_.reset();
|
||||
}
|
||||
@@ -392,12 +432,14 @@ namespace curly_hpp
|
||||
response_.headers = std::move(response_headers_);
|
||||
response_.uploader = std::move(breq_.uploader());
|
||||
response_.downloader = std::move(breq_.downloader());
|
||||
response_.progressor = std::move(breq_.progressor());
|
||||
} catch (...) {
|
||||
status_ = req_status::failed;
|
||||
cvar_.notify_all();
|
||||
return false;
|
||||
}
|
||||
|
||||
progress_ = 1.f;
|
||||
status_ = req_status::done;
|
||||
error_.clear();
|
||||
|
||||
@@ -448,6 +490,11 @@ namespace curly_hpp
|
||||
return true;
|
||||
}
|
||||
|
||||
float progress() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return progress_;
|
||||
}
|
||||
|
||||
req_status status() const noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return status_;
|
||||
@@ -553,6 +600,13 @@ namespace curly_hpp
|
||||
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_(
|
||||
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 {
|
||||
try {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
@@ -631,6 +705,7 @@ namespace curly_hpp
|
||||
bool callbacked_{false};
|
||||
std::exception_ptr callback_exception_{nullptr};
|
||||
private:
|
||||
float progress_{0.f};
|
||||
req_status status_{req_status::pending};
|
||||
std::string error_{"Unknown error"};
|
||||
private:
|
||||
@@ -650,6 +725,10 @@ namespace curly_hpp
|
||||
return state_->cancel();
|
||||
}
|
||||
|
||||
float request::progress() const noexcept {
|
||||
return state_->progress();
|
||||
}
|
||||
|
||||
req_status request::status() const noexcept {
|
||||
return state_->status();
|
||||
}
|
||||
@@ -787,6 +866,11 @@ namespace curly_hpp
|
||||
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 {
|
||||
return url_;
|
||||
}
|
||||
@@ -855,6 +939,14 @@ namespace curly_hpp
|
||||
return downloader_;
|
||||
}
|
||||
|
||||
progressor_uptr& request_builder::progressor() noexcept {
|
||||
return progressor_;
|
||||
}
|
||||
|
||||
const progressor_uptr& request_builder::progressor() const noexcept {
|
||||
return progressor_;
|
||||
}
|
||||
|
||||
request request_builder::send() {
|
||||
auto sreq = std::make_shared<request::internal_state>(std::move(*this));
|
||||
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) {
|
||||
return netex::make_promise<net::content_t>([
|
||||
url = std::move(url)
|
||||
@@ -673,6 +689,14 @@ TEST_CASE("curly") {
|
||||
.send();
|
||||
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") {
|
||||
|
||||
Reference in New Issue
Block a user