mirror of
https://github.com/BlackMATov/curly.hpp.git
synced 2025-12-14 12:10:53 +07:00
first callback impl with tests
This commit is contained in:
22
README.md
22
README.md
@@ -30,6 +30,7 @@
|
||||
- Custom headers
|
||||
- Asynchronous requests
|
||||
- Different types of timeouts
|
||||
- Custom completion callbacks
|
||||
- PUT, GET, HEAD, POST methods
|
||||
- Custom uploading and downloading streams
|
||||
|
||||
@@ -134,7 +135,9 @@ auto request = net::request_builder()
|
||||
.url("http://unavailable.site.com")
|
||||
.send();
|
||||
|
||||
if ( request.wait() == net::request::statuses::done ) {
|
||||
request.wait();
|
||||
|
||||
if ( request.is_done() ) {
|
||||
auto response = request.get();
|
||||
std::cout << "Status code: " << response.http_code() << std::endl;
|
||||
} else {
|
||||
@@ -147,6 +150,23 @@ if ( request.wait() == net::request::statuses::done ) {
|
||||
// Error message: Couldn't resolve host name
|
||||
```
|
||||
|
||||
### Request Callback
|
||||
|
||||
```cpp
|
||||
auto request = net::request_builder("http://www.httpbin.org/get")
|
||||
.callback([](net::request request){
|
||||
if ( request.is_done() ) {
|
||||
auto response = request.get();
|
||||
std::cout << "Status code: " << response.http_code() << std::endl;
|
||||
} else {
|
||||
std::cout << "Error message: " << request.get_error() << std::endl;
|
||||
}
|
||||
}).send();
|
||||
|
||||
request.wait_callback();
|
||||
// Status code: 200
|
||||
```
|
||||
|
||||
### Streamed Requests
|
||||
|
||||
#### Downloading
|
||||
|
||||
@@ -108,6 +108,8 @@ namespace curly_hpp
|
||||
response& operator=(const response&) = delete;
|
||||
|
||||
explicit response(http_code_t c) noexcept;
|
||||
|
||||
bool is_http_error() const noexcept;
|
||||
http_code_t http_code() const noexcept;
|
||||
public:
|
||||
content_t content;
|
||||
@@ -147,8 +149,13 @@ namespace curly_hpp
|
||||
statuses wait_for(time_ms_t ms) const noexcept;
|
||||
statuses wait_until(time_point_t tp) const noexcept;
|
||||
|
||||
statuses wait_callback() const noexcept;
|
||||
statuses wait_callback_for(time_ms_t ms) const noexcept;
|
||||
statuses wait_callback_until(time_point_t tp) const noexcept;
|
||||
|
||||
response get();
|
||||
const std::string& get_error() const noexcept;
|
||||
std::exception_ptr get_callback_exception() const noexcept;
|
||||
private:
|
||||
internal_state_ptr state_;
|
||||
};
|
||||
|
||||
@@ -300,6 +300,10 @@ namespace curly_hpp
|
||||
response::response(http_code_t c) noexcept
|
||||
: http_code_(c) {}
|
||||
|
||||
bool response::is_http_error() const noexcept {
|
||||
return http_code_ >= 400u;
|
||||
}
|
||||
|
||||
http_code_t response::http_code() const noexcept {
|
||||
return http_code_;
|
||||
}
|
||||
@@ -431,11 +435,11 @@ namespace curly_hpp
|
||||
return false;
|
||||
}
|
||||
|
||||
long code = 0;
|
||||
long http_code = 0;
|
||||
if ( CURLE_OK != curl_easy_getinfo(
|
||||
curlh_.get(),
|
||||
CURLINFO_RESPONSE_CODE,
|
||||
&code) || !code )
|
||||
&http_code) || !http_code )
|
||||
{
|
||||
status_ = statuses::failed;
|
||||
cvar_.notify_all();
|
||||
@@ -443,7 +447,7 @@ namespace curly_hpp
|
||||
}
|
||||
|
||||
try {
|
||||
response_ = response(static_cast<http_code_t>(code));
|
||||
response_ = response(static_cast<http_code_t>(http_code));
|
||||
response_.content = std::move(response_content_);
|
||||
response_.headers = std::move(response_headers_);
|
||||
response_.uploader = std::move(breq_.uploader());
|
||||
@@ -519,26 +523,29 @@ namespace curly_hpp
|
||||
return status_ == statuses::pending;
|
||||
}
|
||||
|
||||
statuses wait() const noexcept {
|
||||
statuses wait(bool wait_callback) const noexcept {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cvar_.wait(lock, [this](){
|
||||
return status_ != statuses::pending;
|
||||
cvar_.wait(lock, [this, wait_callback](){
|
||||
return (status_ != statuses::pending)
|
||||
&& (!wait_callback || callbacked_);
|
||||
});
|
||||
return status_;
|
||||
}
|
||||
|
||||
statuses wait_for(time_ms_t ms) const noexcept {
|
||||
statuses wait_for(time_ms_t ms, bool wait_callback) const noexcept {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cvar_.wait_for(lock, ms, [this](){
|
||||
return status_ != statuses::pending;
|
||||
cvar_.wait_for(lock, ms, [this, wait_callback](){
|
||||
return (status_ != statuses::pending)
|
||||
&& (!wait_callback || callbacked_);
|
||||
});
|
||||
return status_;
|
||||
}
|
||||
|
||||
statuses wait_until(time_point_t tp) const noexcept {
|
||||
statuses wait_until(time_point_t tp, bool wait_callback) const noexcept {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cvar_.wait_until(lock, tp, [this](){
|
||||
return status_ != statuses::pending;
|
||||
cvar_.wait_until(lock, tp, [this, wait_callback](){
|
||||
return (status_ != statuses::pending)
|
||||
&& (!wait_callback || callbacked_);
|
||||
});
|
||||
return status_;
|
||||
}
|
||||
@@ -563,6 +570,30 @@ namespace curly_hpp
|
||||
return error_;
|
||||
}
|
||||
|
||||
std::exception_ptr get_callback_exception() const noexcept {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
cvar_.wait(lock, [this](){
|
||||
return callbacked_;
|
||||
});
|
||||
return callback_exception_;
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
void call_callback(Args&&... args) noexcept {
|
||||
try {
|
||||
if ( breq_.callback() ) {
|
||||
breq_.callback()(std::forward<Args>(args)...);
|
||||
}
|
||||
} catch (...) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
callback_exception_ = std::current_exception();
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
assert(!callbacked_ && status_ != statuses::pending);
|
||||
callbacked_ = true;
|
||||
cvar_.notify_all();
|
||||
}
|
||||
|
||||
bool check_response_timeout(time_point_t now) const noexcept {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
return now - last_response_ >= response_timeout_;
|
||||
@@ -650,6 +681,9 @@ namespace curly_hpp
|
||||
private:
|
||||
std::atomic_size_t uploaded_{0u};
|
||||
std::atomic_size_t downloaded_{0u};
|
||||
private:
|
||||
bool callbacked_{false};
|
||||
std::exception_ptr callback_exception_{nullptr};
|
||||
private:
|
||||
statuses status_{statuses::pending};
|
||||
std::string error_{"Unknown error"};
|
||||
@@ -683,15 +717,27 @@ namespace curly_hpp
|
||||
}
|
||||
|
||||
request::statuses request::wait() const noexcept {
|
||||
return state_->wait();
|
||||
return state_->wait(false);
|
||||
}
|
||||
|
||||
request::statuses request::wait_for(time_ms_t ms) const noexcept {
|
||||
return state_->wait_for(ms);
|
||||
return state_->wait_for(ms, false);
|
||||
}
|
||||
|
||||
request::statuses request::wait_until(time_point_t tp) const noexcept {
|
||||
return state_->wait_until(tp);
|
||||
return state_->wait_until(tp, false);
|
||||
}
|
||||
|
||||
request::statuses request::wait_callback() const noexcept {
|
||||
return state_->wait(true);
|
||||
}
|
||||
|
||||
request::statuses request::wait_callback_for(time_ms_t ms) const noexcept {
|
||||
return state_->wait_for(ms, true);
|
||||
}
|
||||
|
||||
request::statuses request::wait_callback_until(time_point_t tp) const noexcept {
|
||||
return state_->wait_until(tp, true);
|
||||
}
|
||||
|
||||
response request::get() {
|
||||
@@ -701,6 +747,10 @@ namespace curly_hpp
|
||||
const std::string& request::get_error() const noexcept {
|
||||
return state_->get_error();
|
||||
}
|
||||
|
||||
std::exception_ptr request::get_callback_exception() const noexcept {
|
||||
return state_->get_callback_exception();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -917,6 +967,7 @@ namespace curly_hpp
|
||||
} catch (...) {
|
||||
sreq->fail(CURLcode::CURLE_FAILED_INIT);
|
||||
sreq->dequeue(curlm);
|
||||
sreq->call_callback(sreq);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -959,6 +1010,7 @@ namespace curly_hpp
|
||||
for ( auto iter = active_handles.begin(); iter != active_handles.end(); ) {
|
||||
if ( !(*iter)->is_pending() ) {
|
||||
(*iter)->dequeue(curlm);
|
||||
(*iter)->call_callback(*iter);
|
||||
iter = active_handles.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
|
||||
@@ -538,6 +538,84 @@ TEST_CASE("curly") {
|
||||
REQUIRE(req.wait() == net::request::statuses::canceled);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("callback") {
|
||||
{
|
||||
std::atomic_size_t call_once{0u};
|
||||
auto req = net::request_builder("http://www.httpbin.org/get")
|
||||
.callback([&call_once](net::request request){
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
++call_once;
|
||||
REQUIRE(request.is_done());
|
||||
REQUIRE(request.status() == net::request::statuses::done);
|
||||
REQUIRE(request.get().http_code() == 200u);
|
||||
}).send();
|
||||
REQUIRE(req.wait_callback() == net::request::statuses::empty);
|
||||
REQUIRE_FALSE(req.get_callback_exception());
|
||||
REQUIRE(call_once.load() == 1u);
|
||||
}
|
||||
{
|
||||
std::atomic_size_t call_once{0u};
|
||||
auto req = net::request_builder("|||")
|
||||
.callback([&call_once](net::request request){
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
++call_once;
|
||||
REQUIRE_FALSE(request.is_done());
|
||||
REQUIRE(request.status() == net::request::statuses::failed);
|
||||
REQUIRE_FALSE(request.get_error().empty());
|
||||
}).send();
|
||||
REQUIRE(req.wait_callback() == net::request::statuses::failed);
|
||||
REQUIRE_FALSE(req.get_callback_exception());
|
||||
REQUIRE(call_once.load() == 1u);
|
||||
}
|
||||
{
|
||||
std::atomic_size_t call_once{0u};
|
||||
auto req = net::request_builder("http://www.httpbin.org/delay/2")
|
||||
.response_timeout(net::time_sec_t(0))
|
||||
.callback([&call_once](net::request request){
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
++call_once;
|
||||
REQUIRE_FALSE(request.is_done());
|
||||
REQUIRE(request.status() == net::request::statuses::timeout);
|
||||
REQUIRE_FALSE(request.get_error().empty());
|
||||
}).send();
|
||||
REQUIRE(req.wait_callback() == net::request::statuses::timeout);
|
||||
REQUIRE_FALSE(req.get_callback_exception());
|
||||
REQUIRE(call_once.load() == 1u);
|
||||
}
|
||||
{
|
||||
std::atomic_size_t call_once{0u};
|
||||
auto req = net::request_builder("http://www.httpbin.org/delay/2")
|
||||
.callback([&call_once](net::request request){
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
++call_once;
|
||||
REQUIRE_FALSE(request.is_done());
|
||||
REQUIRE(request.status() == net::request::statuses::canceled);
|
||||
REQUIRE(request.get_error().empty());
|
||||
}).send();
|
||||
REQUIRE(req.cancel());
|
||||
REQUIRE(req.wait_callback() == net::request::statuses::canceled);
|
||||
REQUIRE_FALSE(req.get_callback_exception());
|
||||
REQUIRE(call_once.load() == 1u);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("callback_exception") {
|
||||
auto req = net::request_builder("http://www.httpbin.org/post")
|
||||
.callback([](net::request request){
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
if ( request.get().is_http_error() ) {
|
||||
throw std::logic_error("my_logic_error");
|
||||
}
|
||||
}).send();
|
||||
REQUIRE(req.wait_callback() == net::request::statuses::empty);
|
||||
REQUIRE(req.get_callback_exception());
|
||||
try {
|
||||
std::rethrow_exception(req.get_callback_exception());
|
||||
} catch (const std::logic_error& e) {
|
||||
REQUIRE(std::string_view("my_logic_error") == e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("curly_examples") {
|
||||
@@ -610,7 +688,9 @@ TEST_CASE("curly_examples") {
|
||||
.url("http://unavailable.site.com")
|
||||
.send();
|
||||
|
||||
if ( request.wait() == net::request::statuses::done ) {
|
||||
request.wait();
|
||||
|
||||
if ( request.is_done() ) {
|
||||
auto response = request.get();
|
||||
std::cout << "Status code: " << response.http_code() << std::endl;
|
||||
} else {
|
||||
@@ -623,6 +703,21 @@ TEST_CASE("curly_examples") {
|
||||
// Error message: Couldn't resolve host name
|
||||
}
|
||||
|
||||
SECTION("Request Callback") {
|
||||
auto request = net::request_builder("http://www.httpbin.org/get")
|
||||
.callback([](net::request request){
|
||||
if ( request.is_done() ) {
|
||||
auto response = request.get();
|
||||
std::cout << "Status code: " << response.http_code() << std::endl;
|
||||
} else {
|
||||
std::cout << "Error message: " << request.get_error() << std::endl;
|
||||
}
|
||||
}).send();
|
||||
|
||||
request.wait_callback();
|
||||
// Status code: 200
|
||||
}
|
||||
|
||||
SECTION("Streamed Requests") {
|
||||
{
|
||||
class file_dowloader : public net::download_handler {
|
||||
|
||||
Reference in New Issue
Block a user