diff --git a/README.md b/README.md
index 3ec3cfc9..93271acc 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,25 @@ 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 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.
+
+*/
+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::post("http://posttestserver.com/post.php?dir=restclientcpptests", uploadInfo);
```
The response is of type [RestClient::Response][restclient_response] and has
@@ -295,6 +314,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..30089915 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 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/helpers.h b/include/restclient-cpp/helpers.h
index 79d161bd..c26a077d 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
diff --git a/include/restclient-cpp/restclient.h b/include/restclient-cpp/restclient.h
index 601e7f2a..8622e202 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"
/**
@@ -40,6 +41,26 @@ typedef struct {
HeaderFields headers;
} Response;
+/** @class FormData
+ * @brief This class represents the form information to send on
+ * POST Form requests
+ */
+class FormData {
+ struct curl_httppost* formPtr;
+ struct curl_httppost* lastFormPtr;
+ public:
+ FormData();
+ ~FormData();
+ /* 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();
@@ -53,6 +74,8 @@ Response get(const std::string& url);
Response post(const std::string& url,
const std::string& content_type,
const std::string& 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 54118633..39c24ab1 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,16 @@ 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 ((uriProxy.length() > 0) && (uriProxyUpper.compare(0, 4, "HTTP") != 0)) {
+ if (uriProxyUpper.compare(0, 4, "HTTP") != 0) {
this->uriProxy = "http://" + uriProxy;
} else {
this->uriProxy = uriProxy;
@@ -483,6 +489,32 @@ RestClient::Connection::performCurlRequest(const std::string& uri,
this->keyPassword.c_str());
}
+ // set web proxy address
+ if (!this->uriProxy.empty()) {
+ curl_easy_setopt(getCurlHandle(), CURLOPT_PROXY,
+ uriProxy.c_str());
+ curl_easy_setopt(getCurlHandle(), CURLOPT_HTTPPROXYTUNNEL,
+ 1L);
+ }
+ // set key password
+ if (!this->keyPassword.empty()) {
+ curl_easy_setopt(getCurlHandle(), CURLOPT_KEYPASSWD,
+ this->keyPassword.c_str());
+ }
+
+ // set web proxy address
+ if (!this->uriProxy.empty()) {
+ curl_easy_setopt(getCurlHandle(), CURLOPT_PROXY,
+ uriProxy.c_str());
+ curl_easy_setopt(getCurlHandle(), CURLOPT_HTTPPROXYTUNNEL,
+ 1L);
+ }
+ // set key password
+ if (!this->keyPassword.empty()) {
+ curl_easy_setopt(getCurlHandle(), CURLOPT_KEYPASSWD,
+ this->keyPassword.c_str());
+ }
+
// set web proxy address
if (!this->uriProxy.empty()) {
curl_easy_setopt(getCurlHandle(), CURLOPT_PROXY,
@@ -580,6 +612,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::post(const std::string& url,
+ const FormData& data) {
+ /** Now specify we want to POST data */
+ curl_easy_setopt(getCurlHandle(), CURLOPT_POST, 1L);
+ /* stating that Expect: 100-continue is not wanted */
+ AppendHeader("Expect", "");
+ /** set post form */
+ curl_easy_setopt(getCurlHandle(), CURLOPT_HTTPPOST, data.getFormPtr());
+
+ return this->performCurlRequest(url);
+}
/**
* @brief HTTP PUT method
*
@@ -620,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/helpers.cc b/source/helpers.cc
index aa51bf58..eb62cac5 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,
diff --git a/source/restclient.cc b/source/restclient.cc
index ed0fe861..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,27 @@ 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;
+}
+
+/**
+ * @brief HTTP POST Form method
+ *
+ * @param url to query
+ * @param data post form information
+ *
+ * @return response struct
+ */
+RestClient::Response RestClient::post(const std::string& url,
+ const FormData& data) {
+ RestClient::Response ret;
+ RestClient::Connection conn("");
+ ret = conn.post(url, data);
return ret;
-#endif
}
/**
@@ -99,18 +102,11 @@ RestClient::Response RestClient::post(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
}
/**
@@ -123,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;
}
@@ -141,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
}
/**
@@ -161,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
}
/**
@@ -182,8 +165,55 @@ 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 FormData constructor
+ */
+RestClient::FormData::FormData()
+ : formPtr(NULL), lastFormPtr(NULL) {
+}
+
+/**
+ * @brief FormData destructor
+ */
+RestClient::FormData::~FormData() {
+ // 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::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(),
+ 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::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(),
+ CURLFORM_END);
+}
diff --git a/test/test_connection.cc b/test/test_connection.cc
index d41bed7b..e54e5955 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);
}
@@ -379,3 +379,4 @@ TEST_F(ConnectionTest, TestSetWriteFunction)
EXPECT_EQ(200, res.code);
EXPECT_EQ(ret, lineReceived.size() + lines);
}
+
diff --git a/test/test_restclient.cc b/test/test_restclient.cc
index cf64b6b2..f4fbfad7 100644
--- a/test/test_restclient.cc
+++ b/test/test_restclient.cc
@@ -1,7 +1,10 @@
#include "restclient-cpp/restclient.h"
#include
#include
+#include
+#include
#include
+#include
#include "tests.h"
@@ -120,6 +123,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::FormData UploadInfo;
+ UploadInfo.addFormFile("submitted", fileName.str());
+ UploadInfo.addFormContent("filename", fileName.str());
+ RestClient::Response res = RestClient::post("http://ptsv2.com/t/restclientcpptests/post", 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;
};