• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2021 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include <grpc/support/port_platform.h>
18 
19 #include "src/core/ext/xds/xds_http_fault_filter.h"
20 
21 #include <grpc/grpc.h>
22 
23 #include <string>
24 
25 #include "absl/status/statusor.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/strings/str_format.h"
28 #include "absl/strings/string_view.h"
29 #include "envoy/extensions/filters/common/fault/v3/fault.upb.h"
30 #include "envoy/extensions/filters/http/fault/v3/fault.upb.h"
31 #include "envoy/extensions/filters/http/fault/v3/fault.upbdefs.h"
32 #include "envoy/type/v3/percent.upb.h"
33 #include "google/protobuf/any.upb.h"
34 #include "google/protobuf/duration.upb.h"
35 #include "google/protobuf/wrappers.upb.h"
36 #include "src/core/ext/filters/fault_injection/fault_injection_filter.h"
37 #include "src/core/ext/xds/xds_http_filters.h"
38 #include "src/core/lib/channel/channel_args.h"
39 #include "src/core/lib/channel/channel_stack.h"
40 #include "src/core/lib/channel/status_util.h"
41 #include "src/core/lib/json/json.h"
42 #include "src/core/lib/transport/status_conversion.h"
43 #include "upb/def.h"
44 
45 namespace grpc_core {
46 
47 const char* kXdsHttpFaultFilterConfigName =
48     "envoy.extensions.filters.http.fault.v3.HTTPFault";
49 
50 namespace {
51 
GetDenominator(const envoy_type_v3_FractionalPercent * fraction)52 uint32_t GetDenominator(const envoy_type_v3_FractionalPercent* fraction) {
53   if (fraction != nullptr) {
54     const auto denominator =
55         static_cast<envoy_type_v3_FractionalPercent_DenominatorType>(
56             envoy_type_v3_FractionalPercent_denominator(fraction));
57     switch (denominator) {
58       case envoy_type_v3_FractionalPercent_MILLION:
59         return 1000000;
60       case envoy_type_v3_FractionalPercent_TEN_THOUSAND:
61         return 10000;
62       case envoy_type_v3_FractionalPercent_HUNDRED:
63       default:
64         return 100;
65     }
66   }
67   // Use 100 as the default denominator
68   return 100;
69 }
70 
ParseHttpFaultIntoJson(upb_strview serialized_http_fault,upb_arena * arena)71 absl::StatusOr<Json> ParseHttpFaultIntoJson(upb_strview serialized_http_fault,
72                                             upb_arena* arena) {
73   auto* http_fault = envoy_extensions_filters_http_fault_v3_HTTPFault_parse(
74       serialized_http_fault.data, serialized_http_fault.size, arena);
75   if (http_fault == nullptr) {
76     return absl::InvalidArgumentError(
77         "could not parse fault injection filter config");
78   }
79   // NOTE(lidiz): Here, we are manually translating the upb messages into the
80   // JSON form of the filter config as part of method config, which will be
81   // directly used later by service config. In this way, we can validate the
82   // filter configs, and NACK if needed. It also allows the service config to
83   // function independently without xDS, but not the other way around.
84   // NOTE(lidiz): please refer to FaultInjectionPolicy for ground truth
85   // definitions, located at:
86   // src/core/ext/filters/fault_injection/service_config_parser.h
87   Json::Object fault_injection_policy_json;
88   // Section 1: Parse the abort injection config
89   const auto* fault_abort =
90       envoy_extensions_filters_http_fault_v3_HTTPFault_abort(http_fault);
91   if (fault_abort != nullptr) {
92     grpc_status_code abort_grpc_status_code = GRPC_STATUS_OK;
93     // Try if gRPC status code is set first
94     int abort_grpc_status_code_raw =
95         envoy_extensions_filters_http_fault_v3_FaultAbort_grpc_status(
96             fault_abort);
97     if (abort_grpc_status_code_raw != 0) {
98       if (!grpc_status_code_from_int(abort_grpc_status_code_raw,
99                                      &abort_grpc_status_code)) {
100         return absl::InvalidArgumentError(absl::StrCat(
101             "invalid gRPC status code: ", abort_grpc_status_code_raw));
102       }
103     } else {
104       // if gRPC status code is empty, check http status
105       int abort_http_status_code =
106           envoy_extensions_filters_http_fault_v3_FaultAbort_http_status(
107               fault_abort);
108       if (abort_http_status_code != 0 and abort_http_status_code != 200) {
109         abort_grpc_status_code =
110             grpc_http2_status_to_grpc_status(abort_http_status_code);
111       }
112     }
113     // Set the abort_code, even if it's OK
114     fault_injection_policy_json["abortCode"] =
115         grpc_status_code_to_string(abort_grpc_status_code);
116     // Set the headers if we enabled header abort injection control
117     if (envoy_extensions_filters_http_fault_v3_FaultAbort_has_header_abort(
118             fault_abort)) {
119       fault_injection_policy_json["abortCodeHeader"] =
120           "x-envoy-fault-abort-grpc-request";
121       fault_injection_policy_json["abortPercentageHeader"] =
122           "x-envoy-fault-abort-percentage";
123     }
124     // Set the fraction percent
125     auto* percent =
126         envoy_extensions_filters_http_fault_v3_FaultAbort_percentage(
127             fault_abort);
128     fault_injection_policy_json["abortPercentageNumerator"] =
129         Json(envoy_type_v3_FractionalPercent_numerator(percent));
130     fault_injection_policy_json["abortPercentageDenominator"] =
131         Json(GetDenominator(percent));
132   }
133   // Section 2: Parse the delay injection config
134   const auto* fault_delay =
135       envoy_extensions_filters_http_fault_v3_HTTPFault_delay(http_fault);
136   if (fault_delay != nullptr) {
137     // Parse the delay duration
138     const auto* delay_duration =
139         envoy_extensions_filters_common_fault_v3_FaultDelay_fixed_delay(
140             fault_delay);
141     if (delay_duration != nullptr) {
142       fault_injection_policy_json["delay"] = absl::StrFormat(
143           "%d.%09ds", google_protobuf_Duration_seconds(delay_duration),
144           google_protobuf_Duration_nanos(delay_duration));
145     }
146     // Set the headers if we enabled header delay injection control
147     if (envoy_extensions_filters_common_fault_v3_FaultDelay_has_header_delay(
148             fault_delay)) {
149       fault_injection_policy_json["delayHeader"] =
150           "x-envoy-fault-delay-request";
151       fault_injection_policy_json["delayPercentageHeader"] =
152           "x-envoy-fault-delay-request-percentage";
153     }
154     // Set the fraction percent
155     auto* percent =
156         envoy_extensions_filters_common_fault_v3_FaultDelay_percentage(
157             fault_delay);
158     fault_injection_policy_json["delayPercentageNumerator"] =
159         Json(envoy_type_v3_FractionalPercent_numerator(percent));
160     fault_injection_policy_json["delayPercentageDenominator"] =
161         Json(GetDenominator(percent));
162   }
163   // Section 3: Parse the maximum active faults
164   const auto* max_fault_wrapper =
165       envoy_extensions_filters_http_fault_v3_HTTPFault_max_active_faults(
166           http_fault);
167   if (max_fault_wrapper != nullptr) {
168     fault_injection_policy_json["maxFaults"] =
169         google_protobuf_UInt32Value_value(max_fault_wrapper);
170   }
171   return fault_injection_policy_json;
172 }
173 
174 }  // namespace
175 
PopulateSymtab(upb_symtab * symtab) const176 void XdsHttpFaultFilter::PopulateSymtab(upb_symtab* symtab) const {
177   envoy_extensions_filters_http_fault_v3_HTTPFault_getmsgdef(symtab);
178 }
179 
180 absl::StatusOr<XdsHttpFilterImpl::FilterConfig>
GenerateFilterConfig(upb_strview serialized_filter_config,upb_arena * arena) const181 XdsHttpFaultFilter::GenerateFilterConfig(upb_strview serialized_filter_config,
182                                          upb_arena* arena) const {
183   absl::StatusOr<Json> parse_result =
184       ParseHttpFaultIntoJson(serialized_filter_config, arena);
185   if (!parse_result.ok()) {
186     return parse_result.status();
187   }
188   return FilterConfig{kXdsHttpFaultFilterConfigName, std::move(*parse_result)};
189 }
190 
191 absl::StatusOr<XdsHttpFilterImpl::FilterConfig>
GenerateFilterConfigOverride(upb_strview serialized_filter_config,upb_arena * arena) const192 XdsHttpFaultFilter::GenerateFilterConfigOverride(
193     upb_strview serialized_filter_config, upb_arena* arena) const {
194   // HTTPFault filter has the same message type in HTTP connection manager's
195   // filter config and in overriding filter config field.
196   return GenerateFilterConfig(serialized_filter_config, arena);
197 }
198 
channel_filter() const199 const grpc_channel_filter* XdsHttpFaultFilter::channel_filter() const {
200   return &FaultInjectionFilterVtable;
201 }
202 
ModifyChannelArgs(grpc_channel_args * args) const203 grpc_channel_args* XdsHttpFaultFilter::ModifyChannelArgs(
204     grpc_channel_args* args) const {
205   grpc_arg args_to_add = grpc_channel_arg_integer_create(
206       const_cast<char*>(GRPC_ARG_PARSE_FAULT_INJECTION_METHOD_CONFIG), 1);
207   grpc_channel_args* new_args =
208       grpc_channel_args_copy_and_add(args, &args_to_add, 1);
209   // Since this function takes the ownership of the channel args, it needs to
210   // deallocate the old ones to prevent leak.
211   grpc_channel_args_destroy(args);
212   return new_args;
213 }
214 
215 absl::StatusOr<XdsHttpFilterImpl::ServiceConfigJsonEntry>
GenerateServiceConfig(const FilterConfig & hcm_filter_config,const FilterConfig * filter_config_override) const216 XdsHttpFaultFilter::GenerateServiceConfig(
217     const FilterConfig& hcm_filter_config,
218     const FilterConfig* filter_config_override) const {
219   Json policy_json = filter_config_override != nullptr
220                          ? filter_config_override->config
221                          : hcm_filter_config.config;
222   // The policy JSON may be empty, that's allowed.
223   return ServiceConfigJsonEntry{"faultInjectionPolicy", policy_json.Dump()};
224 }
225 
226 }  // namespace grpc_core
227