Skip to content

Commit

Permalink
Fall back on WinINET proxy settings if WinHTTP proxy is not defined
Browse files Browse the repository at this point in the history
Implement the recommended behaviour for "well-written WinHTTP apps", see
https://blogs.msdn.microsoft.com/ieinternals/2013/10/11/understanding-web-proxy-configuration/

I.e. use IE proxy settings for the current user unless WinHTTP proxy is
explicitly defined (which seems to be quite rare in practice). This makes
applications using C++ REST SDK work out of the box on the networks where IE
is configured to access the Internet (which will almost always be the case),
whether via a fixed proxy or using proxy-configuration, using either WPAD or
fixed PAC URL (this fixes #182
in passing).
  • Loading branch information
vadz committed Jul 8, 2017
1 parent c24bc6a commit eb108ad
Showing 1 changed file with 85 additions and 6 deletions.
91 changes: 85 additions & 6 deletions Release/src/http/client/http_client_winhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ static DWORD ChooseAuthScheme( DWORD dwSupportedSchemes )
return 0;
}

// Small RAII helper to ensure that the fields of this struct are always
// Small RAII helpers to ensure that the fields of these structs are always
// properly freed.
struct proxy_info : WINHTTP_PROXY_INFO
{
Expand All @@ -315,6 +315,24 @@ struct proxy_info : WINHTTP_PROXY_INFO
}
};

struct ie_proxy_config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
{
ie_proxy_config()
{
memset( this, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG) );
}

~ie_proxy_config()
{
if ( lpszAutoConfigUrl )
::GlobalFree(lpszAutoConfigUrl);
if ( lpszProxy )
::GlobalFree(lpszProxy);
if ( lpszProxyBypass )
::GlobalFree(lpszProxyBypass);
}
};

// WinHTTP client.
class winhttp_client : public _http_client_communicator
{
Expand Down Expand Up @@ -376,8 +394,13 @@ class winhttp_client : public _http_client_communicator
// Open session and connection with the server.
virtual unsigned long open() override
{
// This object have lifetime greater than proxy_name and proxy_bypass
// which may point to its elements.
ie_proxy_config proxyIE;

DWORD access_type;
LPCWSTR proxy_name;
LPCWSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS;
utility::string_t proxy_str;
http::uri uri;

Expand All @@ -388,10 +411,51 @@ class winhttp_client : public _http_client_communicator
access_type = WINHTTP_ACCESS_TYPE_NO_PROXY;
proxy_name = WINHTTP_NO_PROXY_NAME;
}
else if(config.proxy().is_default() || config.proxy().is_auto_discovery())
else if(config.proxy().is_default())
{
// Use the default WinHTTP proxy by default.
access_type = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
proxy_name = WINHTTP_NO_PROXY_NAME;

// However, if it is not configured...
proxy_info proxyDefault;
if(!WinHttpGetDefaultProxyConfiguration(&proxyDefault) ||
proxyDefault.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY)
{
// ... then try to fall back on the default WinINET proxy, as
// recommended for the desktop applications (if we're not
// running under a user account, the function below will just
// fail, so there is no real need to check for this explicitly)
if(WinHttpGetIEProxyConfigForCurrentUser(&proxyIE))
{
if(proxyIE.fAutoDetect)
{
m_proxy_auto_config = true;
}
else if(proxyIE.lpszAutoConfigUrl)
{
m_proxy_auto_config = true;
m_proxy_auto_config_url = proxyIE.lpszAutoConfigUrl;
}
else if(proxyIE.lpszProxy)
{
access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
proxy_name = proxyIE.lpszProxy;

if(proxyIE.lpszProxyBypass)
{
proxy_bypass = proxyIE.lpszProxyBypass;
}
}
}
}
}
else if(config.proxy().is_auto_discovery())
{
access_type = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
proxy_name = WINHTTP_NO_PROXY_NAME;

m_proxy_auto_config = true;
}
else
{
Expand Down Expand Up @@ -426,7 +490,7 @@ class winhttp_client : public _http_client_communicator
NULL,
access_type,
proxy_name,
WINHTTP_NO_PROXY_BYPASS,
proxy_bypass,
WINHTTP_FLAG_ASYNC);
if(!m_hSession)
{
Expand Down Expand Up @@ -513,13 +577,22 @@ class winhttp_client : public _http_client_communicator
proxy_info info;
bool proxy_info_required = false;

if( client_config().proxy().is_auto_discovery() )
if(m_proxy_auto_config)
{
WINHTTP_AUTOPROXY_OPTIONS autoproxy_options;
memset( &autoproxy_options, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS) );

autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
if(m_proxy_auto_config_url.empty())
{
autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
}
else
{
autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
autoproxy_options.lpszAutoConfigUrl = m_proxy_auto_config_url.c_str();
}

autoproxy_options.fAutoLogonIfChallenged = TRUE;

auto result = WinHttpGetProxyForUrl(
Expand Down Expand Up @@ -1370,6 +1443,12 @@ class winhttp_client : public _http_client_communicator
HINTERNET m_hSession;
HINTERNET m_hConnection;
bool m_secure;

// If auto config is true, dynamically find the proxy for each URL using
// the proxy configuration script at the given URL if it's not empty or
// using WPAD otherwise.
bool m_proxy_auto_config{false};
utility::string_t m_proxy_auto_config_url;
};

std::shared_ptr<_http_client_communicator> create_platform_final_pipeline_stage(uri&& base_uri, http_client_config&& client_config)
Expand Down

0 comments on commit eb108ad

Please sign in to comment.