Skip to content

Commit 934aff7

Browse files
committed
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
1 parent b0e0c0a commit 934aff7

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ typedef struct {
117117
std::string certPath;
118118
std::string certType;
119119
std::string keyPath;
120+
std::string keyPassword;
120121
std::string customUserAgent;
122+
std::string uriProxy;
121123
struct {
122124
// total time of the last request in seconds Total time of previous
123125
// transfer. See CURLINFO_TOTAL_TIME
@@ -184,6 +186,25 @@ conn->SetCertPath(certPath);
184186
conn->SetCertType(type);
185187
// set CURLOPT_SSLKEY
186188
conn->SetKeyPath(keyPath);
189+
// set CURLOPT_KEYPASSWD
190+
conn->SetKeyPassword(keyPassword);
191+
```
192+
193+
## HTTP Proxy Tunneling Support
194+
195+
An HTTP Proxy can be set to use for the upcoming request.
196+
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.
197+
The operation will be tunneled through the proxy as curl option `CURLOPT_HTTPPROXYTUNNEL` is enabled by default.
198+
A numerical IPv6 address must be written within [brackets].
199+
200+
```cpp
201+
// set CURLOPT_PROXY
202+
conn->SetProxy("https://37.187.100.23:3128");
203+
/* or you can set it without the protocol scheme and
204+
http:// will be prefixed by default */
205+
conn->SetProxy("37.187.100.23:3128");
206+
/* the following request will be tunneled through the proxy */
207+
RestClient::Response res = conn->get("/get");
187208
```
188209

189210
## Dependencies

include/restclient-cpp/connection.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,18 @@ class Connection {
8585
* Member 'username' contains the basic auth username
8686
* @var basicAuth::password
8787
* Member 'password' contains the basic auth password
88+
* @var Info::certPath
89+
* Member 'certPath' contains the certificate file path
90+
* @var Info::certType
91+
* Member 'certType' contains the certificate type
92+
* @var Info::keyPath
93+
* Member 'keyPath' contains the SSL key file path
94+
* @var Info::keyPassword
95+
* Member 'keyPassword' contains the SSL key password
8896
* @var Info::customUserAgent
8997
* Member 'customUserAgent' contains the custom user agent
98+
* @var Info::uriProxy
99+
* Member 'uriProxy' contains the HTTP proxy address
90100
* @var Info::lastRequest
91101
* Member 'lastRequest' contains metrics about the last request
92102
*/
@@ -104,7 +114,9 @@ class Connection {
104114
std::string certPath;
105115
std::string certType;
106116
std::string keyPath;
117+
std::string keyPassword;
107118
std::string customUserAgent;
119+
std::string uriProxy;
108120
RequestInfo lastRequest;
109121
} Info;
110122

@@ -143,6 +155,12 @@ class Connection {
143155
// set CURLOPT_SSLKEY. Default format is PEM
144156
void SetKeyPath(const std::string& keyPath);
145157

158+
// set CURLOPT_KEYPASSWD.
159+
void SetKeyPassword(const std::string& keyPassword);
160+
161+
// set CURLOPT_PROXY
162+
void SetProxy(const std::string& uriProxy);
163+
146164
std::string GetUserAgent();
147165

148166
RestClient::Connection::Info GetInfo();
@@ -184,6 +202,8 @@ class Connection {
184202
std::string certPath;
185203
std::string certType;
186204
std::string keyPath;
205+
std::string keyPassword;
206+
std::string uriProxy;
187207
RestClient::Response performCurlRequest(const std::string& uri);
188208
};
189209
}; // namespace RestClient

source/connection.cc

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ RestClient::Connection::GetInfo() {
6666
ret.certPath = this->certPath;
6767
ret.certType = this->certType;
6868
ret.keyPath = this->keyPath;
69+
ret.keyPassword = this->keyPassword;
70+
71+
ret.uriProxy = this->uriProxy;
6972

7073
return ret;
7174
}
@@ -194,21 +197,74 @@ RestClient::Connection::SetBasicAuth(const std::string& username,
194197
this->basicAuth.password = password;
195198
}
196199

200+
/**
201+
* @brief set certificate path
202+
*
203+
* @param path to certificate file
204+
*
205+
*/
197206
void
198207
RestClient::Connection::SetCertPath(const std::string& cert) {
199208
this->certPath = cert;
200209
}
201210

211+
/**
212+
* @brief set certificate type
213+
*
214+
* @param certificate type (e.g. "PEM" or "DER")
215+
*
216+
*/
202217
void
203218
RestClient::Connection::SetCertType(const std::string& certType) {
204219
this->certType = certType;
205220
}
206221

222+
/**
223+
* @brief set key path
224+
*
225+
* @param path to key file
226+
*
227+
*/
207228
void
208229
RestClient::Connection::SetKeyPath(const std::string& keyPath) {
209230
this->keyPath = keyPath;
210231
}
211232

233+
/**
234+
* @brief set key password
235+
*
236+
* @param key password
237+
*
238+
*/
239+
void
240+
RestClient::Connection::SetKeyPassword(const std::string& keyPassword) {
241+
this->keyPassword = keyPassword;
242+
}
243+
244+
/**
245+
* @brief set HTTP proxy address and port
246+
*
247+
* @param proxy address with port number
248+
*
249+
*/
250+
void
251+
RestClient::Connection::SetProxy(const std::string& uriProxy) {
252+
if (uriProxy.empty()) {
253+
return;
254+
}
255+
256+
std::string uriProxyUpper = uriProxy;
257+
// check if the provided address is prefixed with "http"
258+
std::transform(uriProxyUpper.begin(), uriProxyUpper.end(),
259+
uriProxyUpper.begin(), ::toupper);
260+
261+
if (uriProxyUpper.compare(0, 4, "HTTP") != 0) {
262+
this->uriProxy = "http://" + uriProxy;
263+
} else {
264+
this->uriProxy = uriProxy;
265+
}
266+
}
267+
212268
/**
213269
* @brief helper function to get called from the actual request methods to
214270
* prepare the curlHandle for transfer with generic options, perform the
@@ -304,6 +360,19 @@ RestClient::Connection::performCurlRequest(const std::string& uri) {
304360
curl_easy_setopt(this->curlHandle, CURLOPT_SSLKEY,
305361
this->keyPath.c_str());
306362
}
363+
// set key password
364+
if (!this->keyPassword.empty()) {
365+
curl_easy_setopt(this->curlHandle, CURLOPT_KEYPASSWD,
366+
this->keyPassword.c_str());
367+
}
368+
369+
// set web proxy address
370+
if (!this->uriProxy.empty()) {
371+
curl_easy_setopt(this->curlHandle, CURLOPT_PROXY,
372+
uriProxy.c_str());
373+
curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPROXYTUNNEL,
374+
1L);
375+
}
307376

308377
res = curl_easy_perform(this->curlHandle);
309378
if (res != CURLE_OK) {

test/test_connection.cc

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ TEST_F(ConnectionTest, TestSSLCert)
9696
{
9797
conn->SetCertPath("non-existent file");
9898
conn->SetKeyPath("non-existent key path");
99+
conn->SetKeyPassword("imaginary_password");
99100
conn->SetCertType("invalid cert type");
100101
RestClient::Response res = conn->get("/get");
101102

@@ -214,3 +215,25 @@ TEST_F(ConnectionTest, TestNoSignal)
214215
RestClient::Response res = conn->get("/get");
215216
EXPECT_EQ(200, res.code);
216217
}
218+
219+
TEST_F(ConnectionTest, TestProxy)
220+
{
221+
conn->SetProxy("37.187.100.23:3128");
222+
RestClient::Response res = conn->get("/get");
223+
EXPECT_EQ(200, res.code);
224+
}
225+
226+
TEST_F(ConnectionTest, TestProxyAddressPrefixed)
227+
{
228+
conn->SetProxy("https://37.187.100.23:3128");
229+
RestClient::Response res = conn->get("/get");
230+
EXPECT_EQ(200, res.code);
231+
}
232+
233+
TEST_F(ConnectionTest, TestInvalidProxy)
234+
{
235+
conn->SetProxy("127.0.0.1:666");
236+
RestClient::Response res = conn->get("/get");
237+
EXPECT_EQ("Failed to query.", res.body);
238+
EXPECT_EQ(-1, res.code);
239+
}

0 commit comments

Comments
 (0)