This fixes an issue seen with Steam, which calls `WinHttpGetProxyForUrl()` before pinging WebSocket servers to determine which has the lowest latency. The `WinHttpGetProxyForUrl()`/`WinHttpDetectAutoProxyConfigUrl()` call seems to be included in the timing, and `WinHttpDetectAutoProxyConfigUrl()` taking too long can cause the ping to time out/fail.
From: Brendan Shanks bshanks@codeweavers.com
--- dlls/winhttp/session.c | 52 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-)
diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 42f23b06a6b..e26fdd0b377 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -1666,6 +1666,13 @@ static WCHAR *detect_autoproxyconfig_url_dns(void) */ BOOL WINAPI WinHttpDetectAutoProxyConfigUrl( DWORD flags, WCHAR **url ) { + static SRWLOCK apc_cache_lock = SRWLOCK_INIT; + static BOOL dhcp_cache_valid, dns_cache_valid; + static WCHAR *dhcp_apc_url, *dns_apc_url; + static HANDLE notifyaddr_handle = INVALID_HANDLE_VALUE; + static OVERLAPPED notifyaddr_ol; + DWORD bytes; + TRACE( "%#lx, %p\n", flags, url );
if (!flags || !url) @@ -1674,14 +1681,53 @@ BOOL WINAPI WinHttpDetectAutoProxyConfigUrl( DWORD flags, WCHAR **url ) return FALSE; } *url = NULL; + + AcquireSRWLockExclusive( &apc_cache_lock ); + /* FIXME: use NotifyIpInterfaceChange once that's implemented */ + if (notifyaddr_handle == INVALID_HANDLE_VALUE) + NotifyAddrChange( ¬ifyaddr_handle, ¬ifyaddr_ol ); + + if ((notifyaddr_handle != INVALID_HANDLE_VALUE) && + GetOverlappedResult( notifyaddr_handle, ¬ifyaddr_ol, &bytes, FALSE )) + { + TRACE( "network config changed, invalidating cache\n" ); + dhcp_cache_valid = dns_cache_valid = FALSE; + notifyaddr_handle = INVALID_HANDLE_VALUE; + NotifyAddrChange( ¬ifyaddr_handle, ¬ifyaddr_ol ); + } + if (flags & WINHTTP_AUTO_DETECT_TYPE_DHCP) { - *url = detect_autoproxyconfig_url_dhcp(); + if (!dhcp_cache_valid) + { + GlobalFree( dhcp_apc_url ); + dhcp_apc_url = detect_autoproxyconfig_url_dhcp(); + dhcp_cache_valid = TRUE; + } + + if (dhcp_apc_url) + { + *url = GlobalAlloc( 0, (wcslen( dhcp_apc_url ) + 1) * sizeof(WCHAR) ); + if (*url) wcscpy( *url, dhcp_apc_url ); + } } - if (flags & WINHTTP_AUTO_DETECT_TYPE_DNS_A) + if ((flags & WINHTTP_AUTO_DETECT_TYPE_DNS_A) && !*url) { - if (!*url) *url = detect_autoproxyconfig_url_dns(); + if (!dns_cache_valid) + { + GlobalFree( dns_apc_url ); + dns_apc_url = detect_autoproxyconfig_url_dns(); + dns_cache_valid = TRUE; + } + + if (dns_apc_url) + { + *url = GlobalAlloc( 0, (wcslen( dns_apc_url ) + 1) * sizeof(WCHAR) ); + if (*url) wcscpy( *url, dns_apc_url ); + } } + ReleaseSRWLockExclusive( &apc_cache_lock ); + if (!*url) { SetLastError( ERROR_WINHTTP_AUTODETECTION_FAILED );
Hans Leidekker (@hans) commented about dlls/winhttp/session.c:
return FALSE; } *url = NULL;
- AcquireSRWLockExclusive( &apc_cache_lock );
- /* FIXME: use NotifyIpInterfaceChange once that's implemented */
- if (notifyaddr_handle == INVALID_HANDLE_VALUE)
NotifyAddrChange( ¬ifyaddr_handle, ¬ifyaddr_ol );
- if ((notifyaddr_handle != INVALID_HANDLE_VALUE) &&
GetOverlappedResult( notifyaddr_handle, ¬ifyaddr_ol, &bytes, FALSE ))
- {
TRACE( "network config changed, invalidating cache\n" );
dhcp_cache_valid = dns_cache_valid = FALSE;
notifyaddr_handle = INVALID_HANDLE_VALUE;
NotifyAddrChange( ¬ifyaddr_handle, ¬ifyaddr_ol );
NotifyAddrChange() seems appropriate for DHCP address changes (although IPv4 only). URLs detected via DNS probably change less often but any change wouldn't be handled by this code. It's not clear what would trigger a change, perhaps add a timer using the TTL on the hostname record?. I think it's okay to ignore that for now.
This merge request was approved by Hans Leidekker.
On Thu May 15 07:22:42 2025 +0000, Hans Leidekker wrote:
NotifyAddrChange() seems appropriate for DHCP address changes (although IPv4 only). URLs detected via DNS probably change less often but any change wouldn't be handled by this code. It's not clear what would trigger a change, perhaps add a timer using the TTL on the hostname record?. I think it's okay to ignore that for now.
I forgot to mention, invalidating the cache based on IP address is what WinHTTP does according to a [Microsoft doc](https://learn.microsoft.com/en-us/windows/win32/winhttp/autoproxy-cache).