]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/opentelemetry-cpp/api/include/opentelemetry/baggage/baggage.h
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / jaegertracing / opentelemetry-cpp / api / include / opentelemetry / baggage / baggage.h
1 // Copyright The OpenTelemetry Authors
2 // SPDX-License-Identifier: Apache-2.0
3
4 #pragma once
5
6 #include <cctype>
7
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"
12
13 OPENTELEMETRY_BEGIN_NAMESPACE
14
15 namespace baggage
16 {
17
18 class Baggage
19 {
20 public:
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 = ';';
27
28 Baggage() noexcept : kv_properties_(new opentelemetry::common::KeyValueProperties()) {}
29 Baggage(size_t size) noexcept
30 : kv_properties_(new opentelemetry::common::KeyValueProperties(size)){};
31
32 template <class T>
33 Baggage(const T &keys_and_values) noexcept
34 : kv_properties_(new opentelemetry::common::KeyValueProperties(keys_and_values))
35 {}
36
37 static nostd::shared_ptr<Baggage> GetDefault()
38 {
39 static nostd::shared_ptr<Baggage> baggage{new Baggage()};
40 return baggage;
41 }
42
43 /* Get value for key in the baggage
44 @returns true if key is found, false otherwise
45 */
46 bool GetValue(nostd::string_view key, std::string &value) const noexcept
47 {
48 return kv_properties_->GetValue(key, value);
49 }
50
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
53 */
54 nostd::shared_ptr<Baggage> Set(const nostd::string_view &key,
55 const nostd::string_view &value) noexcept
56 {
57
58 nostd::shared_ptr<Baggage> baggage(new Baggage(kv_properties_->Size() + 1));
59 const bool valid_kv = IsValidKey(key) && IsValidValue(value);
60
61 if (valid_kv)
62 {
63 baggage->kv_properties_->AddEntry(key, value);
64 }
65
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
70 // otherwise.
71 if (!valid_kv || key != e_key)
72 {
73 baggage->kv_properties_->AddEntry(e_key, e_value);
74 }
75
76 return true;
77 });
78
79 return baggage;
80 }
81
82 // @return all key-values entries by repeatedly invoking the function reference passed as argument
83 // for each entry
84 bool GetAllEntries(
85 nostd::function_ref<bool(nostd::string_view, nostd::string_view)> callback) const noexcept
86 {
87 return kv_properties_->GetAllEntries(callback);
88 }
89
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
93 // first place.
94 nostd::shared_ptr<Baggage> Delete(nostd::string_view key) noexcept
95 {
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) {
100 if (key != e_key)
101 baggage->kv_properties_->AddEntry(e_key, e_value);
102 return true;
103 });
104 return baggage;
105 }
106
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
109 {
110 if (header.size() > kMaxSize)
111 {
112 // header size exceeds maximum threshold, return empty baggage
113 return GetDefault();
114 }
115
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)
119 {
120 cnt = kMaxKeyValuePairs;
121 }
122
123 nostd::shared_ptr<Baggage> baggage(new Baggage(cnt));
124 bool kv_valid;
125 nostd::string_view key, value;
126
127 while (kv_str_tokenizer.next(kv_valid, key, value) && baggage->kv_properties_->Size() < cnt)
128 {
129 if (!kv_valid || (key.size() + value.size() > kMaxKeyValueSize))
130 {
131 // if kv pair is not valid, skip it
132 continue;
133 }
134
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)
140 {
141 metadata = value.substr(metadata_separator);
142 value = value.substr(0, metadata_separator);
143 }
144
145 bool err = 0;
146 auto key_str = UrlDecode(common::StringUtil::Trim(key), err);
147 auto value_str = UrlDecode(common::StringUtil::Trim(value), err);
148
149 if (err == false && IsValidKey(key_str) && IsValidValue(value_str))
150 {
151 if (!metadata.empty())
152 {
153 value_str.append(metadata.data(), metadata.size());
154 }
155 baggage->kv_properties_->AddEntry(key_str, value_str);
156 }
157 }
158
159 return baggage;
160 }
161
162 // Creates string from baggage object.
163 std::string ToHeader() const noexcept
164 {
165 std::string header_s;
166 bool first = true;
167 kv_properties_->GetAllEntries([&](nostd::string_view key, nostd::string_view value) {
168 if (!first)
169 {
170 header_s.push_back(kMembersSeparator);
171 }
172 else
173 {
174 first = false;
175 }
176 header_s.append(UrlEncode(key));
177 header_s.push_back(kKeyValueSeparator);
178
179 // extracting metadata from value. We do not encode metadata
180 auto metadata_separator = value.find(kMetadataSeparator);
181 if (metadata_separator != std::string::npos)
182 {
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()));
186 }
187 else
188 {
189 header_s.append(UrlEncode(value));
190 }
191 return true;
192 });
193 return header_s;
194 }
195
196 private:
197 static bool IsPrintableString(nostd::string_view str)
198 {
199 for (const auto ch : str)
200 {
201 if (ch < ' ' || ch > '~')
202 {
203 return false;
204 }
205 }
206
207 return true;
208 }
209
210 static bool IsValidKey(nostd::string_view key) { return key.size() && IsPrintableString(key); }
211
212 static bool IsValidValue(nostd::string_view value) { return IsPrintableString(value); }
213
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)
217 {
218 auto to_hex = [](char c) -> char {
219 static const char *hex = "0123456789ABCDEF";
220 return hex[c & 15];
221 };
222
223 std::string ret;
224
225 for (auto c : str)
226 {
227 if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
228 {
229 ret.push_back(c);
230 }
231 else if (c == ' ')
232 {
233 ret.push_back('+');
234 }
235 else
236 {
237 ret.push_back('%');
238 ret.push_back(to_hex(c >> 4));
239 ret.push_back(to_hex(c & 15));
240 }
241 }
242
243 return ret;
244 }
245
246 // Uri decode key value pairs after extracting from header
247 static std::string UrlDecode(nostd::string_view str, bool &err)
248 {
249 auto IsHex = [](char c) {
250 return std::isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
251 };
252
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);
257 };
258
259 std::string ret;
260
261 for (size_t i = 0; i < str.size(); i++)
262 {
263 if (str[i] == '%')
264 {
265 if (i + 2 >= str.size() || !IsHex(str[i + 1]) || !IsHex(str[i + 2]))
266 {
267 err = 1;
268 return "";
269 }
270 ret.push_back(from_hex(str[i + 1]) << 4 | from_hex(str[i + 2]));
271 i += 2;
272 }
273 else if (str[i] == '+')
274 {
275 ret.push_back(' ');
276 }
277 else if (std::isalnum(str[i]) || str[i] == '-' || str[i] == '_' || str[i] == '.' ||
278 str[i] == '~')
279 {
280 ret.push_back(str[i]);
281 }
282 else
283 {
284 err = 1;
285 return "";
286 }
287 }
288
289 return ret;
290 }
291
292 private:
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_;
295 };
296
297 } // namespace baggage
298
299 OPENTELEMETRY_END_NAMESPACE