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")