diff --git a/cpr/session.cpp b/cpr/session.cpp index fc9df926c..7d2f76b83 100644 --- a/cpr/session.cpp +++ b/cpr/session.cpp @@ -1,5 +1,6 @@ #include "cpr/session.h" +#include #include #include #include @@ -180,6 +181,15 @@ void Session::prepareCommonShared() { curl_easy_setopt(curl_->handle, CURLOPT_PROXYPASSWORD, proxyAuth_.GetPassword(protocol)); } } + // handle NO_PROXY override passed through Proxies object + // Example: Proxies{"no_proxy": ""} will override environment variable definition with an empty list + const std::array no_proxy{"no_proxy", "NO_PROXY"}; + for (const auto& item : no_proxy) { + if (proxies_.has(item)) { + curl_easy_setopt(curl_->handle, CURLOPT_NOPROXY, proxies_[item].c_str()); + break; + } + } #if LIBCURL_VERSION_NUM >= 0x071506 // 7.21.6 if (acceptEncoding_.empty()) { diff --git a/test/proxy_tests.cpp b/test/proxy_tests.cpp index ce5126dcb..a5a821c6f 100644 --- a/test/proxy_tests.cpp +++ b/test/proxy_tests.cpp @@ -1,15 +1,23 @@ #include +#include +#include #include +#include #include "cpr/cpr.h" // TODO: This uses public servers for proxies and endpoints. This should be replaced with a source // code implementation inside server.cpp +// NOTES: +// * For no-proxy testing need to run the tests with direct connection to the internet +// * List of free proxies for testing can be found at https://proxy-list.org/english/index.php +// Example: #define HTTP_PROXY "http://162.223.90.130:80" #define HTTP_PROXY "51.159.4.98:80" #define HTTPS_PROXY "51.104.53.182:8000" + using namespace cpr; TEST(ProxyTests, SingleProxyTest) { @@ -79,6 +87,7 @@ TEST(ProxyTests, ReferenceProxySessionTest) { Session session; session.SetUrl(url); session.SetProxies(proxies); + session.SetTimeout(std::chrono::seconds(10)); Response response = session.Get(); EXPECT_EQ(url, response.url); EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); @@ -86,6 +95,49 @@ TEST(ProxyTests, ReferenceProxySessionTest) { EXPECT_EQ(ErrorCode::OK, response.error.code); } +TEST(ProxyTests, NoProxyTest) { + setenv("NO_PROXY", "httpbin.org", 1); + try { + Url url{"http://www.httpbin.org/get"}; + Proxies proxies{{"http", HTTP_PROXY}, {"no_proxy", ""}}; + Session session; + session.SetUrl(url); + session.SetProxies(proxies); + session.SetTimeout(std::chrono::seconds(10)); + Response response = session.Get(); + EXPECT_EQ(url, response.url); + EXPECT_EQ(std::string{"application/json"}, response.header["content-type"]); + EXPECT_EQ(200, response.status_code); + EXPECT_EQ(ErrorCode::OK, response.error.code); + + // check that access was performed through the proxy + std::string proxy_ip = HTTP_PROXY; + if (proxy_ip[0] == 'h') { + // drop protocol: + proxy_ip = proxy_ip.substr(proxy_ip.find(':') + 3); + } + // drop port: + proxy_ip = proxy_ip.substr(0, proxy_ip.find(':')); + + // find "origin": "ip" in response: + bool found = false; + std::istringstream body(response.text); + std::string line; + while (std::getline(body, line)) { + // example: "origin": "123.456.789.123" + if (line.find("\"origin\":") != std::string::npos) { + found = line.find(proxy_ip) != std::string::npos; + break; + } + } + EXPECT_TRUE(found); + } catch (...) { + unsetenv("NO_PROXY"); + throw; + } + unsetenv("NO_PROXY"); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();