LCOV - code coverage report
Current view: top level - source/common/http - codes.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 109 209 52.2 %
Date: 2024-01-05 06:35:25 Functions: 12 13 92.3 %

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

Generated by: LCOV version 1.15