Skip to content

[API] Baggage - Trim white spaces and check for semi colon which isn't allowed octet#7009

Open
nabutabu wants to merge 13 commits intoopen-telemetry:mainfrom
nabutabu:trim-baggage-key-and-value
Open

[API] Baggage - Trim white spaces and check for semi colon which isn't allowed octet#7009
nabutabu wants to merge 13 commits intoopen-telemetry:mainfrom
nabutabu:trim-baggage-key-and-value

Conversation

@nabutabu
Copy link
Copy Markdown
Contributor

@nabutabu nabutabu commented Mar 27, 2026

Towards #6816, #5210

Changes

  • Fixed BaggagePropagator to trim optional whitespace (OWS) around = separators
    when parsing the baggage header, as required by the
    W3C Baggage specification. This caused
    incompatibility with .NET 10's default W3C propagator, which emits headers with
    OWS (e.g. key = value), resulting in spaces being URL-encoded as + in
    outbound headers received by downstream services.
  • Fixed BaggagePropagator to strip baggage properties (;metadata) from values
    when parsing the baggage header. Semicolon is not a valid baggage-octet per
    the W3C Baggage specification and must be interpreted as the start of a property,
    not part of the value.
    (#5210)

Merge requirement checklist

  • CONTRIBUTING guidelines followed (license requirements, nullable enabled, static analysis, etc.)
  • Unit tests added/updated
  • Appropriate CHANGELOG.md files updated for non-trivial changes
    I don't think this is non-trivial but let me know if I need to do this
  • Changes in public API reviewed (if applicable)

@github-actions github-actions bot added the pkg:OpenTelemetry.Api Issues related to OpenTelemetry.Api NuGet package label Mar 27, 2026
@nabutabu nabutabu marked this pull request as ready for review March 27, 2026 05:47
@nabutabu nabutabu requested a review from a team as a code owner March 27, 2026 05:47
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.83%. Comparing base (6fc51c6) to head (3cca91d).
✅ All tests successful. No failed tests found.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #7009      +/-   ##
==========================================
+ Coverage   88.79%   88.83%   +0.03%     
==========================================
  Files         271      271              
  Lines       12970    12976       +6     
==========================================
+ Hits        11517    11527      +10     
+ Misses       1453     1449       -4     
Flag Coverage Δ
unittests-Project-Experimental 88.76% <100.00%> (+0.02%) ⬆️
unittests-Project-Stable 88.73% <100.00%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...metry.Api/Context/Propagation/BaggagePropagator.cs 89.01% <100.00%> (+0.77%) ⬆️

... and 3 files with indirect coverage changes

Comment thread src/OpenTelemetry.Api/Context/Propagation/BaggagePropagator.cs Outdated
Comment thread test/OpenTelemetry.Api.Tests/Context/Propagation/BaggagePropagatorTests.cs Outdated
Comment thread test/OpenTelemetry.Api.Tests/Context/Propagation/BaggagePropagatorTests.cs Outdated
Comment thread test/OpenTelemetry.Api.Tests/Context/Propagation/BaggagePropagatorTests.cs Outdated
@martincostello
Copy link
Copy Markdown
Member

Please add a CHANGELOG entry.

Comment thread src/OpenTelemetry.Api/CHANGELOG.md Outdated
Comment thread src/OpenTelemetry.Api/CHANGELOG.md Outdated
Comment thread test/OpenTelemetry.Api.Tests/Context/Propagation/BaggagePropagatorTests.cs Outdated
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

This PR was marked stale due to lack of activity and will be closed in 7 days. Commenting or pushing will instruct the bot to automatically remove the label. This bot runs once per day.

@github-actions github-actions bot added the Stale Issues and pull requests which have been flagged for closing due to inactivity label Apr 9, 2026
@Kielek Kielek removed the Stale Issues and pull requests which have been flagged for closing due to inactivity label Apr 9, 2026
@martincostello
Copy link
Copy Markdown
Member

@nabutabu Please resolve the merge conflicts and make any necessary adjustments for the new tests, then please also run the benchmarks added by #7058 to compare the difference between the base branch and with these changes.

@nabutabu
Copy link
Copy Markdown
Contributor Author

nabutabu commented Apr 14, 2026

Benchmark Results Summary

Completed benchmarks with changes

  • OS: Windows 11 (10.0.26100.8037/24H2)
  • SDK: .NET SDK 10.0.201
  • Runtime: .NET 10.0.5 (10.0.526.15411), X64 RyuJIT x86-64-v3

Raw Data

Before my changes:

| Method  | ItemCount | UseSpecialChars | Mean        | Error     | StdDev     | Gen0   | Gen1   | Allocated |
|-------- |---------- |---------------- |------------:|----------:|-----------:|-------:|-------:|----------:|
| Extract | 1         | False           |    88.00 ns |  1.793 ns |   4.724 ns | 0.0411 |      - |     344 B |
| Inject  | 1         | False           |    55.07 ns |  1.125 ns |   1.052 ns | 0.0181 |      - |     152 B |
| Extract | 1         | True            |   256.89 ns |  5.165 ns |   7.571 ns | 0.0925 |      - |     776 B |
| Inject  | 1         | True            |   167.55 ns |  3.383 ns |   7.209 ns | 0.0515 |      - |     432 B |
| Extract | 5         | False           |   314.40 ns |  6.263 ns |   7.921 ns | 0.1049 | 0.0005 |     880 B |
| Inject  | 5         | False           |   190.14 ns |  3.809 ns |   4.817 ns | 0.0582 |      - |     488 B |
| Extract | 5         | True            | 1,152.35 ns | 22.895 ns |  40.098 ns | 0.3624 | 0.0019 |    3040 B |
| Inject  | 5         | True            |   660.12 ns | 12.979 ns |  25.620 ns | 0.2298 |      - |    1928 B |
| Extract | 20        | False           | 1,065.98 ns | 21.110 ns |  49.345 ns | 0.4272 | 0.0076 |    3576 B |
| Inject  | 20        | False           |   639.94 ns | 12.603 ns |  23.671 ns | 0.2384 | 0.0010 |    2000 B |
| Extract | 20        | True            | 3,973.36 ns | 78.095 ns | 152.318 ns | 1.4648 | 0.0305 |   12296 B |
| Inject  | 20        | True            | 2,329.31 ns | 44.912 ns | 105.863 ns | 0.8163 | 0.0038 |    6832 B |

After my changes


| Method  | ItemCount | UseSpecialChars | Mean        | Error     | StdDev     | Gen0   | Gen1   | Allocated |
|-------- |---------- |---------------- |------------:|----------:|-----------:|-------:|-------:|----------:|
| Extract | 1         | False           |    94.54 ns |  1.810 ns |   1.937 ns | 0.0411 |      - |     344 B |
| Inject  | 1         | False           |    58.25 ns |  1.173 ns |   1.152 ns | 0.0181 |      - |     152 B |
| Extract | 1         | True            |   260.31 ns |  5.219 ns |   5.126 ns | 0.0925 |      - |     776 B |
| Inject  | 1         | True            |   155.90 ns |  2.519 ns |   2.233 ns | 0.0515 |      - |     432 B |
| Extract | 5         | False           |   318.27 ns |  6.388 ns |  10.132 ns | 0.1049 | 0.0005 |     880 B |
| Inject  | 5         | False           |   184.44 ns |  3.389 ns |   4.968 ns | 0.0582 |      - |     488 B |
| Extract | 5         | True            | 1,109.24 ns | 21.478 ns |  24.734 ns | 0.3624 | 0.0019 |    3040 B |
| Inject  | 5         | True            |   647.07 ns | 11.997 ns |  19.028 ns | 0.2298 |      - |    1928 B |
| Extract | 20        | False           | 1,214.97 ns | 24.234 ns |  42.444 ns | 0.4272 | 0.0076 |    3576 B |
| Inject  | 20        | False           |   634.13 ns | 12.609 ns |  25.182 ns | 0.2384 | 0.0010 |    2000 B |
| Extract | 20        | True            | 4,057.60 ns | 79.860 ns | 131.212 ns | 1.4648 | 0.0305 |   12296 B |
| Inject  | 20        | True            | 2,304.73 ns | 45.614 ns | 112.746 ns | 0.8163 | 0.0038 |    6832 B |

@martincostello One other thing, I was looking at how the benchmarking is working. baggageHeader is being built using Uri.EscapeDataString which should mean that my semiColonIndex will always be -1 and that if statement is never triggered. My code only ever hits the .Trim() and IndexOf(';') calls as pure overhead on already-clean data.
Should the benchmarking change?

Execution Summary

  • Run time: 00:07:32 (452.37 sec)
  • Global total time: 00:08:23 (503.12 sec)
  • Executed benchmarks: 12

@martincostello
Copy link
Copy Markdown
Member

If the code path you're touching isn't benchmarked, then let's add another that deals with that path.

@Kielek Kielek changed the title Trim white spaces and check for semi colon which isn't allowed octet [API] Baggage - Trim white spaces and check for semi colon which isn't allowed octet Apr 14, 2026
@nabutabu
Copy link
Copy Markdown
Contributor Author

nabutabu commented Apr 14, 2026

Benchmark changes

I changed how the baggageHeader is being built, keys and values need to be trimmed now and semicolon needs to be removed

var baggageHeader = this.HeaderStyle switch
        {
            "W3C" => 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)}")),
        };

