1
// Copyright The OpenTelemetry Authors
2 // SPDX-License-Identifier: Apache-2.0
8 #include "opentelemetry/common/kv_properties.h"
9 #include "opentelemetry/nostd/shared_ptr.h"
10 #include "opentelemetry/nostd/string_view.h"
11 #include "opentelemetry/version.h"
13 OPENTELEMETRY_BEGIN_NAMESPACE
21 static constexpr size_t kMaxKeyValuePairs
= 180;
22 static constexpr size_t kMaxKeyValueSize
= 4096;
23 static constexpr size_t kMaxSize
= 8192;
24 static constexpr char kKeyValueSeparator
= '=';
25 static constexpr char kMembersSeparator
= ',';
26 static constexpr char kMetadataSeparator
= ';';
28 Baggage() noexcept
: kv_properties_(new opentelemetry::common::KeyValueProperties()) {}
29 Baggage(size_t size
) noexcept
30 : kv_properties_(new opentelemetry::common::KeyValueProperties(size
)){};
33 Baggage(const T
&keys_and_values
) noexcept
34 : kv_properties_(new opentelemetry::common::KeyValueProperties(keys_and_values
))
37 static nostd::shared_ptr
<Baggage
> GetDefault()
39 static nostd::shared_ptr
<Baggage
> baggage
{new Baggage()};
43 /* Get value for key in the baggage
44 @returns true if key is found, false otherwise
46 bool GetValue(nostd::string_view key
, std::string
&value
) const noexcept
48 return kv_properties_
->GetValue(key
, value
);
51 /* Returns shared_ptr of new baggage object which contains new key-value pair. If key or value is
52 invalid, copy of current baggage is returned
54 nostd::shared_ptr
<Baggage
> Set(const nostd::string_view
&key
,
55 const nostd::string_view
&value
) noexcept
58 nostd::shared_ptr
<Baggage
> baggage(new Baggage(kv_properties_
->Size() + 1));
59 const bool valid_kv
= IsValidKey(key
) && IsValidValue(value
);
63 baggage
->kv_properties_
->AddEntry(key
, value
);
66 // add rest of the fields.
67 kv_properties_
->GetAllEntries(
68 [&baggage
, &key
, &valid_kv
](nostd::string_view e_key
, nostd::string_view e_value
) {
69 // if key or value was not valid, add all the entries. Add only remaining entries
71 if (!valid_kv
|| key
!= e_key
)
73 baggage
->kv_properties_
->AddEntry(e_key
, e_value
);
82 // @return all key-values entries by repeatedly invoking the function reference passed as argument
85 nostd::function_ref
<bool(nostd::string_view
, nostd::string_view
)> callback
) const noexcept
87 return kv_properties_
->GetAllEntries(callback
);
90 // delete key from the baggage if it exists. Returns shared_ptr of new baggage object.
91 // if key does not exist, copy of current baggage is returned.
92 // Validity of key is not checked as invalid keys should never be populated in baggage in the
94 nostd::shared_ptr
<Baggage
> Delete(nostd::string_view key
) noexcept
96 // keeping size of baggage same as key might not be found in it
97 nostd::shared_ptr
<Baggage
> baggage(new Baggage(kv_properties_
->Size()));
98 kv_properties_
->GetAllEntries(
99 [&baggage
, &key
](nostd::string_view e_key
, nostd::string_view e_value
) {
101 baggage
->kv_properties_
->AddEntry(e_key
, e_value
);
107 // Returns shared_ptr of baggage after extracting key-value pairs from header
108 static nostd::shared_ptr
<Baggage
> FromHeader(nostd::string_view header
) noexcept
110 if (header
.size() > kMaxSize
)
112 // header size exceeds maximum threshold, return empty baggage
116 common::KeyValueStringTokenizer
kv_str_tokenizer(header
);
117 size_t cnt
= kv_str_tokenizer
.NumTokens(); // upper bound on number of kv pairs
118 if (cnt
> kMaxKeyValuePairs
)
120 cnt
= kMaxKeyValuePairs
;
123 nostd::shared_ptr
<Baggage
> baggage(new Baggage(cnt
));
125 nostd::string_view key
, value
;
127 while (kv_str_tokenizer
.next(kv_valid
, key
, value
) && baggage
->kv_properties_
->Size() < cnt
)
129 if (!kv_valid
|| (key
.size() + value
.size() > kMaxKeyValueSize
))
131 // if kv pair is not valid, skip it
135 // NOTE : metadata is kept as part of value only as it does not have any semantic meaning.
136 // but, we need to extract it (else Decode on value will return error)
137 nostd::string_view metadata
;
138 auto metadata_separator
= value
.find(kMetadataSeparator
);
139 if (metadata_separator
!= std::string::npos
)
141 metadata
= value
.substr(metadata_separator
);
142 value
= value
.substr(0, metadata_separator
);
146 auto key_str
= UrlDecode(common::StringUtil::Trim(key
), err
);
147 auto value_str
= UrlDecode(common::StringUtil::Trim(value
), err
);
149 if (err
== false && IsValidKey(key_str
) && IsValidValue(value_str
))
151 if (!metadata
.empty())
153 value_str
.append(metadata
.data(), metadata
.size());
155 baggage
->kv_properties_
->AddEntry(key_str
, value_str
);
162 // Creates string from baggage object.
163 std::string
ToHeader() const noexcept
165 std::string header_s
;
167 kv_properties_
->GetAllEntries([&](nostd::string_view key
, nostd::string_view value
) {
170 header_s
.push_back(kMembersSeparator
);
176 header_s
.append(UrlEncode(key
));
177 header_s
.push_back(kKeyValueSeparator
);
179 // extracting metadata from value. We do not encode metadata
180 auto metadata_separator
= value
.find(kMetadataSeparator
);
181 if (metadata_separator
!= std::string::npos
)
183 header_s
.append(UrlEncode(value
.substr(0, metadata_separator
)));
184 auto metadata
= value
.substr(metadata_separator
);
185 header_s
.append(std::string(metadata
.data(), metadata
.size()));
189 header_s
.append(UrlEncode(value
));
197 static bool IsPrintableString(nostd::string_view str
)
199 for (const auto ch
: str
)
201 if (ch
< ' ' || ch
> '~')
210 static bool IsValidKey(nostd::string_view key
) { return key
.size() && IsPrintableString(key
); }
212 static bool IsValidValue(nostd::string_view value
) { return IsPrintableString(value
); }
214 // Uri encode key value pairs before injecting into header
215 // Implementation inspired from : https://golang.org/src/net/url/url.go?s=7851:7884#L264
216 static std::string
UrlEncode(nostd::string_view str
)
218 auto to_hex
= [](char c
) -> char {
219 static const char *hex
= "0123456789ABCDEF";
227 if (std::isalnum(c
) || c
== '-' || c
== '_' || c
== '.' || c
== '~')
238 ret
.push_back(to_hex(c
>> 4));
239 ret
.push_back(to_hex(c
& 15));
246 // Uri decode key value pairs after extracting from header
247 static std::string
UrlDecode(nostd::string_view str
, bool &err
)
249 auto IsHex
= [](char c
) {
250 return std::isdigit(c
) || (c
>= 'A' && c
<= 'F') || (c
>= 'a' && c
<= 'f');
253 auto from_hex
= [](char c
) -> char {
254 // c - '0' produces integer type which could trigger error/warning when casting to char,
255 // but the cast is safe here.
256 return static_cast<char>(std::isdigit(c
) ? c
- '0' : std::toupper(c
) - 'A' + 10);
261 for (size_t i
= 0; i
< str
.size(); i
++)
265 if (i
+ 2 >= str
.size() || !IsHex(str
[i
+ 1]) || !IsHex(str
[i
+ 2]))
270 ret
.push_back(from_hex(str
[i
+ 1]) << 4 | from_hex(str
[i
+ 2]));
273 else if (str
[i
] == '+')
277 else if (std::isalnum(str
[i
]) || str
[i
] == '-' || str
[i
] == '_' || str
[i
] == '.' ||
280 ret
.push_back(str
[i
]);
293 // Store entries in a C-style array to avoid using std::array or std::vector.
294 nostd::unique_ptr
<opentelemetry::common::KeyValueProperties
> kv_properties_
;
297 } // namespace baggage
299 OPENTELEMETRY_END_NAMESPACE