diff options
author | Muxi Yan <mxyan@google.com> | 2017-08-11 16:15:53 -0700 |
---|---|---|
committer | Muxi Yan <mxyan@google.com> | 2017-08-11 16:15:53 -0700 |
commit | 229125abb1510dea47e8608779dd93d968ce3fd9 (patch) | |
tree | 1fb2ba60bccb9795cc5659672c3d9cf71d520344 /src/core/ext/filters | |
parent | d36283ab594c4cf8fdffac700efeffa2705e6dc0 (diff) | |
parent | 71cc205aadd7ea2a48298a8e67d2c0d2858841f6 (diff) |
Merge remote-tracking branch 'upstream/master' into stream_compression_config
Diffstat (limited to 'src/core/ext/filters')
4 files changed, 217 insertions, 28 deletions
diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c index 04a7852323..f1480bb1ae 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c @@ -19,7 +19,10 @@ #include <grpc/support/port_platform.h> #if GRPC_ARES == 1 && !defined(GRPC_UV) +#include <limits.h> +#include <stdio.h> #include <string.h> +#include <unistd.h> #include <grpc/support/alloc.h> #include <grpc/support/host_port.h> @@ -31,11 +34,14 @@ #include "src/core/ext/filters/client_channel/resolver_registry.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/gethostname.h" #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/json/json.h" #include "src/core/lib/support/backoff.h" #include "src/core/lib/support/env.h" #include "src/core/lib/support/string.h" +#include "src/core/lib/transport/service_config.h" #define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 #define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 @@ -54,6 +60,8 @@ typedef struct { char *default_port; /** channel args. */ grpc_channel_args *channel_args; + /** whether to request the service config */ + bool request_service_config; /** pollset_set to drive the name resolution process */ grpc_pollset_set *interested_parties; @@ -85,6 +93,8 @@ typedef struct { /** currently resolving addresses */ grpc_lb_addresses *lb_addresses; + /** currently resolving service config */ + char *service_config_json; } ares_dns_resolver; static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); @@ -144,6 +154,77 @@ static void dns_ares_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer"); } +static bool value_in_json_array(grpc_json *array, const char *value) { + for (grpc_json *entry = array->child; entry != NULL; entry = entry->next) { + if (entry->type == GRPC_JSON_STRING && strcmp(entry->value, value) == 0) { + return true; + } + } + return false; +} + +static char *choose_service_config(char *service_config_choice_json) { + grpc_json *choices_json = grpc_json_parse_string(service_config_choice_json); + if (choices_json == NULL || choices_json->type != GRPC_JSON_ARRAY) { + gpr_log(GPR_ERROR, "cannot parse service config JSON string"); + return NULL; + } + char *service_config = NULL; + for (grpc_json *choice = choices_json->child; choice != NULL; + choice = choice->next) { + if (choice->type != GRPC_JSON_OBJECT) { + gpr_log(GPR_ERROR, "cannot parse service config JSON string"); + break; + } + grpc_json *service_config_json = NULL; + for (grpc_json *field = choice->child; field != NULL; field = field->next) { + // Check client language, if specified. + if (strcmp(field->key, "clientLanguage") == 0) { + if (field->type != GRPC_JSON_ARRAY || + !value_in_json_array(field, "c++")) { + service_config_json = NULL; + break; + } + } + // Check client hostname, if specified. + if (strcmp(field->key, "clientHostname") == 0) { + char *hostname = grpc_gethostname(); + if (hostname == NULL || field->type != GRPC_JSON_ARRAY || + !value_in_json_array(field, hostname)) { + service_config_json = NULL; + break; + } + } + // Check percentage, if specified. + if (strcmp(field->key, "percentage") == 0) { + if (field->type != GRPC_JSON_NUMBER) { + service_config_json = NULL; + break; + } + int random_pct = rand() % 100; + int percentage; + if (sscanf(field->value, "%d", &percentage) != 1 || + random_pct > percentage) { + service_config_json = NULL; + break; + } + } + // Save service config. + if (strcmp(field->key, "serviceConfig") == 0) { + if (field->type == GRPC_JSON_OBJECT) { + service_config_json = field; + } + } + } + if (service_config_json != NULL) { + service_config = grpc_json_dump_to_string(service_config_json, 0); + break; + } + } + grpc_json_destroy(choices_json); + return service_config; +} + static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { ares_dns_resolver *r = arg; @@ -152,8 +233,40 @@ static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg, r->resolving = false; r->pending_request = NULL; if (r->lb_addresses != NULL) { - grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(r->lb_addresses); - result = grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1); + static const char *args_to_remove[2]; + size_t num_args_to_remove = 0; + grpc_arg new_args[3]; + size_t num_args_to_add = 0; + new_args[num_args_to_add++] = + grpc_lb_addresses_create_channel_arg(r->lb_addresses); + grpc_service_config *service_config = NULL; + char *service_config_string = NULL; + if (r->service_config_json != NULL) { + service_config_string = choose_service_config(r->service_config_json); + gpr_free(r->service_config_json); + if (service_config_string != NULL) { + gpr_log(GPR_INFO, "selected service config choice: %s", + service_config_string); + args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG; + new_args[num_args_to_add++] = grpc_channel_arg_string_create( + GRPC_ARG_SERVICE_CONFIG, service_config_string); + service_config = grpc_service_config_create(service_config_string); + if (service_config != NULL) { + const char *lb_policy_name = + grpc_service_config_get_lb_policy_name(service_config); + if (lb_policy_name != NULL) { + args_to_remove[num_args_to_remove++] = GRPC_ARG_LB_POLICY_NAME; + new_args[num_args_to_add++] = grpc_channel_arg_string_create( + GRPC_ARG_LB_POLICY_NAME, (char *)lb_policy_name); + } + } + } + } + result = grpc_channel_args_copy_and_add_and_remove( + r->channel_args, args_to_remove, num_args_to_remove, new_args, + num_args_to_add); + if (service_config != NULL) grpc_service_config_destroy(service_config); + gpr_free(service_config_string); grpc_lb_addresses_destroy(exec_ctx, r->lb_addresses); } else { const char *msg = grpc_error_string(error); @@ -207,10 +320,12 @@ static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx, GPR_ASSERT(!r->resolving); r->resolving = true; r->lb_addresses = NULL; + r->service_config_json = NULL; r->pending_request = grpc_dns_lookup_ares( exec_ctx, r->dns_server, r->name_to_resolve, r->default_port, r->interested_parties, &r->dns_ares_on_resolved_locked, &r->lb_addresses, - true /* check_grpclb */); + true /* check_grpclb */, + r->request_service_config ? &r->service_config_json : NULL); } static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, @@ -256,6 +371,10 @@ static grpc_resolver *dns_ares_create(grpc_exec_ctx *exec_ctx, r->name_to_resolve = gpr_strdup(path); r->default_port = gpr_strdup(default_port); r->channel_args = grpc_channel_args_copy(args->args); + const grpc_arg *arg = grpc_channel_args_find( + r->channel_args, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION); + r->request_service_config = !grpc_channel_arg_get_integer( + arg, (grpc_integer_options){false, false, true}); r->interested_parties = grpc_pollset_set_create(); if (args->pollset_set != NULL) { grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties, diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c index 6ec3790a5f..e65723a63b 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c @@ -54,6 +54,8 @@ struct grpc_ares_request { grpc_closure *on_done; /** the pointer to receive the resolved addresses */ grpc_lb_addresses **lb_addrs_out; + /** the pointer to receive the service config in JSON */ + char **service_config_json_out; /** the evernt driver used by this request */ grpc_ares_ev_driver *ev_driver; /** number of ongoing queries */ @@ -266,10 +268,68 @@ static void on_srv_query_done_cb(void *arg, int status, int timeouts, grpc_exec_ctx_finish(&exec_ctx); } +static const char g_service_config_attribute_prefix[] = "grpc_config="; + +static void on_txt_done_cb(void *arg, int status, int timeouts, + unsigned char *buf, int len) { + gpr_log(GPR_DEBUG, "on_txt_done_cb"); + char *error_msg; + grpc_ares_request *r = (grpc_ares_request *)arg; + gpr_mu_lock(&r->mu); + if (status != ARES_SUCCESS) goto fail; + struct ares_txt_ext *reply = NULL; + status = ares_parse_txt_reply_ext(buf, len, &reply); + if (status != ARES_SUCCESS) goto fail; + // Find service config in TXT record. + const size_t prefix_len = sizeof(g_service_config_attribute_prefix) - 1; + struct ares_txt_ext *result; + for (result = reply; result != NULL; result = result->next) { + if (result->record_start && + memcmp(result->txt, g_service_config_attribute_prefix, prefix_len) == + 0) { + break; + } + } + // Found a service config record. + if (result != NULL) { + size_t service_config_len = result->length - prefix_len; + *r->service_config_json_out = gpr_malloc(service_config_len + 1); + memcpy(*r->service_config_json_out, result->txt + prefix_len, + service_config_len); + for (result = result->next; result != NULL && !result->record_start; + result = result->next) { + *r->service_config_json_out = gpr_realloc( + *r->service_config_json_out, service_config_len + result->length + 1); + memcpy(*r->service_config_json_out + service_config_len, result->txt, + result->length); + service_config_len += result->length; + } + (*r->service_config_json_out)[service_config_len] = '\0'; + gpr_log(GPR_INFO, "found service config: %s", *r->service_config_json_out); + } + // Clean up. + ares_free_data(reply); + goto done; +fail: + gpr_asprintf(&error_msg, "C-ares TXT lookup status is not ARES_SUCCESS: %s", + ares_strerror(status)); + grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + if (r->error == GRPC_ERROR_NONE) { + r->error = error; + } else { + r->error = grpc_error_add_child(error, r->error); + } +done: + gpr_mu_unlock(&r->mu); + grpc_ares_request_unref(NULL, r); +} + static grpc_ares_request *grpc_dns_lookup_ares_impl( grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, const char *default_port, grpc_pollset_set *interested_parties, - grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb) { + grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, + char **service_config_json) { grpc_error *error = GRPC_ERROR_NONE; /* TODO(zyc): Enable tracing after #9603 is checked in */ /* if (grpc_dns_trace) { @@ -300,11 +360,12 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl( error = grpc_ares_ev_driver_create(&ev_driver, interested_parties); if (error != GRPC_ERROR_NONE) goto error_cleanup; - grpc_ares_request *r = gpr_malloc(sizeof(grpc_ares_request)); + grpc_ares_request *r = gpr_zalloc(sizeof(grpc_ares_request)); gpr_mu_init(&r->mu); r->ev_driver = ev_driver; r->on_done = on_done; r->lb_addrs_out = addrs; + r->service_config_json_out = service_config_json; r->success = false; r->error = GRPC_ERROR_NONE; ares_channel *channel = grpc_ares_ev_driver_get_channel(r->ev_driver); @@ -315,13 +376,17 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl( grpc_resolved_address addr; if (grpc_parse_ipv4_hostport(dns_server, &addr, false /* log_errors */)) { r->dns_server_addr.family = AF_INET; - memcpy(&r->dns_server_addr.addr.addr4, addr.addr, addr.len); + struct sockaddr_in *in = (struct sockaddr_in *)addr.addr; + memcpy(&r->dns_server_addr.addr.addr4, &in->sin_addr, + sizeof(struct in_addr)); r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); } else if (grpc_parse_ipv6_hostport(dns_server, &addr, false /* log_errors */)) { r->dns_server_addr.family = AF_INET6; - memcpy(&r->dns_server_addr.addr.addr6, addr.addr, addr.len); + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr.addr; + memcpy(&r->dns_server_addr.addr.addr6, &in6->sin6_addr, + sizeof(struct in6_addr)); r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); } else { @@ -342,8 +407,6 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl( goto error_cleanup; } } - // An extra reference is put here to avoid destroying the request in - // on_done_cb before calling grpc_ares_ev_driver_start. gpr_ref_init(&r->pending_queries, 1); if (grpc_ipv6_loopback_available()) { grpc_ares_hostbyname_request *hr = create_hostbyname_request( @@ -362,6 +425,10 @@ static grpc_ares_request *grpc_dns_lookup_ares_impl( r); gpr_free(service_name); } + if (service_config_json != NULL) { + grpc_ares_request_ref(r); + ares_search(*channel, hr->host, ns_c_in, ns_t_txt, on_txt_done_cb, r); + } /* TODO(zyc): Handle CNAME records here. */ grpc_ares_ev_driver_start(exec_ctx, r->ev_driver); grpc_ares_request_unref(exec_ctx, r); @@ -379,8 +446,8 @@ error_cleanup: grpc_ares_request *(*grpc_dns_lookup_ares)( grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, const char *default_port, grpc_pollset_set *interested_parties, - grpc_closure *on_done, grpc_lb_addresses **addrs, - bool check_grpclb) = grpc_dns_lookup_ares_impl; + grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, + char **service_config_json) = grpc_dns_lookup_ares_impl; void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) { if (grpc_dns_lookup_ares == grpc_dns_lookup_ares_impl) { @@ -465,7 +532,8 @@ static void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, grpc_schedule_on_exec_ctx); grpc_dns_lookup_ares(exec_ctx, NULL /* dns_server */, name, default_port, interested_parties, &r->on_dns_lookup_done, &r->lb_addrs, - false /* check_grpclb */); + false /* check_grpclb */, + NULL /* service_config_json */); } void (*grpc_resolve_address_ares)( diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h index 5d2d6c993b..108333047d 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h @@ -27,29 +27,30 @@ typedef struct grpc_ares_request grpc_ares_request; -/* Asynchronously resolve addr. Use \a default_port if a port isn't designated - in addr, otherwise use the port in addr. grpc_ares_init() must be called at - least once before this function. \a on_done may be called directly in this - function without being scheduled with \a exec_ctx, it must not try to acquire - locks that are being held by the caller. */ +/* Asynchronously resolve \a name. Use \a default_port if a port isn't + designated in \a name, otherwise use the port in \a name. grpc_ares_init() + must be called at least once before this function. \a on_done may be + called directly in this function without being scheduled with \a exec_ctx, + so it must not try to acquire locks that are being held by the caller. */ extern void (*grpc_resolve_address_ares)(grpc_exec_ctx *exec_ctx, - const char *addr, + const char *name, const char *default_port, grpc_pollset_set *interested_parties, grpc_closure *on_done, grpc_resolved_addresses **addresses); -/* Asynchronously resolve addr. It will try to resolve grpclb SRV records in +/* Asynchronously resolve \a name. It will try to resolve grpclb SRV records in addition to the normal address records. For normal address records, it uses - \a default_port if a port isn't designated in \a addr, otherwise it uses the - port in \a addr. grpc_ares_init() must be called at least once before this + \a default_port if a port isn't designated in \a name, otherwise it uses the + port in \a name. grpc_ares_init() must be called at least once before this function. \a on_done may be called directly in this function without being - scheduled with \a exec_ctx, it must not try to acquire locks that are being - held by the caller. */ + scheduled with \a exec_ctx, so it must not try to acquire locks that are + being held by the caller. */ extern grpc_ares_request *(*grpc_dns_lookup_ares)( - grpc_exec_ctx *exec_ctx, const char *dns_server, const char *addr, + grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, const char *default_port, grpc_pollset_set *interested_parties, - grpc_closure *on_done, grpc_lb_addresses **addresses, bool check_grpclb); + grpc_closure *on_done, grpc_lb_addresses **addresses, bool check_grpclb, + char **service_config_json); /* Cancel the pending grpc_ares_request \a request */ void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c index b67636a3e4..f2587c4520 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c @@ -28,15 +28,16 @@ struct grpc_ares_request { static grpc_ares_request *grpc_dns_lookup_ares_impl( grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, const char *default_port, grpc_pollset_set *interested_parties, - grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb) { + grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, + char **service_config_json) { return NULL; } grpc_ares_request *(*grpc_dns_lookup_ares)( grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, const char *default_port, grpc_pollset_set *interested_parties, - grpc_closure *on_done, grpc_lb_addresses **addrs, - bool check_grpclb) = grpc_dns_lookup_ares_impl; + grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, + char **service_config_json) = grpc_dns_lookup_ares_impl; void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) {} |