Raw Data

After my changes

| Method  | ItemCount | UseSpecialChars | HeaderStyle | Mean        | Error      | StdDev     | Median      | Gen0   | Gen1   | Allocated |
|-------- |---------- |---------------- |------------ |------------:|-----------:|-----------:|------------:|-------:|-------:|----------:|
| Extract | 1         | False           | Properties  |    93.20 ns |   1.793 ns |   3.539 ns |    93.46 ns | 0.0411 |      - |     344 B |
| Inject  | 1         | False           | Properties  |    58.98 ns |   1.154 ns |   2.357 ns |    58.94 ns | 0.0181 |      - |     152 B |
| Extract | 1         | False           | W3C         |   131.84 ns |   2.616 ns |   5.743 ns |   130.45 ns | 0.0410 |      - |     344 B |
| Inject  | 1         | False           | W3C         |    58.00 ns |   1.109 ns |   2.188 ns |    58.06 ns | 0.0181 |      - |     152 B |
| Extract | 1         | True            | Properties  |   259.62 ns |   5.170 ns |   9.961 ns |   261.21 ns | 0.0925 |      - |     776 B |
| Inject  | 1         | True            | Properties  |   178.68 ns |   3.607 ns |   5.615 ns |   179.06 ns | 0.0515 |      - |     432 B |
| Extract | 1         | True            | W3C         |   276.62 ns |   5.531 ns |  11.545 ns |   275.56 ns | 0.0925 |      - |     776 B |
| Inject  | 1         | True            | W3C         |   158.81 ns |   3.038 ns |   3.120 ns |   159.49 ns | 0.0515 |      - |     432 B |
| Extract | 5         | False           | Properties  |   339.59 ns |   6.237 ns |  11.087 ns |   340.83 ns | 0.1049 | 0.0005 |     880 B |
| Inject  | 5         | False           | Properties  |   193.81 ns |   3.626 ns |   3.392 ns |   194.26 ns | 0.0582 |      - |     488 B |
| Extract | 5         | False           | W3C         |   383.79 ns |   7.643 ns |  10.962 ns |   385.37 ns | 0.1049 | 0.0005 |     880 B |
| Inject  | 5         | False           | W3C         |   202.26 ns |   3.924 ns |   4.199 ns |   203.42 ns | 0.0582 |      - |     488 B |
| Extract | 5         | True            | Properties  | 1,135.22 ns |  22.237 ns |  26.471 ns | 1,145.79 ns | 0.3624 | 0.0019 |    3040 B |
| Inject  | 5         | True            | Properties  |   658.65 ns |  12.994 ns |  23.761 ns |   656.59 ns | 0.2298 |      - |    1928 B |
| Extract | 5         | True            | W3C         | 1,313.78 ns |  26.065 ns |  61.946 ns | 1,304.28 ns | 0.3624 | 0.0019 |    3040 B |
| Inject  | 5         | True            | W3C         |   655.13 ns |  13.039 ns |  30.478 ns |   650.61 ns | 0.2298 |      - |    1928 B |
| Extract | 20        | False           | Properties  | 1,187.44 ns |  22.286 ns |  23.845 ns | 1,185.01 ns | 0.4272 | 0.0076 |    3576 B |
| Inject  | 20        | False           | Properties  |   630.29 ns |  12.575 ns |  22.353 ns |   628.44 ns | 0.2384 | 0.0010 |    2000 B |
| Extract | 20        | False           | W3C         | 1,514.75 ns |  30.307 ns |  49.795 ns | 1,507.19 ns | 0.4272 | 0.0076 |    3576 B |
| Inject  | 20        | False           | W3C         |   625.41 ns |  12.510 ns |  20.202 ns |   627.05 ns | 0.2384 | 0.0010 |    2000 B |
| Extract | 20        | True            | Properties  | 4,295.97 ns |  85.334 ns | 140.207 ns | 4,271.16 ns | 1.4648 | 0.0305 |   12296 B |
| Inject  | 20        | True            | Properties  | 2,308.18 ns |  45.944 ns |  80.467 ns | 2,318.06 ns | 0.8163 | 0.0038 |    6832 B |
| Extract | 20        | True            | W3C         | 4,521.04 ns |  89.422 ns | 174.410 ns | 4,475.90 ns | 1.4648 | 0.0305 |   12296 B |
| Inject  | 20        | True            | W3C         | 2,617.29 ns | 169.164 ns | 454.448 ns | 2,463.39 ns | 0.8163 | 0.0038 |    6832 B |

