commit 934c10f8994dfa91037f2fb9d3072ca6155b95ea
parent 7bf43c6a0f0b5a5856625dd872c0c7d2e7706c00
Author: yooughtul <2733543664@qq.com>
Date: Wed, 7 Jan 2026 12:52:51 +0000
Bug 1944213 - Migrate Unix proxy env var logic to Windows + add TestProtocolProxyService test. r=valentin,necko-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D274405
Diffstat:
8 files changed, 339 insertions(+), 136 deletions(-)
diff --git a/netwerk/test/gtest/TestProtocolProxyService.cpp b/netwerk/test/gtest/TestProtocolProxyService.cpp
@@ -8,6 +8,8 @@
#include "nsServiceManagerUtils.h"
#include "mozilla/Preferences.h"
#include "nsNetUtil.h"
+#include "prenv.h"
+#include "nsISystemProxySettings.h"
namespace mozilla {
namespace net {
@@ -160,5 +162,135 @@ TEST(TestProtocolProxyService, LoadHostFilters)
CheckPortDomain(false);
}
+TEST(TestProtocolProxyService, Proxy_Env_Vars)
+{
+#if defined(ANDROID)
+ GTEST_SKIP() << "Environment variable proxy support not available on Android";
+#endif
+
+ nsCOMPtr<nsISystemProxySettings> systemProxy =
+ do_GetService(NS_SYSTEMPROXYSETTINGS_CONTRACTID);
+ ASSERT_TRUE(systemProxy != nullptr);
+
+ auto CheckProxy = [&](const char* url, const char* expected) {
+ nsCOMPtr<nsIURI> uri;
+ ASSERT_EQ(NS_NewURI(getter_AddRefs(uri), url), NS_OK);
+ nsAutoCString spec, scheme, host;
+ int32_t port = -1;
+ uri->GetSpec(spec);
+ uri->GetScheme(scheme);
+ uri->GetHost(host);
+ uri->GetPort(&port);
+
+ nsAutoCString result;
+ nsresult rv = systemProxy->GetProxyForURI(spec, scheme, host, port, result);
+ ASSERT_EQ(rv, NS_OK);
+ // Check if result contains expected string.
+ EXPECT_TRUE(result.Find(expected) != kNotFound)
+ << "URL: " << url << ", Result: " << result.get()
+ << ", Expected: " << expected;
+ };
+
+ // 1. HTTP Proxy
+ {
+ PR_SetEnv("http_proxy=http://127.0.0.1:8080");
+ CheckProxy("http://example.com", "PROXY 127.0.0.1:8080");
+ PR_SetEnv("http_proxy=");
+ }
+
+ // 2. HTTPS Proxy
+ {
+ PR_SetEnv("https_proxy=http://127.0.0.1:8443");
+ CheckProxy("https://example.com", "PROXY 127.0.0.1:8443");
+ PR_SetEnv("https_proxy=");
+ }
+
+ // 3. All Proxy (fallback)
+ {
+ PR_SetEnv("all_proxy=http://127.0.0.1:9090");
+ CheckProxy("ftp://example.com", "PROXY 127.0.0.1:9090");
+ PR_SetEnv("all_proxy=");
+ }
+
+ // 4. No Proxy
+ {
+ PR_SetEnv("http_proxy=http://127.0.0.1:8080");
+ PR_SetEnv("no_proxy=example.com,.test.com");
+
+ // Matches example.com
+ CheckProxy("http://example.com", "DIRECT");
+ // Matches .test.com suffix
+ CheckProxy("http://sub.test.com", "DIRECT");
+ // Does not match
+ CheckProxy("http://other.com", "PROXY 127.0.0.1:8080");
+
+ PR_SetEnv("http_proxy=");
+ PR_SetEnv("no_proxy=");
+ }
+
+ // 5. No Proxy with specific port
+ {
+ PR_SetEnv("http_proxy=http://127.0.0.1:8080");
+ PR_SetEnv("no_proxy=example.com:8080");
+
+ // Matches example.com:8080
+ CheckProxy("http://example.com:8080", "DIRECT");
+ // Does not match example.com on default port (80)
+ CheckProxy("http://example.com", "PROXY 127.0.0.1:8080");
+ // Does not match example.com:9090
+ CheckProxy("http://example.com:9090", "PROXY 127.0.0.1:8080");
+
+ PR_SetEnv("http_proxy=");
+ PR_SetEnv("no_proxy=");
+ }
+
+ // 6. No Proxy with mixed port rules
+ {
+ PR_SetEnv("http_proxy=http://127.0.0.1:8080");
+ PR_SetEnv("https_proxy=http://127.0.0.1:8443");
+ PR_SetEnv("no_proxy=exact.com:9443,wildcard.com");
+
+ // Matches exact.com:9443
+ CheckProxy("https://exact.com:9443", "DIRECT");
+ // Does not match exact.com on default HTTPS port
+ CheckProxy("https://exact.com", "PROXY 127.0.0.1:8443");
+ // Does not match exact.com on a different port
+ CheckProxy("https://exact.com:8443", "PROXY 127.0.0.1:8443");
+ // Matches wildcard.com on any port
+ CheckProxy("http://wildcard.com", "DIRECT");
+ CheckProxy("http://wildcard.com:8080", "DIRECT");
+ CheckProxy("https://wildcard.com:443", "DIRECT");
+
+ PR_SetEnv("http_proxy=");
+ PR_SetEnv("https_proxy=");
+ PR_SetEnv("no_proxy=");
+ }
+
+ // 7. WebSocket (ws -> http_proxy)
+ {
+ PR_SetEnv("http_proxy=http://127.0.0.1:8080");
+ CheckProxy("ws://example.com", "PROXY 127.0.0.1:8080");
+ PR_SetEnv("http_proxy=");
+ }
+
+ // 8. WebSocket Secure (wss -> https_proxy)
+ {
+ PR_SetEnv("https_proxy=http://127.0.0.1:8443");
+ CheckProxy("wss://example.com", "PROXY 127.0.0.1:8443");
+ PR_SetEnv("https_proxy=");
+ }
+
+ // 9. default port
+ {
+ PR_SetEnv("http_proxy=http://127.0.0.1");
+ CheckProxy("http://example.com", "PROXY 127.0.0.1");
+ PR_SetEnv("http_proxy=");
+
+ PR_SetEnv("https_proxy=http://127.0.0.1");
+ CheckProxy("https://example.com", "PROXY 127.0.0.1");
+ PR_SetEnv("https_proxy=");
+ }
+}
+
} // namespace net
} // namespace mozilla
diff --git a/toolkit/moz.build b/toolkit/moz.build
@@ -52,7 +52,10 @@ DIRS += ["xre"]
if CONFIG["MOZ_WIDGET_TOOLKIT"] != "android":
DIRS += ["mozapps/handling"]
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
- DIRS += ["system/unixproxy"]
+ DIRS += [
+ "system/commonproxy",
+ "system/unixproxy",
+ ]
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
DIRS += [
"mozapps/macos-frameworks",
diff --git a/toolkit/system/commonproxy/ProxyUtils.cpp b/toolkit/system/commonproxy/ProxyUtils.cpp
@@ -10,6 +10,10 @@
#include "nsTArray.h"
#include "prnetdb.h"
#include "prtypes.h"
+#include "prenv.h"
+#include "nsCOMPtr.h"
+#include "nsNetUtil.h"
+#include "nsIURI.h"
namespace mozilla {
namespace toolkit {
@@ -153,6 +157,148 @@ static bool IsMatchWildcard(const nsACString& aHost,
return (star || (offset == static_cast<int32_t>(host.Length())));
}
+static const char* GetEnvRetryUppercase(const nsCString& aEnv) {
+ nsAutoCString env(aEnv);
+ const char* proxyVal = PR_GetEnv(env.get());
+ if (proxyVal) {
+ return proxyVal;
+ }
+ ToUpperCase(env);
+ proxyVal = PR_GetEnv(env.get());
+ return proxyVal;
+}
+
+static bool IsInNoProxyList(const nsACString& aScheme, const nsACString& aHost,
+ int32_t aPort, const char* noProxyVal) {
+ NS_ASSERTION(aPort >= -1, "Invalid port");
+
+ int32_t effectivePort = aPort;
+ if (aPort == -1) {
+ if (aScheme.EqualsLiteral("http") || aScheme.EqualsLiteral("ws")) {
+ effectivePort = 80;
+ } else if (aScheme.EqualsLiteral("https") || aScheme.EqualsLiteral("wss")) {
+ effectivePort = 443;
+ } else if (aScheme.EqualsLiteral("ftp")) {
+ effectivePort = 21;
+ }
+ }
+
+ nsAutoCString noProxy(noProxyVal);
+ if (noProxy.EqualsLiteral("*")) return true;
+
+ noProxy.StripWhitespace();
+
+ nsReadingIterator<char> pos;
+ nsReadingIterator<char> end;
+ noProxy.BeginReading(pos);
+ noProxy.EndReading(end);
+ while (pos != end) {
+ nsReadingIterator<char> last = pos;
+ nsReadingIterator<char> nextPos;
+ if (FindCharInReadable(',', last, end)) {
+ nextPos = last;
+ ++nextPos;
+ } else {
+ last = end;
+ nextPos = end;
+ }
+
+ nsReadingIterator<char> colon = pos;
+ int32_t port = -1;
+ if (FindCharInReadable(':', colon, last)) {
+ ++colon;
+ nsDependentCSubstring portStr(colon, last);
+ nsAutoCString portStr2(
+ portStr); // We need this for ToInteger. String API's suck.
+ nsresult err;
+ port = portStr2.ToInteger(&err);
+ if (NS_FAILED(err)) {
+ port = -2; // don't match any port, so we ignore this pattern
+ }
+ --colon;
+ } else {
+ colon = last;
+ }
+
+ if (port == -1 || port == aPort || (aPort == -1 && port == effectivePort)) {
+ nsDependentCSubstring hostStr(pos, colon);
+ // By using StringEndsWith instead of an equality comparator, we can
+ // include sub-domains
+ if (StringEndsWith(aHost, hostStr, nsCaseInsensitiveCStringComparator))
+ return true;
+ }
+
+ pos = nextPos;
+ }
+
+ return false;
+}
+
+static void SetProxyResultDirect(nsACString& aResult) {
+ aResult.AssignLiteral("DIRECT");
+}
+
+static void SetProxyResult(const char* aType, const nsACString& aHost,
+ int32_t aPort, nsACString& aResult) {
+ aResult.AssignASCII(aType);
+ aResult.Append(' ');
+ aResult.Append(aHost);
+ if (aPort > 0) {
+ aResult.Append(':');
+ aResult.AppendInt(aPort);
+ }
+}
+
+nsresult GetProxyFromEnvironment(const nsACString& aScheme,
+ const nsACString& aHost, int32_t aPort,
+ nsACString& aResult) {
+ nsAutoCString envVar;
+ envVar.Append(aScheme);
+ envVar.AppendLiteral("_proxy");
+ const char* proxyVal = GetEnvRetryUppercase(envVar);
+ if (!proxyVal && aScheme == "ws") {
+ proxyVal = GetEnvRetryUppercase("http_proxy"_ns);
+ } else if (!proxyVal && aScheme == "wss") {
+ proxyVal = GetEnvRetryUppercase("https_proxy"_ns);
+ }
+ if (!proxyVal) {
+ proxyVal = GetEnvRetryUppercase("all_proxy"_ns);
+ if (!proxyVal) {
+ // Return failure so that the caller can detect the failure and
+ // fall back to other proxy detection (e.g., WPAD)
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ const char* noProxyVal = GetEnvRetryUppercase("no_proxy"_ns);
+ if (noProxyVal && IsInNoProxyList(aScheme, aHost, aPort, noProxyVal)) {
+ SetProxyResultDirect(aResult);
+ return NS_OK;
+ }
+
+ // Use our URI parser to crack the proxy URI
+ nsCOMPtr<nsIURI> proxyURI;
+ nsresult rv = NS_NewURI(getter_AddRefs(proxyURI), proxyVal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Is there a way to specify "socks://" or something in these environment
+ // variables? I can't find any documentation.
+ if (!proxyURI->SchemeIs("http")) {
+ return NS_ERROR_UNKNOWN_PROTOCOL;
+ }
+
+ nsAutoCString proxyHost;
+ rv = proxyURI->GetHost(proxyHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t proxyPort;
+ rv = proxyURI->GetPort(&proxyPort);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetProxyResult("PROXY", proxyHost, proxyPort, aResult);
+ return NS_OK;
+}
+
bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) {
return IsMatchMask(aHost, aOverride) || IsMatchWildcard(aHost, aOverride);
}
diff --git a/toolkit/system/commonproxy/ProxyUtils.h b/toolkit/system/commonproxy/ProxyUtils.h
@@ -12,8 +12,10 @@ namespace mozilla {
namespace toolkit {
namespace system {
+nsresult GetProxyFromEnvironment(const nsACString& aScheme,
+ const nsACString& aHost, int32_t aPort,
+ nsACString& aResult);
bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride);
-
} // namespace system
} // namespace toolkit
} // namespace mozilla
diff --git a/toolkit/system/unixproxy/moz.build b/toolkit/system/unixproxy/moz.build
@@ -21,4 +21,8 @@ XPCOM_MANIFESTS += [
"components.conf",
]
+LOCAL_INCLUDES += [
+ "../commonproxy",
+]
+
FINAL_LIBRARY = "xul"
diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
@@ -16,6 +16,7 @@
#include "nsISupportsPrimitives.h"
#include "mozilla/widget/GSettings.h"
#include "nsReadableUtils.h"
+#include "ProxyUtils.h"
using namespace mozilla;
@@ -64,61 +65,6 @@ nsresult nsUnixSystemProxySettings::GetPACURI(nsACString& aResult) {
return NS_OK;
}
-static bool IsInNoProxyList(const nsACString& aHost, int32_t aPort,
- const char* noProxyVal) {
- NS_ASSERTION(aPort >= 0, "Negative port?");
-
- nsAutoCString noProxy(noProxyVal);
- if (noProxy.EqualsLiteral("*")) return true;
-
- noProxy.StripWhitespace();
-
- nsReadingIterator<char> pos;
- nsReadingIterator<char> end;
- noProxy.BeginReading(pos);
- noProxy.EndReading(end);
- while (pos != end) {
- nsReadingIterator<char> last = pos;
- nsReadingIterator<char> nextPos;
- if (FindCharInReadable(',', last, end)) {
- nextPos = last;
- ++nextPos;
- } else {
- last = end;
- nextPos = end;
- }
-
- nsReadingIterator<char> colon = pos;
- int32_t port = -1;
- if (FindCharInReadable(':', colon, last)) {
- ++colon;
- nsDependentCSubstring portStr(colon, last);
- nsAutoCString portStr2(
- portStr); // We need this for ToInteger. String API's suck.
- nsresult err;
- port = portStr2.ToInteger(&err);
- if (NS_FAILED(err)) {
- port = -2; // don't match any port, so we ignore this pattern
- }
- --colon;
- } else {
- colon = last;
- }
-
- if (port == -1 || port == aPort) {
- nsDependentCSubstring hostStr(pos, colon);
- // By using StringEndsWith instead of an equality comparator, we can
- // include sub-domains
- if (StringEndsWith(aHost, hostStr, nsCaseInsensitiveCStringComparator))
- return true;
- }
-
- pos = nextPos;
- }
-
- return false;
-}
-
static void SetProxyResult(const char* aType, const nsACString& aHost,
int32_t aPort, nsACString& aResult) {
aResult.AssignASCII(aType);
@@ -134,67 +80,6 @@ static void SetProxyResultDirect(nsACString& aResult) {
aResult.AssignLiteral("DIRECT");
}
-static const char* GetEnvRetryUppercase(const nsCString& aEnv) {
- nsAutoCString env(aEnv);
- const char* proxyVal = PR_GetEnv(env.get());
- if (proxyVal) {
- return proxyVal;
- }
- ToUpperCase(env);
- proxyVal = PR_GetEnv(env.get());
- return proxyVal;
-}
-
-static nsresult GetProxyFromEnvironment(const nsACString& aScheme,
- const nsACString& aHost, int32_t aPort,
- nsACString& aResult) {
- nsAutoCString envVar;
- envVar.Append(aScheme);
- envVar.AppendLiteral("_proxy");
- const char* proxyVal = GetEnvRetryUppercase(envVar);
- if (!proxyVal && aScheme == "ws") {
- proxyVal = GetEnvRetryUppercase("http_proxy"_ns);
- } else if (!proxyVal && aScheme == "wss") {
- proxyVal = GetEnvRetryUppercase("https_proxy"_ns);
- }
- if (!proxyVal) {
- proxyVal = GetEnvRetryUppercase("all_proxy"_ns);
- if (!proxyVal) {
- // Return failure so that the caller can detect the failure and
- // fall back to other proxy detection (e.g., WPAD)
- return NS_ERROR_FAILURE;
- }
- }
-
- const char* noProxyVal = GetEnvRetryUppercase("no_proxy"_ns);
- if (noProxyVal && IsInNoProxyList(aHost, aPort, noProxyVal)) {
- SetProxyResultDirect(aResult);
- return NS_OK;
- }
-
- // Use our URI parser to crack the proxy URI
- nsCOMPtr<nsIURI> proxyURI;
- nsresult rv = NS_NewURI(getter_AddRefs(proxyURI), proxyVal);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // Is there a way to specify "socks://" or something in these environment
- // variables? I can't find any documentation.
- if (!proxyURI->SchemeIs("http")) {
- return NS_ERROR_UNKNOWN_PROTOCOL;
- }
-
- nsAutoCString proxyHost;
- rv = proxyURI->GetHost(proxyHost);
- NS_ENSURE_SUCCESS(rv, rv);
-
- int32_t proxyPort;
- rv = proxyURI->GetPort(&proxyPort);
- NS_ENSURE_SUCCESS(rv, rv);
-
- SetProxyResult("PROXY", proxyHost, proxyPort, aResult);
- return NS_OK;
-}
-
nsresult nsUnixSystemProxySettings::SetProxyResultFromGSettings(
const nsCString& aSchema, const char* aType, nsACString& aResult) {
auto& settings = mSchemeProxySettings.LookupOrInsertWith(aSchema, [&] {
@@ -377,12 +262,18 @@ nsresult nsUnixSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
const nsACString& aHost,
const int32_t aPort,
nsACString& aResult) {
+ nsresult rv = mozilla::toolkit::system::GetProxyFromEnvironment(
+ aScheme, aHost, aPort, aResult);
+ if (NS_SUCCEEDED(rv)) {
+ return rv;
+ }
+
if (mProxySettings) {
- nsresult rv = GetProxyFromGSettings(aScheme, aHost, aPort, aResult);
+ rv = GetProxyFromGSettings(aScheme, aHost, aPort, aResult);
if (NS_SUCCEEDED(rv)) return rv;
}
- return GetProxyFromEnvironment(aScheme, aHost, aPort, aResult);
+ return rv;
}
NS_IMETHODIMP
diff --git a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp
@@ -15,25 +15,12 @@
#include "nsThreadUtils.h"
#include "prnetdb.h"
#include "ProxyUtils.h"
-
-class nsWindowsSystemProxySettings final : public nsISystemProxySettings {
- public:
- NS_DECL_THREADSAFE_ISUPPORTS
- NS_DECL_NSISYSTEMPROXYSETTINGS
-
- nsWindowsSystemProxySettings() {};
-
- private:
- ~nsWindowsSystemProxySettings() {};
-
- bool MatchOverride(const nsACString& aHost);
- bool PatternMatch(const nsACString& aHost, const nsACString& aOverride);
-};
+#include "nsWindowsSystemProxySettings.h"
NS_IMPL_ISUPPORTS(nsWindowsSystemProxySettings, nsISystemProxySettings)
-NS_IMETHODIMP
-nsWindowsSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) {
+NS_IMETHODIMP nsWindowsSystemProxySettings::GetMainThreadOnly(
+ bool* aMainThreadOnly) {
// bug 1366133: if you change this to main thread only, please handle
// nsProtocolProxyService::Resolve_Internal carefully to avoid hang on main
// thread.
@@ -41,6 +28,9 @@ nsWindowsSystemProxySettings::GetMainThreadOnly(bool* aMainThreadOnly) {
return NS_OK;
}
+nsWindowsSystemProxySettings::nsWindowsSystemProxySettings() {}
+nsWindowsSystemProxySettings::~nsWindowsSystemProxySettings() {}
+
static void SetProxyResult(const char* aType, const nsACString& aHostPort,
nsACString& aResult) {
aResult.AssignASCII(aType);
@@ -178,6 +168,14 @@ nsresult nsWindowsSystemProxySettings::GetProxyForURI(const nsACString& aSpec,
uint32_t flags = 0;
nsAutoString buf;
+ rv = mozilla::toolkit::system::GetProxyFromEnvironment(aScheme, aHost, aPort,
+ aResult);
+ // GetProxyFromEnvironment has already formatted and set the proxy result
+ // string in |aResult|.
+ if (NS_SUCCEEDED(rv) && !aResult.IsEmpty()) {
+ return NS_OK;
+ }
+
rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_SERVER, flags, buf);
if (NS_FAILED(rv) || !(flags & PROXY_TYPE_PROXY)) {
SetProxyResultDirect(aResult);
diff --git a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.h b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_toolkit_system_nsWindowsSystemProxySettings_h
+#define mozilla_toolkit_system_nsWindowsSystemProxySettings_h
+
+#include "nsISystemProxySettings.h"
+#include "nsStringFwd.h"
+
+class nsWindowsSystemProxySettings final : public nsISystemProxySettings {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISYSTEMPROXYSETTINGS
+
+ nsWindowsSystemProxySettings();
+
+ private:
+ ~nsWindowsSystemProxySettings();
+
+ bool MatchOverride(const nsACString& aHost);
+ bool PatternMatch(const nsACString& aHost, const nsACString& aOverride);
+};
+
+#endif // mozilla_toolkit_system_nsWindowsSystemProxySettings_h