Skip to content

Commit eb108ad

Browse files
committed
Fall back on WinINET proxy settings if WinHTTP proxy is not defined
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).
1 parent c24bc6a commit eb108ad

File tree

1 file changed

+85
-6
lines changed

1 file changed

+85
-6
lines changed

Release/src/http/client/http_client_winhttp.cpp

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ static DWORD ChooseAuthScheme( DWORD dwSupportedSchemes )
297297
return 0;
298298
}
299299

300-
// Small RAII helper to ensure that the fields of this struct are always
300+
// Small RAII helpers to ensure that the fields of these structs are always
301301
// properly freed.
302302
struct proxy_info : WINHTTP_PROXY_INFO
303303
{
@@ -315,6 +315,24 @@ struct proxy_info : WINHTTP_PROXY_INFO
315315
}
316316
};
317317

318+
struct ie_proxy_config : WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
319+
{
320+
ie_proxy_config()
321+
{
322+
memset( this, 0, sizeof(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG) );
323+
}
324+
325+
~ie_proxy_config()
326+
{
327+
if ( lpszAutoConfigUrl )
328+
::GlobalFree(lpszAutoConfigUrl);
329+
if ( lpszProxy )
330+
::GlobalFree(lpszProxy);
331+
if ( lpszProxyBypass )
332+
::GlobalFree(lpszProxyBypass);
333+
}
334+
};
335+
318336
// WinHTTP client.
319337
class winhttp_client : public _http_client_communicator
320338
{
@@ -376,8 +394,13 @@ class winhttp_client : public _http_client_communicator
376394
// Open session and connection with the server.
377395
virtual unsigned long open() override
378396
{
397+
// This object have lifetime greater than proxy_name and proxy_bypass
398+
// which may point to its elements.
399+
ie_proxy_config proxyIE;
400+
379401
DWORD access_type;
380402
LPCWSTR proxy_name;
403+
LPCWSTR proxy_bypass = WINHTTP_NO_PROXY_BYPASS;
381404
utility::string_t proxy_str;
382405
http::uri uri;
383406

@@ -388,10 +411,51 @@ class winhttp_client : public _http_client_communicator
388411
access_type = WINHTTP_ACCESS_TYPE_NO_PROXY;
389412
proxy_name = WINHTTP_NO_PROXY_NAME;
390413
}
391-
else if(config.proxy().is_default() || config.proxy().is_auto_discovery())
414+
else if(config.proxy().is_default())
415+
{
416+
// Use the default WinHTTP proxy by default.
417+
access_type = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
418+
proxy_name = WINHTTP_NO_PROXY_NAME;
419+
420+
// However, if it is not configured...
421+
proxy_info proxyDefault;
422+
if(!WinHttpGetDefaultProxyConfiguration(&proxyDefault) ||
423+
proxyDefault.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY)
424+
{
425+
// ... then try to fall back on the default WinINET proxy, as
426+
// recommended for the desktop applications (if we're not
427+
// running under a user account, the function below will just
428+
// fail, so there is no real need to check for this explicitly)
429+
if(WinHttpGetIEProxyConfigForCurrentUser(&proxyIE))
430+
{
431+
if(proxyIE.fAutoDetect)
432+
{
433+
m_proxy_auto_config = true;
434+
}
435+
else if(proxyIE.lpszAutoConfigUrl)
436+
{
437+
m_proxy_auto_config = true;
438+
m_proxy_auto_config_url = proxyIE.lpszAutoConfigUrl;
439+
}
440+
else if(proxyIE.lpszProxy)
441+
{
442+
access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
443+
proxy_name = proxyIE.lpszProxy;
444+
445+
if(proxyIE.lpszProxyBypass)
446+
{
447+
proxy_bypass = proxyIE.lpszProxyBypass;
448+
}
449+
}
450+
}
451+
}
452+
}
453+
else if(config.proxy().is_auto_discovery())
392454
{
393455
access_type = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
394456
proxy_name = WINHTTP_NO_PROXY_NAME;
457+
458+
m_proxy_auto_config = true;
395459
}
396460
else
397461
{
@@ -426,7 +490,7 @@ class winhttp_client : public _http_client_communicator
426490
NULL,
427491
access_type,
428492
proxy_name,
429-
WINHTTP_NO_PROXY_BYPASS,
493+
proxy_bypass,
430494
WINHTTP_FLAG_ASYNC);
431495
if(!m_hSession)
432496
{
@@ -513,13 +577,22 @@ class winhttp_client : public _http_client_communicator
513577
proxy_info info;
514578
bool proxy_info_required = false;
515579

516-
if( client_config().proxy().is_auto_discovery() )
580+
if(m_proxy_auto_config)
517581
{
518582
WINHTTP_AUTOPROXY_OPTIONS autoproxy_options;
519583
memset( &autoproxy_options, 0, sizeof(WINHTTP_AUTOPROXY_OPTIONS) );
520584

521-
autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
522-
autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
585+
if(m_proxy_auto_config_url.empty())
586+
{
587+
autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
588+
autoproxy_options.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
589+
}
590+
else
591+
{
592+
autoproxy_options.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
593+
autoproxy_options.lpszAutoConfigUrl = m_proxy_auto_config_url.c_str();
594+
}
595+
523596
autoproxy_options.fAutoLogonIfChallenged = TRUE;
524597

525598
auto result = WinHttpGetProxyForUrl(
@@ -1370,6 +1443,12 @@ class winhttp_client : public _http_client_communicator
13701443
HINTERNET m_hSession;
13711444
HINTERNET m_hConnection;
13721445
bool m_secure;
1446+
1447+
// If auto config is true, dynamically find the proxy for each URL using
1448+
// the proxy configuration script at the given URL if it's not empty or
1449+
// using WPAD otherwise.
1450+
bool m_proxy_auto_config{false};
1451+
utility::string_t m_proxy_auto_config_url;
13731452
};
13741453

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

0 commit comments

Comments
 (0)