| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- // Copyright The OpenTelemetry Authors
- // SPDX-License-Identifier: Apache-2.0
- #pragma once
- #include <cctype>
- #include "opentelemetry/common/kv_properties.h"
- #include "opentelemetry/common/macros.h"
- #include "opentelemetry/nostd/shared_ptr.h"
- #include "opentelemetry/nostd/string_view.h"
- #include "opentelemetry/version.h"
- OPENTELEMETRY_BEGIN_NAMESPACE
- namespace baggage
- {
- class OPENTELEMETRY_EXPORT Baggage
- {
- public:
- static constexpr size_t kMaxKeyValuePairs = 180;
- static constexpr size_t kMaxKeyValueSize = 4096;
- static constexpr size_t kMaxSize = 8192;
- static constexpr char kKeyValueSeparator = '=';
- static constexpr char kMembersSeparator = ',';
- static constexpr char kMetadataSeparator = ';';
- Baggage() noexcept : kv_properties_(new common::KeyValueProperties()) {}
- Baggage(size_t size) noexcept : kv_properties_(new common::KeyValueProperties(size)) {}
- template <class T>
- Baggage(const T &keys_and_values) noexcept
- : kv_properties_(new common::KeyValueProperties(keys_and_values))
- {}
- OPENTELEMETRY_API_SINGLETON static nostd::shared_ptr<Baggage> GetDefault()
- {
- static nostd::shared_ptr<Baggage> baggage{new Baggage()};
- return baggage;
- }
- /* Get value for key in the baggage
- @returns true if key is found, false otherwise
- */
- bool GetValue(nostd::string_view key, std::string &value) const noexcept
- {
- return kv_properties_->GetValue(key, value);
- }
- /* Returns shared_ptr of new baggage object which contains new key-value pair. If key or value is
- invalid, copy of current baggage is returned
- */
- nostd::shared_ptr<Baggage> Set(const nostd::string_view &key,
- const nostd::string_view &value) noexcept
- {
- nostd::shared_ptr<Baggage> baggage(new Baggage(kv_properties_->Size() + 1));
- const bool valid_kv = IsValidKey(key) && IsValidValue(value);
- if (valid_kv)
- {
- baggage->kv_properties_->AddEntry(key, value);
- }
- // add rest of the fields.
- kv_properties_->GetAllEntries(
- [&baggage, &key, &valid_kv](nostd::string_view e_key, nostd::string_view e_value) {
- // if key or value was not valid, add all the entries. Add only remaining entries
- // otherwise.
- if (!valid_kv || key != e_key)
- {
- baggage->kv_properties_->AddEntry(e_key, e_value);
- }
- return true;
- });
- return baggage;
- }
- // @return all key-values entries by repeatedly invoking the function reference passed as argument
- // for each entry
- bool GetAllEntries(
- nostd::function_ref<bool(nostd::string_view, nostd::string_view)> callback) const noexcept
- {
- return kv_properties_->GetAllEntries(callback);
- }
- // delete key from the baggage if it exists. Returns shared_ptr of new baggage object.
- // if key does not exist, copy of current baggage is returned.
- // Validity of key is not checked as invalid keys should never be populated in baggage in the
- // first place.
- nostd::shared_ptr<Baggage> Delete(nostd::string_view key) noexcept
- {
- // keeping size of baggage same as key might not be found in it
- nostd::shared_ptr<Baggage> baggage(new Baggage(kv_properties_->Size()));
- kv_properties_->GetAllEntries(
- [&baggage, &key](nostd::string_view e_key, nostd::string_view e_value) {
- if (key != e_key)
- baggage->kv_properties_->AddEntry(e_key, e_value);
- return true;
- });
- return baggage;
- }
- // Returns shared_ptr of baggage after extracting key-value pairs from header
- static nostd::shared_ptr<Baggage> FromHeader(nostd::string_view header) noexcept
- {
- if (header.size() > kMaxSize)
- {
- // header size exceeds maximum threshold, return empty baggage
- return GetDefault();
- }
- common::KeyValueStringTokenizer kv_str_tokenizer(header);
- size_t cnt = kv_str_tokenizer.NumTokens(); // upper bound on number of kv pairs
- if (cnt > kMaxKeyValuePairs)
- {
- cnt = kMaxKeyValuePairs;
- }
- nostd::shared_ptr<Baggage> baggage(new Baggage(cnt));
- bool kv_valid;
- nostd::string_view key, value;
- while (kv_str_tokenizer.next(kv_valid, key, value) && baggage->kv_properties_->Size() < cnt)
- {
- if (!kv_valid || (key.size() + value.size() > kMaxKeyValueSize))
- {
- // if kv pair is not valid, skip it
- continue;
- }
- // NOTE : metadata is kept as part of value only as it does not have any semantic meaning.
- // but, we need to extract it (else Decode on value will return error)
- nostd::string_view metadata;
- auto metadata_separator = value.find(kMetadataSeparator);
- if (metadata_separator != std::string::npos)
- {
- metadata = value.substr(metadata_separator);
- value = value.substr(0, metadata_separator);
- }
- bool err = 0;
- auto key_str = UrlDecode(common::StringUtil::Trim(key), err);
- auto value_str = UrlDecode(common::StringUtil::Trim(value), err);
- if (err == false && IsValidKey(key_str) && IsValidValue(value_str))
- {
- if (!metadata.empty())
- {
- value_str.append(metadata.data(), metadata.size());
- }
- baggage->kv_properties_->AddEntry(key_str, value_str);
- }
- }
- return baggage;
- }
- // Creates string from baggage object.
- std::string ToHeader() const noexcept
- {
- std::string header_s;
- bool first = true;
- kv_properties_->GetAllEntries([&](nostd::string_view key, nostd::string_view value) {
- if (!first)
- {
- header_s.push_back(kMembersSeparator);
- }
- else
- {
- first = false;
- }
- header_s.append(UrlEncode(key));
- header_s.push_back(kKeyValueSeparator);
- // extracting metadata from value. We do not encode metadata
- auto metadata_separator = value.find(kMetadataSeparator);
- if (metadata_separator != std::string::npos)
- {
- header_s.append(UrlEncode(value.substr(0, metadata_separator)));
- auto metadata = value.substr(metadata_separator);
- header_s.append(std::string(metadata.data(), metadata.size()));
- }
- else
- {
- header_s.append(UrlEncode(value));
- }
- return true;
- });
- return header_s;
- }
- private:
- static bool IsPrintableString(nostd::string_view str)
- {
- for (const auto ch : str)
- {
- if (ch < ' ' || ch > '~')
- {
- return false;
- }
- }
- return true;
- }
- static bool IsValidKey(nostd::string_view key) { return key.size() && IsPrintableString(key); }
- static bool IsValidValue(nostd::string_view value) { return IsPrintableString(value); }
- // Uri encode key value pairs before injecting into header
- // Implementation inspired from : https://golang.org/src/net/url/url.go?s=7851:7884#L264
- static std::string UrlEncode(nostd::string_view str)
- {
- auto to_hex = [](char c) -> char {
- static const char *hex = "0123456789ABCDEF";
- return hex[c & 15];
- };
- std::string ret;
- for (auto c : str)
- {
- if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
- {
- ret.push_back(c);
- }
- else if (c == ' ')
- {
- ret.push_back('+');
- }
- else
- {
- ret.push_back('%');
- ret.push_back(to_hex(c >> 4));
- ret.push_back(to_hex(c & 15));
- }
- }
- return ret;
- }
- // Uri decode key value pairs after extracting from header
- static std::string UrlDecode(nostd::string_view str, bool &err)
- {
- auto IsHex = [](char c) {
- return std::isdigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
- };
- auto from_hex = [](char c) -> char {
- // c - '0' produces integer type which could trigger error/warning when casting to char,
- // but the cast is safe here.
- return static_cast<char>(std::isdigit(c) ? c - '0' : std::toupper(c) - 'A' + 10);
- };
- std::string ret;
- for (size_t i = 0; i < str.size(); i++)
- {
- if (str[i] == '%')
- {
- if (i + 2 >= str.size() || !IsHex(str[i + 1]) || !IsHex(str[i + 2]))
- {
- err = 1;
- return "";
- }
- ret.push_back(from_hex(str[i + 1]) << 4 | from_hex(str[i + 2]));
- i += 2;
- }
- else if (str[i] == '+')
- {
- ret.push_back(' ');
- }
- else if (std::isalnum(str[i]) || str[i] == '-' || str[i] == '_' || str[i] == '.' ||
- str[i] == '~')
- {
- ret.push_back(str[i]);
- }
- else
- {
- err = 1;
- return "";
- }
- }
- return ret;
- }
- private:
- // Store entries in a C-style array to avoid using std::array or std::vector.
- nostd::unique_ptr<common::KeyValueProperties> kv_properties_;
- };
- } // namespace baggage
- OPENTELEMETRY_END_NAMESPACE
|