/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 | 27.7k | 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 | 27.7k | upstreamRqStatName(Code::OK); |
42 | 27.7k | upstreamRqStatName(Code::NotFound); |
43 | 27.7k | upstreamRqStatName(Code::ServiceUnavailable); |
44 | 27.7k | } |
45 | | |
46 | 11.0k | void CodeStatsImpl::incCounter(Stats::Scope& scope, const Stats::StatNameVec& names) const { |
47 | 11.0k | const Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(names); |
48 | 11.0k | scope.counterFromStatName(Stats::StatName(stat_name_storage.get())).inc(); |
49 | 11.0k | } |
50 | | |
51 | 5.82k | void CodeStatsImpl::incCounter(Stats::Scope& scope, Stats::StatName a, Stats::StatName b) const { |
52 | 5.82k | const Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join({a, b}); |
53 | 5.82k | scope.counterFromStatName(Stats::StatName(stat_name_storage.get())).inc(); |
54 | 5.82k | } |
55 | | |
56 | | void CodeStatsImpl::recordHistogram(Stats::Scope& scope, const Stats::StatNameVec& names, |
57 | 2.39k | Stats::Histogram::Unit unit, uint64_t count) const { |
58 | 2.39k | const Stats::SymbolTable::StoragePtr stat_name_storage = symbol_table_.join(names); |
59 | 2.39k | scope.histogramFromStatName(Stats::StatName(stat_name_storage.get()), unit).recordValue(count); |
60 | 2.39k | } |
61 | | |
62 | | void CodeStatsImpl::chargeBasicResponseStat(Stats::Scope& scope, Stats::StatName prefix, |
63 | | Code response_code, |
64 | 1.94k | bool exclude_http_code_stats) const { |
65 | 1.94k | ASSERT(&symbol_table_ == &scope.symbolTable()); |
66 | | |
67 | | // Build a dynamic stat for the response code and increment it. |
68 | 1.94k | incCounter(scope, prefix, upstream_rq_completed_); |
69 | | |
70 | 1.94k | if (!exclude_http_code_stats) { |
71 | 1.94k | const Stats::StatName rq_group = upstreamRqGroup(response_code); |
72 | 1.94k | if (!rq_group.empty()) { |
73 | 1.94k | incCounter(scope, prefix, rq_group); |
74 | 1.94k | } |
75 | 1.94k | incCounter(scope, prefix, upstreamRqStatName(response_code)); |
76 | 1.94k | } |
77 | 1.94k | } |
78 | | |
79 | | void CodeStatsImpl::chargeResponseStat(const ResponseStatInfo& info, |
80 | 1.94k | bool exclude_http_code_stats) const { |
81 | 1.94k | const Code code = static_cast<Code>(info.response_status_code_); |
82 | | |
83 | 1.94k | ASSERT(&info.cluster_scope_.symbolTable() == &symbol_table_); |
84 | 1.94k | chargeBasicResponseStat(info.cluster_scope_, info.prefix_, code, exclude_http_code_stats); |
85 | | |
86 | 1.94k | const Stats::StatName rq_group = upstreamRqGroup(code); |
87 | 1.94k | const Stats::StatName rq_code = upstreamRqStatName(code); |
88 | | |
89 | | // If the response is from a canary, also create canary stats. |
90 | 1.94k | if (info.upstream_canary_) { |
91 | 0 | writeCategory(info, rq_group, rq_code, canary_); |
92 | 0 | } |
93 | | |
94 | | // Split stats into external vs. internal. |
95 | 1.94k | if (info.internal_request_) { |
96 | 1.03k | writeCategory(info, rq_group, rq_code, internal_); |
97 | 1.03k | } else { |
98 | 908 | writeCategory(info, rq_group, rq_code, external_); |
99 | 908 | } |
100 | | |
101 | | // Handle request virtual cluster. |
102 | 1.94k | 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 | 1.94k | 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 | 1.94k | if (!info.from_zone_.empty() && !info.to_zone_.empty()) { |
123 | 1.74k | incCounter(info.cluster_scope_, |
124 | 1.74k | {info.prefix_, zone_, info.from_zone_, info.to_zone_, upstream_rq_completed_}); |
125 | 1.74k | incCounter(info.cluster_scope_, |
126 | 1.74k | {info.prefix_, zone_, info.from_zone_, info.to_zone_, rq_group}); |
127 | 1.74k | incCounter(info.cluster_scope_, {info.prefix_, zone_, info.from_zone_, info.to_zone_, rq_code}); |
128 | 1.74k | } |
129 | 1.94k | } |
130 | | |
131 | | void CodeStatsImpl::writeCategory(const ResponseStatInfo& info, Stats::StatName rq_group, |
132 | 1.94k | Stats::StatName rq_code, Stats::StatName category) const { |
133 | 1.94k | incCounter(info.cluster_scope_, {info.prefix_, category, upstream_rq_completed_}); |
134 | 1.94k | if (!rq_group.empty()) { |
135 | 1.94k | incCounter(info.cluster_scope_, {info.prefix_, category, rq_group}); |
136 | 1.94k | } |
137 | 1.94k | incCounter(info.cluster_scope_, {info.prefix_, category, rq_code}); |
138 | 1.94k | } |
139 | | |
140 | 799 | void CodeStatsImpl::chargeResponseTiming(const ResponseTimingInfo& info) const { |
141 | 799 | const uint64_t count = info.response_time_.count(); |
142 | 799 | recordHistogram(info.cluster_scope_, {info.prefix_, upstream_rq_time_}, |
143 | 799 | Stats::Histogram::Unit::Milliseconds, count); |
144 | 799 | 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 | 799 | if (info.internal_request_) { |
150 | 1 | recordHistogram(info.cluster_scope_, {info.prefix_, internal_, upstream_rq_time_}, |
151 | 1 | Stats::Histogram::Unit::Milliseconds, count); |
152 | 798 | } else { |
153 | 798 | recordHistogram(info.cluster_scope_, {info.prefix_, external_, upstream_rq_time_}, |
154 | 798 | Stats::Histogram::Unit::Milliseconds, count); |
155 | 798 | } |
156 | | |
157 | 799 | 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 | 799 | 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 | 799 | if (!info.from_zone_.empty() && !info.to_zone_.empty()) { |
173 | 799 | recordHistogram(info.cluster_scope_, |
174 | 799 | {info.prefix_, zone_, info.from_zone_, info.to_zone_, upstream_rq_time_}, |
175 | 799 | Stats::Histogram::Unit::Milliseconds, count); |
176 | 799 | } |
177 | 799 | } |
178 | | |
179 | 3.88k | Stats::StatName CodeStatsImpl::upstreamRqGroup(Code response_code) const { |
180 | 3.88k | switch (enumToInt(response_code) / 100) { |
181 | 6 | case 1: |
182 | 6 | return upstream_rq_1xx_; |
183 | 3.27k | case 2: |
184 | 3.27k | return upstream_rq_2xx_; |
185 | 8 | case 3: |
186 | 8 | return upstream_rq_3xx_; |
187 | 368 | case 4: |
188 | 368 | return upstream_rq_4xx_; |
189 | 226 | case 5: |
190 | 226 | return upstream_rq_5xx_; |
191 | 3.88k | } |
192 | 4 | return empty_; // Unknown codes do not go into a group. |
193 | 3.88k | } |
194 | | |
195 | 87.1k | Stats::StatName CodeStatsImpl::upstreamRqStatName(Code response_code) const { |
196 | | // Take a lock only if we've never seen this response-code before. |
197 | 87.1k | const uint32_t rc_index = static_cast<uint32_t>(response_code) - HttpCodeOffset; |
198 | 87.1k | if (rc_index >= NumHttpCodes) { |
199 | 4 | return upstream_rq_unknown_; |
200 | 4 | } |
201 | 87.1k | return Stats::StatName(rc_stat_names_.get(rc_index, [this, response_code]() -> const uint8_t* { |
202 | 83.3k | return stat_name_pool_.addReturningStorage( |
203 | 83.3k | absl::StrCat("upstream_rq_", enumToInt(response_code))); |
204 | 83.3k | })); |
205 | 87.1k | } |
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 | 24.9k | const char* CodeUtility::toString(Code code) { |
226 | | // clang-format off |
227 | 24.9k | switch (code) { |
228 | | // 1xx |
229 | 7.74k | case Code::Continue: return "Continue"; |
230 | 3 | case Code::SwitchingProtocols: return "Switching Protocols"; |
231 | | |
232 | | // 2xx |
233 | 2.77k | case Code::OK: return "OK"; |
234 | 3 | 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 | 2 | case Code::ResetContent: return "Reset Content"; |
239 | 3 | case Code::PartialContent: return "Partial Content"; |
240 | 4 | case Code::MultiStatus: return "Multi-Status"; |
241 | 6 | case Code::AlreadyReported: return "Already Reported"; |
242 | 4 | case Code::IMUsed: return "IM Used"; |
243 | | |
244 | | // 3xx |
245 | 3 | case Code::MultipleChoices: return "Multiple Choices"; |
246 | 3 | case Code::MovedPermanently: return "Moved Permanently"; |
247 | 3 | case Code::Found: return "Found"; |
248 | 3 | case Code::SeeOther: return "See Other"; |
249 | 16 | case Code::NotModified: return "Not Modified"; |
250 | 4 | case Code::UseProxy: return "Use Proxy"; |
251 | 3 | case Code::TemporaryRedirect: return "Temporary Redirect"; |
252 | 3 | case Code::PermanentRedirect: return "Permanent Redirect"; |
253 | | |
254 | | // 4xx |
255 | 12.9k | 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 | 4 | case Code::NotFound: return "Not Found"; |
260 | 3 | 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 | 6 | case Code::RequestTimeout: return "Request Timeout"; |
264 | 3 | case Code::Conflict: return "Conflict"; |
265 | 10 | case Code::Gone: return "Gone"; |
266 | 1 | case Code::LengthRequired: return "Length Required"; |
267 | 6 | case Code::PreconditionFailed: return "Precondition Failed"; |
268 | 3 | case Code::PayloadTooLarge: return "Payload Too Large"; |
269 | 6 | case Code::URITooLong: return "URI Too Long"; |
270 | 4 | case Code::UnsupportedMediaType: return "Unsupported Media Type"; |
271 | 6 | case Code::RangeNotSatisfiable: return "Range Not Satisfiable"; |
272 | 3 | case Code::ExpectationFailed: return "Expectation Failed"; |
273 | 6 | case Code::MisdirectedRequest: return "Misdirected Request"; |
274 | 6 | case Code::UnprocessableEntity: return "Unprocessable Entity"; |
275 | 6 | case Code::Locked: return "Locked"; |
276 | 3 | case Code::FailedDependency: return "Failed Dependency"; |
277 | 112 | case Code::UpgradeRequired: return "Upgrade Required"; |
278 | 3 | case Code::PreconditionRequired: return "Precondition Required"; |
279 | 3 | case Code::TooManyRequests: return "Too Many Requests"; |
280 | 263 | 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 | 522 | case Code::NotImplemented: return "Not Implemented"; |
286 | 21 | case Code::BadGateway: return "Bad Gateway"; |
287 | 5 | case Code::ServiceUnavailable: return "Service Unavailable"; |
288 | 3 | case Code::GatewayTimeout: return "Gateway Timeout"; |
289 | 1 | case Code::HTTPVersionNotSupported: return "HTTP Version Not Supported"; |
290 | 3 | case Code::VariantAlsoNegotiates: return "Variant Also Negotiates"; |
291 | 4 | case Code::InsufficientStorage: return "Insufficient Storage"; |
292 | 3 | case Code::LoopDetected: return "Loop Detected"; |
293 | 3 | case Code::NotExtended: return "Not Extended"; |
294 | 5 | case Code::NetworkAuthenticationRequired: return "Network Authentication Required"; |
295 | 24.9k | } |
296 | | // clang-format on |
297 | | |
298 | 389 | return "Unknown"; |
299 | 24.9k | } |
300 | | |
301 | | } // namespace Http |
302 | | } // namespace Envoy |