Skip to content

Commit 6d8c627

Browse files
authored
Add automatic proxy handling for WinHTTP and BITS (#129)
Fixes #128
1 parent 65a0c26 commit 6d8c627

File tree

2 files changed

+96
-4
lines changed

2 files changed

+96
-4
lines changed

src/_native/bits.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <Python.h>
22
#include <windows.h>
33
#include <bits.h>
4+
#include <winhttp.h>
45
#include <typeinfo>
56

67
#include "helpers.h"
@@ -204,6 +205,30 @@ PyObject *bits_serialize_job(PyObject *, PyObject *args, PyObject *kwargs) {
204205
}
205206

206207

208+
static HRESULT _job_setproxy(IBackgroundCopyJob *job) {
209+
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxy_config = { 0 };
210+
if (WinHttpGetIEProxyConfigForCurrentUser(&proxy_config)) {
211+
if (proxy_config.lpszProxy || proxy_config.lpszAutoConfigUrl || proxy_config.fAutoDetect) {
212+
// Global settings are present, and so BITS's default behaviour
213+
// should be fine.
214+
if (proxy_config.lpszProxy) {
215+
GlobalFree(proxy_config.lpszProxy);
216+
}
217+
if (proxy_config.lpszProxyBypass) {
218+
GlobalFree(proxy_config.lpszProxyBypass);
219+
}
220+
if (proxy_config.lpszAutoConfigUrl) {
221+
GlobalFree(proxy_config.lpszAutoConfigUrl);
222+
}
223+
return S_OK;
224+
}
225+
}
226+
227+
// Probably no static settings, so tell BITS to do autodetection.
228+
return job->SetProxySettings(BG_JOB_PROXY_USAGE_AUTODETECT, NULL, NULL);
229+
}
230+
231+
207232
static HRESULT _job_setcredentials(IBackgroundCopyJob *job, wchar_t *username, wchar_t *password) {
208233
IBackgroundCopyJob2 *job2 = NULL;
209234
HRESULT hr;
@@ -256,6 +281,10 @@ PyObject *bits_begin(PyObject *, PyObject *args, PyObject *kwargs) {
256281
error_from_bits_hr(bcm, hr, "Creating download job");
257282
goto done;
258283
}
284+
if (FAILED(hr = _job_setproxy(job))) {
285+
error_from_bits_hr(bcm, hr, "Setting proxy");
286+
goto done;
287+
}
259288
if ((username || password) && FAILED(hr = _job_setcredentials(job, username, password))) {
260289
error_from_bits_hr(bcm, hr, "Adding basic credentials to download job");
261290
goto done;

src/_native/winhttp.cpp

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,69 @@ static int crack_url(wchar_t *url, URL_COMPONENTS *parts, int add_nuls) {
150150

151151
extern "C" {
152152

153+
#define CHECK_WINHTTP(x) if (!x) { winhttp_error(); goto exit; }
154+
155+
156+
static bool winhttp_apply_proxy(HINTERNET hSession, HINTERNET hRequest, const wchar_t *url) {
157+
bool result = false;
158+
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG proxy_config = { 0 };
159+
WINHTTP_AUTOPROXY_OPTIONS proxy_opt = {
160+
.dwFlags = WINHTTP_AUTOPROXY_ALLOW_STATIC,
161+
.fAutoLogonIfChallenged = TRUE
162+
};
163+
WINHTTP_PROXY_INFO proxy_info = { 0 };
164+
165+
// First load the global-ish config settings
166+
if (!WinHttpGetIEProxyConfigForCurrentUser(&proxy_config)) {
167+
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
168+
goto exit;
169+
}
170+
// No global config, so assume auto-detect
171+
proxy_config.lpszProxy = proxy_config.lpszProxyBypass = proxy_config.lpszAutoConfigUrl = NULL;
172+
proxy_config.fAutoDetect = TRUE;
173+
}
174+
if (proxy_config.lpszProxy) {
175+
GlobalFree(proxy_config.lpszProxy);
176+
}
177+
if (proxy_config.lpszProxyBypass) {
178+
GlobalFree(proxy_config.lpszProxyBypass);
179+
}
180+
if (proxy_config.fAutoDetect) {
181+
proxy_opt.dwFlags |= WINHTTP_AUTOPROXY_AUTO_DETECT;
182+
proxy_opt.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP
183+
| WINHTTP_AUTO_DETECT_TYPE_DNS_A;
184+
}
185+
if (proxy_config.lpszAutoConfigUrl) {
186+
proxy_opt.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
187+
proxy_opt.lpszAutoConfigUrl = proxy_config.lpszAutoConfigUrl;
188+
}
189+
190+
// Now resolve the proxy required for the specified URL
191+
CHECK_WINHTTP(WinHttpGetProxyForUrl(hSession, url, &proxy_opt, &proxy_info));
192+
193+
// Apply the proxy settings to the request
194+
CHECK_WINHTTP(WinHttpSetOption(
195+
hRequest,
196+
WINHTTP_OPTION_PROXY,
197+
&proxy_info,
198+
sizeof(proxy_info)
199+
));
200+
201+
result = true;
202+
exit:
203+
if (proxy_info.lpszProxy) {
204+
GlobalFree((HGLOBAL)proxy_info.lpszProxy);
205+
}
206+
if (proxy_info.lpszProxyBypass) {
207+
GlobalFree((HGLOBAL)proxy_info.lpszProxyBypass);
208+
}
209+
if (proxy_opt.lpszAutoConfigUrl) {
210+
GlobalFree((HGLOBAL)proxy_opt.lpszAutoConfigUrl);
211+
}
212+
return result;
213+
}
214+
215+
153216
PyObject *winhttp_urlopen(PyObject *, PyObject *args, PyObject *kwargs) {
154217
static const char * keywords[] = {"url", "method", "headers", "accepts", "chunksize", "on_progress", "on_cred_request", NULL};
155218
wchar_t *url = NULL;
@@ -202,11 +265,9 @@ PyObject *winhttp_urlopen(PyObject *, PyObject *args, PyObject *kwargs) {
202265
goto exit;
203266
}
204267

205-
#define CHECK_WINHTTP(x) if (!x) { winhttp_error(); goto exit; }
206-
207268
hSession = WinHttpOpen(
208269
NULL,
209-
WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
270+
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
210271
WINHTTP_NO_PROXY_NAME,
211272
WINHTTP_NO_PROXY_BYPASS,
212273
url_parts.nScheme == INTERNET_SCHEME_HTTPS
@@ -218,7 +279,7 @@ PyObject *winhttp_urlopen(PyObject *, PyObject *args, PyObject *kwargs) {
218279
// retry without it.
219280
hSession = WinHttpOpen(
220281
NULL,
221-
WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,
282+
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
222283
WINHTTP_NO_PROXY_NAME,
223284
WINHTTP_NO_PROXY_BYPASS,
224285
0
@@ -248,6 +309,8 @@ PyObject *winhttp_urlopen(PyObject *, PyObject *args, PyObject *kwargs) {
248309
);
249310
CHECK_WINHTTP(hRequest);
250311

312+
CHECK_WINHTTP(winhttp_apply_proxy(hSession, hRequest, url));
313+
251314
opt = WINHTTP_DECOMPRESSION_FLAG_ALL;
252315
CHECK_WINHTTP(WinHttpSetOption(
253316
hRequest,

0 commit comments

Comments
 (0)