Provide runtime/framework info in gRPC C# user agent string (#25889)
* remove dead code (followup for #25717) * add more details into grpc-csharp user agent string * address review commentspull/26021/head
parent
146e285696
commit
492e72a318
6 changed files with 240 additions and 29 deletions
@ -0,0 +1,63 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2021 The gRPC Authors |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System; |
||||
using Grpc.Core; |
||||
using Grpc.Core.Internal; |
||||
using Grpc.Core.Utils; |
||||
using NUnit.Framework; |
||||
|
||||
namespace Grpc.Core.Internal.Tests |
||||
{ |
||||
public class UserAgentStringProviderTest |
||||
{ |
||||
[Test] |
||||
public void BasicTest() |
||||
{ |
||||
Assert.AreEqual("grpc-csharp/1.0 (.NET Framework 4.6.1; CLR 1.2.3.4; net45; x64)", |
||||
new UserAgentStringProvider("1.0", ".NET Framework 4.6.1", "1.2.3.4", "net45", CommonPlatformDetection.CpuArchitecture.X64).GrpcCsharpUserAgentString); |
||||
Assert.AreEqual("grpc-csharp/1.0 (CLR 1.2.3.4; net45; x64)", |
||||
new UserAgentStringProvider("1.0", null, "1.2.3.4", "net45", CommonPlatformDetection.CpuArchitecture.X64).GrpcCsharpUserAgentString); |
||||
Assert.AreEqual("grpc-csharp/1.0 (.NET Framework 4.6.1; net45; x64)", |
||||
new UserAgentStringProvider("1.0", ".NET Framework 4.6.1", null, "net45", CommonPlatformDetection.CpuArchitecture.X64).GrpcCsharpUserAgentString); |
||||
Assert.AreEqual("grpc-csharp/1.0 (.NET Framework 4.6.1; CLR 1.2.3.4; x64)", |
||||
new UserAgentStringProvider("1.0", ".NET Framework 4.6.1", "1.2.3.4", null, CommonPlatformDetection.CpuArchitecture.X64).GrpcCsharpUserAgentString); |
||||
} |
||||
|
||||
[Test] |
||||
public void ArchitectureTest() |
||||
{ |
||||
Assert.AreEqual("grpc-csharp/1.0 (.NET Framework 4.6.1; CLR 1.2.3.4; net45; arm64)", |
||||
new UserAgentStringProvider("1.0", ".NET Framework 4.6.1", "1.2.3.4", "net45", CommonPlatformDetection.CpuArchitecture.Arm64).GrpcCsharpUserAgentString); |
||||
|
||||
// unknown architecture |
||||
Assert.AreEqual("grpc-csharp/1.0 (.NET Framework 4.6.1; CLR 1.2.3.4; net45)", |
||||
new UserAgentStringProvider("1.0", ".NET Framework 4.6.1", "1.2.3.4", "net45", CommonPlatformDetection.CpuArchitecture.Unknown).GrpcCsharpUserAgentString); |
||||
} |
||||
|
||||
[Test] |
||||
public void FrameworkDescriptionTest() |
||||
{ |
||||
Assert.AreEqual("grpc-csharp/1.0 (Mono 6.12.0.93; x64)", |
||||
new UserAgentStringProvider("1.0", "Mono 6.12.0.93 (2020-02/620cf538206 Tue Aug 25 14:04:52 EDT 2020)", null, null, CommonPlatformDetection.CpuArchitecture.X64).GrpcCsharpUserAgentString); |
||||
|
||||
Assert.AreEqual("grpc-csharp/1.0 (x64)", |
||||
new UserAgentStringProvider("1.0", "(some invalid framework description)", null, null, CommonPlatformDetection.CpuArchitecture.X64).GrpcCsharpUserAgentString); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,108 @@ |
||||
#region Copyright notice and license |
||||
|
||||
// Copyright 2021 The gRPC Authors |
||||
// |
||||
// Licensed under the Apache License, Version 2.0 (the "License"); |
||||
// you may not use this file except in compliance with the License. |
||||
// You may obtain a copy of the License at |
||||
// |
||||
// http://www.apache.org/licenses/LICENSE-2.0 |
||||
// |
||||
// Unless required by applicable law or agreed to in writing, software |
||||
// distributed under the License is distributed on an "AS IS" BASIS, |
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
// See the License for the specific language governing permissions and |
||||
// limitations under the License. |
||||
|
||||
#endregion |
||||
|
||||
using System.Collections.Generic; |
||||
using System.Text.RegularExpressions; |
||||
|
||||
namespace Grpc.Core.Internal |
||||
{ |
||||
/// <summary> |
||||
/// Helps constructing the grpc-csharp component of the user agent string. |
||||
/// </summary> |
||||
internal class UserAgentStringProvider |
||||
{ |
||||
static readonly UserAgentStringProvider defaultInstance; |
||||
readonly string userAgentString; |
||||
|
||||
static UserAgentStringProvider() |
||||
{ |
||||
defaultInstance = new UserAgentStringProvider(VersionInfo.CurrentVersion, PlatformApis.FrameworkDescription, PlatformApis.ClrVersion, PlatformApis.GetGrpcCoreTargetFrameworkMoniker(), PlatformApis.ProcessArchitecture); |
||||
} |
||||
|
||||
public static UserAgentStringProvider DefaultInstance => defaultInstance; |
||||
|
||||
public string GrpcCsharpUserAgentString => userAgentString; |
||||
|
||||
public UserAgentStringProvider(string grpcCsharpVersion, string frameworkDescription, string clrVersion, string tfm, CommonPlatformDetection.CpuArchitecture arch) |
||||
{ |
||||
var detailComponents = new List<string>(); |
||||
|
||||
string sanitizedFrameworkDescription = SanitizeFrameworkDescription(frameworkDescription); |
||||
if (sanitizedFrameworkDescription != null) |
||||
{ |
||||
detailComponents.Add(sanitizedFrameworkDescription); |
||||
} |
||||
|
||||
if (clrVersion != null) |
||||
{ |
||||
detailComponents.Add($"CLR {clrVersion}"); |
||||
} |
||||
|
||||
if (tfm != null) |
||||
{ |
||||
detailComponents.Add(tfm); |
||||
} |
||||
|
||||
string architectureString = TryGetArchitectureString(arch); |
||||
if (architectureString != null) |
||||
{ |
||||
detailComponents.Add(architectureString); |
||||
} |
||||
|
||||
// TODO(jtattermusch): consider adding details about running under unity / xamarin etc. |
||||
var details = string.Join("; ", detailComponents); |
||||
userAgentString = $"grpc-csharp/{grpcCsharpVersion} ({details})"; |
||||
} |
||||
|
||||
static string TryGetArchitectureString(CommonPlatformDetection.CpuArchitecture arch) |
||||
{ |
||||
if (arch == CommonPlatformDetection.CpuArchitecture.Unknown) |
||||
{ |
||||
return null; |
||||
} |
||||
return arch.ToString().ToLowerInvariant(); |
||||
} |
||||
|
||||
static string SanitizeFrameworkDescription(string frameworkDescription) |
||||
{ |
||||
if (frameworkDescription == null) |
||||
{ |
||||
return null; |
||||
} |
||||
|
||||
// Some platforms return more details in the FrameworkDescription string than we want. |
||||
// e.g. on mono, we will get something like "Mono 6.12.0.93 (2020-02/620cf538206 Tue Aug 25 14:04:52 EDT 2020)" |
||||
// For user agent string, we only want basic info on framework name and its version. |
||||
var parts = new List<string>(frameworkDescription.Split(' ')); |
||||
|
||||
int i = 0; |
||||
for (; i < parts.Count; i++) |
||||
{ |
||||
var part = parts[i]; |
||||
if (!Regex.IsMatch(part, @"^[-.,+@A-Za-z0-9]*$")) |
||||
{ |
||||
// stop once we find first part that's not framework name or version |
||||
break; |
||||
} |
||||
} |
||||
|
||||
var result = string.Join(" ", parts.GetRange(0, i)); |
||||
return !string.IsNullOrEmpty(result) ? result : null; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue