Skip to content
Open
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
Expand Up @@ -26,8 +26,8 @@ internal sealed class HttpLoggingRedactionInterceptor : IHttpLoggingInterceptor
private readonly IncomingPathLoggingMode _requestPathLogMode;
private readonly HttpRouteParameterRedactionMode _parameterRedactionMode;
private readonly ILogger<HttpLoggingRedactionInterceptor> _logger;
private readonly IHttpRouteParser _httpRouteParser;
private readonly IHttpRouteFormatter _httpRouteFormatter;
private readonly HttpRouteParser _httpRouteParser;
private readonly HttpRouteFormatter _httpRouteFormatter;
private readonly IIncomingHttpRouteUtility _httpRouteUtility;
private readonly HeaderReader _requestHeadersReader;
private readonly HeaderReader _responseHeadersReader;
Expand All @@ -39,8 +39,8 @@ public HttpLoggingRedactionInterceptor(
IOptions<LoggingRedactionOptions> options,
ILogger<HttpLoggingRedactionInterceptor> logger,
IEnumerable<IHttpLogEnricher> httpLogEnrichers,
IHttpRouteParser httpRouteParser,
IHttpRouteFormatter httpRouteFormatter,
HttpRouteParser httpRouteParser,
HttpRouteFormatter httpRouteFormatter,
IRedactorProvider redactorProvider,
IIncomingHttpRouteUtility httpRouteUtility)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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;

internal sealed class DefaultDownstreamDependencyMetadataManager(
IEnumerable<IDownstreamDependencyMetadata> downstreamDependencyMetadata)
: DownstreamDependencyMetadataManager(downstreamDependencyMetadata);

Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@
using System.Net;
using System.Net.Http;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Telemetry.Internal;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Extensions.Http.Diagnostics;

