Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
35 changes: 0 additions & 35 deletions src/libraries/Common/src/System/AppContextSwitchHelper.cs

This file was deleted.

41 changes: 23 additions & 18 deletions src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,34 @@ 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, null, ref cachedSwitchValue, defaultValue);
}

private static bool GetCachedSwitchValueInternal(string switchName, ref int 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, 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, defaultValue);
}

private static bool GetCachedSwitchValueInternal(string switchName, string? envVariable, 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);
if (envVariable == null || !TryGetBooleanEnvironmentVariable(envVariable, out isSwitchEnabled))
{
isSwitchEnabled = defaultValue;
}
}

AppContext.TryGetSwitch("TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching);
Expand All @@ -42,24 +55,16 @@ private static bool GetCachedSwitchValueInternal(string switchName, ref int cach
return isSwitchEnabled;
}

// Provides default values for switches if they're not always false by default
private static bool GetSwitchDefaultValue(string switchName)
private static bool TryGetBooleanEnvironmentVariable(string envVariable, out bool value)
{
if (switchName == "Switch.System.Runtime.Serialization.SerializationGuard")
if (Environment.GetEnvironmentVariable(envVariable) is string str)
{
return true;
}
value = str == "1" || string.Equals(str, bool.TrueString, StringComparison.OrdinalIgnoreCase);

if (switchName == "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization")
{
return true;
}

if (switchName == "System.Xml.XmlResolver.IsNetworkingEnabledByDefault")
{
return true;
}
Comment on lines +58 to 65

value = false;
return false;
}
}
Expand Down
28 changes: 28 additions & 0 deletions src/libraries/Common/src/System/Net/LocalAppContextSwitches.Net.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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_disableIPv6;
internal static bool DisableIPv6
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue("System.Net.DisableIPv6", "DOTNET_SYSTEM_NET_DISABLEIPV6", ref s_disableIPv6);
}
Comment on lines +10 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're changing things, can we use a pattern more like this?

public static bool DisableIPv6 { get; } = GetSwitchValue("System.Net.DisableIPv6", "DOTNET_SYSTEM_NET_DISABLEIPV6");

This way the JIT can see that the value can't change, and completely remove unreachable branches when tiering up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern defeats the purpose of TestSwitch.LocalAppContext.DisableCaching that allows caching to be disabled and turn the appcontext switch on/off without restarting the process.

Also, this pattern is not friendly for startup and trimming: It will read all switches eagerly, even if they are not used by the app. This startup performance hit may be acceptable for System.Net.*, but we would not want to spread it everywhere (CoreLib in particular).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern defeats the purpose of TestSwitch.LocalAppContext.DisableCaching

I don't believe we expose a similar option for any existing networking switches currently, was your intention to start doing so now @rzikm?

My preference would be to use the same approach as with our HTTP switches here.

Copy link
Member

@jkotas jkotas Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If DisableCaching does not make sense for networking switches, you probably do not want to use the shared AppContext switches infrastructure.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main intent was to deduplicate all the places across networking where we implemented the AppCtx query + envvar fallback, the LocalAppContextSwitches helper came up in code search and it seemed a good idea to use what the rest of the libraries are using for consistency.

TestSwitch.LocalAppContext.DisableCaching support is just a welcome side effect, I guess.


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
}
}
}
6 changes: 1 addition & 5 deletions src/libraries/Common/src/System/Net/Security/SslKeyLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ static SslKeyLogger()

