File: NamedPipeConnectionTests.cs
Web Access
Project: ..\..\..\src\System.Private.ServiceModel\tests\Scenarios\Client\ExpectedExceptions\Client.ExpectedExceptions.IntegrationTests.csproj (Client.ExpectedExceptions.IntegrationTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Linq;
using System.Runtime.Versioning;
using System.ServiceModel;
using System.Threading;
using System.Threading.Tasks;
using CoreWCF.Configuration;
using Infrastructure.Common;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xunit;
using Xunit.Abstractions;
 
[SupportedOSPlatform("windows")]
public class NamedPipeConnectionTests : ConditionalWcfTest
{
    private readonly ITestOutputHelper _output;
 
    public NamedPipeConnectionTests(ITestOutputHelper output) => _output = output;
 
    [WcfFact]
    [Condition(nameof(Is_Windows))]
    [OuterLoop]
    public async Task HighConcurrency_Connections_RetryUntilAvailable()
    {
        const int ConnectionCount = 100;
        TimeSpan timeout = TimeSpan.FromSeconds(30);
        Uri address = new Uri($"net.pipe://localhost/NamedPipeConnectionTests/{Guid.NewGuid():N}/");
        using IHost host = new HostBuilder()
            .UseNetNamedPipe(options => options.Listen(address, listenOptions => listenOptions.MaxPendingAccepts = 1))
            .ConfigureServices(services => services.AddServiceModelServices())
            .Build();
        host.UseServiceModel(services =>
        {
            services.AddService<EchoService>();
            services.AddServiceEndpoint<EchoService, IEchoService>(
                new CoreWCF.NetNamedPipeBinding(CoreWCF.NetNamedPipeSecurityMode.None), address.AbsoluteUri);
        });
        using CancellationTokenSource startupTimeout = new CancellationTokenSource(timeout);
        await host.StartAsync(startupTimeout.Token);
 
        var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None)
        {
            OpenTimeout = timeout,
            SendTimeout = timeout
        };
        var factory = new ChannelFactory<IEchoService>(binding, new EndpointAddress(address));
        IEchoService[] proxies = new IEchoService[ConnectionCount];
        IClientChannel[] channels = new IClientChannel[ConnectionCount];
        Task[] opens = new Task[ConnectionCount];
        using CountdownEvent ready = new CountdownEvent(ConnectionCount);
        using ManualResetEventSlim start = new ManualResetEventSlim();
 
        try
        {
            factory.Open();
            for (int i = 0; i < ConnectionCount; i++)
            {
                proxies[i] = factory.CreateChannel();
                IClientChannel channel = (IClientChannel)proxies[i];
                channels[i] = channel;
 
                // Dedicated workers create a connection burst without starving CoreWCF's accept loop.
                opens[i] = Task.Factory.StartNew(() =>
                {
                    ready.Signal();
                    Assert.True(start.Wait(timeout), "The connection burst was not released.");
                    channel.Open();
                }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
            }
 
            Assert.True(ready.Wait(timeout), "The connection workers did not become ready.");
            start.Set();
            Exception failure = await Record.ExceptionAsync(() => Task.WhenAll(opens));
            int established = channels.Count(channel => channel.State == CommunicationState.Opened);
            int faulted = channels.Count(channel => channel.State == CommunicationState.Faulted);
            _output.WriteLine($"Established: {established}/{ConnectionCount}; faulted: {faulted}/{ConnectionCount}.");
            Assert.True(failure == null, $"Established: {established}/{ConnectionCount}; faulted: {faulted}/{ConnectionCount}. First failure: {failure}");
            Assert.Equal(ConnectionCount, established);
            Assert.All(proxies, proxy => Assert.Equal("Hello", proxy.Echo("Hello")));
        }
        finally
        {
            start.Set();
            // Drain workers even if setup or the readiness assertion failed.
            Exception openFailure = await Record.ExceptionAsync(() => Task.WhenAll(opens.Where(task => task != null)));
            if (openFailure != null)
            {
                _output.WriteLine($"Channel opening failed: {openFailure}");
            }
 
            for (int i = 0; i < ConnectionCount; i++)
            {
                ScenarioTestHelpers.CloseCommunicationObjects(channels[i]);
            }
 
            ScenarioTestHelpers.CloseCommunicationObjects(factory);
            await host.StopAsync(timeout);
        }
    }
 
    [ServiceContract]
    [CoreWCF.ServiceContract]
    public interface IEchoService
    {
        [OperationContract]
        [CoreWCF.OperationContract]
        string Echo(string message);
    }
 
    public class EchoService : IEchoService
    {
        public string Echo(string message) => message;
    }
}