mirror of
https://github.com/BlackMATov/curly.hpp.git
synced 2025-12-15 12:19:47 +07:00
Merge branch 'issue/add-completion-callbacks' into dev
This commit is contained in:
72
README.md
72
README.md
@@ -30,6 +30,7 @@
|
|||||||
- Custom headers
|
- Custom headers
|
||||||
- Asynchronous requests
|
- Asynchronous requests
|
||||||
- Different types of timeouts
|
- Different types of timeouts
|
||||||
|
- Custom completion callbacks
|
||||||
- PUT, GET, HEAD, POST methods
|
- PUT, GET, HEAD, POST methods
|
||||||
- Custom uploading and downloading streams
|
- Custom uploading and downloading streams
|
||||||
|
|
||||||
@@ -74,7 +75,7 @@ auto request = net::request_builder()
|
|||||||
auto response = request.get();
|
auto response = request.get();
|
||||||
|
|
||||||
// prints results
|
// prints results
|
||||||
std::cout << "Status code: " << response.code() << std::endl;
|
std::cout << "Status code: " << response.http_code() << std::endl;
|
||||||
std::cout << "Content type: " << response.headers["content-type"] << std::endl;
|
std::cout << "Content type: " << response.headers["content-type"] << std::endl;
|
||||||
std::cout << "Body content: " << response.content.as_string_view() << std::endl;
|
std::cout << "Body content: " << response.content.as_string_view() << std::endl;
|
||||||
|
|
||||||
@@ -134,9 +135,11 @@ auto request = net::request_builder()
|
|||||||
.url("http://unavailable.site.com")
|
.url("http://unavailable.site.com")
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
if ( request.wait() == net::request::statuses::done ) {
|
request.wait();
|
||||||
|
|
||||||
|
if ( request.is_done() ) {
|
||||||
auto response = request.get();
|
auto response = request.get();
|
||||||
std::cout << "Status code: " << response.code() << std::endl;
|
std::cout << "Status code: " << response.http_code() << std::endl;
|
||||||
} else {
|
} else {
|
||||||
// throws net::exception because a response is unavailable
|
// throws net::exception because a response is unavailable
|
||||||
// auto response = request.get();
|
// auto response = request.get();
|
||||||
@@ -147,6 +150,23 @@ if ( request.wait() == net::request::statuses::done ) {
|
|||||||
// Error message: Couldn't resolve host name
|
// Error message: Couldn't resolve host name
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Request Callbacks
|
||||||
|
|
||||||
|
```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
|
### Streamed Requests
|
||||||
|
|
||||||
#### Downloading
|
#### Downloading
|
||||||
@@ -203,6 +223,52 @@ net::request_builder()
|
|||||||
.send().wait();
|
.send().wait();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Promised Requests
|
||||||
|
|
||||||
|
Also, you can easily integrate promises like a [promise.hpp](https://github.com/BlackMATov/promise.hpp).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <curly.hpp/curly.hpp>
|
||||||
|
namespace net = curly_hpp;
|
||||||
|
|
||||||
|
#include <promise.hpp/promise.hpp>
|
||||||
|
namespace netex = promise_hpp;
|
||||||
|
|
||||||
|
netex::promise<net::content_t> download(std::string url) {
|
||||||
|
return netex::make_promise<net::content_t>([
|
||||||
|
url = std::move(url)
|
||||||
|
](auto resolve, auto reject){
|
||||||
|
net::request_builder(std::move(url))
|
||||||
|
.callback([resolve,reject](net::request request) mutable {
|
||||||
|
if ( !request.is_done() ) {
|
||||||
|
reject(net::exception("network error"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
net::response response = request.get();
|
||||||
|
if ( response.is_http_error() ) {
|
||||||
|
reject(net::exception("server error"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(std::move(response.content));
|
||||||
|
}).send();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto promise = download("https://httpbin.org/image/png")
|
||||||
|
.then([](const net::content_t& content){
|
||||||
|
std::cout << content.size() << " bytes downloaded" << std::endl;
|
||||||
|
}).except([](std::exception_ptr e){
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(e);
|
||||||
|
} catch (const std::exception& ee) {
|
||||||
|
std::cerr << "Failed to download: " << ee.what() << std::endl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.wait();
|
||||||
|
// 8090 bytes downloaded
|
||||||
|
```
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
> coming soon!
|
> coming soon!
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace curly_hpp
|
namespace curly_hpp
|
||||||
{
|
{
|
||||||
@@ -41,13 +42,16 @@ namespace curly_hpp
|
|||||||
post
|
post
|
||||||
};
|
};
|
||||||
|
|
||||||
using response_code_t = std::uint16_t;
|
using http_code_t = std::uint16_t;
|
||||||
using headers_t = std::map<std::string, std::string, icase_string_compare>;
|
using headers_t = std::map<std::string, std::string, icase_string_compare>;
|
||||||
|
|
||||||
using time_sec_t = std::chrono::seconds;
|
using time_sec_t = std::chrono::seconds;
|
||||||
using time_ms_t = std::chrono::milliseconds;
|
using time_ms_t = std::chrono::milliseconds;
|
||||||
using time_point_t = std::chrono::steady_clock::time_point;
|
using time_point_t = std::chrono::steady_clock::time_point;
|
||||||
|
|
||||||
|
class request;
|
||||||
|
using callback_t = std::function<void(request)>;
|
||||||
|
|
||||||
class upload_handler {
|
class upload_handler {
|
||||||
public:
|
public:
|
||||||
virtual ~upload_handler() {}
|
virtual ~upload_handler() {}
|
||||||
@@ -103,15 +107,17 @@ namespace curly_hpp
|
|||||||
response(const response&) = delete;
|
response(const response&) = delete;
|
||||||
response& operator=(const response&) = delete;
|
response& operator=(const response&) = delete;
|
||||||
|
|
||||||
explicit response(response_code_t rc) noexcept;
|
explicit response(http_code_t c) noexcept;
|
||||||
response_code_t code() const noexcept;
|
|
||||||
|
bool is_http_error() const noexcept;
|
||||||
|
http_code_t http_code() const noexcept;
|
||||||
public:
|
public:
|
||||||
content_t content;
|
content_t content;
|
||||||
headers_t headers;
|
headers_t headers;
|
||||||
uploader_uptr uploader;
|
uploader_uptr uploader;
|
||||||
downloader_uptr downloader;
|
downloader_uptr downloader;
|
||||||
private:
|
private:
|
||||||
response_code_t code_{0u};
|
http_code_t http_code_{0u};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,12 +142,20 @@ namespace curly_hpp
|
|||||||
bool cancel() noexcept;
|
bool cancel() noexcept;
|
||||||
statuses status() const noexcept;
|
statuses status() const noexcept;
|
||||||
|
|
||||||
|
bool is_done() const noexcept;
|
||||||
|
bool is_pending() const noexcept;
|
||||||
|
|
||||||
statuses wait() const noexcept;
|
statuses wait() const noexcept;
|
||||||
statuses wait_for(time_ms_t ms) const noexcept;
|
statuses wait_for(time_ms_t ms) const noexcept;
|
||||||
statuses wait_until(time_point_t tp) 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();
|
response get();
|
||||||
const std::string& get_error() const noexcept;
|
const std::string& get_error() const noexcept;
|
||||||
|
std::exception_ptr get_callback_exception() const noexcept;
|
||||||
private:
|
private:
|
||||||
internal_state_ptr state_;
|
internal_state_ptr state_;
|
||||||
};
|
};
|
||||||
@@ -176,6 +190,7 @@ namespace curly_hpp
|
|||||||
|
|
||||||
request_builder& content(std::string_view b);
|
request_builder& content(std::string_view b);
|
||||||
request_builder& content(content_t b) noexcept;
|
request_builder& content(content_t b) 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;
|
||||||
|
|
||||||
@@ -193,6 +208,9 @@ namespace curly_hpp
|
|||||||
content_t& content() noexcept;
|
content_t& content() noexcept;
|
||||||
const content_t& content() const noexcept;
|
const content_t& content() const noexcept;
|
||||||
|
|
||||||
|
callback_t& callback() noexcept;
|
||||||
|
const callback_t& callback() const noexcept;
|
||||||
|
|
||||||
uploader_uptr& uploader() noexcept;
|
uploader_uptr& uploader() noexcept;
|
||||||
const uploader_uptr& uploader() const noexcept;
|
const uploader_uptr& uploader() const noexcept;
|
||||||
|
|
||||||
@@ -201,16 +219,22 @@ namespace curly_hpp
|
|||||||
|
|
||||||
request send();
|
request send();
|
||||||
|
|
||||||
|
template < typename Callback >
|
||||||
|
request_builder& callback(Callback&& f) {
|
||||||
|
static_assert(std::is_convertible_v<Callback, callback_t>);
|
||||||
|
return callback(callback_t(std::forward<Callback>(f)));
|
||||||
|
}
|
||||||
|
|
||||||
template < typename Uploader, typename... Args >
|
template < typename Uploader, typename... Args >
|
||||||
request_builder& uploader(Args&&... args) {
|
request_builder& uploader(Args&&... args) {
|
||||||
return uploader(std::make_unique<Uploader>(
|
static_assert(std::is_base_of_v<upload_handler, Uploader>);
|
||||||
std::forward<Args>(args)...));
|
return uploader(std::make_unique<Uploader>(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename Downloader, typename... Args >
|
template < typename Downloader, typename... Args >
|
||||||
request_builder& downloader(Args&&... args) {
|
request_builder& downloader(Args&&... args) {
|
||||||
return downloader(std::make_unique<Downloader>(
|
static_assert(std::is_base_of_v<download_handler, Downloader>);
|
||||||
std::forward<Args>(args)...));
|
return downloader(std::make_unique<Downloader>(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
std::string url_;
|
std::string url_;
|
||||||
@@ -224,6 +248,7 @@ namespace curly_hpp
|
|||||||
time_sec_t connection_timeout_{20u};
|
time_sec_t connection_timeout_{20u};
|
||||||
private:
|
private:
|
||||||
content_t content_;
|
content_t content_;
|
||||||
|
callback_t callback_;
|
||||||
uploader_uptr uploader_;
|
uploader_uptr uploader_;
|
||||||
downloader_uptr downloader_;
|
downloader_uptr downloader_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
|
||||||
#ifndef NOMINMAX
|
#ifndef NOMINMAX
|
||||||
@@ -48,9 +47,8 @@ namespace
|
|||||||
public:
|
public:
|
||||||
using data_t = std::vector<char>;
|
using data_t = std::vector<char>;
|
||||||
|
|
||||||
default_uploader(const data_t* src, std::mutex* m) noexcept
|
default_uploader(const data_t* src) noexcept
|
||||||
: data_(*src)
|
: data_(*src)
|
||||||
, mutex_(*m)
|
|
||||||
, size_(src->size()) {}
|
, size_(src->size()) {}
|
||||||
|
|
||||||
std::size_t size() const override {
|
std::size_t size() const override {
|
||||||
@@ -58,7 +56,6 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::size_t read(char* dst, std::size_t size) override {
|
std::size_t read(char* dst, std::size_t size) override {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
assert(size <= data_.size() - uploaded_);
|
assert(size <= data_.size() - uploaded_);
|
||||||
std::memcpy(dst, data_.data() + uploaded_, size);
|
std::memcpy(dst, data_.data() + uploaded_, size);
|
||||||
uploaded_ += size;
|
uploaded_ += size;
|
||||||
@@ -66,7 +63,6 @@ namespace
|
|||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
const data_t& data_;
|
const data_t& data_;
|
||||||
std::mutex& mutex_;
|
|
||||||
std::size_t uploaded_{0};
|
std::size_t uploaded_{0};
|
||||||
const std::atomic_size_t size_{0};
|
const std::atomic_size_t size_{0};
|
||||||
};
|
};
|
||||||
@@ -75,18 +71,15 @@ namespace
|
|||||||
public:
|
public:
|
||||||
using data_t = std::vector<char>;
|
using data_t = std::vector<char>;
|
||||||
|
|
||||||
default_downloader(data_t* dst, std::mutex* m) noexcept
|
default_downloader(data_t* dst) noexcept
|
||||||
: data_(*dst)
|
: data_(*dst) {}
|
||||||
, mutex_(*m) {}
|
|
||||||
|
|
||||||
std::size_t write(const char* src, std::size_t size) override {
|
std::size_t write(const char* src, std::size_t size) override {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
data_.insert(data_.end(), src, src + size);
|
data_.insert(data_.end(), src, src + size);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
data_t& data_;
|
data_t& data_;
|
||||||
std::mutex& mutex_;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,11 +291,15 @@ namespace curly_hpp
|
|||||||
|
|
||||||
namespace curly_hpp
|
namespace curly_hpp
|
||||||
{
|
{
|
||||||
response::response(response_code_t rc) noexcept
|
response::response(http_code_t c) noexcept
|
||||||
: code_(rc) {}
|
: http_code_(c) {}
|
||||||
|
|
||||||
response_code_t response::code() const noexcept {
|
bool response::is_http_error() const noexcept {
|
||||||
return code_;
|
return http_code_ >= 400u;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_code_t response::http_code() const noexcept {
|
||||||
|
return http_code_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,11 +317,11 @@ namespace curly_hpp
|
|||||||
: breq_(std::move(rb))
|
: breq_(std::move(rb))
|
||||||
{
|
{
|
||||||
if ( !breq_.uploader() ) {
|
if ( !breq_.uploader() ) {
|
||||||
breq_.uploader<default_uploader>(&breq_.content().data(), &mutex_);
|
breq_.uploader<default_uploader>(&breq_.content().data());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !breq_.downloader() ) {
|
if ( !breq_.downloader() ) {
|
||||||
breq_.downloader<default_downloader>(&response_content_, &mutex_);
|
breq_.downloader<default_downloader>(&response_content_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,11 +429,11 @@ namespace curly_hpp
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
long code = 0;
|
long http_code = 0;
|
||||||
if ( CURLE_OK != curl_easy_getinfo(
|
if ( CURLE_OK != curl_easy_getinfo(
|
||||||
curlh_.get(),
|
curlh_.get(),
|
||||||
CURLINFO_RESPONSE_CODE,
|
CURLINFO_RESPONSE_CODE,
|
||||||
&code) || !code )
|
&http_code) || !http_code )
|
||||||
{
|
{
|
||||||
status_ = statuses::failed;
|
status_ = statuses::failed;
|
||||||
cvar_.notify_all();
|
cvar_.notify_all();
|
||||||
@@ -444,7 +441,7 @@ namespace curly_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
response_ = response(static_cast<response_code_t>(code));
|
response_ = response(static_cast<http_code_t>(http_code));
|
||||||
response_.content = std::move(response_content_);
|
response_.content = std::move(response_content_);
|
||||||
response_.headers = std::move(response_headers_);
|
response_.headers = std::move(response_headers_);
|
||||||
response_.uploader = std::move(breq_.uploader());
|
response_.uploader = std::move(breq_.uploader());
|
||||||
@@ -510,26 +507,39 @@ namespace curly_hpp
|
|||||||
return status_;
|
return status_;
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses wait() const noexcept {
|
bool is_done() const noexcept {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
return status_ == statuses::done;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_pending() const noexcept {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
return status_ == statuses::pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
statuses wait(bool wait_callback) const noexcept {
|
||||||
std::unique_lock<std::mutex> lock(mutex_);
|
std::unique_lock<std::mutex> lock(mutex_);
|
||||||
cvar_.wait(lock, [this](){
|
cvar_.wait(lock, [this, wait_callback](){
|
||||||
return status_ != statuses::pending;
|
return (status_ != statuses::pending)
|
||||||
|
&& (!wait_callback || callbacked_);
|
||||||
});
|
});
|
||||||
return status_;
|
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_);
|
std::unique_lock<std::mutex> lock(mutex_);
|
||||||
cvar_.wait_for(lock, ms, [this](){
|
cvar_.wait_for(lock, ms, [this, wait_callback](){
|
||||||
return status_ != statuses::pending;
|
return (status_ != statuses::pending)
|
||||||
|
&& (!wait_callback || callbacked_);
|
||||||
});
|
});
|
||||||
return status_;
|
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_);
|
std::unique_lock<std::mutex> lock(mutex_);
|
||||||
cvar_.wait_until(lock, tp, [this](){
|
cvar_.wait_until(lock, tp, [this, wait_callback](){
|
||||||
return status_ != statuses::pending;
|
return (status_ != statuses::pending)
|
||||||
|
&& (!wait_callback || callbacked_);
|
||||||
});
|
});
|
||||||
return status_;
|
return status_;
|
||||||
}
|
}
|
||||||
@@ -554,6 +564,30 @@ namespace curly_hpp
|
|||||||
return error_;
|
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 {
|
bool check_response_timeout(time_point_t now) const noexcept {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
return now - last_response_ >= response_timeout_;
|
return now - last_response_ >= response_timeout_;
|
||||||
@@ -580,16 +614,15 @@ namespace curly_hpp
|
|||||||
return self->header_callback_(buffer, size * nitems);
|
return self->header_callback_(buffer, size * nitems);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
void response_callback_() noexcept {
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
|
||||||
last_response_ = time_point_t::clock::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t upload_callback_(char* dst, std::size_t size) noexcept {
|
std::size_t upload_callback_(char* dst, std::size_t size) noexcept {
|
||||||
try {
|
try {
|
||||||
size = std::min(size, breq_.uploader()->size() - uploaded_.load());
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
last_response_ = time_point_t::clock::now();
|
||||||
|
|
||||||
|
size = std::min(size, breq_.uploader()->size() - uploaded_);
|
||||||
const std::size_t read_bytes = breq_.uploader()->read(dst, size);
|
const std::size_t read_bytes = breq_.uploader()->read(dst, size);
|
||||||
uploaded_.fetch_add(read_bytes);
|
uploaded_ += read_bytes;
|
||||||
|
|
||||||
return read_bytes;
|
return read_bytes;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return CURL_READFUNC_ABORT;
|
return CURL_READFUNC_ABORT;
|
||||||
@@ -598,8 +631,12 @@ namespace curly_hpp
|
|||||||
|
|
||||||
std::size_t download_callback_(const char* src, std::size_t size) noexcept {
|
std::size_t download_callback_(const char* src, std::size_t size) noexcept {
|
||||||
try {
|
try {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
last_response_ = time_point_t::clock::now();
|
||||||
|
|
||||||
const std::size_t written_bytes = breq_.downloader()->write(src, size);
|
const std::size_t written_bytes = breq_.downloader()->write(src, size);
|
||||||
downloaded_.fetch_add(written_bytes);
|
downloaded_ += written_bytes;
|
||||||
|
|
||||||
return written_bytes;
|
return written_bytes;
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return 0u;
|
return 0u;
|
||||||
@@ -609,6 +646,8 @@ namespace curly_hpp
|
|||||||
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_);
|
||||||
|
last_response_ = time_point_t::clock::now();
|
||||||
|
|
||||||
const std::string_view header(src, size);
|
const std::string_view header(src, size);
|
||||||
if ( !header.compare(0u, 5u, "HTTP/") ) {
|
if ( !header.compare(0u, 5u, "HTTP/") ) {
|
||||||
response_headers_.clear();
|
response_headers_.clear();
|
||||||
@@ -623,6 +662,7 @@ namespace curly_hpp
|
|||||||
response_headers_.emplace(key, val);
|
response_headers_.emplace(key, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return header.size();
|
return header.size();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -639,8 +679,11 @@ namespace curly_hpp
|
|||||||
headers_t response_headers_;
|
headers_t response_headers_;
|
||||||
std::vector<char> response_content_;
|
std::vector<char> response_content_;
|
||||||
private:
|
private:
|
||||||
std::atomic_size_t uploaded_{0u};
|
std::size_t uploaded_{0u};
|
||||||
std::atomic_size_t downloaded_{0u};
|
std::size_t downloaded_{0u};
|
||||||
|
private:
|
||||||
|
bool callbacked_{false};
|
||||||
|
std::exception_ptr callback_exception_{nullptr};
|
||||||
private:
|
private:
|
||||||
statuses status_{statuses::pending};
|
statuses status_{statuses::pending};
|
||||||
std::string error_{"Unknown error"};
|
std::string error_{"Unknown error"};
|
||||||
@@ -665,16 +708,36 @@ namespace curly_hpp
|
|||||||
return state_->status();
|
return state_->status();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool request::is_done() const noexcept {
|
||||||
|
return state_->is_done();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool request::is_pending() const noexcept {
|
||||||
|
return state_->is_pending();
|
||||||
|
}
|
||||||
|
|
||||||
request::statuses request::wait() const noexcept {
|
request::statuses request::wait() const noexcept {
|
||||||
return state_->wait();
|
return state_->wait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
request::statuses request::wait_for(time_ms_t ms) const noexcept {
|
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 {
|
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() {
|
response request::get() {
|
||||||
@@ -684,6 +747,10 @@ namespace curly_hpp
|
|||||||
const std::string& request::get_error() const noexcept {
|
const std::string& request::get_error() const noexcept {
|
||||||
return state_->get_error();
|
return state_->get_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::exception_ptr request::get_callback_exception() const noexcept {
|
||||||
|
return state_->get_callback_exception();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -759,6 +826,11 @@ namespace curly_hpp
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request_builder& request_builder::callback(callback_t c) noexcept {
|
||||||
|
callback_ = std::move(c);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
request_builder& request_builder::uploader(uploader_uptr u) noexcept {
|
request_builder& request_builder::uploader(uploader_uptr u) noexcept {
|
||||||
uploader_ = std::move(u);
|
uploader_ = std::move(u);
|
||||||
return *this;
|
return *this;
|
||||||
@@ -813,6 +885,14 @@ namespace curly_hpp
|
|||||||
return content_;
|
return content_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback_t& request_builder::callback() noexcept {
|
||||||
|
return callback_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const callback_t& request_builder::callback() const noexcept {
|
||||||
|
return callback_;
|
||||||
|
}
|
||||||
|
|
||||||
uploader_uptr& request_builder::uploader() noexcept {
|
uploader_uptr& request_builder::uploader() noexcept {
|
||||||
return uploader_;
|
return uploader_;
|
||||||
}
|
}
|
||||||
@@ -887,6 +967,7 @@ namespace curly_hpp
|
|||||||
} catch (...) {
|
} catch (...) {
|
||||||
sreq->fail(CURLcode::CURLE_FAILED_INIT);
|
sreq->fail(CURLcode::CURLE_FAILED_INIT);
|
||||||
sreq->dequeue(curlm);
|
sreq->dequeue(curlm);
|
||||||
|
sreq->call_callback(sreq);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -927,8 +1008,9 @@ namespace curly_hpp
|
|||||||
|
|
||||||
curl_state::with([](CURLM* curlm){
|
curl_state::with([](CURLM* curlm){
|
||||||
for ( auto iter = active_handles.begin(); iter != active_handles.end(); ) {
|
for ( auto iter = active_handles.begin(); iter != active_handles.end(); ) {
|
||||||
if ( (*iter)->status() != request::statuses::pending ) {
|
if ( !(*iter)->is_pending() ) {
|
||||||
(*iter)->dequeue(curlm);
|
(*iter)->dequeue(curlm);
|
||||||
|
(*iter)->call_callback(*iter);
|
||||||
iter = active_handles.erase(iter);
|
iter = active_handles.erase(iter);
|
||||||
} else {
|
} else {
|
||||||
++iter;
|
++iter;
|
||||||
|
|||||||
@@ -64,3 +64,19 @@ if(NOT tencent_rapidjson_POPULATED)
|
|||||||
target_include_directories(${PROJECT_NAME}
|
target_include_directories(${PROJECT_NAME}
|
||||||
PRIVATE ${tencent_rapidjson_SOURCE_DIR}/include)
|
PRIVATE ${tencent_rapidjson_SOURCE_DIR}/include)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
#
|
||||||
|
# blackmatov/promise.hpp
|
||||||
|
#
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
blackmatov_promise_hpp
|
||||||
|
GIT_REPOSITORY https://github.com/blackmatov/promise.hpp)
|
||||||
|
|
||||||
|
FetchContent_GetProperties(blackmatov_promise_hpp)
|
||||||
|
if(NOT blackmatov_promise_hpp_POPULATED)
|
||||||
|
FetchContent_Populate(blackmatov_promise_hpp)
|
||||||
|
target_include_directories(${PROJECT_NAME}
|
||||||
|
PRIVATE ${blackmatov_promise_hpp_SOURCE_DIR}/headers)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ namespace json = rapidjson;
|
|||||||
#include <curly.hpp/curly.hpp>
|
#include <curly.hpp/curly.hpp>
|
||||||
namespace net = curly_hpp;
|
namespace net = curly_hpp;
|
||||||
|
|
||||||
|
#include <promise.hpp/promise.hpp>
|
||||||
|
namespace netex = promise_hpp;
|
||||||
|
|
||||||
#include "png_data.h"
|
#include "png_data.h"
|
||||||
#include "jpeg_data.h"
|
#include "jpeg_data.h"
|
||||||
|
|
||||||
@@ -54,6 +57,26 @@ namespace
|
|||||||
throw std::exception();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
netex::promise<net::content_t> download(std::string url) {
|
||||||
|
return netex::make_promise<net::content_t>([
|
||||||
|
url = std::move(url)
|
||||||
|
](auto resolve, auto reject){
|
||||||
|
net::request_builder(std::move(url))
|
||||||
|
.callback([resolve,reject](net::request request) mutable {
|
||||||
|
if ( !request.is_done() ) {
|
||||||
|
reject(net::exception("network error"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
net::response response = request.get();
|
||||||
|
if ( response.is_http_error() ) {
|
||||||
|
reject(net::exception("server error"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(std::move(response.content));
|
||||||
|
}).send();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("curly") {
|
TEST_CASE("curly") {
|
||||||
@@ -67,14 +90,14 @@ TEST_CASE("curly") {
|
|||||||
REQUIRE(req.wait() == net::request::statuses::done);
|
REQUIRE(req.wait() == net::request::statuses::done);
|
||||||
REQUIRE(req.status() == net::request::statuses::done);
|
REQUIRE(req.status() == net::request::statuses::done);
|
||||||
auto resp = req.get();
|
auto resp = req.get();
|
||||||
REQUIRE(resp.code() == 200u);
|
REQUIRE(resp.http_code() == 200u);
|
||||||
REQUIRE(req.status() == net::request::statuses::empty);
|
REQUIRE(req.status() == net::request::statuses::empty);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req = net::request_builder("https://httpbin.org/delay/2").send();
|
auto req = net::request_builder("https://httpbin.org/delay/2").send();
|
||||||
REQUIRE(req.wait_for(net::time_sec_t(1)) == net::request::statuses::pending);
|
REQUIRE(req.wait_for(net::time_sec_t(1)) == net::request::statuses::pending);
|
||||||
REQUIRE(req.wait_for(net::time_sec_t(5)) == net::request::statuses::done);
|
REQUIRE(req.wait_for(net::time_sec_t(5)) == net::request::statuses::done);
|
||||||
REQUIRE(req.get().code() == 200u);
|
REQUIRE(req.get().http_code() == 200u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req = net::request_builder("https://httpbin.org/delay/2").send();
|
auto req = net::request_builder("https://httpbin.org/delay/2").send();
|
||||||
@@ -82,7 +105,7 @@ TEST_CASE("curly") {
|
|||||||
== net::request::statuses::pending);
|
== net::request::statuses::pending);
|
||||||
REQUIRE(req.wait_until(net::time_point_t::clock::now() + net::time_sec_t(5))
|
REQUIRE(req.wait_until(net::time_point_t::clock::now() + net::time_sec_t(5))
|
||||||
== net::request::statuses::done);
|
== net::request::statuses::done);
|
||||||
REQUIRE(req.get().code() == 200u);
|
REQUIRE(req.get().http_code() == 200u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,12 +132,37 @@ TEST_CASE("curly") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("is_done/is_pending") {
|
||||||
|
{
|
||||||
|
auto req = net::request_builder(net::methods::get)
|
||||||
|
.url("https://httpbin.org/delay/1")
|
||||||
|
.send();
|
||||||
|
REQUIRE_FALSE(req.is_done());
|
||||||
|
REQUIRE(req.is_pending());
|
||||||
|
req.wait();
|
||||||
|
REQUIRE(req.is_done());
|
||||||
|
REQUIRE_FALSE(req.is_pending());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto req = net::request_builder(net::methods::post, "http://www.httpbin.org/post")
|
||||||
|
.url("https://httpbin.org/delay/2")
|
||||||
|
.request_timeout(net::time_sec_t(1))
|
||||||
|
.send();
|
||||||
|
REQUIRE_FALSE(req.is_done());
|
||||||
|
REQUIRE(req.is_pending());
|
||||||
|
req.wait();
|
||||||
|
REQUIRE_FALSE(req.is_done());
|
||||||
|
REQUIRE_FALSE(req.is_pending());
|
||||||
|
REQUIRE(!req.get_error().empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("get") {
|
SECTION("get") {
|
||||||
{
|
{
|
||||||
auto req = net::request_builder("https://httpbin.org/status/204").send();
|
auto req = net::request_builder("https://httpbin.org/status/204").send();
|
||||||
auto resp = req.get();
|
auto resp = req.get();
|
||||||
REQUIRE(req.status() == net::request::statuses::empty);
|
REQUIRE(req.status() == net::request::statuses::empty);
|
||||||
REQUIRE(resp.code() == 204u);
|
REQUIRE(resp.http_code() == 204u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req = net::request_builder("https://httpbin.org/delay/2").send();
|
auto req = net::request_builder("https://httpbin.org/delay/2").send();
|
||||||
@@ -138,75 +186,75 @@ TEST_CASE("curly") {
|
|||||||
.url("https://httpbin.org/put")
|
.url("https://httpbin.org/put")
|
||||||
.method(net::methods::put)
|
.method(net::methods::put)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req0.get().code() == 200u);
|
REQUIRE(req0.get().http_code() == 200u);
|
||||||
|
|
||||||
auto req1 = net::request_builder()
|
auto req1 = net::request_builder()
|
||||||
.url("https://httpbin.org/put")
|
.url("https://httpbin.org/put")
|
||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req1.get().code() == 405u);
|
REQUIRE(req1.get().http_code() == 405u);
|
||||||
|
|
||||||
auto req2 = net::request_builder()
|
auto req2 = net::request_builder()
|
||||||
.url("https://httpbin.org/put")
|
.url("https://httpbin.org/put")
|
||||||
.method(net::methods::head)
|
.method(net::methods::head)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req2.get().code() == 405u);
|
REQUIRE(req2.get().http_code() == 405u);
|
||||||
|
|
||||||
auto req3 = net::request_builder()
|
auto req3 = net::request_builder()
|
||||||
.url("https://httpbin.org/put")
|
.url("https://httpbin.org/put")
|
||||||
.method(net::methods::post)
|
.method(net::methods::post)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req3.get().code() == 405u);
|
REQUIRE(req3.get().http_code() == 405u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req0 = net::request_builder()
|
auto req0 = net::request_builder()
|
||||||
.url("https://httpbin.org/get")
|
.url("https://httpbin.org/get")
|
||||||
.method(net::methods::put)
|
.method(net::methods::put)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req0.get().code() == 405u);
|
REQUIRE(req0.get().http_code() == 405u);
|
||||||
|
|
||||||
auto req1 = net::request_builder()
|
auto req1 = net::request_builder()
|
||||||
.url("https://httpbin.org/get")
|
.url("https://httpbin.org/get")
|
||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req1.get().code() == 200u);
|
REQUIRE(req1.get().http_code() == 200u);
|
||||||
|
|
||||||
auto req2 = net::request_builder()
|
auto req2 = net::request_builder()
|
||||||
.url("https://httpbin.org/get")
|
.url("https://httpbin.org/get")
|
||||||
.method(net::methods::head)
|
.method(net::methods::head)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req2.get().code() == 200u);
|
REQUIRE(req2.get().http_code() == 200u);
|
||||||
|
|
||||||
auto req3 = net::request_builder()
|
auto req3 = net::request_builder()
|
||||||
.url("https://httpbin.org/get")
|
.url("https://httpbin.org/get")
|
||||||
.method(net::methods::post)
|
.method(net::methods::post)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req3.get().code() == 405u);
|
REQUIRE(req3.get().http_code() == 405u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req0 = net::request_builder()
|
auto req0 = net::request_builder()
|
||||||
.url("https://httpbin.org/post")
|
.url("https://httpbin.org/post")
|
||||||
.method(net::methods::put)
|
.method(net::methods::put)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req0.get().code() == 405u);
|
REQUIRE(req0.get().http_code() == 405u);
|
||||||
|
|
||||||
auto req1 = net::request_builder()
|
auto req1 = net::request_builder()
|
||||||
.url("https://httpbin.org/post")
|
.url("https://httpbin.org/post")
|
||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req1.get().code() == 405u);
|
REQUIRE(req1.get().http_code() == 405u);
|
||||||
|
|
||||||
auto req2 = net::request_builder()
|
auto req2 = net::request_builder()
|
||||||
.url("https://httpbin.org/post")
|
.url("https://httpbin.org/post")
|
||||||
.method(net::methods::head)
|
.method(net::methods::head)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req2.get().code() == 405u);
|
REQUIRE(req2.get().http_code() == 405u);
|
||||||
|
|
||||||
auto req3 = net::request_builder()
|
auto req3 = net::request_builder()
|
||||||
.url("https://httpbin.org/post")
|
.url("https://httpbin.org/post")
|
||||||
.method(net::methods::post)
|
.method(net::methods::post)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req3.get().code() == 200u);
|
REQUIRE(req3.get().http_code() == 200u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,28 +264,28 @@ TEST_CASE("curly") {
|
|||||||
.url("https://httpbin.org/status/200")
|
.url("https://httpbin.org/status/200")
|
||||||
.method(net::methods::put)
|
.method(net::methods::put)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req.get().code() == 200u);
|
REQUIRE(req.get().http_code() == 200u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req = net::request_builder()
|
auto req = net::request_builder()
|
||||||
.url("https://httpbin.org/status/201")
|
.url("https://httpbin.org/status/201")
|
||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req.get().code() == 201u);
|
REQUIRE(req.get().http_code() == 201u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req = net::request_builder()
|
auto req = net::request_builder()
|
||||||
.url("https://httpbin.org/status/202")
|
.url("https://httpbin.org/status/202")
|
||||||
.method(net::methods::head)
|
.method(net::methods::head)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req.get().code() == 202u);
|
REQUIRE(req.get().http_code() == 202u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req = net::request_builder()
|
auto req = net::request_builder()
|
||||||
.url("https://httpbin.org/status/203")
|
.url("https://httpbin.org/status/203")
|
||||||
.method(net::methods::post)
|
.method(net::methods::post)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req.get().code() == 203u);
|
REQUIRE(req.get().http_code() == 203u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +373,7 @@ TEST_CASE("curly") {
|
|||||||
.url("https://httpbin.org/image/png")
|
.url("https://httpbin.org/image/png")
|
||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.send().get();
|
.send().get();
|
||||||
REQUIRE(resp.code() == 200u);
|
REQUIRE(resp.http_code() == 200u);
|
||||||
REQUIRE(resp.headers.count("Content-Type"));
|
REQUIRE(resp.headers.count("Content-Type"));
|
||||||
REQUIRE(resp.headers.at("Content-Type") == "image/png");
|
REQUIRE(resp.headers.at("Content-Type") == "image/png");
|
||||||
REQUIRE(untests::png_data_length == resp.content.size());
|
REQUIRE(untests::png_data_length == resp.content.size());
|
||||||
@@ -338,7 +386,7 @@ TEST_CASE("curly") {
|
|||||||
.url("https://httpbin.org/image/jpeg")
|
.url("https://httpbin.org/image/jpeg")
|
||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.send().get();
|
.send().get();
|
||||||
REQUIRE(resp.code() == 200u);
|
REQUIRE(resp.http_code() == 200u);
|
||||||
REQUIRE(resp.headers.count("Content-Type"));
|
REQUIRE(resp.headers.count("Content-Type"));
|
||||||
REQUIRE(resp.headers.at("Content-Type") == "image/jpeg");
|
REQUIRE(resp.headers.at("Content-Type") == "image/jpeg");
|
||||||
REQUIRE(untests::jpeg_data_length == resp.content.size());
|
REQUIRE(untests::jpeg_data_length == resp.content.size());
|
||||||
@@ -355,21 +403,21 @@ TEST_CASE("curly") {
|
|||||||
.url("https://httpbin.org/redirect/2")
|
.url("https://httpbin.org/redirect/2")
|
||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req.get().code() == 200u);
|
REQUIRE(req.get().http_code() == 200u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req = net::request_builder()
|
auto req = net::request_builder()
|
||||||
.url("https://httpbin.org/absolute-redirect/2")
|
.url("https://httpbin.org/absolute-redirect/2")
|
||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req.get().code() == 200u);
|
REQUIRE(req.get().http_code() == 200u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req = net::request_builder()
|
auto req = net::request_builder()
|
||||||
.url("https://httpbin.org/relative-redirect/2")
|
.url("https://httpbin.org/relative-redirect/2")
|
||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req.get().code() == 200u);
|
REQUIRE(req.get().http_code() == 200u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -379,7 +427,7 @@ TEST_CASE("curly") {
|
|||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.redirections(0)
|
.redirections(0)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req.get().code() == 302u);
|
REQUIRE(req.get().http_code() == 302u);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto req = net::request_builder()
|
auto req = net::request_builder()
|
||||||
@@ -403,7 +451,7 @@ TEST_CASE("curly") {
|
|||||||
.method(net::methods::get)
|
.method(net::methods::get)
|
||||||
.redirections(3)
|
.redirections(3)
|
||||||
.send();
|
.send();
|
||||||
REQUIRE(req.get().code() == 200u);
|
REQUIRE(req.get().http_code() == 200u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -513,6 +561,84 @@ TEST_CASE("curly") {
|
|||||||
REQUIRE(req.wait() == net::request::statuses::canceled);
|
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") {
|
TEST_CASE("curly_examples") {
|
||||||
@@ -529,7 +655,7 @@ TEST_CASE("curly_examples") {
|
|||||||
auto response = request.get();
|
auto response = request.get();
|
||||||
|
|
||||||
// prints results
|
// prints results
|
||||||
std::cout << "Status code: " << response.code() << std::endl;
|
std::cout << "Status code: " << response.http_code() << std::endl;
|
||||||
std::cout << "Content type: " << response.headers["content-type"] << std::endl;
|
std::cout << "Content type: " << response.headers["content-type"] << std::endl;
|
||||||
std::cout << "Body content: " << response.content.as_string_view() << std::endl;
|
std::cout << "Body content: " << response.content.as_string_view() << std::endl;
|
||||||
|
|
||||||
@@ -585,9 +711,11 @@ TEST_CASE("curly_examples") {
|
|||||||
.url("http://unavailable.site.com")
|
.url("http://unavailable.site.com")
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
if ( request.wait() == net::request::statuses::done ) {
|
request.wait();
|
||||||
|
|
||||||
|
if ( request.is_done() ) {
|
||||||
auto response = request.get();
|
auto response = request.get();
|
||||||
std::cout << "Status code: " << response.code() << std::endl;
|
std::cout << "Status code: " << response.http_code() << std::endl;
|
||||||
} else {
|
} else {
|
||||||
// throws net::exception because a response is unavailable
|
// throws net::exception because a response is unavailable
|
||||||
// auto response = request.get();
|
// auto response = request.get();
|
||||||
@@ -598,6 +726,21 @@ TEST_CASE("curly_examples") {
|
|||||||
// Error message: Couldn't resolve host name
|
// Error message: Couldn't resolve host name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("Request Callbacks") {
|
||||||
|
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") {
|
SECTION("Streamed Requests") {
|
||||||
{
|
{
|
||||||
class file_dowloader : public net::download_handler {
|
class file_dowloader : public net::download_handler {
|
||||||
@@ -648,4 +791,20 @@ TEST_CASE("curly_examples") {
|
|||||||
.send().get();
|
.send().get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("Promised Requests") {
|
||||||
|
auto promise = download("https://httpbin.org/image/png")
|
||||||
|
.then([](const net::content_t& content){
|
||||||
|
std::cout << content.size() << " bytes downloaded" << std::endl;
|
||||||
|
}).except([](std::exception_ptr e){
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(e);
|
||||||
|
} catch (const std::exception& ee) {
|
||||||
|
std::cerr << "Failed to download: " << ee.what() << std::endl;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
promise.wait();
|
||||||
|
// 8090 bytes downloaded
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user