From cfb825da3957a77cffbd1c14eb83b52d8735a757 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Fri, 16 Dec 2016 01:02:44 +0100 Subject: [PATCH 01/12] Added web proxy tunneling support. --- include/restclient-cpp/connection.h | 5 +++++ source/connection.cc | 24 ++++++++++++++++++++++++ test/test_connection.cc | 7 +++++++ 3 files changed, 36 insertions(+) diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index 158f1cde..4e28827d 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -105,6 +105,7 @@ class Connection { std::string certType; std::string keyPath; std::string customUserAgent; + std::string uriProxy; RequestInfo lastRequest; } Info; @@ -143,6 +144,9 @@ class Connection { // set CURLOPT_SSLKEY. Default format is PEM void SetKeyPath(const std::string& keyPath); + // set CURLOPT_PROXY + void SetProxy(const std::string& uriProxy); + std::string GetUserAgent(); RestClient::Connection::Info GetInfo(); @@ -184,6 +188,7 @@ class Connection { std::string certPath; std::string certType; std::string keyPath; + std::string uriProxy; RestClient::Response performCurlRequest(const std::string& uri); }; }; // namespace RestClient diff --git a/source/connection.cc b/source/connection.cc index 5599dc25..37fbdd19 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -67,6 +67,8 @@ RestClient::Connection::GetInfo() { ret.certType = this->certType; ret.keyPath = this->keyPath; + ret.uriProxy = this->uriProxy; + return ret; } @@ -209,6 +211,20 @@ RestClient::Connection::SetKeyPath(const std::string& keyPath) { this->keyPath = keyPath; } +void +RestClient::Connection::SetProxy(const std::string& uriProxy) { + if (uriProxy.empty()) + return; + + std::string uriProxyUpper = uriProxy; + 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 helper function to get called from the actual request methods to * prepare the curlHandle for transfer with generic options, perform the @@ -305,6 +321,14 @@ RestClient::Connection::performCurlRequest(const std::string& uri) { this->keyPath.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); + } + res = curl_easy_perform(this->curlHandle); if (res != CURLE_OK) { switch (res) { diff --git a/test/test_connection.cc b/test/test_connection.cc index a785fd6d..495e3848 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -214,3 +214,10 @@ TEST_F(ConnectionTest, TestNoSignal) RestClient::Response res = conn->get("/get"); EXPECT_EQ(200, res.code); } + +TEST_F(ConnectionTest, TestProxy) +{ + conn->SetProxy("37.187.79.19:3128"); + RestClient::Response res = conn->get("/get"); + EXPECT_EQ(200, res.code); +} From 8b117a541f94f83eda9cc18051c35bb402578a5a Mon Sep 17 00:00:00 2001 From: amzoughi Date: Fri, 16 Dec 2016 01:10:26 +0100 Subject: [PATCH 02/12] Added TestInvalidProxy test. --- test/test_connection.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/test_connection.cc b/test/test_connection.cc index 495e3848..3efeb690 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -221,3 +221,11 @@ TEST_F(ConnectionTest, TestProxy) 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); +} From 553c2384b2ddc1e6cd64b54e25f00c3f77384f24 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Fri, 16 Dec 2016 01:32:10 +0100 Subject: [PATCH 03/12] Reduced a line length to comply with CI rule. --- source/connection.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/connection.cc b/source/connection.cc index 37fbdd19..32c6a2b5 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -217,7 +217,8 @@ RestClient::Connection::SetProxy(const std::string& uriProxy) { return; std::string uriProxyUpper = uriProxy; - std::transform(uriProxyUpper.begin(), uriProxyUpper.end(), uriProxyUpper.begin(), ::toupper); + std::transform(uriProxyUpper.begin(), uriProxyUpper.end(), + uriProxyUpper.begin(), ::toupper); if (uriProxyUpper.compare(0, 4, "HTTP") != 0) this->uriProxy = "http://" + uriProxy; From 66f59c5625574e42d4c521779c48828d56b9658c Mon Sep 17 00:00:00 2001 From: amzoughi Date: Sat, 17 Dec 2016 17:21:57 +0100 Subject: [PATCH 04/12] Updated README, updated test proxy address, added a test to increase coverage and fixed code style. --- README.md | 17 +++++++++++++++++ source/connection.cc | 9 ++++++--- test/test_connection.cc | 9 ++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 802c9d34..fbdac4af 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,23 @@ conn->SetCertType(type); conn->SetKeyPath(keyPath); ``` +## 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/source/connection.cc b/source/connection.cc index 32c6a2b5..398321aa 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -213,17 +213,20 @@ RestClient::Connection::SetKeyPath(const std::string& keyPath) { void RestClient::Connection::SetProxy(const std::string& uriProxy) { - if (uriProxy.empty()) + 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) + if (uriProxyUpper.compare(0, 4, "HTTP") != 0) { this->uriProxy = "http://" + uriProxy; - else + } else { this->uriProxy = uriProxy; + } } /** diff --git a/test/test_connection.cc b/test/test_connection.cc index 3efeb690..f30fcdb6 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -217,7 +217,14 @@ TEST_F(ConnectionTest, TestNoSignal) TEST_F(ConnectionTest, TestProxy) { - conn->SetProxy("37.187.79.19:3128"); + 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); } From 75d61ce57156f7c16255efba37793ccc16d5d949 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Sat, 17 Dec 2016 18:06:41 +0100 Subject: [PATCH 05/12] Added a method to set SSL Key password and added some missing Doxygen headers. --- README.md | 2 ++ include/restclient-cpp/connection.h | 15 +++++++++++ source/connection.cc | 41 +++++++++++++++++++++++++++++ test/test_connection.cc | 1 + 4 files changed, 59 insertions(+) diff --git a/README.md b/README.md index fbdac4af..44b9a0b9 100644 --- a/README.md +++ b/README.md @@ -184,6 +184,8 @@ conn->SetCertPath(certPath); conn->SetCertType(type); // set CURLOPT_SSLKEY conn->SetKeyPath(keyPath); +// set CURLOPT_KEYPASSWD +conn->SetKeyPassword(keyPassword); ``` ## HTTP Proxy Tunneling Support diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index 4e28827d..087b6b4f 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -85,8 +85,18 @@ class Connection { * Member 'username' contains the basic auth username * @var basicAuth::password * Member 'password' contains the basic auth password + * @var Info::certPath + * Member 'certPath' contains the certificate file path + * @var Info::certType + * Member 'certType' contains the certificate type + * @var Info::keyPath + * Member 'keyPath' contains the SSL key file path + * @var Info::keyPassword + * Member 'keyPassword' contains the SSL key password * @var Info::customUserAgent * Member 'customUserAgent' contains the custom user agent + * @var Info::uriProxy + * Member 'uriProxy' contains the HTTP proxy address * @var Info::lastRequest * Member 'lastRequest' contains metrics about the last request */ @@ -104,6 +114,7 @@ class Connection { std::string certPath; std::string certType; std::string keyPath; + std::string keyPassword; std::string customUserAgent; std::string uriProxy; RequestInfo lastRequest; @@ -144,6 +155,9 @@ class Connection { // set CURLOPT_SSLKEY. Default format is PEM void SetKeyPath(const std::string& keyPath); + // set CURLOPT_KEYPASSWD. + void SetKeyPassword(const std::string& keyPassword); + // set CURLOPT_PROXY void SetProxy(const std::string& uriProxy); @@ -188,6 +202,7 @@ class Connection { std::string certPath; std::string certType; std::string keyPath; + std::string keyPassword; std::string uriProxy; RestClient::Response performCurlRequest(const std::string& uri); }; diff --git a/source/connection.cc b/source/connection.cc index 398321aa..e005ed7a 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -66,6 +66,7 @@ RestClient::Connection::GetInfo() { ret.certPath = this->certPath; ret.certType = this->certType; ret.keyPath = this->keyPath; + ret.keyPassword = this->keyPassword; ret.uriProxy = this->uriProxy; @@ -196,21 +197,56 @@ RestClient::Connection::SetBasicAuth(const std::string& username, this->basicAuth.password = password; } +/** + * @brief set certificate path + * + * @param path to certificate file + * + */ void RestClient::Connection::SetCertPath(const std::string& cert) { this->certPath = cert; } +/** + * @brief set certificate type + * + * @param certificate type (e.g. "PEM" or "DER") + * + */ void RestClient::Connection::SetCertType(const std::string& certType) { this->certType = certType; } +/** + * @brief set key path + * + * @param path to key file + * + */ void RestClient::Connection::SetKeyPath(const std::string& keyPath) { this->keyPath = keyPath; } +/** + * @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()) { @@ -324,6 +360,11 @@ RestClient::Connection::performCurlRequest(const std::string& uri) { curl_easy_setopt(this->curlHandle, CURLOPT_SSLKEY, 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()) { diff --git a/test/test_connection.cc b/test/test_connection.cc index f30fcdb6..b25ba0ed 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -96,6 +96,7 @@ TEST_F(ConnectionTest, TestSSLCert) { conn->SetCertPath("non-existent file"); conn->SetKeyPath("non-existent key path"); + conn->SetKeyPassword("imaginary_password"); conn->SetCertType("invalid cert type"); RestClient::Response res = conn->get("/get"); From 9c2913b8cacbb38e408d7fcfdada5a5e270e6960 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Sat, 17 Dec 2016 18:18:41 +0100 Subject: [PATCH 06/12] Updated an info in README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 44b9a0b9..f84ede91 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,9 @@ typedef struct { std::string certPath; std::string certType; std::string keyPath; + std::string keyPassword; std::string customUserAgent; + std::string uriProxy; struct { // total time of the last request in seconds Total time of previous // transfer. See CURLINFO_TOTAL_TIME From 5a6b127629060bdd229aa5821d50c4b27f083d6d Mon Sep 17 00:00:00 2001 From: amzoughi Date: Sat, 24 Dec 2016 21:32:22 +0100 Subject: [PATCH 07/12] Added a method to perform a post form upload. --- README.md | 19 +++++++++++ include/restclient-cpp/connection.h | 2 ++ include/restclient-cpp/helpers.h | 18 +++++++++++ include/restclient-cpp/restclient.h | 3 ++ source/connection.cc | 20 ++++++++++++ source/helpers.cc | 49 +++++++++++++++++++++++++++++ source/restclient.cc | 17 ++++++++++ test/test_restclient.cc | 34 ++++++++++++++++++++ 8 files changed, 162 insertions(+) diff --git a/README.md b/README.md index f84ede91..86476e57 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,25 @@ RestClient::Response r = RestClient::post("http://url.com/post", "text/json", "{ RestClient::Response r = RestClient::put("http://url.com/put", "text/json", "{\"foo\": \"bla\"}") RestClient::Response r = RestClient::del("http://url.com/delete") RestClient::Response r = RestClient::head("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 diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index 087b6b4f..ca959f61 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -180,6 +180,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 del(const std::string& uri); diff --git a/include/restclient-cpp/helpers.h b/include/restclient-cpp/helpers.h index 8509dfdc..6b2ec4ef 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 @@ -38,6 +40,22 @@ namespace Helpers { size_t length; } UploadObject; + /** @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 (even if this last is rarely needed) */ + void addFormContent(const std::string& fieldName, const std::string& fieldValue); + + struct curl_httppost* formPtr; + struct curl_httppost* lastFormPtr; + }; + // writedata callback function size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata); diff --git a/include/restclient-cpp/restclient.h b/include/restclient-cpp/restclient.h index 75dc7c6e..7eb9325f 100644 --- a/include/restclient-cpp/restclient.h +++ b/include/restclient-cpp/restclient.h @@ -14,6 +14,7 @@ #include #include +#include "restclient-cpp/helpers.h" #include "restclient-cpp/version.h" /** @@ -54,6 +55,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 e005ed7a..5888115a 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -448,6 +448,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..659c4cc4 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 962d4dec..db7b83ba 100644 --- a/source/restclient.cc +++ b/source/restclient.cc @@ -73,6 +73,23 @@ RestClient::Response RestClient::post(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 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_restclient.cc b/test/test_restclient.cc index b1d89d24..501466b4 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 class RestClientTest : public ::testing::Test { @@ -107,6 +111,36 @@ TEST_F(RestClientTest, TestRestClientPostBody) EXPECT_EQ("http://httpbin.org/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) { From 85b2e74c74da84cfe87b3ab12f237f93dd38854c Mon Sep 17 00:00:00 2001 From: amzoughi Date: Sat, 24 Dec 2016 22:09:22 +0100 Subject: [PATCH 08/12] Fixed code indentation to comply with Cpplint. --- include/restclient-cpp/helpers.h | 34 +++++++++++++++++--------------- source/helpers.cc | 8 ++++---- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/include/restclient-cpp/helpers.h b/include/restclient-cpp/helpers.h index 6b2ec4ef..0abc28a0 100644 --- a/include/restclient-cpp/helpers.h +++ b/include/restclient-cpp/helpers.h @@ -27,6 +27,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 @@ -40,22 +58,6 @@ namespace Helpers { size_t length; } UploadObject; - /** @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 (even if this last is rarely needed) */ - void addFormContent(const std::string& fieldName, const std::string& fieldValue); - - struct curl_httppost* formPtr; - struct curl_httppost* lastFormPtr; - }; - // writedata callback function size_t write_callback(void *ptr, size_t size, size_t nmemb, void *userdata); diff --git a/source/helpers.cc b/source/helpers.cc index 659c4cc4..6ae12468 100644 --- a/source/helpers.cc +++ b/source/helpers.cc @@ -115,8 +115,8 @@ RestClient::Helpers::PostFormInfo::~PostFormInfo() { * @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) { +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(), @@ -130,8 +130,8 @@ void RestClient::Helpers::PostFormInfo::addFormFile(const std::string& fieldName * @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) { +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(), From 934aff71df88b35044af7d09e304f64c1248a968 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Fri, 16 Dec 2016 01:02:44 +0100 Subject: [PATCH 09/12] 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 | 21 +++++++++ include/restclient-cpp/connection.h | 20 +++++++++ source/connection.cc | 69 +++++++++++++++++++++++++++++ test/test_connection.cc | 23 ++++++++++ 4 files changed, 133 insertions(+) diff --git a/README.md b/README.md index 802c9d34..f84ede91 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,9 @@ typedef struct { std::string certPath; std::string certType; std::string keyPath; + std::string keyPassword; std::string customUserAgent; + std::string uriProxy; struct { // total time of the last request in seconds Total time of previous // transfer. See CURLINFO_TOTAL_TIME @@ -184,6 +186,25 @@ conn->SetCertPath(certPath); conn->SetCertType(type); // set CURLOPT_SSLKEY conn->SetKeyPath(keyPath); +// set CURLOPT_KEYPASSWD +conn->SetKeyPassword(keyPassword); +``` + +## 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 diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index 158f1cde..087b6b4f 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -85,8 +85,18 @@ class Connection { * Member 'username' contains the basic auth username * @var basicAuth::password * Member 'password' contains the basic auth password + * @var Info::certPath + * Member 'certPath' contains the certificate file path + * @var Info::certType + * Member 'certType' contains the certificate type + * @var Info::keyPath + * Member 'keyPath' contains the SSL key file path + * @var Info::keyPassword + * Member 'keyPassword' contains the SSL key password * @var Info::customUserAgent * Member 'customUserAgent' contains the custom user agent + * @var Info::uriProxy + * Member 'uriProxy' contains the HTTP proxy address * @var Info::lastRequest * Member 'lastRequest' contains metrics about the last request */ @@ -104,7 +114,9 @@ class Connection { std::string certPath; std::string certType; std::string keyPath; + std::string keyPassword; std::string customUserAgent; + std::string uriProxy; RequestInfo lastRequest; } Info; @@ -143,6 +155,12 @@ class Connection { // set CURLOPT_SSLKEY. Default format is PEM void SetKeyPath(const std::string& keyPath); + // set CURLOPT_KEYPASSWD. + void SetKeyPassword(const std::string& keyPassword); + + // set CURLOPT_PROXY + void SetProxy(const std::string& uriProxy); + std::string GetUserAgent(); RestClient::Connection::Info GetInfo(); @@ -184,6 +202,8 @@ class Connection { std::string certPath; std::string certType; std::string keyPath; + std::string keyPassword; + std::string uriProxy; RestClient::Response performCurlRequest(const std::string& uri); }; }; // namespace RestClient diff --git a/source/connection.cc b/source/connection.cc index 5599dc25..e005ed7a 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -66,6 +66,9 @@ RestClient::Connection::GetInfo() { ret.certPath = this->certPath; ret.certType = this->certType; ret.keyPath = this->keyPath; + ret.keyPassword = this->keyPassword; + + ret.uriProxy = this->uriProxy; return ret; } @@ -194,21 +197,74 @@ RestClient::Connection::SetBasicAuth(const std::string& username, this->basicAuth.password = password; } +/** + * @brief set certificate path + * + * @param path to certificate file + * + */ void RestClient::Connection::SetCertPath(const std::string& cert) { this->certPath = cert; } +/** + * @brief set certificate type + * + * @param certificate type (e.g. "PEM" or "DER") + * + */ void RestClient::Connection::SetCertType(const std::string& certType) { this->certType = certType; } +/** + * @brief set key path + * + * @param path to key file + * + */ void RestClient::Connection::SetKeyPath(const std::string& keyPath) { this->keyPath = keyPath; } +/** + * @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 helper function to get called from the actual request methods to * prepare the curlHandle for transfer with generic options, perform the @@ -304,6 +360,19 @@ RestClient::Connection::performCurlRequest(const std::string& uri) { curl_easy_setopt(this->curlHandle, CURLOPT_SSLKEY, 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); + } res = curl_easy_perform(this->curlHandle); if (res != CURLE_OK) { diff --git a/test/test_connection.cc b/test/test_connection.cc index a785fd6d..b25ba0ed 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -96,6 +96,7 @@ TEST_F(ConnectionTest, TestSSLCert) { conn->SetCertPath("non-existent file"); conn->SetKeyPath("non-existent key path"); + conn->SetKeyPassword("imaginary_password"); conn->SetCertType("invalid cert type"); RestClient::Response res = conn->get("/get"); @@ -214,3 +215,25 @@ TEST_F(ConnectionTest, TestNoSignal) RestClient::Response res = conn->get("/get"); EXPECT_EQ(200, res.code); } + +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); +} From 5b96eddc88752b2054672a0d080573a9b8454ba5 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Sat, 24 Dec 2016 21:32:22 +0100 Subject: [PATCH 10/12] 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. closes #68 default to 10s timeout in unit tests this makes the conn object in unit tests default to a 10s timeout in order to fail fast if something is up with the connection or configured hosts. --- README.md | 19 +++++++++++ include/restclient-cpp/connection.h | 2 ++ include/restclient-cpp/helpers.h | 20 ++++++++++++ include/restclient-cpp/restclient.h | 3 ++ source/connection.cc | 20 ++++++++++++ source/helpers.cc | 49 +++++++++++++++++++++++++++++ source/restclient.cc | 17 ++++++++++ test/test_connection.cc | 1 + test/test_restclient.cc | 34 ++++++++++++++++++++ 9 files changed, 165 insertions(+) diff --git a/README.md b/README.md index f84ede91..86476e57 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,25 @@ RestClient::Response r = RestClient::post("http://url.com/post", "text/json", "{ RestClient::Response r = RestClient::put("http://url.com/put", "text/json", "{\"foo\": \"bla\"}") RestClient::Response r = RestClient::del("http://url.com/delete") RestClient::Response r = RestClient::head("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 diff --git a/include/restclient-cpp/connection.h b/include/restclient-cpp/connection.h index 087b6b4f..ca959f61 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -180,6 +180,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 del(const std::string& uri); diff --git a/include/restclient-cpp/helpers.h b/include/restclient-cpp/helpers.h index 8509dfdc..0abc28a0 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 @@ -25,6 +27,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 75dc7c6e..7eb9325f 100644 --- a/include/restclient-cpp/restclient.h +++ b/include/restclient-cpp/restclient.h @@ -14,6 +14,7 @@ #include #include +#include "restclient-cpp/helpers.h" #include "restclient-cpp/version.h" /** @@ -54,6 +55,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 e005ed7a..5888115a 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -448,6 +448,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 962d4dec..db7b83ba 100644 --- a/source/restclient.cc +++ b/source/restclient.cc @@ -73,6 +73,23 @@ RestClient::Response RestClient::post(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 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 b25ba0ed..ba8805c0 100644 --- a/test/test_connection.cc +++ b/test/test_connection.cc @@ -22,6 +22,7 @@ class ConnectionTest : public ::testing::Test virtual void SetUp() { conn = new RestClient::Connection("https://httpbin.org"); + conn->SetTimeout(10); } virtual void TearDown() diff --git a/test/test_restclient.cc b/test/test_restclient.cc index b1d89d24..501466b4 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 class RestClientTest : public ::testing::Test { @@ -107,6 +111,36 @@ TEST_F(RestClientTest, TestRestClientPostBody) EXPECT_EQ("http://httpbin.org/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) { From 037efba6b00af45c2f9ac12e7dff101d02f74a09 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Sat, 10 Jun 2017 00:37:40 +0200 Subject: [PATCH 11/12] Moved PostFormInfo from Helpers to RestClient namespace. --- README.md | 4 +-- include/restclient-cpp/connection.h | 2 +- include/restclient-cpp/helpers.h | 18 ----------- include/restclient-cpp/restclient.h | 24 +++++++++++++- source/connection.cc | 4 +-- source/helpers.cc | 48 --------------------------- source/restclient.cc | 50 ++++++++++++++++++++++++++++- test/test_restclient.cc | 3 +- 8 files changed, 78 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index 86476e57..67302878 100644 --- a/README.md +++ b/README.md @@ -30,8 +30,8 @@ RestClient::Response r = RestClient::del("http://url.com/delete") RestClient::Response r = RestClient::head("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 ca959f61..e90d8998 100644 --- a/include/restclient-cpp/connection.h +++ b/include/restclient-cpp/connection.h @@ -181,7 +181,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 del(const std::string& uri); diff --git a/include/restclient-cpp/helpers.h b/include/restclient-cpp/helpers.h index 0abc28a0..99674a08 100644 --- a/include/restclient-cpp/helpers.h +++ b/include/restclient-cpp/helpers.h @@ -27,24 +27,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 7eb9325f..02d37f3b 100644 --- a/include/restclient-cpp/restclient.h +++ b/include/restclient-cpp/restclient.h @@ -42,6 +42,28 @@ typedef struct { HeaderFields headers; } Response; +/** @class PostFormInfo + * @brief This class represents the form information to send on + * POST Form requests + */ +class PostFormInfo { +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; } + +private: + struct curl_httppost* formPtr; + struct curl_httppost* lastFormPtr; +}; + // init and disable functions int init(); void disable(); @@ -56,7 +78,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 5888115a..1f17ebb9 100644 --- a/source/connection.cc +++ b/source/connection.cc @@ -458,13 +458,13 @@ RestClient::Connection::post(const std::string& url, */ RestClient::Response RestClient::Connection::postForm(const std::string& url, - const Helpers::PostFormInfo& data) { + 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.formPtr); + curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPOST, data.GetFormPtr()); return this->performCurlRequest(url); } 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 db7b83ba..1a62315d 100644 --- a/source/restclient.cc +++ b/source/restclient.cc @@ -82,7 +82,7 @@ RestClient::Response RestClient::post(const std::string& url, * @return response struct */ RestClient::Response RestClient::postForm(const std::string& url, - const RestClient::Helpers::PostFormInfo& data) { + const PostFormInfo& data) { RestClient::Response ret; RestClient::Connection *conn = new RestClient::Connection(""); ret = conn->postForm(url, data); @@ -139,3 +139,51 @@ RestClient::Response RestClient::head(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 501466b4..434fd5a0 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 @@ -132,7 +131,7 @@ TEST_F(RestClientTest, TestRestClientPostForm) ofDummyFile.close(); // uploading the dummy file - RestClient::Helpers::PostFormInfo UploadInfo; + 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); From 47171add1ca1963db49d72268ee3767ed1bb7777 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Mon, 12 Jun 2017 21:04:51 +0200 Subject: [PATCH 12/12] Fixed PostFormInfo indentation. --- include/restclient-cpp/restclient.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/include/restclient-cpp/restclient.h b/include/restclient-cpp/restclient.h index 02d37f3b..f345b02e 100644 --- a/include/restclient-cpp/restclient.h +++ b/include/restclient-cpp/restclient.h @@ -47,7 +47,9 @@ typedef struct { * POST Form requests */ class PostFormInfo { -public: + struct curl_httppost* formPtr; + struct curl_httppost* lastFormPtr; + public: PostFormInfo(); ~PostFormInfo(); /* Fill in the file upload field */ @@ -58,10 +60,6 @@ class PostFormInfo { const std::string& fieldValue); /* Get Form pointer */ struct curl_httppost* GetFormPtr() const { return formPtr; } - -private: - struct curl_httppost* formPtr; - struct curl_httppost* lastFormPtr; }; // init and disable functions