diff --git a/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransactionFlow/WSHttpTransactionFlowTests.cs b/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransactionFlow/WSHttpTransactionFlowTests.cs index 7a27a00c760..54bc67777bd 100644 --- a/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransactionFlow/WSHttpTransactionFlowTests.cs +++ b/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransactionFlow/WSHttpTransactionFlowTests.cs @@ -452,7 +452,7 @@ public static void WSHttpBinding_TransactionFlow_Mandatory_RoundTrips() [WcfFact] [Condition(nameof(Windows_Authentication_Available), nameof(Skip_CoreWCFService_FailedTest))] [OuterLoop] - public static void WSHttpBinding_TransactionFlow_Mandatory_WithoutScope_Throws() + public static void WSHttpBinding_TransactionFlow_Mandatory_RoundTrips_And_WithoutScope_Throws() { ChannelFactory factory = null; IWcfTransactionMandatoryService serviceProxy = null; @@ -465,14 +465,28 @@ public static void WSHttpBinding_TransactionFlow_Mandatory_WithoutScope_Throws() factory = new ChannelFactory(binding, new EndpointAddress(Endpoints.WSHttpTransactionFlowMandatoryAddress)); serviceProxy = factory.CreateChannel(); - // *** EXECUTE & VALIDATE *** \\ - // Calling a Mandatory operation without an ambient transaction should throw. - // The client-side TransactionChannel enforces Mandatory by requiring a flowed transaction. + // *** EXECUTE & VALIDATE — negative case (client-side enforcement) *** \\ + // Calling a Mandatory operation without an ambient transaction must throw + // before any wire activity. The client-side TransactionChannel enforces + // [TransactionFlow(Mandatory)] by requiring a flowed transaction. Assert.ThrowsAny(() => { serviceProxy.IsTransactionFlowed(); }); + // *** EXECUTE & VALIDATE — positive case (true end-to-end) *** \\ + // With an ambient transaction, the call must succeed AND the server must + // see the flowed transaction. This portion requires real server support + // for TransactionFlow and exercises the Mandatory contract attribute on + // the wire. + bool flowed; + using (var scope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) + { + flowed = serviceProxy.IsTransactionFlowed(); + scope.Complete(); + } + Assert.True(flowed, "Expected the transaction to flow to the Mandatory service operation, but IsTransactionFlowed returned false."); + // *** CLEANUP *** \\ factory.Close(); ((ICommunicationObject)serviceProxy).Close(); diff --git a/src/System.Private.ServiceModel/tests/Scenarios/Client/ClientBase/ClientBaseTests.4.1.0.cs b/src/System.Private.ServiceModel/tests/Scenarios/Client/ClientBase/ClientBaseTests.4.1.0.cs index e03e55d9527..92b6bb02527 100644 --- a/src/System.Private.ServiceModel/tests/Scenarios/Client/ClientBase/ClientBaseTests.4.1.0.cs +++ b/src/System.Private.ServiceModel/tests/Scenarios/Client/ClientBase/ClientBaseTests.4.1.0.cs @@ -55,6 +55,11 @@ public static void DefaultSettings_Echo_Cookie() } [WcfFact] + // Flaky under CoreWCF on certain Linux distros (Fedora.41, Debian.12): + // the second POST over a keep-alive connection sometimes triggers a + // Kestrel pipe-writer race on the server side + // (System.InvalidOperationException: Writing is not allowed after writer + // was completed -> Connection reset by peer at the client). [Condition(nameof(Skip_CoreWCFService_FailedTest))] [OuterLoop] public static void DefaultSettings_SetCookieOnServerSide() diff --git a/src/System.Private.ServiceModel/tests/Scenarios/Extensibility/WebSockets/WebSocketTests.4.1.0.cs b/src/System.Private.ServiceModel/tests/Scenarios/Extensibility/WebSockets/WebSocketTests.4.1.0.cs index 6ec253ff7bb..20756d8a2e2 100644 --- a/src/System.Private.ServiceModel/tests/Scenarios/Extensibility/WebSockets/WebSocketTests.4.1.0.cs +++ b/src/System.Private.ServiceModel/tests/Scenarios/Extensibility/WebSockets/WebSocketTests.4.1.0.cs @@ -356,8 +356,7 @@ public static void WebSocket_Https_Duplex_Streamed(NetHttpMessageEncoding messag [InlineData(NetHttpMessageEncoding.Binary)] [InlineData(NetHttpMessageEncoding.Text)] [InlineData(NetHttpMessageEncoding.Mtom)] - [Condition(nameof(Root_Certificate_Installed), - nameof(Skip_CoreWCFService_FailedTest))] + [Condition(nameof(Root_Certificate_Installed))] [Issue(3572, OS = OSID.OSX)] [Issue(1438, OS = OSID.Windows_7)] // not supported on Win7 [OuterLoop] diff --git a/src/System.Private.ServiceModel/tests/Scenarios/Security/TransportSecurity/Tcp/ClientCredentialTypeCertificateCanonicalNameTests.4.1.0.cs b/src/System.Private.ServiceModel/tests/Scenarios/Security/TransportSecurity/Tcp/ClientCredentialTypeCertificateCanonicalNameTests.4.1.0.cs index 4c128c4bc3b..75c03a1dcdf 100644 --- a/src/System.Private.ServiceModel/tests/Scenarios/Security/TransportSecurity/Tcp/ClientCredentialTypeCertificateCanonicalNameTests.4.1.0.cs +++ b/src/System.Private.ServiceModel/tests/Scenarios/Security/TransportSecurity/Tcp/ClientCredentialTypeCertificateCanonicalNameTests.4.1.0.cs @@ -98,7 +98,7 @@ public static void Certificate_With_CanonicalName_Localhost_Address_EchoString() [WcfFact] [Issue(3572, OS = OSID.OSX)] - [Condition(nameof(Root_Certificate_Installed), nameof(Skip_CoreWCFService_FailedTest))] + [Condition(nameof(Root_Certificate_Installed))] [OuterLoop] public static void Certificate_With_CanonicalName_DomainName_Address_EchoString() { @@ -175,7 +175,7 @@ public static void Certificate_With_CanonicalName_DomainName_Address_EchoString( [WcfFact] [Issue(3572, OS = OSID.OSX)] - [Condition(nameof(Root_Certificate_Installed), nameof(Skip_CoreWCFService_FailedTest))] + [Condition(nameof(Root_Certificate_Installed))] [OuterLoop] public static void Certificate_With_CanonicalName_Fqdn_Address_EchoString() { diff --git a/src/System.Private.ServiceModel/tools/CertificateGenerator/CertificateGeneratorLibrary/CertificateGeneratorLibrary.cs b/src/System.Private.ServiceModel/tools/CertificateGenerator/CertificateGeneratorLibrary/CertificateGeneratorLibrary.cs index 09d1b13e7e1..1a6c6fde2f2 100644 --- a/src/System.Private.ServiceModel/tools/CertificateGenerator/CertificateGeneratorLibrary/CertificateGeneratorLibrary.cs +++ b/src/System.Private.ServiceModel/tools/CertificateGenerator/CertificateGeneratorLibrary/CertificateGeneratorLibrary.cs @@ -16,10 +16,48 @@ public class CertificateGeneratorLibrary private static string s_fqdn = Dns.GetHostEntry("127.0.0.1").HostName; private static string s_hostname = Dns.GetHostEntry("127.0.0.1").HostName.Split('.')[0]; + + // The SubjectCanonicalName{DomainName,Fqdn} certificates are used by tests that assert a + // NEGATIVE identity check: a client connecting to "localhost" must be rejected because the + // server certificate's CN is a different name. On Windows, Dns.GetHostEntry("127.0.0.1") + // returns the machine name, which satisfies this. On Linux/macOS it returns "localhost", + // making those certs indistinguishable from the localhost cert and breaking the tests. + // Resolve a non-localhost canonical name from the machine name on non-Windows platforms. + // These values are used ONLY for the canonical-name certs; the CRL URI and all other certs + // continue to use s_fqdn/s_hostname so localhost-based CRL fetch and TLS keep working. + private static string s_canonicalFqdn = GetCanonicalFqdn(); + private static string s_canonicalHostname = s_canonicalFqdn.Split('.')[0]; + private static string s_testserverbase = string.Empty; private static string s_crlFileLocation = string.Empty; private static TimeSpan s_validatePeriod; + private static string GetCanonicalFqdn() + { + if (CertificateHelper.CurrentOperatingSystem.IsWindows()) + { + return Dns.GetHostEntry("127.0.0.1").HostName; + } + + // On Linux/macOS, "127.0.0.1" resolves to "localhost"; use the machine name instead so + // that the canonical-name certificates carry a name distinct from "localhost". + try + { + string resolved = Dns.GetHostEntry(Environment.MachineName).HostName; + if (!string.IsNullOrEmpty(resolved) && + !string.Equals(resolved, "localhost", StringComparison.OrdinalIgnoreCase)) + { + return resolved; + } + } + catch + { + // Name may not be resolvable in some sandboxes; fall back to the raw machine name below. + } + + return Environment.MachineName; + } + private static void RemoveCertificatesFromStore(StoreName storeName, StoreLocation storeLocation) { X509Store store = CertificateHelper.GetX509Store(storeName, storeLocation); @@ -104,7 +142,7 @@ public static int SetupCerts(string testserverbase, TimeSpan validatePeriod, str certificateCreationSettings = new CertificateCreationSettings() { FriendlyName = "WCF Bridge - TcpCertificateWithSubjectCanonicalNameDomainNameResource", - Subject = s_hostname, + Subject = s_canonicalHostname, SubjectAlternativeNames = new string[0], ValidityType = CertificateValidityType.NonAuthoritativeForMachine }; @@ -114,7 +152,7 @@ public static int SetupCerts(string testserverbase, TimeSpan validatePeriod, str certificateCreationSettings = new CertificateCreationSettings() { FriendlyName = "WCF Bridge - TcpCertificateWithSubjectCanonicalNameFqdnResource", - Subject = s_fqdn, + Subject = s_canonicalFqdn, SubjectAlternativeNames = new string[0], ValidityType = CertificateValidityType.NonAuthoritativeForMachine };