From 81b74f3602f4609b3d1d5f2dbc4e440895982c49 Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Tue, 10 Mar 2026 11:36:38 +0100 Subject: [PATCH 01/12] Use AppContextSwitchHelper in System.Net.* assemblies Replace inline AppContext.TryGetSwitch + environment variable patterns with AppContextSwitchHelper.GetBooleanConfig() calls in: - SocketProtocolSupportPal (System.Net.Sockets, Ping, NameResolution) - SslStream.Protocol (DisableTlsResume, EnableServerAiaDownloads) - AuthenticationHelper.NtAuth (UsePortInSpn) - SslAuthenticationOptions (OCSP stapling switch) - HttpListener.Windows (EnableKernelResponseBuffering) - SslKeyLogger (EnableSslKeyLogging) - NegotiateAuthenticationPal.Unix (UseManagedNtlm) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/System/Net/Security/SslKeyLogger.cs | 2 +- .../System/Net/SocketProtocolSupportPal.cs | 20 ++---------- .../src/System.Net.Http.csproj | 2 ++ .../AuthenticationHelper.NtAuth.cs | 16 ++-------- .../src/System.Net.HttpListener.csproj | 2 ++ .../Net/Windows/HttpListener.Windows.cs | 2 +- .../src/System.Net.NameResolution.csproj | 2 ++ ...System.Net.NameResolution.Pal.Tests.csproj | 2 ++ .../src/System.Net.Ping.csproj | 2 ++ .../Net/NegotiateAuthenticationPal.Unix.cs | 7 ++-- .../Net/Security/SslAuthenticationOptions.cs | 3 +- .../System/Net/Security/SslStream.Protocol.cs | 32 ++++--------------- .../src/System.Net.Sockets.csproj | 2 ++ 13 files changed, 29 insertions(+), 65 deletions(-) diff --git a/src/libraries/Common/src/System/Net/Security/SslKeyLogger.cs b/src/libraries/Common/src/System/Net/Security/SslKeyLogger.cs index c0f426f082e5bb..75f0e6ab2d22b5 100644 --- a/src/libraries/Common/src/System/Net/Security/SslKeyLogger.cs +++ b/src/libraries/Common/src/System/Net/Security/SslKeyLogger.cs @@ -21,7 +21,7 @@ static SslKeyLogger() #if DEBUG bool isEnabled = true; #else - bool isEnabled = AppContext.TryGetSwitch("System.Net.EnableSslKeyLogging", out bool enabled) && enabled; + bool isEnabled = AppContextSwitchHelper.GetBooleanConfig("System.Net.EnableSslKeyLogging"); #endif if (isEnabled && s_keyLogFile != null) diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs index a61f47a0fa458d..afac43ca72f35f 100644 --- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs +++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs @@ -14,23 +14,7 @@ internal static partial class SocketProtocolSupportPal public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork); public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix); - private static bool IsIPv6Disabled() - { - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(DisableIPv6AppCtxSwitch, out bool disabled)) - { - return disabled; - } - - // AppContext switch wasn't used. Check the environment variable. - string? envVar = Environment.GetEnvironmentVariable(DisableIPv6EnvironmentVariable); - - if (envVar is not null) - { - return envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase); - } - - return false; - } + private static bool IsIPv6Disabled() => + AppContextSwitchHelper.GetBooleanConfig(DisableIPv6AppCtxSwitch, DisableIPv6EnvironmentVariable); } } diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index 782405cfa47233..bc9bce5ffe8df8 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -148,6 +148,8 @@ Link="Common\System\HexConverter.cs" /> + + + diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj index 378012a38768ca..d6556079fb7e7e 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj @@ -29,6 +29,8 @@ Link="Common\System\Net\IPEndPointStatics.cs" /> + + diff --git a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs index 5490cc86ef8a04..8e6796df4d4653 100644 --- a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs @@ -24,10 +24,9 @@ internal partial class NegotiateAuthenticationPal [FeatureSwitchDefinition("System.Net.Security.UseManagedNtlm")] private static bool UseManagedNtlm { get; } = - AppContext.TryGetSwitch("System.Net.Security.UseManagedNtlm", out bool useManagedNtlm) ? - useManagedNtlm : - OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst() || - (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase)); + AppContextSwitchHelper.GetBooleanConfig("System.Net.Security.UseManagedNtlm", + defaultValue: OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst() || + (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase))); public static NegotiateAuthenticationPal Create(NegotiateAuthenticationClientOptions clientOptions) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs index c3a78a098584f5..6809a8c9f06e43 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs @@ -146,8 +146,7 @@ internal void UpdateOptions(SslServerAuthenticationOptions sslServerAuthenticati if (certificateWithKey != null && certificateWithKey.HasPrivateKey) { - bool ocspFetch = false; - _ = AppContext.TryGetSwitch(EnableOcspStaplingContextSwitchName, out ocspFetch); + bool ocspFetch = AppContextSwitchHelper.GetBooleanConfig(EnableOcspStaplingContextSwitchName); // given cert is X509Certificate2 with key. We can use it directly. SetCertificateContextFromCert(certificateWithKey, !ocspFetch); } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 29bd3092b2bf2a..824cb318357644 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -34,20 +34,10 @@ internal static bool DisableTlsResume return disableTlsResume == NullableBool.True; } - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(DisableTlsResumeCtxSwitch, out bool value)) - { - s_disableTlsResume = value ? NullableBool.True : NullableBool.False; - } - else - { - // AppContext switch wasn't used. Check the environment variable. - s_disableTlsResume = - Environment.GetEnvironmentVariable(DisableTlsResumeEnvironmentVariable) is string envVar && - (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) ? NullableBool.True : NullableBool.False; - } + bool value = AppContextSwitchHelper.GetBooleanConfig(DisableTlsResumeCtxSwitch, DisableTlsResumeEnvironmentVariable); + s_disableTlsResume = value ? NullableBool.True : NullableBool.False; - return s_disableTlsResume == NullableBool.True; + return value; } } @@ -61,20 +51,10 @@ internal static bool EnableServerAiaDownloads return enableServerAiaDownloads == NullableBool.True; } - // First check for the AppContext switch, giving it priority over the environment variable. - if (AppContext.TryGetSwitch(EnableServerAiaDownloadsCtxSwitch, out bool value)) - { - s_enableServerAiaDownloads = value ? NullableBool.True : NullableBool.False; - } - else - { - // AppContext switch wasn't used. Check the environment variable. - s_enableServerAiaDownloads = - Environment.GetEnvironmentVariable(EnableServerAiaDownloadsEnvironmentVariable) is string envVar && - (envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) ? NullableBool.True : NullableBool.False; - } + bool value = AppContextSwitchHelper.GetBooleanConfig(EnableServerAiaDownloadsCtxSwitch, EnableServerAiaDownloadsEnvironmentVariable); + s_enableServerAiaDownloads = value ? NullableBool.True : NullableBool.False; - return s_enableServerAiaDownloads == NullableBool.True; + return value; } } diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index bdb03b5a7b5548..3a95ba61853154 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -78,6 +78,8 @@ Link="Common\System\Net\TcpValidationHelpers.cs" /> + Date: Tue, 10 Mar 2026 13:38:01 +0100 Subject: [PATCH 02/12] Adopt LocalAppContextSwitches pattern for System.Net.* switches Extend LocalAppContextSwitches.Common.cs with a GetCachedSwitchValue overload that supports environment variable fallback, and add GetBooleanEnvironmentVariable helper method. Create per-library LocalAppContextSwitches.cs files: - System.Net.Security: DisableTlsResume, EnableServerAiaDownloads, EnableOcspStapling - System.Net.Http: UsePortInSpn - System.Net.HttpListener: EnableKernelResponseBuffering This replaces the ad-hoc NullableBool caching pattern with the standard LocalAppContextSwitches pattern used across the codebase (System.Data.Common, System.Diagnostics.DiagnosticSource, etc.). NullableBool.cs is no longer needed in Http and Security. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Interop.OpenSsl.cs | 2 +- .../System/LocalAppContextSwitches.Common.cs | 41 ++++++++++++++++++ .../src/System.Net.Http.csproj | 7 ++- .../Net/Http/LocalAppContextSwitches.cs | 17 ++++++++ .../AuthenticationHelper.NtAuth.cs | 22 +--------- .../src/System.Net.HttpListener.csproj | 5 ++- .../src/System/Net/LocalAppContextSwitches.cs | 17 ++++++++ .../Net/Windows/HttpListener.Windows.cs | 2 +- .../src/System.Net.Security.csproj | 5 ++- .../Net/Security/LocalAppContextSwitches.cs | 31 +++++++++++++ .../Net/Security/SslAuthenticationOptions.cs | 3 +- .../System/Net/Security/SslStream.Protocol.cs | 43 +------------------ .../Net/Security/SslStreamPal.Windows.cs | 6 +-- .../System.Net.Security.Unit.Tests.csproj | 4 ++ 14 files changed, 127 insertions(+), 78 deletions(-) create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/LocalAppContextSwitches.cs create mode 100644 src/libraries/System.Net.HttpListener/src/System/Net/LocalAppContextSwitches.cs create mode 100644 src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs index 48019c3d86b8e4..10e475e78acfe5 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs @@ -349,7 +349,7 @@ internal static void UpdateClientCertificate(SafeSslHandle ssl, SslAuthenticatio internal static SafeSslHandle AllocateSslHandle(SslAuthenticationOptions sslAuthenticationOptions) { SafeSslHandle? sslHandle = null; - bool cacheSslContext = sslAuthenticationOptions.AllowTlsResume && !SslStream.DisableTlsResume && sslAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.RequireEncryption && sslAuthenticationOptions.CipherSuitesPolicy == null; + bool cacheSslContext = sslAuthenticationOptions.AllowTlsResume && !LocalAppContextSwitches.DisableTlsResume && sslAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.RequireEncryption && sslAuthenticationOptions.CipherSuitesPolicy == null; if (cacheSslContext) { diff --git a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs index 19806ceee1c79d..8cca97e37bf332 100644 --- a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs +++ b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs @@ -25,6 +25,17 @@ internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitc return GetCachedSwitchValueInternal(switchName, ref cachedSwitchValue); } + // Returns value of given switch or environment variable using provided cache. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool GetCachedSwitchValue(string switchName, string envVariable, ref int cachedSwitchValue) + { + // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false + if (cachedSwitchValue < 0) return false; + if (cachedSwitchValue > 0) return true; + + return GetCachedSwitchValueInternal(switchName, envVariable, ref cachedSwitchValue); + } + private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue) { bool hasSwitch = AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled); @@ -42,6 +53,36 @@ private static bool GetCachedSwitchValueInternal(string switchName, ref int cach return isSwitchEnabled; } + private static bool GetCachedSwitchValueInternal(string switchName, string envVariable, ref int cachedSwitchValue) + { + if (!AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled)) + { + isSwitchEnabled = GetBooleanEnvironmentVariable(envVariable); + } + + AppContext.TryGetSwitch("TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching); + if (!disableCaching) + { + cachedSwitchValue = isSwitchEnabled ? 1 /*true*/ : -1 /*false*/; + } + + return isSwitchEnabled; + } + + private static bool GetBooleanEnvironmentVariable(string envVariable) + { + string? str = Environment.GetEnvironmentVariable(envVariable); + if (str is not null) + { + if (str == "1" || string.Equals(str, bool.TrueString, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + // Provides default values for switches if they're not always false by default private static bool GetSwitchDefaultValue(string switchName) { diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index bc9bce5ffe8df8..aee8350897db71 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -146,10 +146,8 @@ Link="Common\System\Text\SimpleRegex.cs" /> - - + + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/LocalAppContextSwitches.cs b/src/libraries/System.Net.Http/src/System/Net/Http/LocalAppContextSwitches.cs new file mode 100644 index 00000000000000..558d63d6d1576a --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/LocalAppContextSwitches.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System +{ + internal static partial class LocalAppContextSwitches + { + private static int s_usePortInSpn; + internal static bool UsePortInSpn + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.Net.Http.UsePortInSpn", "DOTNET_SYSTEM_NET_HTTP_USEPORTINSPN", ref s_usePortInSpn); + } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs index f61cbe49c30c3f..430b9ced95d912 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs @@ -16,27 +16,7 @@ namespace System.Net.Http { internal static partial class AuthenticationHelper { - private const string UsePortInSpnCtxSwitch = "System.Net.Http.UsePortInSpn"; - private const string UsePortInSpnEnvironmentVariable = "DOTNET_SYSTEM_NET_HTTP_USEPORTINSPN"; - - private static volatile NullableBool s_usePortInSpn; - - private static bool UsePortInSpn - { - get - { - NullableBool usePortInSpn = s_usePortInSpn; - if (usePortInSpn != NullableBool.Undefined) - { - return usePortInSpn == NullableBool.True; - } - - bool value = AppContextSwitchHelper.GetBooleanConfig(UsePortInSpnCtxSwitch, UsePortInSpnEnvironmentVariable); - s_usePortInSpn = value ? NullableBool.True : NullableBool.False; - - return value; - } - } + private static bool UsePortInSpn => LocalAppContextSwitches.UsePortInSpn; private static Task InnerSendAsync(HttpRequestMessage request, bool async, bool isProxyAuth, HttpConnectionPool pool, HttpConnection connection, CancellationToken cancellationToken) { diff --git a/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj b/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj index ec8685ca98b9d4..ab89dc2b72b245 100644 --- a/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj +++ b/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj @@ -34,10 +34,11 @@ + - + GetCachedSwitchValue("System.Net.HttpListener.EnableKernelResponseBuffering", ref s_enableKernelResponseBuffering); + } + } +} diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs index 6265c10de52ba1..2c81f36f010c76 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs @@ -36,7 +36,7 @@ public sealed unsafe partial class HttpListener // no more than one outstanding write at a time, and can significantly improve throughput over high-latency connections. // Applications that use asynchronous I/O and that may have more than one send outstanding at a time should not use this flag. // Enabling this can result in higher CPU and memory usage by Http.sys. - internal static bool EnableKernelResponseBuffering { get; } = AppContextSwitchHelper.GetBooleanConfig("System.Net.HttpListener.EnableKernelResponseBuffering"); + internal static bool EnableKernelResponseBuffering => LocalAppContextSwitches.EnableKernelResponseBuffering; // Mitigate potential DOS attacks by limiting the number of unknown headers we accept. Numerous header names // with hash collisions will cause the server to consume excess CPU. 1000 headers limits CPU time to under diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index fa9f8b24212436..0449502c76d82c 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -34,6 +34,8 @@ Link="Common\System\Net\UriScheme.cs" /> + @@ -53,6 +55,7 @@ + @@ -117,8 +120,6 @@ Link="Common\System\Net\NegotiationInfoClass.cs" /> - diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs new file mode 100644 index 00000000000000..2a36a2dae4fc1e --- /dev/null +++ b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System +{ + internal static partial class LocalAppContextSwitches + { + private static int s_disableTlsResume; + internal static bool DisableTlsResume + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.Net.Security.DisableTlsResume", "DOTNET_SYSTEM_NET_SECURITY_DISABLETLSRESUME", ref s_disableTlsResume); + } + + private static int s_enableServerAiaDownloads; + internal static bool EnableServerAiaDownloads + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.Net.Security.EnableServerAiaDownloads", "DOTNET_SYSTEM_NET_SECURITY_ENABLESERVERAIADOWNLOADS", ref s_enableServerAiaDownloads); + } + + private static int s_enableOcspStapling; + internal static bool EnableOcspStapling + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.Net.Security.EnableServerOcspStaplingFromOnlyCertificateOnLinux", ref s_enableOcspStapling); + } + } +} diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs index 6809a8c9f06e43..10dfef558f0441 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs @@ -11,7 +11,6 @@ namespace System.Net.Security { internal sealed class SslAuthenticationOptions : IDisposable { - private const string EnableOcspStaplingContextSwitchName = "System.Net.Security.EnableServerOcspStaplingFromOnlyCertificateOnLinux"; internal const X509RevocationMode DefaultRevocationMode = X509RevocationMode.NoCheck; @@ -146,7 +145,7 @@ internal void UpdateOptions(SslServerAuthenticationOptions sslServerAuthenticati if (certificateWithKey != null && certificateWithKey.HasPrivateKey) { - bool ocspFetch = AppContextSwitchHelper.GetBooleanConfig(EnableOcspStaplingContextSwitchName); + bool ocspFetch = LocalAppContextSwitches.EnableOcspStapling; // given cert is X509Certificate2 with key. We can use it directly. SetCertificateContextFromCert(certificateWithKey, !ocspFetch); } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 824cb318357644..c13bf7aa85d4fa 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -16,47 +16,6 @@ namespace System.Net.Security { public partial class SslStream { - private const string DisableTlsResumeCtxSwitch = "System.Net.Security.DisableTlsResume"; - private const string DisableTlsResumeEnvironmentVariable = "DOTNET_SYSTEM_NET_SECURITY_DISABLETLSRESUME"; - private const string EnableServerAiaDownloadsCtxSwitch = "System.Net.Security.EnableServerAiaDownloads"; - private const string EnableServerAiaDownloadsEnvironmentVariable = "DOTNET_SYSTEM_NET_SECURITY_ENABLESERVERAIADOWNLOADS"; - - private static volatile NullableBool s_disableTlsResume; - private static volatile NullableBool s_enableServerAiaDownloads; - - internal static bool DisableTlsResume - { - get - { - NullableBool disableTlsResume = s_disableTlsResume; - if (disableTlsResume != NullableBool.Undefined) - { - return disableTlsResume == NullableBool.True; - } - - bool value = AppContextSwitchHelper.GetBooleanConfig(DisableTlsResumeCtxSwitch, DisableTlsResumeEnvironmentVariable); - s_disableTlsResume = value ? NullableBool.True : NullableBool.False; - - return value; - } - } - - internal static bool EnableServerAiaDownloads - { - get - { - NullableBool enableServerAiaDownloads = s_enableServerAiaDownloads; - if (enableServerAiaDownloads != NullableBool.Undefined) - { - return enableServerAiaDownloads == NullableBool.True; - } - - bool value = AppContextSwitchHelper.GetBooleanConfig(EnableServerAiaDownloadsCtxSwitch, EnableServerAiaDownloadsEnvironmentVariable); - s_enableServerAiaDownloads = value ? NullableBool.True : NullableBool.False; - - return value; - } - } private SafeFreeCredentials? _credentialsHandle; @@ -1097,7 +1056,7 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot chain.ChainPolicy.RevocationMode = _sslAuthenticationOptions.CertificateRevocationCheckMode; chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; - if (_sslAuthenticationOptions.IsServer && !EnableServerAiaDownloads) + if (_sslAuthenticationOptions.IsServer && !LocalAppContextSwitches.EnableServerAiaDownloads) { chain.ChainPolicy.DisableCertificateDownloads = true; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs index 3bdb83666d4c61..aecaa2feca9e07 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs @@ -195,7 +195,7 @@ public static ProtocolToken InitializeSecurityContext( consumed -= inputBuffers._item1.Token.Length; } - bool allowTlsResume = sslAuthenticationOptions.AllowTlsResume && !SslStream.DisableTlsResume; + bool allowTlsResume = sslAuthenticationOptions.AllowTlsResume && !LocalAppContextSwitches.DisableTlsResume; if (!allowTlsResume && newContext && context != null) { @@ -299,7 +299,7 @@ public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchannelCred(Ss Interop.SspiCli.SCHANNEL_CRED.Flags flags; Interop.SspiCli.CredentialUse direction; - bool allowTlsResume = authOptions.AllowTlsResume && !SslStream.DisableTlsResume; + bool allowTlsResume = authOptions.AllowTlsResume && !LocalAppContextSwitches.DisableTlsResume; if (!isServer) { @@ -374,7 +374,7 @@ public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchCredentials( Interop.SspiCli.SCH_CREDENTIALS.Flags flags; Interop.SspiCli.CredentialUse direction; - bool allowTlsResume = authOptions.AllowTlsResume && !SslStream.DisableTlsResume; + bool allowTlsResume = authOptions.AllowTlsResume && !LocalAppContextSwitches.DisableTlsResume; if (isServer) { diff --git a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj index 42381dbab6c6b8..b1547a2cf237bf 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj @@ -36,6 +36,10 @@ + + Date: Tue, 10 Mar 2026 13:52:28 +0100 Subject: [PATCH 03/12] Move DisableIPv6 and UseManagedNtlm to LocalAppContextSwitches Move DisableIPv6 from SocketProtocolSupportPal.cs to a new shared Common/src/System/Net/LocalAppContextSwitches.Net.cs file, using the GetSwitchValue + GetBooleanEnvironmentVariable pattern for single-evaluation switches with env var fallback. Move UseManagedNtlm from NegotiateAuthenticationPal.Unix.cs to the System.Net.Security LocalAppContextSwitches.cs, using the same GetSwitchValue pattern with a computed default value for platforms that default to managed NTLM. Replace AppContextSwitchHelper.cs with LocalAppContextSwitches in Sockets, Ping, and NameResolution csprojs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../System/Net/LocalAppContextSwitches.Net.cs | 21 +++++++++++++++++++ .../System/Net/SocketProtocolSupportPal.cs | 8 +------ .../src/System.Net.NameResolution.csproj | 6 ++++-- ...System.Net.NameResolution.Pal.Tests.csproj | 6 ++++-- .../src/System.Net.Ping.csproj | 6 ++++-- .../Net/NegotiateAuthenticationPal.Unix.cs | 5 +---- .../Net/Security/LocalAppContextSwitches.cs | 17 +++++++++++++++ .../src/System.Net.Sockets.csproj | 6 ++++-- 8 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs diff --git a/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs b/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs new file mode 100644 index 00000000000000..9659ff6ab9fed2 --- /dev/null +++ b/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System +{ + internal static partial class LocalAppContextSwitches + { + internal static bool DisableIPv6 { get; } = InitializeDisableIPv6(); + + private static bool InitializeDisableIPv6() + { + bool value = false; + if (GetSwitchValue("System.Net.DisableIPv6", ref value)) + { + return value; + } + + return GetBooleanEnvironmentVariable("DOTNET_SYSTEM_NET_DISABLEIPV6"); + } + } +} diff --git a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs index afac43ca72f35f..6f2e307d5bece7 100644 --- a/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs +++ b/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs @@ -7,14 +7,8 @@ namespace System.Net { internal static partial class SocketProtocolSupportPal { - private const string DisableIPv6AppCtxSwitch = "System.Net.DisableIPv6"; - private const string DisableIPv6EnvironmentVariable = "DOTNET_SYSTEM_NET_DISABLEIPV6"; - - public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6) && !IsIPv6Disabled(); + public static bool OSSupportsIPv6 { get; } = IsSupported(AddressFamily.InterNetworkV6) && !LocalAppContextSwitches.DisableIPv6; public static bool OSSupportsIPv4 { get; } = IsSupported(AddressFamily.InterNetwork); public static bool OSSupportsUnixDomainSockets { get; } = IsSupported(AddressFamily.Unix); - - private static bool IsIPv6Disabled() => - AppContextSwitchHelper.GetBooleanConfig(DisableIPv6AppCtxSwitch, DisableIPv6EnvironmentVariable); } } diff --git a/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj b/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj index 4123c8e8a51992..47439bf44a3f63 100644 --- a/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj +++ b/src/libraries/System.Net.NameResolution/src/System.Net.NameResolution.csproj @@ -24,8 +24,10 @@ Link="Common\System\Net\Logging\NetEventSource.Common.cs" /> - + + diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj index d6556079fb7e7e..05e55180110b81 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/System.Net.NameResolution.Pal.Tests.csproj @@ -29,8 +29,10 @@ Link="Common\System\Net\IPEndPointStatics.cs" /> - + + - + + diff --git a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs index 8e6796df4d4653..e56d88fd998f37 100644 --- a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs @@ -23,10 +23,7 @@ internal partial class NegotiateAuthenticationPal internal static bool HasSystemNetSecurityNative => _hasSystemNetSecurityNative.Value; [FeatureSwitchDefinition("System.Net.Security.UseManagedNtlm")] - private static bool UseManagedNtlm { get; } = - AppContextSwitchHelper.GetBooleanConfig("System.Net.Security.UseManagedNtlm", - defaultValue: OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst() || - (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase))); + private static bool UseManagedNtlm => LocalAppContextSwitches.UseManagedNtlm; public static NegotiateAuthenticationPal Create(NegotiateAuthenticationClientOptions clientOptions) { diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs index 2a36a2dae4fc1e..b69c44ffa33fb5 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System { @@ -27,5 +29,20 @@ internal static bool EnableOcspStapling [MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetCachedSwitchValue("System.Net.Security.EnableServerOcspStaplingFromOnlyCertificateOnLinux", ref s_enableOcspStapling); } + + [FeatureSwitchDefinition("System.Net.Security.UseManagedNtlm")] + internal static bool UseManagedNtlm { get; } = InitializeUseManagedNtlm(); + + private static bool InitializeUseManagedNtlm() + { + bool value = false; + if (GetSwitchValue("System.Net.Security.UseManagedNtlm", ref value)) + { + return value; + } + + return OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst() || + (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase)); + } } } diff --git a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj index 3a95ba61853154..16413a42a31fdf 100644 --- a/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj +++ b/src/libraries/System.Net.Sockets/src/System.Net.Sockets.csproj @@ -78,8 +78,10 @@ Link="Common\System\Net\TcpValidationHelpers.cs" /> - + + Date: Tue, 10 Mar 2026 13:57:09 +0100 Subject: [PATCH 04/12] Use cached int field pattern for DisableIPv6 switch Use the same GetCachedSwitchValue pattern with an int backing field as all other networking LocalAppContextSwitches properties. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../System/Net/LocalAppContextSwitches.Net.cs | 16 ++++++---------- .../Net/NegotiateAuthenticationPal.Unix.cs | 5 +---- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs b/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs index 9659ff6ab9fed2..82cfa39e03e92f 100644 --- a/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs +++ b/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs @@ -1,21 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace System { internal static partial class LocalAppContextSwitches { - internal static bool DisableIPv6 { get; } = InitializeDisableIPv6(); - - private static bool InitializeDisableIPv6() + private static int s_disableIPv6; + internal static bool DisableIPv6 { - bool value = false; - if (GetSwitchValue("System.Net.DisableIPv6", ref value)) - { - return value; - } - - return GetBooleanEnvironmentVariable("DOTNET_SYSTEM_NET_DISABLEIPV6"); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.Net.DisableIPv6", "DOTNET_SYSTEM_NET_DISABLEIPV6", ref s_disableIPv6); } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs index e56d88fd998f37..475483944f84a3 100644 --- a/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.Unix.cs @@ -22,12 +22,9 @@ internal partial class NegotiateAuthenticationPal private static readonly Lazy _hasSystemNetSecurityNative = new Lazy(CheckHasSystemNetSecurityNative); internal static bool HasSystemNetSecurityNative => _hasSystemNetSecurityNative.Value; - [FeatureSwitchDefinition("System.Net.Security.UseManagedNtlm")] - private static bool UseManagedNtlm => LocalAppContextSwitches.UseManagedNtlm; - public static NegotiateAuthenticationPal Create(NegotiateAuthenticationClientOptions clientOptions) { - if (UseManagedNtlm) + if (LocalAppContextSwitches.UseManagedNtlm) { switch (clientOptions.Package) { From 6ac0495ec7c92fd57c2fbf5802e7b08cfb4526f6 Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Tue, 10 Mar 2026 13:59:35 +0100 Subject: [PATCH 05/12] Use cached int field pattern for UseManagedNtlm switch Add GetCachedSwitchValue overload with a defaultValue parameter to LocalAppContextSwitches.Common.cs for switches that have a non-false default. Switch UseManagedNtlm to use the same cached int field pattern as all other networking switches. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../System/LocalAppContextSwitches.Common.cs | 27 +++++++++++++++++++ .../Net/Security/LocalAppContextSwitches.cs | 17 +++++------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs index 8cca97e37bf332..f65df1eff896b1 100644 --- a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs +++ b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs @@ -36,6 +36,17 @@ internal static bool GetCachedSwitchValue(string switchName, string envVariable, return GetCachedSwitchValueInternal(switchName, envVariable, ref cachedSwitchValue); } + // Returns value of given switch using provided cache and default value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue, bool defaultValue) + { + // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false + if (cachedSwitchValue < 0) return false; + if (cachedSwitchValue > 0) return true; + + return GetCachedSwitchValueInternal(switchName, ref cachedSwitchValue, defaultValue); + } + private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue) { bool hasSwitch = AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled); @@ -69,6 +80,22 @@ private static bool GetCachedSwitchValueInternal(string switchName, string envVa return isSwitchEnabled; } + private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue, bool defaultValue) + { + if (!AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled)) + { + isSwitchEnabled = defaultValue; + } + + AppContext.TryGetSwitch("TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching); + if (!disableCaching) + { + cachedSwitchValue = isSwitchEnabled ? 1 /*true*/ : -1 /*false*/; + } + + return isSwitchEnabled; + } + private static bool GetBooleanEnvironmentVariable(string envVariable) { string? str = Environment.GetEnvironmentVariable(envVariable); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs index b69c44ffa33fb5..b58ab7d07ac9cb 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs @@ -30,19 +30,14 @@ internal static bool EnableOcspStapling get => GetCachedSwitchValue("System.Net.Security.EnableServerOcspStaplingFromOnlyCertificateOnLinux", ref s_enableOcspStapling); } + private static int s_useManagedNtlm; [FeatureSwitchDefinition("System.Net.Security.UseManagedNtlm")] - internal static bool UseManagedNtlm { get; } = InitializeUseManagedNtlm(); - - private static bool InitializeUseManagedNtlm() + internal static bool UseManagedNtlm { - bool value = false; - if (GetSwitchValue("System.Net.Security.UseManagedNtlm", ref value)) - { - return value; - } - - return OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst() || - (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.Net.Security.UseManagedNtlm", ref s_useManagedNtlm, + defaultValue: OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst() || + (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase))); } } } From b88da594061d41586c94662a39a4ded0f1d4bb6f Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Tue, 10 Mar 2026 14:04:36 +0100 Subject: [PATCH 06/12] Replace remaining AppContextSwitchHelper uses with LocalAppContextSwitches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move UseNetworkFramework and EnableSslKeyLogging switches to LocalAppContextSwitches in System.Net.Security. Move DisableConfigurationCache and AppLocalMsQuic switches to a new LocalAppContextSwitches.cs in System.Net.Quic. Remove AppContextSwitchHelper.cs from Security, Quic, and Security unit test csprojs — all networking AppContext switches now use the LocalAppContextSwitches pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../System/Net/LocalAppContextSwitches.Net.cs | 11 +++++++++ .../src/System/Net/Security/SslKeyLogger.cs | 6 +---- .../src/System.Net.Quic.csproj | 3 ++- .../src/System/Net/Quic/Internal/MsQuicApi.cs | 3 +-- .../Internal/MsQuicConfiguration.Cache.cs | 4 +--- .../Net/Quic/LocalAppContextSwitches.cs | 24 +++++++++++++++++++ .../src/System.Net.Security.csproj | 4 ++-- .../Net/Security/LocalAppContextSwitches.cs | 7 ++++++ .../Security/Pal.OSX/SafeDeleteNwContext.cs | 4 +--- .../System.Net.Security.Unit.Tests.csproj | 4 ++-- 10 files changed, 52 insertions(+), 18 deletions(-) create mode 100644 src/libraries/System.Net.Quic/src/System/Net/Quic/LocalAppContextSwitches.cs diff --git a/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs b/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs index 82cfa39e03e92f..9cf4aba316a059 100644 --- a/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs +++ b/src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs @@ -13,5 +13,16 @@ internal static bool DisableIPv6 [MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetCachedSwitchValue("System.Net.DisableIPv6", "DOTNET_SYSTEM_NET_DISABLEIPV6", ref s_disableIPv6); } + + private static int s_enableSslKeyLogging; + internal static bool EnableSslKeyLogging + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#if DEBUG + get => GetCachedSwitchValue("System.Net.EnableSslKeyLogging", ref s_enableSslKeyLogging, defaultValue: true); +#else + get => GetCachedSwitchValue("System.Net.EnableSslKeyLogging", ref s_enableSslKeyLogging); +#endif + } } } diff --git a/src/libraries/Common/src/System/Net/Security/SslKeyLogger.cs b/src/libraries/Common/src/System/Net/Security/SslKeyLogger.cs index 75f0e6ab2d22b5..aa7885420c9ef8 100644 --- a/src/libraries/Common/src/System/Net/Security/SslKeyLogger.cs +++ b/src/libraries/Common/src/System/Net/Security/SslKeyLogger.cs @@ -18,11 +18,7 @@ static SslKeyLogger() try { -#if DEBUG - bool isEnabled = true; -#else - bool isEnabled = AppContextSwitchHelper.GetBooleanConfig("System.Net.EnableSslKeyLogging"); -#endif + bool isEnabled = LocalAppContextSwitches.EnableSslKeyLogging; if (isEnabled && s_keyLogFile != null) { diff --git a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj index 084eb74b3dda48..50097783501999 100644 --- a/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj +++ b/src/libraries/System.Net.Quic/src/System.Net.Quic.csproj @@ -22,7 +22,8 @@ - + + diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs index d893c9a0c5e569..bd6f8283a90ff1 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicApi.cs @@ -281,6 +281,5 @@ private static bool IsTls13Disabled(bool isServer) return false; } - private static bool ShouldUseAppLocalMsQuic() => AppContextSwitchHelper.GetBooleanConfig( - "System.Net.Quic.AppLocalMsQuic"); + private static bool ShouldUseAppLocalMsQuic() => LocalAppContextSwitches.AppLocalMsQuic; } diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.Cache.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.Cache.cs index 4e06f6c6b0bd1e..4818a7395cdbd0 100644 --- a/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.Cache.cs +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/Internal/MsQuicConfiguration.Cache.cs @@ -16,9 +16,7 @@ namespace System.Net.Quic; internal static partial class MsQuicConfiguration { - internal static bool ConfigurationCacheEnabled { get; } = !AppContextSwitchHelper.GetBooleanConfig( - "System.Net.Quic.DisableConfigurationCache", - "DOTNET_SYSTEM_NET_QUIC_DISABLE_CONFIGURATION_CACHE"); + internal static bool ConfigurationCacheEnabled { get; } = !LocalAppContextSwitches.DisableConfigurationCache; private static readonly MsQuicConfigurationCache s_configurationCache = new MsQuicConfigurationCache(); diff --git a/src/libraries/System.Net.Quic/src/System/Net/Quic/LocalAppContextSwitches.cs b/src/libraries/System.Net.Quic/src/System/Net/Quic/LocalAppContextSwitches.cs new file mode 100644 index 00000000000000..1b44c84b5230b9 --- /dev/null +++ b/src/libraries/System.Net.Quic/src/System/Net/Quic/LocalAppContextSwitches.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System +{ + internal static partial class LocalAppContextSwitches + { + private static int s_disableConfigurationCache; + internal static bool DisableConfigurationCache + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.Net.Quic.DisableConfigurationCache", "DOTNET_SYSTEM_NET_QUIC_DISABLE_CONFIGURATION_CACHE", ref s_disableConfigurationCache); + } + + private static int s_appLocalMsQuic; + internal static bool AppLocalMsQuic + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.Net.Quic.AppLocalMsQuic", ref s_appLocalMsQuic); + } + } +} diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index 0449502c76d82c..eaa3eb567864e0 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -32,10 +32,10 @@ Link="Common\System\Obsoletions.cs" /> - + diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs index b58ab7d07ac9cb..77df0438ac6dff 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs @@ -39,5 +39,12 @@ internal static bool UseManagedNtlm defaultValue: OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst() || (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase))); } + + private static int s_useNetworkFramework; + internal static bool UseNetworkFramework + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetCachedSwitchValue("System.Net.Security.UseNetworkFramework", "DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK", ref s_useNetworkFramework); + } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteNwContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteNwContext.cs index 506d118cb8b5f9..88c5e5346c8a8a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteNwContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteNwContext.cs @@ -32,9 +32,7 @@ namespace System.Net.Security internal sealed class SafeDeleteNwContext : SafeDeleteContext { // AppContext switch to enable Network Framework usage - internal static bool IsSwitchEnabled { get; } = AppContextSwitchHelper.GetBooleanConfig( - "System.Net.Security.UseNetworkFramework", - "DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK"); + internal static bool IsSwitchEnabled => LocalAppContextSwitches.UseNetworkFramework; private static readonly Lazy s_isNetworkFrameworkAvailable = new Lazy(CheckNetworkFrameworkAvailability); private const int InitialReceiveBufferSize = 2 * 1024; diff --git a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj index b1547a2cf237bf..1fe85f24b858ee 100644 --- a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj @@ -34,10 +34,10 @@ - + Date: Tue, 10 Mar 2026 14:10:11 +0100 Subject: [PATCH 07/12] Remove Common AppContextSwitchHelper.cs Delete the file and remove all remaining csproj references (WinHttpHandler src and tests had unused includes). System.Text.Json has its own local copy which is unaffected. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/System/AppContextSwitchHelper.cs | 35 ------------------- .../src/System.Net.Http.WinHttpHandler.csproj | 2 -- ....Net.Http.WinHttpHandler.Unit.Tests.csproj | 2 -- .../Internal/MsQuicConfiguration.Cache.cs | 2 +- .../Net/Security/LocalAppContextSwitches.cs | 2 ++ 5 files changed, 3 insertions(+), 40 deletions(-) delete mode 100644 src/libraries/Common/src/System/AppContextSwitchHelper.cs diff --git a/src/libraries/Common/src/System/AppContextSwitchHelper.cs b/src/libraries/Common/src/System/AppContextSwitchHelper.cs deleted file mode 100644 index e2607ca66eb3e8..00000000000000 --- a/src/libraries/Common/src/System/AppContextSwitchHelper.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Globalization; - -namespace System -{ - internal static class AppContextSwitchHelper - { - internal static bool GetBooleanConfig(string switchName, bool defaultValue = false) => - AppContext.TryGetSwitch(switchName, out bool value) ? value : defaultValue; - - internal static bool GetBooleanConfig(string switchName, string envVariable, bool defaultValue = false) - { - if (AppContext.TryGetSwitch(switchName, out bool value)) - { - return value; - } - - if (Environment.GetEnvironmentVariable(envVariable) is string str) - { - if (str == "1" || string.Equals(str, bool.TrueString, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - if (str == "0" || string.Equals(str, bool.FalseString, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - } - - return defaultValue; - } - } -} diff --git a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj index 2cba1a88684d98..7e6af9f6b954f9 100644 --- a/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj +++ b/src/libraries/System.Net.Http.WinHttpHandler/src/System.Net.Http.WinHttpHandler.csproj @@ -96,8 +96,6 @@ System.Net.Http.WinHttpHandler - - !LocalAppContextSwitches.DisableConfigurationCache; private static readonly MsQuicConfigurationCache s_configurationCache = new MsQuicConfigurationCache(); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs index 77df0438ac6dff..16e14fe7ee863a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs @@ -40,11 +40,13 @@ internal static bool UseManagedNtlm (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase))); } +#if TARGET_APPLE private static int s_useNetworkFramework; internal static bool UseNetworkFramework { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetCachedSwitchValue("System.Net.Security.UseNetworkFramework", "DOTNET_SYSTEM_NET_SECURITY_USENETWORKFRAMEWORK", ref s_useNetworkFramework); } +#endif } } From 74b442cb66b71a8b8b5b1c0115284fe6382849d8 Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Tue, 10 Mar 2026 14:26:33 +0100 Subject: [PATCH 08/12] Remove GetSwitchDefaultValue from LocalAppContextSwitches.Common.cs Move default values to call sites using the defaultValue parameter. Merge the two no-envvar GetCachedSwitchValue overloads into one with 'bool defaultValue = false'. Remove GetSwitchDefaultValue and one GetCachedSwitchValueInternal overload. Affected callers updated to pass defaultValue: true: - SerializationGuard (System.Private.CoreLib) - BinaryFormatterEnabled (System.Runtime.Serialization.Formatters) - IsNetworkingEnabledByDefault (System.Private.Xml) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../System/LocalAppContextSwitches.Common.cs | 59 ++----------------- .../src/System/LocalAppContextSwitches.cs | 2 +- .../Xml/Core/LocalAppContextSwitches.cs | 2 +- .../Serialization/LocalAppContextSwitches.cs | 2 +- 4 files changed, 8 insertions(+), 57 deletions(-) diff --git a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs index f65df1eff896b1..f6f2c145e48c4c 100644 --- a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs +++ b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs @@ -16,13 +16,13 @@ internal static bool GetSwitchValue(string switchName, ref bool switchValue) => // Returns value of given switch using provided cache. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue) + internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue, bool defaultValue = false) { // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false if (cachedSwitchValue < 0) return false; if (cachedSwitchValue > 0) return true; - return GetCachedSwitchValueInternal(switchName, ref cachedSwitchValue); + return GetCachedSwitchValueInternal(switchName, ref cachedSwitchValue, defaultValue); } // Returns value of given switch or environment variable using provided cache. @@ -36,23 +36,11 @@ internal static bool GetCachedSwitchValue(string switchName, string envVariable, return GetCachedSwitchValueInternal(switchName, envVariable, ref cachedSwitchValue); } - // Returns value of given switch using provided cache and default value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue, bool defaultValue) - { - // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false - if (cachedSwitchValue < 0) return false; - if (cachedSwitchValue > 0) return true; - - return GetCachedSwitchValueInternal(switchName, ref cachedSwitchValue, defaultValue); - } - - private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue) + private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue, bool defaultValue) { - bool hasSwitch = AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled); - if (!hasSwitch) + if (!AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled)) { - isSwitchEnabled = GetSwitchDefaultValue(switchName); + isSwitchEnabled = defaultValue; } AppContext.TryGetSwitch("TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching); @@ -80,22 +68,6 @@ private static bool GetCachedSwitchValueInternal(string switchName, string envVa return isSwitchEnabled; } - private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue, bool defaultValue) - { - if (!AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled)) - { - isSwitchEnabled = defaultValue; - } - - AppContext.TryGetSwitch("TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching); - if (!disableCaching) - { - cachedSwitchValue = isSwitchEnabled ? 1 /*true*/ : -1 /*false*/; - } - - return isSwitchEnabled; - } - private static bool GetBooleanEnvironmentVariable(string envVariable) { string? str = Environment.GetEnvironmentVariable(envVariable); @@ -109,26 +81,5 @@ private static bool GetBooleanEnvironmentVariable(string envVariable) return false; } - - // Provides default values for switches if they're not always false by default - private static bool GetSwitchDefaultValue(string switchName) - { - if (switchName == "Switch.System.Runtime.Serialization.SerializationGuard") - { - return true; - } - - if (switchName == "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization") - { - return true; - } - - if (switchName == "System.Xml.XmlResolver.IsNetworkingEnabledByDefault") - { - return true; - } - - return false; - } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs index 7392f64bd801ed..f792fd1793f62b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs +++ b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs @@ -61,7 +61,7 @@ public static bool ForceInterpretedInvoke public static bool SerializationGuard { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => GetCachedSwitchValue("Switch.System.Runtime.Serialization.SerializationGuard", ref s_serializationGuard); + get => GetCachedSwitchValue("Switch.System.Runtime.Serialization.SerializationGuard", ref s_serializationGuard, defaultValue: true); } private static int s_showILOffset; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs b/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs index 13850120d21055..b41f05be289cce 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Core/LocalAppContextSwitches.cs @@ -76,7 +76,7 @@ public static bool IsNetworkingEnabledByDefault [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return SwitchesHelpers.GetCachedSwitchValue("System.Xml.XmlResolver.IsNetworkingEnabledByDefault", ref s_isNetworkingEnabledByDefault); + return SwitchesHelpers.GetCachedSwitchValue("System.Xml.XmlResolver.IsNetworkingEnabledByDefault", ref s_isNetworkingEnabledByDefault, defaultValue: true); } } diff --git a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/LocalAppContextSwitches.cs b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/LocalAppContextSwitches.cs index 169eb991b71d9c..7618044581e1fe 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/LocalAppContextSwitches.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/LocalAppContextSwitches.cs @@ -13,7 +13,7 @@ internal static partial class LocalAppContextSwitches public static bool BinaryFormatterEnabled { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => GetCachedSwitchValue("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", ref s_binaryFormatterEnabled); + get => GetCachedSwitchValue("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", ref s_binaryFormatterEnabled, defaultValue: true); } } } From 1cd5696ec64a6d7d40195f23edf0595d4a40f1f1 Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Tue, 10 Mar 2026 14:32:41 +0100 Subject: [PATCH 09/12] Merge GetCachedSwitchValueInternal overloads Use a single GetCachedSwitchValueInternal with a nullable envVariable parameter instead of two separate overloads. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../System/LocalAppContextSwitches.Common.cs | 43 ++++++------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs index f6f2c145e48c4c..77d658b12300a2 100644 --- a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs +++ b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs @@ -18,29 +18,28 @@ internal static bool GetSwitchValue(string switchName, ref bool switchValue) => [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue, bool defaultValue = false) { - // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false - if (cachedSwitchValue < 0) return false; - if (cachedSwitchValue > 0) return true; - - return GetCachedSwitchValueInternal(switchName, ref cachedSwitchValue, defaultValue); + GetCachedSwitchValueInternal(switchName, null, ref cachedSwitchValue, defaultValue); } // Returns value of given switch or environment variable using provided cache. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool GetCachedSwitchValue(string switchName, string envVariable, ref int cachedSwitchValue) + internal static bool GetCachedSwitchValue(string switchName, string? envVariable, ref int cachedSwitchValue) { // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false if (cachedSwitchValue < 0) return false; if (cachedSwitchValue > 0) return true; - return GetCachedSwitchValueInternal(switchName, envVariable, ref cachedSwitchValue); + return GetCachedSwitchValueInternal(switchName, envVariable, ref cachedSwitchValue, false); } - private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue, bool defaultValue) + private static bool GetCachedSwitchValueInternal(string switchName, string? envVariable, ref int cachedSwitchValue, bool defaultValue) { if (!AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled)) { - isSwitchEnabled = defaultValue; + if (envVariable == null || !TryGetBooleanEnvironmentVariable(envVariable, out isSwitchEnabled)) + { + isSwitchEnabled = defaultValue; + } } AppContext.TryGetSwitch("TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching); @@ -52,31 +51,13 @@ private static bool GetCachedSwitchValueInternal(string switchName, ref int cach return isSwitchEnabled; } - private static bool GetCachedSwitchValueInternal(string switchName, string envVariable, ref int cachedSwitchValue) + private static bool TryGetBooleanEnvironmentVariable(string envVariable, out bool value) { - if (!AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled)) + if (Environment.GetEnvironmentVariable(envVariable) is string str) { - isSwitchEnabled = GetBooleanEnvironmentVariable(envVariable); - } + value = str == "1" || string.Equals(str, bool.TrueString, StringComparison.OrdinalIgnoreCase); - AppContext.TryGetSwitch("TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching); - if (!disableCaching) - { - cachedSwitchValue = isSwitchEnabled ? 1 /*true*/ : -1 /*false*/; - } - - return isSwitchEnabled; - } - - private static bool GetBooleanEnvironmentVariable(string envVariable) - { - string? str = Environment.GetEnvironmentVariable(envVariable); - if (str is not null) - { - if (str == "1" || string.Equals(str, bool.TrueString, StringComparison.OrdinalIgnoreCase)) - { - return true; - } + return true; } return false; From f9eac54204ea94c169598ff51340950fcf6e8e25 Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Tue, 10 Mar 2026 14:48:15 +0100 Subject: [PATCH 10/12] Fix typo --- .../Common/src/System/LocalAppContextSwitches.Common.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs index 77d658b12300a2..ffefa18cb7abec 100644 --- a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs +++ b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs @@ -23,13 +23,13 @@ internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitc // Returns value of given switch or environment variable using provided cache. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool GetCachedSwitchValue(string switchName, string? envVariable, ref int cachedSwitchValue) + internal static bool GetCachedSwitchValue(string switchName, string? envVariable, ref int cachedSwitchValue, bool defaultValue = false) { // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false if (cachedSwitchValue < 0) return false; if (cachedSwitchValue > 0) return true; - return GetCachedSwitchValueInternal(switchName, envVariable, ref cachedSwitchValue, false); + return GetCachedSwitchValueInternal(switchName, envVariable, ref cachedSwitchValue, defaultValue); } private static bool GetCachedSwitchValueInternal(string switchName, string? envVariable, ref int cachedSwitchValue, bool defaultValue) From 0ada26e97ee02b03e8edb3d6775de5206a2b9a0e Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Tue, 10 Mar 2026 15:39:36 +0100 Subject: [PATCH 11/12] Fix missing return and fast-path in GetCachedSwitchValue overload Add missing return statement and fast-path cache check in the GetCachedSwitchValue overload without envVariable parameter. Also fix missing out parameter assignment in TryGetBooleanEnvironmentVariable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Common/src/System/LocalAppContextSwitches.Common.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs index ffefa18cb7abec..5e8e359d5a202c 100644 --- a/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs +++ b/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs @@ -18,7 +18,11 @@ internal static bool GetSwitchValue(string switchName, ref bool switchValue) => [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue, bool defaultValue = false) { - GetCachedSwitchValueInternal(switchName, null, ref cachedSwitchValue, defaultValue); + // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false + if (cachedSwitchValue < 0) return false; + if (cachedSwitchValue > 0) return true; + + return GetCachedSwitchValueInternal(switchName, null, ref cachedSwitchValue, defaultValue); } // Returns value of given switch or environment variable using provided cache. @@ -60,6 +64,7 @@ private static bool TryGetBooleanEnvironmentVariable(string envVariable, out boo return true; } + value = false; return false; } } From 2b1e9ef4bbf6adf11a7ba827f234eb657c1f65b9 Mon Sep 17 00:00:00 2001 From: Radek Zikmund Date: Tue, 10 Mar 2026 15:43:30 +0100 Subject: [PATCH 12/12] One more ifdef --- .../src/System/Net/Security/LocalAppContextSwitches.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs index 16e14fe7ee863a..369ece8ca60d0e 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/LocalAppContextSwitches.cs @@ -30,6 +30,7 @@ internal static bool EnableOcspStapling get => GetCachedSwitchValue("System.Net.Security.EnableServerOcspStaplingFromOnlyCertificateOnLinux", ref s_enableOcspStapling); } +#if !TARGET_WINDOWS private static int s_useManagedNtlm; [FeatureSwitchDefinition("System.Net.Security.UseManagedNtlm")] internal static bool UseManagedNtlm @@ -39,6 +40,7 @@ internal static bool UseManagedNtlm defaultValue: OperatingSystem.IsMacOS() || OperatingSystem.IsIOS() || OperatingSystem.IsMacCatalyst() || (OperatingSystem.IsLinux() && RuntimeInformation.RuntimeIdentifier.StartsWith("linux-bionic-", StringComparison.OrdinalIgnoreCase))); } +#endif #if TARGET_APPLE private static int s_useNetworkFramework;