Before my changes

| Method  | ItemCount | UseSpecialChars | HeaderStyle | Mean        | Error      | StdDev     | Gen0   | Gen1   | Allocated |
|-------- |---------- |---------------- |------------ |------------:|-----------:|-----------:|-------:|-------:|----------:|
| Extract | 1         | False           | Properties  |    88.07 ns |   1.791 ns |   4.461 ns | 0.0411 |      - |     344 B |
| Inject  | 1         | False           | Properties  |    56.36 ns |   1.098 ns |   1.893 ns | 0.0181 |      - |     152 B |
| Extract | 1         | False           | W3C         |   150.05 ns |   2.988 ns |   4.739 ns | 0.0477 |      - |     400 B |
| Inject  | 1         | False           | W3C         |    56.44 ns |   1.137 ns |   1.771 ns | 0.0181 |      - |     152 B |
| Extract | 1         | True            | Properties  |   262.68 ns |   5.287 ns |  10.311 ns | 0.0925 |      - |     776 B |
| Inject  | 1         | True            | Properties  |   145.90 ns |   2.863 ns |   4.196 ns | 0.0515 |      - |     432 B |
| Extract | 1         | True            | W3C         |   332.80 ns |   6.616 ns |  13.809 ns | 0.1163 |      - |     976 B |
| Inject  | 1         | True            | W3C         |   157.18 ns |   3.092 ns |   4.993 ns | 0.0515 |      - |     432 B |
| Extract | 5         | False           | Properties  |   311.59 ns |   6.169 ns |  12.462 ns | 0.1049 | 0.0005 |     880 B |
| Inject  | 5         | False           | Properties  |   183.11 ns |   3.651 ns |   4.618 ns | 0.0582 |      - |     488 B |
| Extract | 5         | False           | W3C         |   323.20 ns |   6.434 ns |  13.850 ns | 0.1421 | 0.0010 |    1192 B |
| Inject  | 5         | False           | W3C         |   189.60 ns |   3.796 ns |   5.681 ns | 0.0582 |      - |     488 B |
| Extract | 5         | True            | Properties  | 1,079.93 ns |  20.724 ns |  43.714 ns | 0.3624 | 0.0019 |    3040 B |
| Inject  | 5         | True            | Properties  |   663.04 ns |  13.297 ns |  22.937 ns | 0.2298 |      - |    1928 B |
| Extract | 5         | True            | W3C         | 1,498.98 ns |  29.920 ns |  33.256 ns | 0.4902 | 0.0038 |    4104 B |
| Inject  | 5         | True            | W3C         |   650.53 ns |  12.781 ns |  25.524 ns | 0.2298 |      - |    1928 B |
| Extract | 20        | False           | Properties  | 1,031.16 ns |  20.383 ns |  36.232 ns | 0.4272 | 0.0076 |    3576 B |
| Inject  | 20        | False           | Properties  |   627.08 ns |  12.454 ns |  18.641 ns | 0.2384 | 0.0010 |    2000 B |
| Extract | 20        | False           | W3C         | 1,250.50 ns |  25.037 ns |  46.408 ns | 0.5779 | 0.0172 |    4848 B |
| Inject  | 20        | False           | W3C         |   609.47 ns |  12.170 ns |  22.857 ns | 0.2384 | 0.0010 |    2000 B |
| Extract | 20        | True            | Properties  | 3,990.27 ns |  79.320 ns | 121.130 ns | 1.4648 | 0.0305 |   12296 B |
| Inject  | 20        | True            | Properties  | 2,349.57 ns |  46.925 ns |  97.950 ns | 0.8163 | 0.0038 |    6832 B |
| Extract | 20        | True            | W3C         | 5,659.98 ns | 110.454 ns | 122.769 ns | 1.9989 | 0.0610 |   16744 B |
| Inject  | 20        | True            | W3C         | 2,330.07 ns |  46.210 ns |  97.474 ns | 0.8163 | 0.0038 |    6832 B |

