diff --git a/README.md b/README.md index 0dd77d9..3906430 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ target_link_libraries(myProject PRIVATE libcpp-http-client CURL::libcurl) Below you can see the simplest use case sending QueryString parameters to an API via HTTP GET. > [!IMPORTANT] -> Please do not use it this way, if more than one call will be made . You do not use the non-blocking +> Please do not use it this way, if more than one call will be made. You do not use the non-blocking > feature in this way. Just keep reading... ```cpp @@ -70,10 +70,13 @@ using namespace lklibs; int main() { - HttpClient httpClient; + HttpRequest httpRequest("https://api.myproject.com"); // The simplest but slowest method if multiple calls will be made - auto response = httpClient.getRequest("https://api.myproject.com?param1=7¶m2=test").get(); + auto response = httpRequest + .setQueryString("param1=7¶m2=test") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -98,13 +101,17 @@ using namespace lklibs; int main() { - HttpClient httpClient; - - auto response1 = httpClient.getRequest("https://api.myproject.com/foo").get(); - auto response2 = httpClient.getRequest("https://api.myproject.com/bar").get(); - auto response3 = httpClient.getRequest("https://api.myproject.com/baz").get(); - auto response4 = httpClient.getRequest("https://api.myproject.com/qux").get(); - auto response5 = httpClient.getRequest("https://api.myproject.com/quux").get(); + HttpRequest httpRequest1("https://api.myproject.com/foo"); + HttpRequest httpRequest2("https://api.myproject.com/bar"); + HttpRequest httpRequest3("https://api.myproject.com/baz"); + HttpRequest httpRequest4("https://api.myproject.com/qux"); + HttpRequest httpRequest5("https://api.myproject.com/quux"); + + auto response1 = httpRequest1.send().get(); + auto response2 = httpRequest2.send().get(); + auto response3 = httpRequest3.send().get(); + auto response4 = httpRequest4.send().get(); + auto response5 = httpRequest5.send().get(); // Takes 2.5 seconds in total @@ -124,13 +131,17 @@ using namespace lklibs; int main() { - HttpClient httpClient; - - auto future1 = httpClient.getRequest("https://api.myproject.com/foo"); - auto future2 = httpClient.getRequest("https://api.myproject.com/bar"); - auto future3 = httpClient.getRequest("https://api.myproject.com/baz"); - auto future4 = httpClient.getRequest("https://api.myproject.com/qux"); - auto future5 = httpClient.getRequest("https://api.myproject.com/quux"); + HttpRequest httpRequest1("https://api.myproject.com/foo"); + HttpRequest httpRequest2("https://api.myproject.com/bar"); + HttpRequest httpRequest3("https://api.myproject.com/baz"); + HttpRequest httpRequest4("https://api.myproject.com/qux"); + HttpRequest httpRequest5("https://api.myproject.com/quux"); + + auto future1 = httpRequest.send(); + auto future2 = httpRequest.send(); + auto future3 = httpRequest.send(); + auto future4 = httpRequest.send(); + auto future5 = httpRequest.send(); auto response1 = future1.get(); auto response2 = future2.get(); @@ -144,7 +155,7 @@ int main() { } ``` -All functions in the library return a future and allow the next line to run without blocking the flow. +**"send"** function in the library return a future and allow the next line to run without blocking the flow. ## What does exception free mean? @@ -166,9 +177,9 @@ using namespace lklibs; int main() { - HttpClient httpClient; + HttpRequest httpRequest("https://www.myinvalidurl.com"); - auto response = httpClient.getRequest("https://www.myinvalidurl.com").get(); + auto response = httpRequest.send().get(); // Instead of throwing an exception, the succeed field of the response object is set to false std::cout << "Succeed: " << response.succeed << std::endl; @@ -189,8 +200,7 @@ int main() { In the examples so far, we have used the **"textData"** property of the returning response object. However, we need binary data for requests made to binary files such as images. In such cases, we can ensure that the returned data is returned in **"binaryData"** of type -***"std::vector<unsigned char>"*** instead of **"textData"** by passing the value **"true"** -to the **"returnAsBinary"** parameter. +***"std::vector<unsigned char>"*** instead of **"textData"** by calling **"returnAsBinary()"** method before send as follow. ```cpp #include @@ -200,10 +210,13 @@ using namespace lklibs; int main() { - HttpClient httpClient; + HttpRequest httpRequest("https://api.myproject.com/image/7"); - // If you need to retrieve binary data such as an image, just pass the "returnAsBinary" parameter as true - auto response = httpClient.getRequest("https://api.myproject.com/image/7", true).get(); + // If you need to retrieve binary data such as an image, just call the "returnAsBinary" method before send + auto response = httpRequest + .returnAsBinary() + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -218,8 +231,7 @@ int main() { ## Sending custom HTTP headers -If you need to send custom HTTP HEADERs during the request, you can send them in -std::map format. +If you need to send custom HTTP HEADERs during the request, you can add them to the request as key-value pairs with **"addHeader()"** method. ```cpp #include @@ -229,16 +241,15 @@ using namespace lklibs; int main() { - HttpClient httpClient; - - // You can send custom headers in a string/string map - auto headers = std::map(); - - headers["Custom-Header1"] = "value1"; - headers["Custom-Header2"] = "value2"; - - auto response = httpClient.getRequest("https://api.myproject.com?param1=7¶m2=test", headers).get(); - + HttpRequest httpRequest("https://api.myproject.com"); + + // You can send custom headers as key-value pairs + auto response = httpRequest + .addHeader("Custom-Header1", "value1") + .addHeader("Custom-Header2", "value2") + .send() + .get(); + std::cout << "Succeed: " << response.succeed << std::endl; return 0; @@ -248,8 +259,7 @@ int main() { ## POST request with form data -Next is submitting form data via HTTP POST. All you have to do is use **"postRequest"** instead -of **"getRequest"**. You can pass the form data to the **"payload"** variable as seen in the sample code below. +Next is submitting form data via HTTP POST. All you have to do is use **"setMethod"** to change HTTP method type. You can pass the form data with **"setPaylod"** method as seen in the sample code below. ```cpp #include @@ -259,12 +269,14 @@ using namespace lklibs; int main() { - HttpClient httpClient; + HttpRequest httpRequest("https://api.myproject.com"); // You can send a POST request with form data in the payload - std::string payload = "param1=7¶m2=test"; - - auto response = httpClient.postRequest("https://api.myproject.com", payload).get(); + auto response = httpRequest + .setMethod(HttpMethod::POST) + .setPayload("param1=7¶m2=test") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -288,16 +300,15 @@ using namespace lklibs; int main() { - HttpClient httpClient; - - std::string payload = R"({"param1": 7, "param2": "test"})"; - + HttpRequest httpRequest("https://api.myproject.com"); + // You need to send the "Content-Type" as "application/json" in the HTTP Header, if you need to send json data in the payload - auto headers = std::map(); - - headers["Content-Type"] = "application/json"; - - auto response = httpClient.postRequest("https://api.myproject.com", payload, headers).get(); + auto response = httpRequest + .setMethod(HttpMethod::POST) + .setPayload(R"({"param1": 7, "param2": "test"})") + .addHeader("Content-Type", "application/json") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -320,13 +331,26 @@ using namespace lklibs; int main() { - HttpClient httpClient; + HttpRequest httpRequest1("https://api.myproject.com"); - std::string payload = "param1=7¶m2=test"; + auto future1 = httpRequest + .setMethod(HttpMethod::PUT) + .setPayload("param1=7¶m2=test") + .send(); + + HttpRequest httpRequest2("https://api.myproject.com"); - auto future1 = httpClient.putRequest("https://api.myproject.com", payload); - auto future2 = httpClient.deleteRequest("https://api.myproject.com", payload); - auto future3 = httpClient.patchRequest("https://api.myproject.com?param1=7¶m2=test"); + auto future2 = httpRequest + .setMethod(HttpMethod::DELETE_) + .setPayload("param1=7¶m2=test") + .send(); + + HttpRequest httpRequest3("https://api.myproject.com"); + + auto future3 = httpRequest + .setMethod(HttpMethod::PATCH) + .setQueryString("param1=7¶m2=test") + .send(); auto response1 = future1.get(); auto response2 = future2.get(); @@ -339,9 +363,8 @@ int main() { ## How to ignore SSL certificate errors? -If you need to ignore SSL certificate errors for any valid reason, you can continue -working by passing **"true"** value to the **"ignoreSslErrors"** variable of the -HttpClient class. +If you need to ignore SSL certificate errors for any valid reason, you can call "ignoreSslErrors" +method before sending the request. ```cpp #include @@ -351,12 +374,13 @@ using namespace lklibs; int main() { - HttpClient httpClient; - - // If you need to ignore SSL errors, you can set the "ignoreSslErrors" field to true - httpClient.ignoreSslErrors = true; + HttpRequest httpRequest("https://api.myinvalidssl.com"); - auto response = httpClient.getRequest("https://api.myinvalidssl.com").get(); + // If you need to ignore SSL errors, you can call "ignoreSslErrors" method before sending the request + auto response = httpRequest + .ignoreSslErrors() + .send() + .get(); return 0; } @@ -381,58 +405,21 @@ section to the documentation. ## Full function list -You can find the complete list of functions in the library below. In fact, they are just -overloaded versions of 5 functions in total. +You can find the complete list of functions in the library below. Since all methods except +send return the class itself, so they can be added one after the other like a chain. > [!TIP] > All methods and parameters descriptions are also available within the code as comment for IDEs. ```cpp -- getRequest - - std::future getRequest(const std::string &url) - - std::future getRequest(const std::string &url, bool returnAsBinary) - - std::future getRequest(const std::string &url, const std::map &headers) - - std::future getRequest(const std::string &url, bool returnAsBinary, const std::map &headers) - - -- postRequest - - std::future postRequest(const std::string &url) - - std::future postRequest(const std::string &url, const std::string &payload) - - std::future postRequest(const std::string &url, bool returnAsBinary) - - std::future postRequest(const std::string &url, const std::map &headers) - - std::future postRequest(const std::string &url, const std::string &payload, bool returnAsBinary) - - std::future postRequest(const std::string &url, const std::string &payload, const std::map &headers) - - std::future postRequest(const std::string &url, bool returnAsBinary, const std::map &headers) - - std::future postRequest(const std::string &url, const std::string &payload, bool returnAsBinary, const std::map &headers) - - -- putRequest - - std::future putRequest(const std::string &url) - - std::future putRequest(const std::string &url, const std::string &payload) - - std::future putRequest(const std::string &url, bool returnAsBinary) - - std::future putRequest(const std::string &url, const std::map &headers) - - std::future putRequest(const std::string &url, const std::string &payload, bool returnAsBinary) - - std::future putRequest(const std::string &url, const std::string &payload, const std::map &headers) - - std::future putRequest(const std::string &url, bool returnAsBinary, const std::map &headers) - - std::future putRequest(const std::string &url, const std::string &payload, bool returnAsBinary, const std::map &headers) - - -- deleteRequest - - std::future deleteRequest(const std::string &url) - - std::future deleteRequest(const std::string &url, const std::string &payload) - - std::future deleteRequest(const std::string &url, bool returnAsBinary) - - std::future deleteRequest(const std::string &url, const std::map &headers) - - std::future deleteRequest(const std::string &url, const std::string &payload, bool returnAsBinary) - - std::future deleteRequest(const std::string &url, const std::string &payload, const std::map &headers) - - std::future deleteRequest(const std::string &url, bool returnAsBinary, const std::map &headers) - - std::future deleteRequest(const std::string &url, const std::string &payload, bool returnAsBinary, const std::map &headers) - - -- patchRequest - - std::future patchRequest(const std::string &url) - - std::future patchRequest(const std::string &url, bool returnAsBinary) - - std::future patchRequest(const std::string &url, const std::map &headers) - - std::future patchRequest(const std::string &url, bool returnAsBinary, const std::map &headers) +- HttpRequest &setMethod(const HttpMethod &method) noexcept +- HttpRequest &setQueryString(const std::string &queryString) noexcept +- HttpRequest &setPayload(const std::string &payload) noexcept +- HttpRequest &returnAsBinary() noexcept +- HttpRequest &ignoreSslErrors() noexcept +- HttpRequest &addHeader(const std::string &key, const std::string &value) noexcept +- std::future send() noexcept + ``` diff --git a/examples/main.cpp b/examples/main.cpp index b00272c..9792bf1 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -5,10 +5,13 @@ using namespace lklibs; void simpleGet() { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/get"); // The simplest but slowest method if multiple calls will be made - auto response = httpClient.getRequest("https://httpbun.com/get?param1=7¶m2=test").get(); + auto response = httpRequest + .setQueryString("param1=7¶m2=test") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -17,12 +20,14 @@ void simpleGet() { void nonBlockingGet() { - HttpClient httpClient; + HttpRequest httpRequest1("https://httpbun.com/get"); + HttpRequest httpRequest2("https://httpbun.com/get"); + HttpRequest httpRequest3("https://httpbun.com/get"); // All requests are made one after the other without waiting for a response - auto future1 = httpClient.getRequest("https://httpbun.com/get?param1=1¶m2=test1"); - auto future2 = httpClient.getRequest("https://httpbun.com/get?param1=2¶m2=test2"); - auto future3 = httpClient.getRequest("https://httpbun.com/get?param1=3¶m2=test3"); + auto future1 = httpRequest1.setQueryString("param1=1¶m2=test1").send(); + auto future2 = httpRequest2.setQueryString("param1=2¶m2=test2").send(); + auto future3 = httpRequest3.setQueryString("param1=3¶m2=test3").send(); // Then all the answers are received. Thus, 3 requests are sent in parallel auto response1 = future1.get(); @@ -44,10 +49,13 @@ void nonBlockingGet() { void receiveBinaryData() { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bytes/100"); - // If you need to retrieve binary data such as an image, just pass the "returnAsBinary" parameter as true - auto response = httpClient.getRequest("https://httpbun.com/bytes/100", true).get(); + // If you need to retrieve binary data such as an image, just call the "returnAsBinary" method before send + auto response = httpRequest + .returnAsBinary() + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -58,10 +66,10 @@ void receiveBinaryData() { void receiveError() { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/not_found"); // This is an exception free library. If an error occurs, no exception is thrown - auto response = httpClient.getRequest("https://httpbun.com/not_found").get(); + auto response = httpRequest.send().get(); // Instead, the succeed field of the response object is set to false std::cout << "Succeed: " << response.succeed << std::endl; @@ -75,27 +83,28 @@ void receiveError() { void sendingHttpHeaders() { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/get?param1=7¶m2=test"); - // You can send custom headers in a string/string map - auto headers = std::map(); - - headers["Custom-Header1"] = "value1"; - headers["Custom-Header2"] = "value2"; - - auto response = httpClient.getRequest("https://httpbun.com/get?param1=7¶m2=test", headers).get(); + // You can send custom headers as key-value pairs + auto response = httpRequest + .addHeader("Custom-Header1", "value1") + .addHeader("Custom-Header2", "value2") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; } void simplePostWithFormData() { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/post"); // You can send a POST request with form data in the payload - std::string payload = "param1=7¶m2=test"; - - auto response = httpClient.postRequest("https://httpbun.com/post", payload).get(); + auto response = httpRequest + .setMethod(HttpMethod::POST) + .setPayload("param1=7¶m2=test") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -104,16 +113,15 @@ void simplePostWithFormData() { void simplePostWithJSONData() { - HttpClient httpClient; - - std::string payload = R"({"param1": 7, "param2": "test"})"; + HttpRequest httpRequest("https://httpbun.com/post"); // You need to send the "Content-Type" as "application/json" in the HTTP Header, if you need to send json data in the payload - auto headers = std::map(); - - headers["Content-Type"] = "application/json"; - - auto response = httpClient.postRequest("https://httpbun.com/post", payload, headers).get(); + auto response = httpRequest + .setMethod(HttpMethod::POST) + .setPayload(R"({"param1": 7, "param2": "test"})") + .addHeader("Content-Type", "application/json") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -122,12 +130,14 @@ void simplePostWithJSONData() { void simplePutWithFormData() { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/put"); // You can send a PUT request with form data in the payload just like POST - std::string payload = "param1=7¶m2=test"; - - auto response = httpClient.putRequest("https://httpbun.com/put", payload).get(); + auto response = httpRequest + .setMethod(HttpMethod::PUT) + .setPayload("param1=7¶m2=test") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -136,12 +146,14 @@ void simplePutWithFormData() { void simpleDeleteWithFormData() { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/delete"); // You can send a DELETE request with form data in the payload just like POST - std::string payload = "param1=7¶m2=test"; - - auto response = httpClient.deleteRequest("https://httpbun.com/delete", payload).get(); + auto response = httpRequest + .setMethod(HttpMethod::DELETE_) + .setPayload("param1=7¶m2=test") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -150,10 +162,14 @@ void simpleDeleteWithFormData() { void simplePatch() { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/patch"); // You can send a PATCH request with QueryString just like GET - auto response = httpClient.patchRequest("https://httpbun.com/patch?param1=7¶m2=test").get(); + auto response = httpRequest + .setMethod(HttpMethod::PATCH) + .setQueryString("param1=7¶m2=test") + .send() + .get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; @@ -162,12 +178,10 @@ void simplePatch() { void ignoreSslErrors() { - HttpClient httpClient; - - // If you need to ignore SSL errors, you can set the "ignoreSslErrors" field to true - httpClient.ignoreSslErrors = true; + HttpRequest httpRequest("https://self-signed-cert.httpbun.com"); - auto response = httpClient.getRequest("https://self-signed-cert.httpbun.com").get(); + // If you need to ignore SSL errors, you can call "ignoreSslErrors" method before sending the request + auto response = httpRequest.ignoreSslErrors().send().get(); std::cout << "Succeed: " << response.succeed << std::endl; std::cout << "Http Status Code: " << response.statusCode << std::endl; diff --git a/src/libcpp-http-client.hpp b/src/libcpp-http-client.hpp index f2799df..e79241c 100644 --- a/src/libcpp-http-client.hpp +++ b/src/libcpp-http-client.hpp @@ -1,7 +1,7 @@ /* Modern non-blocking HTTP Client library for C++ (17+) -version 1.1.0 +version 1.2.0 https://github.com/lk-libs/libcpp-http-client If you encounter any issues, please submit a ticket at https://github.com/lk-libs/libcpp-http-client/issues @@ -78,425 +78,150 @@ namespace lklibs { }; /** - * @brief HTTP client class that makes asynchronous HTTP requests + * @brief HTTP Method options for the request */ - class HttpClient { - public: - - HttpClient() { - curl_global_init(CURL_GLOBAL_DEFAULT); - } - - ~HttpClient() { - curl_global_cleanup(); - } - - /** - * @brief Ignore SSL errors when making HTTP requests - */ - bool ignoreSslErrors = false; - - /** - * @brief Makes an HTTP GET request for the given URL and returns the result - * - * @param url: Request URL - * @return Result of the request (see HttpResult object for details) - */ - std::future getRequest(const std::string &url) noexcept { - - return request(url, "GET", "", false, {}); - } - - /** - * @brief Makes an HTTP GET request for the given URL and returns the result - * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @return Result of the request (see HttpResult object for details) - */ - std::future getRequest(const std::string &url, bool returnAsBinary) noexcept { - - return request(url, "GET", "", returnAsBinary, {}); - } - - /** - * @brief Makes an HTTP GET request for the given URL and returns the result - * - * @param url: Request URL - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future getRequest(const std::string &url, const std::map &headers) noexcept { - - return request(url, "GET", "", false, headers); - } - - /** - * @brief Makes an HTTP GET request for the given URL and returns the result - * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future getRequest(const std::string &url, bool returnAsBinary, const std::map &headers) noexcept { - - return request(url, "GET", "", returnAsBinary, headers); - } - - - /** - * @brief Makes an HTTP POST request for the given URL and returns the result - * - * @param url: Request URL - * @return Result of the request (see HttpResult object for details) - */ - std::future postRequest(const std::string &url) noexcept { - - return request(url, "POST", "", false, {}); - } - - /** - * @brief Makes an HTTP POST request for the given URL and returns the result - * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @return Result of the request (see HttpResult object for details) - */ - std::future postRequest(const std::string &url, const std::string &payload) noexcept { - - return request(url, "POST", payload, false, {}); - } - - /** - * @brief Makes an HTTP POST request for the given URL and returns the result - * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @return Result of the request (see HttpResult object for details) - */ - std::future postRequest(const std::string &url, bool returnAsBinary) noexcept { - - return request(url, "POST", "", returnAsBinary, {}); - } - - /** - * @brief Makes an HTTP POST request for the given URL and returns the result - * - * @param url: Request URL - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future postRequest(const std::string &url, const std::map &headers) noexcept { - - return request(url, "POST", "", false, headers); - } - - /** - * @brief Makes an HTTP POST request for the given URL and returns the result - * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @param returnAsBinary: Return result as binary instead of string - * @return Result of the request (see HttpResult object for details) - */ - std::future postRequest(const std::string &url, const std::string &payload, bool returnAsBinary) noexcept { - - return request(url, "POST", payload, returnAsBinary, {}); - } - - /** - * @brief Makes an HTTP POST request for the given URL and returns the result - * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future postRequest(const std::string &url, const std::string &payload, const std::map &headers) noexcept { - - return request(url, "POST", payload, false, headers); - } - - /** - * @brief Makes an HTTP POST request for the given URL and returns the result - * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future postRequest(const std::string &url, bool returnAsBinary, const std::map &headers) noexcept { - - return request(url, "POST", "", returnAsBinary, headers); - } - - /** - * @brief Makes an HTTP POST request for the given URL and returns the result - * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @param returnAsBinary: Return result as binary instead of string - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future postRequest(const std::string &url, const std::string &payload, bool returnAsBinary, const std::map &headers) noexcept { - - return request(url, "POST", payload, returnAsBinary, headers); - } - - - /** - * @brief Makes an HTTP PUT request for the given URL and returns the result - * - * @param url: Request URL - * @return Result of the request (see HttpResult object for details) - */ - std::future putRequest(const std::string &url) noexcept { + enum class HttpMethod { + GET, + POST, + PUT, + DELETE_, + PATCH + }; - return request(url, "PUT", "", false, {}); - } + /** + * @brief HTTP request class that makes asynchronous HTTP calls + */ + class HttpRequest { + public: /** - * @brief Makes an HTTP PUT request for the given URL and returns the result + * @brief Constructor for the HttpRequest class * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @return Result of the request (see HttpResult object for details) + * @param url: URL for the request */ - std::future putRequest(const std::string &url, const std::string &payload) noexcept { - - return request(url, "PUT", payload, false, {}); - } + explicit HttpRequest(const std::string &url) { - /** - * @brief Makes an HTTP PUT request for the given URL and returns the result - * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @return Result of the request (see HttpResult object for details) - */ - std::future putRequest(const std::string &url, bool returnAsBinary) noexcept { + this->url = url; - return request(url, "PUT", "", returnAsBinary, {}); + curl_global_init(CURL_GLOBAL_DEFAULT); } - /** - * @brief Makes an HTTP PUT request for the given URL and returns the result - * - * @param url: Request URL - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future putRequest(const std::string &url, const std::map &headers) noexcept { - - return request(url, "PUT", "", false, headers); + ~HttpRequest() { + curl_global_cleanup(); } /** - * @brief Makes an HTTP PUT request for the given URL and returns the result + * @brief Set the HTTP method for the request * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @param returnAsBinary: Return result as binary instead of string - * @return Result of the request (see HttpResult object for details) + * @param method: HTTP method to be used for the request */ - std::future putRequest(const std::string &url, const std::string &payload, bool returnAsBinary) noexcept { + HttpRequest &setMethod(const HttpMethod &method) noexcept { - return request(url, "PUT", payload, returnAsBinary, {}); - } + this->method = HttpMethodStrings[static_cast(method)]; - /** - * @brief Makes an HTTP PUT request for the given URL and returns the result - * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future putRequest(const std::string &url, const std::string &payload, const std::map &headers) noexcept { - - return request(url, "PUT", payload, false, headers); + return *this; } /** - * @brief Makes an HTTP PUT request for the given URL and returns the result + * @brief Set the query string for the request * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) + * @param queryString: Query string to be sent with the request */ - std::future putRequest(const std::string &url, bool returnAsBinary, const std::map &headers) noexcept { - - return request(url, "PUT", "", returnAsBinary, headers); - } + HttpRequest &setQueryString(const std::string &queryString) noexcept { - /** - * @brief Makes an HTTP PUT request for the given URL and returns the result - * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @param returnAsBinary: Return result as binary instead of string - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future putRequest(const std::string &url, const std::string &payload, bool returnAsBinary, const std::map &headers) noexcept { + if (this->url.find('?') != std::string::npos) { - return request(url, "PUT", payload, returnAsBinary, headers); - } + this->url += "&" + queryString; + } else { - /** - * @brief Makes an HTTP DELETE request for the given URL and returns the result - * - * @param url: Request URL - * @return Result of the request (see HttpResult object for details) - */ - std::future deleteRequest(const std::string &url) noexcept { + this->url += "?" + queryString; + } - return request(url, "DELETE", "", false, {}); + return *this; } /** - * @brief Makes an HTTP DELETE request for the given URL and returns the result + * @brief Set the payload for the request + * You can send form data like param1=7¶m2=test or JSON data like {"param1": 7, "param2": "test"} + * You need to send the "Content-Type" as "application/json" in the HTTP Header, if you need to send json data in the payload * - * @param url: Request URL * @param payload: Payload to be sent with the request - * @return Result of the request (see HttpResult object for details) */ - std::future deleteRequest(const std::string &url, const std::string &payload) noexcept { + HttpRequest &setPayload(const std::string &payload) noexcept { - return request(url, "DELETE", payload, false, {}); - } + this->payload = payload; - /** - * @brief Makes an HTTP DELETE request for the given URL and returns the result - * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @return Result of the request (see HttpResult object for details) - */ - std::future deleteRequest(const std::string &url, bool returnAsBinary) noexcept { - - return request(url, "DELETE", "", returnAsBinary, {}); + return *this; } /** - * @brief Makes an HTTP DELETE request for the given URL and returns the result - * - * @param url: Request URL - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) + * @brief Set the return format for the request as binary */ - std::future deleteRequest(const std::string &url, const std::map &headers) noexcept { + HttpRequest &returnAsBinary() noexcept { - return request(url, "DELETE", "", false, headers); - } + this->returnFormat = ReturnFormat::BINARY; - /** - * @brief Makes an HTTP DELETE request for the given URL and returns the result - * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @param returnAsBinary: Return result as binary instead of string - * @return Result of the request (see HttpResult object for details) - */ - std::future deleteRequest(const std::string &url, const std::string &payload, bool returnAsBinary) noexcept { - - return request(url, "DELETE", payload, returnAsBinary, {}); + return *this; } /** - * @brief Makes an HTTP DELETE request for the given URL and returns the result - * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) + * @brief Ignore SSL errors when making HTTP requests */ - std::future deleteRequest(const std::string &url, const std::string &payload, const std::map &headers) noexcept { - - return request(url, "DELETE", payload, false, headers); - } + HttpRequest &ignoreSslErrors() noexcept { - /** - * @brief Makes an HTTP DELETE request for the given URL and returns the result - * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future deleteRequest(const std::string &url, bool returnAsBinary, const std::map &headers) noexcept { + this->sslErrorsWillBeIgnored = true; - return request(url, "DELETE", "", returnAsBinary, headers); + return *this; } /** - * @brief Makes an HTTP DELETE request for the given URL and returns the result + * @brief Add a HTTP header to the request * - * @param url: Request URL - * @param payload: Payload to be sent with the request - * @param returnAsBinary: Return result as binary instead of string - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) + * @param key: Header key + * @param value: Header value */ - std::future deleteRequest(const std::string &url, const std::string &payload, bool returnAsBinary, const std::map &headers) noexcept { - - return request(url, "DELETE", payload, returnAsBinary, headers); - } + HttpRequest &addHeader(const std::string &key, const std::string &value) noexcept { + this->headers[key] = value; - /** - * @brief Makes an HTTP PATCH request for the given URL and returns the result - * - * @param url: Request URL - * @return Result of the request (see HttpResult object for details) - */ - std::future patchRequest(const std::string &url) noexcept { - - return request(url, "PATCH", "", false, {}); + return *this; } /** - * @brief Makes an HTTP PATCH request for the given URL and returns the result + * @brief Send the HTTP request and return the result as a future + * The result can be obtained by calling the get() method of the future + * get() method will block until the result is available so it is recommended to use it + * when you need the result and no more other http requests will be made as parallel * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @return Result of the request (see HttpResult object for details) + * @return Result of the request as a future (see HttpResult object for details) */ - std::future patchRequest(const std::string &url, bool returnAsBinary) noexcept { + std::future send() noexcept { - return request(url, "PATCH", "", returnAsBinary, {}); + return this->sendRequest(); } - /** - * @brief Makes an HTTP PATCH request for the given URL and returns the result - * - * @param url: Request URL - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future patchRequest(const std::string &url, const std::map &headers) noexcept { - - return request(url, "PATCH", "", false, headers); - } + private: - /** - * @brief Makes an HTTP PATCH request for the given URL and returns the result - * - * @param url: Request URL - * @param returnAsBinary: Return result as binary instead of string - * @param headers: HTTP Header information to be sent when making the request - * @return Result of the request (see HttpResult object for details) - */ - std::future patchRequest(const std::string &url, bool returnAsBinary, const std::map &headers) noexcept { + enum class ReturnFormat { + TEXT, + BINARY + }; - return request(url, "PATCH", "", returnAsBinary, headers); - } + const char* HttpMethodStrings[5] = { + "GET", + "POST", + "PUT", + "DELETE", + "PATCH" + }; - private: + std::string url; + std::string method = "GET"; + std::string payload; + bool sslErrorsWillBeIgnored = false; + ReturnFormat returnFormat = ReturnFormat::TEXT; + std::map headers; struct CurlDeleter { @@ -520,9 +245,9 @@ namespace lklibs { } }; - std::future request(const std::string &url, const std::string &method, const std::string &payload, bool returnAsBinary, const std::map &headers) { + std::future sendRequest() noexcept { - return std::async(std::launch::async, [this, url, method, payload, returnAsBinary, headers]() -> HttpResult { + return std::async(std::launch::async, [this]() -> HttpResult { std::unique_ptr curl(curl_easy_init()); @@ -533,7 +258,7 @@ namespace lklibs { std::unique_ptr headerList(nullptr); - for (const auto &header: headers) { + for (const auto &header: this->headers) { std::string headerStr = header.first + ": " + header.second; @@ -545,17 +270,17 @@ namespace lklibs { int statusCode = 0; curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headerList.get()); - curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, method.c_str()); - curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, this->ignoreSslErrors ? 0L : 1L); - curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, this->ignoreSslErrors ? 0L : 1L); + curl_easy_setopt(curl.get(), CURLOPT_URL, this->url.c_str()); + curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, this->method.c_str()); + curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, this->sslErrorsWillBeIgnored ? 0L : 1L); + curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, this->sslErrorsWillBeIgnored ? 0L : 1L); - if (!payload.empty()) { + if (!this->payload.empty()) { - curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, payload.c_str()); + curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, this->payload.c_str()); } - if (returnAsBinary) { + if (this->returnFormat == ReturnFormat::BINARY) { curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, binaryWriteCallback); curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &binaryBuffer); diff --git a/test/test.cpp b/test/test.cpp index bbc9823..950ccfa 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -7,9 +7,12 @@ using json = nlohmann::json; TEST(HttpGetTest, HttpGetRequestMustBeCompletedSuccessfullyInItsSimplestForm) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/get"); - auto response = httpClient.getRequest("https://httpbun.com/get?param1=7¶m2=test").get(); + auto response = httpRequest + .setQueryString("param1=7¶m2=test") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -26,11 +29,13 @@ TEST(HttpGetTest, HttpGetRequestMustBeCompletedSuccessfullyInItsSimplestForm) { TEST(HttpGetTest, MultipleHttpGetRequestMustBeCompletedSuccessfullyInNonBlockingForm) { - HttpClient httpClient; + HttpRequest httpRequest1("https://httpbun.com/get"); + HttpRequest httpRequest2("https://httpbun.com/get"); + HttpRequest httpRequest3("https://httpbun.com/get"); - auto future1 = httpClient.getRequest("https://httpbun.com/get?param1=1¶m2=test1"); - auto future2 = httpClient.getRequest("https://httpbun.com/get?param1=2¶m2=test2"); - auto future3 = httpClient.getRequest("https://httpbun.com/get?param1=3¶m2=test3"); + auto future1 = httpRequest1.setQueryString("param1=1¶m2=test1").send(); + auto future2 = httpRequest2.setQueryString("param1=2¶m2=test2").send(); + auto future3 = httpRequest3.setQueryString("param1=3¶m2=test3").send(); auto response1 = future1.get(); auto response2 = future2.get(); @@ -73,9 +78,12 @@ TEST(HttpGetTest, MultipleHttpGetRequestMustBeCompletedSuccessfullyInNonBlocking TEST(HttpGetTest, ResponseOfAnHttpGetRequestCanBeReceivedInBinaryFormat) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bytes/100"); - auto response = httpClient.getRequest("https://httpbun.com/bytes/100", true).get(); + auto response = httpRequest + .returnAsBinary() + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -86,9 +94,9 @@ TEST(HttpGetTest, ResponseOfAnHttpGetRequestCanBeReceivedInBinaryFormat) { TEST(HttpGetTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpGetRequestMadeToAnInvalidAddress) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/not_found"); - auto response = httpClient.getRequest("https://httpbun.com/not_found").get(); + auto response = httpRequest.send().get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 404) << "HTTP Status Code is not 404"; @@ -97,9 +105,9 @@ TEST(HttpGetTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpGetRequestMade TEST(HttpGetTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpGetRequestForAnotherError) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bearer"); - auto response = httpClient.getRequest("https://httpbun.com/bearer").get(); + auto response = httpRequest.send().get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 401) << "HTTP Status Code is not 401"; @@ -108,14 +116,14 @@ TEST(HttpGetTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpGetRequestForA TEST(HttpGetTest, HttpHeadersCanBeSentWithTheHttpGetRequest) { - auto headers = std::map(); + HttpRequest httpRequest("https://httpbun.com/get"); - headers["Custom-Header1"] = "value1"; - headers["Custom-Header2"] = "value2"; - - HttpClient httpClient; - - auto response = httpClient.getRequest("https://httpbun.com/get?param1=7¶m2=test", headers).get(); + auto response = httpRequest + .setQueryString("param1=7¶m2=test") + .addHeader("Custom-Header1", "value1") + .addHeader("Custom-Header2", "value2") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -135,11 +143,13 @@ TEST(HttpGetTest, HttpHeadersCanBeSentWithTheHttpGetRequest) { TEST(HttpPostTest, HttpPostRequestMustBeCompletedSuccessfullyInItsSimplestForm) { - std::string payload = "param1=7¶m2=test"; - - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/post"); - auto response = httpClient.postRequest("https://httpbun.com/post", payload).get(); + auto response = httpRequest + .setMethod(HttpMethod::POST) + .setPayload("param1=7¶m2=test") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -156,15 +166,13 @@ TEST(HttpPostTest, HttpPostRequestMustBeCompletedSuccessfullyInItsSimplestForm) TEST(HttpPostTest, MultipleHttpPostRequestMustBeCompletedSuccessfullyInNonBlockingForm) { - std::string payload1 = "param1=1¶m2=test1"; - std::string payload2 = "param1=2¶m2=test2"; - std::string payload3 = "param1=3¶m2=test3"; + HttpRequest httpRequest1("https://httpbun.com/post"); + HttpRequest httpRequest2("https://httpbun.com/post"); + HttpRequest httpRequest3("https://httpbun.com/post"); - HttpClient httpClient; - - auto future1 = httpClient.postRequest("https://httpbun.com/post", payload1); - auto future2 = httpClient.postRequest("https://httpbun.com/post", payload2); - auto future3 = httpClient.postRequest("https://httpbun.com/post", payload3); + auto future1 = httpRequest1.setMethod(HttpMethod::POST).setPayload("param1=1¶m2=test1").send(); + auto future2 = httpRequest2.setMethod(HttpMethod::POST).setPayload("param1=2¶m2=test2").send(); + auto future3 = httpRequest3.setMethod(HttpMethod::POST).setPayload("param1=3¶m2=test3").send(); auto response1 = future1.get(); auto response2 = future2.get(); @@ -207,9 +215,13 @@ TEST(HttpPostTest, MultipleHttpPostRequestMustBeCompletedSuccessfullyInNonBlocki TEST(HttpPostTest, ResponseOfAnHttpPostRequestCanBeReceivedInBinaryFormat) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bytes/100"); - auto response = httpClient.postRequest("https://httpbun.com/bytes/100", true).get(); + auto response = httpRequest + .setMethod(HttpMethod::POST) + .returnAsBinary() + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -220,9 +232,12 @@ TEST(HttpPostTest, ResponseOfAnHttpPostRequestCanBeReceivedInBinaryFormat) { TEST(HttpPostTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPostRequestMadeToAnInvalidAddress) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/not_found"); - auto response = httpClient.postRequest("https://httpbun.com/not_found").get(); + auto response = httpRequest + .setMethod(HttpMethod::POST) + .send() + .get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 404) << "HTTP Status Code is not 404"; @@ -231,9 +246,12 @@ TEST(HttpPostTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPostRequestMa TEST(HttpPostTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPostRequestForAnotherError) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bearer"); - auto response = httpClient.postRequest("https://httpbun.com/bearer").get(); + auto response = httpRequest + .setMethod(HttpMethod::POST) + .send() + .get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 401) << "HTTP Status Code is not 401"; @@ -242,17 +260,16 @@ TEST(HttpPostTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPostRequestFo TEST(HttpPostTest, HttpHeadersCanBeSentWithTheHttpPostRequest) { - std::string payload = R"({"param1": 7, "param2": "test"})"; - - auto headers = std::map(); - - headers["Content-Type"] = "application/json"; - headers["Custom-Header1"] = "value1"; - headers["Custom-Header2"] = "value2"; + HttpRequest httpRequest("https://httpbun.com/post"); - HttpClient httpClient; - - auto response = httpClient.postRequest("https://httpbun.com/post", payload, headers).get(); + auto response = httpRequest + .setMethod(HttpMethod::POST) + .setPayload(R"({"param1": 7, "param2": "test"})") + .addHeader("Content-Type", "application/json") + .addHeader("Custom-Header1", "value1") + .addHeader("Custom-Header2", "value2") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -273,11 +290,13 @@ TEST(HttpPostTest, HttpHeadersCanBeSentWithTheHttpPostRequest) { TEST(HttpPutTest, HttpPutRequestMustBeCompletedSuccessfullyInItsSimplestForm) { - std::string payload = "param1=7¶m2=test"; - - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/put"); - auto response = httpClient.putRequest("https://httpbun.com/put", payload).get(); + auto response = httpRequest + .setMethod(HttpMethod::PUT) + .setPayload("param1=7¶m2=test") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -294,15 +313,13 @@ TEST(HttpPutTest, HttpPutRequestMustBeCompletedSuccessfullyInItsSimplestForm) { TEST(HttpPutTest, MultipleHttpPutRequestMustBeCompletedSuccessfullyInNonBlockingForm) { - std::string payload1 = "param1=1¶m2=test1"; - std::string payload2 = "param1=2¶m2=test2"; - std::string payload3 = "param1=3¶m2=test3"; - - HttpClient httpClient; + HttpRequest httpRequest1("https://httpbun.com/put"); + HttpRequest httpRequest2("https://httpbun.com/put"); + HttpRequest httpRequest3("https://httpbun.com/put"); - auto future1 = httpClient.putRequest("https://httpbun.com/put", payload1); - auto future2 = httpClient.putRequest("https://httpbun.com/put", payload2); - auto future3 = httpClient.putRequest("https://httpbun.com/put", payload3); + auto future1 = httpRequest1.setMethod(HttpMethod::PUT).setPayload("param1=1¶m2=test1").send(); + auto future2 = httpRequest2.setMethod(HttpMethod::PUT).setPayload("param1=2¶m2=test2").send(); + auto future3 = httpRequest3.setMethod(HttpMethod::PUT).setPayload("param1=3¶m2=test3").send(); auto response1 = future1.get(); auto response2 = future2.get(); @@ -345,9 +362,13 @@ TEST(HttpPutTest, MultipleHttpPutRequestMustBeCompletedSuccessfullyInNonBlocking TEST(HttpPutTest, ResponseOfAnHttpPutRequestCanBeReceivedInBinaryFormat) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bytes/100"); - auto response = httpClient.putRequest("https://httpbun.com/bytes/100", true).get(); + auto response = httpRequest + .setMethod(HttpMethod::PUT) + .returnAsBinary() + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -358,9 +379,12 @@ TEST(HttpPutTest, ResponseOfAnHttpPutRequestCanBeReceivedInBinaryFormat) { TEST(HttpPutTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPutRequestMadeToAnInvalidAddress) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/not_found"); - auto response = httpClient.putRequest("https://httpbun.com/not_found").get(); + auto response = httpRequest + .setMethod(HttpMethod::PUT) + .send() + .get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 404) << "HTTP Status Code is not 404"; @@ -369,9 +393,12 @@ TEST(HttpPutTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPutRequestMade TEST(HttpPutTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPutRequestForAnotherError) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bearer"); - auto response = httpClient.putRequest("https://httpbun.com/bearer").get(); + auto response = httpRequest + .setMethod(HttpMethod::PUT) + .send() + .get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 401) << "HTTP Status Code is not 401"; @@ -380,17 +407,16 @@ TEST(HttpPutTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPutRequestForA TEST(HttpPutTest, HttpHeadersCanBeSentWithTheHttpPutRequest) { - std::string payload = R"({"param1": 7, "param2": "test"})"; - - auto headers = std::map(); - - headers["Content-Type"] = "application/json"; - headers["Custom-Header1"] = "value1"; - headers["Custom-Header2"] = "value2"; + HttpRequest httpRequest("https://httpbun.com/put"); - HttpClient httpClient; - - auto response = httpClient.putRequest("https://httpbun.com/put", payload, headers).get(); + auto response = httpRequest + .setMethod(HttpMethod::PUT) + .setPayload(R"({"param1": 7, "param2": "test"})") + .addHeader("Content-Type", "application/json") + .addHeader("Custom-Header1", "value1") + .addHeader("Custom-Header2", "value2") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -411,11 +437,13 @@ TEST(HttpPutTest, HttpHeadersCanBeSentWithTheHttpPutRequest) { TEST(HttpDeleteTest, HttpDeleteRequestMustBeCompletedSuccessfullyInItsSimplestForm) { - std::string payload = "param1=7¶m2=test"; - - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/delete"); - auto response = httpClient.deleteRequest("https://httpbun.com/delete", payload).get(); + auto response = httpRequest + .setMethod(HttpMethod::DELETE_) + .setPayload("param1=7¶m2=test") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -432,15 +460,13 @@ TEST(HttpDeleteTest, HttpDeleteRequestMustBeCompletedSuccessfullyInItsSimplestFo TEST(HttpDeleteTest, MultipleHttpDeleteRequestMustBeCompletedSuccessfullyInNonBlockingForm) { - std::string payload1 = "param1=1¶m2=test1"; - std::string payload2 = "param1=2¶m2=test2"; - std::string payload3 = "param1=3¶m2=test3"; - - HttpClient httpClient; + HttpRequest httpRequest1("https://httpbun.com/delete"); + HttpRequest httpRequest2("https://httpbun.com/delete"); + HttpRequest httpRequest3("https://httpbun.com/delete"); - auto future1 = httpClient.deleteRequest("https://httpbun.com/delete", payload1); - auto future2 = httpClient.deleteRequest("https://httpbun.com/delete", payload2); - auto future3 = httpClient.deleteRequest("https://httpbun.com/delete", payload3); + auto future1 = httpRequest1.setMethod(HttpMethod::DELETE_).setPayload("param1=1¶m2=test1").send(); + auto future2 = httpRequest2.setMethod(HttpMethod::DELETE_).setPayload("param1=2¶m2=test2").send(); + auto future3 = httpRequest3.setMethod(HttpMethod::DELETE_).setPayload("param1=3¶m2=test3").send(); auto response1 = future1.get(); auto response2 = future2.get(); @@ -483,9 +509,13 @@ TEST(HttpDeleteTest, MultipleHttpDeleteRequestMustBeCompletedSuccessfullyInNonBl TEST(HttpDeleteTest, ResponseOfAnHttpDeleteRequestCanBeReceivedInBinaryFormat) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bytes/100"); - auto response = httpClient.deleteRequest("https://httpbun.com/bytes/100", true).get(); + auto response = httpRequest + .setMethod(HttpMethod::DELETE_) + .returnAsBinary() + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -496,9 +526,12 @@ TEST(HttpDeleteTest, ResponseOfAnHttpDeleteRequestCanBeReceivedInBinaryFormat) { TEST(HttpDeleteTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpDeleteRequestMadeToAnInvalidAddress) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/not_found"); - auto response = httpClient.deleteRequest("https://httpbun.com/not_found").get(); + auto response = httpRequest + .setMethod(HttpMethod::DELETE_) + .send() + .get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 404) << "HTTP Status Code is not 404"; @@ -507,9 +540,12 @@ TEST(HttpDeleteTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpDeleteReque TEST(HttpDeleteTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpDeleteRequestForAnotherError) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bearer"); - auto response = httpClient.deleteRequest("https://httpbun.com/bearer").get(); + auto response = httpRequest + .setMethod(HttpMethod::DELETE_) + .send() + .get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 401) << "HTTP Status Code is not 401"; @@ -518,17 +554,16 @@ TEST(HttpDeleteTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpDeleteReque TEST(HttpDeleteTest, HttpHeadersCanBeSentWithTheHttpDeleteRequest) { - std::string payload = R"({"param1": 7, "param2": "test"})"; - - auto headers = std::map(); - - headers["Content-Type"] = "application/json"; - headers["Custom-Header1"] = "value1"; - headers["Custom-Header2"] = "value2"; - - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/delete"); - auto response = httpClient.deleteRequest("https://httpbun.com/delete", payload, headers).get(); + auto response = httpRequest + .setMethod(HttpMethod::DELETE_) + .setPayload(R"({"param1": 7, "param2": "test"})") + .addHeader("Content-Type", "application/json") + .addHeader("Custom-Header1", "value1") + .addHeader("Custom-Header2", "value2") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -547,11 +582,16 @@ TEST(HttpDeleteTest, HttpHeadersCanBeSentWithTheHttpDeleteRequest) { ASSERT_EQ(data["headers"]["Custom-Header2"], "value2") << "Custom-Header2 is invalid"; } + TEST(HttpPatchTest, HttpPatchRequestMustBeCompletedSuccessfullyInItsSimplestForm) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/patch"); - auto response = httpClient.patchRequest("https://httpbun.com/patch?param1=7¶m2=test").get(); + auto response = httpRequest + .setMethod(HttpMethod::PATCH) + .setQueryString("param1=7¶m2=test") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -568,11 +608,13 @@ TEST(HttpPatchTest, HttpPatchRequestMustBeCompletedSuccessfullyInItsSimplestForm TEST(HttpPatchTest, MultipleHttpPatchRequestMustBeCompletedSuccessfullyInNonBlockingForm) { - HttpClient httpClient; + HttpRequest httpRequest1("https://httpbun.com/patch"); + HttpRequest httpRequest2("https://httpbun.com/patch"); + HttpRequest httpRequest3("https://httpbun.com/patch"); - auto future1 = httpClient.patchRequest("https://httpbun.com/patch?param1=1¶m2=test1"); - auto future2 = httpClient.patchRequest("https://httpbun.com/patch?param1=2¶m2=test2"); - auto future3 = httpClient.patchRequest("https://httpbun.com/patch?param1=3¶m2=test3"); + auto future1 = httpRequest1.setMethod(HttpMethod::PATCH).setQueryString("param1=1¶m2=test1").send(); + auto future2 = httpRequest2.setMethod(HttpMethod::PATCH).setQueryString("param1=2¶m2=test2").send(); + auto future3 = httpRequest3.setMethod(HttpMethod::PATCH).setQueryString("param1=3¶m2=test3").send(); auto response1 = future1.get(); auto response2 = future2.get(); @@ -615,9 +657,13 @@ TEST(HttpPatchTest, MultipleHttpPatchRequestMustBeCompletedSuccessfullyInNonBloc TEST(HttpPatchTest, ResponseOfAnHttpPatchRequestCanBeReceivedInBinaryFormat) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bytes/100"); - auto response = httpClient.patchRequest("https://httpbun.com/bytes/100", true).get(); + auto response = httpRequest + .setMethod(HttpMethod::PATCH) + .returnAsBinary() + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -628,9 +674,9 @@ TEST(HttpPatchTest, ResponseOfAnHttpPatchRequestCanBeReceivedInBinaryFormat) { TEST(HttpPatchTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPatchRequestMadeToAnInvalidAddress) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/not_found"); - auto response = httpClient.patchRequest("https://httpbun.com/not_found").get(); + auto response = httpRequest.setMethod(HttpMethod::PATCH).send().get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 404) << "HTTP Status Code is not 404"; @@ -639,9 +685,9 @@ TEST(HttpPatchTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPatchRequest TEST(HttpPatchTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPatchRequestForAnotherError) { - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/bearer"); - auto response = httpClient.patchRequest("https://httpbun.com/bearer").get(); + auto response = httpRequest.setMethod(HttpMethod::PATCH).send().get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 401) << "HTTP Status Code is not 401"; @@ -650,15 +696,15 @@ TEST(HttpPatchTest, AnErrorMessageShouldBeReturnedInResponseToAnHttpPatchRequest TEST(HttpPatchTest, HttpHeadersCanBeSentWithTheHttpPatchRequest) { - auto headers = std::map(); - - headers["Content-Type"] = "application/json"; - headers["Custom-Header1"] = "value1"; - headers["Custom-Header2"] = "value2"; - - HttpClient httpClient; + HttpRequest httpRequest("https://httpbun.com/patch"); - auto response = httpClient.patchRequest("https://httpbun.com/patch?param1=7¶m2=test", headers).get(); + auto response = httpRequest + .setMethod(HttpMethod::PATCH) + .setQueryString("param1=7¶m2=test") + .addHeader("Custom-Header1", "value1") + .addHeader("Custom-Header2", "value2") + .send() + .get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200"; @@ -672,16 +718,15 @@ TEST(HttpPatchTest, HttpHeadersCanBeSentWithTheHttpPatchRequest) { ASSERT_EQ(data["args"]["param1"], "7") << "Querystring is invalid"; ASSERT_EQ(data["args"]["param2"], "test") << "Querystring is invalid"; - ASSERT_EQ(data["headers"]["Content-Type"], "application/json") << "Content-Type is invalid"; ASSERT_EQ(data["headers"]["Custom-Header1"], "value1") << "Custom-Header1 is invalid"; ASSERT_EQ(data["headers"]["Custom-Header2"], "value2") << "Custom-Header2 is invalid"; } TEST(InvalidSSLTest, HttpGetRequestMustReturnErrorForAnInvalidSsl) { - HttpClient httpClient; + HttpRequest httpRequest("https://self-signed-cert.httpbun.com"); - auto response = httpClient.getRequest("https://self-signed-cert.httpbun.com").get(); + auto response = httpRequest.send().get(); ASSERT_FALSE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 0) << "HTTP Status Code is not 0"; @@ -690,11 +735,9 @@ TEST(InvalidSSLTest, HttpGetRequestMustReturnErrorForAnInvalidSsl) { TEST(InvalidSSLTest, HttpGetRequestMustBeCompletedSuccessfullyForAnInvalidSslIfIgnoreSslErrorsFieldSetTrue) { - HttpClient httpClient; - - httpClient.ignoreSslErrors = true; + HttpRequest httpRequest("https://self-signed-cert.httpbun.com"); - auto response = httpClient.getRequest("https://self-signed-cert.httpbun.com").get(); + auto response = httpRequest.ignoreSslErrors().send().get(); ASSERT_TRUE(response.succeed) << "HTTP Request failed"; ASSERT_EQ(response.statusCode, 200) << "HTTP Status Code is not 200";