Skip to content

HTTP Proxy Support #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions include/restclient-cpp/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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;

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
69 changes: 69 additions & 0 deletions source/connection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line seems to not be covered by a unit test (see here). Do you know of an https proxy you could use to test this behaviour?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add a test similar to "TestProxy" and provide a proxy address that begins with "http" or "https". I think that the proxy I already used in that test is a one using SSL but I might be wrong.

Copy link
Contributor Author

@embeddedmz embeddedmz Dec 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The most important thing is to mention the port of the proxy at the end of the address, the prefix of the address whether it's http:// or https:// doesn't matter. An HTTPS proxy is considered a HTTP proxy. More info here : https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html , here https://curl.haxx.se/libcurl/c/CURLOPT_HTTPPROXYTUNNEL.html and here https://curl.haxx.se/libcurl/c/CURLOPT_PROXYTYPE.html

}
}

/**
* @brief helper function to get called from the actual request methods to
* prepare the curlHandle for transfer with generic options, perform the
Expand Down Expand Up @@ -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) {
Expand Down
23 changes: 23 additions & 0 deletions test/test_connection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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);
}