/proc/self/cwd/source/common/http/codes.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/common/http/codes.h" |
2 | | |
3 | | #include <cstdint> |
4 | | #include <string> |
5 | | |
6 | | #include "envoy/http/header_map.h" |
7 | | #include "envoy/stats/scope.h" |
8 | | |
9 | | #include "source/common/common/enum_to_int.h" |
10 | | #include "source/common/common/utility.h" |
11 | | #include "source/common/http/headers.h" |
12 | | #include "source/common/http/utility.h" |
13 | | |
14 | | #include "absl/strings/match.h" |
15 | | #include "absl/strings/str_cat.h" |
16 | | #include "absl/strings/str_join.h" |
17 | | |
18 | | namespace Envoy { |
19 | | namespace Http { |
20 | | |
21 | | CodeStatsImpl::CodeStatsImpl(Stats::SymbolTable& symbol_table) |
22 | | : stat_name_pool_(symbol_table), symbol_table_(symbol_table), |
23 | | canary_(stat_name_pool_.add("canary")), external_(stat_name_pool_.add("external")), |
24 | | internal_(stat_name_pool_.add("internal")), |
25 | | upstream_rq_1xx_(stat_name_pool_.add("upstream_rq_1xx")), |
26 | | upstream_rq_2xx_(stat_name_pool_.add("upstream_rq_2xx")), |
27 | | upstream_rq_3xx_(stat_name_pool_.add("upstream_rq_3xx")), |
28 | | upstream_rq_4xx_(stat_name_pool_.add("upstream_rq_4xx")), |
29 | | upstream_rq_5xx_(stat_name_pool_.add("upstream_rq_5xx")), |
30 | | upstream_rq_unknown_(stat_name_pool_.add("upstream_rq_unknown")), // Covers invalid http |
31 | | // response codes e.g. 600. |
32 | | upstream_rq_completed_(stat_name_pool_.add("upstream_rq_completed")), |
33 | | upstream_rq_time_(stat_name_pool_.add("upstream_rq_time")), |
34 | | vcluster_(stat_name_pool_.add("vcluster")), vhost_(stat_name_pool_.add("vhost")), |
35 | 24.5k | route_(stat_name_pool_.add("route")), zone_(stat_name_pool_.add("zone")) { |
36 | | |
37 | | // Pre-allocate response codes 200, 404, and 503, as those seem quite likely. |
38 | | // We don't pre-allocate all the HTTP codes because the first 127 allocations |
39 | | // are likely to be encoded in one byte, and we would rather spend those on |
40 | | // common components of stat-names that appear frequently. |
41 | 24.5k | upstreamRqStatName(Code::OK); |
42 | 24.5k | upstreamRqStatName(Code::NotFound); |
43 | 24.5k | upstreamRqStatName(Code::ServiceUnavailable); |
44 | 24.5k | } |
45 | | |
46 | 13.6k | void CodeStatsImpl::incCounter(Stats::Scope& scope, const Stats::StatNameVec& names) const { |
47 | 13.6k | const Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(names); |
48 | 13.6k | scope.counterFromStatName(Stats::StatName(stat_name_storage.get())).inc(); |
49 | 13.6k | } |
50 | | |
51 | 6.83k | void CodeStatsImpl::incCounter(Stats::Scope& scope, Stats::StatName a, Stats::StatName b) const { |
52 | 6.83k | const Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join({a, b}); |
53 | 6.83k | scope.counterFromStatName(Stats::StatName(stat_name_storage.get())).inc(); |
54 | 6.83k | } |
55 | | |
56 | | void CodeStatsImpl::recordHistogram(Stats::Scope& scope, const Stats::StatNameVec& names, |
57 | 3.24k | Stats::Histogram::Unit unit, uint64_t count) const { |
58 | 3.24k | const Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(names); |
59 | 3.24k | scope.histogramFromStatName(Stats::StatName(stat_name_storage.get()), unit).recordValue(count); |
60 | 3.24k | } |
61 | | |
62 | | void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, |
63 | | Code response_code, |
64 | 2.27k | bool exclude_http_code_stats) const { |
65 | 2.27k | ASSERT(&symbol_table_ == &scope.symbolTable()); |
66 | | |
67 | | // Build a dynamic stat for the response code and increment it. |
68 | 2.27k | incCounter(scope, prefix, upstream_rq_completed_); |
69 | | |
70 | 2.27k | if (!exclude_http_code_stats) { |
71 | 2.27k | const Stats::StatName rq_group = upstreamRqGroup(response_code); |
72 | 2.27k | if (!rq_group.empty()) { |
73 | 2.27k | incCounter(scope, prefix, rq_group); |
74 | 2.27k | } |
75 | 2.27k | incCounter(scope, prefix, upstreamRqStatName(response_code)); |
76 | 2.27k | } |
77 | 2.27k | } |
78 | | |
79 | | void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info, |
80 | 2.27k | bool exclude_http_code_stats) const { |
81 | 2.27k | const Code code = static_cast<Code>(info.response_status_code_); |
82 | | |
83 | 2.27k | ASSERT(&info.cluster_scope_.symbolTable() == &symbol_table_); |
84 | 2.27k | chargeBasicResponseStat(info.cluster_scope_, info.prefix_, code, exclude_http_code_stats); |
85 | | |
86 | 2.27k | const Stats::StatName rq_group = upstreamRqGroup(code); |
87 | 2.27k | const Stats::StatName rq_code = upstreamRqStatName(code); |
88 | | |
89 | | // If the response is from a canary, also create canary stats. |
90 | 2.27k | if (info.upstream_canary_) { |
91 | 0 | writeCategory(info, rq_group, rq_code, canary_); |
92 | 0 | } |
93 | | |
94 | | // Split stats into external vs. internal. |
95 | 2.27k | if (info.internal_request_) { |
96 | 1.12k | writeCategory(info, rq_group, rq_code, internal_); |
97 | 1.15k | } else { |
98 | 1.15k | writeCategory(info, rq_group, rq_code, external_); |
99 | 1.15k | } |
100 | | |
101 | | // Handle request virtual cluster. |
102 | 2.27k | if (!info.request_vcluster_name_.empty()) { |
103 | 0 | incCounter(info.global_scope_, {vhost_, info.request_vhost_name_, vcluster_, |
104 | 0 | info.request_vcluster_name_, upstream_rq_completed_}); |
105 | 0 | incCounter(info.global_scope_, {vhost_, info.request_vhost_name_, vcluster_, |
106 | 0 | info.request_vcluster_name_, rq_group}); |
107 | 0 | incCounter(info.global_scope_, |
108 | 0 | {vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, rq_code}); |
109 | 0 | } |
110 | | |
111 | | // Handle route level stats. |
112 | 2.27k | if (!info.request_route_name_.empty()) { |
113 | 0 | incCounter(info.global_scope_, {vhost_, info.request_vhost_name_, route_, |
114 | 0 | info.request_route_name_, upstream_rq_completed_}); |
115 | 0 | incCounter(info.global_scope_, |
116 | 0 | {vhost_, info.request_vhost_name_, route_, info.request_route_name_, rq_group}); |
117 | 0 | incCounter(info.global_scope_, |
118 | 0 | {vhost_, info.request_vhost_name_, route_, info.request_route_name_, rq_code}); |
119 | 0 | } |
120 | | |
121 | | // Handle per zone stats. |
122 | 2.27k | if (!info.from_zone_.empty() && !info.to_zone_.empty()) { |
123 | 2.27k | incCounter(info.cluster_scope_, |
124 | 2.27k | {info.prefix_, zone_, info.from_zone_, info.to_zone_, upstream_rq_completed_}); |
125 | 2.27k | incCounter(info.cluster_scope_, |
126 | 2.27k | {info.prefix_, zone_, info.from_zone_, info.to_zone_, rq_group}); |
127 | 2.27k | incCounter(info.cluster_scope_, {info.prefix_, zone_, info.from_zone_, info.to_zone_, rq_code}); |
128 | 2.27k | } |
129 | 2.27k | } |
130 | | |
131 | | void CodeStatsImpl::writeCategory(const ResponseStatInfo& info, Stats::StatName rq_group, |
132 | 2.27k | Stats::StatName rq_code, Stats::StatName category) const { |
133 | 2.27k | incCounter(info.cluster_scope_, {info.prefix_, category, upstream_rq_completed_}); |
134 | 2.27k | if (!rq_group.empty()) { |
135 | 2.27k | incCounter(info.cluster_scope_, {info.prefix_, category, rq_group}); |
136 | 2.27k | } |
137 | 2.27k | incCounter(info.cluster_scope_, {info.prefix_, category, rq_code}); |
138 | 2.27k | } |
139 | | |
140 | 1.08k | void CodeStatsImpl::chargeResponseTiming(const ResponseTimingInfo& info) const { |
141 | 1.08k | const uint64_t count = info.response_time_.count(); |
142 | 1.08k | recordHistogram(info.cluster_scope_, {info.prefix_, upstream_rq_time_}, |
143 | 1.08k | Stats::Histogram::Unit::Milliseconds, count); |
144 | 1.08k | if (info.upstream_canary_) { |
145 | 0 | recordHistogram(info.cluster_scope_, {info.prefix_, canary_, upstream_rq_time_}, |
146 | 0 | Stats::Histogram::Unit::Milliseconds, count); |
147 | 0 | } |
148 | | |
149 | 1.08k | if (info.internal_request_) { |
150 | 1 | recordHistogram(info.cluster_scope_, {info.prefix_, internal_, upstream_rq_time_}, |
151 | 1 | Stats::Histogram::Unit::Milliseconds, count); |
152 | 1.08k | } else { |
153 | 1.08k | recordHistogram(info.cluster_scope_, {info.prefix_, external_, upstream_rq_time_}, |
154 | 1.08k | Stats::Histogram::Unit::Milliseconds, count); |
155 | 1.08k | } |
156 | | |
157 | 1.08k | if (!info.request_vcluster_name_.empty()) { |
158 | 0 | recordHistogram(info.global_scope_, |
159 | 0 | {vhost_, info.request_vhost_name_, vcluster_, info.request_vcluster_name_, |
160 | 0 | upstream_rq_time_}, |
161 | 0 | Stats::Histogram::Unit::Milliseconds, count); |
162 | 0 | } |
163 | | |
164 | 1.08k | if (!info.request_route_name_.empty()) { |
165 | 0 | recordHistogram( |
166 | 0 | info.global_scope_, |
167 | 0 | {vhost_, info.request_vhost_name_, route_, info.request_route_name_, upstream_rq_time_}, |
168 | 0 | Stats::Histogram::Unit::Milliseconds, count); |
169 | 0 | } |
170 | | |
171 | | // Handle per zone stats. |
172 | 1.08k | if (!info.from_zone_.empty() && !info.to_zone_.empty()) { |
173 | 1.08k | recordHistogram(info.cluster_scope_, |
174 | 1.08k | {info.prefix_, zone_, info.from_zone_, info.to_zone_, upstream_rq_time_}, |
175 | 1.08k | Stats::Histogram::Unit::Milliseconds, count); |
176 | 1.08k | } |
177 | 1.08k | } |
178 | | |
179 | 4.55k | Stats::StatName CodeStatsImpl::upstreamRqGroup(Code response_code) const { |
180 | 4.55k | switch (enumToInt(response_code) / 100) { |
181 | 2 | case 1: |
182 | 2 | return upstream_rq_1xx_; |
183 | 4.44k | case 2: |
184 | 4.44k | return upstream_rq_2xx_; |
185 | 0 | case 3: |
186 | 0 | return upstream_rq_3xx_; |
187 | 0 | case 4: |
188 | 0 | return upstream_rq_4xx_; |
189 | 116 | case 5: |
190 | 116 | return upstream_rq_5xx_; |
191 | 4.55k | } |
192 | 0 | return empty_; // Unknown codes do not go into a group. |
193 | 4.55k | } |
194 | | |
195 | 78.0k | Stats::StatName CodeStatsImpl::upstreamRqStatName(Code response_code) const { |
196 | | // Take a lock only if we've never seen this response-code before. |
197 | 78.0k | const uint32_t rc_index = static_cast<uint32_t>(response_code) - HttpCodeOffset; |
198 | 78.0k | if (rc_index >= NumHttpCodes) { |
199 | 0 | return upstream_rq_unknown_; |
200 | 0 | } |
201 | 78.0k | return Stats::StatName(rc_stat_names_.get(rc_index, [this, response_code]() -> const uint8_t* { |
202 | 73.5k | return stat_name_pool_.addReturningStorage( |
203 | 73.5k | absl::StrCat("upstream_rq_", enumToInt(response_code))); |
204 | 73.5k | })); |
205 | 78.0k | } |
206 | | |
207 | 0 | std::string CodeUtility::groupStringForResponseCode(Code response_code) { |
208 | | // Note: this is only used in the unit test and in dynamo_filter.cc, which |
209 | | // needs the same sort of symbolization treatment we are doing here. |
210 | 0 | if (CodeUtility::is1xx(enumToInt(response_code))) { |
211 | 0 | return "1xx"; |
212 | 0 | } else if (CodeUtility::is2xx(enumToInt(response_code))) { |
213 | 0 | return "2xx"; |
214 | 0 | } else if (CodeUtility::is3xx(enumToInt(response_code))) { |
215 | 0 | return "3xx"; |
216 | 0 | } else if (CodeUtility::is4xx(enumToInt(response_code))) { |
217 | 0 | return "4xx"; |
218 | 0 | } else if (CodeUtility::is5xx(enumToInt(response_code))) { |
219 | 0 | return "5xx"; |
220 | 0 | } else { |
221 | 0 | return ""; |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | 28.5k | const char* CodeUtility::toString(Code code) { |
226 | | // clang-format off |
227 | 28.5k | switch (code) { |
228 | | // 1xx |
229 | 7.62k | case Code::Continue: return "Continue"; |
230 | 1 | case Code::SwitchingProtocols: return "Switching Protocols"; |
231 | | |
232 | | // 2xx |
233 | 5.65k | case Code::OK: return "OK"; |
234 | 4 | case Code::Created: return "Created"; |
235 | 3 | case Code::Accepted: return "Accepted"; |
236 | 3 | case Code::NonAuthoritativeInformation: return "Non-Authoritative Information"; |
237 | 3 | case Code::NoContent: return "No Content"; |
238 | 3 | case Code::ResetContent: return "Reset Content"; |
239 | 3 | case Code::PartialContent: return "Partial Content"; |
240 | 3 | case Code::MultiStatus: return "Multi-Status"; |
241 | 3 | case Code::AlreadyReported: return "Already Reported"; |
242 | 6 | case Code::IMUsed: return "IM Used"; |
243 | | |
244 | | // 3xx |
245 | 5 | case Code::MultipleChoices: return "Multiple Choices"; |
246 | 1 | case Code::MovedPermanently: return "Moved Permanently"; |
247 | 1 | case Code::Found: return "Found"; |
248 | 2 | case Code::SeeOther: return "See Other"; |
249 | 3 | case Code::NotModified: return "Not Modified"; |
250 | 3 | case Code::UseProxy: return "Use Proxy"; |
251 | 6 | case Code::TemporaryRedirect: return "Temporary Redirect"; |
252 | 1 | case Code::PermanentRedirect: return "Permanent Redirect"; |
253 | | |
254 | | // 4xx |
255 | 13.5k | case Code::BadRequest: return "Bad Request"; |
256 | 3 | case Code::Unauthorized: return "Unauthorized"; |
257 | 3 | case Code::PaymentRequired: return "Payment Required"; |
258 | 3 | case Code::Forbidden: return "Forbidden"; |
259 | 9 | case Code::NotFound: return "Not Found"; |
260 | 1 | case Code::MethodNotAllowed: return "Method Not Allowed"; |
261 | 3 | case Code::NotAcceptable: return "Not Acceptable"; |
262 | 3 | case Code::ProxyAuthenticationRequired: return "Proxy Authentication Required"; |
263 | 3 | case Code::RequestTimeout: return "Request Timeout"; |
264 | 3 | case Code::Conflict: return "Conflict"; |
265 | 10 | case Code::Gone: return "Gone"; |
266 | 3 | case Code::LengthRequired: return "Length Required"; |
267 | 4 | case Code::PreconditionFailed: return "Precondition Failed"; |
268 | 4 | case Code::PayloadTooLarge: return "Payload Too Large"; |
269 | 6 | case Code::URITooLong: return "URI Too Long"; |
270 | 3 | case Code::UnsupportedMediaType: return "Unsupported Media Type"; |
271 | 3 | case Code::RangeNotSatisfiable: return "Range Not Satisfiable"; |
272 | 3 | case Code::ExpectationFailed: return "Expectation Failed"; |
273 | 10 | case Code::MisdirectedRequest: return "Misdirected Request"; |
274 | 3 | case Code::UnprocessableEntity: return "Unprocessable Entity"; |
275 | 3 | case Code::Locked: return "Locked"; |
276 | 6 | case Code::FailedDependency: return "Failed Dependency"; |
277 | 208 | case Code::UpgradeRequired: return "Upgrade Required"; |
278 | 1 | case Code::PreconditionRequired: return "Precondition Required"; |
279 | 6 | case Code::TooManyRequests: return "Too Many Requests"; |
280 | 416 | case Code::RequestHeaderFieldsTooLarge: return "Request Header Fields Too Large"; |
281 | 10 | case Code::TooEarly: return "Too Early"; |
282 | | |
283 | | // 5xx |
284 | 3 | case Code::InternalServerError: return "Internal Server Error"; |
285 | 472 | case Code::NotImplemented: return "Not Implemented"; |
286 | 30 | case Code::BadGateway: return "Bad Gateway"; |
287 | 4 | case Code::ServiceUnavailable: return "Service Unavailable"; |
288 | 3 | case Code::GatewayTimeout: return "Gateway Timeout"; |
289 | 3 | case Code::HTTPVersionNotSupported: return "HTTP Version Not Supported"; |
290 | 1 | case Code::VariantAlsoNegotiates: return "Variant Also Negotiates"; |
291 | 3 | case Code::InsufficientStorage: return "Insufficient Storage"; |
292 | 3 | case Code::LoopDetected: return "Loop Detected"; |
293 | 5 | case Code::NotExtended: return "Not Extended"; |
294 | 6 | case Code::NetworkAuthenticationRequired: return "Network Authentication Required"; |
295 | 28.5k | } |
296 | | // clang-format on |
297 | | |
298 | 375 | return "Unknown"; |
299 | 28.5k | } |
300 | | |
301 | | } // namespace Http |
302 | | } // namespace Envoy |