1
#include "source/common/access_log/access_log_impl.h"
2

            
3
#include <cstdint>
4
#include <string>
5

            
6
#include "envoy/common/time.h"
7
#include "envoy/config/accesslog/v3/accesslog.pb.h"
8
#include "envoy/config/accesslog/v3/accesslog.pb.validate.h"
9
#include "envoy/filesystem/filesystem.h"
10
#include "envoy/http/header_map.h"
11
#include "envoy/runtime/runtime.h"
12
#include "envoy/upstream/upstream.h"
13

            
14
#include "source/common/common/assert.h"
15
#include "source/common/common/utility.h"
16
#include "source/common/config/metadata.h"
17
#include "source/common/config/utility.h"
18
#include "source/common/grpc/common.h"
19
#include "source/common/http/header_map_impl.h"
20
#include "source/common/http/header_utility.h"
21
#include "source/common/http/headers.h"
22
#include "source/common/http/utility.h"
23
#include "source/common/protobuf/utility.h"
24
#include "source/common/stream_info/utility.h"
25
#include "source/common/tracing/http_tracer_impl.h"
26

            
27
#include "absl/types/optional.h"
28

            
29
namespace Envoy {
30
namespace AccessLog {
31

            
32
ComparisonFilter::ComparisonFilter(const envoy::config::accesslog::v3::ComparisonFilter& config,
33
                                   Runtime::Loader& runtime)
34
76
    : config_(config), runtime_(runtime) {}
35

            
36
78
bool ComparisonFilter::compareAgainstValue(uint64_t lhs) const {
37
78
  uint64_t value = config_.value().default_value();
38

            
39
78
  if (!config_.value().runtime_key().empty()) {
40
74
    value = runtime_.snapshot().getInteger(config_.value().runtime_key(), value);
41
74
  }
42

            
43
78
  switch (config_.op()) {
44
    PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
45
25
  case envoy::config::accesslog::v3::ComparisonFilter::GE:
46
25
    return lhs >= value;
47
49
  case envoy::config::accesslog::v3::ComparisonFilter::EQ:
48
49
    return lhs == value;
49
2
  case envoy::config::accesslog::v3::ComparisonFilter::LE:
50
2
    return lhs <= value;
51
2
  case envoy::config::accesslog::v3::ComparisonFilter::NE:
52
2
    return lhs != value;
53
78
  }
54
  IS_ENVOY_BUG("unexpected comparison op enum");
55
  return false;
56
78
}
57

            
58
FilterPtr FilterFactory::fromProto(const envoy::config::accesslog::v3::AccessLogFilter& config,
59
9499
                                   Server::Configuration::GenericFactoryContext& context) {
60
9499
  Runtime::Loader& runtime = context.serverFactoryContext().runtime();
61
9499
  Random::RandomGenerator& random = context.serverFactoryContext().api().randomGenerator();
62
9499
  ProtobufMessage::ValidationVisitor& validation_visitor = context.messageValidationVisitor();
63
9499
  switch (config.filter_specifier_case()) {
64
58
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kStatusCodeFilter:
65
58
    return FilterPtr{new StatusCodeFilter(config.status_code_filter(), runtime)};
66
15
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kDurationFilter:
67
15
    return FilterPtr{new DurationFilter(config.duration_filter(), runtime)};
68
9189
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kNotHealthCheckFilter:
69
9189
    return FilterPtr{new NotHealthCheckFilter()};
70
9
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kTraceableFilter:
71
9
    return FilterPtr{new TraceableRequestFilter()};
72
6
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kRuntimeFilter:
73
6
    return FilterPtr{new RuntimeFilter(config.runtime_filter(), runtime, random)};
74
6
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kAndFilter:
75
6
    return FilterPtr{new AndFilter(config.and_filter(), context)};
76
16
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kOrFilter:
77
16
    return FilterPtr{new OrFilter(config.or_filter(), context)};
78
125
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kHeaderFilter:
79
125
    return FilterPtr{new HeaderFilter(config.header_filter(), context.serverFactoryContext())};
80
4
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kResponseFlagFilter:
81
4
    MessageUtil::validate(config, validation_visitor);
82
4
    return FilterPtr{new ResponseFlagFilter(config.response_flag_filter())};
83
31
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kGrpcStatusFilter:
84
31
    MessageUtil::validate(config, validation_visitor);
85
31
    return FilterPtr{new GrpcStatusFilter(config.grpc_status_filter())};
86
4
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kMetadataFilter:
87
4
    return FilterPtr{new MetadataFilter(config.metadata_filter(), context.serverFactoryContext())};
88
4
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kLogTypeFilter:
89
4
    return FilterPtr{new LogTypeFilter(config.log_type_filter())};
90
32
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::kExtensionFilter:
91
32
    MessageUtil::validate(config, validation_visitor);
92
32
    {
93
32
      auto& factory =
94
32
          Config::Utility::getAndCheckFactory<ExtensionFilterFactory>(config.extension_filter());
95
32
      return factory.createFilter(config.extension_filter(), context);
96
    }
97
  case envoy::config::accesslog::v3::AccessLogFilter::FilterSpecifierCase::FILTER_SPECIFIER_NOT_SET:
98
    PANIC_DUE_TO_PROTO_UNSET;
99
9499
  }
100
  IS_ENVOY_BUG("unexpected filter specifier value");
101
  return nullptr;
102
9499
}
103

            
104
bool TraceableRequestFilter::evaluate(const Formatter::Context&,
105
3
                                      const StreamInfo::StreamInfo& info) const {
106
3
  const Tracing::Decision decision = Tracing::TracerUtility::shouldTraceRequest(info);
107
3
  return decision.traced && decision.reason == Tracing::Reason::ServiceForced;
108
3
}
109

            
110
bool StatusCodeFilter::evaluate(const Formatter::Context&,
111
67
                                const StreamInfo::StreamInfo& info) const {
112
67
  if (!info.responseCode()) {
113
3
    return compareAgainstValue(0ULL);
114
3
  }
115

            
116
64
  return compareAgainstValue(info.responseCode().value());
117
67
}
118

            
119
12
bool DurationFilter::evaluate(const Formatter::Context&, const StreamInfo::StreamInfo& info) const {
120
12
  absl::optional<std::chrono::nanoseconds> duration = info.currentDuration();
121
12
  if (!duration.has_value()) {
122
1
    return false;
123
1
  }
124

            
125
11
  return compareAgainstValue(
126
11
      std::chrono::duration_cast<std::chrono::milliseconds>(duration.value()).count());
127
12
}
128

            
129
RuntimeFilter::RuntimeFilter(const envoy::config::accesslog::v3::RuntimeFilter& config,
130
                             Runtime::Loader& runtime, Random::RandomGenerator& random)
131
6
    : runtime_(runtime), random_(random), runtime_key_(config.runtime_key()),
132
6
      percent_(config.percent_sampled()),
133
6
      use_independent_randomness_(config.use_independent_randomness()) {}
134

            
135
bool RuntimeFilter::evaluate(const Formatter::Context&,
136
11
                             const StreamInfo::StreamInfo& stream_info) const {
137
  // This code is verbose to avoid preallocating a random number that is not needed.
138
11
  uint64_t random_value;
139
11
  if (use_independent_randomness_) {
140
2
    random_value = random_.random();
141
9
  } else if (!stream_info.getStreamIdProvider().has_value()) {
142
4
    random_value = random_.random();
143
5
  } else {
144
5
    const auto rid_to_integer = stream_info.getStreamIdProvider()->toInteger();
145
5
    if (!rid_to_integer.has_value()) {
146
      random_value = random_.random();
147
5
    } else {
148
5
      random_value =
149
5
          rid_to_integer.value() %
150
5
          ProtobufPercentHelper::fractionalPercentDenominatorToInt(percent_.denominator());
151
5
    }
152
5
  }
153

            
154
11
  return runtime_.snapshot().featureEnabled(
155
11
      runtime_key_, percent_.numerator(), random_value,
156
11
      ProtobufPercentHelper::fractionalPercentDenominatorToInt(percent_.denominator()));
157
11
}
158

            
159
OperatorFilter::OperatorFilter(
160
    const Protobuf::RepeatedPtrField<envoy::config::accesslog::v3::AccessLogFilter>& configs,
161
22
    Server::Configuration::GenericFactoryContext& context) {
162
57
  for (const auto& config : configs) {
163
57
    auto filter = FilterFactory::fromProto(config, context);
164
57
    if (filter != nullptr) {
165
57
      filters_.emplace_back(std::move(filter));
166
57
    }
167
57
  }
168
22
}
169

            
170
OrFilter::OrFilter(const envoy::config::accesslog::v3::OrFilter& config,
171
                   Server::Configuration::GenericFactoryContext& context)
172
16
    : OperatorFilter(config.filters(), context) {}
173

            
174
AndFilter::AndFilter(const envoy::config::accesslog::v3::AndFilter& config,
175
                     Server::Configuration::GenericFactoryContext& context)
176
6
    : OperatorFilter(config.filters(), context) {}
177

            
178
bool OrFilter::evaluate(const Formatter::Context& context,
179
9
                        const StreamInfo::StreamInfo& info) const {
180
9
  bool result = false;
181
16
  for (auto& filter : filters_) {
182
16
    result |= filter->evaluate(context, info);
183

            
184
16
    if (result) {
185
7
      break;
186
7
    }
187
16
  }
188

            
189
9
  return result;
190
9
}
191

            
192
bool AndFilter::evaluate(const Formatter::Context& context,
193
4
                         const StreamInfo::StreamInfo& info) const {
194
4
  bool result = true;
195
8
  for (auto& filter : filters_) {
196
8
    result &= filter->evaluate(context, info);
197

            
198
8
    if (!result) {
199
2
      break;
200
2
    }
201
8
  }
202

            
203
4
  return result;
204
4
}
205

            
206
bool NotHealthCheckFilter::evaluate(const Formatter::Context&,
207
73472
                                    const StreamInfo::StreamInfo& info) const {
208
73472
  return !info.healthCheck();
209
73472
}
210

            
211
HeaderFilter::HeaderFilter(const envoy::config::accesslog::v3::HeaderFilter& config,
212
                           Server::Configuration::CommonFactoryContext& context)
213
126
    : header_data_(Http::HeaderUtility::createHeaderData(config.header(), context)) {}
214

            
215
bool HeaderFilter::evaluate(const Formatter::Context& context,
216
116
                            const StreamInfo::StreamInfo&) const {
217
116
  return header_data_->matchesHeaders(
218
116
      context.requestHeaders().value_or(*Http::StaticEmptyHeaders::get().request_headers));
219
116
}
220

            
221
ResponseFlagFilter::ResponseFlagFilter(
222
4
    const envoy::config::accesslog::v3::ResponseFlagFilter& config) {
223
4
  if (!config.flags().empty()) {
224
    // Preallocate the vector to avoid frequent heap allocations.
225
3
    configured_flags_.resize(StreamInfo::ResponseFlagUtils::responseFlagsVec().size(), false);
226
36
    for (int i = 0; i < config.flags_size(); i++) {
227
33
      auto response_flag = StreamInfo::ResponseFlagUtils::toResponseFlag(config.flags(i));
228
      // The config has been validated. Therefore, every flag in the config will have a mapping.
229
33
      ASSERT(response_flag.has_value());
230

            
231
      // The vector is allocated with the size of the response flags vec. Therefore, the index
232
      // should always be valid.
233
33
      ASSERT(response_flag.value().value() < configured_flags_.size());
234

            
235
33
      configured_flags_[response_flag.value().value()] = true;
236
33
    }
237
3
  }
238
4
}
239

            
240
bool ResponseFlagFilter::evaluate(const Formatter::Context&,
241
38
                                  const StreamInfo::StreamInfo& info) const {
242
38
  if (!configured_flags_.empty()) {
243
36
    for (const auto flag : info.responseFlags()) {
244
36
      ASSERT(flag.value() < configured_flags_.size());
245
36
      if (configured_flags_[flag.value()]) {
246
32
        return true;
247
32
      }
248
36
    }
249
4
    return false;
250
36
  }
251
2
  return info.hasAnyResponseFlag();
252
38
}
253

            
254
31
GrpcStatusFilter::GrpcStatusFilter(const envoy::config::accesslog::v3::GrpcStatusFilter& config) {
255
62
  for (int i = 0; i < config.statuses_size(); i++) {
256
31
    statuses_.insert(protoToGrpcStatus(config.statuses(i)));
257
31
  }
258

            
259
31
  exclude_ = config.exclude();
260
31
}
261

            
262
bool GrpcStatusFilter::evaluate(const Formatter::Context& context,
263
47
                                const StreamInfo::StreamInfo& info) const {
264

            
265
47
  Grpc::Status::GrpcStatus status = Grpc::Status::WellKnownGrpcStatus::Unknown;
266
47
  const auto optional_status = Grpc::Common::getGrpcStatus(
267
47
      context.responseTrailers().value_or(*Http::StaticEmptyHeaders::get().response_trailers),
268
47
      context.responseHeaders().value_or(*Http::StaticEmptyHeaders::get().response_headers), info);
269
47
  if (optional_status.has_value()) {
270
46
    status = optional_status.value();
271
46
  }
272

            
273
47
  const bool found = statuses_.find(status) != statuses_.end();
274
47
  return exclude_ ? !found : found;
275
47
}
276

            
277
Grpc::Status::GrpcStatus GrpcStatusFilter::protoToGrpcStatus(
278
31
    envoy::config::accesslog::v3::GrpcStatusFilter::Status status) const {
279
31
  return static_cast<Grpc::Status::GrpcStatus>(status);
280
31
}
281

            
282
4
LogTypeFilter::LogTypeFilter(const envoy::config::accesslog::v3::LogTypeFilter& config) {
283
7
  for (auto type_as_int : config.types()) {
284
7
    types_.insert(static_cast<AccessLogType>(type_as_int));
285
7
  }
286

            
287
4
  exclude_ = config.exclude();
288
4
}
289

            
290
bool LogTypeFilter::evaluate(const Formatter::Context& context,
291
9
                             const StreamInfo::StreamInfo&) const {
292
9
  const bool found = types_.contains(context.accessLogType());
293
9
  return exclude_ ? !found : found;
294
9
}
295

            
296
MetadataFilter::MetadataFilter(const envoy::config::accesslog::v3::MetadataFilter& filter_config,
297
                               Server::Configuration::CommonFactoryContext& context)
298
4
    : present_matcher_(true),
299
4
      default_match_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(filter_config, match_if_key_not_found, true)),
300
4
      filter_(filter_config.matcher().filter()) {
301

            
302
4
  if (filter_config.has_matcher()) {
303
3
    auto& matcher_config = filter_config.matcher();
304

            
305
5
    for (const auto& seg : matcher_config.path()) {
306
5
      path_.push_back(seg.key());
307
5
    }
308

            
309
    // Matches if the value equals the configured 'MetadataMatcher' value.
310
3
    const auto& val = matcher_config.value();
311
3
    value_matcher_ = Matchers::ValueMatcher::create(val, context);
312
3
  }
313
4
}
314

            
315
4
bool MetadataFilter::evaluate(const Formatter::Context&, const StreamInfo::StreamInfo& info) const {
316
4
  const auto& value =
317
4
      Envoy::Config::Metadata::metadataValue(&info.dynamicMetadata(), filter_, path_);
318
  // If the key corresponds to a set value in dynamic metadata, return true if the value matches the
319
  // the configured 'MetadataMatcher' value and false otherwise
320
4
  if (present_matcher_.match(value)) {
321
1
    return value_matcher_ && value_matcher_->match(value);
322
1
  }
323

            
324
  // If the key does not correspond to a set value in dynamic metadata, return true if
325
  // 'match_if_key_not_found' is set to true and false otherwise
326
3
  return default_match_;
327
4
}
328

            
329
InstanceSharedPtr
330
AccessLogFactory::fromProto(const envoy::config::accesslog::v3::AccessLog& config,
331
                            Server::Configuration::GenericFactoryContext& context,
332
20498
                            std::vector<Formatter::CommandParserPtr>&& command_parsers) {
333
20498
  FilterPtr filter;
334
20498
  if (config.has_filter()) {
335
9287
    filter = FilterFactory::fromProto(config.filter(), context);
336
9287
  }
337

            
338
20498
  auto& factory = Config::Utility::getAndCheckFactory<AccessLog::AccessLogInstanceFactory>(config);
339
20498
  ProtobufTypes::MessagePtr message = Config::Utility::translateToFactoryConfig(
340
20498
      config, context.messageValidationVisitor(), factory);
341

            
342
20498
  return factory.createAccessLogInstance(*message, std::move(filter), context,
343
20498
                                         std::move(command_parsers));
344
20498
}
345

            
346
} // namespace AccessLog
347
} // namespace Envoy