From 07e33bf8e6d01e24274dae3d7938b09177b00d09 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Fri, 16 Dec 2016 01:02:44 +0100 Subject: [PATCH 1/3] Added web proxy tunneling support. Added TestInvalidProxy test. Reduced a line length to comply with CI rule. Added a method to set SSL Key password and added some missing Doxygen headers. Updated README, updated test proxy address, added a test to increase coverage and fixed code style. Added a method to perform a post form upload. Fixed code indentation to comply with Cpplint. Added web proxy tunneling support. this adds support for using proxies corresponding to the CURLOPT_PROXY and CURLOPT_HTTPPROXYTUNNEL config options in libcurl. default to 10s timeout in unit tests Added web proxy tunneling support. Added TestInvalidProxy test. Reduced a line length to comply with CI rule. Updated README, updated test proxy address, added a test to increase coverage and fixed code style. Added a method to set SSL Key password and added some missing Doxygen headers. Updated an info in README.md --- README.md | 35 ++++++++++++ include/restclient-cpp/connection.h | 2 + include/restclient-cpp/helpers.h | 20 +++++++ include/restclient-cpp/restclient.h | 3 + source/connection.cc | 89 ++++++++++++++++++++++++++++- source/helpers.cc | 49 ++++++++++++++++ source/restclient.cc | 17 ++++++ test/test_connection.cc | 28 ++++++++- test/test_restclient.cc | 34 +++++++++++ test/tests.cpp | 8 ++- test/tests.h | 1 + 11 files changed, 281 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3ec3cfc9..69296f84 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,24 @@ RestClient::Response r = RestClient::patch("http://url.com/patch", "application/ RestClient::Response r = RestClient::del("http://url.com/delete") RestClient::Response r = RestClient::head("http://url.com") RestClient::Response r = RestClient::options("http://url.com") +// Post Form Upload +/* Filling information about the form in a Helpers::PostFormInfo object */ +Helpers::PostFormInfo uploadInfo; +/* "submitted" is the name of the "file" input and "TestPostForm.txt" +is the location of the file to submit. + +*/ +uploadInfo.addFormFile("submitted", "TestPostForm.txt"); +/* In some rare cases, some fields related to the form can be filled with +addFormContent(), the 1st argument is the name of the input element and +the 2nd argument is the value assigned to it. + + +*/ +uploadInfo.addFormContent("filename", "myfile.cpp"); +uploadInfo.addFormContent("submit", "send"); +/* Performing a post form upload with the information provided above */ +RestClient::Response res = RestClient::postForm("http://posttestserver.com/post.php?dir=restclientcpptests", uploadInfo); ``` The response is of type [RestClient::Response][restclient_response] and has @@ -295,6 +313,23 @@ conn->SetHeaders(headers); auto resp = conn->get("/images/json"); ``` +## HTTP Proxy Tunneling Support + +An HTTP Proxy can be set to use for the upcoming request. +To specify a port number, append :[port] to the end of the host name. If not specified, `libcurl` will default to using port 1080 for proxies. The proxy string may be prefixed with `http://` or `https://`. If no HTTP(S) scheme is specified, the address provided to `libcurl` will be prefixed with `http://` to specify an HTTP proxy. A proxy host string can embedded user + password. +The operation will be tunneled through the proxy as curl option `CURLOPT_HTTPPROXYTUNNEL` is enabled by default. +A numerical IPv6 address must be written within [brackets]. + +```cpp +// set CURLOPT_PROXY +conn->SetProxy("https://37.187.100.23:3128"); +/* or you can set it without the protocol scheme and +http:// will be prefixed by default */ +conn->SetProxy("37.187.100.23:3128"); +/* the following request will be tunneled through the proxy */ +RestClient::Response res = conn->get("/get"); +``` + ## Dependencies - [libcurl][] diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index c39c022a..2f175263 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -224,6 +224,8 @@ class Connection { RestClient::Response get(const std::string& uri); RestClient::Response post(const std::string& uri, const std::string& data); + RestClient::Response postForm(const std::string& uri, + const Helpers::PostFormInfo& data); RestClient::Response put(const std::string& uri, const std::string& data); RestClient::Response patch(const std::string& uri, diff --git a/include/restclient-cpp/helpers.h b/include/restclient-cpp/helpers.h index 79d161bd..d5723309 100644 --- a/include/restclient-cpp/helpers.h +++ b/include/restclient-cpp/helpers.h @@ -9,6 +9,8 @@ #ifndef INCLUDE_RESTCLIENT_CPP_HELPERS_H_ #define INCLUDE_RESTCLIENT_CPP_HELPERS_H_ +#include + #include #include #include @@ -26,6 +28,24 @@ namespace RestClient { */ namespace Helpers { +/** @struct PostFormInfo + * @brief This struct represents the form information to send on + * POST Form requests + */ +struct PostFormInfo { + PostFormInfo(); + ~PostFormInfo(); + /* Fill in the file upload field */ + void addFormFile(const std::string& fieldName, + const std::string& fieldValue); + /* Fill in the filename or the submit field */ + void addFormContent(const std::string& fieldName, + const std::string& fieldValue); + + struct curl_httppost* formPtr; + struct curl_httppost* lastFormPtr; +}; + /** @struct UploadObject * @brief This structure represents the payload to upload on POST * requests diff --git a/include/restclient-cpp/restclient.h b/include/restclient-cpp/restclient.h index 601e7f2a..16de1b52 100644 --- a/include/restclient-cpp/restclient.h +++ b/include/restclient-cpp/restclient.h @@ -13,6 +13,7 @@ #include #include +#include "restclient-cpp/helpers.h" #include "restclient-cpp/version.h" /** @@ -53,6 +54,8 @@ Response get(const std::string& url); Response post(const std::string& url, const std::string& content_type, const std::string& data); +Response postForm(const std::string& url, + const RestClient::Helpers::PostFormInfo& data); Response put(const std::string& url, const std::string& content_type, const std::string& data); diff --git a/source/connection.cc b/source/connection.cc index 54118633..5027e107 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -94,6 +94,8 @@ RestClient::Connection::GetInfo() { ret.uriProxy = this->uriProxy; ret.unixSocketPath = this->unixSocketPath; + ret.uriProxy = this->uriProxy; + return ret; } @@ -298,12 +300,51 @@ RestClient::Connection::SetKeyPassword(const std::string& keyPassword) { */ void RestClient::Connection::SetProxy(const std::string& uriProxy) { + if (uriProxy.empty()) { + return; + } + + std::string uriProxyUpper = uriProxy; + // check if the provided address is prefixed with "http" + std::transform(uriProxyUpper.begin(), uriProxyUpper.end(), + uriProxyUpper.begin(), ::toupper); + + if (uriProxyUpper.compare(0, 4, "HTTP") != 0) { + this->uriProxy = "http://" + uriProxy; + } else { + this->uriProxy = uriProxy; + } +} + +/** + * @brief set key password + * + * @param key password + * + */ +void +RestClient::Connection::SetKeyPassword(const std::string& keyPassword) { + this->keyPassword = keyPassword; +} + +/** + * @brief set HTTP proxy address and port + * + * @param proxy address with port number + * + */ +void +RestClient::Connection::SetProxy(const std::string& uriProxy) { + if (uriProxy.empty()) { + return; + } + std::string uriProxyUpper = uriProxy; // check if the provided address is prefixed with "http" std::transform(uriProxyUpper.begin(), uriProxyUpper.end(), uriProxyUpper.begin(), ::toupper); - if ((uriProxy.length() > 0) && (uriProxyUpper.compare(0, 4, "HTTP") != 0)) { + if (uriProxyUpper.compare(0, 4, "HTTP") != 0) { this->uriProxy = "http://" + uriProxy; } else { this->uriProxy = uriProxy; @@ -478,6 +519,32 @@ RestClient::Connection::performCurlRequest(const std::string& uri, this->keyPath.c_str()); } // set key password + if (!this->keyPassword.empty()) { + curl_easy_setopt(this->curlHandle, CURLOPT_KEYPASSWD, + this->keyPassword.c_str()); + } + + // set web proxy address + if (!this->uriProxy.empty()) { + curl_easy_setopt(this->curlHandle, CURLOPT_PROXY, + uriProxy.c_str()); + curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPROXYTUNNEL, + 1L); + } + // set key password + if (!this->keyPassword.empty()) { + curl_easy_setopt(this->curlHandle, CURLOPT_KEYPASSWD, + this->keyPassword.c_str()); + } + + // set web proxy address + if (!this->uriProxy.empty()) { + curl_easy_setopt(this->curlHandle, CURLOPT_PROXY, + uriProxy.c_str()); + curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPROXYTUNNEL, + 1L); + } + // set key password if (!this->keyPassword.empty()) { curl_easy_setopt(getCurlHandle(), CURLOPT_KEYPASSWD, this->keyPassword.c_str()); @@ -580,6 +647,26 @@ RestClient::Connection::post(const std::string& url, return this->performCurlRequest(url); } +/** + * @brief HTTP POST Form method + * + * @param url to query + * @param data form info + * + * @return response struct + */ +RestClient::Response +RestClient::Connection::postForm(const std::string& url, + const Helpers::PostFormInfo& data) { + /** Now specify we want to POST data */ + curl_easy_setopt(this->curlHandle, CURLOPT_POST, 1L); + /* stating that Expect: 100-continue is not wanted */ + AppendHeader("Expect", ""); + /** set post form */ + curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPOST, data.formPtr); + + return this->performCurlRequest(url); +} /** * @brief HTTP PUT method * diff --git a/source/helpers.cc b/source/helpers.cc index aa51bf58..6ae12468 100644 --- a/source/helpers.cc +++ b/source/helpers.cc @@ -36,6 +36,7 @@ size_t RestClient::Helpers::write_callback(void *data, size_t size, * @param size of data * @param nmemb memblock * @param userdata pointer to user data object to save headr data + * * @return size * nmemb; */ size_t RestClient::Helpers::header_callback(void *data, size_t size, @@ -88,3 +89,51 @@ size_t RestClient::Helpers::read_callback(void *data, size_t size, /** return copied size */ return copy_size; } + +/** + * @brief PostFormInfo constructor + */ +RestClient::Helpers::PostFormInfo::PostFormInfo() + : formPtr(NULL), lastFormPtr(NULL) { +} + +/** + * @brief PostFormInfo destructor + */ +RestClient::Helpers::PostFormInfo::~PostFormInfo() { + // cleanup the formpost chain + if (this->formPtr) { + curl_formfree(this->formPtr); + this->formPtr = NULL; + this->lastFormPtr = NULL; + } +} + +/** + * @brief set the name and the value of the HTML "file" form's input + * + * @param fieldName name of the "file" input + * @param fieldValue path to the file to upload + */ +void RestClient::Helpers::PostFormInfo::addFormFile( + const std::string& fieldName, const std::string& fieldValue) { + curl_formadd(&this->formPtr, &this->lastFormPtr, + CURLFORM_COPYNAME, fieldName.c_str(), + CURLFORM_FILE, fieldValue.c_str(), + CURLFORM_END); +} + +/** + * @brief set the name and the value of an HTML form's input + * (other than "file" like "text", "hidden" or "submit") + * + * @param fieldName name of the input element + * @param fieldValue value to be assigned to the input element + */ +void RestClient::Helpers::PostFormInfo::addFormContent( + const std::string& fieldName, const std::string& fieldValue) { + curl_formadd(&this->formPtr, &this->lastFormPtr, + CURLFORM_COPYNAME, fieldName.c_str(), + CURLFORM_COPYCONTENTS, fieldValue.c_str(), + CURLFORM_END); +} diff --git a/source/restclient.cc b/source/restclient.cc index ed0fe861..ff0f5469 100644 --- a/source/restclient.cc +++ b/source/restclient.cc @@ -87,6 +87,23 @@ RestClient::Response RestClient::post(const std::string& url, #endif } +/** + * @brief HTTP POST Form method + * + * @param url to query + * @param data post form information + * + * @return response struct + */ +RestClient::Response RestClient::postForm(const std::string& url, + const RestClient::Helpers::PostFormInfo& data) { + RestClient::Response ret; + RestClient::Connection *conn = new RestClient::Connection(""); + ret = conn->postForm(url, data); + delete conn; + return ret; +} + /** * @brief HTTP PUT method * diff --git a/test/test_connection.cc b/test/test_connection.cc index d41bed7b..303e06b2 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -286,7 +286,7 @@ TEST_F(ConnectionTest, TestSetProgress) TEST_F(ConnectionTestRemote, TestProxy) { - conn->SetProxy(RestClient::TestProxyUrl); + conn->SetProxy(RestClient::TestProxy); RestClient::Response res = conn->get("/get"); EXPECT_EQ(200, res.code); } @@ -378,4 +378,30 @@ TEST_F(ConnectionTest, TestSetWriteFunction) EXPECT_EQ(ret_res, &res); EXPECT_EQ(200, res.code); EXPECT_EQ(ret, lineReceived.size() + lines); +======= + EXPECT_EQ("Failed to query.", res.body); + EXPECT_EQ(-1, res.code); +>>>>>>> 934aff7... Added web proxy tunneling support. +} + +TEST_F(ConnectionTest, TestProxy) +{ + conn->SetProxy("37.187.100.23:3128"); + RestClient::Response res = conn->get("/get"); + EXPECT_EQ(200, res.code); +} + +TEST_F(ConnectionTest, TestProxyAddressPrefixed) +{ + conn->SetProxy("https://37.187.100.23:3128"); + RestClient::Response res = conn->get("/get"); + EXPECT_EQ(200, res.code); +} + +TEST_F(ConnectionTest, TestInvalidProxy) +{ + conn->SetProxy("127.0.0.1:666"); + RestClient::Response res = conn->get("/get"); + EXPECT_EQ("Failed to query.", res.body); + EXPECT_EQ(-1, res.code); } diff --git a/test/test_restclient.cc b/test/test_restclient.cc index cf64b6b2..4ce236c8 100644 --- a/test/test_restclient.cc +++ b/test/test_restclient.cc @@ -1,7 +1,11 @@ #include "restclient-cpp/restclient.h" +#include "restclient-cpp/helpers.h" #include #include +#include +#include #include +#include #include "tests.h" @@ -120,6 +124,36 @@ TEST_F(RestClientTest, TestRestClientPOSTBody) EXPECT_EQ(RestClient::TestUrl+"/post", root.get("url", "no url set").asString()); EXPECT_EQ("restclient-cpp/" RESTCLIENT_VERSION, root["headers"].get("User-Agent", "no url set").asString()); } +TEST_F(RestClientTest, TestRestClientPostForm) +{ + // generating a file name with a timestamp + std::ostringstream fileName; + time_t rawtime; + tm * timeinfo; + time(&rawtime); + timeinfo = localtime( &rawtime ); + + fileName << "TestPostForm_" << (timeinfo->tm_year)+1900 << "_" << timeinfo->tm_mon+1 + << "_" << timeinfo->tm_mday << "-" << timeinfo->tm_hour + << "_"<< timeinfo->tm_min << "_" << timeinfo->tm_sec << ".txt"; + + // creating a dummy file to upload via a post form request + std::ofstream ofDummyFile(fileName.str().c_str()); + ASSERT_TRUE(static_cast(ofDummyFile)); + ofDummyFile << "Dummy file for the unit test 'TestRestClientPostForm' of the restclient-cpp Project."; + ASSERT_TRUE(static_cast(ofDummyFile)); + ofDummyFile.close(); + + // uploading the dummy file + RestClient::Helpers::PostFormInfo UploadInfo; + UploadInfo.addFormFile("submitted", fileName.str()); + UploadInfo.addFormContent("filename", fileName.str()); + RestClient::Response res = RestClient::postForm("http://posttestserver.com/post.php?dir=restclientcpptests", UploadInfo); + EXPECT_EQ(200, res.code); + + // remove dummy file + remove(fileName.str().c_str()); +} // check for failure TEST_F(RestClientTest, TestRestClientPOSTFailureCode) diff --git a/test/tests.cpp b/test/tests.cpp index e94ecccb..8b513908 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -9,6 +9,7 @@ namespace RestClient std::string TestNonExistantUrl; std::string TestServer; std::string TestUrl; + std::string TestProxy; std::string TestProxyUrl; }; @@ -19,8 +20,9 @@ int main(int argc, char **argv) RestClient::TestNonExistantUrl = "http://nonexistent"; RestClient::TestServer = "127.0.0.1:8998"; RestClient::TestUrl = "http://" + RestClient::TestServer; - RestClient::TestProxyUrl = "http://127.0.0.1:3128"; + RestClient::TestProxy = "127.0.0.1:3128"; + RestClient::TestProxyUrl = "http://" + RestClient::TestProxy; - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/test/tests.h b/test/tests.h index 484cdf61..92113980 100644 --- a/test/tests.h +++ b/test/tests.h @@ -8,6 +8,7 @@ namespace RestClient extern std::string TestNonExistantUrl; extern std::string TestServer; extern std::string TestUrl; + extern std::string TestProxy; extern std::string TestProxyUrl; }; From 2b1a1356e0fc1c2168e2b5fc09d0a92fa97e19ba Mon Sep 17 00:00:00 2001 From: amzoughi Date: Sat, 24 Dec 2016 21:32:22 +0100 Subject: [PATCH 2/3] Added a method to perform a post form upload. Fixed code indentation to comply with Cpplint. Moved PostFormInfo from Helpers to RestClient namespace. Fixed PostFormInfo indentation. --- README.md | 5 ++- include/restclient-cpp/connection.h | 2 +- include/restclient-cpp/helpers.h | 18 -------- include/restclient-cpp/restclient.h | 22 +++++++++- source/connection.cc | 20 +++++++++ source/helpers.cc | 48 --------------------- source/restclient.cc | 65 +++++++++++++++++++++++++++++ test/test_restclient.cc | 31 +++++++++++++- 8 files changed, 140 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 69296f84..c3d74531 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,10 @@ RestClient::Response r = RestClient::patch("http://url.com/patch", "application/ RestClient::Response r = RestClient::del("http://url.com/delete") RestClient::Response r = RestClient::head("http://url.com") RestClient::Response r = RestClient::options("http://url.com") + // Post Form Upload -/* Filling information about the form in a Helpers::PostFormInfo object */ -Helpers::PostFormInfo uploadInfo; +/* Filling information about the form in a RestClient::PostFormInfo object */ +RestClient::PostFormInfo uploadInfo; /* "submitted" is the name of the "file" input and "TestPostForm.txt" is the location of the file to submit. diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index 2f175263..471bb8a5 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -225,7 +225,7 @@ class Connection { RestClient::Response post(const std::string& uri, const std::string& data); RestClient::Response postForm(const std::string& uri, - const Helpers::PostFormInfo& data); + const PostFormInfo& data); RestClient::Response put(const std::string& uri, const std::string& data); RestClient::Response patch(const std::string& uri, diff --git a/include/restclient-cpp/helpers.h b/include/restclient-cpp/helpers.h index d5723309..c26a077d 100644 --- a/include/restclient-cpp/helpers.h +++ b/include/restclient-cpp/helpers.h @@ -28,24 +28,6 @@ namespace RestClient { */ namespace Helpers { -/** @struct PostFormInfo - * @brief This struct represents the form information to send on - * POST Form requests - */ -struct PostFormInfo { - PostFormInfo(); - ~PostFormInfo(); - /* Fill in the file upload field */ - void addFormFile(const std::string& fieldName, - const std::string& fieldValue); - /* Fill in the filename or the submit field */ - void addFormContent(const std::string& fieldName, - const std::string& fieldValue); - - struct curl_httppost* formPtr; - struct curl_httppost* lastFormPtr; -}; - /** @struct UploadObject * @brief This structure represents the payload to upload on POST * requests diff --git a/include/restclient-cpp/restclient.h b/include/restclient-cpp/restclient.h index 16de1b52..1c472051 100644 --- a/include/restclient-cpp/restclient.h +++ b/include/restclient-cpp/restclient.h @@ -41,6 +41,26 @@ typedef struct { HeaderFields headers; } Response; +/** @class PostFormInfo + * @brief This class represents the form information to send on + * POST Form requests + */ +class PostFormInfo { + struct curl_httppost* formPtr; + struct curl_httppost* lastFormPtr; + public: + PostFormInfo(); + ~PostFormInfo(); + /* Fill in the file upload field */ + void addFormFile(const std::string& fieldName, + const std::string& fieldValue); + /* Fill in the filename or the submit field */ + void addFormContent(const std::string& fieldName, + const std::string& fieldValue); + /* Get Form pointer */ + struct curl_httppost* GetFormPtr() const { return formPtr; } +}; + // init and disable functions int init(); void disable(); @@ -55,7 +75,7 @@ Response post(const std::string& url, const std::string& content_type, const std::string& data); Response postForm(const std::string& url, - const RestClient::Helpers::PostFormInfo& data); + const PostFormInfo& data); Response put(const std::string& url, const std::string& content_type, const std::string& data); diff --git a/source/connection.cc b/source/connection.cc index 5027e107..356c8fc9 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -667,6 +667,26 @@ RestClient::Connection::postForm(const std::string& url, return this->performCurlRequest(url); } +/** + * @brief HTTP POST Form method + * + * @param url to query + * @param data form info + * + * @return response struct + */ +RestClient::Response +RestClient::Connection::postForm(const std::string& url, + const PostFormInfo& data) { + /** Now specify we want to POST data */ + curl_easy_setopt(this->curlHandle, CURLOPT_POST, 1L); + /* stating that Expect: 100-continue is not wanted */ + AppendHeader("Expect", ""); + /** set post form */ + curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPOST, data.GetFormPtr()); + + return this->performCurlRequest(url); +} /** * @brief HTTP PUT method * diff --git a/source/helpers.cc b/source/helpers.cc index 6ae12468..eb62cac5 100644 --- a/source/helpers.cc +++ b/source/helpers.cc @@ -89,51 +89,3 @@ size_t RestClient::Helpers::read_callback(void *data, size_t size, /** return copied size */ return copy_size; } - -/** - * @brief PostFormInfo constructor - */ -RestClient::Helpers::PostFormInfo::PostFormInfo() - : formPtr(NULL), lastFormPtr(NULL) { -} - -/** - * @brief PostFormInfo destructor - */ -RestClient::Helpers::PostFormInfo::~PostFormInfo() { - // cleanup the formpost chain - if (this->formPtr) { - curl_formfree(this->formPtr); - this->formPtr = NULL; - this->lastFormPtr = NULL; - } -} - -/** - * @brief set the name and the value of the HTML "file" form's input - * - * @param fieldName name of the "file" input - * @param fieldValue path to the file to upload - */ -void RestClient::Helpers::PostFormInfo::addFormFile( - const std::string& fieldName, const std::string& fieldValue) { - curl_formadd(&this->formPtr, &this->lastFormPtr, - CURLFORM_COPYNAME, fieldName.c_str(), - CURLFORM_FILE, fieldValue.c_str(), - CURLFORM_END); -} - -/** - * @brief set the name and the value of an HTML form's input - * (other than "file" like "text", "hidden" or "submit") - * - * @param fieldName name of the input element - * @param fieldValue value to be assigned to the input element - */ -void RestClient::Helpers::PostFormInfo::addFormContent( - const std::string& fieldName, const std::string& fieldValue) { - curl_formadd(&this->formPtr, &this->lastFormPtr, - CURLFORM_COPYNAME, fieldName.c_str(), - CURLFORM_COPYCONTENTS, fieldValue.c_str(), - CURLFORM_END); -} diff --git a/source/restclient.cc b/source/restclient.cc index ff0f5469..dd027f8e 100644 --- a/source/restclient.cc +++ b/source/restclient.cc @@ -104,6 +104,23 @@ RestClient::Response RestClient::postForm(const std::string& url, return ret; } +/** + * @brief HTTP POST Form method + * + * @param url to query + * @param data post form information + * + * @return response struct + */ +RestClient::Response RestClient::postForm(const std::string& url, + const PostFormInfo& data) { + RestClient::Response ret; + RestClient::Connection *conn = new RestClient::Connection(""); + ret = conn->postForm(url, data); + delete conn; + return ret; +} + /** * @brief HTTP PUT method * @@ -204,3 +221,51 @@ RestClient::Response RestClient::options(const std::string& url) { delete conn; return ret; } + +/** + * @brief PostFormInfo constructor + */ +RestClient::PostFormInfo::PostFormInfo() + : formPtr(NULL), lastFormPtr(NULL) { +} + +/** + * @brief PostFormInfo destructor + */ +RestClient::PostFormInfo::~PostFormInfo() { + // cleanup the formpost chain + if (this->formPtr) { + curl_formfree(this->formPtr); + this->formPtr = NULL; + this->lastFormPtr = NULL; + } +} + +/** + * @brief set the name and the value of the HTML "file" form's input + * + * @param fieldName name of the "file" input + * @param fieldValue path to the file to upload + */ +void RestClient::PostFormInfo::addFormFile( + const std::string& fieldName, const std::string& fieldValue) { + curl_formadd(&this->formPtr, &this->lastFormPtr, + CURLFORM_COPYNAME, fieldName.c_str(), + CURLFORM_FILE, fieldValue.c_str(), + CURLFORM_END); +} + +/** + * @brief set the name and the value of an HTML form's input + * (other than "file" like "text", "hidden" or "submit") + * + * @param fieldName name of the input element + * @param fieldValue value to be assigned to the input element + */ +void RestClient::PostFormInfo::addFormContent( + const std::string& fieldName, const std::string& fieldValue) { + curl_formadd(&this->formPtr, &this->lastFormPtr, + CURLFORM_COPYNAME, fieldName.c_str(), + CURLFORM_COPYCONTENTS, fieldValue.c_str(), + CURLFORM_END); +} diff --git a/test/test_restclient.cc b/test/test_restclient.cc index 4ce236c8..20331dbc 100644 --- a/test/test_restclient.cc +++ b/test/test_restclient.cc @@ -1,5 +1,4 @@ #include "restclient-cpp/restclient.h" -#include "restclient-cpp/helpers.h" #include #include #include @@ -144,6 +143,36 @@ TEST_F(RestClientTest, TestRestClientPostForm) ASSERT_TRUE(static_cast(ofDummyFile)); ofDummyFile.close(); + // uploading the dummy file + RestClient::PostFormInfo UploadInfo; + UploadInfo.addFormFile("submitted", fileName.str()); + UploadInfo.addFormContent("filename", fileName.str()); + RestClient::Response res = RestClient::postForm("http://posttestserver.com/post.php?dir=restclientcpptests", UploadInfo); + EXPECT_EQ(200, res.code); + + // remove dummy file + remove(fileName.str().c_str()); +} +TEST_F(RestClientTest, TestRestClientPostForm) +{ + // generating a file name with a timestamp + std::ostringstream fileName; + time_t rawtime; + tm * timeinfo; + time(&rawtime); + timeinfo = localtime( &rawtime ); + + fileName << "TestPostForm_" << (timeinfo->tm_year)+1900 << "_" << timeinfo->tm_mon+1 + << "_" << timeinfo->tm_mday << "-" << timeinfo->tm_hour + << "_"<< timeinfo->tm_min << "_" << timeinfo->tm_sec << ".txt"; + + // creating a dummy file to upload via a post form request + std::ofstream ofDummyFile(fileName.str().c_str()); + ASSERT_TRUE(static_cast(ofDummyFile)); + ofDummyFile << "Dummy file for the unit test 'TestRestClientPostForm' of the restclient-cpp Project."; + ASSERT_TRUE(static_cast(ofDummyFile)); + ofDummyFile.close(); + // uploading the dummy file RestClient::Helpers::PostFormInfo UploadInfo; UploadInfo.addFormFile("submitted", fileName.str()); From 8789b56111a88416c9c9dcd483212d67cdbcf9be Mon Sep 17 00:00:00 2001 From: ejacques Date: Sat, 31 Oct 2020 19:11:31 -0400 Subject: [PATCH 3/3] Rebase https://github.com/embeddedmz/restclient-cpp.git based on https://github.com/mrtazz/restclient-cpp.git --- README.md | 6 +- include/restclient-cpp/connection.h | 4 +- include/restclient-cpp/restclient.h | 14 ++-- source/connection.cc | 77 +++--------------- source/restclient.cc | 116 ++++++++-------------------- test/test_connection.cc | 25 ------ test/test_restclient.cc | 34 +------- 7 files changed, 57 insertions(+), 219 deletions(-) diff --git a/README.md b/README.md index c3d74531..93271acc 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ RestClient::Response r = RestClient::head("http://url.com") RestClient::Response r = RestClient::options("http://url.com") // Post Form Upload -/* Filling information about the form in a RestClient::PostFormInfo object */ -RestClient::PostFormInfo uploadInfo; +/* Filling information about the form in a RestClient::FormData object */ +RestClient::FormData uploadInfo; /* "submitted" is the name of the "file" input and "TestPostForm.txt" is the location of the file to submit. @@ -48,7 +48,7 @@ the 2nd argument is the value assigned to it. uploadInfo.addFormContent("filename", "myfile.cpp"); uploadInfo.addFormContent("submit", "send"); /* Performing a post form upload with the information provided above */ -RestClient::Response res = RestClient::postForm("http://posttestserver.com/post.php?dir=restclientcpptests", uploadInfo); +RestClient::Response res = RestClient::post("http://posttestserver.com/post.php?dir=restclientcpptests", uploadInfo); ``` The response is of type [RestClient::Response][restclient_response] and has diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index 471bb8a5..30089915 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -224,8 +224,8 @@ class Connection { RestClient::Response get(const std::string& uri); RestClient::Response post(const std::string& uri, const std::string& data); - RestClient::Response postForm(const std::string& uri, - const PostFormInfo& data); + RestClient::Response post(const std::string& uri, + const FormData& data); RestClient::Response put(const std::string& uri, const std::string& data); RestClient::Response patch(const std::string& uri, diff --git a/include/restclient-cpp/restclient.h b/include/restclient-cpp/restclient.h index 1c472051..8622e202 100644 --- a/include/restclient-cpp/restclient.h +++ b/include/restclient-cpp/restclient.h @@ -41,16 +41,16 @@ typedef struct { HeaderFields headers; } Response; -/** @class PostFormInfo +/** @class FormData * @brief This class represents the form information to send on * POST Form requests */ -class PostFormInfo { +class FormData { struct curl_httppost* formPtr; struct curl_httppost* lastFormPtr; public: - PostFormInfo(); - ~PostFormInfo(); + FormData(); + ~FormData(); /* Fill in the file upload field */ void addFormFile(const std::string& fieldName, const std::string& fieldValue); @@ -58,7 +58,7 @@ class PostFormInfo { void addFormContent(const std::string& fieldName, const std::string& fieldValue); /* Get Form pointer */ - struct curl_httppost* GetFormPtr() const { return formPtr; } + struct curl_httppost* getFormPtr() const { return formPtr; } }; // init and disable functions @@ -74,8 +74,8 @@ Response get(const std::string& url); Response post(const std::string& url, const std::string& content_type, const std::string& data); -Response postForm(const std::string& url, - const PostFormInfo& data); +Response post(const std::string& url, + const FormData& data); Response put(const std::string& url, const std::string& content_type, const std::string& data); diff --git a/source/connection.cc b/source/connection.cc index 356c8fc9..39c24ab1 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -316,41 +316,6 @@ RestClient::Connection::SetProxy(const std::string& uriProxy) { } } -/** - * @brief set key password - * - * @param key password - * - */ -void -RestClient::Connection::SetKeyPassword(const std::string& keyPassword) { - this->keyPassword = keyPassword; -} - -/** - * @brief set HTTP proxy address and port - * - * @param proxy address with port number - * - */ -void -RestClient::Connection::SetProxy(const std::string& uriProxy) { - if (uriProxy.empty()) { - return; - } - - std::string uriProxyUpper = uriProxy; - // check if the provided address is prefixed with "http" - std::transform(uriProxyUpper.begin(), uriProxyUpper.end(), - uriProxyUpper.begin(), ::toupper); - - if (uriProxyUpper.compare(0, 4, "HTTP") != 0) { - this->uriProxy = "http://" + uriProxy; - } else { - this->uriProxy = uriProxy; - } -} - /** * @brief set custom Unix socket path for connection. * See https://curl.haxx.se/libcurl/c/CURLOPT_UNIX_SOCKET_PATH.html @@ -520,28 +485,28 @@ RestClient::Connection::performCurlRequest(const std::string& uri, } // set key password if (!this->keyPassword.empty()) { - curl_easy_setopt(this->curlHandle, CURLOPT_KEYPASSWD, + curl_easy_setopt(getCurlHandle(), CURLOPT_KEYPASSWD, this->keyPassword.c_str()); } // set web proxy address if (!this->uriProxy.empty()) { - curl_easy_setopt(this->curlHandle, CURLOPT_PROXY, + curl_easy_setopt(getCurlHandle(), CURLOPT_PROXY, uriProxy.c_str()); - curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPROXYTUNNEL, + curl_easy_setopt(getCurlHandle(), CURLOPT_HTTPPROXYTUNNEL, 1L); } // set key password if (!this->keyPassword.empty()) { - curl_easy_setopt(this->curlHandle, CURLOPT_KEYPASSWD, + curl_easy_setopt(getCurlHandle(), CURLOPT_KEYPASSWD, this->keyPassword.c_str()); } // set web proxy address if (!this->uriProxy.empty()) { - curl_easy_setopt(this->curlHandle, CURLOPT_PROXY, + curl_easy_setopt(getCurlHandle(), CURLOPT_PROXY, uriProxy.c_str()); - curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPROXYTUNNEL, + curl_easy_setopt(getCurlHandle(), CURLOPT_HTTPPROXYTUNNEL, 1L); } // set key password @@ -656,34 +621,14 @@ RestClient::Connection::post(const std::string& url, * @return response struct */ RestClient::Response -RestClient::Connection::postForm(const std::string& url, - const Helpers::PostFormInfo& data) { - /** Now specify we want to POST data */ - curl_easy_setopt(this->curlHandle, CURLOPT_POST, 1L); - /* stating that Expect: 100-continue is not wanted */ - AppendHeader("Expect", ""); - /** set post form */ - curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPOST, data.formPtr); - - return this->performCurlRequest(url); -} -/** - * @brief HTTP POST Form method - * - * @param url to query - * @param data form info - * - * @return response struct - */ -RestClient::Response -RestClient::Connection::postForm(const std::string& url, - const PostFormInfo& data) { +RestClient::Connection::post(const std::string& url, + const FormData& data) { /** Now specify we want to POST data */ - curl_easy_setopt(this->curlHandle, CURLOPT_POST, 1L); + curl_easy_setopt(getCurlHandle(), CURLOPT_POST, 1L); /* stating that Expect: 100-continue is not wanted */ AppendHeader("Expect", ""); /** set post form */ - curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPOST, data.GetFormPtr()); + curl_easy_setopt(getCurlHandle(), CURLOPT_HTTPPOST, data.getFormPtr()); return this->performCurlRequest(url); } @@ -727,7 +672,7 @@ RestClient::Connection::put(const std::string& url, */ RestClient::Response RestClient::Connection::patch(const std::string& url, - const std::string& data) { + const std::string& data) { /** initialize upload object */ RestClient::Helpers::UploadObject up_obj; up_obj.data = data.c_str(); diff --git a/source/restclient.cc b/source/restclient.cc index dd027f8e..1cc638d0 100644 --- a/source/restclient.cc +++ b/source/restclient.cc @@ -49,16 +49,10 @@ void RestClient::disable() { * @return response struct */ RestClient::Response RestClient::get(const std::string& url) { -#if __cplusplus >= 201402L - auto conn = std::make_unique(""); - return conn->get(url); -#else RestClient::Response ret; - RestClient::Connection *conn = new RestClient::Connection(""); - ret = conn->get(url); - delete conn; + RestClient::Connection conn(""); + ret = conn.get(url); return ret; -#endif } /** @@ -73,18 +67,11 @@ RestClient::Response RestClient::get(const std::string& url) { RestClient::Response RestClient::post(const std::string& url, const std::string& ctype, const std::string& data) { -#if __cplusplus >= 201402L - auto conn = std::make_unique(""); - conn->AppendHeader("Content-Type", ctype); - return conn->post(url, data); -#else RestClient::Response ret; - RestClient::Connection *conn = new RestClient::Connection(""); - conn->AppendHeader("Content-Type", ctype); - ret = conn->post(url, data); - delete conn; + RestClient::Connection conn(""); + conn.AppendHeader("Content-Type", ctype); + ret = conn.post(url, data); return ret; -#endif } /** @@ -95,29 +82,11 @@ RestClient::Response RestClient::post(const std::string& url, * * @return response struct */ -RestClient::Response RestClient::postForm(const std::string& url, - const RestClient::Helpers::PostFormInfo& data) { - RestClient::Response ret; - RestClient::Connection *conn = new RestClient::Connection(""); - ret = conn->postForm(url, data); - delete conn; - return ret; -} - -/** - * @brief HTTP POST Form method - * - * @param url to query - * @param data post form information - * - * @return response struct - */ -RestClient::Response RestClient::postForm(const std::string& url, - const PostFormInfo& data) { +RestClient::Response RestClient::post(const std::string& url, + const FormData& data) { RestClient::Response ret; - RestClient::Connection *conn = new RestClient::Connection(""); - ret = conn->postForm(url, data); - delete conn; + RestClient::Connection conn(""); + ret = conn.post(url, data); return ret; } @@ -133,18 +102,11 @@ RestClient::Response RestClient::postForm(const std::string& url, RestClient::Response RestClient::put(const std::string& url, const std::string& ctype, const std::string& data) { -#if __cplusplus >= 201402L - auto conn = std::make_unique(""); - conn->AppendHeader("Content-Type", ctype); - return conn->put(url, data); -#else RestClient::Response ret; - RestClient::Connection *conn = new RestClient::Connection(""); - conn->AppendHeader("Content-Type", ctype); - ret = conn->put(url, data); - delete conn; + RestClient::Connection conn(""); + conn.AppendHeader("Content-Type", ctype); + ret = conn.put(url, data); return ret; -#endif } /** @@ -157,13 +119,12 @@ RestClient::Response RestClient::put(const std::string& url, * @return response struct */ RestClient::Response RestClient::patch(const std::string& url, - const std::string& ctype, - const std::string& data) { + const std::string& ctype, + const std::string& data) { RestClient::Response ret; - RestClient::Connection *conn = new RestClient::Connection(""); - conn->AppendHeader("Content-Type", ctype); - ret = conn->patch(url, data); - delete conn; + RestClient::Connection conn(""); + conn.AppendHeader("Content-Type", ctype); + ret = conn.patch(url, data); return ret; } @@ -175,16 +136,10 @@ RestClient::Response RestClient::patch(const std::string& url, * @return response struct */ RestClient::Response RestClient::del(const std::string& url) { -#if __cplusplus >= 201402L - auto conn = std::make_unique(""); - return conn->del(url); -#else RestClient::Response ret; - RestClient::Connection *conn = new RestClient::Connection(""); - ret = conn->del(url); - delete conn; + RestClient::Connection conn(""); + ret = conn.del(url); return ret; -#endif } /** @@ -195,16 +150,10 @@ RestClient::Response RestClient::del(const std::string& url) { * @return response struct */ RestClient::Response RestClient::head(const std::string& url) { -#if __cplusplus >= 201402L - auto conn = std::make_unique(""); - return conn->head(url); -#else RestClient::Response ret; - RestClient::Connection *conn = new RestClient::Connection(""); - ret = conn->head(url); - delete conn; + RestClient::Connection conn(""); + ret = conn.head(url); return ret; -#endif } /** @@ -216,23 +165,22 @@ RestClient::Response RestClient::head(const std::string& url) { */ RestClient::Response RestClient::options(const std::string& url) { RestClient::Response ret; - RestClient::Connection *conn = new RestClient::Connection(""); - ret = conn->options(url); - delete conn; + RestClient::Connection conn(""); + ret = conn.options(url); return ret; } /** - * @brief PostFormInfo constructor + * @brief FormData constructor */ -RestClient::PostFormInfo::PostFormInfo() - : formPtr(NULL), lastFormPtr(NULL) { +RestClient::FormData::FormData() + : formPtr(NULL), lastFormPtr(NULL) { } /** - * @brief PostFormInfo destructor + * @brief FormData destructor */ -RestClient::PostFormInfo::~PostFormInfo() { +RestClient::FormData::~FormData() { // cleanup the formpost chain if (this->formPtr) { curl_formfree(this->formPtr); @@ -247,8 +195,8 @@ RestClient::PostFormInfo::~PostFormInfo() { * @param fieldName name of the "file" input * @param fieldValue path to the file to upload */ -void RestClient::PostFormInfo::addFormFile( - const std::string& fieldName, const std::string& fieldValue) { +void RestClient::FormData::addFormFile(const std::string& fieldName, + const std::string& fieldValue) { curl_formadd(&this->formPtr, &this->lastFormPtr, CURLFORM_COPYNAME, fieldName.c_str(), CURLFORM_FILE, fieldValue.c_str(), @@ -262,8 +210,8 @@ void RestClient::PostFormInfo::addFormFile( * @param fieldName name of the input element * @param fieldValue value to be assigned to the input element */ -void RestClient::PostFormInfo::addFormContent( - const std::string& fieldName, const std::string& fieldValue) { +void RestClient::FormData::addFormContent(const std::string& fieldName, + const std::string& fieldValue) { curl_formadd(&this->formPtr, &this->lastFormPtr, CURLFORM_COPYNAME, fieldName.c_str(), CURLFORM_COPYCONTENTS, fieldValue.c_str(), diff --git a/test/test_connection.cc b/test/test_connection.cc index 303e06b2..e54e5955 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -378,30 +378,5 @@ TEST_F(ConnectionTest, TestSetWriteFunction) EXPECT_EQ(ret_res, &res); EXPECT_EQ(200, res.code); EXPECT_EQ(ret, lineReceived.size() + lines); -======= - EXPECT_EQ("Failed to query.", res.body); - EXPECT_EQ(-1, res.code); ->>>>>>> 934aff7... Added web proxy tunneling support. } -TEST_F(ConnectionTest, TestProxy) -{ - conn->SetProxy("37.187.100.23:3128"); - RestClient::Response res = conn->get("/get"); - EXPECT_EQ(200, res.code); -} - -TEST_F(ConnectionTest, TestProxyAddressPrefixed) -{ - conn->SetProxy("https://37.187.100.23:3128"); - RestClient::Response res = conn->get("/get"); - EXPECT_EQ(200, res.code); -} - -TEST_F(ConnectionTest, TestInvalidProxy) -{ - conn->SetProxy("127.0.0.1:666"); - RestClient::Response res = conn->get("/get"); - EXPECT_EQ("Failed to query.", res.body); - EXPECT_EQ(-1, res.code); -} diff --git a/test/test_restclient.cc b/test/test_restclient.cc index 20331dbc..f4fbfad7 100644 --- a/test/test_restclient.cc +++ b/test/test_restclient.cc @@ -144,40 +144,10 @@ TEST_F(RestClientTest, TestRestClientPostForm) ofDummyFile.close(); // uploading the dummy file - RestClient::PostFormInfo UploadInfo; + RestClient::FormData UploadInfo; UploadInfo.addFormFile("submitted", fileName.str()); UploadInfo.addFormContent("filename", fileName.str()); - RestClient::Response res = RestClient::postForm("http://posttestserver.com/post.php?dir=restclientcpptests", UploadInfo); - EXPECT_EQ(200, res.code); - - // remove dummy file - remove(fileName.str().c_str()); -} -TEST_F(RestClientTest, TestRestClientPostForm) -{ - // generating a file name with a timestamp - std::ostringstream fileName; - time_t rawtime; - tm * timeinfo; - time(&rawtime); - timeinfo = localtime( &rawtime ); - - fileName << "TestPostForm_" << (timeinfo->tm_year)+1900 << "_" << timeinfo->tm_mon+1 - << "_" << timeinfo->tm_mday << "-" << timeinfo->tm_hour - << "_"<< timeinfo->tm_min << "_" << timeinfo->tm_sec << ".txt"; - - // creating a dummy file to upload via a post form request - std::ofstream ofDummyFile(fileName.str().c_str()); - ASSERT_TRUE(static_cast(ofDummyFile)); - ofDummyFile << "Dummy file for the unit test 'TestRestClientPostForm' of the restclient-cpp Project."; - ASSERT_TRUE(static_cast(ofDummyFile)); - ofDummyFile.close(); - - // uploading the dummy file - RestClient::Helpers::PostFormInfo UploadInfo; - UploadInfo.addFormFile("submitted", fileName.str()); - UploadInfo.addFormContent("filename", fileName.str()); - RestClient::Response res = RestClient::postForm("http://posttestserver.com/post.php?dir=restclientcpptests", UploadInfo); + RestClient::Response res = RestClient::post("http://ptsv2.com/t/restclientcpptests/post", UploadInfo); EXPECT_EQ(200, res.code); // remove dummy file