Skip to content

Commit 45958d0

Browse files
AndreyTretyakshirhatti
authored andcommitted
Preserve client order of activity baggage items (#26302)
1 parent 0a52bb7 commit 45958d0

File tree

2 files changed

+43
-6
lines changed

2 files changed

+43
-6
lines changed

src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,14 @@ private Activity StartActivity(HttpContext httpContext, out bool hasDiagnosticLi
267267
// We expect baggage to be empty by default
268268
// Only very advanced users will be using it in near future, we encourage them to keep baggage small (few items)
269269
string[] baggage = headers.GetCommaSeparatedValues(HeaderNames.CorrelationContext);
270-
if (baggage.Length > 0)
270+
271+
// AddBaggage adds items at the beginning of the list, so we need to add them in reverse to keep the same order as the client
272+
// An order could be important if baggage has two items with the same key (that is allowed by the contract)
273+
for (var i = baggage.Length - 1; i >= 0; i--)
271274
{
272-
foreach (var item in baggage)
275+
if (NameValueHeaderValue.TryParse(baggage[i], out var baggageItem))
273276
{
274-
if (NameValueHeaderValue.TryParse(item, out var baggageItem))
275-
{
276-
activity.AddBaggage(baggageItem.Name.ToString(), HttpUtility.UrlDecode(baggageItem.Value.ToString()));
277-
}
277+
activity.AddBaggage(baggageItem.Name.ToString(), HttpUtility.UrlDecode(baggageItem.Value.ToString()));
278278
}
279279
}
280280
}

src/Hosting/Hosting/test/HostingApplicationDiagnosticsTests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,43 @@ public void ActivityParentIdAndBaggeReadFromHeaders()
301301
Assert.Contains(Activity.Current.Baggage, pair => pair.Key == "Key2" && pair.Value == "value2");
302302
}
303303

304+
[Fact]
305+
public void ActivityBaggagePreservesItemsOrder()
306+
{
307+
var diagnosticListener = new DiagnosticListener("DummySource");
308+
var hostingApplication = CreateApplication(out var features, diagnosticListener: diagnosticListener);
309+
310+
diagnosticListener.Subscribe(new CallbackDiagnosticListener(pair => { }),
311+
s =>
312+
{
313+
if (s.StartsWith("Microsoft.AspNetCore.Hosting.HttpRequestIn"))
314+
{
315+
return true;
316+
}
317+
return false;
318+
});
319+
320+
features.Set<IHttpRequestFeature>(new HttpRequestFeature()
321+
{
322+
Headers = new HeaderDictionary()
323+
{
324+
{"Request-Id", "ParentId1"},
325+
{"Correlation-Context", "Key1=value1, Key2=value2, Key1=value3"} // duplicated keys allowed by the contract
326+
}
327+
});
328+
hostingApplication.CreateContext(features);
329+
Assert.Equal("Microsoft.AspNetCore.Hosting.HttpRequestIn", Activity.Current.OperationName);
330+
331+
var expectedBaggage = new []
332+
{
333+
KeyValuePair.Create("Key1","value1"),
334+
KeyValuePair.Create("Key2","value2"),
335+
KeyValuePair.Create("Key1","value3")
336+
};
337+
338+
Assert.Equal(expectedBaggage, Activity.Current.Baggage);
339+
}
340+
304341
[Fact]
305342
public void ActivityBaggageValuesAreUrlDecodedFromHeaders()
306343
{

0 commit comments

Comments
 (0)