File: UserAgentStringBuilder.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Protocol\NuGet.Protocol.csproj (NuGet.Protocol)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#if NETCOREAPP
using System;
#endif
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
using NuGet.Common;
using NuGet.Packaging;

namespace NuGet.Protocol.Core.Types
{
    public class UserAgentStringBuilder
    {
        public static readonly string DefaultNuGetClientName = "NuGet Client V3";

        private const string UserAgentWithMetadataTemplate = "{0}/{1} ({2})";
        private const string UserAgentTemplate = "{0}/{1}";

        private readonly string _clientName;
        private string? _vsInfo;
        private string? _osInfo;
        private string? _ciInfo;

        public UserAgentStringBuilder()
            : this(DefaultNuGetClientName)
        {
        }

        public UserAgentStringBuilder(string clientName)
            : this(clientName, EnvironmentVariableWrapper.Instance)
        {
        }

        /// <summary>
        /// Internal constructor for testing purposes that allows injecting an environment variable reader.
        /// </summary>
        /// <param name="clientName">The client name to use in the user agent string.</param>
        /// <param name="environmentVariableReader">The environment variable reader for CI detection.</param>
        internal UserAgentStringBuilder(string clientName, IEnvironmentVariableReader environmentVariableReader)
        {
            _clientName = clientName;

            // Read the client version from the assembly metadata and normalize it.
            NuGetClientVersion = MinClientVersionUtility.GetNuGetClientVersion().ToNormalizedString();

            _osInfo = GetOS();
            _ciInfo = CIEnvironmentDetector.Detect(environmentVariableReader);
        }

        public string NuGetClientVersion { get; }

        public UserAgentStringBuilder WithVisualStudioSKU(string? vsInfo)
        {
            _vsInfo = vsInfo;
            return this;
        }

        public string Build()
        {
            var clientInfo = _clientName;
            if (NuGetTestMode.Enabled)
            {
                clientInfo = NuGetTestMode.NuGetTestClientName;
            }
            else if (string.IsNullOrEmpty(clientInfo))
            {
                clientInfo = DefaultNuGetClientName;
            }

            string metadataString = BuildMetadataString();

            if (string.IsNullOrEmpty(metadataString))
            {
                return string.Format(
                    CultureInfo.InvariantCulture,
                    UserAgentTemplate,
                    clientInfo,
                    NuGetClientVersion);
            }

            return string.Format(
                CultureInfo.InvariantCulture,
                UserAgentWithMetadataTemplate,
                clientInfo,
                NuGetClientVersion,
                metadataString);
        }

        /// <summary>
        /// Builds the metadata string for the parentheses section.
        /// Items are collected in order (OS, CI, VS) and joined with ", ".
        /// </summary>
        internal string BuildMetadataString()
        {
            var sb = new StringBuilder();

            // OS info
            if (!string.IsNullOrEmpty(_osInfo))
            {
                sb.Append(_osInfo);
            }

            // CI info (formatted as "CI: {provider}")
            if (!string.IsNullOrEmpty(_ciInfo))
            {
                if (sb.Length > 0) sb.Append(", ");
                sb.Append("CI: ").Append(_ciInfo);
            }

            // VS info
            if (!string.IsNullOrEmpty(_vsInfo))
            {
                if (sb.Length > 0) sb.Append(", ");
                sb.Append(_vsInfo);
            }

            return sb.ToString();
        }

        internal static string GetOS()
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return OSPlatform.Windows.ToString();
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                return OSPlatform.Linux.ToString();
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                return OSPlatform.OSX.ToString();
            }
#if NETCOREAPP
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD))
            {
                return OSPlatform.FreeBSD.ToString();
            }
            else if (OperatingSystem.IsBrowser())
            {
                return "BROWSER";
            }
#endif
            else
            {
                return "UnknownOS";
            }
        }
    }
}