From 92f9bbd2c352c452c1f0d2a1ef4a1e04289f4122 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 4 Jul 2019 15:16:53 +0700 Subject: [PATCH 1/8] move sources to curly.hpp subdirectory --- CMakeLists.txt | 17 +++++++++++++++-- sources/{ => curly.hpp}/curly.cpp | 0 2 files changed, 15 insertions(+), 2 deletions(-) rename sources/{ => curly.hpp}/curly.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71afd24..8baa754 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,9 +51,22 @@ endif() # 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 - headers/curly.hpp/curly.hpp - sources/curly.cpp) + ${CURLY_HPP_HEADERS} + ${CURLY_HPP_SOURCES}) + +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES + ${CURLY_HPP_HEADERS} + ${CURLY_HPP_SOURCES}) target_include_directories(${PROJECT_NAME} PUBLIC headers) diff --git a/sources/curly.cpp b/sources/curly.hpp/curly.cpp similarity index 100% rename from sources/curly.cpp rename to sources/curly.hpp/curly.cpp From 9e9960538221ae17fde9fe154fe5d98093128ac9 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 7 Jul 2019 08:27:43 +0700 Subject: [PATCH 2/8] move some code to header --- headers/curly.hpp/curly.hpp | 120 +++++++++++++++++++++++++----------- sources/curly.hpp/curly.cpp | 95 ---------------------------- 2 files changed, 85 insertions(+), 130 deletions(-) diff --git a/headers/curly.hpp/curly.hpp b/headers/curly.hpp/curly.hpp index 2d150f4..7d276d2 100644 --- a/headers/curly.hpp/curly.hpp +++ b/headers/curly.hpp/curly.hpp @@ -6,34 +6,38 @@ #pragma once +#include +#include +#include + #include #include #include +#include #include -#include -#include #include -#include -#include +#include +#include #include #include +#include +#include +#include + namespace curly_hpp { - class exception final : public std::runtime_error { - public: - explicit exception(const char* what); - explicit exception(const std::string& what); - }; + class request; + class response; + class request_builder; - struct icase_string_compare final { - using is_transparent = void; - bool operator()( - std::string_view l, - std::string_view r) const noexcept; - }; + using http_code_t = std::uint16_t; + + using time_sec_t = std::chrono::seconds; + using time_ms_t = std::chrono::milliseconds; + using time_point_t = std::chrono::steady_clock::time_point; enum class methods { put, @@ -42,16 +46,6 @@ namespace curly_hpp post }; - using http_code_t = std::uint16_t; - using headers_t = std::map; - - using time_sec_t = std::chrono::seconds; - using time_ms_t = std::chrono::milliseconds; - using time_point_t = std::chrono::steady_clock::time_point; - - class request; - using callback_t = std::function; - class upload_handler { public: virtual ~upload_handler() {} @@ -65,10 +59,44 @@ namespace curly_hpp virtual std::size_t write(const char* src, std::size_t size) = 0; }; + using callback_t = std::function; using uploader_uptr = std::unique_ptr; using downloader_uptr = std::unique_ptr; } +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 { class content_t final { @@ -81,15 +109,31 @@ namespace curly_hpp content_t(const content_t&) = default; content_t& operator=(const content_t&) = default; - content_t(std::string_view data); - content_t(std::vector data) noexcept; + content_t(std::string_view data) + : data_(data.cbegin(), data.cend() ) {} - std::size_t size() const noexcept; - std::vector& data() noexcept; - const std::vector& data() const noexcept; + content_t(std::vector data) noexcept + : data_(std::move(data)) {} - std::string as_string_copy() const; - std::string_view as_string_view() const noexcept; + std::size_t size() const noexcept { + return data_.size(); + } + + std::vector& data() noexcept { + return data_; + } + + const std::vector& 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: std::vector data_; }; @@ -107,10 +151,16 @@ namespace curly_hpp response(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; - http_code_t http_code() const noexcept; + bool is_http_error() const noexcept { + return http_code_ >= 400u; + } + + http_code_t http_code() const noexcept { + return http_code_; + } public: content_t content; headers_t headers; diff --git a/sources/curly.hpp/curly.cpp b/sources/curly.hpp/curly.cpp index c5d4d80..f76dd26 100644 --- a/sources/curly.hpp/curly.cpp +++ b/sources/curly.hpp/curly.cpp @@ -6,13 +6,8 @@ #include -#include -#include -#include - #include #include -#include #include #ifndef NOMINMAX @@ -213,96 +208,6 @@ namespace std::unique_ptr 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 data) noexcept - : data_(std::move(data)) {} - - std::size_t content_t::size() const noexcept { - return data_.size(); - } - - std::vector& content_t::data() noexcept { - return data_; - } - - const std::vector& 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 From 320c1c2d6e574446dc2bdfce24369248229aa1f2 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 7 Jul 2019 08:38:44 +0700 Subject: [PATCH 3/8] rename request::get to take --- README.md | 12 ++--- headers/curly.hpp/curly.hpp | 2 +- sources/curly.hpp/curly.cpp | 43 +++++++++++++---- untests/curly_tests.cpp | 94 ++++++++++++++++++------------------- 4 files changed, 88 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index c1a25e4..1b3426a 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,8 @@ auto request = net::request_builder() .url("http://www.httpbin.org/get") .send(); -// synchronous waits and get a response -auto response = request.get(); +// synchronous waits and take a response +auto response = request.take(); // prints results std::cout << "Status code: " << response.http_code() << std::endl; @@ -103,7 +103,7 @@ auto request = net::request_builder() .content(R"({"hello" : "world"})") .send(); -auto response = request.get(); +auto response = request.take(); std::cout << "Body content: " << response.content.as_string_view() << std::endl; std::cout << "Content Length: " << response.headers["content-length"] << std::endl; @@ -138,11 +138,11 @@ auto request = net::request_builder() request.wait(); if ( request.is_done() ) { - auto response = request.get(); + auto response = request.take(); std::cout << "Status code: " << response.http_code() << std::endl; } else { // 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; } @@ -156,7 +156,7 @@ if ( request.is_done() ) { auto request = net::request_builder("http://www.httpbin.org/get") .callback([](net::request request){ if ( request.is_done() ) { - auto response = request.get(); + auto response = request.take(); std::cout << "Status code: " << response.http_code() << std::endl; } else { std::cout << "Error message: " << request.get_error() << std::endl; diff --git a/headers/curly.hpp/curly.hpp b/headers/curly.hpp/curly.hpp index 7d276d2..670f459 100644 --- a/headers/curly.hpp/curly.hpp +++ b/headers/curly.hpp/curly.hpp @@ -203,7 +203,7 @@ namespace curly_hpp statuses wait_callback_for(time_ms_t ms) const noexcept; statuses wait_callback_until(time_point_t tp) const noexcept; - response get(); + response take(); const std::string& get_error() const noexcept; std::exception_ptr get_callback_exception() const noexcept; private: diff --git a/sources/curly.hpp/curly.cpp b/sources/curly.hpp/curly.cpp index f76dd26..7d41c69 100644 --- a/sources/curly.hpp/curly.cpp +++ b/sources/curly.hpp/curly.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #ifndef NOMINMAX @@ -91,18 +92,26 @@ namespace template < typename T > class mt_queue final { public: - void enqueue(T v) { + void enqueue(T&& v) { std::lock_guard guard(mutex_); queue_.push(std::move(v)); cvar_.notify_all(); } - bool try_dequeue(T& v) { + void enqueue(const T& v) { + std::lock_guard guard(mutex_); + queue_.push(v); + cvar_.notify_all(); + } + + bool try_dequeue(T& v) noexcept( + std::is_nothrow_move_assignable_v) + { std::lock_guard guard(mutex_); if ( queue_.empty() ) { return false; } - v = queue_.front(); + v = std::move(queue_.front()); queue_.pop(); return true; } @@ -112,9 +121,25 @@ namespace return queue_.empty(); } - bool wait_content_for(time_ms_t ms) const noexcept { + void wait() const noexcept { std::unique_lock 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 duration) const noexcept { + std::unique_lock 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& time) const { + std::unique_lock lock(mutex_); + return cvar_.wait_until(lock, time, [this](){ return !queue_.empty(); }); } @@ -449,7 +474,7 @@ namespace curly_hpp return status_; } - response get() { + response take() { std::unique_lock lock(mutex_); cvar_.wait(lock, [this](){ return status_ != statuses::pending; @@ -645,8 +670,8 @@ namespace curly_hpp return state_->wait_until(tp, true); } - response request::get() { - return state_->get(); + response request::take() { + return state_->take(); } const std::string& request::get_error() const noexcept { @@ -927,7 +952,7 @@ namespace curly_hpp void wait_activity(time_ms_t ms) { curl_state::with([ms](CURLM* curlm){ if ( active_handles.empty() ) { - new_handles.wait_content_for(ms); + new_handles.wait_for(ms); } else if ( new_handles.empty() ) { const int timeout_ms = static_cast(ms.count()); if ( CURLM_OK != curl_multi_wait(curlm, nullptr, 0, timeout_ms, nullptr) ) { diff --git a/untests/curly_tests.cpp b/untests/curly_tests.cpp index 744e6ec..5f10054 100644 --- a/untests/curly_tests.cpp +++ b/untests/curly_tests.cpp @@ -68,7 +68,7 @@ namespace reject(net::exception("network error")); return; } - net::response response = request.get(); + net::response response = request.take(); if ( response.is_http_error() ) { reject(net::exception("server error")); return; @@ -89,7 +89,7 @@ TEST_CASE("curly") { REQUIRE(req.status() == net::request::statuses::pending); REQUIRE(req.wait() == net::request::statuses::done); REQUIRE(req.status() == net::request::statuses::done); - auto resp = req.get(); + auto resp = req.take(); REQUIRE(resp.http_code() == 200u); REQUIRE(req.status() == net::request::statuses::empty); } @@ -97,7 +97,7 @@ TEST_CASE("curly") { 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(5)) == net::request::statuses::done); - REQUIRE(req.get().http_code() == 200u); + REQUIRE(req.take().http_code() == 200u); } { auto req = net::request_builder("https://httpbin.org/delay/2").send(); @@ -105,7 +105,7 @@ TEST_CASE("curly") { == net::request::statuses::pending); REQUIRE(req.wait_until(net::time_point_t::clock::now() + net::time_sec_t(5)) == net::request::statuses::done); - REQUIRE(req.get().http_code() == 200u); + REQUIRE(req.take().http_code() == 200u); } } @@ -160,14 +160,14 @@ TEST_CASE("curly") { SECTION("get") { { 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(resp.http_code() == 204u); } { auto req = net::request_builder("https://httpbin.org/delay/2").send(); REQUIRE(req.cancel()); - REQUIRE_THROWS_AS(req.get(), net::exception); + REQUIRE_THROWS_AS(req.take(), net::exception); REQUIRE(req.status() == net::request::statuses::canceled); } { @@ -175,7 +175,7 @@ TEST_CASE("curly") { .response_timeout(net::time_sec_t(0)) .send(); REQUIRE(req.wait() == net::request::statuses::timeout); - REQUIRE_THROWS_AS(req.get(), net::exception); + REQUIRE_THROWS_AS(req.take(), net::exception); REQUIRE(req.status() == net::request::statuses::timeout); } } @@ -186,75 +186,75 @@ TEST_CASE("curly") { .url("https://httpbin.org/put") .method(net::methods::put) .send(); - REQUIRE(req0.get().http_code() == 200u); + REQUIRE(req0.take().http_code() == 200u); auto req1 = net::request_builder() .url("https://httpbin.org/put") .method(net::methods::get) .send(); - REQUIRE(req1.get().http_code() == 405u); + REQUIRE(req1.take().http_code() == 405u); auto req2 = net::request_builder() .url("https://httpbin.org/put") .method(net::methods::head) .send(); - REQUIRE(req2.get().http_code() == 405u); + REQUIRE(req2.take().http_code() == 405u); auto req3 = net::request_builder() .url("https://httpbin.org/put") .method(net::methods::post) .send(); - REQUIRE(req3.get().http_code() == 405u); + REQUIRE(req3.take().http_code() == 405u); } { auto req0 = net::request_builder() .url("https://httpbin.org/get") .method(net::methods::put) .send(); - REQUIRE(req0.get().http_code() == 405u); + REQUIRE(req0.take().http_code() == 405u); auto req1 = net::request_builder() .url("https://httpbin.org/get") .method(net::methods::get) .send(); - REQUIRE(req1.get().http_code() == 200u); + REQUIRE(req1.take().http_code() == 200u); auto req2 = net::request_builder() .url("https://httpbin.org/get") .method(net::methods::head) .send(); - REQUIRE(req2.get().http_code() == 200u); + REQUIRE(req2.take().http_code() == 200u); auto req3 = net::request_builder() .url("https://httpbin.org/get") .method(net::methods::post) .send(); - REQUIRE(req3.get().http_code() == 405u); + REQUIRE(req3.take().http_code() == 405u); } { auto req0 = net::request_builder() .url("https://httpbin.org/post") .method(net::methods::put) .send(); - REQUIRE(req0.get().http_code() == 405u); + REQUIRE(req0.take().http_code() == 405u); auto req1 = net::request_builder() .url("https://httpbin.org/post") .method(net::methods::get) .send(); - REQUIRE(req1.get().http_code() == 405u); + REQUIRE(req1.take().http_code() == 405u); auto req2 = net::request_builder() .url("https://httpbin.org/post") .method(net::methods::head) .send(); - REQUIRE(req2.get().http_code() == 405u); + REQUIRE(req2.take().http_code() == 405u); auto req3 = net::request_builder() .url("https://httpbin.org/post") .method(net::methods::post) .send(); - REQUIRE(req3.get().http_code() == 200u); + REQUIRE(req3.take().http_code() == 200u); } } @@ -264,28 +264,28 @@ TEST_CASE("curly") { .url("https://httpbin.org/status/200") .method(net::methods::put) .send(); - REQUIRE(req.get().http_code() == 200u); + REQUIRE(req.take().http_code() == 200u); } { auto req = net::request_builder() .url("https://httpbin.org/status/201") .method(net::methods::get) .send(); - REQUIRE(req.get().http_code() == 201u); + REQUIRE(req.take().http_code() == 201u); } { auto req = net::request_builder() .url("https://httpbin.org/status/202") .method(net::methods::head) .send(); - REQUIRE(req.get().http_code() == 202u); + REQUIRE(req.take().http_code() == 202u); } { auto req = net::request_builder() .url("https://httpbin.org/status/203") .method(net::methods::post) .send(); - REQUIRE(req.get().http_code() == 203u); + REQUIRE(req.take().http_code() == 203u); } } @@ -296,7 +296,7 @@ TEST_CASE("curly") { .header("Custom-Header-2", "custom header value 2") .header("Custom-Header-3", std::string()) .send(); - const auto resp = req.get(); + const auto resp = req.take(); 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-2"] == "custom header value 2"); @@ -309,7 +309,7 @@ TEST_CASE("curly") { .url("https://httpbin.org/response-headers?hello=world&world=hello") .method(net::methods::get) .send(); - const auto resp = req.get(); + const auto resp = req.take(); const auto content_j = json_parse(resp.content.as_string_view()); REQUIRE(content_j["hello"] == "world"); REQUIRE(content_j["world"] == "hello"); @@ -319,7 +319,7 @@ TEST_CASE("curly") { .url("https://httpbin.org/response-headers?hello=world&world=hello") .method(net::methods::post) .send(); - const auto resp = req.get(); + const auto resp = req.take(); const auto content_j = json_parse(resp.content.as_string_copy()); REQUIRE(content_j["hello"] == "world"); REQUIRE(content_j["world"] == "hello"); @@ -331,7 +331,7 @@ TEST_CASE("curly") { auto req = net::request_builder() .url("https://httpbin.org/base64/SFRUUEJJTiBpcyBhd2Vzb21l") .send(); - const auto resp = req.get(); + const auto resp = req.take(); REQUIRE(resp.content.as_string_view() == "HTTPBIN is awesome"); REQUIRE(req.get_error().empty()); } @@ -372,7 +372,7 @@ TEST_CASE("curly") { auto resp = net::request_builder() .url("https://httpbin.org/image/png") .method(net::methods::get) - .send().get(); + .send().take(); REQUIRE(resp.http_code() == 200u); REQUIRE(resp.headers.count("Content-Type")); REQUIRE(resp.headers.at("Content-Type") == "image/png"); @@ -385,7 +385,7 @@ TEST_CASE("curly") { auto resp = net::request_builder() .url("https://httpbin.org/image/jpeg") .method(net::methods::get) - .send().get(); + .send().take(); REQUIRE(resp.http_code() == 200u); REQUIRE(resp.headers.count("Content-Type")); REQUIRE(resp.headers.at("Content-Type") == "image/jpeg"); @@ -403,21 +403,21 @@ TEST_CASE("curly") { .url("https://httpbin.org/redirect/2") .method(net::methods::get) .send(); - REQUIRE(req.get().http_code() == 200u); + REQUIRE(req.take().http_code() == 200u); } { auto req = net::request_builder() .url("https://httpbin.org/absolute-redirect/2") .method(net::methods::get) .send(); - REQUIRE(req.get().http_code() == 200u); + REQUIRE(req.take().http_code() == 200u); } { auto req = net::request_builder() .url("https://httpbin.org/relative-redirect/2") .method(net::methods::get) .send(); - REQUIRE(req.get().http_code() == 200u); + REQUIRE(req.take().http_code() == 200u); } } { @@ -427,7 +427,7 @@ TEST_CASE("curly") { .method(net::methods::get) .redirections(0) .send(); - REQUIRE(req.get().http_code() == 302u); + REQUIRE(req.take().http_code() == 302u); } { auto req = net::request_builder() @@ -451,7 +451,7 @@ TEST_CASE("curly") { .method(net::methods::get) .redirections(3) .send(); - REQUIRE(req.get().http_code() == 200u); + REQUIRE(req.take().http_code() == 200u); } } } @@ -463,7 +463,7 @@ TEST_CASE("curly") { .method(net::methods::put) .header("Content-Type", "application/json") .content(R"({"hello":"world"})") - .send().get(); + .send().take(); const auto content_j = json_parse(resp.content.as_string_view()); REQUIRE(content_j["data"] == R"({"hello":"world"})"); } @@ -473,7 +473,7 @@ TEST_CASE("curly") { .method(net::methods::post) .header("Content-Type", "application/json") .content(R"({"hello":"world"})") - .send().get(); + .send().take(); const auto content_j = json_parse(resp.content.as_string_view()); REQUIRE(content_j["data"] == R"({"hello":"world"})"); } @@ -483,7 +483,7 @@ TEST_CASE("curly") { .method(net::methods::post) .header("Content-Type", "application/x-www-form-urlencoded") .content("hello=world&world=hello") - .send().get(); + .send().take(); const auto content_j = json_parse(resp.content.as_string_view()); REQUIRE(content_j["form"]["hello"] == "world"); REQUIRE(content_j["form"]["world"] == "hello"); @@ -571,7 +571,7 @@ TEST_CASE("curly") { ++call_once; REQUIRE(request.is_done()); REQUIRE(request.status() == net::request::statuses::done); - REQUIRE(request.get().http_code() == 200u); + REQUIRE(request.take().http_code() == 200u); }).send(); REQUIRE(req.wait_callback() == net::request::statuses::empty); REQUIRE_FALSE(req.get_callback_exception()); @@ -627,7 +627,7 @@ TEST_CASE("curly") { 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() ) { + if ( request.take().is_http_error() ) { throw std::logic_error("my_logic_error"); } }).send(); @@ -651,8 +651,8 @@ TEST_CASE("curly_examples") { .url("http://www.httpbin.org/get") .send(); - // synchronous waits and get a response - auto response = request.get(); + // synchronous waits and take a response + auto response = request.take(); // prints results std::cout << "Status code: " << response.http_code() << std::endl; @@ -681,7 +681,7 @@ TEST_CASE("curly_examples") { .content(R"({"hello" : "world"})") .send(); - auto response = request.get(); + auto response = request.take(); std::cout << "Body content: " << response.content.as_string_view() << std::endl; std::cout << "Content Length: " << response.headers["content-length"] << std::endl; @@ -714,11 +714,11 @@ TEST_CASE("curly_examples") { request.wait(); if ( request.is_done() ) { - auto response = request.get(); + auto response = request.take(); std::cout << "Status code: " << response.http_code() << std::endl; } else { // 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; } @@ -730,7 +730,7 @@ TEST_CASE("curly_examples") { auto request = net::request_builder("http://www.httpbin.org/get") .callback([](net::request request){ if ( request.is_done() ) { - auto response = request.get(); + auto response = request.take(); std::cout << "Status code: " << response.http_code() << std::endl; } else { std::cout << "Error message: " << request.get_error() << std::endl; @@ -759,7 +759,7 @@ TEST_CASE("curly_examples") { net::request_builder() .url("https://httpbin.org/image/jpeg") .downloader("image.jpeg") - .send().get(); + .send().take(); } { class file_uploader : public net::upload_handler { @@ -788,7 +788,7 @@ TEST_CASE("curly_examples") { .method(net::methods::post) .url("https://httpbin.org/anything") .uploader("image.jpeg") - .send().get(); + .send().take(); } } From 4b9dae2f38209e590c54dbeaf83400449e39b2d9 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 7 Jul 2019 08:48:43 +0700 Subject: [PATCH 4/8] rename methods to http_method, statuses to req_status --- README.md | 6 +- headers/curly.hpp/curly.hpp | 52 +++++------ sources/curly.hpp/curly.cpp | 80 ++++++++--------- untests/curly_tests.cpp | 174 ++++++++++++++++++------------------ 4 files changed, 156 insertions(+), 156 deletions(-) diff --git a/README.md b/README.md index 1b3426a..0c5b396 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ net::perform(); ```cpp // makes a GET request and async send it auto request = net::request_builder() - .method(net::methods::get) + .method(net::http_method::GET) .url("http://www.httpbin.org/get") .send(); @@ -97,7 +97,7 @@ std::cout << "Body content: " << response.content.as_string_view() << std::endl; ```cpp auto request = net::request_builder() - .method(net::methods::post) + .method(net::http_method::POST) .url("http://www.httpbin.org/post") .header("Content-Type", "application/json") .content(R"({"hello" : "world"})") @@ -217,7 +217,7 @@ private: }; net::request_builder() - .method(net::methods::post) + .method(net::http_method::POST) .url("https://httpbin.org/anything") .uploader("image.jpeg") .send().wait(); diff --git a/headers/curly.hpp/curly.hpp b/headers/curly.hpp/curly.hpp index 670f459..1f9a8d4 100644 --- a/headers/curly.hpp/curly.hpp +++ b/headers/curly.hpp/curly.hpp @@ -39,11 +39,11 @@ namespace curly_hpp using time_ms_t = std::chrono::milliseconds; using time_point_t = std::chrono::steady_clock::time_point; - enum class methods { - put, - get, - head, - post + enum class http_method { + PUT, + GET, + HEAD, + POST }; class upload_handler { @@ -173,16 +173,16 @@ namespace curly_hpp namespace curly_hpp { + enum class req_status { + done, + empty, + failed, + timeout, + pending, + canceled + }; + class request final { - public: - enum class statuses { - done, - empty, - failed, - timeout, - pending, - canceled - }; public: class internal_state; using internal_state_ptr = std::shared_ptr; @@ -190,18 +190,18 @@ namespace curly_hpp request(internal_state_ptr); bool cancel() noexcept; - statuses status() const noexcept; + req_status status() const noexcept; bool is_done() const noexcept; bool is_pending() const noexcept; - statuses wait() const noexcept; - statuses wait_for(time_ms_t ms) const noexcept; - statuses wait_until(time_point_t tp) const noexcept; + req_status wait() const noexcept; + req_status wait_for(time_ms_t ms) const noexcept; + req_status 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; + req_status wait_callback() const noexcept; + req_status wait_callback_for(time_ms_t ms) const noexcept; + req_status wait_callback_until(time_point_t tp) const noexcept; response take(); const std::string& get_error() const noexcept; @@ -223,12 +223,12 @@ namespace curly_hpp request_builder(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(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& method(methods m) noexcept; + request_builder& method(http_method m) noexcept; request_builder& header(std::string key, std::string value); request_builder& verbose(bool v) noexcept; @@ -245,7 +245,7 @@ namespace curly_hpp request_builder& downloader(downloader_uptr d) noexcept; const std::string& url() const noexcept; - methods method() const noexcept; + http_method method() const noexcept; const headers_t& headers() const noexcept; bool verbose() const noexcept; @@ -288,7 +288,7 @@ namespace curly_hpp } private: std::string url_; - methods method_{methods::get}; + http_method method_{http_method::GET}; headers_t headers_; bool verbose_{false}; bool verification_{false}; diff --git a/sources/curly.hpp/curly.cpp b/sources/curly.hpp/curly.cpp index 7d41c69..560501d 100644 --- a/sources/curly.hpp/curly.cpp +++ b/sources/curly.hpp/curly.cpp @@ -295,18 +295,18 @@ namespace curly_hpp curl_easy_setopt(curlh_.get(), CURLOPT_VERBOSE, breq_.verbose() ? 1l : 0l); switch ( breq_.method() ) { - case methods::put: + case http_method::PUT: curl_easy_setopt(curlh_.get(), CURLOPT_UPLOAD, 1l); curl_easy_setopt(curlh_.get(), CURLOPT_INFILESIZE_LARGE, static_cast(breq_.uploader()->size())); break; - case methods::get: + case http_method::GET: curl_easy_setopt(curlh_.get(), CURLOPT_HTTPGET, 1l); break; - case methods::head: + case http_method::HEAD: curl_easy_setopt(curlh_.get(), CURLOPT_NOBODY, 1l); break; - case methods::post: + case http_method::POST: curl_easy_setopt(curlh_.get(), CURLOPT_POST, 1l); curl_easy_setopt(curlh_.get(), CURLOPT_POSTFIELDSIZE_LARGE, static_cast(breq_.uploader()->size())); @@ -355,7 +355,7 @@ namespace curly_hpp bool done() noexcept { std::lock_guard guard(mutex_); - if ( status_ != statuses::pending ) { + if ( status_ != req_status::pending ) { return false; } @@ -365,7 +365,7 @@ namespace curly_hpp CURLINFO_RESPONSE_CODE, &http_code) || !http_code ) { - status_ = statuses::failed; + status_ = req_status::failed; cvar_.notify_all(); return false; } @@ -377,12 +377,12 @@ namespace curly_hpp response_.uploader = std::move(breq_.uploader()); response_.downloader = std::move(breq_.downloader()); } catch (...) { - status_ = statuses::failed; + status_ = req_status::failed; cvar_.notify_all(); return false; } - status_ = statuses::done; + status_ = req_status::done; error_.clear(); cvar_.notify_all(); @@ -391,21 +391,21 @@ namespace curly_hpp bool fail(CURLcode err) noexcept { std::lock_guard guard(mutex_); - if ( status_ != statuses::pending ) { + if ( status_ != req_status::pending ) { return false; } switch ( err ) { case CURLE_OPERATION_TIMEDOUT: - status_ = statuses::timeout; + status_ = req_status::timeout; break; case CURLE_READ_ERROR: case CURLE_WRITE_ERROR: case CURLE_ABORTED_BY_CALLBACK: - status_ = statuses::canceled; + status_ = req_status::canceled; break; default: - status_ = statuses::failed; + status_ = req_status::failed; break; } @@ -421,54 +421,54 @@ namespace curly_hpp bool cancel() noexcept { std::lock_guard guard(mutex_); - if ( status_ != statuses::pending ) { + if ( status_ != req_status::pending ) { return false; } - status_ = statuses::canceled; + status_ = req_status::canceled; error_.clear(); cvar_.notify_all(); return true; } - statuses status() const noexcept { + req_status status() const noexcept { std::lock_guard guard(mutex_); return status_; } bool is_done() const noexcept { std::lock_guard guard(mutex_); - return status_ == statuses::done; + return status_ == req_status::done; } bool is_pending() const noexcept { std::lock_guard 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 lock(mutex_); cvar_.wait(lock, [this, wait_callback](){ - return (status_ != statuses::pending) + return (status_ != req_status::pending) && (!wait_callback || callbacked_); }); 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 lock(mutex_); cvar_.wait_for(lock, ms, [this, wait_callback](){ - return (status_ != statuses::pending) + return (status_ != req_status::pending) && (!wait_callback || callbacked_); }); 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 lock(mutex_); cvar_.wait_until(lock, tp, [this, wait_callback](){ - return (status_ != statuses::pending) + return (status_ != req_status::pending) && (!wait_callback || callbacked_); }); return status_; @@ -477,19 +477,19 @@ namespace curly_hpp response take() { std::unique_lock lock(mutex_); 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"); } - status_ = statuses::empty; + status_ = req_status::empty; return std::move(response_); } const std::string& get_error() const noexcept { std::unique_lock lock(mutex_); cvar_.wait(lock, [this](){ - return status_ != statuses::pending; + return status_ != req_status::pending; }); return error_; } @@ -513,7 +513,7 @@ namespace curly_hpp callback_exception_ = std::current_exception(); } std::lock_guard guard(mutex_); - assert(!callbacked_ && status_ != statuses::pending); + assert(!callbacked_ && status_ != req_status::pending); callbacked_ = true; cvar_.notify_all(); } @@ -615,7 +615,7 @@ namespace curly_hpp bool callbacked_{false}; std::exception_ptr callback_exception_{nullptr}; private: - statuses status_{statuses::pending}; + req_status status_{req_status::pending}; std::string error_{"Unknown error"}; private: mutable std::mutex mutex_; @@ -634,7 +634,7 @@ namespace curly_hpp return state_->cancel(); } - request::statuses request::status() const noexcept { + req_status request::status() const noexcept { return state_->status(); } @@ -646,27 +646,27 @@ namespace curly_hpp return state_->is_pending(); } - request::statuses request::wait() const noexcept { + req_status request::wait() const noexcept { 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); } - 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); } - request::statuses request::wait_callback() const noexcept { + req_status request::wait_callback() const noexcept { 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); } - 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); } @@ -691,13 +691,13 @@ namespace curly_hpp namespace curly_hpp { - request_builder::request_builder(methods m) noexcept + request_builder::request_builder(http_method m) noexcept : method_(m) {} request_builder::request_builder(std::string u) noexcept : 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)) , method_(m) {} @@ -706,7 +706,7 @@ namespace curly_hpp return *this; } - request_builder& request_builder::method(methods m) noexcept { + request_builder& request_builder::method(http_method m) noexcept { method_ = m; return *this; } @@ -775,7 +775,7 @@ namespace curly_hpp return url_; } - methods request_builder::method() const noexcept { + http_method request_builder::method() const noexcept { return method_; } diff --git a/untests/curly_tests.cpp b/untests/curly_tests.cpp index 5f10054..cdf9d7a 100644 --- a/untests/curly_tests.cpp +++ b/untests/curly_tests.cpp @@ -86,33 +86,33 @@ TEST_CASE("curly") { SECTION("wait") { { auto req = net::request_builder("https://httpbin.org/delay/1").send(); - REQUIRE(req.status() == net::request::statuses::pending); - REQUIRE(req.wait() == net::request::statuses::done); - REQUIRE(req.status() == net::request::statuses::done); + REQUIRE(req.status() == net::req_status::pending); + REQUIRE(req.wait() == net::req_status::done); + REQUIRE(req.status() == net::req_status::done); auto resp = req.take(); 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(); - 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(1)) == net::req_status::pending); + REQUIRE(req.wait_for(net::time_sec_t(5)) == net::req_status::done); REQUIRE(req.take().http_code() == 200u); } { 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)) - == net::request::statuses::pending); + == net::req_status::pending); 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.take().http_code() == 200u); } } SECTION("error") { auto req = net::request_builder("|||").send(); - REQUIRE(req.wait() == net::request::statuses::failed); - REQUIRE(req.status() == net::request::statuses::failed); + REQUIRE(req.wait() == net::req_status::failed); + REQUIRE(req.status() == net::req_status::failed); REQUIRE_FALSE(req.get_error().empty()); } @@ -120,21 +120,21 @@ TEST_CASE("curly") { { auto req = net::request_builder("https://httpbin.org/delay/1").send(); REQUIRE(req.cancel()); - REQUIRE(req.status() == net::request::statuses::canceled); + REQUIRE(req.status() == net::req_status::canceled); REQUIRE(req.get_error().empty()); } { 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(req.status() == net::request::statuses::done); + REQUIRE(req.status() == net::req_status::done); REQUIRE(req.get_error().empty()); } } 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") .send(); REQUIRE_FALSE(req.is_done()); @@ -144,7 +144,7 @@ TEST_CASE("curly") { 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") .request_timeout(net::time_sec_t(1)) .send(); @@ -161,22 +161,22 @@ TEST_CASE("curly") { { auto req = net::request_builder("https://httpbin.org/status/204").send(); auto resp = req.take(); - REQUIRE(req.status() == net::request::statuses::empty); + REQUIRE(req.status() == net::req_status::empty); REQUIRE(resp.http_code() == 204u); } { auto req = net::request_builder("https://httpbin.org/delay/2").send(); REQUIRE(req.cancel()); 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") .response_timeout(net::time_sec_t(0)) .send(); - REQUIRE(req.wait() == net::request::statuses::timeout); + REQUIRE(req.wait() == net::req_status::timeout); REQUIRE_THROWS_AS(req.take(), net::exception); - REQUIRE(req.status() == net::request::statuses::timeout); + REQUIRE(req.status() == net::req_status::timeout); } } @@ -184,75 +184,75 @@ TEST_CASE("curly") { { auto req0 = net::request_builder() .url("https://httpbin.org/put") - .method(net::methods::put) + .method(net::http_method::PUT) .send(); REQUIRE(req0.take().http_code() == 200u); auto req1 = net::request_builder() .url("https://httpbin.org/put") - .method(net::methods::get) + .method(net::http_method::GET) .send(); REQUIRE(req1.take().http_code() == 405u); auto req2 = net::request_builder() .url("https://httpbin.org/put") - .method(net::methods::head) + .method(net::http_method::HEAD) .send(); REQUIRE(req2.take().http_code() == 405u); auto req3 = net::request_builder() .url("https://httpbin.org/put") - .method(net::methods::post) + .method(net::http_method::POST) .send(); REQUIRE(req3.take().http_code() == 405u); } { auto req0 = net::request_builder() .url("https://httpbin.org/get") - .method(net::methods::put) + .method(net::http_method::PUT) .send(); REQUIRE(req0.take().http_code() == 405u); auto req1 = net::request_builder() .url("https://httpbin.org/get") - .method(net::methods::get) + .method(net::http_method::GET) .send(); REQUIRE(req1.take().http_code() == 200u); auto req2 = net::request_builder() .url("https://httpbin.org/get") - .method(net::methods::head) + .method(net::http_method::HEAD) .send(); REQUIRE(req2.take().http_code() == 200u); auto req3 = net::request_builder() .url("https://httpbin.org/get") - .method(net::methods::post) + .method(net::http_method::POST) .send(); REQUIRE(req3.take().http_code() == 405u); } { auto req0 = net::request_builder() .url("https://httpbin.org/post") - .method(net::methods::put) + .method(net::http_method::PUT) .send(); REQUIRE(req0.take().http_code() == 405u); auto req1 = net::request_builder() .url("https://httpbin.org/post") - .method(net::methods::get) + .method(net::http_method::GET) .send(); REQUIRE(req1.take().http_code() == 405u); auto req2 = net::request_builder() .url("https://httpbin.org/post") - .method(net::methods::head) + .method(net::http_method::HEAD) .send(); REQUIRE(req2.take().http_code() == 405u); auto req3 = net::request_builder() .url("https://httpbin.org/post") - .method(net::methods::post) + .method(net::http_method::POST) .send(); REQUIRE(req3.take().http_code() == 200u); } @@ -262,28 +262,28 @@ TEST_CASE("curly") { { auto req = net::request_builder() .url("https://httpbin.org/status/200") - .method(net::methods::put) + .method(net::http_method::PUT) .send(); REQUIRE(req.take().http_code() == 200u); } { auto req = net::request_builder() .url("https://httpbin.org/status/201") - .method(net::methods::get) + .method(net::http_method::GET) .send(); REQUIRE(req.take().http_code() == 201u); } { auto req = net::request_builder() .url("https://httpbin.org/status/202") - .method(net::methods::head) + .method(net::http_method::HEAD) .send(); REQUIRE(req.take().http_code() == 202u); } { auto req = net::request_builder() .url("https://httpbin.org/status/203") - .method(net::methods::post) + .method(net::http_method::POST) .send(); REQUIRE(req.take().http_code() == 203u); } @@ -307,7 +307,7 @@ TEST_CASE("curly") { { auto req = net::request_builder() .url("https://httpbin.org/response-headers?hello=world&world=hello") - .method(net::methods::get) + .method(net::http_method::GET) .send(); const auto resp = req.take(); const auto content_j = json_parse(resp.content.as_string_view()); @@ -317,7 +317,7 @@ TEST_CASE("curly") { { auto req = net::request_builder() .url("https://httpbin.org/response-headers?hello=world&world=hello") - .method(net::methods::post) + .method(net::http_method::POST) .send(); const auto resp = req.take(); const auto content_j = json_parse(resp.content.as_string_copy()); @@ -340,14 +340,14 @@ TEST_CASE("curly") { .url("https://httpbin.org/delay/10") .request_timeout(net::time_sec_t(0)) .send(); - REQUIRE(req0.wait() == net::request::statuses::timeout); + REQUIRE(req0.wait() == net::req_status::timeout); REQUIRE_FALSE(req0.get_error().empty()); auto req1 = net::request_builder() .url("https://httpbin.org/delay/10") .response_timeout(net::time_sec_t(0)) .send(); - REQUIRE(req1.wait() == net::request::statuses::timeout); + REQUIRE(req1.wait() == net::req_status::timeout); REQUIRE_FALSE(req1.get_error().empty()); } { @@ -355,14 +355,14 @@ TEST_CASE("curly") { .url("https://httpbin.org/delay/10") .request_timeout(net::time_sec_t(1)) .send(); - REQUIRE(req0.wait() == net::request::statuses::timeout); + REQUIRE(req0.wait() == net::req_status::timeout); REQUIRE_FALSE(req0.get_error().empty()); auto req1 = net::request_builder() .url("https://httpbin.org/delay/10") .response_timeout(net::time_sec_t(1)) .send(); - REQUIRE(req1.wait() == net::request::statuses::timeout); + REQUIRE(req1.wait() == net::req_status::timeout); REQUIRE_FALSE(req1.get_error().empty()); } } @@ -371,7 +371,7 @@ TEST_CASE("curly") { { auto resp = net::request_builder() .url("https://httpbin.org/image/png") - .method(net::methods::get) + .method(net::http_method::GET) .send().take(); REQUIRE(resp.http_code() == 200u); REQUIRE(resp.headers.count("Content-Type")); @@ -384,7 +384,7 @@ TEST_CASE("curly") { { auto resp = net::request_builder() .url("https://httpbin.org/image/jpeg") - .method(net::methods::get) + .method(net::http_method::GET) .send().take(); REQUIRE(resp.http_code() == 200u); REQUIRE(resp.headers.count("Content-Type")); @@ -401,21 +401,21 @@ TEST_CASE("curly") { { auto req = net::request_builder() .url("https://httpbin.org/redirect/2") - .method(net::methods::get) + .method(net::http_method::GET) .send(); REQUIRE(req.take().http_code() == 200u); } { auto req = net::request_builder() .url("https://httpbin.org/absolute-redirect/2") - .method(net::methods::get) + .method(net::http_method::GET) .send(); REQUIRE(req.take().http_code() == 200u); } { auto req = net::request_builder() .url("https://httpbin.org/relative-redirect/2") - .method(net::methods::get) + .method(net::http_method::GET) .send(); REQUIRE(req.take().http_code() == 200u); } @@ -424,7 +424,7 @@ TEST_CASE("curly") { { auto req = net::request_builder() .url("https://httpbin.org/redirect/3") - .method(net::methods::get) + .method(net::http_method::GET) .redirections(0) .send(); REQUIRE(req.take().http_code() == 302u); @@ -432,23 +432,23 @@ TEST_CASE("curly") { { auto req = net::request_builder() .url("https://httpbin.org/redirect/3") - .method(net::methods::get) + .method(net::http_method::GET) .redirections(1) .send(); - REQUIRE(req.wait() == net::request::statuses::failed); + REQUIRE(req.wait() == net::req_status::failed); } { auto req = net::request_builder() .url("https://httpbin.org/redirect/3") - .method(net::methods::get) + .method(net::http_method::GET) .redirections(2) .send(); - REQUIRE(req.wait() == net::request::statuses::failed); + REQUIRE(req.wait() == net::req_status::failed); } { auto req = net::request_builder() .url("https://httpbin.org/redirect/3") - .method(net::methods::get) + .method(net::http_method::GET) .redirections(3) .send(); REQUIRE(req.take().http_code() == 200u); @@ -460,7 +460,7 @@ TEST_CASE("curly") { { auto resp = net::request_builder() .url("https://httpbin.org/anything") - .method(net::methods::put) + .method(net::http_method::PUT) .header("Content-Type", "application/json") .content(R"({"hello":"world"})") .send().take(); @@ -470,7 +470,7 @@ TEST_CASE("curly") { { auto resp = net::request_builder() .url("https://httpbin.org/anything") - .method(net::methods::post) + .method(net::http_method::POST) .header("Content-Type", "application/json") .content(R"({"hello":"world"})") .send().take(); @@ -480,7 +480,7 @@ TEST_CASE("curly") { { auto resp = net::request_builder() .url("https://httpbin.org/anything") - .method(net::methods::post) + .method(net::http_method::POST) .header("Content-Type", "application/x-www-form-urlencoded") .content("hello=world&world=hello") .send().take(); @@ -493,53 +493,53 @@ TEST_CASE("curly") { SECTION("ssl_verification") { { auto req0 = net::request_builder("https://expired.badssl.com") - .method(net::methods::head) + .method(net::http_method::HEAD) .verification(true) .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") - .method(net::methods::head) + .method(net::http_method::HEAD) .verification(true) .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") - .method(net::methods::head) + .method(net::http_method::HEAD) .verification(true) .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") - .method(net::methods::head) + .method(net::http_method::HEAD) .verification(true) .send(); - REQUIRE(req3.wait() == net::request::statuses::failed); + REQUIRE(req3.wait() == net::req_status::failed); } { auto req0 = net::request_builder("https://expired.badssl.com") - .method(net::methods::head) + .method(net::http_method::HEAD) .verification(false) .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") - .method(net::methods::head) + .method(net::http_method::HEAD) .verification(false) .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") - .method(net::methods::head) + .method(net::http_method::HEAD) .verification(false) .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") - .method(net::methods::head) + .method(net::http_method::HEAD) .verification(false) .send(); - REQUIRE(req3.wait() == net::request::statuses::done); + REQUIRE(req3.wait() == net::req_status::done); } } @@ -547,18 +547,18 @@ TEST_CASE("curly") { { auto req = net::request_builder("https://httpbin.org/anything") .verbose(true) - .method(net::methods::post) + .method(net::http_method::POST) .uploader() .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::methods::get) + .method(net::http_method::GET) .downloader() .send(); - REQUIRE(req.wait() == net::request::statuses::canceled); + REQUIRE(req.wait() == net::req_status::canceled); } } @@ -570,10 +570,10 @@ TEST_CASE("curly") { std::this_thread::sleep_for(std::chrono::milliseconds(10)); ++call_once; REQUIRE(request.is_done()); - REQUIRE(request.status() == net::request::statuses::done); + REQUIRE(request.status() == net::req_status::done); REQUIRE(request.take().http_code() == 200u); }).send(); - REQUIRE(req.wait_callback() == net::request::statuses::empty); + REQUIRE(req.wait_callback() == net::req_status::empty); REQUIRE_FALSE(req.get_callback_exception()); REQUIRE(call_once.load() == 1u); } @@ -584,10 +584,10 @@ TEST_CASE("curly") { std::this_thread::sleep_for(std::chrono::milliseconds(10)); ++call_once; 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()); }).send(); - REQUIRE(req.wait_callback() == net::request::statuses::failed); + REQUIRE(req.wait_callback() == net::req_status::failed); REQUIRE_FALSE(req.get_callback_exception()); REQUIRE(call_once.load() == 1u); } @@ -599,10 +599,10 @@ TEST_CASE("curly") { std::this_thread::sleep_for(std::chrono::milliseconds(10)); ++call_once; 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()); }).send(); - REQUIRE(req.wait_callback() == net::request::statuses::timeout); + REQUIRE(req.wait_callback() == net::req_status::timeout); REQUIRE_FALSE(req.get_callback_exception()); REQUIRE(call_once.load() == 1u); } @@ -613,11 +613,11 @@ TEST_CASE("curly") { 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.status() == net::req_status::canceled); REQUIRE(request.get_error().empty()); }).send(); 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(call_once.load() == 1u); } @@ -631,7 +631,7 @@ TEST_CASE("curly") { throw std::logic_error("my_logic_error"); } }).send(); - REQUIRE(req.wait_callback() == net::request::statuses::empty); + REQUIRE(req.wait_callback() == net::req_status::empty); REQUIRE(req.get_callback_exception()); try { std::rethrow_exception(req.get_callback_exception()); @@ -647,7 +647,7 @@ TEST_CASE("curly_examples") { SECTION("Get Requests") { // makes a GET request and async send it auto request = net::request_builder() - .method(net::methods::get) + .method(net::http_method::GET) .url("http://www.httpbin.org/get") .send(); @@ -675,7 +675,7 @@ TEST_CASE("curly_examples") { SECTION("Post Requests") { auto request = net::request_builder() - .method(net::methods::post) + .method(net::http_method::POST) .url("http://www.httpbin.org/post") .header("Content-Type", "application/json") .content(R"({"hello" : "world"})") @@ -785,7 +785,7 @@ TEST_CASE("curly_examples") { }; net::request_builder() - .method(net::methods::post) + .method(net::http_method::POST) .url("https://httpbin.org/anything") .uploader("image.jpeg") .send().take(); From 36518720d2d0e0ca7e11d059492ccce45ae50356 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 7 Jul 2019 08:50:36 +0700 Subject: [PATCH 5/8] rename auto_performer to performer --- README.md | 2 +- headers/curly.hpp/curly.hpp | 6 +++--- sources/curly.hpp/curly.cpp | 10 +++++----- untests/curly_tests.cpp | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0c5b396..78c0568 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ target_link_libraries(your_project_target curly.hpp) namespace net = curly_hpp; // 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 net::perform(); diff --git a/headers/curly.hpp/curly.hpp b/headers/curly.hpp/curly.hpp index 1f9a8d4..505fb9c 100644 --- a/headers/curly.hpp/curly.hpp +++ b/headers/curly.hpp/curly.hpp @@ -306,10 +306,10 @@ namespace curly_hpp namespace curly_hpp { - class auto_performer final { + class performer final { public: - auto_performer(); - ~auto_performer() noexcept; + performer(); + ~performer() noexcept; time_ms_t wait_activity() const noexcept; void wait_activity(time_ms_t ms) noexcept; diff --git a/sources/curly.hpp/curly.cpp b/sources/curly.hpp/curly.cpp index 560501d..d294bc6 100644 --- a/sources/curly.hpp/curly.cpp +++ b/sources/curly.hpp/curly.cpp @@ -848,13 +848,13 @@ namespace curly_hpp // ----------------------------------------------------------------------------- // -// auto_performer +// performer // // ----------------------------------------------------------------------------- namespace curly_hpp { - auto_performer::auto_performer() { + performer::performer() { thread_ = std::thread([this](){ while ( !done_ ) { curly_hpp::perform(); @@ -863,18 +863,18 @@ namespace curly_hpp }); } - auto_performer::~auto_performer() noexcept { + performer::~performer() noexcept { done_.store(true); if ( thread_.joinable() ) { thread_.join(); } } - time_ms_t auto_performer::wait_activity() const noexcept { + time_ms_t performer::wait_activity() const noexcept { 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; } } diff --git a/untests/curly_tests.cpp b/untests/curly_tests.cpp index cdf9d7a..dc90b04 100644 --- a/untests/curly_tests.cpp +++ b/untests/curly_tests.cpp @@ -80,7 +80,7 @@ namespace } TEST_CASE("curly") { - net::auto_performer performer; + net::performer performer; performer.wait_activity(net::time_ms_t(10)); SECTION("wait") { @@ -642,7 +642,7 @@ TEST_CASE("curly") { } TEST_CASE("curly_examples") { - net::auto_performer performer; + net::performer performer; SECTION("Get Requests") { // makes a GET request and async send it From f9507e73a299410bd6feb34c9cc2dbdbeb87b367 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 7 Jul 2019 13:06:04 +0700 Subject: [PATCH 6/8] add DELETE, PATCH and OPTIONS http methods #11 --- headers/curly.hpp/curly.hpp | 5 +- sources/curly.hpp/curly.cpp | 16 +++++ untests/curly_tests.cpp | 113 ++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/headers/curly.hpp/curly.hpp b/headers/curly.hpp/curly.hpp index 505fb9c..834b939 100644 --- a/headers/curly.hpp/curly.hpp +++ b/headers/curly.hpp/curly.hpp @@ -43,7 +43,10 @@ namespace curly_hpp PUT, GET, HEAD, - POST + POST, + PATCH, + DELETE, + OPTIONS }; class upload_handler { diff --git a/sources/curly.hpp/curly.cpp b/sources/curly.hpp/curly.cpp index d294bc6..c33bf0e 100644 --- a/sources/curly.hpp/curly.cpp +++ b/sources/curly.hpp/curly.cpp @@ -311,6 +311,22 @@ namespace curly_hpp curl_easy_setopt(curlh_.get(), CURLOPT_POSTFIELDSIZE_LARGE, static_cast(breq_.uploader()->size())); 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(breq_.uploader()->size())); + break; + case http_method::DELETE: + 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(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: throw exception("curly_hpp: unexpected request method"); } diff --git a/untests/curly_tests.cpp b/untests/curly_tests.cpp index dc90b04..1fef52c 100644 --- a/untests/curly_tests.cpp +++ b/untests/curly_tests.cpp @@ -205,6 +205,18 @@ TEST_CASE("curly") { .method(net::http_method::POST) .send(); 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::DELETE) + .send(); + REQUIRE(req5.take().http_code() == 405u); } { auto req0 = net::request_builder() @@ -230,6 +242,18 @@ TEST_CASE("curly") { .method(net::http_method::POST) .send(); 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::DELETE) + .send(); + REQUIRE(req5.take().http_code() == 405u); } { auto req0 = net::request_builder() @@ -255,6 +279,33 @@ TEST_CASE("curly") { .method(net::http_method::POST) .send(); 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::DELETE) + .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")); } } @@ -287,6 +338,20 @@ TEST_CASE("curly") { .send(); 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::DELETE) + .send(); + REQUIRE(req.take().http_code() == 203u); + } } SECTION("request_inspection") { @@ -368,6 +433,18 @@ TEST_CASE("curly") { } SECTION("binary") { + { + auto resp = net::request_builder() + .url("https://httpbin.org/image/png") + .method(net::http_method::HEAD) + .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_FALSE(resp.content.size()); + } { auto resp = net::request_builder() .url("https://httpbin.org/image/png") @@ -375,12 +452,26 @@ TEST_CASE("curly") { .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(!std::memcmp( std::move(resp.content).data().data(), untests::png_data, untests::png_data_length)); } + { + auto resp = net::request_builder() + .url("https://httpbin.org/image/jpeg") + .method(net::http_method::HEAD) + .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_FALSE(resp.content.size()); + } { auto resp = net::request_builder() .url("https://httpbin.org/image/jpeg") @@ -388,7 +479,9 @@ TEST_CASE("curly") { .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(!std::memcmp( std::as_const(resp.content).data().data(), @@ -467,6 +560,26 @@ TEST_CASE("curly") { 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::PATCH) + .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::DELETE) + .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") From 8f1dad571faf1d233690e4855b7890e9345d4719 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 7 Jul 2019 14:49:54 +0700 Subject: [PATCH 7/8] add progress information to request #13 --- headers/curly.hpp/curly.hpp | 22 +++++++++ sources/curly.hpp/curly.cpp | 92 +++++++++++++++++++++++++++++++++++++ untests/curly_tests.cpp | 24 ++++++++++ 3 files changed, 138 insertions(+) diff --git a/headers/curly.hpp/curly.hpp b/headers/curly.hpp/curly.hpp index 834b939..acdd3f3 100644 --- a/headers/curly.hpp/curly.hpp +++ b/headers/curly.hpp/curly.hpp @@ -62,9 +62,18 @@ namespace curly_hpp virtual std::size_t write(const char* src, std::size_t size) = 0; }; + class progress_handler { + public: + virtual ~progress_handler() {} + virtual float update( + std::size_t dnow, std::size_t dtotal, + std::size_t unow, std::size_t utotal) = 0; + }; + using callback_t = std::function; using uploader_uptr = std::unique_ptr; using downloader_uptr = std::unique_ptr; + using progressor_uptr = std::unique_ptr; } namespace curly_hpp @@ -169,6 +178,7 @@ namespace curly_hpp headers_t headers; uploader_uptr uploader; downloader_uptr downloader; + progressor_uptr progressor; private: http_code_t http_code_{0u}; }; @@ -193,6 +203,7 @@ namespace curly_hpp request(internal_state_ptr); bool cancel() noexcept; + float progress() const noexcept; req_status status() const noexcept; bool is_done() const noexcept; @@ -246,6 +257,7 @@ namespace curly_hpp request_builder& callback(callback_t c) noexcept; request_builder& uploader(uploader_uptr u) noexcept; request_builder& downloader(downloader_uptr d) noexcept; + request_builder& progressor(progressor_uptr p) noexcept; const std::string& url() const noexcept; http_method method() const noexcept; @@ -270,6 +282,9 @@ namespace curly_hpp downloader_uptr& downloader() noexcept; const downloader_uptr& downloader() const noexcept; + progressor_uptr& progressor() noexcept; + const progressor_uptr& progressor() const noexcept; + request send(); template < typename Callback > @@ -289,6 +304,12 @@ namespace curly_hpp static_assert(std::is_base_of_v); return downloader(std::make_unique(std::forward(args)...)); } + + template < typename Progressor, typename... Args > + request_builder& progressor(Args&&... args) { + static_assert(std::is_base_of_v); + return progressor(std::make_unique(std::forward(args)...)); + } private: std::string url_; http_method method_{http_method::GET}; @@ -304,6 +325,7 @@ namespace curly_hpp callback_t callback_; uploader_uptr uploader_; downloader_uptr downloader_; + progressor_uptr progressor_; }; } diff --git a/sources/curly.hpp/curly.cpp b/sources/curly.hpp/curly.cpp index c33bf0e..389fcf3 100644 --- a/sources/curly.hpp/curly.cpp +++ b/sources/curly.hpp/curly.cpp @@ -77,6 +77,23 @@ namespace private: data_t& data_; }; + + class default_progressor final : public progress_handler { + public: + default_progressor() = default; + + float update( + std::size_t dnow, std::size_t dtotal, + std::size_t unow, std::size_t utotal) override + { + double now_d = static_cast(dnow + unow); + double total_d = static_cast(dtotal + utotal); + double progress_d = total_d > 0.0 + ? static_cast(now_d / total_d) + : 0.f; + return static_cast(std::min(std::max(progress_d, 0.0), 1.0)); + } + }; } // ----------------------------------------------------------------------------- @@ -253,6 +270,10 @@ namespace curly_hpp if ( !breq_.downloader() ) { breq_.downloader(&response_content_); } + + if ( !breq_.progressor() ) { + breq_.progressor(); + } } void enqueue(CURLM* curlm) { @@ -287,6 +308,10 @@ namespace curly_hpp curl_easy_setopt(curlh_.get(), CURLOPT_WRITEDATA, this); curl_easy_setopt(curlh_.get(), CURLOPT_WRITEFUNCTION, &s_download_callback_); + curl_easy_setopt(curlh_.get(), CURLOPT_NOPROGRESS, 0l); + curl_easy_setopt(curlh_.get(), CURLOPT_XFERINFODATA, this); + curl_easy_setopt(curlh_.get(), CURLOPT_XFERINFOFUNCTION, &s_progress_callback_); + curl_easy_setopt(curlh_.get(), CURLOPT_HEADERDATA, this); curl_easy_setopt(curlh_.get(), CURLOPT_HEADERFUNCTION, &s_header_callback_); @@ -364,6 +389,21 @@ namespace curly_hpp void dequeue(CURLM* curlm) noexcept { std::lock_guard guard(mutex_); if ( curlh_ ) { + curl_easy_setopt(curlh_.get(), CURLOPT_PRIVATE, nullptr); + + curl_easy_setopt(curlh_.get(), CURLOPT_READDATA, nullptr); + curl_easy_setopt(curlh_.get(), CURLOPT_READFUNCTION, nullptr); + + curl_easy_setopt(curlh_.get(), CURLOPT_WRITEDATA, nullptr); + curl_easy_setopt(curlh_.get(), CURLOPT_WRITEFUNCTION, nullptr); + + curl_easy_setopt(curlh_.get(), CURLOPT_NOPROGRESS, 1l); + curl_easy_setopt(curlh_.get(), CURLOPT_XFERINFODATA, nullptr); + curl_easy_setopt(curlh_.get(), CURLOPT_XFERINFOFUNCTION, nullptr); + + curl_easy_setopt(curlh_.get(), CURLOPT_HEADERDATA, nullptr); + curl_easy_setopt(curlh_.get(), CURLOPT_HEADERFUNCTION, nullptr); + curl_multi_remove_handle(curlm, curlh_.get()); curlh_.reset(); } @@ -392,12 +432,14 @@ namespace curly_hpp response_.headers = std::move(response_headers_); response_.uploader = std::move(breq_.uploader()); response_.downloader = std::move(breq_.downloader()); + response_.progressor = std::move(breq_.progressor()); } catch (...) { status_ = req_status::failed; cvar_.notify_all(); return false; } + progress_ = 1.f; status_ = req_status::done; error_.clear(); @@ -448,6 +490,11 @@ namespace curly_hpp return true; } + float progress() const noexcept { + std::lock_guard guard(mutex_); + return progress_; + } + req_status status() const noexcept { std::lock_guard guard(mutex_); return status_; @@ -553,6 +600,13 @@ namespace curly_hpp return self->download_callback_(ptr, size * nmemb); } + static int s_progress_callback_( + void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) noexcept + { + auto* self = static_cast(clientp); + return self->progress_callback(dlnow, dltotal, ulnow, ultotal); + } + static std::size_t s_header_callback_( char* buffer, std::size_t size, std::size_t nitems, void* userdata) noexcept { @@ -589,6 +643,26 @@ namespace curly_hpp } } + int progress_callback( + curl_off_t dlnow, curl_off_t dltotal, + curl_off_t ulnow, curl_off_t ultotal) noexcept + { + try { + std::lock_guard guard(mutex_); + + std::size_t dnow_sz = dlnow > 0 ? static_cast(dlnow) : 0u; + std::size_t dtotal_sz = dltotal > 0 ? static_cast(dltotal) : 0u; + + std::size_t unow_sz = ulnow > 0 ? static_cast(ulnow) : 0u; + std::size_t utotal_sz = ultotal > 0 ? static_cast(ultotal) : 0u; + + progress_ = breq_.progressor()->update(dnow_sz, dtotal_sz, unow_sz, utotal_sz); + return 0; + } catch (...) { + return 1; + } + } + std::size_t header_callback_(const char* src, std::size_t size) noexcept { try { std::lock_guard guard(mutex_); @@ -631,6 +705,7 @@ namespace curly_hpp bool callbacked_{false}; std::exception_ptr callback_exception_{nullptr}; private: + float progress_{0.f}; req_status status_{req_status::pending}; std::string error_{"Unknown error"}; private: @@ -650,6 +725,10 @@ namespace curly_hpp return state_->cancel(); } + float request::progress() const noexcept { + return state_->progress(); + } + req_status request::status() const noexcept { return state_->status(); } @@ -787,6 +866,11 @@ namespace curly_hpp return *this; } + request_builder& request_builder::progressor(progressor_uptr p) noexcept { + progressor_ = std::move(p); + return *this; + } + const std::string& request_builder::url() const noexcept { return url_; } @@ -855,6 +939,14 @@ namespace curly_hpp return downloader_; } + progressor_uptr& request_builder::progressor() noexcept { + return progressor_; + } + + const progressor_uptr& request_builder::progressor() const noexcept { + return progressor_; + } + request request_builder::send() { auto sreq = std::make_shared(std::move(*this)); new_handles.enqueue(sreq); diff --git a/untests/curly_tests.cpp b/untests/curly_tests.cpp index 1fef52c..e90c5cd 100644 --- a/untests/curly_tests.cpp +++ b/untests/curly_tests.cpp @@ -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 download(std::string url) { return netex::make_promise([ url = std::move(url) @@ -673,6 +689,14 @@ TEST_CASE("curly") { .send(); REQUIRE(req.wait() == net::req_status::canceled); } + { + auto req = net::request_builder("https://httpbin.org/anything") + .verbose(true) + .method(net::http_method::GET) + .progressor() + .send(); + REQUIRE(req.wait() == net::req_status::canceled); + } } SECTION("callback") { From fb124a22086439412f1691f4d3773cda8cc791aa Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 7 Jul 2019 15:15:34 +0700 Subject: [PATCH 8/8] rename DELETE method to DEL --- headers/curly.hpp/curly.hpp | 2 +- sources/curly.hpp/curly.cpp | 12 ++++++------ untests/curly_tests.cpp | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/headers/curly.hpp/curly.hpp b/headers/curly.hpp/curly.hpp index acdd3f3..68a1cc6 100644 --- a/headers/curly.hpp/curly.hpp +++ b/headers/curly.hpp/curly.hpp @@ -40,12 +40,12 @@ namespace curly_hpp using time_point_t = std::chrono::steady_clock::time_point; enum class http_method { + DEL, PUT, GET, HEAD, POST, PATCH, - DELETE, OPTIONS }; diff --git a/sources/curly.hpp/curly.cpp b/sources/curly.hpp/curly.cpp index 389fcf3..6b268cf 100644 --- a/sources/curly.hpp/curly.cpp +++ b/sources/curly.hpp/curly.cpp @@ -320,6 +320,12 @@ namespace curly_hpp curl_easy_setopt(curlh_.get(), CURLOPT_VERBOSE, breq_.verbose() ? 1l : 0l); switch ( breq_.method() ) { + 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(breq_.uploader()->size())); + break; case http_method::PUT: curl_easy_setopt(curlh_.get(), CURLOPT_UPLOAD, 1l); curl_easy_setopt(curlh_.get(), CURLOPT_INFILESIZE_LARGE, @@ -342,12 +348,6 @@ namespace curly_hpp curl_easy_setopt(curlh_.get(), CURLOPT_INFILESIZE_LARGE, static_cast(breq_.uploader()->size())); break; - case http_method::DELETE: - 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(breq_.uploader()->size())); - break; case http_method::OPTIONS: curl_easy_setopt(curlh_.get(), CURLOPT_CUSTOMREQUEST, "OPTIONS"); curl_easy_setopt(curlh_.get(), CURLOPT_NOBODY, 1l); diff --git a/untests/curly_tests.cpp b/untests/curly_tests.cpp index e90c5cd..5905d13 100644 --- a/untests/curly_tests.cpp +++ b/untests/curly_tests.cpp @@ -230,7 +230,7 @@ TEST_CASE("curly") { auto req5 = net::request_builder() .url("https://httpbin.org/put") - .method(net::http_method::DELETE) + .method(net::http_method::DEL) .send(); REQUIRE(req5.take().http_code() == 405u); } @@ -267,7 +267,7 @@ TEST_CASE("curly") { auto req5 = net::request_builder() .url("https://httpbin.org/get") - .method(net::http_method::DELETE) + .method(net::http_method::DEL) .send(); REQUIRE(req5.take().http_code() == 405u); } @@ -304,7 +304,7 @@ TEST_CASE("curly") { auto req5 = net::request_builder() .url("https://httpbin.org/post") - .method(net::http_method::DELETE) + .method(net::http_method::DEL) .send(); REQUIRE(req5.take().http_code() == 405u); } @@ -364,7 +364,7 @@ TEST_CASE("curly") { { auto req = net::request_builder() .url("https://httpbin.org/status/203") - .method(net::http_method::DELETE) + .method(net::http_method::DEL) .send(); REQUIRE(req.take().http_code() == 203u); } @@ -589,7 +589,7 @@ TEST_CASE("curly") { { auto resp = net::request_builder() .url("https://httpbin.org/anything") - .method(net::http_method::DELETE) + .method(net::http_method::DEL) .header("Content-Type", "application/json") .content(R"({"hello":"world"})") .send().take();