Coverage Report

Created: 2024-09-19 09:45

/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