File: CoordinatorServer.ConnectedClient.cs
Web Access
Project: src\msbuild\src\MSBuild.Coordinator\MSBuild.Coordinator.csproj (MSBuild.Coordinator)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;
using System.IO;
using System.IO.Pipes;
using Microsoft.Build.Framework.Coordinator;

namespace Microsoft.Build.Coordinator;

internal sealed partial class CoordinatorServer
{
    /// <summary>
    ///  Tracks an accepted MSBuild client alongside its <see cref="BuildGrant"/>.
    /// </summary>
    private sealed class ConnectedClient : IDisposable
    {
        private readonly NamedPipeServerStream _pipeStream;
        private readonly BinaryReader _reader;
        private readonly BinaryWriter _writer;

        /// <summary>
        ///  Gets the unique identifier for this connection, assigned by the client during handshake.
        /// </summary>
        public Guid ConnectionId { get; }

        /// <summary>
        ///  Gets the process ID of the connected MSBuild client.
        /// </summary>
        public int ProcessId { get; }

        /// <summary>
        ///  Gets the capabilities advertised by the client during handshake.
        /// </summary>
        public ImmutableArray<string> Capabilities { get; }

        /// <summary>
        ///  Gets the build grant associated with this client.
        /// </summary>
        public BuildGrant Grant { get; }

        /// <summary>
        ///  Gets a value indicating whether the pipe is still connected to the client.
        /// </summary>
        public bool IsConnected => _pipeStream.IsConnected;

        /// <summary>
        ///  Creates a connected client by taking ownership of a negotiated connection.
        /// </summary>
        public ConnectedClient(Connection connection, BuildGrant grant)
        {
            (_pipeStream, _reader, _writer) = connection.TransferOwnership();

            ConnectionId = connection.Id;
            ProcessId = connection.ProcessId;
            Capabilities = connection.ClientCapabilities;
            Grant = grant;
        }

        /// <summary>
        ///  Reads the next client message from this connected client.
        /// </summary>
        public ClientMessage ReadClientMessage()
            => _reader.ReadClientMessage();

        /// <summary>
        ///  Writes a server message to this connected client.
        /// </summary>
        public void WriteServerMessage(ServerMessage message)
            => _writer.Write(message);

        public void Dispose()
        {
            try
            {
                _writer.Dispose();
            }
            catch (IOException)
            {
                // The pipe may already be broken if the client disconnected.
            }

            _reader.Dispose();
            _pipeStream.Dispose();
        }
    }
}