These two rows are standouts:

| After | Extract | 20        | True            | W3C         | 4,521.04 ns |  89.422 ns | 174.410 ns | 4,475.90 ns | 1.4648 | 0.0305 |   12296 B |
| Before | Extract | 20        | True            | W3C         | 5,659.98 ns | 110.454 ns | 122.769 ns | 1.9989 | 0.0610 |   16744 B |

Interestingly mean time after my changes has decreased, I thought this would be a slight deterioration.
I believe these improvements are simply from the length of string stored in the Baggage being smaller before calling DecodeIfNeeded. "Value" was especially carrying the entire metadata with it which has now become just value. Let me know if I should also commit the benchmark changes, benchmarks took about 16 minutes to run so this wasn't the most effecient

@github-actions github-actions bot added the perf Performance related label Apr 14, 2026
Comment thread test/Benchmarks/Context/Propagation/BaggagePropagatorBenchmarks.cs
Comment thread src/OpenTelemetry.Api/Context/Propagation/BaggagePropagator.cs
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the W3C baggage header parsing in BaggagePropagator to be compatible with optional whitespace around = and to treat ;... as baggage properties rather than part of the value, aligning behavior with the W3C Baggage spec and addressing reported interoperability issues.

Changes:

  • Trim optional whitespace (OWS) around = when extracting baggage key/value pairs.
  • Strip ;properties from extracted baggage values.
  • Unskip/add unit tests covering OWS + semicolon-property extraction, plus extend benchmark scenarios.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
test/OpenTelemetry.Api.Tests/Context/Propagation/BaggagePropagatorTests.cs Enables previously skipped spec-alignment tests and adds reinjection/regression coverage.
test/Benchmarks/Context/Propagation/BaggagePropagatorBenchmarks.cs Adds a benchmark parameter to vary header formatting (OWS/properties) during extraction.
src/OpenTelemetry.Api/Context/Propagation/BaggagePropagator.cs Implements OWS trimming and semicolon-property stripping during extraction.
src/OpenTelemetry.Api/CHANGELOG.md Adds changelog entries describing the baggage parsing fixes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/Benchmarks/Context/Propagation/BaggagePropagatorBenchmarks.cs Outdated
Comment thread src/OpenTelemetry.Api/CHANGELOG.md Outdated
Copy link
Copy Markdown
Member

@Kielek Kielek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed description to make Towards instead of Fixes, as there is still some test/requirements to be fixed.

CHANGELOG fixed.

In general changes LGTM. Could you please double check Copilot Comment related to Properites-benchamrk stuff?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

perf Performance related pkg:OpenTelemetry.Api Issues related to OpenTelemetry.Api NuGet package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants