Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.Generic;

namespace Microsoft.Extensions.Http.Diagnostics;

/// <summary>
/// Default implementation of <see cref="HttpDependencyMetadataResolver"/> that uses the base
/// trie-based lookup algorithm.
/// </summary>
public sealed class DefaultHttpDependencyMetadataResolver : HttpDependencyMetadataResolver

Check failure on line 12 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DefaultHttpDependencyMetadataResolver.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DefaultHttpDependencyMetadataResolver.cs#L12

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DefaultHttpDependencyMetadataResolver.cs(12,21): error LA0003: (NETCORE_ENGINEERING_TELEMETRY=Build) Newly added symbol 'Microsoft.Extensions.Http.Diagnostics.DefaultHttpDependencyMetadataResolver' must be marked as experimental

Check failure on line 12 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DefaultHttpDependencyMetadataResolver.cs

View check run for this annotation

Azure Pipelines / extensions-ci

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DefaultHttpDependencyMetadataResolver.cs#L12

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/DefaultHttpDependencyMetadataResolver.cs(12,21): error LA0003: (NETCORE_ENGINEERING_TELEMETRY=Build) Newly added symbol 'Microsoft.Extensions.Http.Diagnostics.DefaultHttpDependencyMetadataResolver' must be marked as experimental
{
/// <summary>
/// Initializes a new instance of the <see cref="DefaultHttpDependencyMetadataResolver"/> class.
/// </summary>
/// <param name="dependencyMetadata">A collection of HTTP dependency metadata used for request resolution.</param>
public DefaultHttpDependencyMetadataResolver(IEnumerable<IDownstreamDependencyMetadata> dependencyMetadata)
: base(dependencyMetadata)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;

Check failure on line 9 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs#L9

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs(9,1): error S1128: (NETCORE_ENGINEERING_TELEMETRY=Build) Remove this unnecessary 'using'. (https://rules.sonarsource.com/csharp/RSPEC-1128)

Check failure on line 9 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs

View check run for this annotation

Azure Pipelines / extensions-ci

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs#L9

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs(9,1): error S1128: (NETCORE_ENGINEERING_TELEMETRY=Build) Remove this unnecessary 'using'. (https://rules.sonarsource.com/csharp/RSPEC-1128)
using System.Text.RegularExpressions;
using Microsoft.Extensions.Telemetry.Internal;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Extensions.Http.Diagnostics;

internal sealed class DownstreamDependencyMetadataManager : IDownstreamDependencyMetadataManager
/// <summary>
/// Resolves metadata for HTTP requests based on hostname, path, and method patterns.
/// </summary>
/// <remarks>
/// This class provides a high-performance way to identify HTTP requests by mapping them to previously
/// configured metadata using specialized trie-based data structures. This enables efficient lookup
/// of service information, operation names, and other metadata for telemetry and policy application.
/// </remarks>
public abstract class HttpDependencyMetadataResolver

Check failure on line 23 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs#L23

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs(23,23): error LA0003: (NETCORE_ENGINEERING_TELEMETRY=Build) Newly added symbol 'Microsoft.Extensions.Http.Diagnostics.HttpDependencyMetadataResolver' must be marked as experimental

Check failure on line 23 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs

View check run for this annotation

Azure Pipelines / extensions-ci

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs#L23

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDependencyMetadataResolver.cs(23,23): error LA0003: (NETCORE_ENGINEERING_TELEMETRY=Build) Newly added symbol 'Microsoft.Extensions.Http.Diagnostics.HttpDependencyMetadataResolver' must be marked as experimental
{
internal readonly struct ProcessedMetadata
{
Expand All @@ -27,22 +35,35 @@
private readonly HostSuffixTrieNode _hostSuffixTrieRoot = new();
private readonly FrozenDictionary<string, ProcessedMetadata> _frozenProcessedMetadataMap;

public DownstreamDependencyMetadataManager(IEnumerable<IDownstreamDependencyMetadata> downstreamDependencyMetadata)
/// <summary>
/// Initializes a new instance of the <see cref="HttpDependencyMetadataResolver"/> class.
/// </summary>
/// <param name="dependencyMetadata">A collection of HTTP dependency metadata used for request resolution.</param>
/// <exception cref="ArgumentNullException"><paramref name="dependencyMetadata"/> is <see langword="null"/>.</exception>
protected HttpDependencyMetadataResolver(IEnumerable<IDownstreamDependencyMetadata> dependencyMetadata)
{
_ = Throw.IfNull(dependencyMetadata);

Dictionary<string, RequestMetadataTrieNode> dependencyTrieMap = [];
foreach (var dependency in downstreamDependencyMetadata)
foreach (var dependency in dependencyMetadata)
{
AddDependency(dependency, dependencyTrieMap);
}

_frozenProcessedMetadataMap = ProcessDownstreamDependencyMetadata(dependencyTrieMap).ToFrozenDictionary(StringComparer.Ordinal);
_frozenProcessedMetadataMap = ProcessDependencyMetadata(dependencyTrieMap).ToFrozenDictionary(StringComparer.Ordinal);
}

public RequestMetadata? GetRequestMetadata(HttpRequestMessage requestMessage)
#if !NET462
/// <summary>
/// Gets request metadata for the specified HTTP request message.
/// </summary>
/// <param name="requestMessage">The HTTP request message.</param>
/// <returns>The resolved <see cref="RequestMetadata"/> if found; otherwise, <see langword="null"/>.</returns>
public virtual RequestMetadata? GetRequestMetadata(HttpRequestMessage requestMessage)
{
try
{
if (requestMessage.RequestUri == null)
if (requestMessage?.RequestUri == null)
{
return null;
}
Expand All @@ -56,8 +77,13 @@
return null;
}
}

public RequestMetadata? GetRequestMetadata(HttpWebRequest requestMessage)
#else
/// <summary>
/// Gets request metadata for the specified HTTP web request.
/// </summary>
/// <param name="requestMessage">The HTTP web request.</param>
/// <returns>The resolved <see cref="RequestMetadata"/> if found; otherwise, <see langword="null"/>.</returns>
public virtual RequestMetadata? GetRequestMetadata(HttpWebRequest requestMessage)
{
try
{
Expand All @@ -70,12 +96,12 @@
return null;
}
}
#endif

private static char[] MakeToUpperArray()
{
// Initialize the _toUpper array for quick conversion of any ascii char to upper
// without incurring cost of checking whether the character requires conversion.

var a = new char[Constants.ASCIICharCount];
for (int i = 0; i < Constants.ASCIICharCount; i++)
{
Expand Down Expand Up @@ -173,12 +199,12 @@
trieCurrent.RequestMetadata = routeMetadata;
}

private static Dictionary<string, ProcessedMetadata> ProcessDownstreamDependencyMetadata(Dictionary<string, RequestMetadataTrieNode> dependencyTrieMap)
private static Dictionary<string, ProcessedMetadata> ProcessDependencyMetadata(Dictionary<string, RequestMetadataTrieNode> dependencyTrieMap)
{
Dictionary<string, ProcessedMetadata> finalArrayDict = [];
foreach (var dep in dependencyTrieMap)
{
var finalArray = ProcessDownstreamDependencyMetadataInternal(dep.Value);
var finalArray = ProcessDependencyMetadataInternal(dep.Value);
finalArrayDict.Add(dep.Key, finalArray);
}

Expand All @@ -190,7 +216,7 @@
// remove the ExlcudeCodeCoverage attribute and ensure it's covered fully using local runs and enable it back before
// pushing the change to PR.
[ExcludeFromCodeCoverage]
private static ProcessedMetadata ProcessDownstreamDependencyMetadataInternal(RequestMetadataTrieNode requestMetadataTrieRoot)
private static ProcessedMetadata ProcessDependencyMetadataInternal(RequestMetadataTrieNode requestMetadataTrieRoot)
{
Queue<RequestMetadataTrieNode> queue = new();
queue.Enqueue(requestMetadataTrieRoot);
Expand Down Expand Up @@ -278,17 +304,17 @@
return null;
}

private void AddDependency(IDownstreamDependencyMetadata downstreamDependencyMetadata, Dictionary<string, RequestMetadataTrieNode> dependencyTrieMap)
private void AddDependency(IDownstreamDependencyMetadata dependencyMetadata, Dictionary<string, RequestMetadataTrieNode> dependencyTrieMap)
{
foreach (var hostNameSuffix in downstreamDependencyMetadata.UniqueHostNameSuffixes)
foreach (var hostNameSuffix in dependencyMetadata.UniqueHostNameSuffixes)
{
// Add hostname to hostname suffix trie
AddHostnameToTrie(hostNameSuffix, downstreamDependencyMetadata.DependencyName);
AddHostnameToTrie(hostNameSuffix, dependencyMetadata.DependencyName);
}

foreach (var routeMetadata in downstreamDependencyMetadata.RequestMetadata)
foreach (var routeMetadata in dependencyMetadata.RequestMetadata)
{
routeMetadata.DependencyName = downstreamDependencyMetadata.DependencyName;
routeMetadata.DependencyName = dependencyMetadata.DependencyName;

// Add route metadata to the route per dependency trie
AddRouteToTrie(routeMetadata, dependencyTrieMap);
Expand Down Expand Up @@ -445,4 +471,4 @@
hostMetadata.RequestMetadata : // Return the default request metadata for this host if no matching route is found.
routeMetadataTrieRoot.RequestMetadatas[trieCurrent.RequestMetadataEntryIndex];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Http.Diagnostics;
using Microsoft.Extensions.Telemetry.Internal;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Extensions.DependencyInjection;
Expand All @@ -23,9 +22,10 @@
public static IServiceCollection AddDownstreamDependencyMetadata(this IServiceCollection services, IDownstreamDependencyMetadata downstreamDependencyMetadata)
{
_ = Throw.IfNull(services);
services.TryAddSingleton<IDownstreamDependencyMetadataManager, DownstreamDependencyMetadataManager>();
_ = services.AddSingleton(downstreamDependencyMetadata);
_ = Throw.IfNull(downstreamDependencyMetadata);

services.TryAddEnumerable(ServiceDescriptor.Singleton<IDownstreamDependencyMetadata>(downstreamDependencyMetadata));
services.TryAddSingleton<HttpDependencyMetadataResolver, DefaultHttpDependencyMetadataResolver>();
return services;
}

Expand All @@ -39,9 +39,40 @@
where T : class, IDownstreamDependencyMetadata
{
_ = Throw.IfNull(services);
services.TryAddSingleton<IDownstreamDependencyMetadataManager, DownstreamDependencyMetadataManager>();
_ = services.AddSingleton<IDownstreamDependencyMetadata, T>();
services.TryAddEnumerable(ServiceDescriptor.Singleton<IDownstreamDependencyMetadata, T>());
services.TryAddSingleton<HttpDependencyMetadataResolver, DefaultHttpDependencyMetadataResolver>();
return services;
}

/// <summary>
/// Adds services required for HTTP dependency metadata resolution.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddHttpDependencyMetadataResolver(this IServiceCollection services)

Check failure on line 52 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDiagnosticsServiceCollectionExtensions.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDiagnosticsServiceCollectionExtensions.cs#L52

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Http/HttpDiagnosticsServiceCollectionExtensions.cs(52,38): error LA0003: (NETCORE_ENGINEERING_TELEMETRY=Build) Newly added symbol 'Microsoft.Extensions.DependencyInjection.HttpDiagnosticsServiceCollectionExtensions.AddHttpDependencyMetadataResolver(Microsoft.Extensions.DependencyInjection.IServiceCollection)' must be marked as experimental
{
services.TryAddSingleton<HttpDependencyMetadataResolver, DefaultHttpDependencyMetadataResolver>();
return services;
}

/// <summary>
/// Adds services required for HTTP dependency metadata resolution with the specified metadata providers.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="providers">The HTTP dependency metadata providers to register.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddHttpDependencyMetadataResolver(
this IServiceCollection services,
params IDownstreamDependencyMetadata[] providers)
{
_ = Throw.IfNull(services);

foreach (var provider in providers)
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IDownstreamDependencyMetadata>(provider));
}

services.TryAddSingleton<HttpDependencyMetadataResolver, DefaultHttpDependencyMetadataResolver>();
return services;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http.Diagnostics;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Telemetry.Internal;
using Microsoft.Shared.Diagnostics;
using Microsoft.Shared.Pools;

Expand Down Expand Up @@ -43,23 +42,23 @@

private readonly OutgoingPathLoggingMode _outgoingPathLogMode;
private readonly IOutgoingRequestContext _requestMetadataContext;
private readonly IDownstreamDependencyMetadataManager? _downstreamDependencyMetadataManager;
private readonly HttpDependencyMetadataResolver? _dependencyMetadataResolver;

public HttpRequestReader(
IServiceProvider serviceProvider,
IOptionsMonitor<LoggingOptions> optionsMonitor,
IHttpRouteFormatter routeFormatter,
IHttpRouteParser httpRouteParser,
IOutgoingRequestContext requestMetadataContext,
IDownstreamDependencyMetadataManager? downstreamDependencyMetadataManager = null,
HttpDependencyMetadataResolver? dependencyMetadataResolver = null,
[ServiceKey] string? serviceKey = null)
: this(
optionsMonitor.GetKeyedOrCurrent(serviceKey),
routeFormatter,
httpRouteParser,
serviceProvider.GetRequiredOrKeyedRequiredService<IHttpHeadersReader>(serviceKey),
requestMetadataContext,
downstreamDependencyMetadataManager)
dependencyMetadataResolver)
{
}

Expand All @@ -69,15 +68,15 @@
IHttpRouteParser httpRouteParser,
IHttpHeadersReader httpHeadersReader,
IOutgoingRequestContext requestMetadataContext,
IDownstreamDependencyMetadataManager? downstreamDependencyMetadataManager = null)
HttpDependencyMetadataResolver? dependencyMetadataResolver = null)
{
_outgoingPathLogMode = Throw.IfOutOfRange(options.RequestPathLoggingMode);
_httpHeadersReader = httpHeadersReader;

_routeFormatter = routeFormatter;
_httpRouteParser = httpRouteParser;
_requestMetadataContext = requestMetadataContext;
_downstreamDependencyMetadataManager = downstreamDependencyMetadataManager;
_dependencyMetadataResolver = dependencyMetadataResolver;

_defaultSensitiveParameters = options.RouteParameterDataClasses.ToFrozenDictionary(StringComparer.Ordinal);
_queryParameterDataClasses = options.RequestQueryParametersDataClasses.ToFrozenDictionary(StringComparer.Ordinal);
Expand Down Expand Up @@ -234,7 +233,7 @@

var requestMetadata = request.GetRequestMetadata() ??
_requestMetadataContext.RequestMetadata ??
_downstreamDependencyMetadataManager?.GetRequestMetadata(request);
_dependencyMetadataResolver?.GetRequestMetadata(request);

Check failure on line 236 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Correctness WarningsCheck)

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs#L236

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs(236,61): error CS1503: (NETCORE_ENGINEERING_TELEMETRY=Build) Argument 1: cannot convert from 'System.Net.Http.HttpRequestMessage' to 'System.Net.HttpWebRequest'

Check failure on line 236 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs

View check run for this annotation

Azure Pipelines / extensions-ci (Build Ubuntu)

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs#L236

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs(236,61): error CS1503: (NETCORE_ENGINEERING_TELEMETRY=Build) Argument 1: cannot convert from 'System.Net.Http.HttpRequestMessage' to 'System.Net.HttpWebRequest'

Check failure on line 236 in src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs

View check run for this annotation

Azure Pipelines / extensions-ci

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs#L236

src/Libraries/Microsoft.Extensions.Http.Diagnostics/Logging/Internal/HttpRequestReader.cs(236,61): error CS1503: (NETCORE_ENGINEERING_TELEMETRY=Build) Argument 1: cannot convert from 'System.Net.Http.HttpRequestMessage' to 'System.Net.HttpWebRequest'

if (requestMetadata == null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Net;
using System.Net.Http;
using Microsoft.Extensions.Http.Diagnostics;

namespace Microsoft.Extensions.Telemetry.Internal;

/// <summary>
/// Interface to manage dependency metadata.
/// (Obsolete) Use <see cref="HttpDependencyMetadataResolver"/>.
/// </summary>
[Obsolete("Use HttpDependencyMetadataResolver instead. This internal interface will be removed.")]
internal interface IDownstreamDependencyMetadataManager
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
using System;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Telemetry.Internal;
using Microsoft.Extensions.Http.Diagnostics;
using Xunit;

namespace Microsoft.Extensions.Telemetry.Telemetry;

public class DownstreamDependencyMetadataManagerTests : IDisposable
{
private readonly IDownstreamDependencyMetadataManager _depMetadataManager;
private readonly HttpDependencyMetadataResolver _metadataResolver;
private readonly ServiceProvider _sp;

public DownstreamDependencyMetadataManagerTests()
Expand All @@ -20,7 +20,7 @@ public DownstreamDependencyMetadataManagerTests()
.AddDownstreamDependencyMetadata(new BackslashDownstreamDependencyMetadata())
.AddDownstreamDependencyMetadata(new EmptyRouteDependencyMetadata())
.BuildServiceProvider();
_depMetadataManager = _sp.GetRequiredService<IDownstreamDependencyMetadataManager>();
_metadataResolver = _sp.GetRequiredService<HttpDependencyMetadataResolver>();
}

[Theory]
Expand Down Expand Up @@ -51,7 +51,7 @@ public void GetRequestMetadata_RoutesRegisteredWithBackslashes_ShouldReturnHostM
RequestUri = new Uri(uriString: urlString)
};

var requestMetadata = _depMetadataManager.GetRequestMetadata(requestMessage);
var requestMetadata = _metadataResolver.GetRequestMetadata(requestMessage);
Assert.NotNull(requestMetadata);
Assert.Equal(new BackslashDownstreamDependencyMetadata().DependencyName, requestMetadata.DependencyName);
Assert.Equal(expectedRequestName, requestMetadata.RequestName);
Expand All @@ -71,7 +71,7 @@ public void GetRequestMetadata_EmptyRouteRegisteredForDependency_ShouldReturnMet
RequestUri = new Uri(uriString: urlString)
};

var requestMetadata = _depMetadataManager.GetRequestMetadata(requestMessage);
var requestMetadata = _metadataResolver.GetRequestMetadata(requestMessage);

Assert.NotNull(requestMetadata);
Assert.Equal("EmptyRouteService", requestMetadata.DependencyName);
Expand Down
Loading