try
{
#if DEBUG
bool isEnabled = true;
#else
bool isEnabled = AppContext.TryGetSwitch("System.Net.EnableSslKeyLogging", out bool enabled) && enabled;
#endif
bool isEnabled = LocalAppContextSwitches.EnableSslKeyLogging;

if (isEnabled && s_keyLogFile != null)
{
Expand Down
24 changes: 1 addition & 23 deletions src/libraries/Common/src/System/Net/SocketProtocolSupportPal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +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()
{
// 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ System.Net.Http.WinHttpHandler</PackageDescription>
<Compile Include="System\Net\Http\WinHttpTraceHelper.cs" />
<Compile Include="System\Net\Http\WinHttpTrailersHelper.cs" />
<Compile Include="System\Net\Http\WinHttpTransportContext.cs" />
<Compile Include="$(CommonPath)System\AppContextSwitchHelper.cs"
Link="Common\System\AppContextSwitchHelper.cs" />
<Compile Include="$(CommonPath)System\IO\StreamHelpers.CopyValidation.cs"
Link="Common\System\IO\StreamHelpers.CopyValidation.cs" />
<Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
Link="Common\Interop\Windows\WinHttp\Interop.SafeWinHttpHandle.cs" />
<Compile Include="$(CommonPath)Interop\Windows\WinHttp\Interop.winhttp_types.cs"
Link="Common\Interop\Windows\WinHttp\Interop.winhttp_types.cs" />
<Compile Include="$(CommonPath)System\AppContextSwitchHelper.cs"
Link="Common\System\AppContextSwitchHelper.cs" />
<Compile Include="$(CommonPath)System\CharArrayHelpers.cs"
Link="Common\System\CharArrayHelpers.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs"
Expand Down
5 changes: 3 additions & 2 deletions src/libraries/System.Net.Http/src/System.Net.Http.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@
Link="Common\System\Text\SimpleRegex.cs" />
<Compile Include="$(CommonPath)System\HexConverter.cs"
Link="Common\System\HexConverter.cs" />
<Compile Include="$(CommonPath)System\NullableBool.cs"
Link="Common\System\NullableBool.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="Common\System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)System\Net\ArrayBuffer.cs"
Link="Common\System\Net\ArrayBuffer.cs"/>
<Compile Include="$(CommonPath)System\Net\MultiArrayBuffer.cs"
Expand Down Expand Up @@ -181,6 +181,7 @@
<Compile Include="System\Net\Http\SocketsHttpHandler\AuthenticationHelper.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\AuthenticationHelper.Digest.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\AuthenticationHelper.NtAuth.cs" />
<Compile Include="System\Net\Http\LocalAppContextSwitches.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\ChunkedEncodingReadStream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\ChunkedEncodingWriteStream.cs" />
<Compile Include="System\Net\Http\SocketsHttpHandler\ConnectHelper.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +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 NullableBool s_usePortInSpn;

private static bool UsePortInSpn
{
get
{
NullableBool usePortInSpn = s_usePortInSpn;
if (usePortInSpn != NullableBool.Undefined)
{
return usePortInSpn == NullableBool.True;
}

// First check for the AppContext switch, giving it priority over the environment variable.
if (AppContext.TryGetSwitch(UsePortInSpnCtxSwitch, out bool value))
{
s_usePortInSpn = value ? NullableBool.True : NullableBool.False;
}
else
{
// AppContext switch wasn't used. Check the environment variable.
s_usePortInSpn =
Environment.GetEnvironmentVariable(UsePortInSpnEnvironmentVariable) is string envVar &&
(envVar == "1" || envVar.Equals("true", StringComparison.OrdinalIgnoreCase)) ? NullableBool.True : NullableBool.False;
}

return s_usePortInSpn == NullableBool.True;
}
}
private static bool UsePortInSpn => LocalAppContextSwitches.UsePortInSpn;

private static Task<HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool async, bool isProxyAuth, HttpConnectionPool pool, HttpConnection connection, CancellationToken cancellationToken)
{
Comment on lines +19 to 22
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@
<Compile Include="System\Net\NetEventSource.HttpListener.cs" />
<Compile Include="System\Net\WebSockets\HttpListenerWebSocketContext.cs" />
<Compile Include="System\Net\WebSockets\HttpWebSocket.cs" />
<Compile Include="System\Net\LocalAppContextSwitches.cs" />
<Compile Include="$(CommonPath)DisableRuntimeMarshalling.cs"
Link="Common\DisableRuntimeMarshalling.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="Common\System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.cs"
Link="Common\System\Net\Logging\NetEventSource.Common.cs" />
<Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.Associate.cs"
Expand Down
Original file line number Diff line number Diff line change
@@ -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_enableKernelResponseBuffering;
internal static bool EnableKernelResponseBuffering
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue("System.Net.HttpListener.EnableKernelResponseBuffering", ref s_enableKernelResponseBuffering);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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; } = AppContext.TryGetSwitch("System.Net.HttpListener.EnableKernelResponseBuffering", out bool enabled) && enabled;
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
Link="Common\System\Net\Logging\NetEventSource.Common.cs" />
<Compile Include="$(CommonPath)System\Net\InternalException.cs"
Link="Common\System\Net\InternalException.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="Common\System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)System\Net\LocalAppContextSwitches.Net.cs"
Link="Common\System\Net\LocalAppContextSwitches.Net.cs" />
<!-- System.Net common -->
<Compile Include="$(CommonPath)System\Net\IPAddressParserStatics.cs"
Link="Common\System\Net\IPAddressParserStatics.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
Link="Common\System\Net\IPEndPointStatics.cs" />
<Compile Include="$(CommonPath)System\Net\IPAddressParserStatics.cs"
Link="Common\System\Net\IPAddressParserStatics.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="Common\System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)System\Net\LocalAppContextSwitches.Net.cs"
Link="Common\System\Net\LocalAppContextSwitches.Net.cs" />
<Compile Include="$(CommonPath)System\Net\SocketProtocolSupportPal.cs"
Link="Common\System\Net\SocketProtocolSupportPal.cs" />
<Compile Include="$(CommonTestPath)System\Net\Configuration.cs"
Expand Down
4 changes: 4 additions & 0 deletions src/libraries/System.Net.Ping/src/System.Net.Ping.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
Link="Common\DisableRuntimeMarshalling.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs"
Link="Common\System\Obsoletions.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs"
Link="Common\System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)System\Net\LocalAppContextSwitches.Net.cs"
Link="Common\System\Net\LocalAppContextSwitches.Net.cs" />
<!-- System.Net Common -->
<Compile Include="$(CommonPath)System\Net\SocketProtocolSupportPal.cs"
Link="Common\System\Net\SocketProtocolSupportPal.cs" />
Expand Down
3 changes: 2 additions & 1 deletion src/libraries/System.Net.Quic/src/System.Net.Quic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
<Compile Include="System\Net\Quic\**\*.cs" Exclude="System\Net\Quic\*.Unsupported.cs"/>
<!-- System.Net common -->
<Compile Include="$(CommonPath)DisableRuntimeMarshalling.cs" Link="Common\DisableRuntimeMarshalling.cs" />
<Compile Include="$(CommonPath)System\AppContextSwitchHelper.cs" Link="Common\System\AppContextSwitchHelper.cs" />
<Compile Include="$(CommonPath)System\LocalAppContextSwitches.Common.cs" Link="Common\System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(CommonPath)System\Net\LocalAppContextSwitches.Net.cs" Link="Common\System\Net\LocalAppContextSwitches.Net.cs" />
<Compile Include="$(CommonPath)System\Net\SafeHandleCache.cs" Link="Common\System\Net\SafeHandleCache.cs" />
<Compile Include="$(CommonPath)System\Net\ArrayBuffer.cs" Link="Common\System\Net\ArrayBuffer.cs" />
<Compile Include="$(CommonPath)System\Net\MultiArrayBuffer.cs" Link="Common\System\Net\MultiArrayBuffer.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 => !LocalAppContextSwitches.DisableConfigurationCache;

private static readonly MsQuicConfigurationCache s_configurationCache = new MsQuicConfigurationCache();

Expand Down
Loading
Loading