|
|
|
@ -110,8 +110,6 @@ class AresDnsResolver : public Resolver { |
|
|
|
|
UniquePtr<ServerAddressList> addresses_; |
|
|
|
|
/// currently resolving service config
|
|
|
|
|
char* service_config_json_ = nullptr; |
|
|
|
|
/// last valid service config
|
|
|
|
|
RefCountedPtr<ServiceConfig> saved_service_config_; |
|
|
|
|
// has shutdown been initiated
|
|
|
|
|
bool shutdown_initiated_ = false; |
|
|
|
|
// timeout in milliseconds for active DNS queries
|
|
|
|
@ -232,66 +230,93 @@ bool ValueInJsonArray(grpc_json* array, const char* value) { |
|
|
|
|
char* ChooseServiceConfig(char* service_config_choice_json, |
|
|
|
|
grpc_error** error) { |
|
|
|
|
grpc_json* choices_json = grpc_json_parse_string(service_config_choice_json); |
|
|
|
|
if (choices_json == nullptr || choices_json->type != GRPC_JSON_ARRAY) { |
|
|
|
|
if (choices_json == nullptr) { |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"cannot parse service config JSON string"); |
|
|
|
|
"Service Config JSON Parsing, error: could not parse"); |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
if (choices_json->type != GRPC_JSON_ARRAY) { |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"Service Config Choices, error: should be of type array"); |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
char* service_config = nullptr; |
|
|
|
|
InlinedVector<grpc_error*, 4> error_list; |
|
|
|
|
bool found_choice = false; // have we found a choice?
|
|
|
|
|
for (grpc_json* choice = choices_json->child; choice != nullptr; |
|
|
|
|
choice = choice->next) { |
|
|
|
|
if (choice->type != GRPC_JSON_OBJECT) { |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"cannot parse service config JSON string"); |
|
|
|
|
break; |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"Service Config Choice, error: should be of type object")); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
grpc_json* service_config_json = nullptr; |
|
|
|
|
bool not_choice = false; // has this choice been rejected?
|
|
|
|
|
for (grpc_json* field = choice->child; field != nullptr; |
|
|
|
|
field = field->next) { |
|
|
|
|
// Check client language, if specified.
|
|
|
|
|
if (strcmp(field->key, "clientLanguage") == 0) { |
|
|
|
|
if (field->type != GRPC_JSON_ARRAY || !ValueInJsonArray(field, "c++")) { |
|
|
|
|
service_config_json = nullptr; |
|
|
|
|
break; |
|
|
|
|
if (field->type != GRPC_JSON_ARRAY) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:clientLanguage error:should be of type array")); |
|
|
|
|
} else if (!ValueInJsonArray(field, "c++")) { |
|
|
|
|
not_choice = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Check client hostname, if specified.
|
|
|
|
|
if (strcmp(field->key, "clientHostname") == 0) { |
|
|
|
|
if (field->type != GRPC_JSON_ARRAY) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:clientHostname error:should be of type array")); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
char* hostname = grpc_gethostname(); |
|
|
|
|
if (hostname == nullptr || field->type != GRPC_JSON_ARRAY || |
|
|
|
|
!ValueInJsonArray(field, hostname)) { |
|
|
|
|
service_config_json = nullptr; |
|
|
|
|
break; |
|
|
|
|
if (hostname == nullptr || !ValueInJsonArray(field, hostname)) { |
|
|
|
|
not_choice = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Check percentage, if specified.
|
|
|
|
|
if (strcmp(field->key, "percentage") == 0) { |
|
|
|
|
if (field->type != GRPC_JSON_NUMBER) { |
|
|
|
|
service_config_json = nullptr; |
|
|
|
|
break; |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:percentage error:should be of type number")); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
int random_pct = rand() % 100; |
|
|
|
|
int percentage; |
|
|
|
|
if (sscanf(field->value, "%d", &percentage) != 1 || |
|
|
|
|
random_pct > percentage || percentage == 0) { |
|
|
|
|
service_config_json = nullptr; |
|
|
|
|
break; |
|
|
|
|
if (sscanf(field->value, "%d", &percentage) != 1) { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:percentage error:should be of type integer")); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
if (random_pct > percentage || percentage == 0) { |
|
|
|
|
not_choice = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Save service config.
|
|
|
|
|
if (strcmp(field->key, "serviceConfig") == 0) { |
|
|
|
|
if (field->type == GRPC_JSON_OBJECT) { |
|
|
|
|
service_config_json = field; |
|
|
|
|
} else { |
|
|
|
|
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( |
|
|
|
|
"field:serviceConfig error:should be of type object")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (service_config_json != nullptr) { |
|
|
|
|
if (!found_choice && !not_choice && service_config_json != nullptr) { |
|
|
|
|
service_config = grpc_json_dump_to_string(service_config_json, 0); |
|
|
|
|
break; |
|
|
|
|
found_choice = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
grpc_json_destroy(choices_json); |
|
|
|
|
return service_config; |
|
|
|
|
if (error_list.empty()) { |
|
|
|
|
return service_config; |
|
|
|
|
} else { |
|
|
|
|
gpr_free(service_config); |
|
|
|
|
*error = GRPC_ERROR_CREATE_FROM_VECTOR("Service Config Choices Parser", |
|
|
|
|
&error_list); |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) { |
|
|
|
@ -308,38 +333,18 @@ void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) { |
|
|
|
|
Result result; |
|
|
|
|
result.addresses = std::move(*r->addresses_); |
|
|
|
|
if (r->service_config_json_ != nullptr) { |
|
|
|
|
grpc_error* service_config_error = GRPC_ERROR_NONE; |
|
|
|
|
char* service_config_string = |
|
|
|
|
ChooseServiceConfig(r->service_config_json_, &service_config_error); |
|
|
|
|
char* service_config_string = ChooseServiceConfig( |
|
|
|
|
r->service_config_json_, &result.service_config_error); |
|
|
|
|
gpr_free(r->service_config_json_); |
|
|
|
|
RefCountedPtr<ServiceConfig> new_service_config; |
|
|
|
|
if (service_config_error == GRPC_ERROR_NONE && |
|
|
|
|
if (result.service_config_error == GRPC_ERROR_NONE && |
|
|
|
|
service_config_string != nullptr) { |
|
|
|
|
GRPC_CARES_TRACE_LOG("resolver:%p selected service config choice: %s", |
|
|
|
|
r, service_config_string); |
|
|
|
|
new_service_config = |
|
|
|
|
ServiceConfig::Create(service_config_string, &service_config_error); |
|
|
|
|
result.service_config = ServiceConfig::Create( |
|
|
|
|
service_config_string, &result.service_config_error); |
|
|
|
|
} |
|
|
|
|
gpr_free(service_config_string); |
|
|
|
|
if (service_config_error == GRPC_ERROR_NONE) { |
|
|
|
|
// Valid service config receivd
|
|
|
|
|
r->saved_service_config_ = std::move(new_service_config); |
|
|
|
|
} else { |
|
|
|
|
if (r->saved_service_config_ != nullptr) { |
|
|
|
|
// Ignore the new service config error, since we have a previously
|
|
|
|
|
// saved service config
|
|
|
|
|
GRPC_ERROR_UNREF(service_config_error); |
|
|
|
|
} else { |
|
|
|
|
// No previously valid service config found.
|
|
|
|
|
// service_config_error is passed to the channel.
|
|
|
|
|
result.service_config_error = service_config_error; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// No service config received
|
|
|
|
|
r->saved_service_config_.reset(); |
|
|
|
|
} |
|
|
|
|
result.service_config = r->saved_service_config_; |
|
|
|
|
result.args = grpc_channel_args_copy(r->channel_args_); |
|
|
|
|
r->result_handler()->ReturnResult(std::move(result)); |
|
|
|
|
r->addresses_.reset(); |
|
|
|
|