Skip to content

Commit 541dd2c

Browse files
authored
Fix: Prevent broken traces when HttpClient content headers contain tracing headers. (#1843) (#1888)
1 parent 737b4f1 commit 541dd2c

File tree

3 files changed

+34
-1
lines changed

3 files changed

+34
-1
lines changed

src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/HttpClient/SendAsync.cs

+3
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ private static void TryAttachHeadersToRequest(IAgent agent, HttpRequestMessage h
135135
{
136136
var setHeaders = new Action<HttpRequestMessage, string, string>((carrier, key, value) =>
137137
{
138+
// Content headers should not contain the expected header keys, but sometimes they do,
139+
// and their presence can cause problems downstream by having 2 values for the same key.
140+
carrier.Content?.Headers?.Remove(key);
138141
// "Add" will throw if value exists, so we must remove it first
139142
carrier.Headers?.Remove(key);
140143
carrier.Headers?.Add(key, value);

tests/Agent/IntegrationTests/Applications/AspNetCoreDistTracingApplication/Controllers/SecondCallController.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,17 @@ public async Task<string> CallNext(string nextUrl)
1818
{
1919
using (var httpClient = new HttpClient())
2020
{
21-
var result = await httpClient.GetStringAsync(nextUrl);
21+
var requestMessage = new HttpRequestMessage(HttpMethod.Get, nextUrl);
22+
// Purposely include bad traceparent headers to try to break the distributed trace
23+
requestMessage.Headers.TryAddWithoutValidation("traceparent", "bad-traceparent-requestheader");
24+
requestMessage.Content = new StringContent(string.Empty);
25+
// This is not a valid content header, but there are some cases where this has happened
26+
requestMessage.Content.Headers.TryAddWithoutValidation("traceparent", "bad-traceparent-contentheader");
27+
28+
//var result = await httpClient.GetStringAsync(nextUrl);
29+
var response = await httpClient.SendAsync(requestMessage);
30+
var result = await response.Content.ReadAsStringAsync();
31+
2232
return result;
2333
}
2434
}

tests/Agent/IntegrationTests/IntegrationTests/DistributedTracing/W3CInstrumentationTests/HttpClientW3CTestsNetCore.cs

+20
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,15 @@ public void Test()
6464
var receiverAppTxEvents = _fixture.SecondCallApplication.AgentLog.GetTransactionEvents().FirstOrDefault();
6565
Assert.NotNull(receiverAppTxEvents);
6666

67+
var lastCallAppTxEvents = _fixture.RemoteApplication.AgentLog.GetTransactionEvents().FirstOrDefault();
68+
Assert.NotNull(lastCallAppTxEvents);
69+
6770
var senderAppSpanEvents = _fixture.FirstCallApplication.AgentLog.GetSpanEvents();
6871
var receiverAppSpanEvents = _fixture.SecondCallApplication.AgentLog.GetSpanEvents();
72+
var lastCallAppSpanEvents = _fixture.RemoteApplication.AgentLog.GetSpanEvents();
6973

7074
Assert.Equal(senderAppTxEvent.IntrinsicAttributes["guid"], receiverAppTxEvents.IntrinsicAttributes["parentId"]);
75+
Assert.Equal(receiverAppTxEvents.IntrinsicAttributes["guid"], lastCallAppTxEvents.IntrinsicAttributes["parentId"]);
7176

7277
foreach (var span in senderAppSpanEvents)
7378
{
@@ -79,10 +84,18 @@ public void Test()
7984
Assert.Equal(TestTraceId, span.IntrinsicAttributes["traceId"]);
8085
}
8186

87+
foreach (var span in lastCallAppSpanEvents)
88+
{
89+
Assert.Equal(TestTraceId, span.IntrinsicAttributes["traceId"]);
90+
}
91+
8292
var senderRootSpanEvent = senderAppSpanEvents.Where(@event => @event?.IntrinsicAttributes?["name"]?.ToString() == "WebTransaction/MVC/FirstCall/CallNext/{nextUrl}").FirstOrDefault();
8393
var externalSpanEvent = senderAppSpanEvents.Where(@event => @event?.IntrinsicAttributes?["name"]?.ToString() == "External/localhost/Stream/GET").FirstOrDefault();
8494

8595
var receiverRootSpanEvent = receiverAppSpanEvents.Where(@event => @event?.IntrinsicAttributes?["name"]?.ToString() == "WebTransaction/MVC/SecondCall/CallNext/{nextUrl}").FirstOrDefault();
96+
var receiverExternalSpanEvent = receiverAppSpanEvents.Where(@event => @event?.IntrinsicAttributes?["name"]?.ToString() == "External/localhost/Stream/GET").FirstOrDefault();
97+
98+
var lastRootSpanEvent = lastCallAppSpanEvents.Where(@event => @event?.IntrinsicAttributes?["name"]?.ToString() == "WebTransaction/MVC/LastCall/CallEnd").FirstOrDefault();
8699

87100
Assert.NotNull(senderRootSpanEvent);
88101
Assert.Equal(TestTracingVendors, senderRootSpanEvent.IntrinsicAttributes["tracingVendors"]);
@@ -97,6 +110,13 @@ public void Test()
97110
Assert.Equal(externalSpanEvent.IntrinsicAttributes["guid"], receiverRootSpanEvent.IntrinsicAttributes["trustedParentId"]);
98111
Assert.True(AttributeComparer.IsEqualTo(senderAppTxEvent.IntrinsicAttributes["priority"], receiverRootSpanEvent.IntrinsicAttributes["priority"]));
99112

113+
Assert.NotNull(lastRootSpanEvent);
114+
Assert.Equal(TestTracingVendors, lastRootSpanEvent.IntrinsicAttributes["tracingVendors"]);
115+
Assert.Equal(receiverExternalSpanEvent.IntrinsicAttributes["guid"], lastRootSpanEvent.IntrinsicAttributes["parentId"]);
116+
Assert.Equal(receiverExternalSpanEvent.IntrinsicAttributes["guid"], lastCallAppTxEvents.IntrinsicAttributes["parentSpanId"]);
117+
Assert.Equal(receiverExternalSpanEvent.IntrinsicAttributes["guid"], lastRootSpanEvent.IntrinsicAttributes["trustedParentId"]);
118+
Assert.True(AttributeComparer.IsEqualTo(receiverAppTxEvents.IntrinsicAttributes["priority"], lastRootSpanEvent.IntrinsicAttributes["priority"]));
119+
100120
var senderExpectedMetrics = new List<Assertions.ExpectedMetric>
101121
{
102122
new Assertions.ExpectedMetric { metricName = @"Supportability/DistributedTrace/CreatePayload/Success", callCount = 1 },

0 commit comments

Comments
 (0)