Fix https proxy not set when pac is used (#40950)

Set https_proxy to http_proxy when PAC is used.

Feng Wang committed Jul 3, 2026 at 10:19 UTC 533014f9a5f4644b0765693f5a501a36b1ceab73
2 files changed +55 -5
src/windows/service/exe/LxssUserSession.cpp
+8
@@ -4102,6 +4102,14 @@ try
4102 // We only add uppercase as there is no standard environment variable for PAC proxies.
4103 // This at least makes the PAC url available to the user in case they wish to use it.
4104 environment.emplace_back(std::format("{}={}", c_pacProxy, proxySettings.PacUrl));
4105 +
4106 + // When PAC is used, the reply only populates the proxy field.
4107 + // Set both envs to this value as best effort since PAC is not functional in headless Linux.
4108 + if (proxySettings.SecureProxy.empty() && !proxySettings.Proxy.empty())
4109 + {
4110 + environment.emplace_back(std::format("{}={}", c_httpsProxyLower, proxySettings.Proxy));
4111 + environment.emplace_back(std::format("{}={}", c_httpsProxyUpper, proxySettings.Proxy));
4112 + }
4113 }
4114 }
4115 CATCH_LOG()
test/windows/NetworkTests.cpp
+47 -5
@@ -1044,10 +1044,10 @@ class NetworkTests
1044
1045 static void ClearHttpProxySettings(bool userScope)
1046 {
1047 - auto command = L"Set-WinhttpProxy -SettingScope Machine -Proxy \\\"\\\"";
1047 + auto command = L"Set-WinhttpProxy -SettingScope Machine -Proxy \\\"\\\" -AutoconfigUrl \\\"\\\"";
1048 if (userScope)
1049 {
1050 - command = L"Set-WinhttpProxy -SettingScope User -Proxy \\\"\\\"";
1050 + command = L"Set-WinhttpProxy -SettingScope User -Proxy \\\"\\\" -AutoconfigUrl \\\"\\\"";
1051 }
1052 LxsstuLaunchPowershellAndCaptureOutput(command);
1053 }
@@ -1087,7 +1087,8 @@ class NetworkTests
1087 static constexpr auto c_proxyBypassLower = L"no_proxy";
1088 static constexpr auto c_proxyBypassUpper = L"NO_PROXY";
1089 static constexpr auto c_pacProxy = L"WSL_PAC_URL";
1090 - static constexpr auto c_httpProxyString = L"http://test.com:8888";
1090 + static constexpr auto c_httpProxyHostPort = L"test.com:8888";
1091 + static inline const std::wstring c_httpProxyString = std::wstring(L"http://") + c_httpProxyHostPort;
1092 static constexpr auto c_httpProxyString2 = L"http://otherServer.com:1234";
1093 static constexpr auto c_httpProxyLocalhost = L"http://localhost:8888";
1094 static constexpr auto c_httpProxyLoopback = L"http://loopback:8888";
@@ -1096,7 +1097,10 @@ class NetworkTests
1097 static constexpr auto c_httpProxyIpV4 = L"http://198.168.1.128:8888";
1098 static constexpr auto c_httpProxyIpV6 = L"http://[2001::1]:8888";
1099 static constexpr auto c_httpProxyBypassString = L"test";
1099 - static constexpr auto c_httpProxyPACurl = L"testpac.pac";
1100 + static constexpr auto c_pacServerPrefix = L"http://127.0.0.1:12399/";
1101 + static constexpr auto c_pacUrl = L"http://127.0.0.1:12399/wslproxy.pac";
1102 + static inline const std::wstring c_pacScript =
1103 + std::wstring(LR"(function FindProxyForURL(url, host) { return \"PROXY )") + c_httpProxyHostPort + LR"(\"; })";
1104
1105 static void VerifyWslEnvVariable(const std::wstring& envVar, const std::wstring& proxyString)
1106 {
@@ -1174,7 +1178,7 @@ class NetworkTests
1178 THROW_LAST_ERROR_IF(!SetEnvironmentVariable(L"WSLENV", nullptr));
1179 });
1180
1177 - THROW_LAST_ERROR_IF(!SetEnvironmentVariable(c_httpProxyLower, c_httpProxyString));
1181 + THROW_LAST_ERROR_IF(!SetEnvironmentVariable(c_httpProxyLower, c_httpProxyString.c_str()));
1182 std::wstring wslEnvVal{c_httpProxyLower};
1183 THROW_LAST_ERROR_IF(!SetEnvironmentVariable(L"WSLENV", wslEnvVal.append(L"/u").c_str()));
1184
@@ -1270,6 +1274,27 @@ class NetworkTests
1274 VerifyHttpProxyFilterByNetworkConfiguration(false);
1275 }
1276
1277 + static void VerifyHttpProxyPac(bool userScope = true)
1278 + {
1279 + UniqueWebServer pacServer(c_pacServerPrefix, c_pacScript.c_str());
1280 +
1281 + auto restoreProxySettings = wil::scope_exit([&] { ClearHttpProxySettings(userScope); });
1282 +
1283 + SetHttpProxySettings(L"", L"", c_pacUrl, userScope);
1284 +
1285 + // The update race condition is more likely to trigger for PAC as there is an additional http round trip.
1286 + wsl::shared::retry::RetryWithTimeout<void>(
1287 + [&]() {
1288 + auto [out, _] = LxsstuLaunchWslAndCaptureOutput(std::wstring(L"echo -n $") + c_httpProxyLower);
1289 + THROW_HR_IF(E_FAIL, out != c_httpProxyString);
1290 + },
1291 + std::chrono::seconds(1),
1292 + std::chrono::minutes(2));
1293 +
1294 + VerifyHttpProxyPacUrlMirrored(c_pacUrl);
1295 + VerifyHttpProxyStringMirrored(c_httpProxyString);
1296 + }
1297 +
1298 WSL2_TEST_METHOD(NatHttpProxyVerifyConfigDisabled)
1299 {
1300 WINHTTP_PROXY_TEST_ONLY();
@@ -1333,6 +1358,13 @@ class NetworkTests
1358 VerifyHttpProxyFilterByNetworkConfigurationNAT();
1359 }
1360
1361 + WSL2_TEST_METHOD(NatHttpProxyPac)
1362 + {
1363 + WINHTTP_PROXY_TEST_ONLY();
1364 + WslConfigChange config(LxssGenerateTestConfig({.autoProxy = true}));
1365 + VerifyHttpProxyPac();
1366 + }
1367 +
1368 WSL2_TEST_METHOD(RenameInterface)
1369 {
1370 // Disconnect "eth0" interface so it can be renamed
@@ -3868,6 +3900,16 @@ class MirroredTests
3900 NetworkTests::VerifyHttpProxyFilterByNetworkConfigurationMirrored();
3901 }
3902
3903 + WSL2_TEST_METHOD(HttpProxyPac)
3904 + {
3905 + MIRRORED_NETWORKING_TEST_ONLY();
3906 + WINHTTP_PROXY_TEST_ONLY();
3907 +
3908 + m_config->Update(LxssGenerateTestConfig({.networkingMode = wsl::core::NetworkingMode::Mirrored, .autoProxy = true}));
3909 + WaitForMirroredStateInLinux();
3910 + NetworkTests::VerifyHttpProxyPac();
3911 + }
3912 +
3913 WSL2_TEST_METHOD(SmokeTest)
3914 {
3915 MIRRORED_NETWORKING_TEST_ONLY();