diff --git a/src/_native/bits.cpp b/src/_native/bits.cpp index a6ba9cc..5bc7718 100644 --- a/src/_native/bits.cpp +++ b/src/_native/bits.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "helpers.h" @@ -204,6 +205,30 @@ PyObject *bits_serialize_job(PyObject *, PyObject *args, PyObject *kwargs) { } +static HRESULT _job_setproxy(IBackgroundCopyJob *job) { + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxy_config = { 0 }; + if (WinHttpGetIEProxyConfigForCurrentUser(&proxy_config)) { + if (proxy_config.lpszProxy || proxy_config.lpszAutoConfigUrl || proxy_config.fAutoDetect) { + // Global settings are present, and so BITS's default behaviour + // should be fine. + if (proxy_config.lpszProxy) { + GlobalFree(proxy_config.lpszProxy); + } + if (proxy_config.lpszProxyBypass) { + GlobalFree(proxy_config.lpszProxyBypass); + } + if (proxy_config.lpszAutoConfigUrl) { + GlobalFree(proxy_config.lpszAutoConfigUrl); + } + return S_OK; + } + } + + // Probably no static settings, so tell BITS to do autodetection. + return job->SetProxySettings(BG_JOB_PROXY_USAGE_AUTODETECT, NULL, NULL); +} + + static HRESULT _job_setcredentials(IBackgroundCopyJob *job, wchar_t *username, wchar_t *password) { IBackgroundCopyJob2 *job2 = NULL; HRESULT hr; @@ -256,6 +281,10 @@ PyObject *bits_begin(PyObject *, PyObject *args, PyObject *kwargs) { error_from_bits_hr(bcm, hr, "Creating download job"); goto done; } + if (FAILED(hr = _job_setproxy(job))) { + error_from_bits_hr(bcm, hr, "Setting proxy"); + goto done; + } if ((username || password) && FAILED(hr = _job_setcredentials(job, username, password))) { error_from_bits_hr(bcm, hr, "Adding basic credentials to download job"); goto done; diff --git a/src/_native/winhttp.cpp b/src/_native/winhttp.cpp index 0cf7396..d0323f4 100644 --- a/src/_native/winhttp.cpp +++ b/src/_native/winhttp.cpp @@ -150,6 +150,69 @@ static int crack_url(wchar_t *url, URL_COMPONENTS *parts, int add_nuls) { extern "C" { +#define CHECK_WINHTTP(x) if (!x) { winhttp_error(); goto exit; } + + +static bool winhttp_apply_proxy(HINTERNET hSession, HINTERNET hRequest, const wchar_t *url) { + bool result = false; + WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxy_config = { 0 }; + WINHTTP_AUTOPROXY_OPTIONS proxy_opt = { + .dwFlags = WINHTTP_AUTOPROXY_ALLOW_STATIC, + .fAutoLogonIfChallenged = TRUE + }; + WINHTTP_PROXY_INFO proxy_info = { 0 }; + + // First load the global-ish config settings + if (!WinHttpGetIEProxyConfigForCurrentUser(&proxy_config)) { + if (GetLastError() != ERROR_FILE_NOT_FOUND) { + goto exit; + } + // No global config, so assume auto-detect + proxy_config.lpszProxy = proxy_config.lpszProxyBypass = proxy_config.lpszAutoConfigUrl = NULL; + proxy_config.fAutoDetect = TRUE; + } + if (proxy_config.lpszProxy) { + GlobalFree(proxy_config.lpszProxy); + } + if (proxy_config.lpszProxyBypass) { + GlobalFree(proxy_config.lpszProxyBypass); + } + if (proxy_config.fAutoDetect) { + proxy_opt.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT; + proxy_opt.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP + | WINHTTP_AUTO_DETECT_TYPE_DNS_A; + } + if (proxy_config.lpszAutoConfigUrl) { + proxy_opt.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; + proxy_opt.lpszAutoConfigUrl = proxy_config.lpszAutoConfigUrl; + } + + // Now resolve the proxy required for the specified URL + CHECK_WINHTTP(WinHttpGetProxyForUrl(hSession, url, &proxy_opt, &proxy_info)); + + // Apply the proxy settings to the request + CHECK_WINHTTP(WinHttpSetOption( + hRequest, + WINHTTP_OPTION_PROXY, + &proxy_info, + sizeof(proxy_info) + )); + + result = true; +exit: + if (proxy_info.lpszProxy) { + GlobalFree((HGLOBAL)proxy_info.lpszProxy); + } + if (proxy_info.lpszProxyBypass) { + GlobalFree((HGLOBAL)proxy_info.lpszProxyBypass); + } + if (proxy_opt.lpszAutoConfigUrl) { + GlobalFree((HGLOBAL)proxy_opt.lpszAutoConfigUrl); + } + return result; +} + + PyObject *winhttp_urlopen(PyObject *, PyObject *args, PyObject *kwargs) { static const char * keywords[] = {"url", "method", "headers", "accepts", "chunksize", "on_progress", "on_cred_request", NULL}; wchar_t *url = NULL; @@ -202,11 +265,9 @@ PyObject *winhttp_urlopen(PyObject *, PyObject *args, PyObject *kwargs) { goto exit; } -#define CHECK_WINHTTP(x) if (!x) { winhttp_error(); goto exit; } - hSession = WinHttpOpen( NULL, - WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, url_parts.nScheme == INTERNET_SCHEME_HTTPS @@ -218,7 +279,7 @@ PyObject *winhttp_urlopen(PyObject *, PyObject *args, PyObject *kwargs) { // retry without it. hSession = WinHttpOpen( NULL, - WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 @@ -248,6 +309,8 @@ PyObject *winhttp_urlopen(PyObject *, PyObject *args, PyObject *kwargs) { ); CHECK_WINHTTP(hRequest); + CHECK_WINHTTP(winhttp_apply_proxy(hSession, hRequest, url)); + opt = WINHTTP_DECOMPRESSION_FLAG_ALL; CHECK_WINHTTP(WinHttpSetOption( hRequest,