File: DiagnosticsClient\DiagnosticsClientConnector.cs
Web Access
Project: src\src\diagnostics\src\Microsoft.Diagnostics.NETCore.Client\Microsoft.Diagnostics.NETCore.Client.csproj (Microsoft.Diagnostics.NETCore.Client)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Diagnostics.NETCore.Client
{
    /// <summary>
    /// A connector that allows to create a <see cref="DiagnosticsClient"/> from a diagnostic port.
    /// </summary>
    public sealed class DiagnosticsClientConnector : IAsyncDisposable
    {
        private bool _disposed;
        private readonly IAsyncDisposable _server;

        internal DiagnosticsClientConnector(DiagnosticsClient diagnosticClient, IAsyncDisposable server)
        {
            _server = server;
            Instance = diagnosticClient;
        }

        /// <summary>
        /// Gets the <see cref="DiagnosticsClient"/> instance.
        /// </summary>
        public DiagnosticsClient Instance { get; }

        /// <inheritdoc />
        public async ValueTask DisposeAsync()
        {
            if (_disposed)
            {
                return;
            }

            if (_server != null)
            {
                await _server.DisposeAsync().ConfigureAwait(false);
            }

            _disposed = true;
        }

        /// <summary>
        /// Create a new <see cref="DiagnosticsClientConnector"/> instance using the specified diagnostic port.
        /// </summary>
        /// <param name="diagnosticPort">The diagnostic port.</param>
        /// <param name="ct">The token to monitor for cancellation requests.</param>
        /// <returns>A <see cref="DiagnosticsClientConnector"/> instance</returns>
        public static async Task<DiagnosticsClientConnector> FromDiagnosticPort(string diagnosticPort, CancellationToken ct)
        {
            if (diagnosticPort is null)
            {
                throw new ArgumentNullException(nameof(diagnosticPort));
            }

            IpcEndpointConfig portConfig = IpcEndpointConfig.Parse(diagnosticPort);

            if (portConfig.IsListenConfig)
            {
                string fullPort = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? portConfig.Address : Path.GetFullPath(portConfig.Address);
                ReversedDiagnosticsServer server = new(fullPort);
                server.Start();

                try
                {
                    IpcEndpointInfo endpointInfo = await server.AcceptAsync(ct).ConfigureAwait(false);
                    return new DiagnosticsClientConnector(new DiagnosticsClient(endpointInfo.Endpoint), server);
                }
                catch (TaskCanceledException)
                {
                    //clean up the server
                    await server.DisposeAsync().ConfigureAwait(false);
                    if (!ct.IsCancellationRequested)
                    {
                        throw;
                    }
                    return null;
                }
            }

            Debug.Assert(portConfig.IsConnectConfig);
            return new DiagnosticsClientConnector(new DiagnosticsClient(portConfig), null);
        }
    }
}