/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 |