Skip to content
Merged
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
9 changes: 9 additions & 0 deletions src/OpenTelemetry.Api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ Notes](../../RELEASENOTES.md).
align with the W3C Trace Context specification.
([#7065](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7065))

* Fixed `BaggagePropagator` to trim optional whitespace (OWS) around `=`
separators when parsing the `baggage` header, as required by the
[W3C Baggage specification](https://www.w3.org/TR/baggage/).
([#7009](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7009))

* Fixed `BaggagePropagator` to strip baggage properties (e.g. `;metadata`)
from values when parsing the `baggage` header.
([#7009](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7009))

## 1.15.2

Released 2026-Apr-08
Expand Down
16 changes: 14 additions & 2 deletions src/OpenTelemetry.Api/Context/Propagation/BaggagePropagator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,20 @@ internal static bool TryExtractBaggage(
continue;
}

var key = DecodeIfNeeded(pair.Slice(0, separatorIndex));
var value = DecodeIfNeeded(pair.Slice(separatorIndex + 1));
var rawKey = pair.Slice(0, separatorIndex).Trim();

var rawValue = pair.Slice(separatorIndex + 1);

var semicolonIndex = rawValue.IndexOf(';');
if (semicolonIndex >= 0)
Comment thread
martincostello marked this conversation as resolved.
{
rawValue = rawValue.Slice(0, semicolonIndex);
}

rawValue = rawValue.Trim();

var key = DecodeIfNeeded(rawKey);
var value = DecodeIfNeeded(rawValue);

if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public class BaggagePropagatorBenchmarks
[Params(false, true)]
public bool UseSpecialChars { get; set; }

/// <summary>
/// Gets or sets the header style used in each benchmark run.
/// </summary>
[Params("WithOWSAndProperties", "Clean")]
public string HeaderStyle { get; set; } = "Clean";

public Dictionary<string, string> ExtractCarrier { get; private set; } = [];

public Dictionary<string, string> InjectCarrier { get; private set; } = [];
Expand All @@ -43,8 +49,11 @@ public void Setup()
: ($"key{i}", $"value{i}"));
}

var baggageHeader = string.Join(",", Items().Select(p =>
$"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}"));
var baggageHeader = this.HeaderStyle switch
{
"WithOWSAndProperties" => string.Join(" , ", Items().Select(p => $"{Uri.EscapeDataString(p.Key)} = {Uri.EscapeDataString(p.Value)} ; prop1 ; propKey=propValue")),
_ => string.Join(",", Items().Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")),
};

this.ExtractCarrier = new Dictionary<string, string>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ public void ValidateEmptyValueSkipped()
Assert.Empty(propagationContext.Baggage.GetBaggage());
}

[Fact(Skip = "Fails due to spec mismatch, tracked in https://github.com/open-telemetry/opentelemetry-dotnet/issues/5210")]
[Fact]
public void ValidateOWSOnExtraction()
{
var carrier = new Dictionary<string, string>
Expand All @@ -248,7 +248,7 @@ public void ValidateOWSOnExtraction()
Assert.Equal("SomeValue2", baggage[1].Value);
}

[Fact(Skip = "Fails due to spec mismatch, tracked in https://github.com/open-telemetry/opentelemetry-dotnet/issues/5210")]
[Fact]
public void ValidateSemicolonMetadataIgnoredOnExtraction()
{
var carrier = new Dictionary<string, string>
Expand All @@ -265,6 +265,39 @@ public void ValidateSemicolonMetadataIgnoredOnExtraction()
Assert.Equal("SomeValue", baggage.Value);
}

[Fact]
public void ValidateOptionalWhiteSpaceExtractionDoesNotCorruptOnReinjection()
{
// Simulates a header emitted by .NET 10's W3C propagator
var carrier = new Dictionary<string, string>
{
{ BaggagePropagator.BaggageHeaderName, "correlationId = 12345, userId = user-abc" },
};

var extractedContext = this.baggage.Extract(default, carrier, Getter);

var outboundCarrier = new Dictionary<string, string>();
this.baggage.Inject(extractedContext, outboundCarrier, Setter);

Assert.Equal("correlationId=12345,userId=user-abc", outboundCarrier[BaggagePropagator.BaggageHeaderName]);
}

[Fact]
public void ValidateOptionalWhiteSpaceBeforeSemicolonIgnored()
{
var carrier = new Dictionary<string, string>
{
{ BaggagePropagator.BaggageHeaderName, "SomeKey=SomeValue ; propertyKey=propertyValue" },
Comment thread
martincostello marked this conversation as resolved.
};

var propagationContext = this.baggage.Extract(default, carrier, Getter);

var baggage = Assert.Single(propagationContext.Baggage.GetBaggage());

Assert.Equal("SomeKey", baggage.Key);
Assert.Equal("SomeValue", baggage.Value);
}

[Fact]
public void ValidatePercentEncoding()
{
Expand Down
Loading