File: Projects\AppHostServerSession.cs
Web Access
Project: src\src\Aspire.Cli\Aspire.Cli.Tool.csproj (aspire)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using Aspire.Cli.Utils;
using Microsoft.Extensions.Logging;
 
namespace Aspire.Cli.Projects;
 
/// <summary>
/// Implementation of <see cref="IAppHostServerSession"/> that manages an AppHost server process.
/// </summary>
internal sealed class AppHostServerSession : IAppHostServerSession
{
    private readonly ILogger _logger;
    private readonly Process _serverProcess;
    private readonly OutputCollector _output;
    private readonly string _socketPath;
    private IAppHostRpcClient? _rpcClient;
    private bool _disposed;
 
    internal AppHostServerSession(
        Process serverProcess,
        OutputCollector output,
        string socketPath,
        ILogger logger)
    {
        _serverProcess = serverProcess;
        _output = output;
        _socketPath = socketPath;
        _logger = logger;
    }
 
    /// <inheritdoc />
    public string SocketPath => _socketPath;
 
    /// <inheritdoc />
    public Process ServerProcess => _serverProcess;
 
    /// <inheritdoc />
    public OutputCollector Output => _output;
 
    /// <inheritdoc />
    public async Task<IAppHostRpcClient> GetRpcClientAsync(CancellationToken cancellationToken)
    {
        ObjectDisposedException.ThrowIf(_disposed, nameof(AppHostServerSession));
 
        return _rpcClient ??= await AppHostRpcClient.ConnectAsync(_socketPath, cancellationToken);
    }
 
    /// <inheritdoc />
    public async ValueTask DisposeAsync()
    {
        if (_disposed)
        {
            return;
        }
 
        _disposed = true;
 
        if (_rpcClient != null)
        {
            await _rpcClient.DisposeAsync();
            _rpcClient = null;
        }
 
        if (!_serverProcess.HasExited)
        {
            try
            {
                _serverProcess.Kill(entireProcessTree: true);
            }
            catch (Exception ex)
            {
                _logger.LogDebug(ex, "Error killing AppHost server process");
            }
        }
 
        _serverProcess.Dispose();
    }
}
 
/// <summary>
/// Factory for creating <see cref="IAppHostServerSession"/> instances.
/// </summary>
internal sealed class AppHostServerSessionFactory : IAppHostServerSessionFactory
{
    private readonly IAppHostServerProjectFactory _projectFactory;
    private readonly ILogger<AppHostServerSession> _logger;
 
    public AppHostServerSessionFactory(
        IAppHostServerProjectFactory projectFactory,
        ILogger<AppHostServerSession> logger)
    {
        _projectFactory = projectFactory;
        _logger = logger;
    }
 
    /// <inheritdoc />
    public async Task<AppHostServerSessionResult> CreateAsync(
        string appHostPath,
        string sdkVersion,
        IEnumerable<(string PackageId, string Version)> packages,
        Dictionary<string, string>? launchSettingsEnvVars,
        bool debug,
        CancellationToken cancellationToken)
    {
        var appHostServerProject = _projectFactory.Create(appHostPath);
        var socketPath = appHostServerProject.GetSocketPath();
 
        // Create project files and get channel info
        var (_, channelName) = await appHostServerProject.CreateProjectFilesAsync(sdkVersion, packages, cancellationToken);
 
        // Build the project
        var (buildSuccess, buildOutput) = await appHostServerProject.BuildAsync(cancellationToken);
        if (!buildSuccess)
        {
            return new AppHostServerSessionResult(
                Success: false,
                Session: null,
                BuildOutput: buildOutput,
                ChannelName: channelName);
        }
 
        // Start the server process
        var currentPid = Environment.ProcessId;
        var (serverProcess, serverOutput) = appHostServerProject.Run(
            socketPath,
            currentPid,
            launchSettingsEnvVars,
            debug: debug);
 
        // Create the session
        var session = new AppHostServerSession(
            serverProcess,
            serverOutput,
            socketPath,
            _logger);
 
        return new AppHostServerSessionResult(
            Success: true,
            Session: session,
            BuildOutput: buildOutput,
            ChannelName: channelName);
    }
}