diff --git a/src/libraries/Common/src/System/Net/Security/SslClientAuthenticationOptionsExtensions.cs b/src/libraries/Common/src/System/Net/Security/SslClientAuthenticationOptionsExtensions.cs index 337fff63df324..d1ad29435f9be 100644 --- a/src/libraries/Common/src/System/Net/Security/SslClientAuthenticationOptionsExtensions.cs +++ b/src/libraries/Common/src/System/Net/Security/SslClientAuthenticationOptionsExtensions.cs @@ -14,6 +14,7 @@ internal static class SslClientAuthenticationOptionsExtensions { public static SslClientAuthenticationOptions ShallowClone(this SslClientAuthenticationOptions options) { + // Use non-default values to verify the clone works fine. var clone = new SslClientAuthenticationOptions() { AllowRenegotiation = options.AllowRenegotiation, @@ -33,7 +34,10 @@ public static SslClientAuthenticationOptions ShallowClone(this SslClientAuthenti #if DEBUG // Try to detect if a property gets added that we're not copying correctly. - foreach (PropertyInfo pi in typeof(SslClientAuthenticationOptions).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + // The property count is guard for new properties that also needs to be added above. + PropertyInfo[] properties = typeof(SslClientAuthenticationOptions).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)!; + Debug.Assert(properties.Length == 13); + foreach (PropertyInfo pi in properties) { object? origValue = pi.GetValue(options); object? cloneValue = pi.GetValue(clone); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslAuthenticationOptionsTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslAuthenticationOptionsTest.cs index b026fc03b04a2..a086f6f123b81 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslAuthenticationOptionsTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslAuthenticationOptionsTest.cs @@ -144,6 +144,33 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( Assert.Equal(string.Empty, server.TargetHostName); } } + + [Fact] + public void ClientOptions_ShallowCopy_OK() + { + using X509Certificate2 clientCert = Configuration.Certificates.GetClientCertificate(); + + // needs to non-default values so we can verify it was copied correctly. + var clientOptions = new SslClientAuthenticationOptions + { + AllowRenegotiation = false, + AllowTlsResume = false, + ApplicationProtocols = new List { SslApplicationProtocol.Http11, SslApplicationProtocol.Http2 }, + CertificateRevocationCheckMode = X509RevocationMode.Online, + ClientCertificates = new X509CertificateCollection() { clientCert }, + EnabledSslProtocols = SslProtocols.Tls12, + EncryptionPolicy = EncryptionPolicy.RequireEncryption, + TargetHost = "foo", + CertificateChainPolicy = new X509ChainPolicy(), + RemoteCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; }), + LocalCertificateSelectionCallback = new LocalCertificateSelectionCallback(delegate { return null; }), + ClientCertificateContext = SslStreamCertificateContext.Create(clientCert, null, false), + }; + + // There is consistency check inside of the ShallowClone + _ = clientOptions.ShallowClone(); + } + } public sealed class SslClientAuthenticationOptionsTestBase_Sync : SslClientAuthenticationOptionsTestBase diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index b72ebc87fa38a..87685eba5598f 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -99,6 +99,8 @@ Link="ProductionCode\Common\System\Net\MultiArrayBuffer.cs" /> +