LCOV - code coverage report
Current view: top level - source/common/protobuf - utility.h (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 75 143 52.4 %
Date: 2024-01-05 06:35:25 Functions: 175 575 30.4 %

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <numeric>
       4             : 
       5             : #include "envoy/api/api.h"
       6             : #include "envoy/common/exception.h"
       7             : #include "envoy/protobuf/message_validator.h"
       8             : #include "envoy/runtime/runtime.h"
       9             : #include "envoy/type/v3/percent.pb.h"
      10             : 
      11             : #include "source/common/common/hash.h"
      12             : #include "source/common/common/stl_helpers.h"
      13             : #include "source/common/common/utility.h"
      14             : #include "source/common/protobuf/protobuf.h"
      15             : #include "source/common/singleton/const_singleton.h"
      16             : 
      17             : #include "absl/status/status.h"
      18             : #include "absl/status/statusor.h"
      19             : #include "absl/strings/str_join.h"
      20             : 
      21             : // Obtain the value of a wrapped field (e.g. google.protobuf.UInt32Value) if set. Otherwise, return
      22             : // the default value.
      23             : #define PROTOBUF_GET_WRAPPED_OR_DEFAULT(message, field_name, default_value)                        \
      24        5293 :   ((message).has_##field_name() ? (message).field_name().value() : (default_value))
      25             : 
      26             : // Obtain the value of a wrapped field (e.g. google.protobuf.UInt32Value) if set. Otherwise, throw
      27             : // a EnvoyException.
      28             : 
      29             : #define PROTOBUF_GET_WRAPPED_REQUIRED(message, field_name)                                         \
      30         304 :   ([](const auto& msg) {                                                                           \
      31         304 :     if (!msg.has_##field_name()) {                                                                 \
      32           0 :       ::Envoy::ProtoExceptionUtil::throwMissingFieldException(#field_name, msg);                   \
      33           0 :     }                                                                                              \
      34         304 :     return msg.field_name().value();                                                               \
      35         304 :   }((message)))
      36             : // Obtain the milliseconds value of a google.protobuf.Duration field if set. Otherwise, return the
      37             : // default value.
      38             : #define PROTOBUF_GET_MS_OR_DEFAULT(message, field_name, default_value)                             \
      39        2347 :   ((message).has_##field_name() ? DurationUtil::durationToMilliseconds((message).field_name())     \
      40        2347 :                                 : (default_value))
      41             : 
      42             : // Obtain the string value if the field is set. Otherwise, return the default value.
      43             : #define PROTOBUF_GET_STRING_OR_DEFAULT(message, field_name, default_value)                         \
      44             :   (!(message).field_name().empty() ? (message).field_name() : (default_value))
      45             : 
      46             : // Obtain the milliseconds value of a google.protobuf.Duration field if set. Otherwise, return
      47             : // absl::nullopt.
      48             : #define PROTOBUF_GET_OPTIONAL_MS(message, field_name)                                              \
      49          16 :   ((message).has_##field_name()                                                                    \
      50          16 :        ? absl::optional<std::chrono::milliseconds>(                                                \
      51           0 :              DurationUtil::durationToMilliseconds((message).field_name()))                         \
      52          16 :        : absl::nullopt)
      53             : 
      54             : // Obtain the milliseconds value of a google.protobuf.Duration field if set. Otherwise, throw an
      55             : // EnvoyException.
      56             : #define PROTOBUF_GET_MS_REQUIRED(message, field_name)                                              \
      57         355 :   ([](const auto& msg) {                                                                           \
      58         355 :     if (!msg.has_##field_name()) {                                                                 \
      59           3 :       ::Envoy::ProtoExceptionUtil::throwMissingFieldException(#field_name, msg);                   \
      60           3 :     }                                                                                              \
      61         355 :     return DurationUtil::durationToMilliseconds(msg.field_name());                                 \
      62         355 :   }((message)))
      63             : 
      64             : // Obtain the milliseconds value of a google.protobuf.Duration field if set. Otherwise, return the
      65             : // default value.
      66             : #define PROTOBUF_GET_SECONDS_OR_DEFAULT(message, field_name, default_value)                        \
      67             :   ((message).has_##field_name() ? DurationUtil::durationToSeconds((message).field_name())          \
      68             :                                 : (default_value))
      69             : 
      70             : // Obtain the seconds value of a google.protobuf.Duration field if set. Otherwise, throw an
      71             : // EnvoyException.
      72             : #define PROTOBUF_GET_SECONDS_REQUIRED(message, field_name)                                         \
      73             :   ([](const auto& msg) {                                                                           \
      74             :     if (!msg.has_##field_name()) {                                                                 \
      75             :       ::Envoy::ProtoExceptionUtil::throwMissingFieldException(#field_name, msg);                   \
      76             :     }                                                                                              \
      77             :     return DurationUtil::durationToSeconds(msg.field_name());                                      \
      78             :   }((message)))
      79             : 
      80             : namespace Envoy {
      81             : namespace ProtobufPercentHelper {
      82             : 
      83             : // The following are helpers used in the PROTOBUF_PERCENT_TO_ROUNDED_INTEGER_OR_DEFAULT macro.
      84             : // This avoids a giant macro mess when trying to do asserts, casts, etc.
      85             : uint64_t checkAndReturnDefault(uint64_t default_value, uint64_t max_value);
      86             : uint64_t convertPercent(double percent, uint64_t max_value);
      87             : 
      88             : /**
      89             :  * Given a fractional percent chance of a given event occurring, evaluate to a yes/no decision
      90             :  * based on a provided random value.
      91             :  * @param percent the chance of a given event happening.
      92             :  * @param random_value supplies a numerical value to use to evaluate the event.
      93             :  * @return bool decision about whether the event should occur.
      94             :  */
      95             : bool evaluateFractionalPercent(envoy::type::v3::FractionalPercent percent, uint64_t random_value);
      96             : 
      97             : /**
      98             :  * Convert a fractional percent denominator enum into an integer.
      99             :  * @param denominator supplies denominator to convert.
     100             :  * @return the converted denominator.
     101             :  */
     102             : uint64_t fractionalPercentDenominatorToInt(
     103             :     const envoy::type::v3::FractionalPercent::DenominatorType& denominator);
     104             : 
     105             : } // namespace ProtobufPercentHelper
     106             : } // namespace Envoy
     107             : 
     108             : // Convert an envoy::type::v3::Percent to a double or a default.
     109             : // @param message supplies the proto message containing the field.
     110             : // @param field_name supplies the field name in the message.
     111             : // @param default_value supplies the default if the field is not present.
     112             : #define PROTOBUF_PERCENT_TO_DOUBLE_OR_DEFAULT(message, field_name, default_value)                  \
     113         278 :   ([](const auto& msg) -> double {                                                                 \
     114         276 :     if (std::isnan(msg.field_name().value())) {                                                    \
     115           0 :       ::Envoy::ExceptionUtil::throwEnvoyException(                                                 \
     116           0 :           fmt::format("Value not in the range of 0..100 range."));                                 \
     117           0 :     }                                                                                              \
     118         276 :     return (msg).has_##field_name() ? (msg).field_name().value() : default_value;                  \
     119         276 :   }((message)))
     120             : // Convert an envoy::type::v3::Percent to a rounded integer or a default.
     121             : // @param message supplies the proto message containing the field.
     122             : // @param field_name supplies the field name in the message.
     123             : // @param max_value supplies the maximum allowed integral value (e.g., 100, 10000, etc.).
     124             : // @param default_value supplies the default if the field is not present.
     125             : //
     126             : // TODO(anirudhmurali): Recommended to capture and validate NaN values in PGV
     127             : // Issue: https://github.com/bufbuild/protoc-gen-validate/issues/85
     128             : #define PROTOBUF_PERCENT_TO_ROUNDED_INTEGER_OR_DEFAULT(message, field_name, max_value,             \
     129             :                                                        default_value)                              \
     130         690 :   ([](const auto& msg) {                                                                           \
     131         690 :     if (std::isnan(msg.field_name().value())) {                                                    \
     132          34 :       ::Envoy::ExceptionUtil::throwEnvoyException(                                                 \
     133          34 :           fmt::format("Value not in the range of 0..100 range."));                                 \
     134          34 :     }                                                                                              \
     135         690 :     return (msg).has_##field_name()                                                                \
     136         690 :                ? ProtobufPercentHelper::convertPercent((msg).field_name().value(), max_value)      \
     137         690 :                : ProtobufPercentHelper::checkAndReturnDefault(default_value, max_value);           \
     138         690 :   }((message)))
     139             : 
     140             : namespace Envoy {
     141             : 
     142             : class TypeUtil {
     143             : public:
     144             :   static absl::string_view typeUrlToDescriptorFullName(absl::string_view type_url);
     145             : 
     146             :   static std::string descriptorFullNameToTypeUrl(absl::string_view type);
     147             : };
     148             : 
     149             : class RepeatedPtrUtil {
     150             : public:
     151             :   static std::string join(const Protobuf::RepeatedPtrField<std::string>& source,
     152           0 :                           const std::string& delimiter) {
     153           0 :     return absl::StrJoin(std::vector<std::string>(source.begin(), source.end()), delimiter);
     154           0 :   }
     155             : 
     156             :   template <class ProtoType>
     157             :   static std::string debugString(const Protobuf::RepeatedPtrField<ProtoType>& source) {
     158             :     return accumulateToString<ProtoType>(
     159             :         source, [](const Protobuf::Message& message) { return message.DebugString(); });
     160             :   }
     161             : 
     162             :   // Based on MessageUtil::hash() defined below.
     163             :   template <class ProtoType>
     164           0 :   static std::size_t hash(const Protobuf::RepeatedPtrField<ProtoType>& source) {
     165           0 :     std::string text;
     166           0 : #if defined(ENVOY_ENABLE_FULL_PROTOS)
     167           0 :     {
     168           0 :       Protobuf::TextFormat::Printer printer;
     169           0 :       printer.SetExpandAny(true);
     170           0 :       printer.SetUseFieldNumber(true);
     171           0 :       printer.SetSingleLineMode(true);
     172           0 :       printer.SetHideUnknownFields(true);
     173           0 :       for (const auto& message : source) {
     174           0 :         std::string text_message;
     175           0 :         printer.PrintToString(message, &text_message);
     176           0 :         absl::StrAppend(&text, text_message);
     177           0 :       }
     178           0 :     }
     179             : #else
     180             :     for (const auto& message : source) {
     181             :       absl::StrAppend(&text, message.SerializeAsString());
     182             :     }
     183             : #endif
     184           0 :     return HashUtil::xxHash64(text);
     185           0 :   }
     186             : 
     187             :   /**
     188             :    * Converts a proto repeated field into a container of const Protobuf::Message unique_ptr's.
     189             :    *
     190             :    * @param repeated_field the proto repeated field to convert.
     191             :    * @return ReturnType the container of const Message pointers.
     192             :    */
     193             :   template <typename ProtoType, typename ReturnType>
     194             :   static ReturnType
     195           0 :   convertToConstMessagePtrContainer(const Protobuf::RepeatedPtrField<ProtoType>& repeated_field) {
     196           0 :     ReturnType ret_container;
     197           0 :     std::transform(repeated_field.begin(), repeated_field.end(), std::back_inserter(ret_container),
     198           0 :                    [](const ProtoType& proto_message) -> std::unique_ptr<const Protobuf::Message> {
     199           0 :                      Protobuf::Message* clone = proto_message.New();
     200           0 :                      clone->CheckTypeAndMergeFrom(proto_message);
     201           0 :                      return std::unique_ptr<const Protobuf::Message>(clone);
     202           0 :                    });
     203           0 :     return ret_container;
     204           0 :   }
     205             : };
     206             : 
     207             : using ProtoValidationException = EnvoyException;
     208             : 
     209             : /**
     210             :  * utility functions to call when throwing exceptions in header files
     211             :  */
     212             : class ProtoExceptionUtil {
     213             : public:
     214             :   static void throwMissingFieldException(const std::string& field_name,
     215             :                                          const Protobuf::Message& message);
     216             :   static void throwProtoValidationException(const std::string& validation_error,
     217             :                                             const Protobuf::Message& message);
     218             : };
     219             : 
     220             : class MessageUtil {
     221             : public:
     222             :   // std::hash
     223         818 :   std::size_t operator()(const Protobuf::Message& message) const { return hash(message); }
     224             : 
     225             :   // std::equals_to
     226          63 :   bool operator()(const Protobuf::Message& lhs, const Protobuf::Message& rhs) const {
     227          63 :     return Protobuf::util::MessageDifferencer::Equivalent(lhs, rhs);
     228          63 :   }
     229             : 
     230             :   class FileExtensionValues {
     231             :   public:
     232             :     const std::string ProtoBinary = ".pb";
     233             :     const std::string ProtoBinaryLengthDelimited = ".pb_length_delimited";
     234             :     const std::string ProtoText = ".pb_text";
     235             :     const std::string Json = ".json";
     236             :     const std::string Yaml = ".yaml";
     237             :     const std::string Yml = ".yml";
     238             :   };
     239             : 
     240             :   using FileExtensions = ConstSingleton<FileExtensionValues>;
     241             : 
     242             :   /**
     243             :    * A hash function uses Protobuf::TextFormat to force deterministic serialization recursively
     244             :    * including known types in google.protobuf.Any. See
     245             :    * https://github.com/protocolbuffers/protobuf/issues/5731 for the context.
     246             :    * Using this function is discouraged, see discussion in
     247             :    * https://github.com/envoyproxy/envoy/issues/8301.
     248             :    */
     249             :   static std::size_t hash(const Protobuf::Message& message);
     250             : 
     251             : #ifdef ENVOY_ENABLE_YAML
     252             :   static void loadFromJson(const std::string& json, Protobuf::Message& message,
     253             :                            ProtobufMessage::ValidationVisitor& validation_visitor);
     254             :   /**
     255             :    * Return ok only when strict conversion(don't ignore unknown field) succeeds.
     256             :    * Return error status for strict conversion and set has_unknown_field to true if relaxed
     257             :    * conversion(ignore unknown field) succeeds.
     258             :    * Return error status for relaxed conversion and set has_unknown_field to false if relaxed
     259             :    * conversion(ignore unknown field) fails.
     260             :    */
     261             :   static absl::Status loadFromJsonNoThrow(const std::string& json, Protobuf::Message& message,
     262             :                                           bool& has_unknown_fileld);
     263             :   static void loadFromJson(const std::string& json, ProtobufWkt::Struct& message);
     264             :   static void loadFromYaml(const std::string& yaml, Protobuf::Message& message,
     265             :                            ProtobufMessage::ValidationVisitor& validation_visitor);
     266             :   static void loadFromFile(const std::string& path, Protobuf::Message& message,
     267             :                            ProtobufMessage::ValidationVisitor& validation_visitor, Api::Api& api);
     268             : #endif
     269             : 
     270             :   /**
     271             :    * Checks for use of deprecated fields in message and all sub-messages.
     272             :    * @param message message to validate.
     273             :    * @param validation_visitor the validation visitor to use.
     274             :    * @param recurse_into_any whether to recurse into Any messages during unexpected checking.
     275             :    * @throw EnvoyException if deprecated fields are used and listed
     276             :    *    in disallowed_features in runtime_features.h
     277             :    */
     278             :   static void checkForUnexpectedFields(const Protobuf::Message& message,
     279             :                                        ProtobufMessage::ValidationVisitor& validation_visitor,
     280             :                                        bool recurse_into_any = false);
     281             : 
     282             :   /**
     283             :    * Perform a PGV check on the entire message tree, recursing into Any messages as needed.
     284             :    */
     285             :   static void recursivePgvCheck(const Protobuf::Message& message);
     286             : 
     287             :   /**
     288             :    * Validate protoc-gen-validate constraints on a given protobuf as well as performing
     289             :    * unexpected field validation.
     290             :    * Note the corresponding `.pb.validate.h` for the message has to be included in the source file
     291             :    * of caller.
     292             :    * @param message message to validate.
     293             :    * @param validation_visitor the validation visitor to use.
     294             :    * @param recurse_into_any whether to recurse into Any messages during unexpected checking.
     295             :    * @throw EnvoyException if the message does not satisfy its type constraints.
     296             :    */
     297             :   template <class MessageType>
     298             :   static void validate(const MessageType& message,
     299             :                        ProtobufMessage::ValidationVisitor& validation_visitor,
     300       10725 :                        bool recurse_into_any = false) {
     301             :     // Log warnings or throw errors if deprecated fields or unknown fields are in use.
     302       10725 :     if (!validation_visitor.skipValidation()) {
     303       10725 :       checkForUnexpectedFields(message, validation_visitor, recurse_into_any);
     304       10725 :     }
     305             : 
     306             :     // TODO(mattklein123): This will recurse the message twice, once above and once for PGV. When
     307             :     // we move to always recursing, satisfying the TODO below, we should merge into a single
     308             :     // recursion for performance reasons.
     309       10725 :     if (recurse_into_any) {
     310           0 :       return recursivePgvCheck(message);
     311           0 :     }
     312             : 
     313             :     // TODO(mattklein123): Now that PGV is capable of doing recursive message checks on abstract
     314             :     // types, we can remove bottom up validation from the entire codebase and only validate
     315             :     // at top level ingestion (bootstrap, discovery response). This is a large change and will be
     316             :     // done as a separate PR. This change will also allow removing templating from most/all of
     317             :     // related functions.
     318       10725 :     std::string err;
     319       10725 :     if (!Validate(message, &err)) {
     320        3925 :       ProtoExceptionUtil::throwProtoValidationException(err, message);
     321        3925 :     }
     322       10725 :   }
     323             : 
     324             : #ifdef ENVOY_ENABLE_YAML
     325             :   template <class MessageType>
     326             :   static void loadFromYamlAndValidate(const std::string& yaml, MessageType& message,
     327           0 :                                       ProtobufMessage::ValidationVisitor& validation_visitor) {
     328           0 :     loadFromYaml(yaml, message, validation_visitor);
     329           0 :     validate(message, validation_visitor);
     330           0 :   }
     331             : #endif
     332             : 
     333             :   /**
     334             :    * Downcast and validate protoc-gen-validate constraints on a given protobuf.
     335             :    * Note the corresponding `.pb.validate.h` for the message has to be included in the source file
     336             :    * of caller.
     337             :    * @param message const Protobuf::Message& to downcast and validate.
     338             :    * @return const MessageType& the concrete message type downcasted to on success.
     339             :    * @throw EnvoyException if the message does not satisfy its type constraints.
     340             :    */
     341             :   template <class MessageType>
     342             :   static const MessageType&
     343             :   downcastAndValidate(const Protobuf::Message& config,
     344        1871 :                       ProtobufMessage::ValidationVisitor& validation_visitor) {
     345        1871 :     const auto& typed_config = dynamic_cast<MessageType>(config);
     346        1871 :     validate(typed_config, validation_visitor);
     347        1871 :     return typed_config;
     348        1871 :   }
     349             : 
     350             :   /**
     351             :    * Convert from a typed message into a google.protobuf.Any. This should be used
     352             :    * instead of the inbuilt PackTo, as PackTo is not available with lite protos.
     353             :    *
     354             :    * @param any_message destination google.protobuf.Any.
     355             :    * @param message source to pack from.
     356             :    *
     357             :    * @throw EnvoyException if the message does not unpack.
     358             :    */
     359             :   static void packFrom(ProtobufWkt::Any& any_message, const Protobuf::Message& message);
     360             : 
     361             :   /**
     362             :    * Convert from google.protobuf.Any to a typed message. This should be used
     363             :    * instead of the inbuilt UnpackTo as it performs validation of results.
     364             :    *
     365             :    * @param any_message source google.protobuf.Any message.
     366             :    * @param message destination to unpack to.
     367             :    *
     368             :    * @throw EnvoyException if the message does not unpack.
     369             :    */
     370             :   static void unpackTo(const ProtobufWkt::Any& any_message, Protobuf::Message& message);
     371             : 
     372             :   /**
     373             :    * Convert from google.protobuf.Any to a typed message. This should be used
     374             :    * instead of the inbuilt UnpackTo as it performs validation of results.
     375             :    *
     376             :    * @param any_message source google.protobuf.Any message.
     377             :    * @param message destination to unpack to.
     378             :    *
     379             :    * @return absl::Status
     380             :    */
     381             :   static absl::Status unpackToNoThrow(const ProtobufWkt::Any& any_message,
     382             :                                       Protobuf::Message& message);
     383             : 
     384             :   /**
     385             :    * Convert from google.protobuf.Any to bytes as std::string
     386             :    * @param any source google.protobuf.Any message.
     387             :    *
     388             :    * @return std::string consists of bytes in the input message.
     389             :    */
     390           8 :   static std::string anyToBytes(const ProtobufWkt::Any& any) {
     391           8 :     if (any.Is<ProtobufWkt::StringValue>()) {
     392           0 :       ProtobufWkt::StringValue s;
     393           0 :       MessageUtil::unpackTo(any, s);
     394           0 :       return s.value();
     395           0 :     }
     396           8 :     if (any.Is<ProtobufWkt::BytesValue>()) {
     397           0 :       Protobuf::BytesValue b;
     398           0 :       MessageUtil::unpackTo(any, b);
     399           0 :       return b.value();
     400           0 :     }
     401           8 :     return any.value();
     402           8 :   };
     403             : 
     404             :   /**
     405             :    * Convert from google.protobuf.Any to a typed message.
     406             :    * @param message source google.protobuf.Any message.
     407             :    *
     408             :    * @return MessageType the typed message inside the Any.
     409             :    */
     410             :   template <class MessageType>
     411         598 :   static inline void anyConvert(const ProtobufWkt::Any& message, MessageType& typed_message) {
     412         598 :     unpackTo(message, typed_message);
     413         598 :   };
     414             : 
     415             :   template <class MessageType>
     416         200 :   static inline MessageType anyConvert(const ProtobufWkt::Any& message) {
     417         200 :     MessageType typed_message;
     418         200 :     anyConvert(message, typed_message);
     419         200 :     return typed_message;
     420         200 :   };
     421             : 
     422             :   /**
     423             :    * Convert and validate from google.protobuf.Any to a typed message.
     424             :    * @param message source google.protobuf.Any message.
     425             :    *
     426             :    * @return MessageType the typed message inside the Any.
     427             :    * @throw EnvoyException if the message does not satisfy its type constraints.
     428             :    */
     429             :   template <class MessageType>
     430             :   static inline void anyConvertAndValidate(const ProtobufWkt::Any& message,
     431             :                                            MessageType& typed_message,
     432         398 :                                            ProtobufMessage::ValidationVisitor& validation_visitor) {
     433         398 :     anyConvert<MessageType>(message, typed_message);
     434         398 :     validate(typed_message, validation_visitor);
     435         398 :   };
     436             : 
     437             :   template <class MessageType>
     438             :   static inline MessageType
     439             :   anyConvertAndValidate(const ProtobufWkt::Any& message,
     440           0 :                         ProtobufMessage::ValidationVisitor& validation_visitor) {
     441           0 :     MessageType typed_message;
     442           0 :     anyConvertAndValidate<MessageType>(message, typed_message, validation_visitor);
     443           0 :     return typed_message;
     444           0 :   };
     445             : 
     446             :   /**
     447             :    * Obtain a string field from a protobuf message dynamically.
     448             :    *
     449             :    * @param message message to extract from.
     450             :    * @param field_name field name.
     451             :    *
     452             :    * @return std::string with field value.
     453             :    */
     454             :   static inline std::string getStringField(const Protobuf::Message& message,
     455         354 :                                            const std::string& field_name) {
     456         354 :     Protobuf::ReflectableMessage reflectable_message = createReflectableMessage(message);
     457         354 :     const Protobuf::Descriptor* descriptor = reflectable_message->GetDescriptor();
     458         354 :     const Protobuf::FieldDescriptor* name_field = descriptor->FindFieldByName(field_name);
     459         354 :     const Protobuf::Reflection* reflection = reflectable_message->GetReflection();
     460         354 :     return reflection->GetString(*reflectable_message, name_field);
     461           0 :     return name_field->name();
     462         354 :   }
     463             : 
     464             : #ifdef ENVOY_ENABLE_YAML
     465             :   /**
     466             :    * Convert between two protobufs via a JSON round-trip. This is used to translate arbitrary
     467             :    * messages to/from google.protobuf.Struct.
     468             :    * TODO(htuch): Avoid round-tripping via JSON strings by doing whatever
     469             :    * Protobuf::util::MessageToJsonString does but generating a google.protobuf.Struct instead.
     470             :    * @param source message.
     471             :    * @param dest message.
     472             :    */
     473             :   static void jsonConvert(const Protobuf::Message& source, Protobuf::Message& dest);
     474             :   static void jsonConvert(const Protobuf::Message& source, ProtobufWkt::Struct& dest);
     475             :   static void jsonConvert(const ProtobufWkt::Struct& source,
     476             :                           ProtobufMessage::ValidationVisitor& validation_visitor,
     477             :                           Protobuf::Message& dest);
     478             :   // Convert a message to a ProtobufWkt::Value, return false upon failure.
     479             :   static bool jsonConvertValue(const Protobuf::Message& source, ProtobufWkt::Value& dest);
     480             : 
     481             :   /**
     482             :    * Extract YAML as string from a google.protobuf.Message.
     483             :    * @param message message of type type.googleapis.com/google.protobuf.Message.
     484             :    * @param block_print whether the returned JSON should be in block style rather than flow style.
     485             :    * @param always_print_primitive_fields whether to include primitive fields set to their default
     486             :    * values, e.g. an int32 set to 0 or a bool set to false.
     487             :    * @return std::string of formatted YAML object.
     488             :    */
     489             :   static std::string getYamlStringFromMessage(const Protobuf::Message& message,
     490             :                                               const bool block_print = true,
     491             :                                               const bool always_print_primitive_fields = false);
     492             : 
     493             :   /**
     494             :    * Extract JSON as string from a google.protobuf.Message. Returns an error if the message cannot
     495             :    * be represented as JSON, which can occur if it contains an Any proto with an unrecognized type
     496             :    * URL or invalid data, or if memory cannot be allocated.
     497             :    * @param message message of type type.googleapis.com/google.protobuf.Message.
     498             :    * @param pretty_print whether the returned JSON should be formatted.
     499             :    * @param always_print_primitive_fields whether to include primitive fields set to their default
     500             :    * values, e.g. an int32 set to 0 or a bool set to false.
     501             :    * @return ProtobufUtil::StatusOr<std::string> of formatted JSON object, or an error status if
     502             :    * conversion fails.
     503             :    */
     504             :   static absl::StatusOr<std::string>
     505             :   getJsonStringFromMessage(const Protobuf::Message& message, bool pretty_print = false,
     506             :                            bool always_print_primitive_fields = false);
     507             : 
     508             :   /**
     509             :    * Extract JSON as string from a google.protobuf.Message, returning some error string if the
     510             :    * conversion to JSON fails.
     511             :    * @param message message of type type.googleapis.com/google.protobuf.Message.
     512             :    * @param pretty_print whether the returned JSON should be formatted.
     513             :    * @param always_print_primitive_fields whether to include primitive fields set to their default
     514             :    * values, e.g. an int32 set to 0 or a bool set to false.
     515             :    * @return std::string of formatted JSON object, or an error message if conversion fails.
     516             :    */
     517             :   static std::string getJsonStringFromMessageOrError(const Protobuf::Message& message,
     518             :                                                      bool pretty_print = false,
     519             :                                                      bool always_print_primitive_fields = false);
     520             : #endif
     521             : 
     522             :   static std::string convertToStringForLogs(const Protobuf::Message& message,
     523             :                                             bool pretty_print = false,
     524             :                                             bool always_print_primitive_fields = false);
     525             : 
     526             :   /**
     527             :    * Utility method to create a Struct containing the passed in key/value strings.
     528             :    *
     529             :    * @param key the key to use to set the value
     530             :    * @param value the string value to associate with the key
     531             :    */
     532             :   static ProtobufWkt::Struct keyValueStruct(const std::string& key, const std::string& value);
     533             : 
     534             :   /**
     535             :    * Utility method to create a Struct containing the passed in key/value map.
     536             :    *
     537             :    * @param fields the key/value pairs to initialize the Struct proto
     538             :    */
     539             :   static ProtobufWkt::Struct keyValueStruct(const std::map<std::string, std::string>& fields);
     540             : 
     541             :   /**
     542             :    * Utility method to print a human readable string of the code passed in.
     543             :    *
     544             :    * @param code the protobuf error code
     545             :    */
     546             :   static std::string codeEnumToString(absl::StatusCode code);
     547             : 
     548             :   /**
     549             :    * Modifies a message such that all sensitive data (that is, fields annotated as
     550             :    * `udpa.annotations.sensitive`) is redacted for display. String-typed fields annotated as
     551             :    * `sensitive` will be replaced with the string "[redacted]", bytes-typed fields will be replaced
     552             :    * with the bytes `5B72656461637465645D` (the ASCII / UTF-8 encoding of the string "[redacted]"),
     553             :    * primitive-typed fields (including enums) will be cleared, and message-typed fields will be
     554             :    * traversed recursively to redact their contents.
     555             :    *
     556             :    * LIMITATION: This works properly for strongly-typed messages, as well as for messages packed in
     557             :    * a `ProtobufWkt::Any` with a `type_url` corresponding to a proto that was compiled into the
     558             :    * Envoy binary. However it does not work for messages encoded as `ProtobufWkt::Struct`, since
     559             :    * structs are missing the "sensitive" annotations that this function expects. Similarly, it fails
     560             :    * for messages encoded as `ProtobufWkt::Any` with a `type_url` that isn't registered with the
     561             :    * binary. If you're working with struct-typed messages, including those that might be hiding
     562             :    * within strongly-typed messages, please reify them to strongly-typed messages using
     563             :    * `MessageUtil::jsonConvert()` before calling `MessageUtil::redact()`.
     564             :    *
     565             :    * @param message message to redact.
     566             :    */
     567             :   static void redact(Protobuf::Message& message);
     568             : 
     569             :   /**
     570             :    * Reinterpret a Protobuf message as another Protobuf message by converting to wire format and
     571             :    * back. This only works for messages that can be effectively duck typed this way, e.g. with a
     572             :    * subtype relationship modulo field name.
     573             :    *
     574             :    * @param src source message.
     575             :    * @param dst destination message.
     576             :    * @throw EnvoyException if a conversion error occurs.
     577             :    */
     578             :   static void wireCast(const Protobuf::Message& src, Protobuf::Message& dst);
     579             : 
     580             :   /**
     581             :    * Sanitizes a string to contain only valid UTF-8. Invalid UTF-8 characters will be replaced. If
     582             :    * the input string is valid UTF-8, it will be returned unmodified.
     583             :    */
     584             :   static std::string sanitizeUtf8String(absl::string_view str);
     585             : };
     586             : 
     587             : class ValueUtil {
     588             : public:
     589          32 :   static std::size_t hash(const ProtobufWkt::Value& value) { return MessageUtil::hash(value); }
     590             : 
     591             : #ifdef ENVOY_ENABLE_YAML
     592             :   /**
     593             :    * Load YAML string into ProtobufWkt::Value.
     594             :    */
     595             :   static ProtobufWkt::Value loadFromYaml(const std::string& yaml);
     596             : #endif
     597             : 
     598             :   /**
     599             :    * Compare two ProtobufWkt::Values for equality.
     600             :    * @param v1 message of type type.googleapis.com/google.protobuf.Value
     601             :    * @param v2 message of type type.googleapis.com/google.protobuf.Value
     602             :    * @return true if v1 and v2 are identical
     603             :    */
     604             :   static bool equal(const ProtobufWkt::Value& v1, const ProtobufWkt::Value& v2);
     605             : 
     606             :   /**
     607             :    * @return wrapped ProtobufWkt::NULL_VALUE.
     608             :    */
     609             :   static const ProtobufWkt::Value& nullValue();
     610             : 
     611             :   /**
     612             :    * Wrap std::string into ProtobufWkt::Value string value.
     613             :    * @param str string to be wrapped.
     614             :    * @return wrapped string.
     615             :    */
     616             :   static ProtobufWkt::Value stringValue(const std::string& str);
     617             : 
     618             :   /**
     619             :    * Wrap optional std::string into ProtobufWkt::Value string value.
     620             :    * If the argument contains a null optional, return ProtobufWkt::NULL_VALUE.
     621             :    * @param str string to be wrapped.
     622             :    * @return wrapped string.
     623             :    */
     624             :   static ProtobufWkt::Value optionalStringValue(const absl::optional<std::string>& str);
     625             : 
     626             :   /**
     627             :    * Wrap boolean into ProtobufWkt::Value boolean value.
     628             :    * @param str boolean to be wrapped.
     629             :    * @return wrapped boolean.
     630             :    */
     631             :   static ProtobufWkt::Value boolValue(bool b);
     632             : 
     633             :   /**
     634             :    * Wrap ProtobufWkt::Struct into ProtobufWkt::Value struct value.
     635             :    * @param obj struct to be wrapped.
     636             :    * @return wrapped struct.
     637             :    */
     638             :   static ProtobufWkt::Value structValue(const ProtobufWkt::Struct& obj);
     639             : 
     640             :   /**
     641             :    * Wrap number into ProtobufWkt::Value double value.
     642             :    * @param num number to be wrapped.
     643             :    * @return wrapped number.
     644             :    */
     645           0 :   template <typename T> static ProtobufWkt::Value numberValue(const T num) {
     646           0 :     ProtobufWkt::Value val;
     647           0 :     val.set_number_value(static_cast<double>(num));
     648           0 :     return val;
     649           0 :   }
     650             : 
     651             :   /**
     652             :    * Wrap a collection of ProtobufWkt::Values into ProtobufWkt::Value list value.
     653             :    * @param values collection of ProtobufWkt::Values to be wrapped.
     654             :    * @return wrapped list value.
     655             :    */
     656             :   static ProtobufWkt::Value listValue(const std::vector<ProtobufWkt::Value>& values);
     657             : };
     658             : 
     659             : /**
     660             :  * HashedValue is a wrapper around ProtobufWkt::Value that computes
     661             :  * and stores a hash code for the Value at construction.
     662             :  */
     663             : class HashedValue {
     664             : public:
     665          32 :   HashedValue(const ProtobufWkt::Value& value) : value_(value), hash_(ValueUtil::hash(value)){};
     666          32 :   HashedValue(const HashedValue& v) = default;
     667             : 
     668           0 :   const ProtobufWkt::Value& value() const { return value_; }
     669           0 :   std::size_t hash() const { return hash_; }
     670             : 
     671           0 :   bool operator==(const HashedValue& rhs) const {
     672           0 :     return hash_ == rhs.hash_ && ValueUtil::equal(value_, rhs.value_);
     673           0 :   }
     674             : 
     675           0 :   bool operator!=(const HashedValue& rhs) const { return !(*this == rhs); }
     676             : 
     677             : private:
     678             :   const ProtobufWkt::Value value_;
     679             :   const std::size_t hash_;
     680             : };
     681             : 
     682             : class DurationUtil {
     683             : public:
     684             :   /**
     685             :    * Same as DurationUtil::durationToMilliseconds but with extra validation logic.
     686             :    * Same as Protobuf::util::TimeUtil::DurationToSeconds but with extra validation logic.
     687             :    * Specifically, we ensure that the duration is positive.
     688             :    * @param duration protobuf.
     689             :    * @return duration in milliseconds.
     690             :    * @throw EnvoyException when duration is out-of-range.
     691             :    */
     692             :   static uint64_t durationToMilliseconds(const ProtobufWkt::Duration& duration);
     693             : 
     694             :   /**
     695             :    * Same as DurationUtil::durationToMilliseconds but does not throw an exception.
     696             :    * @param duration protobuf.
     697             :    * @return duration in milliseconds or an error status.
     698             :    */
     699             :   static absl::StatusOr<uint64_t>
     700             :   durationToMillisecondsNoThrow(const ProtobufWkt::Duration& duration);
     701             : 
     702             :   /**
     703             :    * Same as Protobuf::util::TimeUtil::DurationToSeconds but with extra validation logic.
     704             :    * Specifically, we ensure that the duration is positive.
     705             :    * @param duration protobuf.
     706             :    * @return duration in seconds.
     707             :    * @throw EnvoyException when duration is out-of-range.
     708             :    */
     709             :   static uint64_t durationToSeconds(const ProtobufWkt::Duration& duration);
     710             : };
     711             : 
     712             : class TimestampUtil {
     713             : public:
     714             :   /**
     715             :    * Writes a time_point<system_clock> (SystemTime) to a protobuf Timestamp, by way of time_t.
     716             :    * @param system_clock_time the time to write
     717             :    * @param timestamp a pointer to the mutable protobuf member to be written into.
     718             :    */
     719             :   static void systemClockToTimestamp(const SystemTime system_clock_time,
     720             :                                      ProtobufWkt::Timestamp& timestamp);
     721             : };
     722             : 
     723             : class StructUtil {
     724             : public:
     725             :   /**
     726             :    * Recursively updates in-place a protobuf structure with keys from another
     727             :    * object.
     728             :    *
     729             :    * The merging strategy is the following. If a key from \p other does not
     730             :    * exists, it's just copied into \p obj. If the key exists but has a
     731             :    * different type, it is replaced by the new value. Otherwise:
     732             :    * - for scalar values (null, string, number, boolean) are replaced with the new value
     733             :    * - for lists: new values are added to the current list
     734             :    * - for structures: recursively apply this scheme
     735             :    *
     736             :    * @param obj the object to update in-place
     737             :    * @param with the object to update \p obj with
     738             :    */
     739             :   static void update(ProtobufWkt::Struct& obj, const ProtobufWkt::Struct& with);
     740             : };
     741             : 
     742             : } // namespace Envoy
     743             : 
     744             : namespace std {
     745             : // Inject an implementation of std::hash for Envoy::HashedValue into the std namespace.
     746             : template <> struct hash<Envoy::HashedValue> {
     747           0 :   std::size_t operator()(Envoy::HashedValue const& v) const { return v.hash(); }
     748             : };
     749             : 
     750             : } // namespace std

Generated by: LCOV version 1.15