From cfb825da3957a77cffbd1c14eb83b52d8735a757 Mon Sep 17 00:00:00 2001 From: amzoughi Date: Fri, 16 Dec 2016 01:02:44 +0100 Subject: [PATCH 1/6] 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 2/6] 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 3/6] 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 4/6] 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 5/6] 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 6/6] 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