Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/extensions/filters/network/ratelimit/ratelimit.cc
Line
Count
Source (jump to first uncovered line)
1
#include "source/extensions/filters/network/ratelimit/ratelimit.h"
2
3
#include <cstdint>
4
#include <iterator>
5
#include <string>
6
#include <vector>
7
8
#include "envoy/extensions/filters/network/ratelimit/v3/rate_limit.pb.h"
9
#include "envoy/stats/scope.h"
10
11
#include "source/common/common/fmt.h"
12
#include "source/common/formatter/substitution_formatter.h"
13
#include "source/common/tracing/http_tracer_impl.h"
14
#include "source/extensions/filters/network/well_known_names.h"
15
16
namespace Envoy {
17
namespace Extensions {
18
namespace NetworkFilters {
19
namespace RateLimitFilter {
20
21
Config::Config(const envoy::extensions::filters::network::ratelimit::v3::RateLimit& config,
22
               Stats::Scope& scope, Runtime::Loader& runtime)
23
    : domain_(config.domain()), stats_(generateStats(config.stat_prefix(), scope)),
24
      runtime_(runtime), failure_mode_deny_(config.failure_mode_deny()),
25
      request_headers_(Http::RequestHeaderMapImpl::create()),
26
      response_headers_(Http::ResponseHeaderMapImpl::create()),
27
12
      response_trailers_(Http::ResponseTrailerMapImpl::create()) {
28
41
  for (const auto& descriptor : config.descriptors()) {
29
41
    RateLimit::Descriptor new_descriptor;
30
211
    for (const auto& entry : descriptor.entries()) {
31
211
      new_descriptor.entries_.push_back({entry.key(), entry.value()});
32
211
      substitution_formatters_.push_back(
33
211
          std::make_unique<Formatter::FormatterImpl>(entry.value(), false));
34
211
    }
35
41
    original_descriptors_.push_back(new_descriptor);
36
41
  }
37
12
}
38
39
std::vector<RateLimit::Descriptor>
40
0
Config::applySubstitutionFormatter(StreamInfo::StreamInfo& stream_info) {
41
42
0
  std::vector<RateLimit::Descriptor> dynamic_descriptors;
43
0
  dynamic_descriptors.reserve(descriptors().size());
44
0
  std::vector<std::unique_ptr<Formatter::FormatterImpl>>::iterator formatter_it =
45
0
      substitution_formatters_.begin();
46
0
  for (const RateLimit::Descriptor& descriptor : descriptors()) {
47
0
    RateLimit::Descriptor new_descriptor;
48
0
    new_descriptor.entries_.reserve(descriptor.entries_.size());
49
0
    for (const RateLimit::DescriptorEntry& descriptor_entry : descriptor.entries_) {
50
51
0
      std::string value = descriptor_entry.value_;
52
0
      value = formatter_it->get()->formatWithContext(
53
0
          {request_headers_.get(), response_headers_.get(), response_trailers_.get(), value},
54
0
          stream_info);
55
0
      formatter_it++;
56
0
      new_descriptor.entries_.push_back({descriptor_entry.key_, value});
57
0
    }
58
0
    dynamic_descriptors.push_back(new_descriptor);
59
0
  }
60
0
  return dynamic_descriptors;
61
0
}
62
63
12
InstanceStats Config::generateStats(const std::string& name, Stats::Scope& scope) {
64
12
  std::string final_prefix = fmt::format("ratelimit.{}.", name);
65
12
  return {ALL_TCP_RATE_LIMIT_STATS(POOL_COUNTER_PREFIX(scope, final_prefix),
66
12
                                   POOL_GAUGE_PREFIX(scope, final_prefix))};
67
12
}
68
69
48
Network::FilterStatus Filter::onData(Buffer::Instance&, bool) {
70
48
  return status_ == Status::Calling ? Network::FilterStatus::StopIteration
71
48
                                    : Network::FilterStatus::Continue;
72
48
}
73
74
56
Network::FilterStatus Filter::onNewConnection() {
75
56
  if (status_ == Status::NotStarted &&
76
56
      !config_->runtime().snapshot().featureEnabled("ratelimit.tcp_filter_enabled", 100)) {
77
5
    status_ = Status::Complete;
78
5
  }
79
80
56
  if (status_ == Status::NotStarted) {
81
0
    status_ = Status::Calling;
82
0
    config_->stats().active_.inc();
83
0
    config_->stats().total_.inc();
84
0
    calling_limit_ = true;
85
0
    client_->limit(
86
0
        *this, config_->domain(),
87
0
        config_->applySubstitutionFormatter(filter_callbacks_->connection().streamInfo()),
88
0
        Tracing::NullSpan::instance(), filter_callbacks_->connection().streamInfo(), 0);
89
0
    calling_limit_ = false;
90
0
  }
91
92
56
  return status_ == Status::Calling ? Network::FilterStatus::StopIteration
93
56
                                    : Network::FilterStatus::Continue;
94
56
}
95
96
6
void Filter::onEvent(Network::ConnectionEvent event) {
97
  // Make sure that any pending request in the client is cancelled. This will be NOP if the
98
  // request already completed.
99
6
  if (event == Network::ConnectionEvent::RemoteClose ||
100
6
      event == Network::ConnectionEvent::LocalClose) {
101
6
    if (status_ == Status::Calling) {
102
0
      client_->cancel();
103
0
      config_->stats().active_.dec();
104
0
    }
105
6
  }
106
6
}
107
108
void Filter::complete(Filters::Common::RateLimit::LimitStatus status,
109
                      Filters::Common::RateLimit::DescriptorStatusListPtr&&,
110
                      Http::ResponseHeaderMapPtr&&, Http::RequestHeaderMapPtr&&, const std::string&,
111
0
                      Filters::Common::RateLimit::DynamicMetadataPtr&& dynamic_metadata) {
112
0
  if (dynamic_metadata != nullptr && !dynamic_metadata->fields().empty()) {
113
0
    filter_callbacks_->connection().streamInfo().setDynamicMetadata(
114
0
        NetworkFilterNames::get().RateLimit, *dynamic_metadata);
115
0
  }
116
117
0
  status_ = Status::Complete;
118
0
  config_->stats().active_.dec();
119
120
0
  switch (status) {
121
0
  case Filters::Common::RateLimit::LimitStatus::OK:
122
0
    config_->stats().ok_.inc();
123
0
    break;
124
0
  case Filters::Common::RateLimit::LimitStatus::Error:
125
0
    config_->stats().error_.inc();
126
0
    break;
127
0
  case Filters::Common::RateLimit::LimitStatus::OverLimit:
128
0
    config_->stats().over_limit_.inc();
129
0
    break;
130
0
  }
131
132
0
  if (status == Filters::Common::RateLimit::LimitStatus::OverLimit &&
133
0
      config_->runtime().snapshot().featureEnabled("ratelimit.tcp_filter_enforcing", 100)) {
134
0
    config_->stats().cx_closed_.inc();
135
0
    filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush,
136
0
                                          "ratelimit_close_over_limit");
137
0
  } else if (status == Filters::Common::RateLimit::LimitStatus::Error) {
138
0
    if (config_->failureModeAllow()) {
139
0
      config_->stats().failure_mode_allowed_.inc();
140
0
      if (!calling_limit_) {
141
0
        filter_callbacks_->continueReading();
142
0
      }
143
0
    } else {
144
0
      config_->stats().cx_closed_.inc();
145
0
      filter_callbacks_->connection().close(Network::ConnectionCloseType::NoFlush,
146
0
                                            "ratelimit_error_failure_mode_connection_close");
147
0
    }
148
0
  } else {
149
    // We can get completion inline, so only call continue if that isn't happening.
150
0
    if (!calling_limit_) {
151
0
      filter_callbacks_->continueReading();
152
0
    }
153
0
  }
154
0
}
155
156
} // namespace RateLimitFilter
157
} // namespace NetworkFilters
158
} // namespace Extensions
159
} // namespace Envoy