diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bdb09f65d..aee2c35b23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- Revert W3C traceparent support ([#4204](https://github.com/getsentry/sentry-dotnet/pull/4204)) - Support musl on Linux ([#4188](https://github.com/getsentry/sentry-dotnet/pull/4188)) - Support for Windows ARM64 with Native AOT ([#4187](https://github.com/getsentry/sentry-dotnet/pull/4187)) - Addressed potential performance issue with Sentry.Maui ([#4219](https://github.com/getsentry/sentry-dotnet/pull/4219)) diff --git a/src/Sentry.AspNet/HttpContextExtensions.cs b/src/Sentry.AspNet/HttpContextExtensions.cs index b652062969..d3d9e46245 100644 --- a/src/Sentry.AspNet/HttpContextExtensions.cs +++ b/src/Sentry.AspNet/HttpContextExtensions.cs @@ -34,27 +34,6 @@ public static class HttpContextExtensions } } - private static W3CTraceHeader? TryGetW3CTraceHeader(HttpContext context, SentryOptions? options) - { - var value = context.Request.Headers.Get(W3CTraceHeader.HttpHeaderName); - if (string.IsNullOrWhiteSpace(value)) - { - return null; - } - - options?.LogDebug("Received Sentry trace header '{0}'.", value); - - try - { - return W3CTraceHeader.Parse(value); - } - catch (Exception ex) - { - options?.LogError(ex, "Invalid Sentry trace header '{0}'.", value); - return null; - } - } - private static BaggageHeader? TryGetBaggageHeader(HttpContext context, SentryOptions? options) { var value = context.Request.Headers.Get(BaggageHeader.HttpHeaderName); @@ -86,10 +65,7 @@ public static void StartOrContinueTrace(this HttpContext httpContext) { var options = SentrySdk.CurrentOptions; - // If both sentry-trace and traceparent headers are present, sentry-trace takes precedence. - // See: https://github.com/getsentry/team-sdks/issues/41 var traceHeader = TryGetSentryTraceHeader(httpContext, options); - traceHeader ??= TryGetW3CTraceHeader(httpContext, options)?.SentryTraceHeader; var baggageHeader = TryGetBaggageHeader(httpContext, options); var method = httpContext.Request.HttpMethod; diff --git a/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs b/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs index 1ba81d1444..19e4dde3c4 100644 --- a/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs +++ b/src/Sentry.AspNetCore/Extensions/HttpContextExtensions.cs @@ -65,27 +65,6 @@ internal static class HttpContextExtensions } } - public static W3CTraceHeader? TryGetW3CTraceHeader(this HttpContext context, SentryOptions? options) - { - var value = context.Request.Headers.GetValueOrDefault(W3CTraceHeader.HttpHeaderName); - if (string.IsNullOrWhiteSpace(value)) - { - return null; - } - - options?.LogDebug("Received Sentry trace header '{0}'.", value); - - try - { - return W3CTraceHeader.Parse(value!); - } - catch (Exception ex) - { - options?.LogError(ex, "Invalid Sentry trace header '{0}'.", value); - return null; - } - } - public static BaggageHeader? TryGetBaggageHeader(this HttpContext context, SentryOptions? options) { var value = context.Request.Headers.GetValueOrDefault(BaggageHeader.HttpHeaderName); diff --git a/src/Sentry.AspNetCore/SentryMiddleware.cs b/src/Sentry.AspNetCore/SentryMiddleware.cs index 77bd2fc8bf..33fca3e622 100644 --- a/src/Sentry.AspNetCore/SentryMiddleware.cs +++ b/src/Sentry.AspNetCore/SentryMiddleware.cs @@ -105,10 +105,7 @@ public async Task InvokeAsync(HttpContext context, RequestDelegate next) context.Response.OnCompleted(() => hub.FlushAsync(_options.FlushTimeout)); } - // If both sentry-trace and traceparent headers are present, sentry-trace takes precedence. - // See: https://github.com/getsentry/team-sdks/issues/41 var traceHeader = context.TryGetSentryTraceHeader(_options); - traceHeader ??= context.TryGetW3CTraceHeader(_options)?.SentryTraceHeader; var baggageHeader = context.TryGetBaggageHeader(_options); var transactionContext = hub.ContinueTrace(traceHeader, baggageHeader); diff --git a/src/Sentry.Azure.Functions.Worker/HttpRequestDataExtensions.cs b/src/Sentry.Azure.Functions.Worker/HttpRequestDataExtensions.cs index b4a311812b..194e8dcd6f 100644 --- a/src/Sentry.Azure.Functions.Worker/HttpRequestDataExtensions.cs +++ b/src/Sentry.Azure.Functions.Worker/HttpRequestDataExtensions.cs @@ -30,31 +30,6 @@ internal static class HttpRequestDataExtensions } } - public static W3CTraceHeader? TryGetW3CTraceHeader(this HttpRequestData context, IDiagnosticLogger? logger) - { - var traceHeaderValue = context.Headers.TryGetValues(W3CTraceHeader.HttpHeaderName, out var values) - ? values.FirstOrDefault() - : null; - - if (traceHeaderValue is null) - { - logger?.LogDebug("Did not receive a Sentry trace header."); - return null; - } - - logger?.LogDebug("Received Sentry trace header '{0}'.", traceHeaderValue); - - try - { - return W3CTraceHeader.Parse(traceHeaderValue); - } - catch (Exception ex) - { - logger?.LogError(ex, "Invalid Sentry trace header '{0}'.", traceHeaderValue); - return null; - } - } - public static BaggageHeader? TryGetBaggageHeader(this HttpRequestData context, IDiagnosticLogger? logger) { var baggageValue = context.Headers.TryGetValues(BaggageHeader.HttpHeaderName, out var value) diff --git a/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerMiddleware.cs b/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerMiddleware.cs index 2ae17d004a..c3c57fe71e 100644 --- a/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerMiddleware.cs +++ b/src/Sentry.Azure.Functions.Worker/SentryFunctionsWorkerMiddleware.cs @@ -123,10 +123,7 @@ private async Task StartOrContinueTraceAsync(FunctionContext TransactionNameCache.TryAdd(transactionNameKey, transactionName); } - // If both sentry-trace and traceparent headers are present, sentry-trace takes precedence. - // See: https://github.com/getsentry/team-sdks/issues/41 var traceHeader = requestData.TryGetSentryTraceHeader(_logger); - traceHeader ??= requestData.TryGetW3CTraceHeader(_logger)?.SentryTraceHeader; var baggageHeader = requestData.TryGetBaggageHeader(_logger); return SentrySdk.ContinueTrace(traceHeader, baggageHeader, transactionName, Operation); diff --git a/src/Sentry/SentryMessageHandler.cs b/src/Sentry/SentryMessageHandler.cs index 69b1f6560e..8784ef1822 100644 --- a/src/Sentry/SentryMessageHandler.cs +++ b/src/Sentry/SentryMessageHandler.cs @@ -136,7 +136,6 @@ private void PropagateTraceHeaders(HttpRequestMessage request, string url) if (_options?.TracePropagationTargets.MatchesSubstringOrRegex(url) is true or null) { AddSentryTraceHeader(request); - AddW3CTraceHeader(request); AddBaggageHeader(request); } } @@ -150,15 +149,6 @@ private void AddSentryTraceHeader(HttpRequestMessage request) } } - private void AddW3CTraceHeader(HttpRequestMessage request) - { - // Set trace header if it hasn't already been set - if (!request.Headers.Contains(W3CTraceHeader.HttpHeaderName) && _hub.GetTraceHeader() is { } traceHeader) - { - request.Headers.Add(W3CTraceHeader.HttpHeaderName, new W3CTraceHeader(traceHeader).ToString()); - } - } - private void AddBaggageHeader(HttpRequestMessage request) { var baggage = _hub.GetBaggage(); diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index a1c056acdc..f8d728e0d3 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -952,8 +952,7 @@ public double? ProfilesSampleRate /// /// A customizable list of objects, each containing either a /// substring or regular expression pattern that can be used to control which outgoing HTTP requests - /// will have the sentry-trace, traceparent, and baggage headers propagated, - /// for purposes of distributed tracing. + /// will have the sentry-trace and baggage headers propagated, for purposes of distributed tracing. /// The default value contains a single value of .*, which matches everything. /// To disable propagation completely, clear this collection or set it to an empty collection. /// diff --git a/src/Sentry/W3CTraceHeader.cs b/src/Sentry/W3CTraceHeader.cs deleted file mode 100644 index 65043594a6..0000000000 --- a/src/Sentry/W3CTraceHeader.cs +++ /dev/null @@ -1,131 +0,0 @@ -namespace Sentry; - -/// -/// Extension methods for working with Sentry trace headers. -/// -internal class W3CTraceHeader -{ - private const string SupportedVersion = "00"; - - /// - /// The name of the W3C trace context header used for distributed tracing. - /// This field contains the value "traceparent" which is part of the W3C Trace Context specification. - /// - public const string HttpHeaderName = "traceparent"; - - /// - /// Represents the sampled trace flags value ("01") in W3C Trace Context specification. - /// This flag indicates that the trace is part of the sampling set and should be recorded. - /// - public const string TraceFlagsSampled = "01"; - - /// - /// Represents the unsampled trace flags value ("00") in W3C Trace Context specification. - /// This flag indicates that the trace is not part of the sampling set and should not be recorded. - /// - public const string TraceFlagsNotSampled = "00"; - - /// - /// Initializes a new instance of the class from a Sentry trace header. - /// - /// The source Sentry trace header to create the W3C trace header from. - /// Thrown when is null. - public W3CTraceHeader(SentryTraceHeader source) - { - if (source is null) - { - throw new ArgumentNullException(nameof(source), "Source Sentry trace header cannot be null."); - } - - SentryTraceHeader = source; - } - - /// - /// Gets the Sentry trace header containing trace identification and sampling information. - /// - /// - /// The Sentry trace header that contains the trace ID, span ID, and sampling decision. - /// - public SentryTraceHeader SentryTraceHeader { get; } - - /// - /// Parses a from a string representation of the Sentry trace header. - /// - /// - /// A string containing the Sentry trace header, expected to follow the format "traceId-spanId-sampled", - /// where "sampled" is optional. - /// - /// - /// A object if parsing succeeds, or null if the input string is null, empty, or whitespace. - /// - /// - /// Thrown if the input string does not contain a valid trace header format, specifically if it lacks required trace ID and span ID components. - /// - public static W3CTraceHeader? Parse(string value) - { - if (string.IsNullOrWhiteSpace(value)) - { - return null; - } - - var components = value.Split('-', StringSplitOptions.RemoveEmptyEntries); - if (components.Length < 4) - { - throw new FormatException($"Invalid W3C trace header: {value}."); - } - - var version = components[0]; - if (version != SupportedVersion) - { - throw new FormatException($"Invalid W3C trace header version: {version}."); - } - - var traceId = SentryId.Parse(components[1]); - var spanId = SpanId.Parse(components[2]); - var isSampled = ConvertTraceFlagsToSampled(components[3]); - - return new W3CTraceHeader(new SentryTraceHeader(traceId, spanId, isSampled)); - } - - /// - public override string ToString() - { - var traceFlags = ConvertSampledToTraceFlags(SentryTraceHeader.IsSampled); - return $"{SupportedVersion}-{SentryTraceHeader.TraceId}-{SentryTraceHeader.SpanId}-{traceFlags}"; - } - - private static string? ConvertSampledToTraceFlags(bool? isSampled) => (isSampled ?? false) ? TraceFlagsSampled : TraceFlagsNotSampled; - - private static bool? ConvertTraceFlagsToSampled(string? traceFlags) - { - if (string.IsNullOrWhiteSpace(traceFlags) || traceFlags.Length != 2) - { - return null; - } - - // In version 00 of the W3C Trace Context specification, the trace flags field is 2 hex digits. - // Only the first bit is used. We use string comparison first to avoid parsing the hex value in - // the bulk of all cases. - // See https://github.com/getsentry/sentry-dotnet/pull/4084#discussion_r2035771628 - if (string.Equals(traceFlags, TraceFlagsSampled, StringComparison.Ordinal)) - { - return true; - } - else if (string.Equals(traceFlags, TraceFlagsNotSampled, StringComparison.Ordinal)) - { - return false; - } - - // If the trace flags field is not "01" or "00", we try to parse it as a hex number. - // This is a fallback for cases where the trace flags field is not in the expected format. - if (!byte.TryParse(traceFlags, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out byte traceFlagsBytes)) - { - // If it's not a valid hex number, we can't parse it. - return null; - } - - // The first bit of the trace flags field indicates whether the trace is sampled. - // We use bitwise AND to check if the first bit is set. - return (traceFlagsBytes & 0x01) == 1; - } -} diff --git a/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs b/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs index 0ad0628f77..d6cedd9b73 100644 --- a/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs +++ b/test/Sentry.AspNetCore.Tests/SentryMiddlewareTests.cs @@ -682,10 +682,8 @@ public async Task InvokeAsync_InstrumenterOpenTelemetry_SavesScope() } } - [Theory] - [InlineData("Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de", "4b4d2878507b43d3af7dd8c4ab7a96d9")] - [InlineData("traceparent", "00-4b4d2878507b43d3af7dd8c4ab7a96d8-3cc6fd1337d243de-00", "4b4d2878507b43d3af7dd8c4ab7a96d8")] - public async Task InvokeAsync_RequestContainsSentryHeaders_ContinuesTrace(string headerName, string headerValue, string expectedTraceId) + [Fact] + public async Task InvokeAsync_RequestContainsSentryHeaders_ContinuesTrace() { SentryTraceHeader capturedTraceHeader = null; BaggageHeader capturedBaggageHeader = null; @@ -700,7 +698,7 @@ public async Task InvokeAsync_RequestContainsSentryHeaders_ContinuesTrace(string var request = Substitute.For(); var fakeHeaders = new HeaderDictionary { - { headerName, headerValue}, + { "Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de"}, { "Baggage", "sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c"}, }; _ = request.Headers.Returns(fakeHeaders); @@ -712,21 +710,19 @@ public async Task InvokeAsync_RequestContainsSentryHeaders_ContinuesTrace(string _fixture.Hub.Received().ContinueTrace(Arg.Any(), Arg.Any()); Assert.NotNull(capturedTraceHeader); - Assert.Equal(expectedTraceId, capturedTraceHeader.TraceId.ToString()); + Assert.Equal("4b4d2878507b43d3af7dd8c4ab7a96d9", capturedTraceHeader.TraceId.ToString()); Assert.NotNull(capturedBaggageHeader); Assert.Equal("sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c", capturedBaggageHeader.ToString()); } - [Theory] - [InlineData("Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de", "4b4d2878507b43d3af7dd8c4ab7a96d9")] - [InlineData("traceparent", "00-4b4d2878507b43d3af7dd8c4ab7a96d8-3cc6fd1337d243de-00", "4b4d2878507b43d3af7dd8c4ab7a96d8")] - public async Task InvokeAsync_RequestContainsSentryHeaders_AddsHeadersAndTransactionContextToItems(string headerName, string headerValue, string expectedTraceId) + [Fact] + public async Task InvokeAsync_RequestContainsSentryHeaders_AddsHeadersAndTransactionContextToItems() { var sut = _fixture.GetSut(); var request = Substitute.For(); var fakeHeaders = new HeaderDictionary { - { headerName, headerValue}, + { "Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de"}, { "Baggage", "sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c"}, }; var contextItems = new Dictionary(); @@ -745,7 +741,7 @@ public async Task InvokeAsync_RequestContainsSentryHeaders_AddsHeadersAndTransac var traceHeader = contextItems[SentryMiddleware.TraceHeaderItemKey] as SentryTraceHeader; Assert.NotNull(traceHeader); - Assert.Equal(expectedTraceId, traceHeader.TraceId.ToString()); + Assert.Equal("4b4d2878507b43d3af7dd8c4ab7a96d9", traceHeader.TraceId.ToString()); var baggageHeader = contextItems[SentryMiddleware.BaggageHeaderItemKey] as BaggageHeader; Assert.NotNull(baggageHeader); Assert.Equal("sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c", baggageHeader.ToString()); @@ -753,16 +749,14 @@ public async Task InvokeAsync_RequestContainsSentryHeaders_AddsHeadersAndTransac Assert.NotNull(transactionContext); } - [Theory] - [InlineData("Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de")] - [InlineData("traceparent", "00-4b4d2878507b43d3af7dd8c4ab7a96d8-3cc6fd1337d243de-01")] - public async Task InvokeAsync_InvokingWithTheSameContextTwice_DoesNotThrow(string headerName, string headerValue) + [Fact] + public async Task InvokeAsync_InvokingWithTheSameContextTwice_DoesNotThrow() { var sut = _fixture.GetSut(); var request = Substitute.For(); var fakeHeaders = new HeaderDictionary { - { headerName, headerValue}, + { "Sentry-Trace", "4b4d2878507b43d3af7dd8c4ab7a96d9-3cc6fd1337d243de"}, { "Baggage", "sentry-trace_id=4b4d2878507b43d3af7dd8c4ab7a96d9, sentry-public_key=eb18e953812b41c3aeb042e666fd3b5c"}, }; var contextItems = new Dictionary(); diff --git a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs index 19619fe1b1..7cc372f5b9 100644 --- a/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs +++ b/test/Sentry.Tests/SentryHttpMessageHandlerTests.cs @@ -5,33 +5,21 @@ namespace Sentry.Tests; /* * NOTE: All tests should be done for both asynchronous `SendAsync` and synchronous `Send` methods. * TODO: Find a way to consolidate these tests cleanly. - */ + */ public class SentryHttpMessageHandlerTests { - [Theory] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1")] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-00")] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-01")] - public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOptions(string traceHeader, string headerName, string expectedValue) + [Fact] + public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_ByDefault() { // Arrange var hub = Substitute.For(); - var failedRequestHandler = Substitute.For(); - var options = new SentryOptions - { - TracePropagationTargets = new List - { - new("localhost") - } - }; hub.GetTraceHeader().ReturnsForAnyArgs( - SentryTraceHeader.Parse(traceHeader)); + SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); - using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); + using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); using var client = new HttpClient(sentryHandler); // Act @@ -41,14 +29,12 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPro // Assert request.Headers.Should().Contain(h => - h.Key == headerName && - string.Concat(h.Value) == expectedValue); + h.Key == "sentry-trace" && + string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); } - [Theory] - [InlineData("sentry-trace")] - [InlineData("traceparent")] - public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPropagationOptions(string headerName) + [Fact] + public async Task SendAsync_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOptions() { // Arrange var hub = Substitute.For(); @@ -57,7 +43,7 @@ public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesn { TracePropagationTargets = new List { - new("foo") + new("localhost") } }; @@ -74,11 +60,13 @@ public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesn using var request = innerHandler.GetRequests().Single(); // Assert - request.Headers.Should().NotContain(h => h.Key == headerName); + request.Headers.Should().Contain(h => + h.Key == "sentry-trace" && + string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); } [Fact] - public async Task SendAsync_SentryTraceHeaderNotSet_SetsBothHeadersHeader_WhenUrlMatchesPropagationOptions() + public async Task SendAsync_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPropagationOptions() { // Arrange var hub = Substitute.For(); @@ -87,12 +75,12 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsBothHeadersHeader_WhenUr { TracePropagationTargets = new List { - new("localhost") + new("foo") } }; hub.GetTraceHeader().ReturnsForAnyArgs( - SentryTraceHeader.Parse("6877cc6ac231622a3d1d518a472a65b8-5e3bc28befdb2e3c")); + SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); @@ -104,19 +92,11 @@ public async Task SendAsync_SentryTraceHeaderNotSet_SetsBothHeadersHeader_WhenUr using var request = innerHandler.GetRequests().Single(); // Assert - // Both headers should be set, see https://github.com/getsentry/team-sdks/issues/41 - request.Headers.Should().Contain(h => - h.Key == SentryTraceHeader.HttpHeaderName && - string.Concat(h.Value) == "6877cc6ac231622a3d1d518a472a65b8-5e3bc28befdb2e3c"); - request.Headers.Should().Contain(h => - h.Key == W3CTraceHeader.HttpHeaderName && - string.Concat(h.Value) == "00-6877cc6ac231622a3d1d518a472a65b8-5e3bc28befdb2e3c-00"); + request.Headers.Should().NotContain(h => h.Key == "sentry-trace"); } - [Theory] - [InlineData("sentry-trace")] - [InlineData("traceparent")] - public async Task SendAsync_SentryTraceHeaderAlreadySet_NotOverwritten(string headerName) + [Fact] + public async Task SendAsync_SentryTraceHeaderAlreadySet_NotOverwritten() { // Arrange var hub = Substitute.For(); @@ -128,7 +108,7 @@ public async Task SendAsync_SentryTraceHeaderAlreadySet_NotOverwritten(string he using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); using var client = new HttpClient(sentryHandler); - client.DefaultRequestHeaders.Add(headerName, "foobar"); + client.DefaultRequestHeaders.Add("sentry-trace", "foobar"); // Act await client.GetAsync("https://localhost/"); @@ -137,7 +117,7 @@ public async Task SendAsync_SentryTraceHeaderAlreadySet_NotOverwritten(string he // Assert request.Headers.Should().Contain(h => - h.Key == headerName && + h.Key == "sentry-trace" && string.Concat(h.Value) == "foobar"); } @@ -311,17 +291,14 @@ public void HandleResponse_SetsSpanData() } #if NET5_0_OR_GREATER - [Theory] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-00")] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-01")] - public void Send_SentryTraceHeaderNotSet_SetsHeader_ByDefault(string traceHeader, string headerName, string expectedValue) + [Fact] + public void Send_SentryTraceHeaderNotSet_SetsHeader_ByDefault() { // Arrange var hub = Substitute.For(); hub.GetTraceHeader().ReturnsForAnyArgs( - SentryTraceHeader.Parse(traceHeader)); + SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); @@ -334,16 +311,12 @@ public void Send_SentryTraceHeaderNotSet_SetsHeader_ByDefault(string traceHeader // Assert request.Headers.Should().Contain(h => - h.Key == headerName && - string.Concat(h.Value) == expectedValue); + h.Key == "sentry-trace" && + string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); } - [Theory] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "sentry-trace", "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1")] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-00")] - [InlineData("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-1", "traceparent", "00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-01")] - public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOptions(string traceHeader, string headerName, string expectedValue) + [Fact] + public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOptions() { // Arrange var hub = Substitute.For(); @@ -357,7 +330,7 @@ public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOpt }; hub.GetTraceHeader().ReturnsForAnyArgs( - SentryTraceHeader.Parse(traceHeader)); + SentryTraceHeader.Parse("75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0")); using var innerHandler = new RecordingHttpMessageHandler(new FakeHttpMessageHandler()); using var sentryHandler = new SentryHttpMessageHandler(hub, options, innerHandler, failedRequestHandler); @@ -370,14 +343,12 @@ public void Send_SentryTraceHeaderNotSet_SetsHeader_WhenUrlMatchesPropagationOpt // Assert request.Headers.Should().Contain(h => - h.Key == headerName && - string.Concat(h.Value) == expectedValue); + h.Key == "sentry-trace" && + string.Concat(h.Value) == "75302ac48a024bde9a3b3734a82e36c8-1000000000000000-0"); } - [Theory] - [InlineData("sentry-trace")] - [InlineData("traceparent")] - public void Send_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPropagationOptions(string headerName) + [Fact] + public void Send_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPropagationOptions() { // Arrange var hub = Substitute.For(); @@ -403,13 +374,11 @@ public void Send_SentryTraceHeaderNotSet_DoesntSetHeader_WhenUrlDoesntMatchesPro using var request = innerHandler.GetRequests().Single(); // Assert - request.Headers.Should().NotContain(h => h.Key == headerName); + request.Headers.Should().NotContain(h => h.Key == "sentry-trace"); } - [Theory] - [InlineData("sentry-trace")] - [InlineData("traceparent")] - public void Send_SentryTraceHeaderAlreadySet_NotOverwritten(string headerName) + [Fact] + public void Send_SentryTraceHeaderAlreadySet_NotOverwritten() { // Arrange var hub = Substitute.For(); @@ -421,7 +390,7 @@ public void Send_SentryTraceHeaderAlreadySet_NotOverwritten(string headerName) using var sentryHandler = new SentryHttpMessageHandler(innerHandler, hub); using var client = new HttpClient(sentryHandler); - client.DefaultRequestHeaders.Add(headerName, "foobar"); + client.DefaultRequestHeaders.Add("sentry-trace", "foobar"); // Act client.Get("https://localhost/"); @@ -430,7 +399,7 @@ public void Send_SentryTraceHeaderAlreadySet_NotOverwritten(string headerName) // Assert request.Headers.Should().Contain(h => - h.Key == headerName && + h.Key == "sentry-trace" && string.Concat(h.Value) == "foobar"); } diff --git a/test/Sentry.Tests/W3CTraceHeaderTests.cs b/test/Sentry.Tests/W3CTraceHeaderTests.cs deleted file mode 100644 index f20bdccbfa..0000000000 --- a/test/Sentry.Tests/W3CTraceHeaderTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Sentry.Tests; - -public class W3CTraceHeaderTests -{ - [Theory] - [InlineData(true, W3CTraceHeader.TraceFlagsSampled)] - [InlineData(false, W3CTraceHeader.TraceFlagsNotSampled)] - [InlineData(null, W3CTraceHeader.TraceFlagsNotSampled)] - public void ToString_ConvertsToW3CFormat(bool? isSampled, string traceFlags) - { - // Arrange - var source = new SentryTraceHeader(SentryId.Parse("75302ac48a024bde9a3b3734a82e36c8"), SpanId.Parse("1000000000000000"), isSampled); - var traceHeader = new W3CTraceHeader(source); - - // Act - var result = traceHeader.ToString(); - - // Assert - result.Should().Be($"00-75302ac48a024bde9a3b3734a82e36c8-1000000000000000-{traceFlags}"); - } - - [Theory] - [InlineData("00-4bc7d217a6721c0e60e85e46d25fb3e5-f51f11f284da5299-01", "4bc7d217a6721c0e60e85e46d25fb3e5", "f51f11f284da5299", true)] - [InlineData("00-3d19f80b6f7da306d7b5652745ec6173-703b42311109c14e-09", "3d19f80b6f7da306d7b5652745ec6173", "703b42311109c14e", true)] - [InlineData("00-992d690c7a3691eb0f409a3ba6ecc0cc-b4f1f8cbcc61a0e5-00", "992d690c7a3691eb0f409a3ba6ecc0cc", "b4f1f8cbcc61a0e5", false)] - [InlineData("00-19938c125f92c552c2e2711393725319-2ce52eea8ffe1335-xz", "19938c125f92c552c2e2711393725319", "2ce52eea8ffe1335", null)] // Invalid trace flags should not cause an exception - [InlineData("00-fba65b9f95900925670373fc2943339e-c5113ff625da6c9a-420", "fba65b9f95900925670373fc2943339e", "c5113ff625da6c9a", null)] // Invalid trace flags should not cause an exception - public void Parse_ValidW3CHeader_ReturnsW3CTraceHeader(string header, string expectedTraceId, string expectedSpanId, bool? expectedIsSampled) - { - // Act - var result = W3CTraceHeader.Parse(header); - - // Assert - result.Should().NotBeNull(); - result.SentryTraceHeader.TraceId.ToString().Should().Be(expectedTraceId); - result.SentryTraceHeader.SpanId.ToString().Should().Be(expectedSpanId); - result.SentryTraceHeader.IsSampled.Should().Be(expectedIsSampled); - } - - [Theory] - [InlineData("")] - [InlineData(" ")] - [InlineData("\n")] - [InlineData(null)] - public void Parse_Returns_Null_WhenHeaderIsNullOrEmpty(string header) - { - // Act - var result = W3CTraceHeader.Parse(header); - - // Assert - result.Should().BeNull(); - } - - [Theory] - [InlineData("00-4bc7d217a6721c0e60e85e46d25fb3e5-1000000000000000")] - [InlineData("01-f5cb855e16344ddd1538d10f82f6a018-a7018579d434fee4")] - [InlineData("01-7f97dee64921546b7c238cb8a0c1209d-a82702bf47683069-00")] - public void Parse_InvalidW3CHeader_ThrowsFormatException(string header) - { - // Act - Action act = () => W3CTraceHeader.Parse(header); - - // Assert - act.Should().Throw(); - } - - [Fact] - public void Null_Source_Throws_ArgumentNullException() - { - // Arrange &Act - Action act = static () => new W3CTraceHeader(null!); - - // Assert - act.Should().Throw().WithParameterName("source").WithMessage("*source*"); - } -}