internal sealed class DownstreamDependencyMetadataManager : IDownstreamDependencyMetadataManager
/// <summary>
/// Manages downstream dependency metadata and resolves route information for outgoing HTTP requests.
/// </summary>
[Experimental(diagnosticId: DiagnosticIds.Experiments.Telemetry, UrlFormat = DiagnosticIds.UrlFormat)]
[SuppressMessage("Minor Code Smell", "S1694:An abstract class should have both abstract and concrete methods", Justification = "Want abstract class for extensibility.")]
public abstract class DownstreamDependencyMetadataManager
{
internal readonly struct ProcessedMetadata
private readonly struct ProcessedMetadata
{
public FrozenRequestMetadataTrieNode[] Nodes { get; init; }
public RequestMetadata[] RequestMetadatas { get; init; }
Expand All @@ -27,19 +33,31 @@ internal readonly struct ProcessedMetadata
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="DownstreamDependencyMetadataManager"/> class.
/// </summary>
/// <param name="downstreamDependencyMetadata">A collection of downstream dependency metadata.</param>
protected DownstreamDependencyMetadataManager(IEnumerable<IDownstreamDependencyMetadata> downstreamDependencyMetadata)
{
_ = Throw.IfNull(downstreamDependencyMetadata);

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

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

public RequestMetadata? GetRequestMetadata(HttpRequestMessage requestMessage)
/// <summary>
/// Gets request metadata for the specified HTTP request message.
/// </summary>
/// <param name="requestMessage">The HTTP request.</param>
/// <returns>The resolved <see cref="RequestMetadata"/> if found; otherwise, <see langword="false" />.</returns>
public virtual RequestMetadata? GetRequestMetadata(HttpRequestMessage requestMessage)
{
_ = Throw.IfNull(requestMessage);
#pragma warning disable CA1031 // Do not catch general exception types
try
{
Expand All @@ -48,7 +66,7 @@ public DownstreamDependencyMetadataManager(IEnumerable<IDownstreamDependencyMeta
return null;
}

var hostMetadata = GetHostMetadata(requestMessage.RequestUri.Host);
HostSuffixTrieNode? hostMetadata = GetHostMetadata(requestMessage.RequestUri.Host);
return GetRequestMetadataInternal(requestMessage.Method.Method, requestMessage.RequestUri.AbsolutePath, hostMetadata);
}
catch (Exception)
Expand All @@ -59,12 +77,18 @@ public DownstreamDependencyMetadataManager(IEnumerable<IDownstreamDependencyMeta
#pragma warning restore CA1031 // Do not catch general exception types
}

public RequestMetadata? GetRequestMetadata(HttpWebRequest requestMessage)
/// <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, null.</returns>
public virtual RequestMetadata? GetRequestMetadata(HttpWebRequest requestMessage)
{
_ = Throw.IfNull(requestMessage);
#pragma warning disable CA1031 // Do not catch general exception types
try
{
var hostMetadata = GetHostMetadata(requestMessage.RequestUri.Host);
HostSuffixTrieNode? hostMetadata = GetHostMetadata(requestMessage.RequestUri.Host);
return GetRequestMetadataInternal(requestMessage.Method, requestMessage.RequestUri.AbsolutePath, hostMetadata);
}
catch (Exception)
Expand All @@ -91,16 +115,16 @@ private static char[] MakeToUpperArray()

private static void AddRouteToTrie(RequestMetadata routeMetadata, Dictionary<string, RequestMetadataTrieNode> dependencyTrieMap)
{
if (!dependencyTrieMap.TryGetValue(routeMetadata.DependencyName, out var routeMetadataTrieRoot))
if (!dependencyTrieMap.TryGetValue(routeMetadata.DependencyName, out RequestMetadataTrieNode? routeMetadataTrieRoot))
{
routeMetadataTrieRoot = new RequestMetadataTrieNode();
dependencyTrieMap.Add(routeMetadata.DependencyName, routeMetadataTrieRoot);
}

var trieCurrent = routeMetadataTrieRoot;
RequestMetadataTrieNode? trieCurrent = routeMetadataTrieRoot;
trieCurrent.Parent = trieCurrent;

var route = routeMetadata.RequestRoute;
string route = routeMetadata.RequestRoute;
if (!string.IsNullOrEmpty(route))
{
var routeSpan = route.AsSpan();
Expand Down Expand Up @@ -157,7 +181,7 @@ private static void AddRouteToTrie(RequestMetadata routeMetadata, Dictionary<str
}
}

var httpMethod = routeMetadata.MethodType.ToUpperInvariant();
string httpMethod = routeMetadata.MethodType.ToUpperInvariant();
for (int j = 0; j < httpMethod.Length; j++)
{
char ch = httpMethod[j];
Expand All @@ -180,20 +204,15 @@ private static void AddRouteToTrie(RequestMetadata routeMetadata, Dictionary<str
private static Dictionary<string, ProcessedMetadata> ProcessDownstreamDependencyMetadata(Dictionary<string, RequestMetadataTrieNode> dependencyTrieMap)
{
Dictionary<string, ProcessedMetadata> finalArrayDict = [];
foreach (var dep in dependencyTrieMap)
foreach (KeyValuePair<string, RequestMetadataTrieNode> dep in dependencyTrieMap)
{
var finalArray = ProcessDownstreamDependencyMetadataInternal(dep.Value);
ProcessedMetadata finalArray = ProcessDownstreamDependencyMetadataInternal(dep.Value);
finalArrayDict.Add(dep.Key, finalArray);
}

return finalArrayDict;
}

// This method has 100% coverage but there is some issue with the code coverage tool in the CI pipeline which makes it
// buggy and complain about some parts the code in this method as not covered. If you make changes to this method, please
// 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)
{
Queue<RequestMetadataTrieNode> queue = new();
Expand All @@ -202,14 +221,14 @@ private static ProcessedMetadata ProcessDownstreamDependencyMetadataInternal(Req
int requestMetadataArraySize = 1;
while (queue.Count > 0)
{
var trieNode = queue.Dequeue();
RequestMetadataTrieNode trieNode = queue.Dequeue();
finalArraySize += trieNode.ChildNodesCount;
for (int i = 0; i < Constants.ASCIICharCount; i++)
{
var node = trieNode.Nodes[i];
if (node != null)
RequestMetadataTrieNode node = trieNode.Nodes[i];
if (node is not null)
{
if (node.RequestMetadata != null)
if (node.RequestMetadata is not null)
{
requestMetadataArraySize++;
}
Expand Down Expand Up @@ -237,10 +256,10 @@ private static ProcessedMetadata ProcessDownstreamDependencyMetadataInternal(Req
int requestMetadataIndex = 0;
while (queue.Count > 0)
{
var trieNode = queue.Dequeue();
RequestMetadataTrieNode trieNode = queue.Dequeue();
for (int i = 0; i < Constants.ASCIICharCount; i++)
{
var node = trieNode.Nodes[i];
RequestMetadataTrieNode node = trieNode.Nodes[i];
if (node != null)
{
var d = new FrozenRequestMetadataTrieNode
Expand Down Expand Up @@ -274,23 +293,18 @@ private static ProcessedMetadata ProcessDownstreamDependencyMetadataInternal(Req
private static FrozenRequestMetadataTrieNode? GetChildNode(char ch, FrozenRequestMetadataTrieNode node, ProcessedMetadata routeMetadataRoot)
{
bool isValid = ch >= node.YoungestChild && ch <= node.YoungestChild + node.ChildNodesCount;
if (isValid)
{
return routeMetadataRoot.Nodes![node.ChildStartIndex + ch - node.YoungestChild];
}

return null;
return isValid ? routeMetadataRoot.Nodes[node.ChildStartIndex + ch - node.YoungestChild] : null;
}

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

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

Expand All @@ -302,7 +316,7 @@ private void AddDependency(IDownstreamDependencyMetadata downstreamDependencyMet
private void AddHostnameToTrie(string hostNameSuffix, string dependencyName)
{
hostNameSuffix = hostNameSuffix.ToUpperInvariant();
var trieCurrent = _hostSuffixTrieRoot;
HostSuffixTrieNode trieCurrent = _hostSuffixTrieRoot;
for (int i = hostNameSuffix.Length - 1; i >= 0; i--)
{
char ch = hostNameSuffix[i];
Expand All @@ -323,8 +337,7 @@ private void AddHostnameToTrie(string hostNameSuffix, string dependencyName)
private HostSuffixTrieNode? GetHostMetadata(string host)
{
HostSuffixTrieNode? hostMetadataNode = null;
string dependencyName = string.Empty;
var trieCurrent = _hostSuffixTrieRoot;
HostSuffixTrieNode trieCurrent = _hostSuffixTrieRoot;
for (int i = host.Length - 1; i >= 0; i--)
{
char ch = host[i];
Expand Down Expand Up @@ -376,17 +389,17 @@ private void AddHostnameToTrie(string hostNameSuffix, string dependencyName)
}
}

var trieCurrent = routeMetadataTrieRoot.Nodes[0];
var lastStartNode = trieCurrent;
var requestPathEndIndex = requestRouteAsSpan.Length;
FrozenRequestMetadataTrieNode trieCurrent = routeMetadataTrieRoot.Nodes[0];
FrozenRequestMetadataTrieNode lastStartNode = trieCurrent;
int requestPathEndIndex = requestRouteAsSpan.Length;
for (int i = 0; i < requestPathEndIndex; i++)
{
char ch = _toUpper[requestRouteAsSpan[i]];
var childNode = GetChildNode(ch, trieCurrent, routeMetadataTrieRoot);
FrozenRequestMetadataTrieNode? childNode = GetChildNode(ch, trieCurrent, routeMetadataTrieRoot);
if (childNode == null)
{
trieCurrent = lastStartNode;
var asteriskChildNode = GetChildNode(AsteriskChar, trieCurrent, routeMetadataTrieRoot);
FrozenRequestMetadataTrieNode? asteriskChildNode = GetChildNode(AsteriskChar, trieCurrent, routeMetadataTrieRoot);
if (asteriskChildNode == null)
{
break;
Expand All @@ -401,10 +414,10 @@ private void AddHostnameToTrie(string hostNameSuffix, string dependencyName)
}

// we add i to the index, because the index returned from ReadOnlySpan<char> is the index in the new slice, not in the original slice.
var nextDelimiterIndex = requestRouteAsSpan.Slice(i, requestPathEndIndex - i).IndexOf(trieCurrent.Delimiter) + i;
int nextDelimiterIndex = requestRouteAsSpan.Slice(i, requestPathEndIndex - i).IndexOf(trieCurrent.Delimiter) + i;

// if we reached end of the request path or end of trie, break
var delimChildNode = GetChildNode(trieCurrent.Delimiter, trieCurrent, routeMetadataTrieRoot);
FrozenRequestMetadataTrieNode? delimChildNode = GetChildNode(trieCurrent.Delimiter, trieCurrent, routeMetadataTrieRoot);
if (nextDelimiterIndex == i - 1 || delimChildNode == null)
{
break;
Expand Down Expand Up @@ -434,7 +447,7 @@ private void AddHostnameToTrie(string hostNameSuffix, string dependencyName)
for (int j = 0; j < httpMethod.Length; j++)
{
char ch = _toUpper[httpMethod[j]];
var childNode = GetChildNode(ch, trieCurrent, routeMetadataTrieRoot);
FrozenRequestMetadataTrieNode? childNode = GetChildNode(ch, trieCurrent, routeMetadataTrieRoot);
if (childNode == null)
{
// Return the default request metadata for the host which
Expand Down
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,7 +22,7 @@ public static class HttpDiagnosticsServiceCollectionExtensions
public static IServiceCollection AddDownstreamDependencyMetadata(this IServiceCollection services, IDownstreamDependencyMetadata downstreamDependencyMetadata)
{
_ = Throw.IfNull(services);
services.TryAddSingleton<IDownstreamDependencyMetadataManager, DownstreamDependencyMetadataManager>();
services.TryAddSingleton<DownstreamDependencyMetadataManager, DefaultDownstreamDependencyMetadataManager>();
_ = services.AddSingleton(downstreamDependencyMetadata);

return services;
Expand All @@ -39,7 +38,7 @@ public static IServiceCollection AddDownstreamDependencyMetadata(this IServiceCo
where T : class, IDownstreamDependencyMetadata
{
_ = Throw.IfNull(services);
services.TryAddSingleton<IDownstreamDependencyMetadataManager, DownstreamDependencyMetadataManager>();
services.TryAddSingleton<DownstreamDependencyMetadataManager, DefaultDownstreamDependencyMetadataManager>();
_ = services.AddSingleton<IDownstreamDependencyMetadata, T>();

return services;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Http.Diagnostics;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Telemetry.Internal;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Extensions.Http.Logging.Internal;

internal sealed class HttpRequestReader : IHttpRequestReader
{
private readonly IHttpRouteFormatter _routeFormatter;
private readonly IHttpRouteParser _httpRouteParser;
private readonly HttpRouteFormatter _routeFormatter;
private readonly HttpRouteParser _httpRouteParser;
private readonly IHttpHeadersReader _httpHeadersReader;
private readonly FrozenDictionary<string, DataClassification> _defaultSensitiveParameters;

Expand All @@ -40,15 +39,15 @@ internal sealed class HttpRequestReader : IHttpRequestReader

private readonly OutgoingPathLoggingMode _outgoingPathLogMode;
private readonly IOutgoingRequestContext _requestMetadataContext;
private readonly IDownstreamDependencyMetadataManager? _downstreamDependencyMetadataManager;
private readonly DownstreamDependencyMetadataManager? _downstreamDependencyMetadataManager;

public HttpRequestReader(
IServiceProvider serviceProvider,
IOptionsMonitor<LoggingOptions> optionsMonitor,
IHttpRouteFormatter routeFormatter,
IHttpRouteParser httpRouteParser,
HttpRouteFormatter routeFormatter,
HttpRouteParser httpRouteParser,
IOutgoingRequestContext requestMetadataContext,
IDownstreamDependencyMetadataManager? downstreamDependencyMetadataManager = null,
DownstreamDependencyMetadataManager? downstreamDependencyMetadataManager = null,
[ServiceKey] string? serviceKey = null)
: this(
optionsMonitor.GetKeyedOrCurrent(serviceKey),
Expand All @@ -62,11 +61,11 @@ public HttpRequestReader(

internal HttpRequestReader(
LoggingOptions options,
IHttpRouteFormatter routeFormatter,
IHttpRouteParser httpRouteParser,
HttpRouteFormatter routeFormatter,
HttpRouteParser httpRouteParser,
IHttpHeadersReader httpHeadersReader,
IOutgoingRequestContext requestMetadataContext,
IDownstreamDependencyMetadataManager? downstreamDependencyMetadataManager = null)
DownstreamDependencyMetadataManager? downstreamDependencyMetadataManager = null)
{
_outgoingPathLogMode = Throw.IfOutOfRange(options.RequestPathLoggingMode);
_httpHeadersReader = httpHeadersReader;
Expand Down
Loading
Loading