diff options
Diffstat (limited to 'src/core/ext')
-rw-r--r-- | src/core/ext/census/gen/README.md | 2 | ||||
-rw-r--r-- | src/core/ext/client_channel/resolver_registry.c | 1 | ||||
-rw-r--r-- | src/core/ext/resolver/dns/c_ares/dns_resolver_ares.c | 350 | ||||
-rw-r--r-- | src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver.h | 65 | ||||
-rw-r--r-- | src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c | 319 | ||||
-rw-r--r-- | src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.c | 289 | ||||
-rw-r--r-- | src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.h | 63 | ||||
-rw-r--r-- | src/core/ext/resolver/dns/native/dns_resolver.c | 17 | ||||
-rw-r--r-- | src/core/ext/transport/chttp2/transport/chttp2_transport.c | 83 | ||||
-rw-r--r-- | src/core/ext/transport/chttp2/transport/frame_ping.c | 20 | ||||
-rw-r--r-- | src/core/ext/transport/chttp2/transport/frame_ping.h | 3 | ||||
-rw-r--r-- | src/core/ext/transport/chttp2/transport/internal.h | 7 | ||||
-rw-r--r-- | src/core/ext/transport/chttp2/transport/writing.c | 8 |
13 files changed, 1201 insertions, 26 deletions
diff --git a/src/core/ext/census/gen/README.md b/src/core/ext/census/gen/README.md index fdbac1084c..d4612bc7c8 100644 --- a/src/core/ext/census/gen/README.md +++ b/src/core/ext/census/gen/README.md @@ -1,6 +1,6 @@ Files generated for use by Census stats and trace recording subsystem. -#Files +# Files * census.pb.{h,c} - Generated from src/core/ext/census/census.proto, using the script `tools/codegen/core/gen_nano_proto.sh src/proto/census/census.proto $PWD/src/core/ext/census/gen src/core/ext/census/gen` diff --git a/src/core/ext/client_channel/resolver_registry.c b/src/core/ext/client_channel/resolver_registry.c index 3c5a6fb3ff..0f074a3386 100644 --- a/src/core/ext/client_channel/resolver_registry.c +++ b/src/core/ext/client_channel/resolver_registry.c @@ -93,7 +93,6 @@ static grpc_resolver_factory *lookup_factory(const char *name) { return g_all_of_the_resolvers[i]; } } - return NULL; } diff --git a/src/core/ext/resolver/dns/c_ares/dns_resolver_ares.c b/src/core/ext/resolver/dns/c_ares/dns_resolver_ares.c new file mode 100644 index 0000000000..f27da231f5 --- /dev/null +++ b/src/core/ext/resolver/dns/c_ares/dns_resolver_ares.c @@ -0,0 +1,350 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <grpc/support/port_platform.h> +#if GRPC_ARES == 1 && !defined(GRPC_UV) + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/host_port.h> +#include <grpc/support/string_util.h> + +#include "src/core/ext/client_channel/http_connect_handshaker.h" +#include "src/core/ext/client_channel/lb_policy_registry.h" +#include "src/core/ext/client_channel/resolver_registry.h" +#include "src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/support/backoff.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" + +#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 +#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_DNS_RECONNECT_JITTER 0.2 + +typedef struct { + /** base class: must be first */ + grpc_resolver base; + /** name to resolve (usually the same as target_name) */ + char *name_to_resolve; + /** default port to use */ + char *default_port; + /** channel args. */ + grpc_channel_args *channel_args; + /** pollset_set to drive the name resolution process */ + grpc_pollset_set *interested_parties; + + /** Closures used by the combiner */ + grpc_closure dns_ares_on_retry_timer_locked; + grpc_closure dns_ares_on_resolved_locked; + + /** Combiner guarding the rest of the state */ + grpc_combiner *combiner; + /** are we currently resolving? */ + bool resolving; + /** which version of the result have we published? */ + int published_version; + /** which version of the result is current? */ + int resolved_version; + /** pending next completion, or NULL */ + grpc_closure *next_completion; + /** target result address for next completion */ + grpc_channel_args **target_result; + /** current (fully resolved) result */ + grpc_channel_args *resolved_result; + /** retry timer */ + bool have_retry_timer; + grpc_timer retry_timer; + /** retry backoff state */ + gpr_backoff backoff_state; + + /** currently resolving addresses */ + grpc_resolved_addresses *addresses; +} ares_dns_resolver; + +static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); + +static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx, + ares_dns_resolver *r); +static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + ares_dns_resolver *r); + +static void dns_ares_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r); +static void dns_ares_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *r); +static void dns_ares_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r, + grpc_channel_args **target_result, + grpc_closure *on_complete); + +static const grpc_resolver_vtable dns_ares_resolver_vtable = { + dns_ares_destroy, dns_ares_shutdown_locked, + dns_ares_channel_saw_error_locked, dns_ares_next_locked}; + +static void dns_ares_shutdown_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + ares_dns_resolver *r = (ares_dns_resolver *)resolver; + if (r->have_retry_timer) { + grpc_timer_cancel(exec_ctx, &r->retry_timer); + } + if (r->next_completion != NULL) { + *r->target_result = NULL; + grpc_closure_sched( + exec_ctx, r->next_completion, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver Shutdown")); + r->next_completion = NULL; + } +} + +static void dns_ares_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + ares_dns_resolver *r = (ares_dns_resolver *)resolver; + if (!r->resolving) { + gpr_backoff_reset(&r->backoff_state); + dns_ares_start_resolving_locked(exec_ctx, r); + } +} + +static void dns_ares_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + ares_dns_resolver *r = arg; + r->have_retry_timer = false; + if (error == GRPC_ERROR_NONE) { + if (!r->resolving) { + dns_ares_start_resolving_locked(exec_ctx, r); + } + } + GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer"); +} + +static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + ares_dns_resolver *r = arg; + grpc_channel_args *result = NULL; + GPR_ASSERT(r->resolving); + r->resolving = false; + if (r->addresses != NULL) { + grpc_lb_addresses *addresses = grpc_lb_addresses_create( + r->addresses->naddrs, NULL /* user_data_vtable */); + for (size_t i = 0; i < r->addresses->naddrs; ++i) { + grpc_lb_addresses_set_address( + addresses, i, &r->addresses->addrs[i].addr, + r->addresses->addrs[i].len, false /* is_balancer */, + NULL /* balancer_name */, NULL /* user_data */); + } + grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(addresses); + result = grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1); + grpc_resolved_addresses_destroy(r->addresses); + grpc_lb_addresses_destroy(exec_ctx, addresses); + } else { + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); + gpr_timespec timeout = gpr_time_sub(next_try, now); + gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", + grpc_error_string(error)); + GPR_ASSERT(!r->have_retry_timer); + r->have_retry_timer = true; + GRPC_RESOLVER_REF(&r->base, "retry-timer"); + if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) { + gpr_log(GPR_DEBUG, "retrying in %" PRId64 ".%09d seconds", timeout.tv_sec, + timeout.tv_nsec); + } else { + gpr_log(GPR_DEBUG, "retrying immediately"); + } + grpc_timer_init(exec_ctx, &r->retry_timer, next_try, + &r->dns_ares_on_retry_timer_locked, now); + } + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(exec_ctx, r->resolved_result); + } + r->resolved_result = result; + r->resolved_version++; + dns_ares_maybe_finish_next_locked(exec_ctx, r); + GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "dns-resolving"); +} + +static void dns_ares_next_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver, + grpc_channel_args **target_result, + grpc_closure *on_complete) { + gpr_log(GPR_DEBUG, "dns_ares_next is called."); + ares_dns_resolver *r = (ares_dns_resolver *)resolver; + GPR_ASSERT(!r->next_completion); + r->next_completion = on_complete; + r->target_result = target_result; + if (r->resolved_version == 0 && !r->resolving) { + gpr_backoff_reset(&r->backoff_state); + dns_ares_start_resolving_locked(exec_ctx, r); + } else { + dns_ares_maybe_finish_next_locked(exec_ctx, r); + } +} + +static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx, + ares_dns_resolver *r) { + GRPC_RESOLVER_REF(&r->base, "dns-resolving"); + GPR_ASSERT(!r->resolving); + r->resolving = true; + r->addresses = NULL; + grpc_resolve_address(exec_ctx, r->name_to_resolve, r->default_port, + r->interested_parties, &r->dns_ares_on_resolved_locked, + &r->addresses); +} + +static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + ares_dns_resolver *r) { + if (r->next_completion != NULL && + r->resolved_version != r->published_version) { + *r->target_result = r->resolved_result == NULL + ? NULL + : grpc_channel_args_copy(r->resolved_result); + grpc_closure_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE); + r->next_completion = NULL; + r->published_version = r->resolved_version; + } +} + +static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { + gpr_log(GPR_DEBUG, "dns_ares_destroy"); + ares_dns_resolver *r = (ares_dns_resolver *)gr; + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(exec_ctx, r->resolved_result); + } + grpc_pollset_set_destroy(exec_ctx, r->interested_parties); + gpr_free(r->name_to_resolve); + gpr_free(r->default_port); + grpc_channel_args_destroy(exec_ctx, r->channel_args); + gpr_free(r); +} + +static grpc_resolver *dns_ares_create(grpc_exec_ctx *exec_ctx, + grpc_resolver_args *args, + const char *default_port) { + // Get name from args. + const char *path = args->uri->path; + if (0 != strcmp(args->uri->authority, "")) { + gpr_log(GPR_ERROR, "authority based dns uri's not supported"); + return NULL; + } + if (path[0] == '/') ++path; + // Create resolver. + ares_dns_resolver *r = gpr_zalloc(sizeof(ares_dns_resolver)); + grpc_resolver_init(&r->base, &dns_ares_resolver_vtable, args->combiner); + r->name_to_resolve = gpr_strdup(path); + r->default_port = gpr_strdup(default_port); + r->channel_args = grpc_channel_args_copy(args->args); + r->interested_parties = grpc_pollset_set_create(); + if (args->pollset_set != NULL) { + grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties, + args->pollset_set); + } + gpr_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS, + GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER, + GRPC_DNS_RECONNECT_JITTER, + GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000, + GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + grpc_closure_init(&r->dns_ares_on_retry_timer_locked, + dns_ares_on_retry_timer_locked, r, + grpc_combiner_scheduler(r->base.combiner, false)); + grpc_closure_init(&r->dns_ares_on_resolved_locked, + dns_ares_on_resolved_locked, r, + grpc_combiner_scheduler(r->base.combiner, false)); + return &r->base; +} + +/* + * FACTORY + */ + +static void dns_ares_factory_ref(grpc_resolver_factory *factory) {} + +static void dns_ares_factory_unref(grpc_resolver_factory *factory) {} + +static grpc_resolver *dns_factory_create_resolver( + grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory, + grpc_resolver_args *args) { + return dns_ares_create(exec_ctx, args, "https"); +} + +static char *dns_ares_factory_get_default_host_name( + grpc_resolver_factory *factory, grpc_uri *uri) { + const char *path = uri->path; + if (path[0] == '/') ++path; + return gpr_strdup(path); +} + +static const grpc_resolver_factory_vtable dns_ares_factory_vtable = { + dns_ares_factory_ref, dns_ares_factory_unref, dns_factory_create_resolver, + dns_ares_factory_get_default_host_name, "dns"}; +static grpc_resolver_factory dns_resolver_factory = {&dns_ares_factory_vtable}; + +static grpc_resolver_factory *dns_ares_resolver_factory_create() { + return &dns_resolver_factory; +} + +void grpc_resolver_dns_ares_init(void) { + char *resolver = gpr_getenv("GRPC_DNS_RESOLVER"); + /* TODO(zyc): Turn on c-ares based resolver by default after the address + sorter and the CNAME support are added. */ + if (resolver != NULL && gpr_stricmp(resolver, "ares") == 0) { + grpc_error *error = grpc_ares_init(); + if (error != GRPC_ERROR_NONE) { + GRPC_LOG_IF_ERROR("ares_library_init() failed", error); + return; + } + grpc_resolve_address = grpc_resolve_address_ares; + grpc_register_resolver_type(dns_ares_resolver_factory_create()); + } + gpr_free(resolver); +} + +void grpc_resolver_dns_ares_shutdown(void) { + char *resolver = gpr_getenv("GRPC_DNS_RESOLVER"); + if (resolver != NULL && gpr_stricmp(resolver, "ares") == 0) { + grpc_ares_cleanup(); + } + gpr_free(resolver); +} + +#else /* GRPC_ARES == 1 && !defined(GRPC_UV) */ + +void grpc_resolver_dns_ares_init(void) {} + +void grpc_resolver_dns_ares_shutdown(void) {} + +#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */ diff --git a/src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver.h b/src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver.h new file mode 100644 index 0000000000..334feaa2ab --- /dev/null +++ b/src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_CORE_EXT_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H +#define GRPC_CORE_EXT_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H + +#include <ares.h> + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/pollset_set.h" + +typedef struct grpc_ares_ev_driver grpc_ares_ev_driver; + +/* Start \a ev_driver. It will keep working until all IO on its ares_channel is + done, or grpc_ares_ev_driver_destroy() is called. It may notify the callbacks + bound to its ares_channel when necessary. */ +void grpc_ares_ev_driver_start(grpc_exec_ctx *exec_ctx, + grpc_ares_ev_driver *ev_driver); + +/* Returns the ares_channel owned by \a ev_driver. To bind a c-ares query to + \a ev_driver, use the ares_channel owned by \a ev_driver as the arg of the + query. */ +ares_channel *grpc_ares_ev_driver_get_channel(grpc_ares_ev_driver *ev_driver); + +/* Creates a new grpc_ares_ev_driver. Returns GRPC_ERROR_NONE if \a ev_driver is + created successfully. */ +grpc_error *grpc_ares_ev_driver_create(grpc_ares_ev_driver **ev_driver, + grpc_pollset_set *pollset_set); + +/* Destroys \a ev_driver asynchronously. Pending lookups made on \a ev_driver + will be cancelled and their on_done callbacks will be invoked with a status + of ARES_ECANCELLED. */ +void grpc_ares_ev_driver_destroy(grpc_ares_ev_driver *ev_driver); + +#endif /* GRPC_CORE_EXT_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H */ diff --git a/src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c b/src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c new file mode 100644 index 0000000000..fab4f0c977 --- /dev/null +++ b/src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c @@ -0,0 +1,319 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" +#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET) + +#include "src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver.h" + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> +#include <grpc/support/time.h> +#include <grpc/support/useful.h> +#include "src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/support/string.h" + +typedef struct fd_node { + /** the owner of this fd node */ + grpc_ares_ev_driver *ev_driver; + /** the grpc_fd owned by this fd node */ + grpc_fd *grpc_fd; + /** a closure wrapping on_readable_cb, which should be invoked when the + grpc_fd in this node becomes readable. */ + grpc_closure read_closure; + /** a closure wrapping on_writable_cb, which should be invoked when the + grpc_fd in this node becomes writable. */ + grpc_closure write_closure; + /** next fd node in the list */ + struct fd_node *next; + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** if the readable closure has been registered */ + bool readable_registered; + /** if the writable closure has been registered */ + bool writable_registered; +} fd_node; + +struct grpc_ares_ev_driver { + /** the ares_channel owned by this event driver */ + ares_channel channel; + /** pollset set for driving the IO events of the channel */ + grpc_pollset_set *pollset_set; + /** refcount of the event driver */ + gpr_refcount refs; + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** a list of grpc_fd that this event driver is currently using. */ + fd_node *fds; + /** is this event driver currently working? */ + bool working; + /** is this event driver being shut down */ + bool shutting_down; +}; + +static void grpc_ares_notify_on_event_locked(grpc_exec_ctx *exec_ctx, + grpc_ares_ev_driver *ev_driver); + +static grpc_ares_ev_driver *grpc_ares_ev_driver_ref( + grpc_ares_ev_driver *ev_driver) { + gpr_log(GPR_DEBUG, "Ref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); + gpr_ref(&ev_driver->refs); + return ev_driver; +} + +static void grpc_ares_ev_driver_unref(grpc_ares_ev_driver *ev_driver) { + gpr_log(GPR_DEBUG, "Unref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); + if (gpr_unref(&ev_driver->refs)) { + gpr_log(GPR_DEBUG, "destroy ev_driver %" PRIuPTR, (uintptr_t)ev_driver); + GPR_ASSERT(ev_driver->fds == NULL); + gpr_mu_destroy(&ev_driver->mu); + ares_destroy(ev_driver->channel); + gpr_free(ev_driver); + } +} + +static void fd_node_destroy(grpc_exec_ctx *exec_ctx, fd_node *fdn) { + gpr_log(GPR_DEBUG, "delete fd: %d", grpc_fd_wrapped_fd(fdn->grpc_fd)); + GPR_ASSERT(!fdn->readable_registered); + GPR_ASSERT(!fdn->writable_registered); + gpr_mu_destroy(&fdn->mu); + grpc_pollset_set_del_fd(exec_ctx, fdn->ev_driver->pollset_set, fdn->grpc_fd); + grpc_fd_shutdown(exec_ctx, fdn->grpc_fd, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("fd node destroyed")); + grpc_fd_orphan(exec_ctx, fdn->grpc_fd, NULL, NULL, "c-ares query finished"); + gpr_free(fdn); +} + +grpc_error *grpc_ares_ev_driver_create(grpc_ares_ev_driver **ev_driver, + grpc_pollset_set *pollset_set) { + *ev_driver = gpr_malloc(sizeof(grpc_ares_ev_driver)); + int status = ares_init(&(*ev_driver)->channel); + gpr_log(GPR_DEBUG, "grpc_ares_ev_driver_create"); + if (status != ARES_SUCCESS) { + char *err_msg; + gpr_asprintf(&err_msg, "Failed to init ares channel. C-ares error: %s", + ares_strerror(status)); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg); + gpr_free(err_msg); + gpr_free(*ev_driver); + return err; + } + gpr_mu_init(&(*ev_driver)->mu); + gpr_ref_init(&(*ev_driver)->refs, 1); + (*ev_driver)->pollset_set = pollset_set; + (*ev_driver)->fds = NULL; + (*ev_driver)->working = false; + (*ev_driver)->shutting_down = false; + return GRPC_ERROR_NONE; +} + +void grpc_ares_ev_driver_destroy(grpc_ares_ev_driver *ev_driver) { + // It's not safe to shut down remaining fds here directly, becauses + // ares_host_callback does not provide an exec_ctx. We mark the event driver + // as being shut down. If the event driver is working, + // grpc_ares_notify_on_event_locked will shut down the fds; if it's not + // working, there are no fds to shut down. + gpr_mu_lock(&ev_driver->mu); + ev_driver->shutting_down = true; + gpr_mu_unlock(&ev_driver->mu); + grpc_ares_ev_driver_unref(ev_driver); +} + +// Search fd in the fd_node list head. This is an O(n) search, the max possible +// value of n is ARES_GETSOCK_MAXNUM (16). n is typically 1 - 2 in our tests. +static fd_node *pop_fd_node(fd_node **head, int fd) { + fd_node dummy_head; + dummy_head.next = *head; + fd_node *node = &dummy_head; + while (node->next != NULL) { + if (grpc_fd_wrapped_fd(node->next->grpc_fd) == fd) { + fd_node *ret = node->next; + node->next = node->next->next; + *head = dummy_head.next; + return ret; + } + node = node->next; + } + return NULL; +} + +static void on_readable_cb(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + fd_node *fdn = arg; + grpc_ares_ev_driver *ev_driver = fdn->ev_driver; + gpr_mu_lock(&fdn->mu); + fdn->readable_registered = false; + gpr_mu_unlock(&fdn->mu); + + gpr_log(GPR_DEBUG, "readable on %d", grpc_fd_wrapped_fd(fdn->grpc_fd)); + if (error == GRPC_ERROR_NONE) { + ares_process_fd(ev_driver->channel, grpc_fd_wrapped_fd(fdn->grpc_fd), + ARES_SOCKET_BAD); + } else { + // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or + // timed out. The pending lookups made on this ev_driver will be cancelled + // by the following ares_cancel() and the on_done callbacks will be invoked + // with a status of ARES_ECANCELLED. The remaining file descriptors in this + // ev_driver will be cleaned up in the follwing + // grpc_ares_notify_on_event_locked(). + ares_cancel(ev_driver->channel); + } + gpr_mu_lock(&ev_driver->mu); + grpc_ares_notify_on_event_locked(exec_ctx, ev_driver); + gpr_mu_unlock(&ev_driver->mu); + grpc_ares_ev_driver_unref(ev_driver); +} + +static void on_writable_cb(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + fd_node *fdn = arg; + grpc_ares_ev_driver *ev_driver = fdn->ev_driver; + gpr_mu_lock(&fdn->mu); + fdn->writable_registered = false; + gpr_mu_unlock(&fdn->mu); + + gpr_log(GPR_DEBUG, "writable on %d", grpc_fd_wrapped_fd(fdn->grpc_fd)); + if (error == GRPC_ERROR_NONE) { + ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, + grpc_fd_wrapped_fd(fdn->grpc_fd)); + } else { + // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or + // timed out. The pending lookups made on this ev_driver will be cancelled + // by the following ares_cancel() and the on_done callbacks will be invoked + // with a status of ARES_ECANCELLED. The remaining file descriptors in this + // ev_driver will be cleaned up in the follwing + // grpc_ares_notify_on_event_locked(). + ares_cancel(ev_driver->channel); + } + gpr_mu_lock(&ev_driver->mu); + grpc_ares_notify_on_event_locked(exec_ctx, ev_driver); + gpr_mu_unlock(&ev_driver->mu); + grpc_ares_ev_driver_unref(ev_driver); +} + +ares_channel *grpc_ares_ev_driver_get_channel(grpc_ares_ev_driver *ev_driver) { + return &ev_driver->channel; +} + +// Get the file descriptors used by the ev_driver's ares channel, register +// driver_closure with these filedescriptors. +static void grpc_ares_notify_on_event_locked(grpc_exec_ctx *exec_ctx, + grpc_ares_ev_driver *ev_driver) { + fd_node *new_list = NULL; + if (!ev_driver->shutting_down) { + ares_socket_t socks[ARES_GETSOCK_MAXNUM]; + int socks_bitmask = + ares_getsock(ev_driver->channel, socks, ARES_GETSOCK_MAXNUM); + for (size_t i = 0; i < ARES_GETSOCK_MAXNUM; i++) { + if (ARES_GETSOCK_READABLE(socks_bitmask, i) || + ARES_GETSOCK_WRITABLE(socks_bitmask, i)) { + fd_node *fdn = pop_fd_node(&ev_driver->fds, socks[i]); + // Create a new fd_node if sock[i] is not in the fd_node list. + if (fdn == NULL) { + char *fd_name; + gpr_asprintf(&fd_name, "ares_ev_driver-%" PRIuPTR, i); + fdn = gpr_malloc(sizeof(fd_node)); + gpr_log(GPR_DEBUG, "new fd: %d", socks[i]); + fdn->grpc_fd = grpc_fd_create(socks[i], fd_name); + fdn->ev_driver = ev_driver; + fdn->readable_registered = false; + fdn->writable_registered = false; + gpr_mu_init(&fdn->mu); + grpc_closure_init(&fdn->read_closure, on_readable_cb, fdn, + grpc_schedule_on_exec_ctx); + grpc_closure_init(&fdn->write_closure, on_writable_cb, fdn, + grpc_schedule_on_exec_ctx); + grpc_pollset_set_add_fd(exec_ctx, ev_driver->pollset_set, + fdn->grpc_fd); + gpr_free(fd_name); + } + fdn->next = new_list; + new_list = fdn; + gpr_mu_lock(&fdn->mu); + // Register read_closure if the socket is readable and read_closure has + // not been registered with this socket. + if (ARES_GETSOCK_READABLE(socks_bitmask, i) && + !fdn->readable_registered) { + grpc_ares_ev_driver_ref(ev_driver); + gpr_log(GPR_DEBUG, "notify read on: %d", + grpc_fd_wrapped_fd(fdn->grpc_fd)); + grpc_fd_notify_on_read(exec_ctx, fdn->grpc_fd, &fdn->read_closure); + fdn->readable_registered = true; + } + // Register write_closure if the socket is writable and write_closure + // has not been registered with this socket. + if (ARES_GETSOCK_WRITABLE(socks_bitmask, i) && + !fdn->writable_registered) { + gpr_log(GPR_DEBUG, "notify write on: %d", + grpc_fd_wrapped_fd(fdn->grpc_fd)); + grpc_ares_ev_driver_ref(ev_driver); + grpc_fd_notify_on_write(exec_ctx, fdn->grpc_fd, &fdn->write_closure); + fdn->writable_registered = true; + } + gpr_mu_unlock(&fdn->mu); + } + } + } + // Any remaining fds in ev_driver->fds were not returned by ares_getsock() and + // are therefore no longer in use, so they can be shut down and removed from + // the list. + while (ev_driver->fds != NULL) { + fd_node *cur = ev_driver->fds; + ev_driver->fds = ev_driver->fds->next; + fd_node_destroy(exec_ctx, cur); + } + ev_driver->fds = new_list; + // If the ev driver has no working fd, all the tasks are done. + if (new_list == NULL) { + ev_driver->working = false; + gpr_log(GPR_DEBUG, "ev driver stop working"); + } +} + +void grpc_ares_ev_driver_start(grpc_exec_ctx *exec_ctx, + grpc_ares_ev_driver *ev_driver) { + gpr_mu_lock(&ev_driver->mu); + if (!ev_driver->working) { + ev_driver->working = true; + grpc_ares_notify_on_event_locked(exec_ctx, ev_driver); + } + gpr_mu_unlock(&ev_driver->mu); +} + +#endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET) */ diff --git a/src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.c b/src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.c new file mode 100644 index 0000000000..3eee8e3513 --- /dev/null +++ b/src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.c @@ -0,0 +1,289 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <grpc/support/port_platform.h> +#if GRPC_ARES == 1 && !defined(GRPC_UV) + +#include "src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" + +#include <string.h> +#include <sys/types.h> + +#include <ares.h> +#include <grpc/support/alloc.h> +#include <grpc/support/host_port.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> +#include <grpc/support/time.h> +#include <grpc/support/useful.h> +#include "src/core/ext/resolver/dns/c_ares/grpc_ares_ev_driver.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/support/string.h" + +static gpr_once g_basic_init = GPR_ONCE_INIT; +static gpr_mu g_init_mu; + +typedef struct grpc_ares_request { + /** following members are set in grpc_resolve_address_ares_impl */ + /** host to resolve, parsed from the name to resolve */ + char *host; + /** port to fill in sockaddr_in, parsed from the name to resolve */ + char *port; + /** default port to use */ + char *default_port; + /** closure to call when the request completes */ + grpc_closure *on_done; + /** the pointer to receive the resolved addresses */ + grpc_resolved_addresses **addrs_out; + /** the evernt driver used by this request */ + grpc_ares_ev_driver *ev_driver; + /** number of ongoing queries */ + gpr_refcount pending_queries; + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** is there at least one successful query, set in on_done_cb */ + bool success; + /** the errors explaining the request failure, set in on_done_cb */ + grpc_error *error; +} grpc_ares_request; + +static void do_basic_init(void) { gpr_mu_init(&g_init_mu); } + +static uint16_t strhtons(const char *port) { + if (strcmp(port, "http") == 0) { + return htons(80); + } else if (strcmp(port, "https") == 0) { + return htons(443); + } + return htons((unsigned short)atoi(port)); +} + +static void grpc_ares_request_unref(grpc_exec_ctx *exec_ctx, + grpc_ares_request *r) { + /* If there are no pending queries, invoke on_done callback and destroy the + request */ + if (gpr_unref(&r->pending_queries)) { + /* TODO(zyc): Sort results with RFC6724 before invoking on_done. */ + if (exec_ctx == NULL) { + /* A new exec_ctx is created here, as the c-ares interface does not + provide one in ares_host_callback. It's safe to schedule on_done with + the newly created exec_ctx, since the caller has been warned not to + acquire locks in on_done. ares_dns_resolver is using combiner to + protect resources needed by on_done. */ + grpc_exec_ctx new_exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_closure_sched(&new_exec_ctx, r->on_done, r->error); + grpc_exec_ctx_finish(&new_exec_ctx); + } else { + grpc_closure_sched(exec_ctx, r->on_done, r->error); + } + gpr_mu_destroy(&r->mu); + grpc_ares_ev_driver_destroy(r->ev_driver); + gpr_free(r->host); + gpr_free(r->port); + gpr_free(r->default_port); + gpr_free(r); + } +} + +static void on_done_cb(void *arg, int status, int timeouts, + struct hostent *hostent) { + grpc_ares_request *r = (grpc_ares_request *)arg; + gpr_mu_lock(&r->mu); + if (status == ARES_SUCCESS) { + GRPC_ERROR_UNREF(r->error); + r->error = GRPC_ERROR_NONE; + r->success = true; + grpc_resolved_addresses **addresses = r->addrs_out; + if (*addresses == NULL) { + *addresses = gpr_malloc(sizeof(grpc_resolved_addresses)); + (*addresses)->naddrs = 0; + (*addresses)->addrs = NULL; + } + size_t prev_naddr = (*addresses)->naddrs; + size_t i; + for (i = 0; hostent->h_addr_list[i] != NULL; i++) { + } + (*addresses)->naddrs += i; + (*addresses)->addrs = + gpr_realloc((*addresses)->addrs, + sizeof(grpc_resolved_address) * (*addresses)->naddrs); + for (i = prev_naddr; i < (*addresses)->naddrs; i++) { + memset(&(*addresses)->addrs[i], 0, sizeof(grpc_resolved_address)); + if (hostent->h_addrtype == AF_INET6) { + (*addresses)->addrs[i].len = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *addr = + (struct sockaddr_in6 *)&(*addresses)->addrs[i].addr; + addr->sin6_family = (sa_family_t)hostent->h_addrtype; + addr->sin6_port = strhtons(r->port); + + char output[INET6_ADDRSTRLEN]; + memcpy(&addr->sin6_addr, hostent->h_addr_list[i - prev_naddr], + sizeof(struct in6_addr)); + ares_inet_ntop(AF_INET6, &addr->sin6_addr, output, INET6_ADDRSTRLEN); + gpr_log(GPR_DEBUG, + "c-ares resolver gets a AF_INET6 result: \n" + " addr: %s\n port: %s\n sin6_scope_id: %d\n", + output, r->port, addr->sin6_scope_id); + } else { + (*addresses)->addrs[i].len = sizeof(struct sockaddr_in); + struct sockaddr_in *addr = + (struct sockaddr_in *)&(*addresses)->addrs[i].addr; + memcpy(&addr->sin_addr, hostent->h_addr_list[i - prev_naddr], + sizeof(struct in_addr)); + addr->sin_family = (sa_family_t)hostent->h_addrtype; + addr->sin_port = strhtons(r->port); + + char output[INET_ADDRSTRLEN]; + ares_inet_ntop(AF_INET, &addr->sin_addr, output, INET_ADDRSTRLEN); + gpr_log(GPR_DEBUG, + "c-ares resolver gets a AF_INET result: \n" + " addr: %s\n port: %s\n", + output, r->port); + } + } + } else if (!r->success) { + char *error_msg; + gpr_asprintf(&error_msg, "C-ares 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); + } + } + gpr_mu_unlock(&r->mu); + grpc_ares_request_unref(NULL, r); +} + +void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, const char *name, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_resolved_addresses **addrs) { + /* TODO(zyc): Enable tracing after #9603 is checked in */ + /* if (grpc_dns_trace) { + gpr_log(GPR_DEBUG, "resolve_address (blocking): name=%s, default_port=%s", + name, default_port); + } */ + + /* parse name, splitting it into host and port parts */ + char *host; + char *port; + gpr_split_host_port(name, &host, &port); + if (host == NULL) { + grpc_error *err = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("unparseable host:port"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + grpc_closure_sched(exec_ctx, on_done, err); + goto error_cleanup; + } else if (port == NULL) { + if (default_port == NULL) { + grpc_error *err = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("no port in name"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + grpc_closure_sched(exec_ctx, on_done, err); + goto error_cleanup; + } + port = gpr_strdup(default_port); + } + + grpc_ares_ev_driver *ev_driver; + grpc_error *err = grpc_ares_ev_driver_create(&ev_driver, interested_parties); + if (err != GRPC_ERROR_NONE) { + GRPC_LOG_IF_ERROR("grpc_ares_ev_driver_create() failed", err); + goto error_cleanup; + } + + grpc_ares_request *r = gpr_malloc(sizeof(grpc_ares_request)); + gpr_mu_init(&r->mu); + r->ev_driver = ev_driver; + r->on_done = on_done; + r->addrs_out = addrs; + r->default_port = gpr_strdup(default_port); + r->port = port; + r->host = host; + r->success = false; + r->error = GRPC_ERROR_NONE; + ares_channel *channel = grpc_ares_ev_driver_get_channel(r->ev_driver); + gpr_ref_init(&r->pending_queries, 2); + if (grpc_ipv6_loopback_available()) { + gpr_ref(&r->pending_queries); + ares_gethostbyname(*channel, r->host, AF_INET6, on_done_cb, r); + } + ares_gethostbyname(*channel, r->host, AF_INET, on_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); + return; + +error_cleanup: + gpr_free(host); + gpr_free(port); +} + +void (*grpc_resolve_address_ares)( + grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, + grpc_pollset_set *interested_parties, grpc_closure *on_done, + grpc_resolved_addresses **addrs) = grpc_resolve_address_ares_impl; + +grpc_error *grpc_ares_init(void) { + gpr_once_init(&g_basic_init, do_basic_init); + gpr_mu_lock(&g_init_mu); + int status = ares_library_init(ARES_LIB_INIT_ALL); + gpr_mu_unlock(&g_init_mu); + + if (status != ARES_SUCCESS) { + char *error_msg; + gpr_asprintf(&error_msg, "ares_library_init failed: %s", + ares_strerror(status)); + grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + return error; + } + return GRPC_ERROR_NONE; +} + +void grpc_ares_cleanup(void) { + gpr_mu_lock(&g_init_mu); + ares_library_cleanup(); + gpr_mu_unlock(&g_init_mu); +} + +#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */ diff --git a/src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.h b/src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.h new file mode 100644 index 0000000000..ab00a26b36 --- /dev/null +++ b/src/core/ext/resolver/dns/c_ares/grpc_ares_wrapper.h @@ -0,0 +1,63 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_CORE_EXT_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H +#define GRPC_CORE_EXT_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/iomgr/resolve_address.h" + +/* 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. */ +extern void (*grpc_resolve_address_ares)(grpc_exec_ctx *exec_ctx, + const char *addr, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_resolved_addresses **addresses); + +/* Initialize gRPC ares wrapper. Must be called at least once before + grpc_resolve_address_ares(). */ +grpc_error *grpc_ares_init(void); + +/* Uninitialized gRPC ares wrapper. If there was more than one previous call to + grpc_ares_init(), this function uninitializes the gRPC ares wrapper only if + it has been called the same number of times as grpc_ares_init(). */ +void grpc_ares_cleanup(void); + +#endif /* GRPC_CORE_EXT_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H */ diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c index d6cb6a95a2..97cd0486a9 100644 --- a/src/core/ext/resolver/dns/native/dns_resolver.c +++ b/src/core/ext/resolver/dns/native/dns_resolver.c @@ -44,6 +44,7 @@ #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/support/backoff.h" +#include "src/core/lib/support/env.h" #include "src/core/lib/support/string.h" #define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 @@ -304,7 +305,21 @@ static grpc_resolver_factory *dns_resolver_factory_create() { } void grpc_resolver_dns_native_init(void) { - grpc_register_resolver_type(dns_resolver_factory_create()); + char *resolver = gpr_getenv("GRPC_DNS_RESOLVER"); + if (resolver != NULL && gpr_stricmp(resolver, "native") == 0) { + gpr_log(GPR_DEBUG, "Using native dns resolver"); + grpc_register_resolver_type(dns_resolver_factory_create()); + } else { + grpc_resolver_factory *existing_factory = + grpc_resolver_factory_lookup("dns"); + if (existing_factory == NULL) { + gpr_log(GPR_DEBUG, "Using native dns resolver"); + grpc_register_resolver_type(dns_resolver_factory_create()); + } else { + grpc_resolver_factory_unref(existing_factory); + } + } + gpr_free(resolver); } void grpc_resolver_dns_native_shutdown(void) {} diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c index 8676a3752e..73f9454f7a 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c @@ -69,10 +69,16 @@ #define MAX_WRITE_BUFFER_SIZE (64 * 1024 * 1024) #define DEFAULT_MAX_HEADER_LIST_SIZE (16 * 1024) -#define DEFAULT_KEEPALIVE_TIME_SECOND INT_MAX -#define DEFAULT_KEEPALIVE_TIMEOUT_SECOND 20 +#define DEFAULT_CLIENT_KEEPALIVE_TIME_S INT_MAX +#define DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_S 20 #define DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS false +static int g_default_client_keepalive_time_s = DEFAULT_CLIENT_KEEPALIVE_TIME_S; +static int g_default_client_keepalive_timeout_s = + DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_S; +static bool g_default_keepalive_permit_without_calls = + DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS; + #define MAX_CLIENT_STREAM_ID 0x7fffffffu int grpc_http_trace = 0; int grpc_flowctl_trace = 0; @@ -142,6 +148,8 @@ static void send_ping_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_chttp2_ping_type ping_type, grpc_closure *on_initiate, grpc_closure *on_complete); +static void retry_initiate_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error); #define DEFAULT_MIN_TIME_BETWEEN_PINGS_MS 0 #define DEFAULT_MAX_PINGS_BETWEEN_DATA 3 @@ -267,6 +275,8 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_closure_init(&t->destructive_reclaimer_locked, destructive_reclaimer_locked, t, grpc_combiner_scheduler(t->combiner, false)); + grpc_closure_init(&t->retry_initiate_ping_locked, retry_initiate_ping_locked, + t, grpc_combiner_scheduler(t->combiner, false)); grpc_closure_init(&t->start_bdp_ping_locked, start_bdp_ping_locked, t, grpc_combiner_scheduler(t->combiner, false)); grpc_closure_init(&t->finish_bdp_ping_locked, finish_bdp_ping_locked, t, @@ -345,15 +355,16 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, /* client-side keepalive setting */ t->keepalive_time = - DEFAULT_KEEPALIVE_TIME_SECOND == INT_MAX + g_default_client_keepalive_time_s == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_seconds(DEFAULT_KEEPALIVE_TIME_SECOND, GPR_TIMESPAN); + : gpr_time_from_seconds(g_default_client_keepalive_time_s, + GPR_TIMESPAN); t->keepalive_timeout = - DEFAULT_KEEPALIVE_TIMEOUT_SECOND == INT_MAX + g_default_client_keepalive_timeout_s == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_seconds(DEFAULT_KEEPALIVE_TIMEOUT_SECOND, + : gpr_time_from_seconds(g_default_client_keepalive_timeout_s, GPR_TIMESPAN); - t->keepalive_permit_without_calls = DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS; + t->keepalive_permit_without_calls = g_default_keepalive_permit_without_calls; if (channel_args) { for (i = 0; i < channel_args->num_args; i++) { @@ -403,24 +414,25 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, t->enable_bdp_probe = grpc_channel_arg_get_integer( &channel_args->args[i], (grpc_integer_options){1, 0, 1}); } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_KEEPALIVE_TIME)) { + GRPC_ARG_CLIENT_KEEPALIVE_TIME_S)) { const int value = grpc_channel_arg_get_integer( &channel_args->args[i], - (grpc_integer_options){DEFAULT_KEEPALIVE_TIME_SECOND, 1, INT_MAX}); + (grpc_integer_options){g_default_client_keepalive_time_s, 1, + INT_MAX}); t->keepalive_time = value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_seconds(value, GPR_TIMESPAN); } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_KEEPALIVE_TIMEOUT)) { + GRPC_ARG_CLIENT_KEEPALIVE_TIMEOUT_S)) { const int value = grpc_channel_arg_get_integer( &channel_args->args[i], - (grpc_integer_options){DEFAULT_KEEPALIVE_TIMEOUT_SECOND, 0, + (grpc_integer_options){g_default_client_keepalive_timeout_s, 0, INT_MAX}); t->keepalive_timeout = value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_seconds(value, GPR_TIMESPAN); } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { + GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { t->keepalive_permit_without_calls = (uint32_t)grpc_channel_arg_get_integer( &channel_args->args[i], (grpc_integer_options){0, 0, 1}); @@ -474,6 +486,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, t->ping_state.pings_before_data_required = t->ping_policy.max_pings_without_data; + t->ping_state.is_delayed_ping_timer_set = false; /** Start client-side keepalive pings */ if (t->is_client) { @@ -1210,8 +1223,13 @@ static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op, } } else { GPR_ASSERT(s->id != 0); - grpc_chttp2_become_writable(exec_ctx, t, s, - GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED, + grpc_chttp2_stream_write_type write_type = + GRPC_CHTTP2_STREAM_WRITE_INITIATE_COVERED; + if (op->send_message != NULL && + (op->send_message->flags & GRPC_WRITE_BUFFER_HINT)) { + write_type = GRPC_CHTTP2_STREAM_WRITE_PIGGYBACK; + } + grpc_chttp2_become_writable(exec_ctx, t, s, write_type, "op.send_initial_metadata"); } } else { @@ -1394,6 +1412,13 @@ static void send_ping_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, } } +static void retry_initiate_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error) { + grpc_chttp2_transport *t = tp; + t->ping_state.is_delayed_ping_timer_set = false; + grpc_chttp2_initiate_write(exec_ctx, t, false, "retry_send_ping"); +} + void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, uint64_t id) { grpc_chttp2_ping_queue *pq = @@ -2103,6 +2128,32 @@ static void finish_bdp_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "bdp_ping"); } +void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args) { + size_t i; + if (args) { + for (i = 0; i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_CLIENT_KEEPALIVE_TIME_S)) { + g_default_client_keepalive_time_s = grpc_channel_arg_get_integer( + &args->args[i], (grpc_integer_options){ + g_default_client_keepalive_time_s, 1, INT_MAX}); + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_CLIENT_KEEPALIVE_TIMEOUT_S)) { + g_default_client_keepalive_timeout_s = grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){g_default_client_keepalive_timeout_s, 0, + INT_MAX}); + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { + g_default_keepalive_permit_without_calls = + (uint32_t)grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){g_default_keepalive_permit_without_calls, + 0, 1}); + } + } + } +} + static void init_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { grpc_chttp2_transport *t = arg; @@ -2146,9 +2197,7 @@ static void finish_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, grpc_timer_init( exec_ctx, &t->keepalive_ping_timer, gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time), - grpc_closure_create(init_keepalive_ping_locked, t, - grpc_combiner_scheduler(t->combiner, false)), - gpr_now(GPR_CLOCK_MONOTONIC)); + &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC)); } } GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keepalive ping end"); diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.c b/src/core/ext/transport/chttp2/transport/frame_ping.c index de8462a17e..46dafdb62f 100644 --- a/src/core/ext/transport/chttp2/transport/frame_ping.c +++ b/src/core/ext/transport/chttp2/transport/frame_ping.c @@ -40,6 +40,8 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> +static bool g_disable_ping_ack = false; + grpc_slice grpc_chttp2_ping_create(uint8_t ack, uint64_t opaque_8bytes) { grpc_slice slice = grpc_slice_malloc(9 + 8); uint8_t *p = GRPC_SLICE_START_PTR(slice); @@ -101,15 +103,21 @@ grpc_error *grpc_chttp2_ping_parser_parse(grpc_exec_ctx *exec_ctx, void *parser, if (p->is_ack) { grpc_chttp2_ack_ping(exec_ctx, t, p->opaque_8bytes); } else { - if (t->ping_ack_count == t->ping_ack_capacity) { - t->ping_ack_capacity = GPR_MAX(t->ping_ack_capacity * 3 / 2, 3); - t->ping_acks = gpr_realloc( - t->ping_acks, t->ping_ack_capacity * sizeof(*t->ping_acks)); + if (!g_disable_ping_ack) { + if (t->ping_ack_count == t->ping_ack_capacity) { + t->ping_ack_capacity = GPR_MAX(t->ping_ack_capacity * 3 / 2, 3); + t->ping_acks = gpr_realloc( + t->ping_acks, t->ping_ack_capacity * sizeof(*t->ping_acks)); + } + t->ping_acks[t->ping_ack_count++] = p->opaque_8bytes; + grpc_chttp2_initiate_write(exec_ctx, t, false, "ping response"); } - t->ping_acks[t->ping_ack_count++] = p->opaque_8bytes; - grpc_chttp2_initiate_write(exec_ctx, t, false, "ping response"); } } return GRPC_ERROR_NONE; } + +void grpc_set_disable_ping_ack(bool disable_ping_ack) { + g_disable_ping_ack = disable_ping_ack; +} diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.h b/src/core/ext/transport/chttp2/transport/frame_ping.h index ef642465d7..01983d2b12 100644 --- a/src/core/ext/transport/chttp2/transport/frame_ping.h +++ b/src/core/ext/transport/chttp2/transport/frame_ping.h @@ -53,4 +53,7 @@ grpc_error *grpc_chttp2_ping_parser_parse(grpc_exec_ctx *exec_ctx, void *parser, grpc_chttp2_stream *s, grpc_slice slice, int is_last); +/* Test-only function for disabling ping ack */ +void grpc_set_disable_ping_ack(bool disable_ping_ack); + #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H */ diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index 3c56c21599..8b718e963c 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -102,6 +102,8 @@ typedef struct { typedef struct { gpr_timespec last_ping_sent_time; int pings_before_data_required; + grpc_timer delayed_ping_timer; + bool is_delayed_ping_timer_set; } grpc_chttp2_repeated_ping_state; /* deframer state for the overall http2 stream of bytes */ @@ -308,6 +310,7 @@ struct grpc_chttp2_transport { grpc_chttp2_repeated_ping_policy ping_policy; grpc_chttp2_repeated_ping_state ping_state; uint64_t ping_ctr; /* unique id for pings */ + grpc_closure retry_initiate_ping_locked; /** ping acks */ size_t ping_ack_count; @@ -827,4 +830,8 @@ void grpc_chttp2_fail_pending_writes(grpc_exec_ctx *exec_ctx, uint32_t grpc_chttp2_target_incoming_window(grpc_chttp2_transport *t); +/** Set the default keepalive configurations, must only be called at + initialization */ +void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args); + #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H */ diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c index 2b9d93cae7..0869056f56 100644 --- a/src/core/ext/transport/chttp2/transport/writing.c +++ b/src/core/ext/transport/chttp2/transport/writing.c @@ -101,6 +101,14 @@ static void maybe_initiate_ping(grpc_exec_ctx *exec_ctx, "Ping delayed [%p]: not enough time elapsed since last ping", t->peer_string); } + if (!t->ping_state.is_delayed_ping_timer_set) { + t->ping_state.is_delayed_ping_timer_set = true; + grpc_timer_init(exec_ctx, &t->ping_state.delayed_ping_timer, + gpr_time_add(t->ping_state.last_ping_sent_time, + t->ping_policy.min_time_between_pings), + &t->retry_initiate_ping_locked, + gpr_now(GPR_CLOCK_MONOTONIC)); + } return; } /* coalesce equivalent pings into this one */ |