1
#include "source/common/protobuf/visitor.h"
2

            
3
#include <vector>
4

            
5
#include "source/common/protobuf/utility.h"
6
#include "source/common/protobuf/visitor_helper.h"
7

            
8
#include "udpa/type/v1/typed_struct.pb.h"
9
#include "xds/type/v3/typed_struct.pb.h"
10

            
11
namespace Envoy {
12
namespace ProtobufMessage {
13
namespace {
14

            
15
absl::Status traverseMessageWorker(ConstProtoVisitor& visitor, const Protobuf::Message& message,
16
                                   std::vector<const Protobuf::Message*>& parents,
17
2986430
                                   bool was_any_or_top_level, bool recurse_into_any) {
18
2986430
  RETURN_IF_NOT_OK(visitor.onMessage(message, parents, was_any_or_top_level));
19

            
20
  // If told to recurse into Any messages, do that here and skip the rest of the function.
21
2986416
  if (recurse_into_any) {
22
369
    std::unique_ptr<Protobuf::Message> inner_message;
23
369
    absl::string_view target_type_url;
24

            
25
369
    if (message.GetTypeName() == "google.protobuf.Any") {
26
40
      auto* any_message = Protobuf::DynamicCastMessage<Protobuf::Any>(&message);
27
40
      inner_message = Helper::typeUrlToMessage(any_message->type_url());
28
40
      target_type_url = any_message->type_url();
29
40
      if (inner_message == nullptr) {
30
1
        return absl::InvalidArgumentError(
31
1
            fmt::format("Invalid type_url '{}' during traversal", target_type_url));
32
1
      }
33
39
      RETURN_IF_NOT_OK(MessageUtil::unpackTo(*any_message, *inner_message));
34
329
    } else if (message.GetTypeName() == "xds.type.v3.TypedStruct") {
35
5
      auto output_or_error = Helper::convertTypedStruct<xds::type::v3::TypedStruct>(message);
36
5
      RETURN_IF_NOT_OK_REF(output_or_error.status());
37
5
      std::tie(inner_message, target_type_url) = std::move(output_or_error.value());
38
324
    } else if (message.GetTypeName() == "udpa.type.v1.TypedStruct") {
39
1
      auto output_or_error = Helper::convertTypedStruct<udpa::type::v1::TypedStruct>(message);
40
1
      RETURN_IF_NOT_OK_REF(output_or_error.status());
41
1
      std::tie(inner_message, target_type_url) = std::move(output_or_error.value());
42
1
    }
43

            
44
368
    if (inner_message != nullptr) {
45
      // Push the Any message as a wrapper.
46
43
      Helper::ScopedMessageParents scoped_parents(parents, message);
47
43
      return traverseMessageWorker(visitor, *inner_message, parents, true, recurse_into_any);
48
325
    } else if (!target_type_url.empty()) {
49
2
      return absl::InvalidArgumentError(
50
2
          fmt::format("Invalid type_url '{}' during traversal", target_type_url));
51
2
    }
52
368
  }
53
2986370
  Protobuf::ReflectableMessage reflectable_message = createReflectableMessage(message);
54
2986370
  const Protobuf::Descriptor* descriptor = reflectable_message->GetDescriptor();
55
2986370
  const Protobuf::Reflection* reflection = reflectable_message->GetReflection();
56
26589040
  for (int i = 0; i < descriptor->field_count(); ++i) {
57
23602701
    const Protobuf::FieldDescriptor* field = descriptor->field(i);
58
23602701
    visitor.onField(message, *field);
59

            
60
    // If this is a message, recurse in to the sub-message.
61
23602701
    if (field->cpp_type() == Protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
62
15157333
      Helper::ScopedMessageParents scoped_parents(parents, message);
63

            
64
15157333
      if (field->is_repeated()) {
65
2875682
        const int size = reflection->FieldSize(*reflectable_message, field);
66
3708454
        for (int j = 0; j < size; ++j) {
67
832785
          RETURN_IF_NOT_OK(traverseMessageWorker(
68
832785
              visitor, reflection->GetRepeatedMessage(*reflectable_message, field, j), parents,
69
832785
              false, recurse_into_any));
70
832772
        }
71
12282597
      } else if (reflection->HasField(*reflectable_message, field)) {
72
1848113
        RETURN_IF_NOT_OK(traverseMessageWorker(visitor,
73
1848113
                                               reflection->GetMessage(*reflectable_message, field),
74
1848113
                                               parents, false, recurse_into_any));
75
1848095
      }
76
15157333
    }
77
23602701
  }
78
2986339
  return absl::OkStatus();
79
2986370
}
80

            
81
} // namespace
82

            
83
absl::Status traverseMessage(ConstProtoVisitor& visitor, const Protobuf::Message& message,
84
305489
                             bool recurse_into_any) {
85
305489
  std::vector<const Protobuf::Message*> parents;
86
305489
  return traverseMessageWorker(visitor, message, parents, true, recurse_into_any);
87
305489
}
88

            
89
} // namespace ProtobufMessage
90
} // namespace Envoy