1
#include "source/extensions/matching/common_inputs/transport_socket/config.h"
2

            
3
#include "envoy/config/core/v3/base.pb.h"
4
#include "envoy/config/core/v3/base.pb.validate.h"
5

            
6
#include "source/common/common/fmt.h"
7
#include "source/common/config/metadata.h"
8
#include "source/common/config/well_known_names.h"
9
#include "source/common/json/json_utility.h"
10
#include "source/common/protobuf/utility.h"
11

            
12
namespace Envoy {
13
namespace Extensions {
14
namespace Matching {
15
namespace CommonInputs {
16
namespace TransportSocket {
17

            
18
namespace {
19

            
20
/**
21
 * Anonymous helper function to extract metadata values using filter and path.
22
 * Shared between endpoint and locality metadata inputs to avoid code duplication.
23
 * @param metadata The metadata source to extract from.
24
 * @param filter The filter name for metadata extraction.
25
 * @param path The path segments for nested metadata extraction.
26
 * @return Optional string value extracted from metadata, nullopt if not found or empty.
27
 */
28
absl::optional<std::string> extractMetadataValue(const envoy::config::core::v3::Metadata* metadata,
29
                                                 const std::string& filter,
30
20
                                                 const std::vector<std::string>& path) {
31
20
  if (!metadata) {
32
2
    return absl::nullopt;
33
2
  }
34

            
35
  // Use metadata extraction with filter and path support.
36
18
  const Protobuf::Value& value = Config::Metadata::metadataValue(metadata, filter, path);
37

            
38
  // Convert the protobuf value to string.
39
18
  std::string result;
40
18
  if (value.kind_case() == Protobuf::Value::kStringValue) {
41
16
    result = value.string_value();
42
16
  } else {
43
2
    Json::Utility::appendValueToString(value, result);
44
2
  }
45

            
46
18
  if (result.empty()) {
47
2
    return absl::nullopt;
48
2
  }
49

            
50
16
  return result;
51
18
}
52

            
53
/**
54
 * Anonymous helper function to create metadata input factory callbacks.
55
 * Shared between endpoint and locality metadata inputs to reduce code duplication.
56
 * @tparam InputType The metadata input class type (EndpointMetadataInput or LocalityMetadataInput).
57
 * @param config The proto configuration message.
58
 * @return Factory callback that creates the appropriate input instance.
59
 */
60
template <typename InputType, typename ConfigType>
61
Matcher::DataInputFactoryCb<Upstream::TransportSocketMatchingData>
62
8
createMetadataInputFactoryCb(const Protobuf::Message& config) {
63
8
  const auto& typed_config = dynamic_cast<const ConfigType&>(config);
64

            
65
8
  std::string filter = typed_config.filter().empty()
66
8
                           ? std::string(Envoy::Config::MetadataFilters::get().ENVOY_LB)
67
8
                           : std::string(typed_config.filter());
68

            
69
8
  std::vector<std::string> path;
70
8
  if (typed_config.path_size() > 0) {
71
8
    path.reserve(typed_config.path_size());
72
8
    for (const auto& segment : typed_config.path()) {
73
      // Only key segments are supported per proto.
74
8
      if (segment.has_key()) {
75
8
        path.push_back(segment.key());
76
8
      }
77
8
    }
78
8
  }
79

            
80
8
  return [filter = std::move(filter), path = std::move(path)]() {
81
8
    return std::make_unique<InputType>(filter, path);
82
8
  };
83
8
}
84

            
85
} // namespace
86

            
87
Matcher::DataInputGetResult
88
30
TransportSocketInputBase::get(const Upstream::TransportSocketMatchingData& data) const {
89
30
  auto value = getValue(data);
90
30
  if (value.has_value()) {
91
20
    return Matcher::DataInputGetResult::CreateString(std::move(value.value()));
92
20
  }
93
10
  return Matcher::DataInputGetResult::NoData();
94
30
}
95

            
96
absl::optional<std::string>
97
13
EndpointMetadataInput::getValue(const Upstream::TransportSocketMatchingData& data) const {
98
13
  return extractMetadataValue(data.endpoint_metadata_, filter_, path_);
99
13
}
100

            
101
absl::optional<std::string>
102
7
LocalityMetadataInput::getValue(const Upstream::TransportSocketMatchingData& data) const {
103
7
  return extractMetadataValue(data.locality_metadata_, filter_, path_);
104
7
}
105

            
106
Matcher::DataInputFactoryCb<Upstream::TransportSocketMatchingData>
107
EndpointMetadataInputFactory::createDataInputFactoryCb(
108
6
    const Protobuf::Message& config, ProtobufMessage::ValidationVisitor& validation_visitor) {
109
6
  UNREFERENCED_PARAMETER(validation_visitor);
110
6
  return createMetadataInputFactoryCb<
111
6
      EndpointMetadataInput,
112
6
      envoy::extensions::matching::common_inputs::transport_socket::v3::EndpointMetadataInput>(
113
6
      config);
114
6
}
115

            
116
471
ProtobufTypes::MessagePtr EndpointMetadataInputFactory::createEmptyConfigProto() {
117
471
  return std::make_unique<
118
471
      envoy::extensions::matching::common_inputs::transport_socket::v3::EndpointMetadataInput>();
119
471
}
120

            
121
Matcher::DataInputFactoryCb<Upstream::TransportSocketMatchingData>
122
LocalityMetadataInputFactory::createDataInputFactoryCb(
123
2
    const Protobuf::Message& config, ProtobufMessage::ValidationVisitor& validation_visitor) {
124
2
  UNREFERENCED_PARAMETER(validation_visitor);
125
2
  return createMetadataInputFactoryCb<
126
2
      LocalityMetadataInput,
127
2
      envoy::extensions::matching::common_inputs::transport_socket::v3::LocalityMetadataInput>(
128
2
      config);
129
2
}
130

            
131
467
ProtobufTypes::MessagePtr LocalityMetadataInputFactory::createEmptyConfigProto() {
132
467
  return std::make_unique<
133
467
      envoy::extensions::matching::common_inputs::transport_socket::v3::LocalityMetadataInput>();
134
467
}
135

            
136
absl::optional<std::string>
137
10
FilterStateInput::getValue(const Upstream::TransportSocketMatchingData& data) const {
138
10
  if (!data.filter_state_) {
139
3
    return absl::nullopt;
140
3
  }
141

            
142
  // Try to get the filter state object by key.
143
7
  const auto* object = data.filter_state_->getDataReadOnly<StreamInfo::FilterState::Object>(key_);
144
7
  if (!object) {
145
1
    return absl::nullopt;
146
1
  }
147

            
148
  // Try to serialize the object to a string.
149
6
  const auto serialized = object->serializeAsString();
150
6
  if (!serialized.has_value() || serialized->empty()) {
151
2
    return absl::nullopt;
152
2
  }
153

            
154
4
  return serialized.value();
155
6
}
156

            
157
Matcher::DataInputFactoryCb<Upstream::TransportSocketMatchingData>
158
FilterStateInputFactory::createDataInputFactoryCb(
159
2
    const Protobuf::Message& config, ProtobufMessage::ValidationVisitor& validation_visitor) {
160
2
  UNREFERENCED_PARAMETER(validation_visitor);
161
2
  const auto& typed_config = dynamic_cast<
162
2
      const envoy::extensions::matching::common_inputs::transport_socket::v3::FilterStateInput&>(
163
2
      config);
164

            
165
2
  std::string key = typed_config.key();
166
2
  return [key = std::move(key)]() { return std::make_unique<FilterStateInput>(key); };
167
2
}
168

            
169
468
ProtobufTypes::MessagePtr FilterStateInputFactory::createEmptyConfigProto() {
170
468
  return std::make_unique<
171
468
      envoy::extensions::matching::common_inputs::transport_socket::v3::FilterStateInput>();
172
468
}
173

            
174
Matcher::ActionConstSharedPtr
175
TransportSocketNameActionFactory::createAction(const Protobuf::Message& config,
176
                                               Server::Configuration::ServerFactoryContext&,
177
12
                                               ProtobufMessage::ValidationVisitor&) {
178
12
  const auto& typed_config =
179
12
      dynamic_cast<const envoy::extensions::matching::common_inputs::transport_socket::v3::
180
12
                       TransportSocketNameAction&>(config);
181
12
  return std::make_shared<TransportSocketNameAction>(typed_config.name());
182
12
}
183

            
184
448
ProtobufTypes::MessagePtr TransportSocketNameActionFactory::createEmptyConfigProto() {
185
448
  return std::make_unique<envoy::extensions::matching::common_inputs::transport_socket::v3::
186
448
                              TransportSocketNameAction>();
187
448
}
188

            
189
// Register factories for transport socket matchers.
190
REGISTER_FACTORY(EndpointMetadataInputFactory,
191
                 Matcher::DataInputFactory<Upstream::TransportSocketMatchingData>);
192
REGISTER_FACTORY(LocalityMetadataInputFactory,
193
                 Matcher::DataInputFactory<Upstream::TransportSocketMatchingData>);
194
REGISTER_FACTORY(FilterStateInputFactory,
195
                 Matcher::DataInputFactory<Upstream::TransportSocketMatchingData>);
196
REGISTER_FACTORY(TransportSocketNameActionFactory,
197
                 Matcher::ActionFactory<Server::Configuration::ServerFactoryContext>);
198

            
199
} // namespace TransportSocket
200
} // namespace CommonInputs
201
} // namespace Matching
202
} // namespace Extensions
203
} // namespace Envoy