diff --git a/headers/curly.hpp/curly.hpp b/headers/curly.hpp/curly.hpp index 38844af..874e305 100644 --- a/headers/curly.hpp/curly.hpp +++ b/headers/curly.hpp/curly.hpp @@ -104,10 +104,6 @@ namespace curly_hpp response& operator=(const response&) = delete; explicit response(response_code_t rc) noexcept; - response(response_code_t rc, headers_t h) noexcept; - response(response_code_t rc, content_t c) noexcept; - response(response_code_t rc, headers_t h, content_t c) noexcept; - response_code_t code() const noexcept; content_t& content() noexcept; @@ -209,7 +205,7 @@ namespace curly_hpp downloader_uptr& downloader() noexcept; const downloader_uptr& downloader() const noexcept; - request perform(); + request send(); template < typename Uploader, typename... Args > request_builder& uploader(Args&&... args) { @@ -238,10 +234,10 @@ namespace curly_hpp namespace curly_hpp { - class auto_updater { + class auto_performer { public: - auto_updater(); - ~auto_updater() noexcept; + auto_performer(); + ~auto_performer() noexcept; time_ms_t wait_activity() const noexcept; void wait_activity(time_ms_t ms) noexcept; @@ -254,9 +250,6 @@ namespace curly_hpp namespace curly_hpp { - void update(); + void perform(); void wait_activity(time_ms_t ms); - - request perform(request_builder& rb); - request perform(request_builder&& rb); } diff --git a/sources/curly.cpp b/sources/curly.cpp index 53bfa72..41620f1 100644 --- a/sources/curly.cpp +++ b/sources/curly.cpp @@ -230,19 +230,6 @@ namespace curly_hpp response::response(response_code_t rc) noexcept : code_(rc) {} - response::response(response_code_t rc, headers_t h) noexcept - : code_(rc) - , headers_(std::move(h)) {} - - response::response(response_code_t rc, content_t c) noexcept - : code_(rc) - , content_(std::move(c)) {} - - response::response(response_code_t rc, headers_t h, content_t c) noexcept - : code_(rc) - , headers_(std::move(h)) - , content_(std::move(c)) {} - response_code_t response::code() const noexcept { return code_; } @@ -376,16 +363,28 @@ namespace curly_hpp return false; } - long response_code = 0; - curl_easy_getinfo( + long code = 0; + if ( CURLE_OK != curl_easy_getinfo( handle_.get(), CURLINFO_RESPONSE_CODE, - &response_code); + &code) || !code ) + { + status_ = statuses::failed; + cond_var_.notify_all(); + return false; + } - response_ = response( - static_cast(response_code), - std::move(response_headers_), - std::move(response_content_)); + try { + response_ = response(static_cast(code)); + response_.content() = std::move(response_content_); + response_.headers() = std::move(response_headers_); + response_.uploader() = std::move(uploader_); + response_.downloader() = std::move(downloader_); + } catch (...) { + status_ = statuses::failed; + cond_var_.notify_all(); + return false; + } status_ = statuses::done; error_.clear(); @@ -716,40 +715,65 @@ namespace curly_hpp return downloader_; } - request request_builder::perform() { - return ::perform(*this); + request request_builder::send() { + return curl_state::with([this](CURLM* curlm){ + handle_t handle{ + curl_easy_init(), + &curl_easy_cleanup}; + + if ( !handle ) { + throw exception("curly_hpp: failed to curl_easy_init"); + } + + auto sreq = std::make_shared( + std::move(handle), + std::move(*this)); + + if ( CURLM_OK != curl_multi_add_handle(curlm, sreq->handle().get()) ) { + throw exception("curly_hpp: failed to curl_multi_add_handle"); + } + + try { + handles.emplace(sreq->handle().get(), sreq); + } catch (...) { + curl_multi_remove_handle(curlm, sreq->handle().get()); + throw; + } + + return request(sreq); + }); } } // ----------------------------------------------------------------------------- // -// auto_updater +// auto_performer // // ----------------------------------------------------------------------------- namespace curly_hpp { - auto_updater::auto_updater() { + auto_performer::auto_performer() { thread_ = std::thread([this](){ while ( !done_ ) { - ::update(); - ::wait_activity(wait_activity()); + curly_hpp::perform(); + curly_hpp::wait_activity(wait_activity()); } }); } - auto_updater::~auto_updater() noexcept { + auto_performer::~auto_performer() noexcept { done_.store(true); if ( thread_.joinable() ) { thread_.join(); } } - time_ms_t auto_updater::wait_activity() const noexcept { + time_ms_t auto_performer::wait_activity() const noexcept { return wait_activity_; } - void auto_updater::wait_activity(time_ms_t ms) noexcept { + void auto_performer::wait_activity(time_ms_t ms) noexcept { wait_activity_ = ms; } } @@ -762,7 +786,7 @@ namespace curly_hpp namespace curly_hpp { - void update() { + void perform() { curl_state::with([](CURLM* curlm){ int running_handles = 0; curl_multi_perform(curlm, &running_handles); @@ -809,26 +833,4 @@ namespace curly_hpp curl_multi_wait(curlm, nullptr, 0, static_cast(ms.count()), nullptr); }); } - - request perform(request_builder& rb) { - return perform(std::move(rb)); - } - - request perform(request_builder&& rb) { - return curl_state::with([&rb](CURLM* curlm){ - handle_t handle{curl_easy_init(), &curl_easy_cleanup}; - if ( !handle ) { - throw exception("curly_hpp: failed to curl_easy_init"); - } - auto sreq = std::make_shared(std::move(handle), std::move(rb)); - curl_multi_add_handle(curlm, sreq->handle().get()); - try { - handles.emplace(sreq->handle().get(), sreq); - } catch (...) { - curl_multi_remove_handle(curlm, sreq->handle().get()); - throw; - } - return request(sreq); - }); - } } diff --git a/untests/curly_tests.cpp b/untests/curly_tests.cpp index 0cd393a..7c8f277 100644 --- a/untests/curly_tests.cpp +++ b/untests/curly_tests.cpp @@ -48,10 +48,10 @@ namespace } TEST_CASE("curly"){ - net::auto_updater updater; + net::auto_performer performer; SECTION("wait") { - auto req = net::request_builder("https://httpbin.org/delay/1").perform(); + 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); @@ -61,7 +61,7 @@ TEST_CASE("curly"){ } SECTION("error") { - auto req = net::request_builder("|||").perform(); + auto req = net::request_builder("|||").send(); REQUIRE(req.wait() == net::request::statuses::failed); REQUIRE(req.status() == net::request::statuses::failed); REQUIRE_FALSE(req.error().empty()); @@ -69,13 +69,13 @@ TEST_CASE("curly"){ SECTION("cancel") { { - auto req = net::request_builder("https://httpbin.org/delay/1").perform(); + auto req = net::request_builder("https://httpbin.org/delay/1").send(); REQUIRE(req.cancel()); REQUIRE(req.status() == net::request::statuses::canceled); REQUIRE(req.error().empty()); } { - auto req = net::request_builder("https://httpbin.org/status/200").perform(); + auto req = net::request_builder("https://httpbin.org/status/200").send(); REQUIRE(req.wait() == net::request::statuses::done); REQUIRE_FALSE(req.cancel()); REQUIRE(req.status() == net::request::statuses::done); @@ -85,13 +85,13 @@ TEST_CASE("curly"){ SECTION("get") { { - auto req = net::request_builder("https://httpbin.org/status/204").perform(); + auto req = net::request_builder("https://httpbin.org/status/204").send(); auto resp = req.get(); REQUIRE(req.status() == net::request::statuses::empty); REQUIRE(resp.code() == 204u); } { - auto req = net::request_builder("https://httpbin.org/delay/2").perform(); + auto req = net::request_builder("https://httpbin.org/delay/2").send(); REQUIRE(req.cancel()); REQUIRE_THROWS_AS(req.get(), net::exception); REQUIRE(req.status() == net::request::statuses::canceled); @@ -99,7 +99,7 @@ TEST_CASE("curly"){ { auto req = net::request_builder("https://httpbin.org/delay/2") .response_timeout(net::time_sec_t(0)) - .perform(); + .send(); REQUIRE(req.wait() == net::request::statuses::timeout); REQUIRE_THROWS_AS(req.get(), net::exception); REQUIRE(req.status() == net::request::statuses::timeout); @@ -111,75 +111,75 @@ TEST_CASE("curly"){ auto req0 = net::request_builder() .url("https://httpbin.org/put") .method(net::methods::put) - .perform(); + .send(); REQUIRE(req0.get().code() == 200u); auto req1 = net::request_builder() .url("https://httpbin.org/put") .method(net::methods::get) - .perform(); + .send(); REQUIRE(req1.get().code() == 405u); auto req2 = net::request_builder() .url("https://httpbin.org/put") .method(net::methods::head) - .perform(); + .send(); REQUIRE(req2.get().code() == 405u); auto req3 = net::request_builder() .url("https://httpbin.org/put") .method(net::methods::post) - .perform(); + .send(); REQUIRE(req3.get().code() == 405u); } { auto req0 = net::request_builder() .url("https://httpbin.org/get") .method(net::methods::put) - .perform(); + .send(); REQUIRE(req0.get().code() == 405u); auto req1 = net::request_builder() .url("https://httpbin.org/get") .method(net::methods::get) - .perform(); + .send(); REQUIRE(req1.get().code() == 200u); auto req2 = net::request_builder() .url("https://httpbin.org/get") .method(net::methods::head) - .perform(); + .send(); REQUIRE(req2.get().code() == 200u); auto req3 = net::request_builder() .url("https://httpbin.org/get") .method(net::methods::post) - .perform(); + .send(); REQUIRE(req3.get().code() == 405u); } { auto req0 = net::request_builder() .url("https://httpbin.org/post") .method(net::methods::put) - .perform(); + .send(); REQUIRE(req0.get().code() == 405u); auto req1 = net::request_builder() .url("https://httpbin.org/post") .method(net::methods::get) - .perform(); + .send(); REQUIRE(req1.get().code() == 405u); auto req2 = net::request_builder() .url("https://httpbin.org/post") .method(net::methods::head) - .perform(); + .send(); REQUIRE(req2.get().code() == 405u); auto req3 = net::request_builder() .url("https://httpbin.org/post") .method(net::methods::post) - .perform(); + .send(); REQUIRE(req3.get().code() == 200u); } } @@ -189,28 +189,28 @@ TEST_CASE("curly"){ auto req = net::request_builder() .url("https://httpbin.org/status/200") .method(net::methods::put) - .perform(); + .send(); REQUIRE(req.get().code() == 200u); } { auto req = net::request_builder() .url("https://httpbin.org/status/201") .method(net::methods::get) - .perform(); + .send(); REQUIRE(req.get().code() == 201u); } { auto req = net::request_builder() .url("https://httpbin.org/status/202") .method(net::methods::head) - .perform(); + .send(); REQUIRE(req.get().code() == 202u); } { auto req = net::request_builder() .url("https://httpbin.org/status/203") .method(net::methods::post) - .perform(); + .send(); REQUIRE(req.get().code() == 203u); } } @@ -220,7 +220,7 @@ TEST_CASE("curly"){ .url("https://httpbin.org/headers") .header("Custom-Header-1", "custom_header_value_1") .header("Custom-Header-2", "custom header value 2") - .perform(); + .send(); const auto resp = req.get(); const auto content_j = json::parse(resp.content().as_string_view()); REQUIRE(content_j["headers"]["Custom-Header-1"] == "custom_header_value_1"); @@ -232,7 +232,7 @@ TEST_CASE("curly"){ auto req = net::request_builder() .url("https://httpbin.org/response-headers?hello=world&world=hello") .method(net::methods::get) - .perform(); + .send(); const auto resp = req.get(); const auto content_j = json::parse(resp.content().as_string_view()); REQUIRE(content_j["hello"] == "world"); @@ -242,7 +242,7 @@ TEST_CASE("curly"){ auto req = net::request_builder() .url("https://httpbin.org/response-headers?hello=world&world=hello") .method(net::methods::post) - .perform(); + .send(); const auto resp = req.get(); const auto content_j = json::parse(resp.content().as_string_view()); REQUIRE(content_j["hello"] == "world"); @@ -254,7 +254,7 @@ TEST_CASE("curly"){ { auto req = net::request_builder() .url("https://httpbin.org/base64/SFRUUEJJTiBpcyBhd2Vzb21l") - .perform(); + .send(); const auto resp = req.get(); REQUIRE(resp.content().as_string_view() == "HTTPBIN is awesome"); REQUIRE(req.error().empty()); @@ -263,7 +263,7 @@ TEST_CASE("curly"){ auto req = net::request_builder() .url("https://httpbin.org/delay/10") .response_timeout(net::time_sec_t(0)) - .perform(); + .send(); REQUIRE(req.wait() == net::request::statuses::timeout); REQUIRE_FALSE(req.error().empty()); } @@ -271,7 +271,7 @@ TEST_CASE("curly"){ auto req = net::request_builder() .url("https://httpbin.org/delay/10") .response_timeout(net::time_sec_t(1)) - .perform(); + .send(); REQUIRE(req.wait() == net::request::statuses::timeout); REQUIRE_FALSE(req.error().empty()); } @@ -282,7 +282,7 @@ TEST_CASE("curly"){ auto resp = net::request_builder() .url("https://httpbin.org/image/png") .method(net::methods::get) - .perform().get(); + .send().get(); REQUIRE(resp.code() == 200u); REQUIRE(resp.headers().count("Content-Type")); REQUIRE(resp.headers().at("Content-Type") == "image/png"); @@ -293,7 +293,7 @@ TEST_CASE("curly"){ auto resp = net::request_builder() .url("https://httpbin.org/image/jpeg") .method(net::methods::get) - .perform().get(); + .send().get(); REQUIRE(resp.code() == 200u); REQUIRE(resp.headers().count("Content-Type")); REQUIRE(resp.headers().at("Content-Type") == "image/jpeg"); @@ -307,21 +307,21 @@ TEST_CASE("curly"){ auto req = net::request_builder() .url("https://httpbin.org/redirect/2") .method(net::methods::get) - .perform(); + .send(); REQUIRE(req.get().code() == 200u); } { auto req = net::request_builder() .url("https://httpbin.org/absolute-redirect/2") .method(net::methods::get) - .perform(); + .send(); REQUIRE(req.get().code() == 200u); } { auto req = net::request_builder() .url("https://httpbin.org/relative-redirect/2") .method(net::methods::get) - .perform(); + .send(); REQUIRE(req.get().code() == 200u); } } @@ -333,7 +333,7 @@ TEST_CASE("curly"){ .method(net::methods::put) .header("Content-Type", "application/json") .content(R"({"hello":"world"})") - .perform().get(); + .send().get(); const auto content_j = json::parse(resp.content().as_string_view()); REQUIRE(content_j["data"] == R"({"hello":"world"})"); } @@ -343,7 +343,7 @@ TEST_CASE("curly"){ .method(net::methods::post) .header("Content-Type", "application/json") .content(R"({"hello":"world"})") - .perform().get(); + .send().get(); const auto content_j = json::parse(resp.content().as_string_view()); REQUIRE(content_j["data"] == R"({"hello":"world"})"); } @@ -353,7 +353,7 @@ TEST_CASE("curly"){ .method(net::methods::post) .header("Content-Type", "application/x-www-form-urlencoded") .content("hello=world&world=hello") - .perform().get(); + .send().get(); const auto content_j = json::parse(resp.content().as_string_view()); REQUIRE(content_j["form"]["hello"] == "world"); REQUIRE(content_j["form"]["world"] == "hello");