Skip to content

Commit 0d6fdcc

Browse files
authored
fix(resourcemanager): append custom user-agent headers (Azure#26556)
Right now, the mgmt telemetry policy will override `user-agent` header to either default header value, or `UserAgentOverride` which is set in the generated codes of each SDK. Then the custom `user-agent` headers are all lost. That will prevent 3rd parties (like Powershell) to do their analytics. This commit will: 1. change the telemetry policy behavior to append custom headers after the official value (either default one or set by `SDKUserAgent`), which is also a best practice in the industry 2. rename `UserAgentOverride` property to `SDKUserAgent` since we now allow append `User-Agent` header 2. add unit test and benchmark test fix Azure#26406
1 parent 4321dcc commit 0d6fdcc

File tree

7 files changed

+138
-2
lines changed

7 files changed

+138
-2
lines changed

sdk/resourcemanager/Azure.ResourceManager/Azure.ResourceManager.sln

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{4E159B41-7
2727
EndProject
2828
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Core", "..\..\core\Azure.Core\src\Azure.Core.csproj", "{E0F3952A-D1C7-4CE4-B2A8-5C34945EBED8}"
2929
EndProject
30+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.ResourceManager.Perf", "perf\Azure.ResourceManager.Perf.csproj", "{138A2FF1-F1AB-480A-9588-136FB875026E}"
31+
EndProject
32+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Test.Perf", "..\..\..\common\Perf\Azure.Test.Perf\Azure.Test.Perf.csproj", "{AD397048-D114-4A32-8F35-64E5C72A4697}"
33+
EndProject
3034
Global
3135
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3236
Debug|Any CPU = Debug|Any CPU
@@ -49,6 +53,14 @@ Global
4953
{E0F3952A-D1C7-4CE4-B2A8-5C34945EBED8}.Debug|Any CPU.Build.0 = Debug|Any CPU
5054
{E0F3952A-D1C7-4CE4-B2A8-5C34945EBED8}.Release|Any CPU.ActiveCfg = Release|Any CPU
5155
{E0F3952A-D1C7-4CE4-B2A8-5C34945EBED8}.Release|Any CPU.Build.0 = Release|Any CPU
56+
{138A2FF1-F1AB-480A-9588-136FB875026E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57+
{138A2FF1-F1AB-480A-9588-136FB875026E}.Debug|Any CPU.Build.0 = Debug|Any CPU
58+
{138A2FF1-F1AB-480A-9588-136FB875026E}.Release|Any CPU.ActiveCfg = Release|Any CPU
59+
{138A2FF1-F1AB-480A-9588-136FB875026E}.Release|Any CPU.Build.0 = Release|Any CPU
60+
{AD397048-D114-4A32-8F35-64E5C72A4697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
61+
{AD397048-D114-4A32-8F35-64E5C72A4697}.Debug|Any CPU.Build.0 = Debug|Any CPU
62+
{AD397048-D114-4A32-8F35-64E5C72A4697}.Release|Any CPU.ActiveCfg = Release|Any CPU
63+
{AD397048-D114-4A32-8F35-64E5C72A4697}.Release|Any CPU.Build.0 = Release|Any CPU
5264
EndGlobalSection
5365
GlobalSection(SolutionProperties) = preSolution
5466
HideSolutionNode = FALSE
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<!--Exlucde some shared codes which will cause conflict-->
6+
<IsMgmtLibrary>false</IsMgmtLibrary>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\..\..\..\common\Perf\Azure.Test.Perf\Azure.Test.Perf.csproj" />
11+
<ProjectReference Include="..\..\..\core\Azure.Core.TestFramework\src\Azure.Core.TestFramework.csproj" />
12+
</ItemGroup>
13+
</Project>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using BenchmarkDotNet.Attributes;
5+
using Azure.Core;
6+
using Azure.Core.TestFramework;
7+
using Azure.ResourceManager.Core;
8+
9+
namespace Azure.ResourceManager.Perf
10+
{
11+
[InProcess]
12+
[MemoryDiagnoser]
13+
public class MgmtTelemetryPolicyBenchmark
14+
{
15+
[Params("", "test")]
16+
public string SDKAgentOverride;
17+
18+
[Params(new string[] { }, new string[] { "foo" }, new string[] {"foo", "bar"})]
19+
public string[] CustomHeaders;
20+
21+
[Benchmark]
22+
public void UserAgentOverrideBenchmark()
23+
{
24+
var requestMock = new MockRequest();
25+
var message = new HttpMessage(requestMock, new ResponseClassifier());
26+
if (!string.IsNullOrEmpty(SDKAgentOverride))
27+
{
28+
message.SetProperty("SDKUserAgent", SDKAgentOverride);
29+
}
30+
if (CustomHeaders.Length > 0)
31+
{
32+
foreach (var header in CustomHeaders)
33+
{
34+
requestMock.Headers.Add(HttpHeader.Names.UserAgent, header);
35+
}
36+
}
37+
var policy = new MgmtTelemetryPolicy(message, ClientOptions.Default);
38+
policy.OnSendingRequest(message);
39+
}
40+
}
41+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using BenchmarkDotNet.Running;
5+
using Azure.ResourceManager.Perf;
6+
7+
public class Program
8+
{
9+
public static void Main(string[] args)
10+
{
11+
var summary = BenchmarkRunner.Run<MgmtTelemetryPolicyBenchmark>();
12+
}
13+
}

sdk/resourcemanager/Azure.ResourceManager/src/Internal/MgmtTelemetryPolicy.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
using System.Runtime.InteropServices;
4+
using System.Text;
55
using Azure.Core;
66
using Azure.Core.Pipeline;
77

@@ -18,7 +18,12 @@ public MgmtTelemetryPolicy(object source, ClientOptions options)
1818

1919
public override void OnSendingRequest(HttpMessage message)
2020
{
21-
var header = message.TryGetProperty("UserAgentOverride", out var userAgent) ? userAgent as string : _defaultHeader;
21+
var header = message.TryGetProperty("SDKUserAgent", out var userAgent) ? userAgent as string : _defaultHeader;
22+
if (message.Request.Headers.TryGetValues(HttpHeader.Names.UserAgent, out var customHeaders))
23+
{
24+
// append custom "user-agent" headers
25+
header = $"{header} {string.Join(" ", customHeaders)}";
26+
}
2227
message.Request.Headers.SetValue(HttpHeader.Names.UserAgent, header);
2328
}
2429
}

sdk/resourcemanager/Azure.ResourceManager/src/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
[assembly: AzureResourceProviderNamespace("Microsoft.Resources")]
88

99
[assembly: InternalsVisibleTo("Azure.ResourceManager.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4")]
10+
[assembly: InternalsVisibleTo("Azure.ResourceManager.Perf, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d15ddcb29688295338af4b7686603fe614abd555e09efba8fb88ee09e1f7b1ccaeed2e8f823fa9eef3fdd60217fc012ea67d2479751a0b8c087a4185541b851bd8b16f8d91b840e51b1cb0ba6fe647997e57429265e85ef62d565db50a69ae1647d54d7bd855e4db3d8a91510e5bcbd0edfbbecaa20a7bd9ae74593daa7b11b4")]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using NUnit.Framework;
7+
using Azure.Core.TestFramework;
8+
using Azure.Core;
9+
using Azure.ResourceManager.Core;
10+
11+
namespace Azure.ResourceManager.Tests
12+
{
13+
public class MgmtTelemetryPolicyTests
14+
{
15+
[TestCase("test", new string[] { }, "test")]
16+
[TestCase("test", new string[] { "foo" }, "test foo")]
17+
[TestCase("test", new string[] { "foo", "bar" }, "test foo bar")]
18+
// default header pattern: azsdk-net-ResourceManager.Tests/1.0.0-alpha.20220124.1 (.NET Framework 4.8.4420.0; Microsoft Windows 10.0.19043 )
19+
[TestCase("", new string[] { }, "azsdk-net-ResourceManager.Tests\\/[a-zA-Z0-9.\\-]+ \\(.*\\)")]
20+
[TestCase("", new string[] { "foo" }, "azsdk-net-ResourceManager.Tests\\/[a-zA-Z0-9.\\-]+ \\(.*\\) foo")]
21+
[TestCase("", new string[] { "foo", "bar" }, "azsdk-net-ResourceManager.Tests\\/[a-zA-Z0-9.\\-]+ \\(.*\\) foo bar")]
22+
public void UserAgentOverride(string userAgentOverride, string[] customHeaders, string expect)
23+
{
24+
var requestMock = new MockRequest();
25+
var message = new HttpMessage(requestMock, new ResponseClassifier());
26+
if (!string.IsNullOrEmpty(userAgentOverride))
27+
{
28+
message.SetProperty("SDKUserAgent", userAgentOverride);
29+
}
30+
if (customHeaders.Length > 0)
31+
{
32+
foreach (var header in customHeaders)
33+
{
34+
requestMock.Headers.Add(HttpHeader.Names.UserAgent, header);
35+
}
36+
}
37+
38+
var policy = new MgmtTelemetryPolicy(this, ClientOptions.Default);
39+
40+
policy.OnSendingRequest(message);
41+
42+
string actual;
43+
Assert.IsTrue(requestMock.Headers.TryGetValue(HttpHeader.Names.UserAgent, out actual));
44+
Assert.That(actual, Does.Match(expect));
45+
46+
IEnumerable<string> actualValues;
47+
Assert.IsTrue(requestMock.Headers.TryGetValues(HttpHeader.Names.UserAgent, out actualValues));
48+
Assert.That(actualValues.ToArray(), Has.Length.EqualTo(1));
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)