From 7aa97087dbf8b51198d657ac61159fa3cc5c0ef8 Mon Sep 17 00:00:00 2001 From: hidingmyname Date: Thu, 5 Nov 2026 16:06:49 +0800 Subject: [PATCH 1/2] [API] Percent-encode spaces in Baggage::ToHeader Baggage::ToHeader encoded a space as '+' (form-style), which the W3C Baggage specification does not define; other implementations read it as a literal '+'. Encode spaces as %20 instead. Existing C++ readers already decode %20, so this is safe across versions. Decoding of '+' as space is intentionally left unchanged. Part of #4745 --- CHANGELOG.md | 4 ++++ api/include/opentelemetry/baggage/baggage.h | 4 ---- api/test/baggage/baggage_test.cc | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0bde603a7..3fb914693c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ Increment the: ## [Unreleased] +* [API] `Baggage::ToHeader` now percent-encodes spaces as `%20` instead of + `+`, as required by the W3C Baggage specification. Decoding is unchanged. + [#4745](https://github.com/open-telemetry/opentelemetry-cpp/issues/4745) + * [API/SDK] Add `Enabled()` to synchronous metric instruments (ABI v2). It returns `false` when the Meter is disabled or every matching view uses the Drop aggregation diff --git a/api/include/opentelemetry/baggage/baggage.h b/api/include/opentelemetry/baggage/baggage.h index 8f71262d0f..85c887dcf7 100644 --- a/api/include/opentelemetry/baggage/baggage.h +++ b/api/include/opentelemetry/baggage/baggage.h @@ -320,10 +320,6 @@ class OPENTELEMETRY_EXPORT Baggage { ret.push_back(c); } - else if (c == ' ') - { - ret.push_back('+'); - } else { ret.push_back('%'); diff --git a/api/test/baggage/baggage_test.cc b/api/test/baggage/baggage_test.cc index 3d2110cfa7..57352acf11 100644 --- a/api/test/baggage/baggage_test.cc +++ b/api/test/baggage/baggage_test.cc @@ -201,7 +201,8 @@ TEST(BaggageTest, ValidateExtractHeader) {"v1", "v2; metadata"}}, // key and value are trimmed {"1a-2f%40foo=bar%251,a%2A%2Ffoo-_%2Fbar=bar+4", {"1a-2f@foo", "a*/foo-_/bar"}, - {"bar%1", "bar 4"}}, // decoding is done properly + {"bar%1", "bar 4"}}, // decoding is done properly, '+' still decodes as space + {"k=hello%20world", {"k"}, {"hello world"}}, // %20 decodes as space {"field=foo:bar", {"field"}, {"foo:bar"}}, // colon in value {"mixed=a/b:c?d=e", {"mixed"}, {"a/b:c?d=e"}}, // mixed special characters {"k1=v1,invalidmember,k2=v2", {"k1", "k2"}, {"v1", "v2"}}, // invalid member is skipped @@ -256,10 +257,10 @@ TEST(BaggageTest, ValidateInjectHeader) {{"k3", "k2", "k1"}, {"", "v2", "v1"}, "k1=v1,k2=v2,k3="}, // empty value {{"1a-2f@foo", "a*/foo-_/bar"}, {"bar%1", "bar 4"}, - "a%2A%2Ffoo-_%2Fbar=bar+4,1a-2f%40foo=bar%251"}, // encoding is done properly + "a%2A%2Ffoo-_%2Fbar=bar%204,1a-2f%40foo=bar%251"}, // encoding is done properly {{"foo 1"}, {"bar 1; metadata ; flag=encoded%20value"}, - "foo+1=bar+1; metadata ; flag=encoded%20value"}}; + "foo%201=bar%201; metadata ; flag=encoded%20value"}}; for (auto &testcase : testcases) { @@ -272,6 +273,17 @@ TEST(BaggageTest, ValidateInjectHeader) } } +TEST(BaggageTest, SpaceIsPercentEncoded) +{ + auto baggage = Baggage().Set("k", "hello world"); + auto header = baggage->ToHeader(); + EXPECT_EQ(header, "k=hello%20world"); + + std::string value; + EXPECT_TRUE(Baggage::FromHeader(header)->GetValue("k", value)); + EXPECT_EQ(value, "hello world"); +} + TEST(BaggageTest, BaggageGet) { auto header = header_with_custom_entries(Baggage::kMaxKeyValuePairs); From a93090a045d2cfc1a47ccff99b39feb68955434a Mon Sep 17 00:00:00 2001 From: Marc Alff Date: Sat, 7 Nov 2026 19:15:54 +0100 Subject: [PATCH 2/2] Update CHANGELOG.md Co-authored-by: Mateen Anjum --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fb914693c..56be7fac77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ Increment the: * [API] `Baggage::ToHeader` now percent-encodes spaces as `%20` instead of `+`, as required by the W3C Baggage specification. Decoding is unchanged. - [#4745](https://github.com/open-telemetry/opentelemetry-cpp/issues/4745) + [#4765](https://github.com/open-telemetry/opentelemetry-cpp/pull/4765) * [API/SDK] Add `Enabled()` to synchronous metric instruments (ABI v2). It returns `false` when the Meter is disabled or every matching view uses the