Merge pull request #14 from BlackMATov/dev

Dev
This commit is contained in:
2019-07-07 15:39:37 +07:00
committed by GitHub
5 changed files with 621 additions and 358 deletions

View File

@@ -51,9 +51,22 @@ endif()
# library # library
# #
file(GLOB_RECURSE CURLY_HPP_HEADERS
headers/curly.hpp/*.hpp
headers/curly.hpp/*.inl)
file(GLOB_RECURSE CURLY_HPP_SOURCES
sources/curly.hpp/*.cpp
sources/curly.hpp/*.hpp
sources/curly.hpp/*.inl)
add_library(${PROJECT_NAME} STATIC add_library(${PROJECT_NAME} STATIC
headers/curly.hpp/curly.hpp ${CURLY_HPP_HEADERS}
sources/curly.cpp) ${CURLY_HPP_SOURCES})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES
${CURLY_HPP_HEADERS}
${CURLY_HPP_SOURCES})
target_include_directories(${PROJECT_NAME} target_include_directories(${PROJECT_NAME}
PUBLIC headers) PUBLIC headers)

View File

@@ -56,7 +56,7 @@ target_link_libraries(your_project_target curly.hpp)
namespace net = curly_hpp; namespace net = curly_hpp;
// creates and hold a separate thread for automatically update async requests // creates and hold a separate thread for automatically update async requests
net::auto_performer performer; net::performer performer;
// also, you can update requests manually from your favorite thread // also, you can update requests manually from your favorite thread
net::perform(); net::perform();
@@ -67,12 +67,12 @@ net::perform();
```cpp ```cpp
// makes a GET request and async send it // makes a GET request and async send it
auto request = net::request_builder() auto request = net::request_builder()
.method(net::methods::get) .method(net::http_method::GET)
.url("http://www.httpbin.org/get") .url("http://www.httpbin.org/get")
.send(); .send();
// synchronous waits and get a response // synchronous waits and take a response
auto response = request.get(); auto response = request.take();
// prints results // prints results
std::cout << "Status code: " << response.http_code() << std::endl; std::cout << "Status code: " << response.http_code() << std::endl;
@@ -97,13 +97,13 @@ std::cout << "Body content: " << response.content.as_string_view() << std::endl;
```cpp ```cpp
auto request = net::request_builder() auto request = net::request_builder()
.method(net::methods::post) .method(net::http_method::POST)
.url("http://www.httpbin.org/post") .url("http://www.httpbin.org/post")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.content(R"({"hello" : "world"})") .content(R"({"hello" : "world"})")
.send(); .send();
auto response = request.get(); auto response = request.take();
std::cout << "Body content: " << response.content.as_string_view() << std::endl; std::cout << "Body content: " << response.content.as_string_view() << std::endl;
std::cout << "Content Length: " << response.headers["content-length"] << std::endl; std::cout << "Content Length: " << response.headers["content-length"] << std::endl;
@@ -138,11 +138,11 @@ auto request = net::request_builder()
request.wait(); request.wait();
if ( request.is_done() ) { if ( request.is_done() ) {
auto response = request.get(); auto response = request.take();
std::cout << "Status code: " << response.http_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.take();
std::cout << "Error message: " << request.get_error() << std::endl; std::cout << "Error message: " << request.get_error() << std::endl;
} }
@@ -156,7 +156,7 @@ if ( request.is_done() ) {
auto request = net::request_builder("http://www.httpbin.org/get") auto request = net::request_builder("http://www.httpbin.org/get")
.callback([](net::request request){ .callback([](net::request request){
if ( request.is_done() ) { if ( request.is_done() ) {
auto response = request.get(); auto response = request.take();
std::cout << "Status code: " << response.http_code() << std::endl; std::cout << "Status code: " << response.http_code() << std::endl;
} else { } else {
std::cout << "Error message: " << request.get_error() << std::endl; std::cout << "Error message: " << request.get_error() << std::endl;
@@ -217,7 +217,7 @@ private:
}; };
net::request_builder() net::request_builder()
.method(net::methods::post) .method(net::http_method::POST)
.url("https://httpbin.org/anything") .url("https://httpbin.org/anything")
.uploader<file_uploader>("image.jpeg") .uploader<file_uploader>("image.jpeg")
.send().wait(); .send().wait();

View File

@@ -6,51 +6,48 @@
#pragma once #pragma once
#include <cctype>
#include <cassert>
#include <cstring>
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#include <atomic> #include <atomic>
#include <chrono>
#include <thread> #include <thread>
#include <map>
#include <chrono>
#include <memory> #include <memory>
#include <string> #include <utility>
#include <vector> #include <algorithm>
#include <stdexcept> #include <stdexcept>
#include <functional> #include <functional>
#include <map>
#include <vector>
#include <string>
namespace curly_hpp namespace curly_hpp
{ {
class exception final : public std::runtime_error { class request;
public: class response;
explicit exception(const char* what); class request_builder;
explicit exception(const std::string& what);
};
struct icase_string_compare final {
using is_transparent = void;
bool operator()(
std::string_view l,
std::string_view r) const noexcept;
};
enum class methods {
put,
get,
head,
post
};
using http_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 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; enum class http_method {
using callback_t = std::function<void(request)>; DEL,
PUT,
GET,
HEAD,
POST,
PATCH,
OPTIONS
};
class upload_handler { class upload_handler {
public: public:
@@ -65,8 +62,51 @@ 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 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
{
class exception final : public std::runtime_error {
public:
explicit exception(const char* what)
: std::runtime_error(what) {}
explicit exception(const std::string& what)
: std::runtime_error(what) {}
};
}
namespace curly_hpp
{
namespace detail
{
struct icase_string_compare final {
using is_transparent = void;
bool operator()(std::string_view l, std::string_view r) const noexcept {
return std::lexicographical_compare(
l.begin(), l.end(), r.begin(), r.end(),
[](const char lc, const char rc) noexcept {
return std::tolower(lc) < std::tolower(rc);
});
}
};
}
using headers_t = std::map<
std::string, std::string,
detail::icase_string_compare>;
} }
namespace curly_hpp namespace curly_hpp
@@ -81,15 +121,31 @@ namespace curly_hpp
content_t(const content_t&) = default; content_t(const content_t&) = default;
content_t& operator=(const content_t&) = default; content_t& operator=(const content_t&) = default;
content_t(std::string_view data); content_t(std::string_view data)
content_t(std::vector<char> data) noexcept; : data_(data.cbegin(), data.cend() ) {}
std::size_t size() const noexcept; content_t(std::vector<char> data) noexcept
std::vector<char>& data() noexcept; : data_(std::move(data)) {}
const std::vector<char>& data() const noexcept;
std::string as_string_copy() const; std::size_t size() const noexcept {
std::string_view as_string_view() const noexcept; return data_.size();
}
std::vector<char>& data() noexcept {
return data_;
}
const std::vector<char>& data() const noexcept {
return data_;
}
std::string as_string_copy() const {
return {data_.data(), data_.size()};
}
std::string_view as_string_view() const noexcept {
return {data_.data(), data_.size()};
}
private: private:
std::vector<char> data_; std::vector<char> data_;
}; };
@@ -107,15 +163,22 @@ namespace curly_hpp
response(const response&) = delete; response(const response&) = delete;
response& operator=(const response&) = delete; response& operator=(const response&) = delete;
explicit response(http_code_t c) noexcept; explicit response(http_code_t c) noexcept
: http_code_(c) {}
bool is_http_error() const noexcept; bool is_http_error() const noexcept {
http_code_t http_code() const noexcept; return http_code_ >= 400u;
}
http_code_t http_code() const noexcept {
return http_code_;
}
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;
progressor_uptr progressor;
private: private:
http_code_t http_code_{0u}; http_code_t http_code_{0u};
}; };
@@ -123,16 +186,16 @@ namespace curly_hpp
namespace curly_hpp namespace curly_hpp
{ {
enum class req_status {
done,
empty,
failed,
timeout,
pending,
canceled
};
class request final { class request final {
public:
enum class statuses {
done,
empty,
failed,
timeout,
pending,
canceled
};
public: public:
class internal_state; class internal_state;
using internal_state_ptr = std::shared_ptr<internal_state>; using internal_state_ptr = std::shared_ptr<internal_state>;
@@ -140,20 +203,21 @@ namespace curly_hpp
request(internal_state_ptr); request(internal_state_ptr);
bool cancel() noexcept; bool cancel() noexcept;
statuses status() const noexcept; float progress() const noexcept;
req_status status() const noexcept;
bool is_done() const noexcept; bool is_done() const noexcept;
bool is_pending() const noexcept; bool is_pending() const noexcept;
statuses wait() const noexcept; req_status wait() const noexcept;
statuses wait_for(time_ms_t ms) const noexcept; req_status wait_for(time_ms_t ms) const noexcept;
statuses wait_until(time_point_t tp) const noexcept; req_status wait_until(time_point_t tp) const noexcept;
statuses wait_callback() const noexcept; req_status wait_callback() const noexcept;
statuses wait_callback_for(time_ms_t ms) const noexcept; req_status wait_callback_for(time_ms_t ms) const noexcept;
statuses wait_callback_until(time_point_t tp) const noexcept; req_status wait_callback_until(time_point_t tp) const noexcept;
response get(); response take();
const std::string& get_error() const noexcept; const std::string& get_error() const noexcept;
std::exception_ptr get_callback_exception() const noexcept; std::exception_ptr get_callback_exception() const noexcept;
private: private:
@@ -173,12 +237,12 @@ namespace curly_hpp
request_builder(const request_builder&) = delete; request_builder(const request_builder&) = delete;
request_builder& operator=(const request_builder&) = delete; request_builder& operator=(const request_builder&) = delete;
explicit request_builder(methods m) noexcept; explicit request_builder(http_method m) noexcept;
explicit request_builder(std::string u) noexcept; explicit request_builder(std::string u) noexcept;
explicit request_builder(methods m, std::string u) noexcept; explicit request_builder(http_method m, std::string u) noexcept;
request_builder& url(std::string u) noexcept; request_builder& url(std::string u) noexcept;
request_builder& method(methods m) noexcept; request_builder& method(http_method m) noexcept;
request_builder& header(std::string key, std::string value); request_builder& header(std::string key, std::string value);
request_builder& verbose(bool v) noexcept; request_builder& verbose(bool v) noexcept;
@@ -193,9 +257,10 @@ 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;
methods method() const noexcept; http_method method() const noexcept;
const headers_t& headers() const noexcept; const headers_t& headers() const noexcept;
bool verbose() const noexcept; bool verbose() const noexcept;
@@ -217,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 >
@@ -236,9 +304,15 @@ 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_;
methods method_{methods::get}; http_method method_{http_method::GET};
headers_t headers_; headers_t headers_;
bool verbose_{false}; bool verbose_{false};
bool verification_{false}; bool verification_{false};
@@ -251,15 +325,16 @@ namespace curly_hpp
callback_t callback_; callback_t callback_;
uploader_uptr uploader_; uploader_uptr uploader_;
downloader_uptr downloader_; downloader_uptr downloader_;
progressor_uptr progressor_;
}; };
} }
namespace curly_hpp namespace curly_hpp
{ {
class auto_performer final { class performer final {
public: public:
auto_performer(); performer();
~auto_performer() noexcept; ~performer() noexcept;
time_ms_t wait_activity() const noexcept; time_ms_t wait_activity() const noexcept;
void wait_activity(time_ms_t ms) noexcept; void wait_activity(time_ms_t ms) noexcept;

View File

@@ -6,13 +6,9 @@
#include <curly.hpp/curly.hpp> #include <curly.hpp/curly.hpp>
#include <cctype>
#include <cassert>
#include <cstring>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <algorithm> #include <type_traits>
#include <condition_variable> #include <condition_variable>
#ifndef NOMINMAX #ifndef NOMINMAX
@@ -81,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));
}
};
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -96,18 +109,26 @@ namespace
template < typename T > template < typename T >
class mt_queue final { class mt_queue final {
public: public:
void enqueue(T v) { void enqueue(T&& v) {
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard<std::mutex> guard(mutex_);
queue_.push(std::move(v)); queue_.push(std::move(v));
cvar_.notify_all(); cvar_.notify_all();
} }
bool try_dequeue(T& v) { void enqueue(const T& v) {
std::lock_guard<std::mutex> guard(mutex_);
queue_.push(v);
cvar_.notify_all();
}
bool try_dequeue(T& v) noexcept(
std::is_nothrow_move_assignable_v<T>)
{
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard<std::mutex> guard(mutex_);
if ( queue_.empty() ) { if ( queue_.empty() ) {
return false; return false;
} }
v = queue_.front(); v = std::move(queue_.front());
queue_.pop(); queue_.pop();
return true; return true;
} }
@@ -117,9 +138,25 @@ namespace
return queue_.empty(); return queue_.empty();
} }
bool wait_content_for(time_ms_t ms) const noexcept { void wait() const noexcept {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
return cvar_.wait_for(lock, ms, [this](){ cvar_.wait(lock, [this](){
return !queue_.empty();
});
}
template < typename Rep, typename Period >
bool wait_for(const std::chrono::duration<Rep, Period> duration) const noexcept {
std::unique_lock<std::mutex> lock(mutex_);
return cvar_.wait_for(lock, duration, [this](){
return !queue_.empty();
});
}
template < typename Clock, typename Duration >
bool wait_until(const std::chrono::time_point<Clock, Duration>& time) const {
std::unique_lock<std::mutex> lock(mutex_);
return cvar_.wait_until(lock, time, [this](){
return !queue_.empty(); return !queue_.empty();
}); });
} }
@@ -213,96 +250,6 @@ namespace
std::unique_ptr<curl_state> curl_state::self_; std::unique_ptr<curl_state> curl_state::self_;
} }
// -----------------------------------------------------------------------------
//
// exception
//
// -----------------------------------------------------------------------------
namespace curly_hpp
{
exception::exception(const char* what)
: std::runtime_error(what) {}
exception::exception(const std::string& what)
: std::runtime_error(what) {}
}
// -----------------------------------------------------------------------------
//
// icase_string_compare
//
// -----------------------------------------------------------------------------
namespace curly_hpp
{
bool icase_string_compare::operator()(
std::string_view l,
std::string_view r) const noexcept
{
return std::lexicographical_compare(
l.begin(), l.end(), r.begin(), r.end(),
[](const auto lc, const auto rc) noexcept {
return std::tolower(lc) < std::tolower(rc);
});
}
}
// -----------------------------------------------------------------------------
//
// content_t
//
// -----------------------------------------------------------------------------
namespace curly_hpp
{
content_t::content_t(std::string_view data)
: data_(data.cbegin(), data.cend() ) {}
content_t::content_t(std::vector<char> data) noexcept
: data_(std::move(data)) {}
std::size_t content_t::size() const noexcept {
return data_.size();
}
std::vector<char>& content_t::data() noexcept {
return data_;
}
const std::vector<char>& content_t::data() const noexcept {
return data_;
}
std::string content_t::as_string_copy() const {
return {data_.data(), data_.size()};
}
std::string_view content_t::as_string_view() const noexcept {
return {data_.data(), data_.size()};
}
}
// -----------------------------------------------------------------------------
//
// response
//
// -----------------------------------------------------------------------------
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_;
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// request // request
@@ -323,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) {
@@ -357,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_);
@@ -365,22 +320,38 @@ namespace curly_hpp
curl_easy_setopt(curlh_.get(), CURLOPT_VERBOSE, breq_.verbose() ? 1l : 0l); curl_easy_setopt(curlh_.get(), CURLOPT_VERBOSE, breq_.verbose() ? 1l : 0l);
switch ( breq_.method() ) { switch ( breq_.method() ) {
case methods::put: case http_method::DEL:
curl_easy_setopt(curlh_.get(), CURLOPT_CUSTOMREQUEST, "DELETE");
curl_easy_setopt(curlh_.get(), CURLOPT_POST, 1l);
curl_easy_setopt(curlh_.get(), CURLOPT_POSTFIELDSIZE_LARGE,
static_cast<curl_off_t>(breq_.uploader()->size()));
break;
case http_method::PUT:
curl_easy_setopt(curlh_.get(), CURLOPT_UPLOAD, 1l); curl_easy_setopt(curlh_.get(), CURLOPT_UPLOAD, 1l);
curl_easy_setopt(curlh_.get(), CURLOPT_INFILESIZE_LARGE, curl_easy_setopt(curlh_.get(), CURLOPT_INFILESIZE_LARGE,
static_cast<curl_off_t>(breq_.uploader()->size())); static_cast<curl_off_t>(breq_.uploader()->size()));
break; break;
case methods::get: case http_method::GET:
curl_easy_setopt(curlh_.get(), CURLOPT_HTTPGET, 1l); curl_easy_setopt(curlh_.get(), CURLOPT_HTTPGET, 1l);
break; break;
case methods::head: case http_method::HEAD:
curl_easy_setopt(curlh_.get(), CURLOPT_NOBODY, 1l); curl_easy_setopt(curlh_.get(), CURLOPT_NOBODY, 1l);
break; break;
case methods::post: case http_method::POST:
curl_easy_setopt(curlh_.get(), CURLOPT_POST, 1l); curl_easy_setopt(curlh_.get(), CURLOPT_POST, 1l);
curl_easy_setopt(curlh_.get(), CURLOPT_POSTFIELDSIZE_LARGE, curl_easy_setopt(curlh_.get(), CURLOPT_POSTFIELDSIZE_LARGE,
static_cast<curl_off_t>(breq_.uploader()->size())); static_cast<curl_off_t>(breq_.uploader()->size()));
break; break;
case http_method::PATCH:
curl_easy_setopt(curlh_.get(), CURLOPT_CUSTOMREQUEST, "PATCH");
curl_easy_setopt(curlh_.get(), CURLOPT_UPLOAD, 1l);
curl_easy_setopt(curlh_.get(), CURLOPT_INFILESIZE_LARGE,
static_cast<curl_off_t>(breq_.uploader()->size()));
break;
case http_method::OPTIONS:
curl_easy_setopt(curlh_.get(), CURLOPT_CUSTOMREQUEST, "OPTIONS");
curl_easy_setopt(curlh_.get(), CURLOPT_NOBODY, 1l);
break;
default: default:
throw exception("curly_hpp: unexpected request method"); throw exception("curly_hpp: unexpected request method");
} }
@@ -418,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();
} }
@@ -425,7 +411,7 @@ namespace curly_hpp
bool done() noexcept { bool done() noexcept {
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard<std::mutex> guard(mutex_);
if ( status_ != statuses::pending ) { if ( status_ != req_status::pending ) {
return false; return false;
} }
@@ -435,7 +421,7 @@ namespace curly_hpp
CURLINFO_RESPONSE_CODE, CURLINFO_RESPONSE_CODE,
&http_code) || !http_code ) &http_code) || !http_code )
{ {
status_ = statuses::failed; status_ = req_status::failed;
cvar_.notify_all(); cvar_.notify_all();
return false; return false;
} }
@@ -446,13 +432,15 @@ 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_ = statuses::failed; status_ = req_status::failed;
cvar_.notify_all(); cvar_.notify_all();
return false; return false;
} }
status_ = statuses::done; progress_ = 1.f;
status_ = req_status::done;
error_.clear(); error_.clear();
cvar_.notify_all(); cvar_.notify_all();
@@ -461,21 +449,21 @@ namespace curly_hpp
bool fail(CURLcode err) noexcept { bool fail(CURLcode err) noexcept {
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard<std::mutex> guard(mutex_);
if ( status_ != statuses::pending ) { if ( status_ != req_status::pending ) {
return false; return false;
} }
switch ( err ) { switch ( err ) {
case CURLE_OPERATION_TIMEDOUT: case CURLE_OPERATION_TIMEDOUT:
status_ = statuses::timeout; status_ = req_status::timeout;
break; break;
case CURLE_READ_ERROR: case CURLE_READ_ERROR:
case CURLE_WRITE_ERROR: case CURLE_WRITE_ERROR:
case CURLE_ABORTED_BY_CALLBACK: case CURLE_ABORTED_BY_CALLBACK:
status_ = statuses::canceled; status_ = req_status::canceled;
break; break;
default: default:
status_ = statuses::failed; status_ = req_status::failed;
break; break;
} }
@@ -491,75 +479,80 @@ namespace curly_hpp
bool cancel() noexcept { bool cancel() noexcept {
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard<std::mutex> guard(mutex_);
if ( status_ != statuses::pending ) { if ( status_ != req_status::pending ) {
return false; return false;
} }
status_ = statuses::canceled; status_ = req_status::canceled;
error_.clear(); error_.clear();
cvar_.notify_all(); cvar_.notify_all();
return true; return true;
} }
statuses status() const noexcept { 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_); std::lock_guard<std::mutex> guard(mutex_);
return status_; return status_;
} }
bool is_done() const noexcept { bool is_done() const noexcept {
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard<std::mutex> guard(mutex_);
return status_ == statuses::done; return status_ == req_status::done;
} }
bool is_pending() const noexcept { bool is_pending() const noexcept {
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard<std::mutex> guard(mutex_);
return status_ == statuses::pending; return status_ == req_status::pending;
} }
statuses wait(bool wait_callback) const noexcept { req_status wait(bool wait_callback) const noexcept {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
cvar_.wait(lock, [this, wait_callback](){ cvar_.wait(lock, [this, wait_callback](){
return (status_ != statuses::pending) return (status_ != req_status::pending)
&& (!wait_callback || callbacked_); && (!wait_callback || callbacked_);
}); });
return status_; return status_;
} }
statuses wait_for(time_ms_t ms, bool wait_callback) const noexcept { req_status 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, wait_callback](){ cvar_.wait_for(lock, ms, [this, wait_callback](){
return (status_ != statuses::pending) return (status_ != req_status::pending)
&& (!wait_callback || callbacked_); && (!wait_callback || callbacked_);
}); });
return status_; return status_;
} }
statuses wait_until(time_point_t tp, bool wait_callback) const noexcept { req_status 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, wait_callback](){ cvar_.wait_until(lock, tp, [this, wait_callback](){
return (status_ != statuses::pending) return (status_ != req_status::pending)
&& (!wait_callback || callbacked_); && (!wait_callback || callbacked_);
}); });
return status_; return status_;
} }
response get() { response take() {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
cvar_.wait(lock, [this](){ cvar_.wait(lock, [this](){
return status_ != statuses::pending; return status_ != req_status::pending;
}); });
if ( status_ != statuses::done ) { if ( status_ != req_status::done ) {
throw exception("curly_hpp: response is unavailable"); throw exception("curly_hpp: response is unavailable");
} }
status_ = statuses::empty; status_ = req_status::empty;
return std::move(response_); return std::move(response_);
} }
const std::string& get_error() const noexcept { const std::string& get_error() const noexcept {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
cvar_.wait(lock, [this](){ cvar_.wait(lock, [this](){
return status_ != statuses::pending; return status_ != req_status::pending;
}); });
return error_; return error_;
} }
@@ -583,7 +576,7 @@ namespace curly_hpp
callback_exception_ = std::current_exception(); callback_exception_ = std::current_exception();
} }
std::lock_guard<std::mutex> guard(mutex_); std::lock_guard<std::mutex> guard(mutex_);
assert(!callbacked_ && status_ != statuses::pending); assert(!callbacked_ && status_ != req_status::pending);
callbacked_ = true; callbacked_ = true;
cvar_.notify_all(); cvar_.notify_all();
} }
@@ -607,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
{ {
@@ -643,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_);
@@ -685,7 +705,8 @@ namespace curly_hpp
bool callbacked_{false}; bool callbacked_{false};
std::exception_ptr callback_exception_{nullptr}; std::exception_ptr callback_exception_{nullptr};
private: private:
statuses status_{statuses::pending}; float progress_{0.f};
req_status status_{req_status::pending};
std::string error_{"Unknown error"}; std::string error_{"Unknown error"};
private: private:
mutable std::mutex mutex_; mutable std::mutex mutex_;
@@ -704,7 +725,11 @@ namespace curly_hpp
return state_->cancel(); return state_->cancel();
} }
request::statuses request::status() const noexcept { float request::progress() const noexcept {
return state_->progress();
}
req_status request::status() const noexcept {
return state_->status(); return state_->status();
} }
@@ -716,32 +741,32 @@ namespace curly_hpp
return state_->is_pending(); return state_->is_pending();
} }
request::statuses request::wait() const noexcept { req_status request::wait() const noexcept {
return state_->wait(false); return state_->wait(false);
} }
request::statuses request::wait_for(time_ms_t ms) const noexcept { req_status request::wait_for(time_ms_t ms) const noexcept {
return state_->wait_for(ms, false); return state_->wait_for(ms, false);
} }
request::statuses request::wait_until(time_point_t tp) const noexcept { req_status request::wait_until(time_point_t tp) const noexcept {
return state_->wait_until(tp, false); return state_->wait_until(tp, false);
} }
request::statuses request::wait_callback() const noexcept { req_status request::wait_callback() const noexcept {
return state_->wait(true); return state_->wait(true);
} }
request::statuses request::wait_callback_for(time_ms_t ms) const noexcept { req_status request::wait_callback_for(time_ms_t ms) const noexcept {
return state_->wait_for(ms, true); return state_->wait_for(ms, true);
} }
request::statuses request::wait_callback_until(time_point_t tp) const noexcept { req_status request::wait_callback_until(time_point_t tp) const noexcept {
return state_->wait_until(tp, true); return state_->wait_until(tp, true);
} }
response request::get() { response request::take() {
return state_->get(); return state_->take();
} }
const std::string& request::get_error() const noexcept { const std::string& request::get_error() const noexcept {
@@ -761,13 +786,13 @@ namespace curly_hpp
namespace curly_hpp namespace curly_hpp
{ {
request_builder::request_builder(methods m) noexcept request_builder::request_builder(http_method m) noexcept
: method_(m) {} : method_(m) {}
request_builder::request_builder(std::string u) noexcept request_builder::request_builder(std::string u) noexcept
: url_(std::move(u)) {} : url_(std::move(u)) {}
request_builder::request_builder(methods m, std::string u) noexcept request_builder::request_builder(http_method m, std::string u) noexcept
: url_(std::move(u)) : url_(std::move(u))
, method_(m) {} , method_(m) {}
@@ -776,7 +801,7 @@ namespace curly_hpp
return *this; return *this;
} }
request_builder& request_builder::method(methods m) noexcept { request_builder& request_builder::method(http_method m) noexcept {
method_ = m; method_ = m;
return *this; return *this;
} }
@@ -841,11 +866,16 @@ 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_;
} }
methods request_builder::method() const noexcept { http_method request_builder::method() const noexcept {
return method_; return method_;
} }
@@ -909,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);
@@ -918,13 +956,13 @@ namespace curly_hpp
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// //
// auto_performer // performer
// //
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
namespace curly_hpp namespace curly_hpp
{ {
auto_performer::auto_performer() { performer::performer() {
thread_ = std::thread([this](){ thread_ = std::thread([this](){
while ( !done_ ) { while ( !done_ ) {
curly_hpp::perform(); curly_hpp::perform();
@@ -933,18 +971,18 @@ namespace curly_hpp
}); });
} }
auto_performer::~auto_performer() noexcept { performer::~performer() noexcept {
done_.store(true); done_.store(true);
if ( thread_.joinable() ) { if ( thread_.joinable() ) {
thread_.join(); thread_.join();
} }
} }
time_ms_t auto_performer::wait_activity() const noexcept { time_ms_t performer::wait_activity() const noexcept {
return wait_activity_; return wait_activity_;
} }
void auto_performer::wait_activity(time_ms_t ms) noexcept { void performer::wait_activity(time_ms_t ms) noexcept {
wait_activity_ = ms; wait_activity_ = ms;
} }
} }
@@ -1022,7 +1060,7 @@ namespace curly_hpp
void wait_activity(time_ms_t ms) { void wait_activity(time_ms_t ms) {
curl_state::with([ms](CURLM* curlm){ curl_state::with([ms](CURLM* curlm){
if ( active_handles.empty() ) { if ( active_handles.empty() ) {
new_handles.wait_content_for(ms); new_handles.wait_for(ms);
} else if ( new_handles.empty() ) { } else if ( new_handles.empty() ) {
const int timeout_ms = static_cast<int>(ms.count()); const int timeout_ms = static_cast<int>(ms.count());
if ( CURLM_OK != curl_multi_wait(curlm, nullptr, 0, timeout_ms, nullptr) ) { if ( CURLM_OK != curl_multi_wait(curlm, nullptr, 0, timeout_ms, nullptr) ) {

View File

@@ -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)
@@ -68,7 +84,7 @@ namespace
reject(net::exception("network error")); reject(net::exception("network error"));
return; return;
} }
net::response response = request.get(); net::response response = request.take();
if ( response.is_http_error() ) { if ( response.is_http_error() ) {
reject(net::exception("server error")); reject(net::exception("server error"));
return; return;
@@ -80,39 +96,39 @@ namespace
} }
TEST_CASE("curly") { TEST_CASE("curly") {
net::auto_performer performer; net::performer performer;
performer.wait_activity(net::time_ms_t(10)); performer.wait_activity(net::time_ms_t(10));
SECTION("wait") { SECTION("wait") {
{ {
auto req = net::request_builder("https://httpbin.org/delay/1").send(); auto req = net::request_builder("https://httpbin.org/delay/1").send();
REQUIRE(req.status() == net::request::statuses::pending); REQUIRE(req.status() == net::req_status::pending);
REQUIRE(req.wait() == net::request::statuses::done); REQUIRE(req.wait() == net::req_status::done);
REQUIRE(req.status() == net::request::statuses::done); REQUIRE(req.status() == net::req_status::done);
auto resp = req.get(); auto resp = req.take();
REQUIRE(resp.http_code() == 200u); REQUIRE(resp.http_code() == 200u);
REQUIRE(req.status() == net::request::statuses::empty); REQUIRE(req.status() == net::req_status::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::req_status::pending);
REQUIRE(req.wait_for(net::time_sec_t(5)) == net::request::statuses::done); REQUIRE(req.wait_for(net::time_sec_t(5)) == net::req_status::done);
REQUIRE(req.get().http_code() == 200u); REQUIRE(req.take().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();
REQUIRE(req.wait_until(net::time_point_t::clock::now() + net::time_sec_t(1)) REQUIRE(req.wait_until(net::time_point_t::clock::now() + net::time_sec_t(1))
== net::request::statuses::pending); == net::req_status::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::req_status::done);
REQUIRE(req.get().http_code() == 200u); REQUIRE(req.take().http_code() == 200u);
} }
} }
SECTION("error") { SECTION("error") {
auto req = net::request_builder("|||").send(); auto req = net::request_builder("|||").send();
REQUIRE(req.wait() == net::request::statuses::failed); REQUIRE(req.wait() == net::req_status::failed);
REQUIRE(req.status() == net::request::statuses::failed); REQUIRE(req.status() == net::req_status::failed);
REQUIRE_FALSE(req.get_error().empty()); REQUIRE_FALSE(req.get_error().empty());
} }
@@ -120,21 +136,21 @@ TEST_CASE("curly") {
{ {
auto req = net::request_builder("https://httpbin.org/delay/1").send(); auto req = net::request_builder("https://httpbin.org/delay/1").send();
REQUIRE(req.cancel()); REQUIRE(req.cancel());
REQUIRE(req.status() == net::request::statuses::canceled); REQUIRE(req.status() == net::req_status::canceled);
REQUIRE(req.get_error().empty()); REQUIRE(req.get_error().empty());
} }
{ {
auto req = net::request_builder("https://httpbin.org/status/200").send(); auto req = net::request_builder("https://httpbin.org/status/200").send();
REQUIRE(req.wait() == net::request::statuses::done); REQUIRE(req.wait() == net::req_status::done);
REQUIRE_FALSE(req.cancel()); REQUIRE_FALSE(req.cancel());
REQUIRE(req.status() == net::request::statuses::done); REQUIRE(req.status() == net::req_status::done);
REQUIRE(req.get_error().empty()); REQUIRE(req.get_error().empty());
} }
} }
SECTION("is_done/is_pending") { SECTION("is_done/is_pending") {
{ {
auto req = net::request_builder(net::methods::get) auto req = net::request_builder(net::http_method::GET)
.url("https://httpbin.org/delay/1") .url("https://httpbin.org/delay/1")
.send(); .send();
REQUIRE_FALSE(req.is_done()); REQUIRE_FALSE(req.is_done());
@@ -144,7 +160,7 @@ TEST_CASE("curly") {
REQUIRE_FALSE(req.is_pending()); REQUIRE_FALSE(req.is_pending());
} }
{ {
auto req = net::request_builder(net::methods::post, "http://www.httpbin.org/post") auto req = net::request_builder(net::http_method::POST, "http://www.httpbin.org/post")
.url("https://httpbin.org/delay/2") .url("https://httpbin.org/delay/2")
.request_timeout(net::time_sec_t(1)) .request_timeout(net::time_sec_t(1))
.send(); .send();
@@ -160,23 +176,23 @@ TEST_CASE("curly") {
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.take();
REQUIRE(req.status() == net::request::statuses::empty); REQUIRE(req.status() == net::req_status::empty);
REQUIRE(resp.http_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();
REQUIRE(req.cancel()); REQUIRE(req.cancel());
REQUIRE_THROWS_AS(req.get(), net::exception); REQUIRE_THROWS_AS(req.take(), net::exception);
REQUIRE(req.status() == net::request::statuses::canceled); REQUIRE(req.status() == net::req_status::canceled);
} }
{ {
auto req = net::request_builder("https://httpbin.org/delay/2") auto req = net::request_builder("https://httpbin.org/delay/2")
.response_timeout(net::time_sec_t(0)) .response_timeout(net::time_sec_t(0))
.send(); .send();
REQUIRE(req.wait() == net::request::statuses::timeout); REQUIRE(req.wait() == net::req_status::timeout);
REQUIRE_THROWS_AS(req.get(), net::exception); REQUIRE_THROWS_AS(req.take(), net::exception);
REQUIRE(req.status() == net::request::statuses::timeout); REQUIRE(req.status() == net::req_status::timeout);
} }
} }
@@ -184,77 +200,128 @@ TEST_CASE("curly") {
{ {
auto req0 = net::request_builder() auto req0 = net::request_builder()
.url("https://httpbin.org/put") .url("https://httpbin.org/put")
.method(net::methods::put) .method(net::http_method::PUT)
.send(); .send();
REQUIRE(req0.get().http_code() == 200u); REQUIRE(req0.take().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::http_method::GET)
.send(); .send();
REQUIRE(req1.get().http_code() == 405u); REQUIRE(req1.take().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::http_method::HEAD)
.send(); .send();
REQUIRE(req2.get().http_code() == 405u); REQUIRE(req2.take().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::http_method::POST)
.send(); .send();
REQUIRE(req3.get().http_code() == 405u); REQUIRE(req3.take().http_code() == 405u);
auto req4 = net::request_builder()
.url("https://httpbin.org/put")
.method(net::http_method::PATCH)
.send();
REQUIRE(req4.take().http_code() == 405u);
auto req5 = net::request_builder()
.url("https://httpbin.org/put")
.method(net::http_method::DEL)
.send();
REQUIRE(req5.take().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::http_method::PUT)
.send(); .send();
REQUIRE(req0.get().http_code() == 405u); REQUIRE(req0.take().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::http_method::GET)
.send(); .send();
REQUIRE(req1.get().http_code() == 200u); REQUIRE(req1.take().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::http_method::HEAD)
.send(); .send();
REQUIRE(req2.get().http_code() == 200u); REQUIRE(req2.take().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::http_method::POST)
.send(); .send();
REQUIRE(req3.get().http_code() == 405u); REQUIRE(req3.take().http_code() == 405u);
auto req4 = net::request_builder()
.url("https://httpbin.org/get")
.method(net::http_method::PATCH)
.send();
REQUIRE(req4.take().http_code() == 405u);
auto req5 = net::request_builder()
.url("https://httpbin.org/get")
.method(net::http_method::DEL)
.send();
REQUIRE(req5.take().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::http_method::PUT)
.send(); .send();
REQUIRE(req0.get().http_code() == 405u); REQUIRE(req0.take().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::http_method::GET)
.send(); .send();
REQUIRE(req1.get().http_code() == 405u); REQUIRE(req1.take().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::http_method::HEAD)
.send(); .send();
REQUIRE(req2.get().http_code() == 405u); REQUIRE(req2.take().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::http_method::POST)
.send(); .send();
REQUIRE(req3.get().http_code() == 200u); REQUIRE(req3.take().http_code() == 200u);
auto req4 = net::request_builder()
.url("https://httpbin.org/post")
.method(net::http_method::PATCH)
.send();
REQUIRE(req4.take().http_code() == 405u);
auto req5 = net::request_builder()
.url("https://httpbin.org/post")
.method(net::http_method::DEL)
.send();
REQUIRE(req5.take().http_code() == 405u);
}
{
auto req1 = net::request_builder()
.url("https://httpbin.org/put")
.method(net::http_method::OPTIONS)
.send();
const auto allow1 = req1.take().headers.at("Allow");
REQUIRE((allow1 == "PUT, OPTIONS" || allow1 == "OPTIONS, PUT"));
auto req2 = net::request_builder()
.url("https://httpbin.org/post")
.method(net::http_method::OPTIONS)
.send();
const auto allow2 = req2.take().headers.at("Allow");
REQUIRE((allow2 == "POST, OPTIONS" || allow2 == "OPTIONS, POST"));
} }
} }
@@ -262,30 +329,44 @@ TEST_CASE("curly") {
{ {
auto req = net::request_builder() auto req = net::request_builder()
.url("https://httpbin.org/status/200") .url("https://httpbin.org/status/200")
.method(net::methods::put) .method(net::http_method::PUT)
.send(); .send();
REQUIRE(req.get().http_code() == 200u); REQUIRE(req.take().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::http_method::GET)
.send(); .send();
REQUIRE(req.get().http_code() == 201u); REQUIRE(req.take().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::http_method::HEAD)
.send(); .send();
REQUIRE(req.get().http_code() == 202u); REQUIRE(req.take().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::http_method::POST)
.send(); .send();
REQUIRE(req.get().http_code() == 203u); REQUIRE(req.take().http_code() == 203u);
}
{
auto req = net::request_builder()
.url("https://httpbin.org/status/203")
.method(net::http_method::PATCH)
.send();
REQUIRE(req.take().http_code() == 203u);
}
{
auto req = net::request_builder()
.url("https://httpbin.org/status/203")
.method(net::http_method::DEL)
.send();
REQUIRE(req.take().http_code() == 203u);
} }
} }
@@ -296,7 +377,7 @@ TEST_CASE("curly") {
.header("Custom-Header-2", "custom header value 2") .header("Custom-Header-2", "custom header value 2")
.header("Custom-Header-3", std::string()) .header("Custom-Header-3", std::string())
.send(); .send();
const auto resp = req.get(); const auto resp = req.take();
const auto content_j = json_parse(resp.content.as_string_view()); const auto content_j = json_parse(resp.content.as_string_view());
REQUIRE(content_j["headers"]["Custom-Header-1"] == "custom_header_value_1"); REQUIRE(content_j["headers"]["Custom-Header-1"] == "custom_header_value_1");
REQUIRE(content_j["headers"]["Custom-Header-2"] == "custom header value 2"); REQUIRE(content_j["headers"]["Custom-Header-2"] == "custom header value 2");
@@ -307,9 +388,9 @@ TEST_CASE("curly") {
{ {
auto req = net::request_builder() auto req = net::request_builder()
.url("https://httpbin.org/response-headers?hello=world&world=hello") .url("https://httpbin.org/response-headers?hello=world&world=hello")
.method(net::methods::get) .method(net::http_method::GET)
.send(); .send();
const auto resp = req.get(); const auto resp = req.take();
const auto content_j = json_parse(resp.content.as_string_view()); const auto content_j = json_parse(resp.content.as_string_view());
REQUIRE(content_j["hello"] == "world"); REQUIRE(content_j["hello"] == "world");
REQUIRE(content_j["world"] == "hello"); REQUIRE(content_j["world"] == "hello");
@@ -317,9 +398,9 @@ TEST_CASE("curly") {
{ {
auto req = net::request_builder() auto req = net::request_builder()
.url("https://httpbin.org/response-headers?hello=world&world=hello") .url("https://httpbin.org/response-headers?hello=world&world=hello")
.method(net::methods::post) .method(net::http_method::POST)
.send(); .send();
const auto resp = req.get(); const auto resp = req.take();
const auto content_j = json_parse(resp.content.as_string_copy()); const auto content_j = json_parse(resp.content.as_string_copy());
REQUIRE(content_j["hello"] == "world"); REQUIRE(content_j["hello"] == "world");
REQUIRE(content_j["world"] == "hello"); REQUIRE(content_j["world"] == "hello");
@@ -331,7 +412,7 @@ TEST_CASE("curly") {
auto req = net::request_builder() auto req = net::request_builder()
.url("https://httpbin.org/base64/SFRUUEJJTiBpcyBhd2Vzb21l") .url("https://httpbin.org/base64/SFRUUEJJTiBpcyBhd2Vzb21l")
.send(); .send();
const auto resp = req.get(); const auto resp = req.take();
REQUIRE(resp.content.as_string_view() == "HTTPBIN is awesome"); REQUIRE(resp.content.as_string_view() == "HTTPBIN is awesome");
REQUIRE(req.get_error().empty()); REQUIRE(req.get_error().empty());
} }
@@ -340,14 +421,14 @@ TEST_CASE("curly") {
.url("https://httpbin.org/delay/10") .url("https://httpbin.org/delay/10")
.request_timeout(net::time_sec_t(0)) .request_timeout(net::time_sec_t(0))
.send(); .send();
REQUIRE(req0.wait() == net::request::statuses::timeout); REQUIRE(req0.wait() == net::req_status::timeout);
REQUIRE_FALSE(req0.get_error().empty()); REQUIRE_FALSE(req0.get_error().empty());
auto req1 = net::request_builder() auto req1 = net::request_builder()
.url("https://httpbin.org/delay/10") .url("https://httpbin.org/delay/10")
.response_timeout(net::time_sec_t(0)) .response_timeout(net::time_sec_t(0))
.send(); .send();
REQUIRE(req1.wait() == net::request::statuses::timeout); REQUIRE(req1.wait() == net::req_status::timeout);
REQUIRE_FALSE(req1.get_error().empty()); REQUIRE_FALSE(req1.get_error().empty());
} }
{ {
@@ -355,14 +436,14 @@ TEST_CASE("curly") {
.url("https://httpbin.org/delay/10") .url("https://httpbin.org/delay/10")
.request_timeout(net::time_sec_t(1)) .request_timeout(net::time_sec_t(1))
.send(); .send();
REQUIRE(req0.wait() == net::request::statuses::timeout); REQUIRE(req0.wait() == net::req_status::timeout);
REQUIRE_FALSE(req0.get_error().empty()); REQUIRE_FALSE(req0.get_error().empty());
auto req1 = net::request_builder() auto req1 = net::request_builder()
.url("https://httpbin.org/delay/10") .url("https://httpbin.org/delay/10")
.response_timeout(net::time_sec_t(1)) .response_timeout(net::time_sec_t(1))
.send(); .send();
REQUIRE(req1.wait() == net::request::statuses::timeout); REQUIRE(req1.wait() == net::req_status::timeout);
REQUIRE_FALSE(req1.get_error().empty()); REQUIRE_FALSE(req1.get_error().empty());
} }
} }
@@ -371,11 +452,25 @@ TEST_CASE("curly") {
{ {
auto resp = net::request_builder() auto resp = net::request_builder()
.url("https://httpbin.org/image/png") .url("https://httpbin.org/image/png")
.method(net::methods::get) .method(net::http_method::HEAD)
.send().get(); .send().take();
REQUIRE(resp.http_code() == 200u); REQUIRE(resp.http_code() == 200u);
REQUIRE(resp.headers.count("Content-Type")); REQUIRE(resp.headers.count("Content-Type"));
REQUIRE(resp.headers.count("Content-Length"));
REQUIRE(resp.headers.at("Content-Type") == "image/png"); REQUIRE(resp.headers.at("Content-Type") == "image/png");
REQUIRE(resp.headers.at("Content-Length") == std::to_string(untests::png_data_length));
REQUIRE_FALSE(resp.content.size());
}
{
auto resp = net::request_builder()
.url("https://httpbin.org/image/png")
.method(net::http_method::GET)
.send().take();
REQUIRE(resp.http_code() == 200u);
REQUIRE(resp.headers.count("Content-Type"));
REQUIRE(resp.headers.count("Content-Length"));
REQUIRE(resp.headers.at("Content-Type") == "image/png");
REQUIRE(resp.headers.at("Content-Length") == std::to_string(untests::png_data_length));
REQUIRE(untests::png_data_length == resp.content.size()); REQUIRE(untests::png_data_length == resp.content.size());
REQUIRE(!std::memcmp( REQUIRE(!std::memcmp(
std::move(resp.content).data().data(), std::move(resp.content).data().data(),
@@ -384,11 +479,25 @@ TEST_CASE("curly") {
{ {
auto resp = net::request_builder() auto resp = net::request_builder()
.url("https://httpbin.org/image/jpeg") .url("https://httpbin.org/image/jpeg")
.method(net::methods::get) .method(net::http_method::HEAD)
.send().get(); .send().take();
REQUIRE(resp.http_code() == 200u); REQUIRE(resp.http_code() == 200u);
REQUIRE(resp.headers.count("Content-Type")); REQUIRE(resp.headers.count("Content-Type"));
REQUIRE(resp.headers.count("Content-Length"));
REQUIRE(resp.headers.at("Content-Type") == "image/jpeg"); REQUIRE(resp.headers.at("Content-Type") == "image/jpeg");
REQUIRE(resp.headers.at("Content-Length") == std::to_string(untests::jpeg_data_length));
REQUIRE_FALSE(resp.content.size());
}
{
auto resp = net::request_builder()
.url("https://httpbin.org/image/jpeg")
.method(net::http_method::GET)
.send().take();
REQUIRE(resp.http_code() == 200u);
REQUIRE(resp.headers.count("Content-Type"));
REQUIRE(resp.headers.count("Content-Length"));
REQUIRE(resp.headers.at("Content-Type") == "image/jpeg");
REQUIRE(resp.headers.at("Content-Length") == std::to_string(untests::jpeg_data_length));
REQUIRE(untests::jpeg_data_length == resp.content.size()); REQUIRE(untests::jpeg_data_length == resp.content.size());
REQUIRE(!std::memcmp( REQUIRE(!std::memcmp(
std::as_const(resp.content).data().data(), std::as_const(resp.content).data().data(),
@@ -401,57 +510,57 @@ TEST_CASE("curly") {
{ {
auto req = net::request_builder() auto req = net::request_builder()
.url("https://httpbin.org/redirect/2") .url("https://httpbin.org/redirect/2")
.method(net::methods::get) .method(net::http_method::GET)
.send(); .send();
REQUIRE(req.get().http_code() == 200u); REQUIRE(req.take().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::http_method::GET)
.send(); .send();
REQUIRE(req.get().http_code() == 200u); REQUIRE(req.take().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::http_method::GET)
.send(); .send();
REQUIRE(req.get().http_code() == 200u); REQUIRE(req.take().http_code() == 200u);
} }
} }
{ {
{ {
auto req = net::request_builder() auto req = net::request_builder()
.url("https://httpbin.org/redirect/3") .url("https://httpbin.org/redirect/3")
.method(net::methods::get) .method(net::http_method::GET)
.redirections(0) .redirections(0)
.send(); .send();
REQUIRE(req.get().http_code() == 302u); REQUIRE(req.take().http_code() == 302u);
} }
{ {
auto req = net::request_builder() auto req = net::request_builder()
.url("https://httpbin.org/redirect/3") .url("https://httpbin.org/redirect/3")
.method(net::methods::get) .method(net::http_method::GET)
.redirections(1) .redirections(1)
.send(); .send();
REQUIRE(req.wait() == net::request::statuses::failed); REQUIRE(req.wait() == net::req_status::failed);
} }
{ {
auto req = net::request_builder() auto req = net::request_builder()
.url("https://httpbin.org/redirect/3") .url("https://httpbin.org/redirect/3")
.method(net::methods::get) .method(net::http_method::GET)
.redirections(2) .redirections(2)
.send(); .send();
REQUIRE(req.wait() == net::request::statuses::failed); REQUIRE(req.wait() == net::req_status::failed);
} }
{ {
auto req = net::request_builder() auto req = net::request_builder()
.url("https://httpbin.org/redirect/3") .url("https://httpbin.org/redirect/3")
.method(net::methods::get) .method(net::http_method::GET)
.redirections(3) .redirections(3)
.send(); .send();
REQUIRE(req.get().http_code() == 200u); REQUIRE(req.take().http_code() == 200u);
} }
} }
} }
@@ -460,30 +569,50 @@ TEST_CASE("curly") {
{ {
auto resp = net::request_builder() auto resp = net::request_builder()
.url("https://httpbin.org/anything") .url("https://httpbin.org/anything")
.method(net::methods::put) .method(net::http_method::PUT)
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.content(R"({"hello":"world"})") .content(R"({"hello":"world"})")
.send().get(); .send().take();
const auto content_j = json_parse(resp.content.as_string_view()); const auto content_j = json_parse(resp.content.as_string_view());
REQUIRE(content_j["data"] == R"({"hello":"world"})"); REQUIRE(content_j["data"] == R"({"hello":"world"})");
} }
{ {
auto resp = net::request_builder() auto resp = net::request_builder()
.url("https://httpbin.org/anything") .url("https://httpbin.org/anything")
.method(net::methods::post) .method(net::http_method::PATCH)
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.content(R"({"hello":"world"})") .content(R"({"hello":"world"})")
.send().get(); .send().take();
const auto content_j = json_parse(resp.content.as_string_view()); const auto content_j = json_parse(resp.content.as_string_view());
REQUIRE(content_j["data"] == R"({"hello":"world"})"); REQUIRE(content_j["data"] == R"({"hello":"world"})");
} }
{ {
auto resp = net::request_builder() auto resp = net::request_builder()
.url("https://httpbin.org/anything") .url("https://httpbin.org/anything")
.method(net::methods::post) .method(net::http_method::DEL)
.header("Content-Type", "application/json")
.content(R"({"hello":"world"})")
.send().take();
const auto content_j = json_parse(resp.content.as_string_view());
REQUIRE(content_j["data"] == R"({"hello":"world"})");
}
{
auto resp = net::request_builder()
.url("https://httpbin.org/anything")
.method(net::http_method::POST)
.header("Content-Type", "application/json")
.content(R"({"hello":"world"})")
.send().take();
const auto content_j = json_parse(resp.content.as_string_view());
REQUIRE(content_j["data"] == R"({"hello":"world"})");
}
{
auto resp = net::request_builder()
.url("https://httpbin.org/anything")
.method(net::http_method::POST)
.header("Content-Type", "application/x-www-form-urlencoded") .header("Content-Type", "application/x-www-form-urlencoded")
.content("hello=world&world=hello") .content("hello=world&world=hello")
.send().get(); .send().take();
const auto content_j = json_parse(resp.content.as_string_view()); const auto content_j = json_parse(resp.content.as_string_view());
REQUIRE(content_j["form"]["hello"] == "world"); REQUIRE(content_j["form"]["hello"] == "world");
REQUIRE(content_j["form"]["world"] == "hello"); REQUIRE(content_j["form"]["world"] == "hello");
@@ -493,53 +622,53 @@ TEST_CASE("curly") {
SECTION("ssl_verification") { SECTION("ssl_verification") {
{ {
auto req0 = net::request_builder("https://expired.badssl.com") auto req0 = net::request_builder("https://expired.badssl.com")
.method(net::methods::head) .method(net::http_method::HEAD)
.verification(true) .verification(true)
.send(); .send();
REQUIRE(req0.wait() == net::request::statuses::failed); REQUIRE(req0.wait() == net::req_status::failed);
auto req1 = net::request_builder("https://wrong.host.badssl.com") auto req1 = net::request_builder("https://wrong.host.badssl.com")
.method(net::methods::head) .method(net::http_method::HEAD)
.verification(true) .verification(true)
.send(); .send();
REQUIRE(req1.wait() == net::request::statuses::failed); REQUIRE(req1.wait() == net::req_status::failed);
auto req2 = net::request_builder("https://self-signed.badssl.com") auto req2 = net::request_builder("https://self-signed.badssl.com")
.method(net::methods::head) .method(net::http_method::HEAD)
.verification(true) .verification(true)
.send(); .send();
REQUIRE(req2.wait() == net::request::statuses::failed); REQUIRE(req2.wait() == net::req_status::failed);
auto req3 = net::request_builder("https://untrusted-root.badssl.com") auto req3 = net::request_builder("https://untrusted-root.badssl.com")
.method(net::methods::head) .method(net::http_method::HEAD)
.verification(true) .verification(true)
.send(); .send();
REQUIRE(req3.wait() == net::request::statuses::failed); REQUIRE(req3.wait() == net::req_status::failed);
} }
{ {
auto req0 = net::request_builder("https://expired.badssl.com") auto req0 = net::request_builder("https://expired.badssl.com")
.method(net::methods::head) .method(net::http_method::HEAD)
.verification(false) .verification(false)
.send(); .send();
REQUIRE(req0.wait() == net::request::statuses::done); REQUIRE(req0.wait() == net::req_status::done);
auto req1 = net::request_builder("https://wrong.host.badssl.com") auto req1 = net::request_builder("https://wrong.host.badssl.com")
.method(net::methods::head) .method(net::http_method::HEAD)
.verification(false) .verification(false)
.send(); .send();
REQUIRE(req1.wait() == net::request::statuses::done); REQUIRE(req1.wait() == net::req_status::done);
auto req2 = net::request_builder("https://self-signed.badssl.com") auto req2 = net::request_builder("https://self-signed.badssl.com")
.method(net::methods::head) .method(net::http_method::HEAD)
.verification(false) .verification(false)
.send(); .send();
REQUIRE(req2.wait() == net::request::statuses::done); REQUIRE(req2.wait() == net::req_status::done);
auto req3 = net::request_builder("https://untrusted-root.badssl.com") auto req3 = net::request_builder("https://untrusted-root.badssl.com")
.method(net::methods::head) .method(net::http_method::HEAD)
.verification(false) .verification(false)
.send(); .send();
REQUIRE(req3.wait() == net::request::statuses::done); REQUIRE(req3.wait() == net::req_status::done);
} }
} }
@@ -547,18 +676,26 @@ TEST_CASE("curly") {
{ {
auto req = net::request_builder("https://httpbin.org/anything") auto req = net::request_builder("https://httpbin.org/anything")
.verbose(true) .verbose(true)
.method(net::methods::post) .method(net::http_method::POST)
.uploader<canceled_uploader>() .uploader<canceled_uploader>()
.send(); .send();
REQUIRE(req.wait() == net::request::statuses::canceled); REQUIRE(req.wait() == net::req_status::canceled);
} }
{ {
auto req = net::request_builder("https://httpbin.org/anything") auto req = net::request_builder("https://httpbin.org/anything")
.verbose(true) .verbose(true)
.method(net::methods::get) .method(net::http_method::GET)
.downloader<canceled_downloader>() .downloader<canceled_downloader>()
.send(); .send();
REQUIRE(req.wait() == net::request::statuses::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);
} }
} }
@@ -570,10 +707,10 @@ TEST_CASE("curly") {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
++call_once; ++call_once;
REQUIRE(request.is_done()); REQUIRE(request.is_done());
REQUIRE(request.status() == net::request::statuses::done); REQUIRE(request.status() == net::req_status::done);
REQUIRE(request.get().http_code() == 200u); REQUIRE(request.take().http_code() == 200u);
}).send(); }).send();
REQUIRE(req.wait_callback() == net::request::statuses::empty); REQUIRE(req.wait_callback() == net::req_status::empty);
REQUIRE_FALSE(req.get_callback_exception()); REQUIRE_FALSE(req.get_callback_exception());
REQUIRE(call_once.load() == 1u); REQUIRE(call_once.load() == 1u);
} }
@@ -584,10 +721,10 @@ TEST_CASE("curly") {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
++call_once; ++call_once;
REQUIRE_FALSE(request.is_done()); REQUIRE_FALSE(request.is_done());
REQUIRE(request.status() == net::request::statuses::failed); REQUIRE(request.status() == net::req_status::failed);
REQUIRE_FALSE(request.get_error().empty()); REQUIRE_FALSE(request.get_error().empty());
}).send(); }).send();
REQUIRE(req.wait_callback() == net::request::statuses::failed); REQUIRE(req.wait_callback() == net::req_status::failed);
REQUIRE_FALSE(req.get_callback_exception()); REQUIRE_FALSE(req.get_callback_exception());
REQUIRE(call_once.load() == 1u); REQUIRE(call_once.load() == 1u);
} }
@@ -599,10 +736,10 @@ TEST_CASE("curly") {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
++call_once; ++call_once;
REQUIRE_FALSE(request.is_done()); REQUIRE_FALSE(request.is_done());
REQUIRE(request.status() == net::request::statuses::timeout); REQUIRE(request.status() == net::req_status::timeout);
REQUIRE_FALSE(request.get_error().empty()); REQUIRE_FALSE(request.get_error().empty());
}).send(); }).send();
REQUIRE(req.wait_callback() == net::request::statuses::timeout); REQUIRE(req.wait_callback() == net::req_status::timeout);
REQUIRE_FALSE(req.get_callback_exception()); REQUIRE_FALSE(req.get_callback_exception());
REQUIRE(call_once.load() == 1u); REQUIRE(call_once.load() == 1u);
} }
@@ -613,11 +750,11 @@ TEST_CASE("curly") {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
++call_once; ++call_once;
REQUIRE_FALSE(request.is_done()); REQUIRE_FALSE(request.is_done());
REQUIRE(request.status() == net::request::statuses::canceled); REQUIRE(request.status() == net::req_status::canceled);
REQUIRE(request.get_error().empty()); REQUIRE(request.get_error().empty());
}).send(); }).send();
REQUIRE(req.cancel()); REQUIRE(req.cancel());
REQUIRE(req.wait_callback() == net::request::statuses::canceled); REQUIRE(req.wait_callback() == net::req_status::canceled);
REQUIRE_FALSE(req.get_callback_exception()); REQUIRE_FALSE(req.get_callback_exception());
REQUIRE(call_once.load() == 1u); REQUIRE(call_once.load() == 1u);
} }
@@ -627,11 +764,11 @@ TEST_CASE("curly") {
auto req = net::request_builder("http://www.httpbin.org/post") auto req = net::request_builder("http://www.httpbin.org/post")
.callback([](net::request request){ .callback([](net::request request){
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
if ( request.get().is_http_error() ) { if ( request.take().is_http_error() ) {
throw std::logic_error("my_logic_error"); throw std::logic_error("my_logic_error");
} }
}).send(); }).send();
REQUIRE(req.wait_callback() == net::request::statuses::empty); REQUIRE(req.wait_callback() == net::req_status::empty);
REQUIRE(req.get_callback_exception()); REQUIRE(req.get_callback_exception());
try { try {
std::rethrow_exception(req.get_callback_exception()); std::rethrow_exception(req.get_callback_exception());
@@ -642,17 +779,17 @@ TEST_CASE("curly") {
} }
TEST_CASE("curly_examples") { TEST_CASE("curly_examples") {
net::auto_performer performer; net::performer performer;
SECTION("Get Requests") { SECTION("Get Requests") {
// makes a GET request and async send it // makes a GET request and async send it
auto request = net::request_builder() auto request = net::request_builder()
.method(net::methods::get) .method(net::http_method::GET)
.url("http://www.httpbin.org/get") .url("http://www.httpbin.org/get")
.send(); .send();
// synchronous waits and get a response // synchronous waits and take a response
auto response = request.get(); auto response = request.take();
// prints results // prints results
std::cout << "Status code: " << response.http_code() << std::endl; std::cout << "Status code: " << response.http_code() << std::endl;
@@ -675,13 +812,13 @@ TEST_CASE("curly_examples") {
SECTION("Post Requests") { SECTION("Post Requests") {
auto request = net::request_builder() auto request = net::request_builder()
.method(net::methods::post) .method(net::http_method::POST)
.url("http://www.httpbin.org/post") .url("http://www.httpbin.org/post")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.content(R"({"hello" : "world"})") .content(R"({"hello" : "world"})")
.send(); .send();
auto response = request.get(); auto response = request.take();
std::cout << "Body content: " << response.content.as_string_view() << std::endl; std::cout << "Body content: " << response.content.as_string_view() << std::endl;
std::cout << "Content Length: " << response.headers["content-length"] << std::endl; std::cout << "Content Length: " << response.headers["content-length"] << std::endl;
@@ -714,11 +851,11 @@ TEST_CASE("curly_examples") {
request.wait(); request.wait();
if ( request.is_done() ) { if ( request.is_done() ) {
auto response = request.get(); auto response = request.take();
std::cout << "Status code: " << response.http_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.take();
std::cout << "Error message: " << request.get_error() << std::endl; std::cout << "Error message: " << request.get_error() << std::endl;
} }
@@ -730,7 +867,7 @@ TEST_CASE("curly_examples") {
auto request = net::request_builder("http://www.httpbin.org/get") auto request = net::request_builder("http://www.httpbin.org/get")
.callback([](net::request request){ .callback([](net::request request){
if ( request.is_done() ) { if ( request.is_done() ) {
auto response = request.get(); auto response = request.take();
std::cout << "Status code: " << response.http_code() << std::endl; std::cout << "Status code: " << response.http_code() << std::endl;
} else { } else {
std::cout << "Error message: " << request.get_error() << std::endl; std::cout << "Error message: " << request.get_error() << std::endl;
@@ -759,7 +896,7 @@ TEST_CASE("curly_examples") {
net::request_builder() net::request_builder()
.url("https://httpbin.org/image/jpeg") .url("https://httpbin.org/image/jpeg")
.downloader<file_dowloader>("image.jpeg") .downloader<file_dowloader>("image.jpeg")
.send().get(); .send().take();
} }
{ {
class file_uploader : public net::upload_handler { class file_uploader : public net::upload_handler {
@@ -785,10 +922,10 @@ TEST_CASE("curly_examples") {
}; };
net::request_builder() net::request_builder()
.method(net::methods::post) .method(net::http_method::POST)
.url("https://httpbin.org/anything") .url("https://httpbin.org/anything")
.uploader<file_uploader>("image.jpeg") .uploader<file_uploader>("image.jpeg")
.send().get(); .send().take();
} }
} }