3 * Copyright 2018 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include <grpc/support/port_platform.h>
21 #include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
27 #include <grpc/support/alloc.h>
28 #include <grpc/support/log.h>
29 #include <grpc/support/string_util.h>
31 #include "src/core/ext/filters/client_channel/client_channel.h"
32 #include "src/core/ext/filters/client_channel/lb_policy_registry.h"
33 #include "src/core/ext/filters/client_channel/server_address.h"
34 #include "src/core/lib/channel/channel_args.h"
35 #include "src/core/lib/channel/status_util.h"
36 #include "src/core/lib/gpr/string.h"
37 #include "src/core/lib/gprpp/memory.h"
38 #include "src/core/lib/gprpp/optional.h"
39 #include "src/core/lib/uri/uri_parser.h"
41 // As per the retry design, we do not allow more than 5 retry attempts.
42 #define MAX_MAX_RETRY_ATTEMPTS 5
48 size_t g_client_channel_service_config_parser_index;
51 size_t ClientChannelServiceConfigParser::ParserIndex() {
52 return g_client_channel_service_config_parser_index;
55 void ClientChannelServiceConfigParser::Register() {
56 g_client_channel_service_config_parser_index =
57 ServiceConfig::RegisterParser(UniquePtr<ServiceConfig::Parser>(
58 New<ClientChannelServiceConfigParser>()));
63 // Parses a JSON field of the form generated for a google.proto.Duration
64 // proto message, as per:
65 // https://developers.google.com/protocol-buffers/docs/proto3#json
66 bool ParseDuration(grpc_json* field, grpc_millis* duration) {
67 if (field->type != GRPC_JSON_STRING) return false;
68 size_t len = strlen(field->value);
69 if (field->value[len - 1] != 's') return false;
70 UniquePtr<char> buf(gpr_strdup(field->value));
71 *(buf.get() + len - 1) = '\0'; // Remove trailing 's'.
72 char* decimal_point = strchr(buf.get(), '.');
74 if (decimal_point != nullptr) {
75 *decimal_point = '\0';
76 nanos = gpr_parse_nonnegative_int(decimal_point + 1);
80 int num_digits = static_cast<int>(strlen(decimal_point + 1));
81 if (num_digits > 9) { // We don't accept greater precision than nanos.
84 for (int i = 0; i < (9 - num_digits); ++i) {
89 decimal_point == buf.get() ? 0 : gpr_parse_nonnegative_int(buf.get());
90 if (seconds == -1) return false;
91 *duration = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS;
95 UniquePtr<ClientChannelMethodParsedConfig::RetryPolicy> ParseRetryPolicy(
96 grpc_json* field, grpc_error** error) {
97 GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
99 MakeUnique<ClientChannelMethodParsedConfig::RetryPolicy>();
100 if (field->type != GRPC_JSON_OBJECT) {
101 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
102 "field:retryPolicy error:should be of type object");
105 InlinedVector<grpc_error*, 4> error_list;
106 for (grpc_json* sub_field = field->child; sub_field != nullptr;
107 sub_field = sub_field->next) {
108 if (sub_field->key == nullptr) continue;
109 if (strcmp(sub_field->key, "maxAttempts") == 0) {
110 if (retry_policy->max_attempts != 0) {
111 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
112 "field:maxAttempts error:Duplicate entry"));
113 } // Duplicate. Continue Parsing
114 if (sub_field->type != GRPC_JSON_NUMBER) {
115 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
116 "field:maxAttempts error:should be of type number"));
119 retry_policy->max_attempts = gpr_parse_nonnegative_int(sub_field->value);
120 if (retry_policy->max_attempts <= 1) {
121 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
122 "field:maxAttempts error:should be at least 2"));
125 if (retry_policy->max_attempts > MAX_MAX_RETRY_ATTEMPTS) {
127 "service config: clamped retryPolicy.maxAttempts at %d",
128 MAX_MAX_RETRY_ATTEMPTS);
129 retry_policy->max_attempts = MAX_MAX_RETRY_ATTEMPTS;
131 } else if (strcmp(sub_field->key, "initialBackoff") == 0) {
132 if (retry_policy->initial_backoff > 0) {
133 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
134 "field:initialBackoff error:Duplicate entry"));
135 } // Duplicate, continue parsing.
136 if (!ParseDuration(sub_field, &retry_policy->initial_backoff)) {
137 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
138 "field:initialBackoff error:Failed to parse"));
141 if (retry_policy->initial_backoff == 0) {
142 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
143 "field:initialBackoff error:must be greater than 0"));
145 } else if (strcmp(sub_field->key, "maxBackoff") == 0) {
146 if (retry_policy->max_backoff > 0) {
147 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
148 "field:maxBackoff error:Duplicate entry"));
149 } // Duplicate, continue parsing.
150 if (!ParseDuration(sub_field, &retry_policy->max_backoff)) {
151 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
152 "field:maxBackoff error:failed to parse"));
155 if (retry_policy->max_backoff == 0) {
156 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
157 "field:maxBackoff error:should be greater than 0"));
159 } else if (strcmp(sub_field->key, "backoffMultiplier") == 0) {
160 if (retry_policy->backoff_multiplier != 0) {
161 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
162 "field:backoffMultiplier error:Duplicate entry"));
163 } // Duplicate, continue parsing.
164 if (sub_field->type != GRPC_JSON_NUMBER) {
165 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
166 "field:backoffMultiplier error:should be of type number"));
169 if (sscanf(sub_field->value, "%f", &retry_policy->backoff_multiplier) !=
171 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
172 "field:backoffMultiplier error:failed to parse"));
175 if (retry_policy->backoff_multiplier <= 0) {
176 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
177 "field:backoffMultiplier error:should be greater than 0"));
179 } else if (strcmp(sub_field->key, "retryableStatusCodes") == 0) {
180 if (!retry_policy->retryable_status_codes.Empty()) {
181 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
182 "field:retryableStatusCodes error:Duplicate entry"));
183 } // Duplicate, continue parsing.
184 if (sub_field->type != GRPC_JSON_ARRAY) {
185 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
186 "field:retryableStatusCodes error:should be of type array"));
189 for (grpc_json* element = sub_field->child; element != nullptr;
190 element = element->next) {
191 if (element->type != GRPC_JSON_STRING) {
192 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
193 "field:retryableStatusCodes error:status codes should be of type "
197 grpc_status_code status;
198 if (!grpc_status_code_from_string(element->value, &status)) {
199 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
200 "field:retryableStatusCodes error:failed to parse status code"));
203 retry_policy->retryable_status_codes.Add(status);
205 if (retry_policy->retryable_status_codes.Empty()) {
206 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
207 "field:retryableStatusCodes error:should be non-empty"));
211 // Make sure required fields are set.
212 if (error_list.empty()) {
213 if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 ||
214 retry_policy->max_backoff == 0 ||
215 retry_policy->backoff_multiplier == 0 ||
216 retry_policy->retryable_status_codes.Empty()) {
217 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
218 "field:retryPolicy error:Missing required field(s)");
222 *error = GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list);
223 return *error == GRPC_ERROR_NONE ? std::move(retry_policy) : nullptr;
226 const char* ParseHealthCheckConfig(const grpc_json* field, grpc_error** error) {
227 GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
228 const char* service_name = nullptr;
229 GPR_DEBUG_ASSERT(strcmp(field->key, "healthCheckConfig") == 0);
230 if (field->type != GRPC_JSON_OBJECT) {
231 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
232 "field:healthCheckConfig error:should be of type object");
235 InlinedVector<grpc_error*, 2> error_list;
236 for (grpc_json* sub_field = field->child; sub_field != nullptr;
237 sub_field = sub_field->next) {
238 if (sub_field->key == nullptr) {
239 GPR_DEBUG_ASSERT(false);
242 if (strcmp(sub_field->key, "serviceName") == 0) {
243 if (service_name != nullptr) {
244 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
245 "field:serviceName error:Duplicate "
247 } // Duplicate. Continue parsing
248 if (sub_field->type != GRPC_JSON_STRING) {
249 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
250 "field:serviceName error:should be of type string"));
253 service_name = sub_field->value;
256 if (!error_list.empty()) {
260 GRPC_ERROR_CREATE_FROM_VECTOR("field:healthCheckConfig", &error_list);
266 UniquePtr<ServiceConfig::ParsedConfig>
267 ClientChannelServiceConfigParser::ParseGlobalParams(const grpc_json* json,
268 grpc_error** error) {
269 GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
270 InlinedVector<grpc_error*, 4> error_list;
271 RefCountedPtr<LoadBalancingPolicy::Config> parsed_lb_config;
272 UniquePtr<char> lb_policy_name;
273 Optional<ClientChannelGlobalParsedConfig::RetryThrottling> retry_throttling;
274 const char* health_check_service_name = nullptr;
275 for (grpc_json* field = json->child; field != nullptr; field = field->next) {
276 if (field->key == nullptr) {
277 continue; // Not the LB config global parameter
279 // Parsed Load balancing config
280 if (strcmp(field->key, "loadBalancingConfig") == 0) {
281 if (parsed_lb_config != nullptr) {
282 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
283 "field:loadBalancingConfig error:Duplicate entry"));
284 } // Duplicate, continue parsing.
285 grpc_error* parse_error = GRPC_ERROR_NONE;
286 parsed_lb_config = LoadBalancingPolicyRegistry::ParseLoadBalancingConfig(
287 field, &parse_error);
288 if (parsed_lb_config == nullptr) {
289 error_list.push_back(parse_error);
292 // Parse deprecated loadBalancingPolicy
293 if (strcmp(field->key, "loadBalancingPolicy") == 0) {
294 if (lb_policy_name != nullptr) {
295 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
296 "field:loadBalancingPolicy error:Duplicate entry"));
297 } // Duplicate, continue parsing.
298 if (field->type != GRPC_JSON_STRING) {
299 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
300 "field:loadBalancingPolicy error:type should be string"));
303 lb_policy_name.reset(gpr_strdup(field->value));
304 char* lb_policy = lb_policy_name.get();
305 if (lb_policy != nullptr) {
306 for (size_t i = 0; i < strlen(lb_policy); ++i) {
307 lb_policy[i] = tolower(lb_policy[i]);
310 bool requires_config = false;
311 if (!LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(
312 lb_policy, &requires_config)) {
313 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
314 "field:loadBalancingPolicy error:Unknown lb policy"));
315 } else if (requires_config) {
317 gpr_asprintf(&error_msg,
318 "field:loadBalancingPolicy error:%s requires a config. "
319 "Please use loadBalancingConfig instead.",
321 error_list.push_back(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg));
325 // Parse retry throttling
326 if (strcmp(field->key, "retryThrottling") == 0) {
327 if (retry_throttling.has_value()) {
328 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
329 "field:retryThrottling error:Duplicate entry"));
330 } // Duplicate, continue parsing.
331 if (field->type != GRPC_JSON_OBJECT) {
332 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
333 "field:retryThrottling error:Type should be object"));
336 Optional<int> max_milli_tokens;
337 Optional<int> milli_token_ratio;
338 for (grpc_json* sub_field = field->child; sub_field != nullptr;
339 sub_field = sub_field->next) {
340 if (sub_field->key == nullptr) continue;
341 if (strcmp(sub_field->key, "maxTokens") == 0) {
342 if (max_milli_tokens.has_value()) {
343 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
344 "field:retryThrottling field:maxTokens error:Duplicate "
346 } // Duplicate, continue parsing.
347 if (sub_field->type != GRPC_JSON_NUMBER) {
348 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
349 "field:retryThrottling field:maxTokens error:Type should be "
352 max_milli_tokens.set(gpr_parse_nonnegative_int(sub_field->value) *
354 if (max_milli_tokens.value() <= 0) {
355 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
356 "field:retryThrottling field:maxTokens error:should be "
357 "greater than zero"));
360 } else if (strcmp(sub_field->key, "tokenRatio") == 0) {
361 if (milli_token_ratio.has_value()) {
362 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
363 "field:retryThrottling field:tokenRatio error:Duplicate "
365 } // Duplicate, continue parsing.
366 if (sub_field->type != GRPC_JSON_NUMBER) {
367 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
368 "field:retryThrottling field:tokenRatio error:type should be "
371 // We support up to 3 decimal digits.
372 size_t whole_len = strlen(sub_field->value);
373 uint32_t multiplier = 1;
374 uint32_t decimal_value = 0;
375 const char* decimal_point = strchr(sub_field->value, '.');
376 if (decimal_point != nullptr) {
377 whole_len = static_cast<size_t>(decimal_point - sub_field->value);
379 size_t decimal_len = strlen(decimal_point + 1);
380 if (decimal_len > 3) decimal_len = 3;
381 if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len,
383 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
384 "field:retryThrottling field:tokenRatio error:Failed "
388 uint32_t decimal_multiplier = 1;
389 for (size_t i = 0; i < (3 - decimal_len); ++i) {
390 decimal_multiplier *= 10;
392 decimal_value *= decimal_multiplier;
394 uint32_t whole_value;
395 if (!gpr_parse_bytes_to_uint32(sub_field->value, whole_len,
397 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
398 "field:retryThrottling field:tokenRatio error:Failed "
402 milli_token_ratio.set(
403 static_cast<int>((whole_value * multiplier) + decimal_value));
404 if (milli_token_ratio.value() <= 0) {
405 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
406 "field:retryThrottling field:tokenRatio error:value should "
407 "be greater than 0"));
412 ClientChannelGlobalParsedConfig::RetryThrottling data;
413 if (!max_milli_tokens.has_value()) {
414 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
415 "field:retryThrottling field:maxTokens error:Not found"));
417 data.max_milli_tokens = max_milli_tokens.value();
419 if (!milli_token_ratio.has_value()) {
420 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
421 "field:retryThrottling field:tokenRatio error:Not found"));
423 data.milli_token_ratio = milli_token_ratio.value();
425 retry_throttling.set(data);
427 if (strcmp(field->key, "healthCheckConfig") == 0) {
428 if (health_check_service_name != nullptr) {
429 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
430 "field:healthCheckConfig error:Duplicate entry"));
431 } // Duplicate continue parsing
432 grpc_error* parsing_error = GRPC_ERROR_NONE;
433 health_check_service_name = ParseHealthCheckConfig(field, &parsing_error);
434 if (parsing_error != GRPC_ERROR_NONE) {
435 error_list.push_back(parsing_error);
439 *error = GRPC_ERROR_CREATE_FROM_VECTOR("Client channel global parser",
441 if (*error == GRPC_ERROR_NONE) {
442 return UniquePtr<ServiceConfig::ParsedConfig>(
443 New<ClientChannelGlobalParsedConfig>(
444 std::move(parsed_lb_config), std::move(lb_policy_name),
445 retry_throttling, health_check_service_name));
450 UniquePtr<ServiceConfig::ParsedConfig>
451 ClientChannelServiceConfigParser::ParsePerMethodParams(const grpc_json* json,
452 grpc_error** error) {
453 GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
454 InlinedVector<grpc_error*, 4> error_list;
455 Optional<bool> wait_for_ready;
456 grpc_millis timeout = 0;
457 UniquePtr<ClientChannelMethodParsedConfig::RetryPolicy> retry_policy;
458 for (grpc_json* field = json->child; field != nullptr; field = field->next) {
459 if (field->key == nullptr) continue;
460 if (strcmp(field->key, "waitForReady") == 0) {
461 if (wait_for_ready.has_value()) {
462 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
463 "field:waitForReady error:Duplicate entry"));
464 } // Duplicate, continue parsing.
465 if (field->type == GRPC_JSON_TRUE) {
466 wait_for_ready.set(true);
467 } else if (field->type == GRPC_JSON_FALSE) {
468 wait_for_ready.set(false);
470 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
471 "field:waitForReady error:Type should be true/false"));
473 } else if (strcmp(field->key, "timeout") == 0) {
475 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
476 "field:timeout error:Duplicate entry"));
477 } // Duplicate, continue parsing.
478 if (!ParseDuration(field, &timeout)) {
479 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
480 "field:timeout error:Failed parsing"));
482 } else if (strcmp(field->key, "retryPolicy") == 0) {
483 if (retry_policy != nullptr) {
484 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
485 "field:retryPolicy error:Duplicate entry"));
486 } // Duplicate, continue parsing.
487 grpc_error* error = GRPC_ERROR_NONE;
488 retry_policy = ParseRetryPolicy(field, &error);
489 if (retry_policy == nullptr) {
490 error_list.push_back(error);
494 *error = GRPC_ERROR_CREATE_FROM_VECTOR("Client channel parser", &error_list);
495 if (*error == GRPC_ERROR_NONE) {
496 return UniquePtr<ServiceConfig::ParsedConfig>(
497 New<ClientChannelMethodParsedConfig>(timeout, wait_for_ready,
498 std::move(retry_policy)));
503 } // namespace internal
504 } // namespace grpc_core