diff options
author | Nicolas "Pixel" Noble <pixel@nobis-crew.org> | 2016-10-26 23:34:16 +0200 |
---|---|---|
committer | Nicolas "Pixel" Noble <pixel@nobis-crew.org> | 2016-10-26 23:34:16 +0200 |
commit | 6ab40a5fd395ec0db6231a1c1ffd4dd468b61bd0 (patch) | |
tree | b5104f137d8b4f4b54318b7ecf12c037abac62da /src | |
parent | 89e23643538cd632e3f0e78671795ab42bf3371c (diff) | |
parent | 832b685740f52dd1ac04fe7cdda4cc5ad1e7fa92 (diff) |
Merge branch 'master' of https://github.com/grpc/grpc into bazel-take-2
Diffstat (limited to 'src')
218 files changed, 5343 insertions, 1936 deletions
diff --git a/src/boringssl/gen_build_yaml.py b/src/boringssl/gen_build_yaml.py index 20f6413adf..c53beb0da5 100755 --- a/src/boringssl/gen_build_yaml.py +++ b/src/boringssl/gen_build_yaml.py @@ -36,7 +36,7 @@ import yaml sys.dont_write_bytecode = True boring_ssl_root = os.path.abspath(os.path.join( - os.path.dirname(sys.argv[0]), + os.path.dirname(sys.argv[0]), '../../third_party/boringssl')) sys.path.append(os.path.join(boring_ssl_root, 'util')) diff --git a/src/compiler/cpp_generator.cc b/src/compiler/cpp_generator.cc index d0c35ea1ab..fa72f9b0d9 100644 --- a/src/compiler/cpp_generator.cc +++ b/src/compiler/cpp_generator.cc @@ -624,7 +624,7 @@ void PrintHeaderServerMethodStreamedUnary( printer->Indent(); printer->Print(*vars, "WithStreamedUnaryMethod_$Method$() {\n" - " ::grpc::Service::MarkMethodStreamedUnary($Idx$,\n" + " ::grpc::Service::MarkMethodStreamed($Idx$,\n" " new ::grpc::StreamedUnaryHandler< $Request$, " "$Response$>(std::bind" "(&WithStreamedUnaryMethod_$Method$<BaseClass>::" @@ -656,6 +656,58 @@ void PrintHeaderServerMethodStreamedUnary( } } +void PrintHeaderServerMethodSplitStreaming( + Printer *printer, const Method *method, + std::map<grpc::string, grpc::string> *vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->ServerOnlyStreaming()) { + printer->Print(*vars, "template <class BaseClass>\n"); + printer->Print(*vars, + "class WithSplitStreamingMethod_$Method$ : " + "public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) " + "{}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithSplitStreamingMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodStreamed($Idx$,\n" + " new ::grpc::SplitServerStreamingHandler< $Request$, " + "$Response$>(std::bind" + "(&WithSplitStreamingMethod_$Method$<BaseClass>::" + "Streamed$Method$, this, std::placeholders::_1, " + "std::placeholders::_2)));\n" + "}\n"); + printer->Print(*vars, + "~WithSplitStreamingMethod_$Method$() GRPC_OVERRIDE {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + printer->Print( + *vars, + "// disable regular version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) GRPC_FINAL GRPC_OVERRIDE " + "{\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print(*vars, + "// replace default version of method with split streamed\n" + "virtual ::grpc::Status Streamed$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerSplitStreamer< " + "$Request$,$Response$>* server_split_streamer)" + " = 0;\n"); + printer->Outdent(); + printer->Print(*vars, "};\n"); + } +} + void PrintHeaderServerMethodGeneric( Printer *printer, const Method *method, std::map<grpc::string, grpc::string> *vars) { @@ -844,6 +896,48 @@ void PrintHeaderService(Printer *printer, const Service *service, } printer->Print(" StreamedUnaryService;\n"); + // Server side - controlled server-side streaming + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodSplitStreaming(printer, service->method(i).get(), + vars); + } + + printer->Print("typedef "); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["method_name"] = service->method(i).get()->name(); + if (service->method(i)->ServerOnlyStreaming()) { + printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<"); + } + } + printer->Print("Service"); + for (int i = 0; i < service->method_count(); ++i) { + if (service->method(i)->ServerOnlyStreaming()) { + printer->Print(" >"); + } + } + printer->Print(" SplitStreamedService;\n"); + + // Server side - typedef for controlled both unary and server-side streaming + printer->Print("typedef "); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["method_name"] = service->method(i).get()->name(); + if (service->method(i)->ServerOnlyStreaming()) { + printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<"); + } + if (service->method(i)->NoStreaming()) { + printer->Print(*vars, "WithStreamedUnaryMethod_$method_name$<"); + } + } + printer->Print("Service"); + for (int i = 0; i < service->method_count(); ++i) { + if (service->method(i)->NoStreaming() || + service->method(i)->ServerOnlyStreaming()) { + printer->Print(" >"); + } + } + printer->Print(" StreamedService;\n"); + printer->Outdent(); printer->Print("};\n"); printer->Print(service->GetTrailingComments().c_str()); diff --git a/src/core/ext/client_config/README.md b/src/core/ext/client_channel/README.md index eda01e3e71..7c209db12e 100644 --- a/src/core/ext/client_config/README.md +++ b/src/core/ext/client_channel/README.md @@ -5,28 +5,27 @@ This library provides high level configuration machinery to construct client channels and load balance between them. Each grpc_channel is created with a grpc_resolver. It is the resolver's duty -to resolve a name into configuration data for the channel. Such configuration -data might include: +to resolve a name into a set of arguments for the channel. Such arguments +might include: - a list of (ip, port) addresses to connect to - a load balancing policy to decide which server to send a request to - a set of filters to mutate outgoing requests (say, by adding metadata) -The resolver provides this data as a stream of grpc_resolver_result objects to -the channel. We represent configuration as a stream so that it can be changed -by the resolver during execution, by reacting to external events (such as a -new configuration file being pushed to some store). +The resolver provides this data as a stream of grpc_channel_args objects to +the channel. We represent arguments as a stream so that they can be changed +by the resolver during execution, by reacting to external events (such as +new service configuration data being pushed to some store). Load Balancing -------------- -Load balancing configuration is provided by a grpc_lb_policy object, stored as -part of grpc_resolver_result. +Load balancing configuration is provided by a grpc_lb_policy object. -The primary job of the load balancing policies is to pick a target server given only the -initial metadata for a request. It does this by providing a grpc_subchannel -object to the owning channel. +The primary job of the load balancing policies is to pick a target server +given only the initial metadata for a request. It does this by providing +a grpc_subchannel object to the owning channel. Sub-Channels diff --git a/src/core/ext/client_config/channel_connectivity.c b/src/core/ext/client_channel/channel_connectivity.c index ce3c13a4ee..9797e66564 100644 --- a/src/core/ext/client_config/channel_connectivity.c +++ b/src/core/ext/client_channel/channel_connectivity.c @@ -36,7 +36,7 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include "src/core/ext/client_config/client_channel.h" +#include "src/core/ext/client_channel/client_channel.h" #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/completion_queue.h" diff --git a/src/core/ext/client_config/client_channel.c b/src/core/ext/client_channel/client_channel.c index beaa9637c3..80b4f048c2 100644 --- a/src/core/ext/client_config/client_channel.c +++ b/src/core/ext/client_channel/client_channel.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/client_channel.h" +#include "src/core/ext/client_channel/client_channel.h" #include <stdbool.h> #include <stdio.h> @@ -42,9 +42,8 @@ #include <grpc/support/sync.h> #include <grpc/support/useful.h> -#include "src/core/ext/client_config/lb_policy_registry.h" -#include "src/core/ext/client_config/method_config.h" -#include "src/core/ext/client_config/subchannel.h" +#include "src/core/ext/client_channel/lb_policy_registry.h" +#include "src/core/ext/client_channel/subchannel.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/connected_channel.h" #include "src/core/lib/channel/deadline_filter.h" @@ -56,11 +55,60 @@ #include "src/core/lib/transport/connectivity_state.h" #include "src/core/lib/transport/metadata.h" #include "src/core/lib/transport/metadata_batch.h" +#include "src/core/lib/transport/method_config.h" #include "src/core/lib/transport/static_metadata.h" /* Client channel implementation */ /************************************************************************* + * METHOD-CONFIG TABLE + */ + +typedef enum { + WAIT_FOR_READY_UNSET, + WAIT_FOR_READY_FALSE, + WAIT_FOR_READY_TRUE +} wait_for_ready_value; + +typedef struct method_parameters { + gpr_timespec timeout; + wait_for_ready_value wait_for_ready; +} method_parameters; + +static void *method_parameters_copy(void *value) { + void *new_value = gpr_malloc(sizeof(method_parameters)); + memcpy(new_value, value, sizeof(method_parameters)); + return new_value; +} + +static int method_parameters_cmp(void *value1, void *value2) { + const method_parameters *v1 = value1; + const method_parameters *v2 = value2; + const int retval = gpr_time_cmp(v1->timeout, v2->timeout); + if (retval != 0) return retval; + if (v1->wait_for_ready > v2->wait_for_ready) return 1; + if (v1->wait_for_ready < v2->wait_for_ready) return -1; + return 0; +} + +static const grpc_mdstr_hash_table_vtable method_parameters_vtable = { + gpr_free, method_parameters_copy, method_parameters_cmp}; + +static void *method_config_convert_value( + const grpc_method_config *method_config) { + method_parameters *value = gpr_malloc(sizeof(method_parameters)); + const gpr_timespec *timeout = grpc_method_config_get_timeout(method_config); + value->timeout = timeout != NULL ? *timeout : gpr_time_0(GPR_TIMESPAN); + const bool *wait_for_ready = + grpc_method_config_get_wait_for_ready(method_config); + value->wait_for_ready = + wait_for_ready == NULL + ? WAIT_FOR_READY_UNSET + : (wait_for_ready ? WAIT_FOR_READY_TRUE : WAIT_FOR_READY_FALSE); + return value; +} + +/************************************************************************* * CHANNEL-WIDE FUNCTIONS */ @@ -76,10 +124,10 @@ typedef struct client_channel_channel_data { gpr_mu mu; /** currently active load balancer */ grpc_lb_policy *lb_policy; - /** method config table */ - grpc_method_config_table *method_config_table; + /** maps method names to method_parameters structs */ + grpc_mdstr_hash_table *method_params_table; /** incoming resolver result - set by resolver.next() */ - grpc_resolver_result *resolver_result; + grpc_channel_args *resolver_result; /** a list of closures that are all waiting for config to come in */ grpc_closure_list waiting_for_config_closures; /** resolver callback */ @@ -177,42 +225,49 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, channel_data *chand = arg; grpc_lb_policy *lb_policy = NULL; grpc_lb_policy *old_lb_policy; - grpc_method_config_table *method_config_table = NULL; + grpc_mdstr_hash_table *method_params_table = NULL; grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE; bool exit_idle = false; grpc_error *state_error = GRPC_ERROR_CREATE("No load balancing policy"); if (chand->resolver_result != NULL) { grpc_lb_policy_args lb_policy_args; - lb_policy_args.server_name = - grpc_resolver_result_get_server_name(chand->resolver_result); - lb_policy_args.addresses = - grpc_resolver_result_get_addresses(chand->resolver_result); - lb_policy_args.additional_args = - grpc_resolver_result_get_lb_policy_args(chand->resolver_result); + lb_policy_args.args = chand->resolver_result; lb_policy_args.client_channel_factory = chand->client_channel_factory; + // Find LB policy name. + const char *lb_policy_name = NULL; + const grpc_arg *channel_arg = + grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_LB_POLICY_NAME); + if (channel_arg != NULL) { + GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); + lb_policy_name = channel_arg->value.string; + } // Special case: If all of the addresses are balancer addresses, // assume that we should use the grpclb policy, regardless of what the // resolver actually specified. - const char *lb_policy_name = - grpc_resolver_result_get_lb_policy_name(chand->resolver_result); - bool found_backend_address = false; - for (size_t i = 0; i < lb_policy_args.addresses->num_addresses; ++i) { - if (!lb_policy_args.addresses->addresses[i].is_balancer) { - found_backend_address = true; - break; + channel_arg = + grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_LB_ADDRESSES); + if (channel_arg != NULL) { + GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); + grpc_lb_addresses *addresses = channel_arg->value.pointer.p; + bool found_backend_address = false; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (!addresses->addresses[i].is_balancer) { + found_backend_address = true; + break; + } } - } - if (!found_backend_address) { - if (lb_policy_name != NULL && strcmp(lb_policy_name, "grpclb") != 0) { - gpr_log(GPR_INFO, - "resolver requested LB policy %s but provided only balancer " - "addresses, no backend addresses -- forcing use of grpclb LB " - "policy", - (lb_policy_name == NULL ? "(none)" : lb_policy_name)); + if (!found_backend_address) { + if (lb_policy_name != NULL && strcmp(lb_policy_name, "grpclb") != 0) { + gpr_log(GPR_INFO, + "resolver requested LB policy %s but provided only balancer " + "addresses, no backend addresses -- forcing use of grpclb LB " + "policy", + lb_policy_name); + } + lb_policy_name = "grpclb"; } - lb_policy_name = "grpclb"; } // Use pick_first if nothing was specified and we didn't select grpclb // above. @@ -226,14 +281,15 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, state = grpc_lb_policy_check_connectivity(exec_ctx, lb_policy, &state_error); } - const grpc_arg *channel_arg = grpc_channel_args_find( - lb_policy_args.additional_args, GRPC_ARG_SERVICE_CONFIG); + channel_arg = + grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_SERVICE_CONFIG); if (channel_arg != NULL) { GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); - method_config_table = grpc_method_config_table_ref( - (grpc_method_config_table *)channel_arg->value.pointer.p); + method_params_table = grpc_method_config_table_convert( + (grpc_method_config_table *)channel_arg->value.pointer.p, + method_config_convert_value, &method_parameters_vtable); } - grpc_resolver_result_unref(exec_ctx, chand->resolver_result); + grpc_channel_args_destroy(chand->resolver_result); chand->resolver_result = NULL; } @@ -245,10 +301,10 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, gpr_mu_lock(&chand->mu); old_lb_policy = chand->lb_policy; chand->lb_policy = lb_policy; - if (chand->method_config_table != NULL) { - grpc_method_config_table_unref(chand->method_config_table); + if (chand->method_params_table != NULL) { + grpc_mdstr_hash_table_unref(chand->method_params_table); } - chand->method_config_table = method_config_table; + chand->method_params_table = method_params_table; if (lb_policy != NULL) { grpc_exec_ctx_enqueue_list(exec_ctx, &chand->waiting_for_config_closures, NULL); @@ -409,8 +465,8 @@ static void cc_destroy_channel_elem(grpc_exec_ctx *exec_ctx, chand->interested_parties); GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); } - if (chand->method_config_table != NULL) { - grpc_method_config_table_unref(chand->method_config_table); + if (chand->method_params_table != NULL) { + grpc_mdstr_hash_table_unref(chand->method_params_table); } grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker); grpc_pollset_set_destroy(chand->interested_parties); @@ -448,11 +504,7 @@ typedef struct client_channel_call_data { grpc_mdstr *path; // Request path. gpr_timespec call_start_time; gpr_timespec deadline; - enum { - WAIT_FOR_READY_UNSET, - WAIT_FOR_READY_FALSE, - WAIT_FOR_READY_TRUE - } wait_for_ready_from_service_config; + wait_for_ready_value wait_for_ready_from_service_config; grpc_closure read_service_config; grpc_error *cancel_error; @@ -846,41 +898,39 @@ static void read_service_config(grpc_exec_ctx *exec_ctx, void *arg, if (error == GRPC_ERROR_NONE) { // Get the method config table from channel data. gpr_mu_lock(&chand->mu); - grpc_method_config_table *method_config_table = NULL; - if (chand->method_config_table != NULL) { - method_config_table = - grpc_method_config_table_ref(chand->method_config_table); + grpc_mdstr_hash_table *method_params_table = NULL; + if (chand->method_params_table != NULL) { + method_params_table = + grpc_mdstr_hash_table_ref(chand->method_params_table); } gpr_mu_unlock(&chand->mu); // If the method config table was present, use it. - if (method_config_table != NULL) { - const grpc_method_config *method_config = - grpc_method_config_table_get_method_config(method_config_table, - calld->path); - if (method_config != NULL) { - const gpr_timespec *per_method_timeout = - grpc_method_config_get_timeout(method_config); - const bool *wait_for_ready = - grpc_method_config_get_wait_for_ready(method_config); - if (per_method_timeout != NULL || wait_for_ready != NULL) { + if (method_params_table != NULL) { + const method_parameters *method_params = + grpc_method_config_table_get(method_params_table, calld->path); + if (method_params != NULL) { + const bool have_method_timeout = + gpr_time_cmp(method_params->timeout, gpr_time_0(GPR_TIMESPAN)) != 0; + if (have_method_timeout || + method_params->wait_for_ready != WAIT_FOR_READY_UNSET) { gpr_mu_lock(&calld->mu); - if (per_method_timeout != NULL) { - gpr_timespec per_method_deadline = - gpr_time_add(calld->call_start_time, *per_method_timeout); + if (have_method_timeout) { + const gpr_timespec per_method_deadline = + gpr_time_add(calld->call_start_time, method_params->timeout); if (gpr_time_cmp(per_method_deadline, calld->deadline) < 0) { calld->deadline = per_method_deadline; // Reset deadline timer. grpc_deadline_state_reset(exec_ctx, elem, calld->deadline); } } - if (wait_for_ready != NULL) { + if (method_params->wait_for_ready != WAIT_FOR_READY_UNSET) { calld->wait_for_ready_from_service_config = - *wait_for_ready ? WAIT_FOR_READY_TRUE : WAIT_FOR_READY_FALSE; + method_params->wait_for_ready; } gpr_mu_unlock(&calld->mu); } } - grpc_method_config_table_unref(method_config_table); + grpc_mdstr_hash_table_unref(method_params_table); } } GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "read_service_config"); @@ -917,29 +967,25 @@ static grpc_error *cc_init_call_elem(grpc_exec_ctx *exec_ctx, gpr_mu_lock(&chand->mu); if (chand->lb_policy != NULL) { // We already have a resolver result, so check for service config. - if (chand->method_config_table != NULL) { - grpc_method_config_table *method_config_table = - grpc_method_config_table_ref(chand->method_config_table); + if (chand->method_params_table != NULL) { + grpc_mdstr_hash_table *method_params_table = + grpc_mdstr_hash_table_ref(chand->method_params_table); gpr_mu_unlock(&chand->mu); - grpc_method_config *method_config = - grpc_method_config_table_get_method_config(method_config_table, - args->path); - if (method_config != NULL) { - const gpr_timespec *per_method_timeout = - grpc_method_config_get_timeout(method_config); - if (per_method_timeout != NULL) { + method_parameters *method_params = + grpc_method_config_table_get(method_params_table, args->path); + if (method_params != NULL) { + if (gpr_time_cmp(method_params->timeout, + gpr_time_0(GPR_CLOCK_MONOTONIC)) != 0) { gpr_timespec per_method_deadline = - gpr_time_add(calld->call_start_time, *per_method_timeout); + gpr_time_add(calld->call_start_time, method_params->timeout); calld->deadline = gpr_time_min(calld->deadline, per_method_deadline); } - const bool *wait_for_ready = - grpc_method_config_get_wait_for_ready(method_config); - if (wait_for_ready != NULL) { + if (method_params->wait_for_ready != WAIT_FOR_READY_UNSET) { calld->wait_for_ready_from_service_config = - *wait_for_ready ? WAIT_FOR_READY_TRUE : WAIT_FOR_READY_FALSE; + method_params->wait_for_ready; } } - grpc_method_config_table_unref(method_config_table); + grpc_mdstr_hash_table_unref(method_params_table); } else { gpr_mu_unlock(&chand->mu); } diff --git a/src/core/ext/client_config/client_channel.h b/src/core/ext/client_channel/client_channel.h index abb5ac4d87..ab5a84fdfb 100644 --- a/src/core/ext/client_config/client_channel.h +++ b/src/core/ext/client_channel/client_channel.h @@ -31,11 +31,11 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CHANNEL_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CHANNEL_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_CLIENT_CHANNEL_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_CLIENT_CHANNEL_H -#include "src/core/ext/client_config/client_channel_factory.h" -#include "src/core/ext/client_config/resolver.h" +#include "src/core/ext/client_channel/client_channel_factory.h" +#include "src/core/ext/client_channel/resolver.h" #include "src/core/lib/channel/channel_stack.h" /* A client channel is a channel that begins disconnected, and can connect @@ -61,4 +61,4 @@ void grpc_client_channel_watch_connectivity_state( grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, grpc_connectivity_state *state, grpc_closure *on_complete); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CHANNEL_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_CLIENT_CHANNEL_H */ diff --git a/src/core/ext/client_config/client_channel_factory.c b/src/core/ext/client_channel/client_channel_factory.c index 71c64c0da1..4900832d57 100644 --- a/src/core/ext/client_config/client_channel_factory.c +++ b/src/core/ext/client_channel/client_channel_factory.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/client_channel_factory.h" +#include "src/core/ext/client_channel/client_channel_factory.h" void grpc_client_channel_factory_ref(grpc_client_channel_factory* factory) { factory->vtable->ref(factory); @@ -44,14 +44,14 @@ void grpc_client_channel_factory_unref(grpc_exec_ctx* exec_ctx, grpc_subchannel* grpc_client_channel_factory_create_subchannel( grpc_exec_ctx* exec_ctx, grpc_client_channel_factory* factory, - grpc_subchannel_args* args) { + const grpc_subchannel_args* args) { return factory->vtable->create_subchannel(exec_ctx, factory, args); } grpc_channel* grpc_client_channel_factory_create_channel( grpc_exec_ctx* exec_ctx, grpc_client_channel_factory* factory, const char* target, grpc_client_channel_type type, - grpc_channel_args* args) { + const grpc_channel_args* args) { return factory->vtable->create_client_channel(exec_ctx, factory, target, type, args); } diff --git a/src/core/ext/client_config/client_channel_factory.h b/src/core/ext/client_channel/client_channel_factory.h index 1241b9b781..2b8fc577b3 100644 --- a/src/core/ext/client_config/client_channel_factory.h +++ b/src/core/ext/client_channel/client_channel_factory.h @@ -31,12 +31,12 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CHANNEL_FACTORY_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CHANNEL_FACTORY_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H #include <grpc/impl/codegen/grpc_types.h> -#include "src/core/ext/client_config/subchannel.h" +#include "src/core/ext/client_channel/subchannel.h" #include "src/core/lib/channel/channel_stack.h" typedef struct grpc_client_channel_factory grpc_client_channel_factory; @@ -60,12 +60,12 @@ struct grpc_client_channel_factory_vtable { void (*unref)(grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory); grpc_subchannel *(*create_subchannel)(grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory, - grpc_subchannel_args *args); + const grpc_subchannel_args *args); grpc_channel *(*create_client_channel)(grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory, const char *target, grpc_client_channel_type type, - grpc_channel_args *args); + const grpc_channel_args *args); }; void grpc_client_channel_factory_ref(grpc_client_channel_factory *factory); @@ -75,11 +75,12 @@ void grpc_client_channel_factory_unref(grpc_exec_ctx *exec_ctx, /** Create a new grpc_subchannel */ grpc_subchannel *grpc_client_channel_factory_create_subchannel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory, - grpc_subchannel_args *args); + const grpc_subchannel_args *args); /** Create a new grpc_channel */ grpc_channel *grpc_client_channel_factory_create_channel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *factory, - const char *target, grpc_client_channel_type type, grpc_channel_args *args); + const char *target, grpc_client_channel_type type, + const grpc_channel_args *args); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_CLIENT_CHANNEL_FACTORY_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H */ diff --git a/src/core/ext/client_config/client_config_plugin.c b/src/core/ext/client_channel/client_channel_plugin.c index dc3629d383..a3e5079843 100644 --- a/src/core/ext/client_config/client_config_plugin.c +++ b/src/core/ext/client_channel/client_channel_plugin.c @@ -37,10 +37,10 @@ #include <grpc/support/alloc.h> -#include "src/core/ext/client_config/client_channel.h" -#include "src/core/ext/client_config/lb_policy_registry.h" -#include "src/core/ext/client_config/resolver_registry.h" -#include "src/core/ext/client_config/subchannel_index.h" +#include "src/core/ext/client_channel/client_channel.h" +#include "src/core/ext/client_channel/lb_policy_registry.h" +#include "src/core/ext/client_channel/resolver_registry.h" +#include "src/core/ext/client_channel/subchannel_index.h" #include "src/core/lib/surface/channel_init.h" static bool append_filter(grpc_channel_stack_builder *builder, void *arg) { @@ -73,7 +73,7 @@ static bool set_default_host_if_unset(grpc_channel_stack_builder *builder, return true; } -void grpc_client_config_init(void) { +void grpc_client_channel_init(void) { grpc_lb_policy_registry_init(); grpc_resolver_registry_init(); grpc_subchannel_index_init(); @@ -83,7 +83,7 @@ void grpc_client_config_init(void) { (void *)&grpc_client_channel_filter); } -void grpc_client_config_shutdown(void) { +void grpc_client_channel_shutdown(void) { grpc_subchannel_index_shutdown(); grpc_channel_init_shutdown(); grpc_resolver_registry_shutdown(); diff --git a/src/core/ext/client_config/connector.c b/src/core/ext/client_channel/connector.c index 5b629ed5fb..0582e5b372 100644 --- a/src/core/ext/client_config/connector.c +++ b/src/core/ext/client_channel/connector.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/connector.h" +#include "src/core/ext/client_channel/connector.h" grpc_connector* grpc_connector_ref(grpc_connector* connector) { connector->vtable->ref(connector); diff --git a/src/core/ext/client_config/connector.h b/src/core/ext/client_channel/connector.h index ea9d23706e..ed7d5450de 100644 --- a/src/core/ext/client_config/connector.h +++ b/src/core/ext/client_channel/connector.h @@ -31,11 +31,11 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_CONNECTOR_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_CONNECTOR_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_CONNECTOR_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_CONNECTOR_H #include "src/core/lib/channel/channel_stack.h" -#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/transport/transport.h" typedef struct grpc_connector grpc_connector; @@ -49,7 +49,7 @@ typedef struct { /** set of pollsets interested in this connection */ grpc_pollset_set *interested_parties; /** address to connect to */ - const struct sockaddr *addr; + const grpc_resolved_address *addr; size_t addr_len; /** initial connect string to send */ gpr_slice initial_connect_string; @@ -89,4 +89,4 @@ void grpc_connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *connector, void grpc_connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *connector); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_CONNECTOR_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_CONNECTOR_H */ diff --git a/src/core/ext/client_config/default_initial_connect_string.c b/src/core/ext/client_channel/default_initial_connect_string.c index a70da4a84a..0b251372fd 100644 --- a/src/core/ext/client_config/default_initial_connect_string.c +++ b/src/core/ext/client_channel/default_initial_connect_string.c @@ -32,8 +32,7 @@ */ #include <grpc/support/slice.h> -#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/resolve_address.h" -void grpc_set_default_initial_connect_string(struct sockaddr **addr, - size_t *addr_len, +void grpc_set_default_initial_connect_string(grpc_resolved_address **addr, gpr_slice *initial_str) {} diff --git a/src/core/ext/client_config/http_connect_handshaker.c b/src/core/ext/client_channel/http_connect_handshaker.c index fda1df173e..ea2cbbdd97 100644 --- a/src/core/ext/client_config/http_connect_handshaker.c +++ b/src/core/ext/client_channel/http_connect_handshaker.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/http_connect_handshaker.h" +#include "src/core/ext/client_channel/http_connect_handshaker.h" #include <string.h> @@ -40,7 +40,7 @@ #include <grpc/support/slice_buffer.h> #include <grpc/support/string_util.h> -#include "src/core/ext/client_config/uri_parser.h" +#include "src/core/ext/client_channel/uri_parser.h" #include "src/core/lib/http/format_request.h" #include "src/core/lib/http/parser.h" #include "src/core/lib/iomgr/timer.h" diff --git a/src/core/ext/client_config/http_connect_handshaker.h b/src/core/ext/client_channel/http_connect_handshaker.h index 1fc3948267..c689df2b2b 100644 --- a/src/core/ext/client_config/http_connect_handshaker.h +++ b/src/core/ext/client_channel/http_connect_handshaker.h @@ -31,8 +31,8 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_HTTP_CONNECT_HANDSHAKER_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_HTTP_CONNECT_HANDSHAKER_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H #include "src/core/lib/channel/handshaker.h" @@ -44,4 +44,4 @@ grpc_handshaker* grpc_http_connect_handshaker_create(const char* proxy_server, /// Caller takes ownership of result. char* grpc_get_http_proxy_server(); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_HTTP_CONNECT_HANDSHAKER_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H */ diff --git a/src/core/ext/client_config/initial_connect_string.c b/src/core/ext/client_channel/initial_connect_string.c index 41580d2106..fb1493d77d 100644 --- a/src/core/ext/client_config/initial_connect_string.c +++ b/src/core/ext/client_channel/initial_connect_string.c @@ -31,13 +31,12 @@ * */ -#include "src/core/ext/client_config/initial_connect_string.h" +#include "src/core/ext/client_channel/initial_connect_string.h" #include <stddef.h> -extern void grpc_set_default_initial_connect_string(struct sockaddr **addr, - size_t *addr_len, - gpr_slice *initial_str); +extern void grpc_set_default_initial_connect_string( + grpc_resolved_address **addr, gpr_slice *initial_str); static grpc_set_initial_connect_string_func g_set_initial_connect_string_func = grpc_set_default_initial_connect_string; @@ -47,7 +46,7 @@ void grpc_test_set_initial_connect_string_function( g_set_initial_connect_string_func = func; } -void grpc_set_initial_connect_string(struct sockaddr **addr, size_t *addr_len, +void grpc_set_initial_connect_string(grpc_resolved_address **addr, gpr_slice *initial_str) { - g_set_initial_connect_string_func(addr, addr_len, initial_str); + g_set_initial_connect_string_func(addr, initial_str); } diff --git a/src/core/ext/client_config/initial_connect_string.h b/src/core/ext/client_channel/initial_connect_string.h index 06f0767832..68adb0373c 100644 --- a/src/core/ext/client_config/initial_connect_string.h +++ b/src/core/ext/client_channel/initial_connect_string.h @@ -31,20 +31,20 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_INITIAL_CONNECT_STRING_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_INITIAL_CONNECT_STRING_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_INITIAL_CONNECT_STRING_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_INITIAL_CONNECT_STRING_H #include <grpc/support/slice.h> -#include "src/core/lib/iomgr/sockaddr.h" -typedef void (*grpc_set_initial_connect_string_func)(struct sockaddr **addr, - size_t *addr_len, - gpr_slice *initial_str); +#include "src/core/lib/iomgr/resolve_address.h" + +typedef void (*grpc_set_initial_connect_string_func)( + grpc_resolved_address **addr, gpr_slice *initial_str); void grpc_test_set_initial_connect_string_function( grpc_set_initial_connect_string_func func); /** Set a string to be sent once connected. Optionally reset addr. */ -void grpc_set_initial_connect_string(struct sockaddr **addr, size_t *addr_len, +void grpc_set_initial_connect_string(grpc_resolved_address **addr, gpr_slice *connect_string); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_INITIAL_CONNECT_STRING_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_INITIAL_CONNECT_STRING_H */ diff --git a/src/core/ext/client_config/lb_policy.c b/src/core/ext/client_channel/lb_policy.c index 46391272a6..45ee72e2f0 100644 --- a/src/core/ext/client_config/lb_policy.c +++ b/src/core/ext/client_channel/lb_policy.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/lb_policy.h" +#include "src/core/ext/client_channel/lb_policy.h" #define WEAK_REF_BITS 16 diff --git a/src/core/ext/client_config/lb_policy.h b/src/core/ext/client_channel/lb_policy.h index de424cd105..54ad779792 100644 --- a/src/core/ext/client_config/lb_policy.h +++ b/src/core/ext/client_channel/lb_policy.h @@ -31,10 +31,11 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_LB_POLICY_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_LB_POLICY_H -#include "src/core/ext/client_config/subchannel.h" +#include "src/core/ext/client_channel/subchannel.h" +#include "src/core/lib/iomgr/polling_entity.h" #include "src/core/lib/transport/connectivity_state.h" /** A load balancing policy: specified by a vtable and a struct (which @@ -192,4 +193,4 @@ grpc_connectivity_state grpc_lb_policy_check_connectivity( grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, grpc_error **connectivity_error); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_LB_POLICY_H */ diff --git a/src/core/ext/client_config/lb_policy_factory.c b/src/core/ext/client_channel/lb_policy_factory.c index c17af91a09..8a474c8818 100644 --- a/src/core/ext/client_config/lb_policy_factory.c +++ b/src/core/ext/client_channel/lb_policy_factory.c @@ -36,21 +36,22 @@ #include <grpc/support/alloc.h> #include <grpc/support/string_util.h> -#include "src/core/ext/client_config/lb_policy_factory.h" +#include "src/core/ext/client_channel/lb_policy_factory.h" -grpc_lb_addresses* grpc_lb_addresses_create(size_t num_addresses) { +grpc_lb_addresses* grpc_lb_addresses_create( + size_t num_addresses, const grpc_lb_user_data_vtable* user_data_vtable) { grpc_lb_addresses* addresses = gpr_malloc(sizeof(grpc_lb_addresses)); addresses->num_addresses = num_addresses; + addresses->user_data_vtable = user_data_vtable; const size_t addresses_size = sizeof(grpc_lb_address) * num_addresses; addresses->addresses = gpr_malloc(addresses_size); memset(addresses->addresses, 0, addresses_size); return addresses; } -grpc_lb_addresses* grpc_lb_addresses_copy(grpc_lb_addresses* addresses, - void* (*user_data_copy)(void*)) { - grpc_lb_addresses* new_addresses = - grpc_lb_addresses_create(addresses->num_addresses); +grpc_lb_addresses* grpc_lb_addresses_copy(const grpc_lb_addresses* addresses) { + grpc_lb_addresses* new_addresses = grpc_lb_addresses_create( + addresses->num_addresses, addresses->user_data_vtable); memcpy(new_addresses->addresses, addresses->addresses, sizeof(grpc_lb_address) * addresses->num_addresses); for (size_t i = 0; i < addresses->num_addresses; ++i) { @@ -58,9 +59,9 @@ grpc_lb_addresses* grpc_lb_addresses_copy(grpc_lb_addresses* addresses, new_addresses->addresses[i].balancer_name = gpr_strdup(new_addresses->addresses[i].balancer_name); } - if (user_data_copy != NULL) { - new_addresses->addresses[i].user_data = - user_data_copy(new_addresses->addresses[i].user_data); + if (new_addresses->addresses[i].user_data != NULL) { + new_addresses->addresses[i].user_data = addresses->user_data_vtable->copy( + new_addresses->addresses[i].user_data); } } return new_addresses; @@ -71,6 +72,7 @@ void grpc_lb_addresses_set_address(grpc_lb_addresses* addresses, size_t index, bool is_balancer, char* balancer_name, void* user_data) { GPR_ASSERT(index < addresses->num_addresses); + if (user_data != NULL) GPR_ASSERT(addresses->user_data_vtable != NULL); grpc_lb_address* target = &addresses->addresses[index]; memcpy(target->address.addr, address, address_len); target->address.len = address_len; @@ -79,18 +81,70 @@ void grpc_lb_addresses_set_address(grpc_lb_addresses* addresses, size_t index, target->user_data = user_data; } -void grpc_lb_addresses_destroy(grpc_lb_addresses* addresses, - void (*user_data_destroy)(void*)) { +int grpc_lb_addresses_cmp(const grpc_lb_addresses* addresses1, + const grpc_lb_addresses* addresses2) { + if (addresses1->num_addresses > addresses2->num_addresses) return 1; + if (addresses1->num_addresses < addresses2->num_addresses) return -1; + if (addresses1->user_data_vtable > addresses2->user_data_vtable) return 1; + if (addresses1->user_data_vtable < addresses2->user_data_vtable) return -1; + for (size_t i = 0; i < addresses1->num_addresses; ++i) { + const grpc_lb_address* target1 = &addresses1->addresses[i]; + const grpc_lb_address* target2 = &addresses2->addresses[i]; + if (target1->address.len > target2->address.len) return 1; + if (target1->address.len < target2->address.len) return -1; + int retval = memcmp(target1->address.addr, target2->address.addr, + target1->address.len); + if (retval != 0) return retval; + if (target1->is_balancer > target2->is_balancer) return 1; + if (target1->is_balancer < target2->is_balancer) return -1; + const char* balancer_name1 = + target1->balancer_name != NULL ? target1->balancer_name : ""; + const char* balancer_name2 = + target2->balancer_name != NULL ? target2->balancer_name : ""; + retval = strcmp(balancer_name1, balancer_name2); + if (retval != 0) return retval; + if (addresses1->user_data_vtable != NULL) { + retval = addresses1->user_data_vtable->cmp(target1->user_data, + target2->user_data); + if (retval != 0) return retval; + } + } + return 0; +} + +void grpc_lb_addresses_destroy(grpc_lb_addresses* addresses) { for (size_t i = 0; i < addresses->num_addresses; ++i) { gpr_free(addresses->addresses[i].balancer_name); - if (user_data_destroy != NULL) { - user_data_destroy(addresses->addresses[i].user_data); + if (addresses->addresses[i].user_data != NULL) { + addresses->user_data_vtable->destroy(addresses->addresses[i].user_data); } } gpr_free(addresses->addresses); gpr_free(addresses); } +static void* lb_addresses_copy(void* addresses) { + return grpc_lb_addresses_copy(addresses); +} +static void lb_addresses_destroy(void* addresses) { + grpc_lb_addresses_destroy(addresses); +} +static int lb_addresses_cmp(void* addresses1, void* addresses2) { + return grpc_lb_addresses_cmp(addresses1, addresses2); +} +static const grpc_arg_pointer_vtable lb_addresses_arg_vtable = { + lb_addresses_copy, lb_addresses_destroy, lb_addresses_cmp}; + +grpc_arg grpc_lb_addresses_create_channel_arg( + const grpc_lb_addresses* addresses) { + grpc_arg arg; + arg.type = GRPC_ARG_POINTER; + arg.key = GRPC_ARG_LB_ADDRESSES; + arg.value.pointer.p = (void*)addresses; + arg.value.pointer.vtable = &lb_addresses_arg_vtable; + return arg; +} + void grpc_lb_policy_factory_ref(grpc_lb_policy_factory* factory) { factory->vtable->ref(factory); } diff --git a/src/core/ext/client_config/lb_policy_factory.h b/src/core/ext/client_channel/lb_policy_factory.h index ade55704f2..e2b8080a32 100644 --- a/src/core/ext/client_config/lb_policy_factory.h +++ b/src/core/ext/client_channel/lb_policy_factory.h @@ -31,11 +31,11 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_FACTORY_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_FACTORY_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_LB_POLICY_FACTORY_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_LB_POLICY_FACTORY_H -#include "src/core/ext/client_config/client_channel_factory.h" -#include "src/core/ext/client_config/lb_policy.h" +#include "src/core/ext/client_channel/client_channel_factory.h" +#include "src/core/ext/client_channel/lb_policy.h" #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/resolve_address.h" @@ -59,19 +59,26 @@ typedef struct grpc_lb_address { void *user_data; } grpc_lb_address; +typedef struct grpc_lb_user_data_vtable { + void *(*copy)(void *); + void (*destroy)(void *); + int (*cmp)(void *, void *); +} grpc_lb_user_data_vtable; + typedef struct grpc_lb_addresses { size_t num_addresses; grpc_lb_address *addresses; + const grpc_lb_user_data_vtable *user_data_vtable; } grpc_lb_addresses; /** Returns a grpc_addresses struct with enough space for - * \a num_addresses addresses. */ -grpc_lb_addresses *grpc_lb_addresses_create(size_t num_addresses); + \a num_addresses addresses. The \a user_data_vtable argument may be + NULL if no user data will be added. */ +grpc_lb_addresses *grpc_lb_addresses_create( + size_t num_addresses, const grpc_lb_user_data_vtable *user_data_vtable); -/** Creates a copy of \a addresses. If \a user_data_copy is not NULL, - * it will be invoked to copy the \a user_data field of each address. */ -grpc_lb_addresses *grpc_lb_addresses_copy(grpc_lb_addresses *addresses, - void *(*user_data_copy)(void *)); +/** Creates a copy of \a addresses. */ +grpc_lb_addresses *grpc_lb_addresses_copy(const grpc_lb_addresses *addresses); /** Sets the value of the address at index \a index of \a addresses. * \a address is a socket address of length \a address_len. @@ -81,20 +88,21 @@ void grpc_lb_addresses_set_address(grpc_lb_addresses *addresses, size_t index, bool is_balancer, char *balancer_name, void *user_data); -/** Destroys \a addresses. If \a user_data_destroy is not NULL, it will - * be invoked to destroy the \a user_data field of each address. */ -void grpc_lb_addresses_destroy(grpc_lb_addresses *addresses, - void (*user_data_destroy)(void *)); +/** Compares \a addresses1 and \a addresses2. */ +int grpc_lb_addresses_cmp(const grpc_lb_addresses *addresses1, + const grpc_lb_addresses *addresses2); + +/** Destroys \a addresses. */ +void grpc_lb_addresses_destroy(grpc_lb_addresses *addresses); + +/** Returns a channel arg containing \a addresses. */ +grpc_arg grpc_lb_addresses_create_channel_arg( + const grpc_lb_addresses *addresses); /** Arguments passed to LB policies. */ -/* TODO(roth, ctiller): Consider replacing this struct with - grpc_channel_args. See comment in resolver_result.h for details. */ typedef struct grpc_lb_policy_args { - const char *server_name; - grpc_lb_addresses *addresses; grpc_client_channel_factory *client_channel_factory; - /* Can be used to pass implementation-specific parameters to the LB policy. */ - grpc_channel_args *additional_args; + grpc_channel_args *args; } grpc_lb_policy_args; struct grpc_lb_policy_factory_vtable { @@ -118,4 +126,4 @@ grpc_lb_policy *grpc_lb_policy_factory_create_lb_policy( grpc_exec_ctx *exec_ctx, grpc_lb_policy_factory *factory, grpc_lb_policy_args *args); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_FACTORY_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_LB_POLICY_FACTORY_H */ diff --git a/src/core/ext/client_config/lb_policy_registry.c b/src/core/ext/client_channel/lb_policy_registry.c index a23643ecc6..f46a721f9d 100644 --- a/src/core/ext/client_config/lb_policy_registry.c +++ b/src/core/ext/client_channel/lb_policy_registry.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/lb_policy_registry.h" +#include "src/core/ext/client_channel/lb_policy_registry.h" #include <string.h> diff --git a/src/core/ext/client_config/lb_policy_registry.h b/src/core/ext/client_channel/lb_policy_registry.h index 92f38d6de6..21c468e021 100644 --- a/src/core/ext/client_config/lb_policy_registry.h +++ b/src/core/ext/client_channel/lb_policy_registry.h @@ -31,10 +31,10 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_REGISTRY_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_REGISTRY_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H -#include "src/core/ext/client_config/lb_policy_factory.h" +#include "src/core/ext/client_channel/lb_policy_factory.h" #include "src/core/lib/iomgr/exec_ctx.h" /** Initialize the registry and set \a default_factory as the factory to be @@ -52,4 +52,4 @@ void grpc_register_lb_policy(grpc_lb_policy_factory *factory); grpc_lb_policy *grpc_lb_policy_create(grpc_exec_ctx *exec_ctx, const char *name, grpc_lb_policy_args *args); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_LB_POLICY_REGISTRY_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H */ diff --git a/src/core/ext/client_config/parse_address.c b/src/core/ext/client_channel/parse_address.c index 8b4abe24a6..b1d55ad0f5 100644 --- a/src/core/ext/client_config/parse_address.c +++ b/src/core/ext/client_channel/parse_address.c @@ -31,11 +31,12 @@ * */ -#include "src/core/ext/client_config/parse_address.h" +#include "src/core/ext/client_channel/parse_address.h" +#include "src/core/lib/iomgr/sockaddr.h" #include <stdio.h> #include <string.h> -#ifdef GPR_HAVE_UNIX_SOCKET +#ifdef GRPC_HAVE_UNIX_SOCKET #include <sys/un.h> #endif @@ -44,33 +45,39 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> -#ifdef GPR_HAVE_UNIX_SOCKET -int parse_unix(grpc_uri *uri, struct sockaddr_storage *addr, size_t *len) { - struct sockaddr_un *un = (struct sockaddr_un *)addr; +#ifdef GRPC_HAVE_UNIX_SOCKET + +int parse_unix(grpc_uri *uri, grpc_resolved_address *resolved_addr) { + struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr; un->sun_family = AF_UNIX; strcpy(un->sun_path, uri->path); - *len = strlen(un->sun_path) + sizeof(un->sun_family) + 1; + resolved_addr->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1; return 1; } -#endif -int parse_ipv4(grpc_uri *uri, struct sockaddr_storage *addr, size_t *len) { +#else /* GRPC_HAVE_UNIX_SOCKET */ + +int parse_unix(grpc_uri *uri, grpc_resolved_address *resolved_addr) { abort(); } + +#endif /* GRPC_HAVE_UNIX_SOCKET */ + +int parse_ipv4(grpc_uri *uri, grpc_resolved_address *resolved_addr) { const char *host_port = uri->path; char *host; char *port; int port_num; int result = 0; - struct sockaddr_in *in = (struct sockaddr_in *)addr; + struct sockaddr_in *in = (struct sockaddr_in *)resolved_addr->addr; if (*host_port == '/') ++host_port; if (!gpr_split_host_port(host_port, &host, &port)) { return 0; } - memset(in, 0, sizeof(*in)); - *len = sizeof(*in); + memset(resolved_addr, 0, sizeof(grpc_resolved_address)); + resolved_addr->len = sizeof(struct sockaddr_in); in->sin_family = AF_INET; if (inet_pton(AF_INET, host, &in->sin_addr) == 0) { gpr_log(GPR_ERROR, "invalid ipv4 address: '%s'", host); @@ -96,13 +103,13 @@ done: return result; } -int parse_ipv6(grpc_uri *uri, struct sockaddr_storage *addr, size_t *len) { +int parse_ipv6(grpc_uri *uri, grpc_resolved_address *resolved_addr) { const char *host_port = uri->path; char *host; char *port; int port_num; int result = 0; - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)resolved_addr->addr; if (*host_port == '/') ++host_port; if (!gpr_split_host_port(host_port, &host, &port)) { @@ -110,7 +117,7 @@ int parse_ipv6(grpc_uri *uri, struct sockaddr_storage *addr, size_t *len) { } memset(in6, 0, sizeof(*in6)); - *len = sizeof(*in6); + resolved_addr->len = sizeof(*in6); in6->sin6_family = AF_INET6; if (inet_pton(AF_INET6, host, &in6->sin6_addr) == 0) { gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host); diff --git a/src/core/ext/client_config/parse_address.h b/src/core/ext/client_channel/parse_address.h index 74c86f4d93..bf99c5298a 100644 --- a/src/core/ext/client_config/parse_address.h +++ b/src/core/ext/client_channel/parse_address.h @@ -31,26 +31,24 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_PARSE_ADDRESS_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_PARSE_ADDRESS_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_PARSE_ADDRESS_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_PARSE_ADDRESS_H #include <stddef.h> -#include "src/core/ext/client_config/uri_parser.h" -#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/ext/client_channel/uri_parser.h" +#include "src/core/lib/iomgr/resolve_address.h" -#ifdef GPR_HAVE_UNIX_SOCKET /** Populate \a addr and \a len from \a uri, whose path is expected to contain a * unix socket path. Returns true upon success. */ -int parse_unix(grpc_uri *uri, struct sockaddr_storage *addr, size_t *len); -#endif +int parse_unix(grpc_uri *uri, grpc_resolved_address *resolved_addr); /** Populate /a addr and \a len from \a uri, whose path is expected to contain a * host:port pair. Returns true upon success. */ -int parse_ipv4(grpc_uri *uri, struct sockaddr_storage *addr, size_t *len); +int parse_ipv4(grpc_uri *uri, grpc_resolved_address *resolved_addr); /** Populate /a addr and \a len from \a uri, whose path is expected to contain a * host:port pair. Returns true upon success. */ -int parse_ipv6(grpc_uri *uri, struct sockaddr_storage *addr, size_t *len); +int parse_ipv6(grpc_uri *uri, grpc_resolved_address *resolved_addr); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_PARSE_ADDRESS_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_PARSE_ADDRESS_H */ diff --git a/src/core/ext/client_config/resolver.c b/src/core/ext/client_channel/resolver.c index 7534ea62af..2ae4fe862e 100644 --- a/src/core/ext/client_config/resolver.c +++ b/src/core/ext/client_channel/resolver.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/resolver.h" +#include "src/core/ext/client_channel/resolver.h" void grpc_resolver_init(grpc_resolver *resolver, const grpc_resolver_vtable *vtable) { @@ -76,7 +76,6 @@ void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, } void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **result, - grpc_closure *on_complete) { + grpc_channel_args **result, grpc_closure *on_complete) { resolver->vtable->next(exec_ctx, resolver, result, on_complete); } diff --git a/src/core/ext/client_config/resolver.h b/src/core/ext/client_channel/resolver.h index 88ac262d51..96ece92b9d 100644 --- a/src/core/ext/client_config/resolver.h +++ b/src/core/ext/client_channel/resolver.h @@ -31,18 +31,16 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_H -#include "src/core/ext/client_config/resolver_result.h" -#include "src/core/ext/client_config/subchannel.h" +#include "src/core/ext/client_channel/subchannel.h" #include "src/core/lib/iomgr/iomgr.h" typedef struct grpc_resolver grpc_resolver; typedef struct grpc_resolver_vtable grpc_resolver_vtable; -/** grpc_resolver provides grpc_resolver_result objects to grpc_channel - objects */ +/** \a grpc_resolver provides \a grpc_channel_args objects to its caller */ struct grpc_resolver { const grpc_resolver_vtable *vtable; gpr_refcount refs; @@ -53,7 +51,7 @@ struct grpc_resolver_vtable { void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); void (*channel_saw_error)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); void (*next)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **result, grpc_closure *on_complete); + grpc_channel_args **result, grpc_closure *on_complete); }; #ifdef GRPC_RESOLVER_REFCOUNT_DEBUG @@ -81,14 +79,12 @@ void grpc_resolver_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); -/** Get the next client config. Called by the channel to fetch a new - configuration. Expected to set *result with a new configuration, - and then schedule on_complete for execution. +/** Get the next result from the resolver. Expected to set \a *result with + new channel args and then schedule \a on_complete for execution. - If resolution is fatally broken, set *result to NULL and - schedule on_complete. */ + If resolution is fatally broken, set \a *result to NULL and + schedule \a on_complete. */ void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **result, - grpc_closure *on_complete); + grpc_channel_args **result, grpc_closure *on_complete); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_H */ diff --git a/src/core/ext/client_config/resolver_factory.c b/src/core/ext/client_channel/resolver_factory.c index 67832dcf59..7c3d644257 100644 --- a/src/core/ext/client_config/resolver_factory.c +++ b/src/core/ext/client_channel/resolver_factory.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/resolver_factory.h" +#include "src/core/ext/client_channel/resolver_factory.h" void grpc_resolver_factory_ref(grpc_resolver_factory* factory) { factory->vtable->ref(factory); diff --git a/src/core/ext/client_config/resolver_factory.h b/src/core/ext/client_channel/resolver_factory.h index 9ec5b9a70e..4da42e84d2 100644 --- a/src/core/ext/client_config/resolver_factory.h +++ b/src/core/ext/client_channel/resolver_factory.h @@ -31,23 +31,24 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_FACTORY_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_FACTORY_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_FACTORY_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_FACTORY_H -#include "src/core/ext/client_config/client_channel_factory.h" -#include "src/core/ext/client_config/resolver.h" -#include "src/core/ext/client_config/uri_parser.h" +#include "src/core/ext/client_channel/client_channel_factory.h" +#include "src/core/ext/client_channel/resolver.h" +#include "src/core/ext/client_channel/uri_parser.h" typedef struct grpc_resolver_factory grpc_resolver_factory; typedef struct grpc_resolver_factory_vtable grpc_resolver_factory_vtable; -/** grpc_resolver provides grpc_resolver_result objects to grpc_channel - objects */ struct grpc_resolver_factory { const grpc_resolver_factory_vtable *vtable; }; -typedef struct grpc_resolver_args { grpc_uri *uri; } grpc_resolver_args; +typedef struct grpc_resolver_args { + grpc_uri *uri; + const grpc_channel_args *args; +} grpc_resolver_args; struct grpc_resolver_factory_vtable { void (*ref)(grpc_resolver_factory *factory); @@ -76,4 +77,4 @@ grpc_resolver *grpc_resolver_factory_create_resolver( char *grpc_resolver_factory_get_default_authority( grpc_resolver_factory *factory, grpc_uri *uri); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_FACTORY_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_FACTORY_H */ diff --git a/src/core/ext/client_config/resolver_registry.c b/src/core/ext/client_channel/resolver_registry.c index bd5c683878..d0f0fc3f33 100644 --- a/src/core/ext/client_config/resolver_registry.c +++ b/src/core/ext/client_channel/resolver_registry.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/resolver_registry.h" +#include "src/core/ext/client_channel/resolver_registry.h" #include <string.h> @@ -55,7 +55,7 @@ void grpc_resolver_registry_shutdown(void) { grpc_resolver_factory_unref(g_all_of_the_resolvers[i]); } // FIXME(ctiller): this should live in grpc_resolver_registry_init, - // however that would have the client_config plugin call this AFTER we start + // however that would have the client_channel plugin call this AFTER we start // registering resolvers from third party plugins, and so they'd never show // up. // We likely need some kind of dependency system for plugins.... what form @@ -131,14 +131,16 @@ static grpc_resolver_factory *resolve_factory(const char *target, return factory; } -grpc_resolver *grpc_resolver_create(const char *target) { +grpc_resolver *grpc_resolver_create(const char *target, + const grpc_channel_args *args) { grpc_uri *uri = NULL; grpc_resolver_factory *factory = resolve_factory(target, &uri); grpc_resolver *resolver; - grpc_resolver_args args; - memset(&args, 0, sizeof(args)); - args.uri = uri; - resolver = grpc_resolver_factory_create_resolver(factory, &args); + grpc_resolver_args resolver_args; + memset(&resolver_args, 0, sizeof(resolver_args)); + resolver_args.uri = uri; + resolver_args.args = args; + resolver = grpc_resolver_factory_create_resolver(factory, &resolver_args); grpc_uri_destroy(uri); return resolver; } diff --git a/src/core/ext/client_config/resolver_registry.h b/src/core/ext/client_channel/resolver_registry.h index 4c6279b978..2a95a669f0 100644 --- a/src/core/ext/client_config/resolver_registry.h +++ b/src/core/ext/client_channel/resolver_registry.h @@ -31,10 +31,10 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_REGISTRY_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_REGISTRY_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_REGISTRY_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_REGISTRY_H -#include "src/core/ext/client_config/resolver_factory.h" +#include "src/core/ext/client_channel/resolver_factory.h" void grpc_resolver_registry_init(); void grpc_resolver_registry_shutdown(void); @@ -57,8 +57,11 @@ void grpc_register_resolver_type(grpc_resolver_factory *factory); was not NULL). If a resolver factory was found, use it to instantiate a resolver and return it. - If a resolver factory was not found, return NULL. */ -grpc_resolver *grpc_resolver_create(const char *target); + If a resolver factory was not found, return NULL. + \a args is a set of channel arguments to be included in the result + (typically the set of arguments passed in from the client API). */ +grpc_resolver *grpc_resolver_create(const char *target, + const grpc_channel_args *args); /** Find a resolver factory given a name and return an (owned-by-the-caller) * reference to it */ @@ -68,4 +71,4 @@ grpc_resolver_factory *grpc_resolver_factory_lookup(const char *name); representing the default authority to pass from a client. */ char *grpc_get_default_authority(const char *target); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_REGISTRY_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_RESOLVER_REGISTRY_H */ diff --git a/src/core/ext/client_config/subchannel.c b/src/core/ext/client_channel/subchannel.c index c531b3b1a8..ab6aede23a 100644 --- a/src/core/ext/client_config/subchannel.c +++ b/src/core/ext/client_channel/subchannel.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/subchannel.h" +#include "src/core/ext/client_channel/subchannel.h" #include <limits.h> #include <string.h> @@ -39,9 +39,9 @@ #include <grpc/support/alloc.h> #include <grpc/support/avl.h> -#include "src/core/ext/client_config/client_channel.h" -#include "src/core/ext/client_config/initial_connect_string.h" -#include "src/core/ext/client_config/subchannel_index.h" +#include "src/core/ext/client_channel/client_channel.h" +#include "src/core/ext/client_channel/initial_connect_string.h" +#include "src/core/ext/client_channel/subchannel_index.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/connected_channel.h" #include "src/core/lib/iomgr/timer.h" @@ -95,8 +95,7 @@ struct grpc_subchannel { /** channel arguments */ grpc_channel_args *args; /** address to connect to */ - struct sockaddr *addr; - size_t addr_len; + grpc_resolved_address *addr; grpc_subchannel_key *key; @@ -298,7 +297,7 @@ void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx, grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx, grpc_connector *connector, - grpc_subchannel_args *args) { + const grpc_subchannel_args *args) { grpc_subchannel_key *key = grpc_subchannel_key_create(connector, args); grpc_subchannel *c = grpc_subchannel_index_find(exec_ctx, key); if (c) { @@ -320,12 +319,11 @@ grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx, } else { c->filters = NULL; } - c->addr = gpr_malloc(args->addr_len); - if (args->addr_len) memcpy(c->addr, args->addr, args->addr_len); + c->addr = gpr_malloc(sizeof(grpc_resolved_address)); + if (args->addr->len) + memcpy(c->addr, args->addr, sizeof(grpc_resolved_address)); c->pollset_set = grpc_pollset_set_create(); - c->addr_len = args->addr_len; - grpc_set_initial_connect_string(&c->addr, &c->addr_len, - &c->initial_connect_string); + grpc_set_initial_connect_string(&c->addr, &c->initial_connect_string); c->args = grpc_channel_args_copy(args->args); c->root_external_state_watcher.next = c->root_external_state_watcher.prev = &c->root_external_state_watcher; @@ -376,7 +374,6 @@ static void continue_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { args.interested_parties = c->pollset_set; args.addr = c->addr; - args.addr_len = c->addr_len; args.deadline = c->next_attempt; args.channel_args = c->args; args.initial_connect_string = c->initial_connect_string; diff --git a/src/core/ext/client_config/subchannel.h b/src/core/ext/client_channel/subchannel.h index f8de26dfd5..5652074074 100644 --- a/src/core/ext/client_config/subchannel.h +++ b/src/core/ext/client_channel/subchannel.h @@ -31,10 +31,10 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_SUBCHANNEL_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_SUBCHANNEL_H -#include "src/core/ext/client_config/connector.h" +#include "src/core/ext/client_channel/connector.h" #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/iomgr/polling_entity.h" #include "src/core/lib/transport/connectivity_state.h" @@ -167,13 +167,12 @@ struct grpc_subchannel_args { /** Server name */ const char *server_name; /** Address to connect to */ - struct sockaddr *addr; - size_t addr_len; + grpc_resolved_address *addr; }; /** create a subchannel given a connector */ grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx, grpc_connector *connector, - grpc_subchannel_args *args); + const grpc_subchannel_args *args); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_SUBCHANNEL_H */ diff --git a/src/core/ext/client_config/subchannel_index.c b/src/core/ext/client_channel/subchannel_index.c index 673f85b8cb..227013a7d7 100644 --- a/src/core/ext/client_config/subchannel_index.c +++ b/src/core/ext/client_channel/subchannel_index.c @@ -31,7 +31,7 @@ // // -#include "src/core/ext/client_config/subchannel_index.h" +#include "src/core/ext/client_channel/subchannel_index.h" #include <stdbool.h> #include <string.h> @@ -73,7 +73,7 @@ static grpc_exec_ctx *current_ctx() { } static grpc_subchannel_key *create_key( - grpc_connector *connector, grpc_subchannel_args *args, + grpc_connector *connector, const grpc_subchannel_args *args, grpc_channel_args *(*copy_channel_args)(const grpc_channel_args *args)) { grpc_subchannel_key *k = gpr_malloc(sizeof(*k)); k->connector = grpc_connector_ref(connector); @@ -87,17 +87,17 @@ static grpc_subchannel_key *create_key( k->args.filters = NULL; } k->args.server_name = gpr_strdup(args->server_name); - k->args.addr_len = args->addr_len; - k->args.addr = gpr_malloc(args->addr_len); - if (k->args.addr_len > 0) { - memcpy(k->args.addr, args->addr, k->args.addr_len); + k->args.addr = gpr_malloc(sizeof(grpc_resolved_address)); + k->args.addr->len = args->addr->len; + if (k->args.addr->len > 0) { + memcpy(k->args.addr, args->addr, sizeof(grpc_resolved_address)); } k->args.args = copy_channel_args(args->args); return k; } -grpc_subchannel_key *grpc_subchannel_key_create(grpc_connector *connector, - grpc_subchannel_args *args) { +grpc_subchannel_key *grpc_subchannel_key_create( + grpc_connector *connector, const grpc_subchannel_args *args) { return create_key(connector, args, grpc_channel_args_normalize); } @@ -109,14 +109,14 @@ static int subchannel_key_compare(grpc_subchannel_key *a, grpc_subchannel_key *b) { int c = GPR_ICMP(a->connector, b->connector); if (c != 0) return c; - c = GPR_ICMP(a->args.addr_len, b->args.addr_len); + c = GPR_ICMP(a->args.addr->len, b->args.addr->len); if (c != 0) return c; c = GPR_ICMP(a->args.filter_count, b->args.filter_count); if (c != 0) return c; c = strcmp(a->args.server_name, b->args.server_name); if (c != 0) return c; - if (a->args.addr_len) { - c = memcmp(a->args.addr, b->args.addr, a->args.addr_len); + if (a->args.addr->len) { + c = memcmp(a->args.addr->addr, b->args.addr->addr, a->args.addr->len); if (c != 0) return c; } if (a->args.filter_count > 0) { diff --git a/src/core/ext/client_config/subchannel_index.h b/src/core/ext/client_channel/subchannel_index.h index 6b8d063855..a67bd5e219 100644 --- a/src/core/ext/client_config/subchannel_index.h +++ b/src/core/ext/client_channel/subchannel_index.h @@ -31,11 +31,11 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_INDEX_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_INDEX_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H -#include "src/core/ext/client_config/connector.h" -#include "src/core/ext/client_config/subchannel.h" +#include "src/core/ext/client_channel/connector.h" +#include "src/core/ext/client_channel/subchannel.h" /** \file Provides an index of active subchannels so that they can be shared amongst channels */ @@ -43,8 +43,8 @@ typedef struct grpc_subchannel_key grpc_subchannel_key; /** Create a key that can be used to uniquely identify a subchannel */ -grpc_subchannel_key *grpc_subchannel_key_create(grpc_connector *con, - grpc_subchannel_args *args); +grpc_subchannel_key *grpc_subchannel_key_create( + grpc_connector *con, const grpc_subchannel_args *args); /** Destroy a subchannel key */ void grpc_subchannel_key_destroy(grpc_exec_ctx *exec_ctx, @@ -74,4 +74,4 @@ void grpc_subchannel_index_init(void); /** Shutdown the subchannel index (global) */ void grpc_subchannel_index_shutdown(void); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_SUBCHANNEL_INDEX_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H */ diff --git a/src/core/ext/client_config/uri_parser.c b/src/core/ext/client_channel/uri_parser.c index 3ca1a58e69..bcb6a1dee4 100644 --- a/src/core/ext/client_config/uri_parser.c +++ b/src/core/ext/client_channel/uri_parser.c @@ -31,7 +31,7 @@ * */ -#include "src/core/ext/client_config/uri_parser.h" +#include "src/core/ext/client_channel/uri_parser.h" #include <string.h> diff --git a/src/core/ext/client_config/uri_parser.h b/src/core/ext/client_channel/uri_parser.h index 875a7cb07c..5fe0e8f35e 100644 --- a/src/core/ext/client_config/uri_parser.h +++ b/src/core/ext/client_channel/uri_parser.h @@ -31,8 +31,8 @@ * */ -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_URI_PARSER_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_URI_PARSER_H +#ifndef GRPC_CORE_EXT_CLIENT_CHANNEL_URI_PARSER_H +#define GRPC_CORE_EXT_CLIENT_CHANNEL_URI_PARSER_H #include <stddef.h> @@ -60,4 +60,4 @@ const char *grpc_uri_get_query_arg(const grpc_uri *uri, const char *key); /** destroy a uri */ void grpc_uri_destroy(grpc_uri *uri); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_URI_PARSER_H */ +#endif /* GRPC_CORE_EXT_CLIENT_CHANNEL_URI_PARSER_H */ diff --git a/src/core/ext/client_config/message_size_filter.c b/src/core/ext/client_config/message_size_filter.c index a435ad5543..eaae75ae81 100644 --- a/src/core/ext/client_config/message_size_filter.c +++ b/src/core/ext/client_config/message_size_filter.c @@ -38,13 +38,51 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> -#include "src/core/ext/client_config/method_config.h" #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/transport/method_config.h" #define DEFAULT_MAX_SEND_MESSAGE_LENGTH -1 // Unlimited. // The protobuf library will (by default) start warning at 100 megs. #define DEFAULT_MAX_RECV_MESSAGE_LENGTH (4 * 1024 * 1024) +typedef struct message_size_limits { + int max_send_size; + int max_recv_size; +} message_size_limits; + +static void* message_size_limits_copy(void* value) { + void* new_value = gpr_malloc(sizeof(message_size_limits)); + memcpy(new_value, value, sizeof(message_size_limits)); + return new_value; +} + +static int message_size_limits_cmp(void* value1, void* value2) { + const message_size_limits* v1 = value1; + const message_size_limits* v2 = value2; + if (v1->max_send_size > v2->max_send_size) return 1; + if (v1->max_send_size < v2->max_send_size) return -1; + if (v1->max_recv_size > v2->max_recv_size) return 1; + if (v1->max_recv_size < v2->max_recv_size) return -1; + return 0; +} + +static const grpc_mdstr_hash_table_vtable message_size_limits_vtable = { + gpr_free, message_size_limits_copy, message_size_limits_cmp}; + +static void* method_config_convert_value( + const grpc_method_config* method_config) { + message_size_limits* value = gpr_malloc(sizeof(message_size_limits)); + const int32_t* max_request_message_bytes = + grpc_method_config_get_max_request_message_bytes(method_config); + value->max_send_size = + max_request_message_bytes != NULL ? *max_request_message_bytes : -1; + const int32_t* max_response_message_bytes = + grpc_method_config_get_max_response_message_bytes(method_config); + value->max_recv_size = + max_response_message_bytes != NULL ? *max_response_message_bytes : -1; + return value; +} + typedef struct call_data { int max_send_size; int max_recv_size; @@ -61,8 +99,8 @@ typedef struct call_data { typedef struct channel_data { int max_send_size; int max_recv_size; - // Method config table. - grpc_method_config_table* method_config_table; + // Maps path names to message_size_limits structs. + grpc_mdstr_hash_table* method_limit_table; } channel_data; // Callback invoked when we receive a message. Here we check the max @@ -132,24 +170,19 @@ static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, // size to the receive limit. calld->max_send_size = chand->max_send_size; calld->max_recv_size = chand->max_recv_size; - if (chand->method_config_table != NULL) { - grpc_method_config* method_config = - grpc_method_config_table_get_method_config(chand->method_config_table, - args->path); - if (method_config != NULL) { - const int32_t* max_request_message_bytes = - grpc_method_config_get_max_request_message_bytes(method_config); - if (max_request_message_bytes != NULL && - (*max_request_message_bytes < calld->max_send_size || + if (chand->method_limit_table != NULL) { + message_size_limits* limits = + grpc_method_config_table_get(chand->method_limit_table, args->path); + if (limits != NULL) { + if (limits->max_send_size >= 0 && + (limits->max_send_size < calld->max_send_size || calld->max_send_size < 0)) { - calld->max_send_size = *max_request_message_bytes; + calld->max_send_size = limits->max_send_size; } - const int32_t* max_response_message_bytes = - grpc_method_config_get_max_response_message_bytes(method_config); - if (max_response_message_bytes != NULL && - (*max_response_message_bytes < calld->max_recv_size || + if (limits->max_recv_size >= 0 && + (limits->max_recv_size < calld->max_recv_size || calld->max_recv_size < 0)) { - calld->max_recv_size = *max_response_message_bytes; + calld->max_recv_size = limits->max_recv_size; } } } @@ -191,8 +224,9 @@ static void init_channel_elem(grpc_exec_ctx* exec_ctx, grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG); if (channel_arg != NULL) { GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); - chand->method_config_table = grpc_method_config_table_ref( - (grpc_method_config_table*)channel_arg->value.pointer.p); + chand->method_limit_table = grpc_method_config_table_convert( + (grpc_method_config_table*)channel_arg->value.pointer.p, + method_config_convert_value, &message_size_limits_vtable); } } @@ -200,7 +234,7 @@ static void init_channel_elem(grpc_exec_ctx* exec_ctx, static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, grpc_channel_element* elem) { channel_data* chand = elem->channel_data; - grpc_method_config_table_unref(chand->method_config_table); + grpc_mdstr_hash_table_unref(chand->method_limit_table); } const grpc_channel_filter grpc_message_size_filter = { diff --git a/src/core/ext/client_config/resolver_result.c b/src/core/ext/client_config/resolver_result.c deleted file mode 100644 index 63480d152b..0000000000 --- a/src/core/ext/client_config/resolver_result.c +++ /dev/null @@ -1,94 +0,0 @@ -// -// 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 "src/core/ext/client_config/resolver_result.h" - -#include <string.h> - -#include <grpc/support/alloc.h> -#include <grpc/support/string_util.h> - -#include "src/core/lib/channel/channel_args.h" - -struct grpc_resolver_result { - gpr_refcount refs; - char* server_name; - grpc_lb_addresses* addresses; - char* lb_policy_name; - grpc_channel_args* lb_policy_args; -}; - -grpc_resolver_result* grpc_resolver_result_create( - const char* server_name, grpc_lb_addresses* addresses, - const char* lb_policy_name, grpc_channel_args* lb_policy_args) { - grpc_resolver_result* result = gpr_malloc(sizeof(*result)); - memset(result, 0, sizeof(*result)); - gpr_ref_init(&result->refs, 1); - result->server_name = gpr_strdup(server_name); - result->addresses = addresses; - result->lb_policy_name = gpr_strdup(lb_policy_name); - result->lb_policy_args = lb_policy_args; - return result; -} - -void grpc_resolver_result_ref(grpc_resolver_result* result) { - gpr_ref(&result->refs); -} - -void grpc_resolver_result_unref(grpc_exec_ctx* exec_ctx, - grpc_resolver_result* result) { - if (gpr_unref(&result->refs)) { - gpr_free(result->server_name); - grpc_lb_addresses_destroy(result->addresses, NULL /* user_data_destroy */); - gpr_free(result->lb_policy_name); - grpc_channel_args_destroy(result->lb_policy_args); - gpr_free(result); - } -} - -const char* grpc_resolver_result_get_server_name(grpc_resolver_result* result) { - return result->server_name; -} - -grpc_lb_addresses* grpc_resolver_result_get_addresses( - grpc_resolver_result* result) { - return result->addresses; -} - -const char* grpc_resolver_result_get_lb_policy_name( - grpc_resolver_result* result) { - return result->lb_policy_name; -} - -grpc_channel_args* grpc_resolver_result_get_lb_policy_args( - grpc_resolver_result* result) { - return result->lb_policy_args; -} diff --git a/src/core/ext/client_config/resolver_result.h b/src/core/ext/client_config/resolver_result.h deleted file mode 100644 index a7ea7c0f4b..0000000000 --- a/src/core/ext/client_config/resolver_result.h +++ /dev/null @@ -1,69 +0,0 @@ -// -// 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. -// - -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H - -#include "src/core/ext/client_config/lb_policy_factory.h" -#include "src/core/lib/iomgr/resolve_address.h" - -// TODO(roth, ctiller): In the long term, we are considering replacing -// the resolver_result data structure with grpc_channel_args. The idea is -// that the resolver will return a set of channel args that contains the -// information that is currently in the resolver_result struct. For -// example, there will be specific args indicating the set of addresses -// and the name of the LB policy to instantiate. Note that if we did -// this, we would probably want to change the data structure of -// grpc_channel_args such to a hash table or AVL or some other data -// structure that does not require linear search to find keys. - -/// Results reported from a grpc_resolver. -typedef struct grpc_resolver_result grpc_resolver_result; - -/// Takes ownership of \a addresses and \a lb_policy_args. -grpc_resolver_result* grpc_resolver_result_create( - const char* server_name, grpc_lb_addresses* addresses, - const char* lb_policy_name, grpc_channel_args* lb_policy_args); - -void grpc_resolver_result_ref(grpc_resolver_result* result); -void grpc_resolver_result_unref(grpc_exec_ctx* exec_ctx, - grpc_resolver_result* result); - -/// Accessors. Caller does NOT take ownership of results. -const char* grpc_resolver_result_get_server_name(grpc_resolver_result* result); -grpc_lb_addresses* grpc_resolver_result_get_addresses( - grpc_resolver_result* result); -const char* grpc_resolver_result_get_lb_policy_name( - grpc_resolver_result* result); -grpc_channel_args* grpc_resolver_result_get_lb_policy_args( - grpc_resolver_result* result); - -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_RESOLVER_RESULT_H */ diff --git a/src/core/ext/lb_policy/grpclb/grpclb.c b/src/core/ext/lb_policy/grpclb/grpclb.c index b24b522449..6da4febf26 100644 --- a/src/core/ext/lb_policy/grpclb/grpclb.c +++ b/src/core/ext/lb_policy/grpclb/grpclb.c @@ -96,6 +96,12 @@ * - Implement LB service forwarding (point 2c. in the doc's diagram). */ +/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when + using that endpoint. Because of various transitive includes in uv.h, + including windows.h on Windows, uv.h must be included before other system + headers. Therefore, sockaddr.h must always be included first */ +#include "src/core/lib/iomgr/sockaddr.h" + #include <errno.h> #include <string.h> @@ -107,14 +113,13 @@ #include <grpc/support/string_util.h> #include <grpc/support/time.h> -#include "src/core/ext/client_config/client_channel_factory.h" -#include "src/core/ext/client_config/lb_policy_factory.h" -#include "src/core/ext/client_config/lb_policy_registry.h" -#include "src/core/ext/client_config/parse_address.h" +#include "src/core/ext/client_channel/client_channel_factory.h" +#include "src/core/ext/client_channel/lb_policy_factory.h" +#include "src/core/ext/client_channel/lb_policy_registry.h" +#include "src/core/ext/client_channel/parse_address.h" #include "src/core/ext/lb_policy/grpclb/grpclb.h" #include "src/core/ext/lb_policy/grpclb/load_balancer_api.h" #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/support/string.h" #include "src/core/lib/surface/call.h" @@ -338,6 +343,21 @@ static bool is_server_valid(const grpc_grpclb_server *server, size_t idx, return true; } +/* vtable for LB tokens in grpc_lb_addresses. */ +static void *lb_token_copy(void *token) { + return token == NULL ? NULL : GRPC_MDELEM_REF(token); +} +static void lb_token_destroy(void *token) { + if (token != NULL) GRPC_MDELEM_UNREF(token); +} +static int lb_token_cmp(void *token1, void *token2) { + if (token1 > token2) return 1; + if (token1 < token2) return -1; + return 0; +} +static const grpc_lb_user_data_vtable lb_token_vtable = { + lb_token_copy, lb_token_destroy, lb_token_cmp}; + /* Returns addresses extracted from \a serverlist. */ static grpc_lb_addresses *process_serverlist( const grpc_grpclb_serverlist *serverlist) { @@ -349,7 +369,8 @@ static grpc_lb_addresses *process_serverlist( } if (num_valid == 0) return NULL; - grpc_lb_addresses *lb_addresses = grpc_lb_addresses_create(num_valid); + grpc_lb_addresses *lb_addresses = + grpc_lb_addresses_create(num_valid, &lb_token_vtable); /* second pass: actually populate the addresses and LB tokens (aka user data * to the outside world) to be read by the RR policy during its creation. @@ -396,7 +417,7 @@ static grpc_lb_addresses *process_serverlist( gpr_log(GPR_ERROR, "Missing LB token for backend address '%s'. The empty token will " "be used instead", - grpc_sockaddr_to_uri((struct sockaddr *)&addr.addr)); + grpc_sockaddr_to_uri(&addr)); user_data = GRPC_MDELEM_LB_TOKEN_EMPTY; } @@ -410,11 +431,6 @@ static grpc_lb_addresses *process_serverlist( return lb_addresses; } -/* A plugin for grpc_lb_addresses_destroy that unrefs the LB token metadata. */ -static void lb_token_destroy(void *token) { - if (token != NULL) GRPC_MDELEM_UNREF(token); -} - /* perform a pick over \a rr_policy. Given that a pick can return immediately * (ignoring its completion callback) we need to perform the cleanups this * callback would be otherwise resposible for */ @@ -454,20 +470,27 @@ static grpc_lb_policy *create_rr_locked( glb_lb_policy *glb_policy) { GPR_ASSERT(serverlist != NULL && serverlist->num_servers > 0); + if (glb_policy->addresses != NULL) { + /* dispose of the previous version */ + grpc_lb_addresses_destroy(glb_policy->addresses); + } + glb_policy->addresses = process_serverlist(serverlist); + grpc_lb_policy_args args; memset(&args, 0, sizeof(args)); - args.server_name = glb_policy->server_name; args.client_channel_factory = glb_policy->cc_factory; - args.addresses = process_serverlist(serverlist); - args.additional_args = glb_policy->args; - grpc_lb_policy *rr = grpc_lb_policy_create(exec_ctx, "round_robin", &args); + // Replace the LB addresses in the channel args that we pass down to + // the subchannel. + static const char *keys_to_remove[] = {GRPC_ARG_LB_ADDRESSES}; + const grpc_arg arg = + grpc_lb_addresses_create_channel_arg(glb_policy->addresses); + args.args = grpc_channel_args_copy_and_add_and_remove( + glb_policy->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &arg, + 1); - if (glb_policy->addresses != NULL) { - /* dispose of the previous version */ - grpc_lb_addresses_destroy(glb_policy->addresses, lb_token_destroy); - } - glb_policy->addresses = args.addresses; + grpc_lb_policy *rr = grpc_lb_policy_create(exec_ctx, "round_robin", &args); + grpc_channel_args_destroy(args.args); return rr; } @@ -563,6 +586,12 @@ static void glb_rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, grpc_lb_policy_factory *factory, grpc_lb_policy_args *args) { + /* Get server name. */ + const grpc_arg *arg = + grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME); + const char *server_name = + arg != NULL && arg->type == GRPC_ARG_STRING ? arg->value.string : NULL; + /* Count the number of gRPC-LB addresses. There must be at least one. * TODO(roth): For now, we ignore non-balancer addresses, but in the * future, we may change the behavior such that we fall back to using @@ -570,24 +599,27 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, * time, this should be changed to allow a list with no balancer addresses, * since the resolver might fail to return a balancer address even when * this is the right LB policy to use. */ + arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER); + grpc_lb_addresses *addresses = arg->value.pointer.p; size_t num_grpclb_addrs = 0; - for (size_t i = 0; i < args->addresses->num_addresses; ++i) { - if (args->addresses->addresses[i].is_balancer) ++num_grpclb_addrs; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs; } if (num_grpclb_addrs == 0) return NULL; glb_lb_policy *glb_policy = gpr_malloc(sizeof(*glb_policy)); memset(glb_policy, 0, sizeof(*glb_policy)); - /* All input addresses in args->addresses come from a resolver that claims + /* All input addresses in addresses come from a resolver that claims * they are LB services. It's the resolver's responsibility to make sure * this * policy is only instantiated and used in that case. * * Create a client channel over them to communicate with a LB service */ - glb_policy->server_name = gpr_strdup(args->server_name); + glb_policy->server_name = gpr_strdup(server_name); glb_policy->cc_factory = args->client_channel_factory; - glb_policy->args = grpc_channel_args_copy(args->additional_args); + glb_policy->args = grpc_channel_args_copy(args->args); GPR_ASSERT(glb_policy->cc_factory != NULL); /* construct a target from the addresses in args, given in the form @@ -595,22 +627,19 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, * TODO(dgq): support mixed ip version */ char **addr_strs = gpr_malloc(sizeof(char *) * num_grpclb_addrs); size_t addr_index = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { - if (args->addresses->addresses[i].user_data != NULL) { + for (size_t i = 0; i < addresses->num_addresses; i++) { + if (addresses->addresses[i].user_data != NULL) { gpr_log(GPR_ERROR, "This LB policy doesn't support user data. It will be ignored"); } - if (args->addresses->addresses[i].is_balancer) { + if (addresses->addresses[i].is_balancer) { if (addr_index == 0) { - addr_strs[addr_index++] = grpc_sockaddr_to_uri( - (const struct sockaddr *)&args->addresses->addresses[i] - .address.addr); + addr_strs[addr_index++] = + grpc_sockaddr_to_uri(&addresses->addresses[i].address); } else { - GPR_ASSERT(grpc_sockaddr_to_string( - &addr_strs[addr_index++], - (const struct sockaddr *)&args->addresses->addresses[i] - .address.addr, - true) > 0); + GPR_ASSERT(grpc_sockaddr_to_string(&addr_strs[addr_index++], + &addresses->addresses[i].address, + true) > 0); } } } @@ -618,10 +647,29 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, char *target_uri_str = gpr_strjoin_sep((const char **)addr_strs, num_grpclb_addrs, ",", &uri_path_len); - /* will pick using pick_first */ + /* Create a channel to talk to the LBs. + * + * We strip out the channel arg for the LB policy name, since we want + * to use the default (pick_first) in this case. + * + * We also strip out the channel arg for the resolved addresses, since + * that will be generated by the name resolver used in the LB channel. + * Note that the LB channel will use the sockaddr resolver, so this + * won't actually generate a query to DNS (or some other name service). + * However, the addresses returned by the sockaddr resolver will have + * is_balancer=false, whereas our own addresses have is_balancer=true. + * We need the LB channel to return addresses with is_balancer=false + * so that it does not wind up recursively using the grpclb LB policy, + * as per the special case logic in client_channel.c. + */ + static const char *keys_to_remove[] = {GRPC_ARG_LB_POLICY_NAME, + GRPC_ARG_LB_ADDRESSES}; + grpc_channel_args *new_args = grpc_channel_args_copy_and_remove( + args->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove)); glb_policy->lb_channel = grpc_client_channel_factory_create_channel( exec_ctx, glb_policy->cc_factory, target_uri_str, - GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, NULL); + GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, new_args); + grpc_channel_args_destroy(new_args); gpr_free(target_uri_str); for (size_t i = 0; i < num_grpclb_addrs; i++) { @@ -662,7 +710,7 @@ static void glb_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { grpc_grpclb_destroy_serverlist(glb_policy->serverlist); } gpr_mu_destroy(&glb_policy->mu); - grpc_lb_addresses_destroy(glb_policy->addresses, lb_token_destroy); + grpc_lb_addresses_destroy(glb_policy->addresses); gpr_free(glb_policy); } diff --git a/src/core/ext/lb_policy/grpclb/grpclb.h b/src/core/ext/lb_policy/grpclb/grpclb.h index 83552b4fa0..ff23f3a545 100644 --- a/src/core/ext/lb_policy/grpclb/grpclb.h +++ b/src/core/ext/lb_policy/grpclb/grpclb.h @@ -34,7 +34,7 @@ #ifndef GRPC_CORE_EXT_LB_POLICY_GRPCLB_GRPCLB_H #define GRPC_CORE_EXT_LB_POLICY_GRPCLB_GRPCLB_H -#include "src/core/ext/client_config/lb_policy_factory.h" +#include "src/core/ext/client_channel/lb_policy_factory.h" /** Returns a load balancing factory for the glb policy, which tries to connect * to a load balancing server to decide the next successfully connected diff --git a/src/core/ext/lb_policy/grpclb/load_balancer_api.h b/src/core/ext/lb_policy/grpclb/load_balancer_api.h index c1e73d08ef..079a64a3f3 100644 --- a/src/core/ext/lb_policy/grpclb/load_balancer_api.h +++ b/src/core/ext/lb_policy/grpclb/load_balancer_api.h @@ -36,7 +36,7 @@ #include <grpc/support/slice_buffer.h> -#include "src/core/ext/client_config/lb_policy_factory.h" +#include "src/core/ext/client_channel/lb_policy_factory.h" #include "src/core/ext/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" #ifdef __cplusplus diff --git a/src/core/ext/lb_policy/pick_first/pick_first.c b/src/core/ext/lb_policy/pick_first/pick_first.c index da20b9281b..5d3433df74 100644 --- a/src/core/ext/lb_policy/pick_first/pick_first.c +++ b/src/core/ext/lb_policy/pick_first/pick_first.c @@ -34,7 +34,9 @@ #include <string.h> #include <grpc/support/alloc.h> -#include "src/core/ext/client_config/lb_policy_registry.h" + +#include "src/core/ext/client_channel/lb_policy_registry.h" +#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/transport/connectivity_state.h" typedef struct pending_pick { @@ -432,14 +434,22 @@ static void pick_first_factory_unref(grpc_lb_policy_factory *factory) {} static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx, grpc_lb_policy_factory *factory, grpc_lb_policy_args *args) { - GPR_ASSERT(args->addresses != NULL); GPR_ASSERT(args->client_channel_factory != NULL); + /* Get server name. */ + const grpc_arg *arg = + grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME); + const char *server_name = + arg != NULL && arg->type == GRPC_ARG_STRING ? arg->value.string : NULL; + /* Find the number of backend addresses. We ignore balancer * addresses, since we don't know how to handle them. */ + arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER); + grpc_lb_addresses *addresses = arg->value.pointer.p; size_t num_addrs = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { - if (!args->addresses->addresses[i].is_balancer) ++num_addrs; + for (size_t i = 0; i < addresses->num_addresses; i++) { + if (!addresses->addresses[i].is_balancer) ++num_addrs; } if (num_addrs == 0) return NULL; @@ -450,23 +460,21 @@ static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx, memset(p->subchannels, 0, sizeof(*p->subchannels) * num_addrs); grpc_subchannel_args sc_args; size_t subchannel_idx = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { + for (size_t i = 0; i < addresses->num_addresses; i++) { /* Skip balancer addresses, since we only know how to handle backends. */ - if (args->addresses->addresses[i].is_balancer) continue; + if (addresses->addresses[i].is_balancer) continue; - if (args->addresses->addresses[i].user_data != NULL) { + if (addresses->addresses[i].user_data != NULL) { gpr_log(GPR_ERROR, "This LB policy doesn't support user data. It will be ignored"); } memset(&sc_args, 0, sizeof(grpc_subchannel_args)); /* server_name will be copied as part of the subchannel creation. This makes - * the copying of args->server_name (a borrowed pointer) OK. */ - sc_args.server_name = args->server_name; - sc_args.addr = - (struct sockaddr *)(&args->addresses->addresses[i].address.addr); - sc_args.addr_len = args->addresses->addresses[i].address.len; - sc_args.args = args->additional_args; + * the copying of server_name (a borrowed pointer) OK. */ + sc_args.server_name = server_name; + sc_args.addr = &addresses->addresses[i].address; + sc_args.args = args->args; grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( exec_ctx, args->client_channel_factory, &sc_args); diff --git a/src/core/ext/lb_policy/round_robin/round_robin.c b/src/core/ext/lb_policy/round_robin/round_robin.c index b8f4f67aeb..c0743b00e8 100644 --- a/src/core/ext/lb_policy/round_robin/round_robin.c +++ b/src/core/ext/lb_policy/round_robin/round_robin.c @@ -63,7 +63,8 @@ #include <grpc/support/alloc.h> -#include "src/core/ext/client_config/lb_policy_registry.h" +#include "src/core/ext/client_channel/lb_policy_registry.h" +#include "src/core/lib/channel/channel_args.h" #include "src/core/lib/debug/trace.h" #include "src/core/lib/transport/connectivity_state.h" #include "src/core/lib/transport/static_metadata.h" @@ -598,14 +599,22 @@ static void round_robin_factory_unref(grpc_lb_policy_factory *factory) {} static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, grpc_lb_policy_factory *factory, grpc_lb_policy_args *args) { - GPR_ASSERT(args->addresses != NULL); GPR_ASSERT(args->client_channel_factory != NULL); + /* Get server name. */ + const grpc_arg *arg = + grpc_channel_args_find(args->args, GRPC_ARG_SERVER_NAME); + const char *server_name = + arg != NULL && arg->type == GRPC_ARG_STRING ? arg->value.string : NULL; + /* Find the number of backend addresses. We ignore balancer * addresses, since we don't know how to handle them. */ + arg = grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + GPR_ASSERT(arg != NULL && arg->type == GRPC_ARG_POINTER); + grpc_lb_addresses *addresses = arg->value.pointer.p; size_t num_addrs = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { - if (!args->addresses->addresses[i].is_balancer) ++num_addrs; + for (size_t i = 0; i < addresses->num_addresses; i++) { + if (!addresses->addresses[i].is_balancer) ++num_addrs; } if (num_addrs == 0) return NULL; @@ -618,18 +627,16 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, grpc_subchannel_args sc_args; size_t subchannel_idx = 0; - for (size_t i = 0; i < args->addresses->num_addresses; i++) { + for (size_t i = 0; i < addresses->num_addresses; i++) { /* Skip balancer addresses, since we only know how to handle backends. */ - if (args->addresses->addresses[i].is_balancer) continue; + if (addresses->addresses[i].is_balancer) continue; memset(&sc_args, 0, sizeof(grpc_subchannel_args)); /* server_name will be copied as part of the subchannel creation. This makes - * the copying of args->server_name (a borrowed pointer) OK. */ - sc_args.server_name = args->server_name; - sc_args.addr = - (struct sockaddr *)(&args->addresses->addresses[i].address.addr); - sc_args.addr_len = args->addresses->addresses[i].address.len; - sc_args.args = args->additional_args; + * the copying of server_name (a borrowed pointer) OK. */ + sc_args.server_name = server_name; + sc_args.addr = &addresses->addresses[i].address; + sc_args.args = args->args; grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( exec_ctx, args->client_channel_factory, &sc_args); @@ -641,7 +648,7 @@ static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, sd->policy = p; sd->index = subchannel_idx; sd->subchannel = subchannel; - sd->user_data = args->addresses->addresses[i].user_data; + sd->user_data = addresses->addresses[i].user_data; ++subchannel_idx; grpc_closure_init(&sd->connectivity_changed_closure, rr_connectivity_changed, sd); diff --git a/src/core/ext/load_reporting/load_reporting.h b/src/core/ext/load_reporting/load_reporting.h index e13097654d..a157844734 100644 --- a/src/core/ext/load_reporting/load_reporting.h +++ b/src/core/ext/load_reporting/load_reporting.h @@ -51,7 +51,7 @@ * The value corresponding to this key is an opaque binary blob reported by the * backend as part of its trailing metadata containing cost information for the * call. */ -#define GRPC_LB_COST_MD_KEY "lb-cost" +#define GRPC_LB_COST_MD_KEY "lb-cost-bin" /** Identifiers for the invocation point of the users LR callback */ typedef enum grpc_load_reporting_source { diff --git a/src/core/ext/load_reporting/load_reporting_filter.c b/src/core/ext/load_reporting/load_reporting_filter.c index 22bf36367f..eeae2400fb 100644 --- a/src/core/ext/load_reporting/load_reporting_filter.c +++ b/src/core/ext/load_reporting/load_reporting_filter.c @@ -193,7 +193,7 @@ static grpc_mdelem *lr_trailing_md_filter(void *user_data, grpc_mdelem *md) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; - if (md->key == GRPC_MDSTR_LB_COST) { + if (md->key == GRPC_MDSTR_LB_COST_BIN) { calld->trailing_md_string = gpr_strdup(grpc_mdstr_as_c_string(md->value)); return NULL; } diff --git a/src/core/ext/resolver/dns/native/dns_resolver.c b/src/core/ext/resolver/dns/native/dns_resolver.c index fa33ffd7bd..958b8af8b2 100644 --- a/src/core/ext/resolver/dns/native/dns_resolver.c +++ b/src/core/ext/resolver/dns/native/dns_resolver.c @@ -37,9 +37,10 @@ #include <grpc/support/host_port.h> #include <grpc/support/string_util.h> -#include "src/core/ext/client_config/http_connect_handshaker.h" -#include "src/core/ext/client_config/lb_policy_registry.h" -#include "src/core/ext/client_config/resolver_registry.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/lib/channel/channel_args.h" #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/support/backoff.h" @@ -53,12 +54,12 @@ typedef struct { /** base class: must be first */ grpc_resolver base; - /** target name */ - char *target_name; - /** name to resolve (usually the same as target_name) */ + /** name to resolve */ char *name_to_resolve; /** default port to use */ char *default_port; + /** channel args. */ + grpc_channel_args *channel_args; /** mutex guarding the rest of the state */ gpr_mu mu; @@ -71,9 +72,9 @@ typedef struct { /** pending next completion, or NULL */ grpc_closure *next_completion; /** target result address for next completion */ - grpc_resolver_result **target_result; + grpc_channel_args **target_result; /** current (fully resolved) result */ - grpc_resolver_result *resolved_result; + grpc_channel_args *resolved_result; /** retry timer */ bool have_retry_timer; grpc_timer retry_timer; @@ -94,7 +95,7 @@ static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, static void dns_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void dns_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_resolver_result **target_result, + grpc_channel_args **target_result, grpc_closure *on_complete); static const grpc_resolver_vtable dns_resolver_vtable = { @@ -127,7 +128,7 @@ static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, } static void dns_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **target_result, + grpc_channel_args **target_result, grpc_closure *on_complete) { dns_resolver *r = (dns_resolver *)resolver; gpr_mu_lock(&r->mu); @@ -162,22 +163,23 @@ static void dns_on_retry_timer(grpc_exec_ctx *exec_ctx, void *arg, static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { dns_resolver *r = arg; - grpc_resolver_result *result = NULL; + grpc_channel_args *result = NULL; gpr_mu_lock(&r->mu); GPR_ASSERT(r->resolving); r->resolving = false; if (r->addresses != NULL) { - grpc_lb_addresses *addresses = - grpc_lb_addresses_create(r->addresses->naddrs); + 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); - result = grpc_resolver_result_create(r->target_name, addresses, - NULL /* lb_policy_name */, NULL); + grpc_lb_addresses_destroy(addresses); } else { gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); @@ -197,8 +199,8 @@ static void dns_on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_timer_init(exec_ctx, &r->retry_timer, next_try, dns_on_retry_timer, r, now); } - if (r->resolved_result) { - grpc_resolver_result_unref(exec_ctx, r->resolved_result); + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(r->resolved_result); } r->resolved_result = result; r->resolved_version++; @@ -222,10 +224,9 @@ static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, dns_resolver *r) { if (r->next_completion != NULL && r->resolved_version != r->published_version) { - *r->target_result = r->resolved_result; - if (r->resolved_result) { - grpc_resolver_result_ref(r->resolved_result); - } + *r->target_result = r->resolved_result == NULL + ? NULL + : grpc_channel_args_copy(r->resolved_result); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; r->published_version = r->resolved_version; @@ -235,12 +236,12 @@ static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { dns_resolver *r = (dns_resolver *)gr; gpr_mu_destroy(&r->mu); - if (r->resolved_result) { - grpc_resolver_result_unref(exec_ctx, r->resolved_result); + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(r->resolved_result); } - gpr_free(r->target_name); gpr_free(r->name_to_resolve); gpr_free(r->default_port); + grpc_channel_args_destroy(r->channel_args); gpr_free(r); } @@ -260,9 +261,14 @@ static grpc_resolver *dns_create(grpc_resolver_args *args, memset(r, 0, sizeof(*r)); gpr_mu_init(&r->mu); grpc_resolver_init(&r->base, &dns_resolver_vtable); - r->target_name = gpr_strdup(path); r->name_to_resolve = proxy_name == NULL ? gpr_strdup(path) : proxy_name; r->default_port = gpr_strdup(default_port); + grpc_arg server_name_arg; + server_name_arg.type = GRPC_ARG_STRING; + server_name_arg.key = GRPC_ARG_SERVER_NAME; + server_name_arg.value.string = (char *)path; + r->channel_args = + grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1); gpr_backoff_init(&r->backoff_state, BACKOFF_MULTIPLIER, BACKOFF_JITTER, BACKOFF_MIN_SECONDS * 1000, BACKOFF_MAX_SECONDS * 1000); return &r->base; diff --git a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c index 28dd2569e8..5fec03a8e4 100644 --- a/src/core/ext/resolver/sockaddr/sockaddr_resolver.c +++ b/src/core/ext/resolver/sockaddr/sockaddr_resolver.c @@ -41,8 +41,9 @@ #include <grpc/support/port_platform.h> #include <grpc/support/string_util.h> -#include "src/core/ext/client_config/parse_address.h" -#include "src/core/ext/client_config/resolver_registry.h" +#include "src/core/ext/client_channel/lb_policy_factory.h" +#include "src/core/ext/client_channel/parse_address.h" +#include "src/core/ext/client_channel/resolver_registry.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/unix_sockets_posix.h" @@ -51,10 +52,10 @@ typedef struct { /** base class: must be first */ grpc_resolver base; - /** the path component of the uri passed in */ - char *target_name; /** the addresses that we've 'resolved' */ grpc_lb_addresses *addresses; + /** channel args */ + grpc_channel_args *channel_args; /** mutex guarding the rest of the state */ gpr_mu mu; /** have we published? */ @@ -62,7 +63,7 @@ typedef struct { /** pending next completion, or NULL */ grpc_closure *next_completion; /** target result address for next completion */ - grpc_resolver_result **target_result; + grpc_channel_args **target_result; } sockaddr_resolver; static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); @@ -74,7 +75,7 @@ static void sockaddr_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_resolver_result **target_result, + grpc_channel_args **target_result, grpc_closure *on_complete); static const grpc_resolver_vtable sockaddr_resolver_vtable = { @@ -103,7 +104,7 @@ static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx, } static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_resolver_result **target_result, + grpc_channel_args **target_result, grpc_closure *on_complete) { sockaddr_resolver *r = (sockaddr_resolver *)resolver; gpr_mu_lock(&r->mu); @@ -118,10 +119,9 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, sockaddr_resolver *r) { if (r->next_completion != NULL && !r->published) { r->published = true; - *r->target_result = grpc_resolver_result_create( - r->target_name, - grpc_lb_addresses_copy(r->addresses, NULL /* user_data_copy */), - NULL /* lb_policy_name */, NULL /* lb_policy_args */); + grpc_arg arg = grpc_lb_addresses_create_channel_arg(r->addresses); + *r->target_result = + grpc_channel_args_copy_and_add(r->channel_args, &arg, 1); grpc_exec_ctx_sched(exec_ctx, r->next_completion, GRPC_ERROR_NONE, NULL); r->next_completion = NULL; } @@ -130,8 +130,8 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { sockaddr_resolver *r = (sockaddr_resolver *)gr; gpr_mu_destroy(&r->mu); - gpr_free(r->target_name); - grpc_lb_addresses_destroy(r->addresses, NULL /* user_data_destroy */); + grpc_lb_addresses_destroy(r->addresses); + grpc_channel_args_destroy(r->channel_args); gpr_free(r); } @@ -151,7 +151,7 @@ static char *ipv6_get_default_authority(grpc_resolver_factory *factory, return ip_get_default_authority(uri); } -#ifdef GPR_HAVE_UNIX_SOCKET +#ifdef GRPC_HAVE_UNIX_SOCKET char *unix_get_default_authority(grpc_resolver_factory *factory, grpc_uri *uri) { return gpr_strdup("localhost"); @@ -162,8 +162,7 @@ static void do_nothing(void *ignored) {} static grpc_resolver *sockaddr_create(grpc_resolver_args *args, int parse(grpc_uri *uri, - struct sockaddr_storage *dst, - size_t *len)) { + grpc_resolved_address *dst)) { if (0 != strcmp(args->uri->authority, "")) { gpr_log(GPR_ERROR, "authority based uri's not supported by the %s scheme", args->uri->scheme); @@ -175,17 +174,15 @@ static grpc_resolver *sockaddr_create(grpc_resolver_args *args, gpr_slice_buffer path_parts; gpr_slice_buffer_init(&path_parts); gpr_slice_split(path_slice, ",", &path_parts); - grpc_lb_addresses *addresses = grpc_lb_addresses_create(path_parts.count); + grpc_lb_addresses *addresses = + grpc_lb_addresses_create(path_parts.count, NULL /* user_data_vtable */); bool errors_found = false; for (size_t i = 0; i < addresses->num_addresses; i++) { grpc_uri ith_uri = *args->uri; char *part_str = gpr_dump_slice(path_parts.slices[i], GPR_DUMP_ASCII); ith_uri.path = part_str; - if (!parse( - &ith_uri, - (struct sockaddr_storage *)(&addresses->addresses[i].address.addr), - &addresses->addresses[i].address.len)) { - errors_found = true; + if (!parse(&ith_uri, &addresses->addresses[i].address)) { + errors_found = true; /* GPR_TRUE */ } gpr_free(part_str); if (errors_found) break; @@ -193,14 +190,19 @@ static grpc_resolver *sockaddr_create(grpc_resolver_args *args, gpr_slice_buffer_destroy(&path_parts); gpr_slice_unref(path_slice); if (errors_found) { - grpc_lb_addresses_destroy(addresses, NULL /* user_data_destroy */); + grpc_lb_addresses_destroy(addresses); return NULL; } /* Instantiate resolver. */ sockaddr_resolver *r = gpr_malloc(sizeof(sockaddr_resolver)); memset(r, 0, sizeof(*r)); - r->target_name = gpr_strdup(args->uri->path); r->addresses = addresses; + grpc_arg server_name_arg; + server_name_arg.type = GRPC_ARG_STRING; + server_name_arg.key = GRPC_ARG_SERVER_NAME; + server_name_arg.value.string = args->uri->path; + r->channel_args = + grpc_channel_args_copy_and_add(args->args, &server_name_arg, 1); gpr_mu_init(&r->mu); grpc_resolver_init(&r->base, &sockaddr_resolver_vtable); return &r->base; @@ -225,7 +227,7 @@ static void sockaddr_factory_unref(grpc_resolver_factory *factory) {} static grpc_resolver_factory name##_resolver_factory = { \ &name##_factory_vtable} -#ifdef GPR_HAVE_UNIX_SOCKET +#ifdef GRPC_HAVE_UNIX_SOCKET DECL_FACTORY(unix); #endif DECL_FACTORY(ipv4); @@ -234,7 +236,7 @@ DECL_FACTORY(ipv6); void grpc_resolver_sockaddr_init(void) { grpc_register_resolver_type(&ipv4_resolver_factory); grpc_register_resolver_type(&ipv6_resolver_factory); -#ifdef GPR_HAVE_UNIX_SOCKET +#ifdef GRPC_HAVE_UNIX_SOCKET grpc_register_resolver_type(&unix_resolver_factory); #endif } diff --git a/src/core/ext/transport/chttp2/alpn/alpn.c b/src/core/ext/transport/chttp2/alpn/alpn.c index 48b0217265..55710dc5ae 100644 --- a/src/core/ext/transport/chttp2/alpn/alpn.c +++ b/src/core/ext/transport/chttp2/alpn/alpn.c @@ -36,7 +36,7 @@ #include <grpc/support/useful.h> /* in order of preference */ -static const char *const supported_versions[] = {"h2"}; +static const char *const supported_versions[] = {"grpc-exp", "h2"}; int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size) { size_t i; diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c index c2b59569fd..372fb7bf4c 100644 --- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c +++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.c @@ -40,9 +40,9 @@ #include <grpc/support/slice.h> #include <grpc/support/slice_buffer.h> -#include "src/core/ext/client_config/client_channel.h" -#include "src/core/ext/client_config/http_connect_handshaker.h" -#include "src/core/ext/client_config/resolver_registry.h" +#include "src/core/ext/client_channel/client_channel.h" +#include "src/core/ext/client_channel/http_connect_handshaker.h" +#include "src/core/ext/client_channel/resolver_registry.h" #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/compress_filter.h" @@ -52,6 +52,10 @@ #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/channel.h" +// +// connector +// + typedef struct { grpc_connector base; gpr_refcount refs; @@ -150,42 +154,26 @@ static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con, c->tcp = NULL; grpc_closure_init(&c->connected, connected, c); grpc_tcp_client_connect(exec_ctx, &c->connected, &c->tcp, - args->interested_parties, args->addr, args->addr_len, - args->deadline); + args->interested_parties, args->addr, args->deadline); } static const grpc_connector_vtable connector_vtable = { connector_ref, connector_unref, connector_shutdown, connector_connect}; -typedef struct { - grpc_client_channel_factory base; - gpr_refcount refs; - grpc_channel_args *merge_args; -} client_channel_factory; +// +// client_channel_factory +// static void client_channel_factory_ref( - grpc_client_channel_factory *cc_factory) { - client_channel_factory *f = (client_channel_factory *)cc_factory; - gpr_ref(&f->refs); -} + grpc_client_channel_factory *cc_factory) {} static void client_channel_factory_unref( - grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) { - client_channel_factory *f = (client_channel_factory *)cc_factory; - if (gpr_unref(&f->refs)) { - grpc_channel_args_destroy(f->merge_args); - gpr_free(f); - } -} + grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) {} static grpc_subchannel *client_channel_factory_create_subchannel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, - grpc_subchannel_args *args) { - client_channel_factory *f = (client_channel_factory *)cc_factory; + const grpc_subchannel_args *args) { connector *c = gpr_malloc(sizeof(*c)); - grpc_channel_args *final_args = - grpc_channel_args_merge(args->args, f->merge_args); - grpc_subchannel *s; memset(c, 0, sizeof(*c)); c->base.vtable = &connector_vtable; gpr_ref_init(&c->refs, 1); @@ -197,23 +185,18 @@ static grpc_subchannel *client_channel_factory_create_subchannel( grpc_http_connect_handshaker_create(proxy_name, args->server_name)); gpr_free(proxy_name); } - args->args = final_args; - s = grpc_subchannel_create(exec_ctx, &c->base, args); + grpc_subchannel *s = grpc_subchannel_create(exec_ctx, &c->base, args); grpc_connector_unref(exec_ctx, &c->base); - grpc_channel_args_destroy(final_args); return s; } static grpc_channel *client_channel_factory_create_channel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, const char *target, grpc_client_channel_type type, - grpc_channel_args *args) { - client_channel_factory *f = (client_channel_factory *)cc_factory; - grpc_channel_args *final_args = grpc_channel_args_merge(args, f->merge_args); - grpc_channel *channel = grpc_channel_create(exec_ctx, target, final_args, - GRPC_CLIENT_CHANNEL, NULL); - grpc_channel_args_destroy(final_args); - grpc_resolver *resolver = grpc_resolver_create(target); + const grpc_channel_args *args) { + grpc_channel *channel = + grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL); + grpc_resolver *resolver = grpc_resolver_create(target, args); if (!resolver) { GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, "client_channel_factory_create_channel"); @@ -221,7 +204,7 @@ static grpc_channel *client_channel_factory_create_channel( } grpc_client_channel_finish_initialization( - exec_ctx, grpc_channel_get_channel_stack(channel), resolver, &f->base); + exec_ctx, grpc_channel_get_channel_stack(channel), resolver, cc_factory); GRPC_RESOLVER_UNREF(exec_ctx, resolver, "create_channel"); return channel; @@ -232,6 +215,9 @@ static const grpc_client_channel_factory_vtable client_channel_factory_vtable = client_channel_factory_create_subchannel, client_channel_factory_create_channel}; +static grpc_client_channel_factory client_channel_factory = { + &client_channel_factory_vtable}; + /* Create a client channel: Asynchronously: - resolve target - connect to it (trying alternatives as presented) @@ -245,16 +231,12 @@ grpc_channel *grpc_insecure_channel_create(const char *target, (target, args, reserved)); GPR_ASSERT(!reserved); - client_channel_factory *f = gpr_malloc(sizeof(*f)); - memset(f, 0, sizeof(*f)); - f->base.vtable = &client_channel_factory_vtable; - gpr_ref_init(&f->refs, 1); - f->merge_args = grpc_channel_args_copy(args); - + grpc_client_channel_factory *factory = + (grpc_client_channel_factory *)&client_channel_factory; grpc_channel *channel = client_channel_factory_create_channel( - &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, NULL); + &exec_ctx, factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, args); - grpc_client_channel_factory_unref(&exec_ctx, &f->base); + grpc_client_channel_factory_unref(&exec_ctx, factory); grpc_exec_ctx_finish(&exec_ctx); return channel != NULL ? channel : grpc_lame_client_channel_create( diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c index 31c54ff74c..29095af207 100644 --- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c +++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c @@ -40,9 +40,9 @@ #include <grpc/support/slice.h> #include <grpc/support/slice_buffer.h> -#include "src/core/ext/client_config/client_channel.h" -#include "src/core/ext/client_config/http_connect_handshaker.h" -#include "src/core/ext/client_config/resolver_registry.h" +#include "src/core/ext/client_channel/client_channel.h" +#include "src/core/ext/client_channel/http_connect_handshaker.h" +#include "src/core/ext/client_channel/resolver_registry.h" #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" #include "src/core/lib/channel/channel_args.h" #include "src/core/lib/channel/handshaker.h" @@ -54,6 +54,10 @@ #include "src/core/lib/surface/channel.h" #include "src/core/lib/tsi/transport_security_interface.h" +// +// connector +// + typedef struct { grpc_connector base; gpr_refcount refs; @@ -208,18 +212,21 @@ static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con, GPR_ASSERT(c->connecting_endpoint == NULL); gpr_mu_unlock(&c->mu); grpc_closure_init(&c->connected_closure, connected, c); - grpc_tcp_client_connect( - exec_ctx, &c->connected_closure, &c->newly_connecting_endpoint, - args->interested_parties, args->addr, args->addr_len, args->deadline); + grpc_tcp_client_connect(exec_ctx, &c->connected_closure, + &c->newly_connecting_endpoint, + args->interested_parties, args->addr, args->deadline); } static const grpc_connector_vtable connector_vtable = { connector_ref, connector_unref, connector_shutdown, connector_connect}; +// +// client_channel_factory +// + typedef struct { grpc_client_channel_factory base; gpr_refcount refs; - grpc_channel_args *merge_args; grpc_channel_security_connector *security_connector; } client_channel_factory; @@ -235,19 +242,15 @@ static void client_channel_factory_unref( if (gpr_unref(&f->refs)) { GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base, "client_channel_factory"); - grpc_channel_args_destroy(f->merge_args); gpr_free(f); } } static grpc_subchannel *client_channel_factory_create_subchannel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, - grpc_subchannel_args *args) { + const grpc_subchannel_args *args) { client_channel_factory *f = (client_channel_factory *)cc_factory; connector *c = gpr_malloc(sizeof(*c)); - grpc_channel_args *final_args = - grpc_channel_args_merge(args->args, f->merge_args); - grpc_subchannel *s; memset(c, 0, sizeof(*c)); c->base.vtable = &connector_vtable; c->security_connector = f->security_connector; @@ -261,25 +264,19 @@ static grpc_subchannel *client_channel_factory_create_subchannel( } gpr_mu_init(&c->mu); gpr_ref_init(&c->refs, 1); - args->args = final_args; - s = grpc_subchannel_create(exec_ctx, &c->base, args); + grpc_subchannel *s = grpc_subchannel_create(exec_ctx, &c->base, args); grpc_connector_unref(exec_ctx, &c->base); - grpc_channel_args_destroy(final_args); return s; } static grpc_channel *client_channel_factory_create_channel( grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, const char *target, grpc_client_channel_type type, - grpc_channel_args *args) { + const grpc_channel_args *args) { client_channel_factory *f = (client_channel_factory *)cc_factory; - - grpc_channel_args *final_args = grpc_channel_args_merge(args, f->merge_args); - grpc_channel *channel = grpc_channel_create(exec_ctx, target, final_args, - GRPC_CLIENT_CHANNEL, NULL); - grpc_channel_args_destroy(final_args); - - grpc_resolver *resolver = grpc_resolver_create(target); + grpc_channel *channel = + grpc_channel_create(exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL); + grpc_resolver *resolver = grpc_resolver_create(target, args); if (resolver != NULL) { grpc_client_channel_finish_initialization( exec_ctx, grpc_channel_get_channel_stack(channel), resolver, &f->base); @@ -289,9 +286,6 @@ static grpc_channel *client_channel_factory_create_channel( "client_channel_factory_create_channel"); channel = NULL; } - - GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base, - "client_channel_factory_create_channel"); return channel; } @@ -308,19 +302,13 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, const char *target, const grpc_channel_args *args, void *reserved) { - grpc_arg connector_arg; - grpc_channel_args *args_copy; - grpc_channel_args *new_args_from_connector; - grpc_channel_security_connector *security_connector; - client_channel_factory *f; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE( "grpc_secure_channel_create(creds=%p, target=%s, args=%p, " "reserved=%p)", 4, (creds, target, args, reserved)); GPR_ASSERT(reserved == NULL); - + // Make sure security connector does not already exist in args. if (grpc_find_security_connector_in_args(args) != NULL) { gpr_log(GPR_ERROR, "Cannot set security context in channel args."); grpc_exec_ctx_finish(&exec_ctx); @@ -328,7 +316,9 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, target, GRPC_STATUS_INTERNAL, "Security connector exists in channel args."); } - + // Create security connector and construct new channel args. + grpc_channel_security_connector *security_connector; + grpc_channel_args *new_args_from_connector; if (grpc_channel_credentials_create_security_connector( creds, target, args, &security_connector, &new_args_from_connector) != GRPC_SECURITY_OK) { @@ -336,32 +326,30 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, return grpc_lame_client_channel_create( target, GRPC_STATUS_INTERNAL, "Failed to create security connector."); } - - connector_arg = grpc_security_connector_to_arg(&security_connector->base); - args_copy = grpc_channel_args_copy_and_add( + grpc_arg connector_arg = + grpc_security_connector_to_arg(&security_connector->base); + grpc_channel_args *new_args = grpc_channel_args_copy_and_add( new_args_from_connector != NULL ? new_args_from_connector : args, &connector_arg, 1); - - f = gpr_malloc(sizeof(*f)); - memset(f, 0, sizeof(*f)); - f->base.vtable = &client_channel_factory_vtable; - gpr_ref_init(&f->refs, 1); - - f->merge_args = grpc_channel_args_copy(args_copy); - grpc_channel_args_destroy(args_copy); if (new_args_from_connector != NULL) { grpc_channel_args_destroy(new_args_from_connector); } - + // Create client channel factory. + client_channel_factory *f = gpr_malloc(sizeof(*f)); + memset(f, 0, sizeof(*f)); + f->base.vtable = &client_channel_factory_vtable; + gpr_ref_init(&f->refs, 1); GRPC_SECURITY_CONNECTOR_REF(&security_connector->base, "grpc_secure_channel_create"); f->security_connector = security_connector; - + // Create channel. grpc_channel *channel = client_channel_factory_create_channel( - &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, NULL); - + &exec_ctx, &f->base, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args); + // Clean up. + GRPC_SECURITY_CONNECTOR_UNREF(&f->security_connector->base, + "client_channel_factory_create_channel"); + grpc_channel_args_destroy(new_args); grpc_client_channel_factory_unref(&exec_ctx, &f->base); grpc_exec_ctx_finish(&exec_ctx); - return channel; /* may be NULL */ } diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c index f0e07429fa..e4967c2f08 100644 --- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c +++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c @@ -148,9 +148,7 @@ int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { const size_t naddrs = resolved->naddrs; errors = gpr_malloc(sizeof(*errors) * naddrs); for (i = 0; i < naddrs; i++) { - errors[i] = grpc_tcp_server_add_port( - tcp, (struct sockaddr *)&resolved->addrs[i].addr, - resolved->addrs[i].len, &port_temp); + errors[i] = grpc_tcp_server_add_port(tcp, &resolved->addrs[i], &port_temp); if (errors[i] == GRPC_ERROR_NONE) { if (port_num == -1) { port_num = port_temp; diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c index 563271f4f8..0fab888541 100644 --- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c +++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c @@ -286,9 +286,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, errors = gpr_malloc(sizeof(*errors) * resolved->naddrs); for (i = 0; i < resolved->naddrs; i++) { - errors[i] = grpc_tcp_server_add_port( - tcp, (struct sockaddr *)&resolved->addrs[i].addr, - resolved->addrs[i].len, &port_temp); + errors[i] = grpc_tcp_server_add_port(tcp, &resolved->addrs[i], &port_temp); if (errors[i] == GRPC_ERROR_NONE) { if (port_num == -1) { port_num = port_temp; diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c index 97780d90f2..ecf3aea870 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.c @@ -853,53 +853,51 @@ static bool contains_non_ok_status(grpc_metadata_batch *batch) { static void add_fetched_slice_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_stream *s); + grpc_chttp2_stream *s) { + s->fetched_send_message_length += + (uint32_t)GPR_SLICE_LENGTH(s->fetching_slice); + gpr_slice_buffer_add(&s->flow_controlled_buffer, s->fetching_slice); + if (s->id != 0) { + grpc_chttp2_become_writable(exec_ctx, t, s, true, "op.send_message"); + } +} static void continue_fetching_send_locked(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_chttp2_stream *s) { - if (s->fetching_send_message == NULL) { - /* Stream was cancelled before message fetch completed */ - abort(); /* TODO(ctiller): what cleanup here? */ - return; - } - if (s->fetched_send_message_length == s->fetching_send_message->length) { - int64_t notify_offset = s->next_message_end_offset; - if (notify_offset <= s->flow_controlled_bytes_written) { - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->fetching_send_message_finished, GRPC_ERROR_NONE, - "fetching_send_message_finished"); - } else { - grpc_chttp2_write_cb *cb = t->write_cb_pool; - if (cb == NULL) { - cb = gpr_malloc(sizeof(*cb)); + for (;;) { + if (s->fetching_send_message == NULL) { + /* Stream was cancelled before message fetch completed */ + abort(); /* TODO(ctiller): what cleanup here? */ + return; /* early out */ + } + if (s->fetched_send_message_length == s->fetching_send_message->length) { + int64_t notify_offset = s->next_message_end_offset; + if (notify_offset <= s->flow_controlled_bytes_written) { + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->fetching_send_message_finished, GRPC_ERROR_NONE, + "fetching_send_message_finished"); } else { - t->write_cb_pool = cb->next; + grpc_chttp2_write_cb *cb = t->write_cb_pool; + if (cb == NULL) { + cb = gpr_malloc(sizeof(*cb)); + } else { + t->write_cb_pool = cb->next; + } + cb->call_at_byte = notify_offset; + cb->closure = s->fetching_send_message_finished; + s->fetching_send_message_finished = NULL; + cb->next = s->on_write_finished_cbs; + s->on_write_finished_cbs = cb; } - cb->call_at_byte = notify_offset; - cb->closure = s->fetching_send_message_finished; - s->fetching_send_message_finished = NULL; - cb->next = s->on_write_finished_cbs; - s->on_write_finished_cbs = cb; + s->fetching_send_message = NULL; + return; /* early out */ + } else if (grpc_byte_stream_next(exec_ctx, s->fetching_send_message, + &s->fetching_slice, UINT32_MAX, + &s->complete_fetch)) { + add_fetched_slice_locked(exec_ctx, t, s); } - s->fetching_send_message = NULL; - } else if (grpc_byte_stream_next(exec_ctx, s->fetching_send_message, - &s->fetching_slice, UINT32_MAX, - &s->complete_fetch)) { - add_fetched_slice_locked(exec_ctx, t, s); - } -} - -static void add_fetched_slice_locked(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - s->fetched_send_message_length += - (uint32_t)GPR_SLICE_LENGTH(s->fetching_slice); - gpr_slice_buffer_add(&s->flow_controlled_buffer, s->fetching_slice); - if (s->id != 0) { - grpc_chttp2_become_writable(exec_ctx, t, s, true, "op.send_message"); } - continue_fetching_send_locked(exec_ctx, t, s); } static void complete_fetch_locked(grpc_exec_ctx *exec_ctx, void *gs, @@ -908,6 +906,7 @@ static void complete_fetch_locked(grpc_exec_ctx *exec_ctx, void *gs, grpc_chttp2_transport *t = s->t; if (error == GRPC_ERROR_NONE) { add_fetched_slice_locked(exec_ctx, t, s); + continue_fetching_send_locked(exec_ctx, t, s); } else { /* TODO(ctiller): what to do here */ abort(); diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c index 2957d2c818..cfc072c0b5 100644 --- a/src/core/lib/channel/channel_args.c +++ b/src/core/lib/channel/channel_args.c @@ -66,22 +66,59 @@ static grpc_arg copy_arg(const grpc_arg *src) { grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, const grpc_arg *to_add, size_t num_to_add) { + return grpc_channel_args_copy_and_add_and_remove(src, NULL, 0, to_add, + num_to_add); +} + +grpc_channel_args *grpc_channel_args_copy_and_remove( + const grpc_channel_args *src, const char **to_remove, + size_t num_to_remove) { + return grpc_channel_args_copy_and_add_and_remove(src, to_remove, + num_to_remove, NULL, 0); +} + +static bool should_remove_arg(const grpc_arg *arg, const char **to_remove, + size_t num_to_remove) { + for (size_t i = 0; i < num_to_remove; ++i) { + if (strcmp(arg->key, to_remove[i]) == 0) return true; + } + return false; +} + +grpc_channel_args *grpc_channel_args_copy_and_add_and_remove( + const grpc_channel_args *src, const char **to_remove, size_t num_to_remove, + const grpc_arg *to_add, size_t num_to_add) { + // Figure out how many args we'll be copying. + size_t num_args_to_copy = 0; + if (src != NULL) { + for (size_t i = 0; i < src->num_args; ++i) { + if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { + ++num_args_to_copy; + } + } + } + // Create result. grpc_channel_args *dst = gpr_malloc(sizeof(grpc_channel_args)); - size_t i; - size_t src_num_args = (src == NULL) ? 0 : src->num_args; - if (!src && !to_add) { - dst->num_args = 0; + dst->num_args = num_args_to_copy + num_to_add; + if (dst->num_args == 0) { dst->args = NULL; return dst; } - dst->num_args = src_num_args + num_to_add; dst->args = gpr_malloc(sizeof(grpc_arg) * dst->num_args); - for (i = 0; i < src_num_args; i++) { - dst->args[i] = copy_arg(&src->args[i]); + // Copy args from src that are not being removed. + size_t dst_idx = 0; + if (src != NULL) { + for (size_t i = 0; i < src->num_args; ++i) { + if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { + dst->args[dst_idx++] = copy_arg(&src->args[i]); + } + } } - for (i = 0; i < num_to_add; i++) { - dst->args[i + src_num_args] = copy_arg(&to_add[i]); + // Add args from to_add. + for (size_t i = 0; i < num_to_add; ++i) { + dst->args[dst_idx++] = copy_arg(&to_add[i]); } + GPR_ASSERT(dst_idx == dst->num_args); return dst; } diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h index a80340c0fa..1e05303471 100644 --- a/src/core/lib/channel/channel_args.h +++ b/src/core/lib/channel/channel_args.h @@ -51,6 +51,17 @@ grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, const grpc_arg *to_add, size_t num_to_add); +/** Copies the arguments in \a src except for those whose keys are in + \a to_remove. */ +grpc_channel_args *grpc_channel_args_copy_and_remove( + const grpc_channel_args *src, const char **to_remove, size_t num_to_remove); + +/** Copies the arguments from \a src except for those whose keys are in + \a to_remove and appends the arguments in \a to_add. */ +grpc_channel_args *grpc_channel_args_copy_and_add_and_remove( + const grpc_channel_args *src, const char **to_remove, size_t num_to_remove, + const grpc_arg *to_add, size_t num_to_add); + /** Concatenate args from \a a and \a b into a new instance */ grpc_channel_args *grpc_channel_args_merge(const grpc_channel_args *a, const grpc_channel_args *b); diff --git a/src/core/lib/channel/http_server_filter.c b/src/core/lib/channel/http_server_filter.c index 0f2bf97824..f2221fb0fb 100644 --- a/src/core/lib/channel/http_server_filter.c +++ b/src/core/lib/channel/http_server_filter.c @@ -42,6 +42,8 @@ #define EXPECTED_CONTENT_TYPE "application/grpc" #define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1 +extern int grpc_http_trace; + typedef struct call_data { uint8_t seen_path; uint8_t seen_method; @@ -209,6 +211,11 @@ static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, err, GRPC_ERROR_CREATE("Missing te: trailers header")); } /* Error this call out */ + if (grpc_http_trace) { + const char *error_str = grpc_error_string(err); + gpr_log(GPR_ERROR, "Invalid http2 headers: %s", error_str); + grpc_error_free_string(error_str); + } grpc_call_element_send_cancel(exec_ctx, elem); } } else { diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c index 7f3c2d120d..55de1b5990 100644 --- a/src/core/lib/http/httpcli.c +++ b/src/core/lib/http/httpcli.c @@ -32,7 +32,6 @@ */ #include "src/core/lib/http/httpcli.h" -#include "src/core/lib/iomgr/sockaddr.h" #include <string.h> @@ -126,7 +125,7 @@ static void append_error(internal_request *req, grpc_error *error) { req->overall_error = GRPC_ERROR_CREATE("Failed HTTP/1 client request"); } grpc_resolved_address *addr = &req->addresses->addrs[req->next_address - 1]; - char *addr_text = grpc_sockaddr_to_uri((struct sockaddr *)addr->addr); + char *addr_text = grpc_sockaddr_to_uri(addr); req->overall_error = grpc_error_add_child( req->overall_error, grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, addr_text)); @@ -224,9 +223,8 @@ static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req, } addr = &req->addresses->addrs[req->next_address++]; grpc_closure_init(&req->connected, on_connected, req); - grpc_tcp_client_connect( - exec_ctx, &req->connected, &req->ep, req->context->pollset_set, - (struct sockaddr *)&addr->addr, addr->len, req->deadline); + grpc_tcp_client_connect(exec_ctx, &req->connected, &req->ep, + req->context->pollset_set, addr, req->deadline); } static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { diff --git a/src/core/lib/iomgr/endpoint_pair_posix.c b/src/core/lib/iomgr/endpoint_pair_posix.c index e295fb4867..ec2cd782b1 100644 --- a/src/core/lib/iomgr/endpoint_pair_posix.c +++ b/src/core/lib/iomgr/endpoint_pair_posix.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET #include "src/core/lib/iomgr/endpoint_pair.h" #include "src/core/lib/iomgr/socket_utils_posix.h" diff --git a/src/core/lib/security/credentials/google_default/credentials_windows.c b/src/core/lib/iomgr/endpoint_pair_uv.c index 208b8fd9ad..7941e20388 100644 --- a/src/core/lib/security/credentials/google_default/credentials_windows.c +++ b/src/core/lib/iomgr/endpoint_pair_uv.c @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,31 +31,23 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINDOWS +#ifdef GRPC_UV -#include "src/core/lib/security/credentials/google_default/google_default_credentials.h" +#include <stdlib.h> -#include <grpc/support/alloc.h> #include <grpc/support/log.h> -#include <grpc/support/string_util.h> -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" +#include "src/core/lib/iomgr/endpoint_pair.h" -char *grpc_get_well_known_google_credentials_file_path_impl(void) { - char *result = NULL; - char *appdata_path = gpr_getenv("APPDATA"); - if (appdata_path == NULL) { - gpr_log(GPR_ERROR, "Could not get APPDATA environment variable."); - return NULL; - } - gpr_asprintf(&result, "%s/%s/%s", appdata_path, - GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY, - GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE); - gpr_free(appdata_path); - return result; +grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, + size_t read_slice_size) { + grpc_endpoint_pair endpoint_pair; + // TODO(mlumish): implement this properly under libuv + GPR_ASSERT(false && + "grpc_iomgr_create_endpoint_pair is not suppoted with libuv"); + return endpoint_pair; } -#endif /* GPR_WINDOWS */ +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/endpoint_pair_windows.c b/src/core/lib/iomgr/endpoint_pair_windows.c index 582704e267..5c78c95492 100644 --- a/src/core/lib/iomgr/endpoint_pair_windows.c +++ b/src/core/lib/iomgr/endpoint_pair_windows.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINSOCK_SOCKET +#ifdef GRPC_WINSOCK_SOCKET #include "src/core/lib/iomgr/endpoint_pair.h" #include "src/core/lib/iomgr/sockaddr_utils.h" diff --git a/src/core/lib/iomgr/ev_epoll_linux.c b/src/core/lib/iomgr/ev_epoll_linux.c index e5909d9380..8381f4a63a 100644 --- a/src/core/lib/iomgr/ev_epoll_linux.c +++ b/src/core/lib/iomgr/ev_epoll_linux.c @@ -32,10 +32,10 @@ */ #include <grpc/grpc_posix.h> -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" /* This polling engine is only relevant on linux kernels supporting epoll() */ -#ifdef GPR_LINUX_EPOLL +#ifdef GRPC_LINUX_EPOLL #include "src/core/lib/iomgr/ev_epoll_linux.h" @@ -2049,13 +2049,13 @@ const grpc_event_engine_vtable *grpc_init_epoll_linux(void) { return &vtable; } -#else /* defined(GPR_LINUX_EPOLL) */ -#if defined(GPR_POSIX_SOCKET) +#else /* defined(GRPC_LINUX_EPOLL) */ +#if defined(GRPC_POSIX_SOCKET) #include "src/core/lib/iomgr/ev_posix.h" -/* If GPR_LINUX_EPOLL is not defined, it means epoll is not available. Return +/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return * NULL */ const grpc_event_engine_vtable *grpc_init_epoll_linux(void) { return NULL; } -#endif /* defined(GPR_POSIX_SOCKET) */ +#endif /* defined(GRPC_POSIX_SOCKET) */ void grpc_use_signal(int signum) {} -#endif /* !defined(GPR_LINUX_EPOLL) */ +#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/src/core/lib/iomgr/ev_epoll_linux.h b/src/core/lib/iomgr/ev_epoll_linux.h index 7a494aba19..8fc3ff59a3 100644 --- a/src/core/lib/iomgr/ev_epoll_linux.h +++ b/src/core/lib/iomgr/ev_epoll_linux.h @@ -35,13 +35,14 @@ #define GRPC_CORE_LIB_IOMGR_EV_EPOLL_LINUX_H #include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/port.h" const grpc_event_engine_vtable *grpc_init_epoll_linux(void); -#ifdef GPR_LINUX_EPOLL +#ifdef GRPC_LINUX_EPOLL void *grpc_fd_get_polling_island(grpc_fd *fd); void *grpc_pollset_get_polling_island(grpc_pollset *ps); bool grpc_are_polling_islands_equal(void *p, void *q); -#endif /* defined(GPR_LINUX_EPOLL) */ +#endif /* defined(GRPC_LINUX_EPOLL) */ #endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLL_LINUX_H */ diff --git a/src/core/lib/iomgr/ev_poll_and_epoll_posix.c b/src/core/lib/iomgr/ev_poll_and_epoll_posix.c index 1829440a6e..bf51404203 100644 --- a/src/core/lib/iomgr/ev_poll_and_epoll_posix.c +++ b/src/core/lib/iomgr/ev_poll_and_epoll_posix.c @@ -42,9 +42,9 @@ * - ev_epoll_posix.{h,c} */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET #include "src/core/lib/iomgr/ev_poll_and_epoll_posix.h" @@ -1338,7 +1338,7 @@ static void become_basic_pollset(grpc_pollset *pollset, grpc_fd *fd_or_null) { * pollset_multipoller_with_poll_posix.c */ -#ifndef GPR_LINUX_MULTIPOLL_WITH_EPOLL +#ifndef GRPC_LINUX_MULTIPOLL_WITH_EPOLL typedef struct { /* all polled fds */ @@ -1520,13 +1520,13 @@ static void poll_become_multipoller(grpc_exec_ctx *exec_ctx, } } -#endif /* !GPR_LINUX_MULTIPOLL_WITH_EPOLL */ +#endif /* !GRPC_LINUX_MULTIPOLL_WITH_EPOLL */ /******************************************************************************* * pollset_multipoller_with_epoll_posix.c */ -#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL +#ifdef GRPC_LINUX_MULTIPOLL_WITH_EPOLL #include <errno.h> #include <poll.h> @@ -1839,11 +1839,11 @@ static void epoll_become_multipoller(grpc_exec_ctx *exec_ctx, } } -#else /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */ +#else /* GRPC_LINUX_MULTIPOLL_WITH_EPOLL */ static void remove_fd_from_all_epoll_sets(int fd) {} -#endif /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */ +#endif /* GRPC_LINUX_MULTIPOLL_WITH_EPOLL */ /******************************************************************************* * pollset_set_posix.c @@ -2063,7 +2063,7 @@ static const grpc_event_engine_vtable vtable = { }; const grpc_event_engine_vtable *grpc_init_poll_and_epoll_posix(void) { -#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL +#ifdef GRPC_LINUX_MULTIPOLL_WITH_EPOLL platform_become_multipoller = epoll_become_multipoller; #else platform_become_multipoller = poll_become_multipoller; diff --git a/src/core/lib/iomgr/ev_poll_posix.c b/src/core/lib/iomgr/ev_poll_posix.c index 27e966c18c..e1d620cfff 100644 --- a/src/core/lib/iomgr/ev_poll_posix.c +++ b/src/core/lib/iomgr/ev_poll_posix.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET #include "src/core/lib/iomgr/ev_poll_posix.h" diff --git a/src/core/lib/iomgr/ev_posix.c b/src/core/lib/iomgr/ev_posix.c index 9857b0bce9..ef36ba89b2 100644 --- a/src/core/lib/iomgr/ev_posix.c +++ b/src/core/lib/iomgr/ev_posix.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET #include "src/core/lib/iomgr/ev_posix.h" @@ -282,4 +282,4 @@ void grpc_workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue, g_event_engine->workqueue_enqueue(exec_ctx, workqueue, closure, error); } -#endif // GPR_POSIX_SOCKET +#endif // GRPC_POSIX_SOCKET diff --git a/src/core/lib/iomgr/iocp_windows.c b/src/core/lib/iomgr/iocp_windows.c index 2532e52e48..60ebe43676 100644 --- a/src/core/lib/iomgr/iocp_windows.c +++ b/src/core/lib/iomgr/iocp_windows.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINSOCK_SOCKET +#ifdef GRPC_WINSOCK_SOCKET #include <winsock2.h> @@ -166,4 +166,4 @@ void grpc_iocp_add_socket(grpc_winsocket *socket) { GPR_ASSERT(ret == g_iocp); } -#endif /* GPR_WINSOCK_SOCKET */ +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/iomgr.h b/src/core/lib/iomgr/iomgr.h index 6c82de78ac..c1cfaf302e 100644 --- a/src/core/lib/iomgr/iomgr.h +++ b/src/core/lib/iomgr/iomgr.h @@ -34,6 +34,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_IOMGR_H #define GRPC_CORE_LIB_IOMGR_IOMGR_H +#include "src/core/lib/iomgr/port.h" + /** Initializes the iomgr. */ void grpc_iomgr_init(void); diff --git a/src/core/lib/iomgr/iomgr_posix.c b/src/core/lib/iomgr/iomgr_posix.c index cede97f4c6..f5ee0c9ee4 100644 --- a/src/core/lib/iomgr/iomgr_posix.c +++ b/src/core/lib/iomgr/iomgr_posix.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/ev_posix.h" diff --git a/src/core/lib/iomgr/iomgr_uv.c b/src/core/lib/iomgr/iomgr_uv.c new file mode 100644 index 0000000000..96516ff167 --- /dev/null +++ b/src/core/lib/iomgr/iomgr_uv.c @@ -0,0 +1,49 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/pollset_uv.h" +#include "src/core/lib/iomgr/tcp_uv.h" + +void grpc_iomgr_platform_init(void) { + grpc_pollset_global_init(); + grpc_register_tracer("tcp", &grpc_tcp_trace); +} +void grpc_iomgr_platform_flush(void) {} +void grpc_iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); } + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/iomgr_windows.c b/src/core/lib/iomgr/iomgr_windows.c index 7653f6e635..b659264ede 100644 --- a/src/core/lib/iomgr/iomgr_windows.c +++ b/src/core/lib/iomgr/iomgr_windows.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINSOCK_SOCKET +#ifdef GRPC_WINSOCK_SOCKET #include "src/core/lib/iomgr/sockaddr_windows.h" diff --git a/src/core/lib/iomgr/pollset_set_uv.c b/src/core/lib/iomgr/pollset_set_uv.c new file mode 100644 index 0000000000..e5ef8b29e0 --- /dev/null +++ b/src/core/lib/iomgr/pollset_set_uv.c @@ -0,0 +1,62 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include "src/core/lib/iomgr/pollset_set.h" + +grpc_pollset_set* grpc_pollset_set_create(void) { + return (grpc_pollset_set*)((intptr_t)0xdeafbeef); +} + +void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) {} + +void grpc_pollset_set_add_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void grpc_pollset_set_del_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void grpc_pollset_set_add_pollset_set(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +void grpc_pollset_set_del_pollset_set(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/pollset_set_windows.c b/src/core/lib/iomgr/pollset_set_windows.c index a35a9766fc..645650db9b 100644 --- a/src/core/lib/iomgr/pollset_set_windows.c +++ b/src/core/lib/iomgr/pollset_set_windows.c @@ -31,10 +31,10 @@ * */ -#include <grpc/support/port_platform.h> #include <stdint.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINSOCK_SOCKET +#ifdef GRPC_WINSOCK_SOCKET #include "src/core/lib/iomgr/pollset_set_windows.h" @@ -60,4 +60,4 @@ void grpc_pollset_set_del_pollset_set(grpc_exec_ctx* exec_ctx, grpc_pollset_set* bag, grpc_pollset_set* item) {} -#endif /* GPR_WINSOCK_SOCKET */ +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/pollset_uv.c b/src/core/lib/iomgr/pollset_uv.c new file mode 100644 index 0000000000..3a74b842b6 --- /dev/null +++ b/src/core/lib/iomgr/pollset_uv.c @@ -0,0 +1,142 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include <uv.h> + +#include <string.h> + +#include <grpc/support/log.h> +#include <grpc/support/sync.h> + +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_uv.h" + +struct grpc_pollset { + uv_timer_t timer; + int shutting_down; +}; + +/* Indicates that grpc_pollset_work should run an iteration of the UV loop + before running callbacks. This defaults to 1, and should be disabled if + grpc_pollset_work will be called within the callstack of uv_run */ +int grpc_pollset_work_run_loop; + +gpr_mu grpc_polling_mu; + +size_t grpc_pollset_size() { return sizeof(grpc_pollset); } + +void grpc_pollset_global_init(void) { + gpr_mu_init(&grpc_polling_mu); + grpc_pollset_work_run_loop = 1; +} + +void grpc_pollset_global_shutdown(void) { gpr_mu_destroy(&grpc_polling_mu); } + +void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) { + *mu = &grpc_polling_mu; + memset(pollset, 0, sizeof(grpc_pollset)); + uv_timer_init(uv_default_loop(), &pollset->timer); + pollset->shutting_down = 0; +} + +static void timer_close_cb(uv_handle_t *handle) { handle->data = (void *)1; } + +void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { + GPR_ASSERT(!pollset->shutting_down); + pollset->shutting_down = 1; + if (grpc_pollset_work_run_loop) { + // Drain any pending UV callbacks without blocking + uv_run(uv_default_loop(), UV_RUN_NOWAIT); + } + grpc_exec_ctx_sched(exec_ctx, closure, GRPC_ERROR_NONE, NULL); +} + +void grpc_pollset_destroy(grpc_pollset *pollset) { + uv_close((uv_handle_t *)&pollset->timer, timer_close_cb); + // timer.data is a boolean indicating that the timer has finished closing + pollset->timer.data = (void *)0; + if (grpc_pollset_work_run_loop) { + while (!pollset->timer.data) { + uv_run(uv_default_loop(), UV_RUN_NOWAIT); + } + } +} + +void grpc_pollset_reset(grpc_pollset *pollset) { + GPR_ASSERT(pollset->shutting_down); + pollset->shutting_down = 0; +} + +static void timer_run_cb(uv_timer_t *timer) {} + +grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker **worker_hdl, + gpr_timespec now, gpr_timespec deadline) { + uint64_t timeout; + gpr_mu_unlock(&grpc_polling_mu); + if (grpc_pollset_work_run_loop) { + if (gpr_time_cmp(deadline, now) >= 0) { + timeout = (uint64_t)gpr_time_to_millis(gpr_time_sub(deadline, now)); + } else { + timeout = 0; + } + /* We special-case timeout=0 so that we don't bother with the timer when + the loop won't block anyway */ + if (timeout > 0) { + uv_timer_start(&pollset->timer, timer_run_cb, timeout, 0); + /* Run until there is some I/O activity or the timer triggers. It doesn't + matter which happens */ + uv_run(uv_default_loop(), UV_RUN_ONCE); + uv_timer_stop(&pollset->timer); + } else { + uv_run(uv_default_loop(), UV_RUN_NOWAIT); + } + } + if (!grpc_closure_list_empty(exec_ctx->closure_list)) { + grpc_exec_ctx_flush(exec_ctx); + } + gpr_mu_lock(&grpc_polling_mu); + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_pollset_kick(grpc_pollset *pollset, + grpc_pollset_worker *specific_worker) { + return GRPC_ERROR_NONE; +} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/pollset_uv.h b/src/core/lib/iomgr/pollset_uv.h new file mode 100644 index 0000000000..0715eb4295 --- /dev/null +++ b/src/core/lib/iomgr/pollset_uv.h @@ -0,0 +1,42 @@ +/* + * + * 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_LIB_IOMGR_POLLSET_UV_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_UV_H + +extern int grpc_pollset_work_run_loop; + +void grpc_pollset_global_init(void); +void grpc_pollset_global_shutdown(void); + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_UV_H */ diff --git a/src/core/lib/iomgr/pollset_windows.c b/src/core/lib/iomgr/pollset_windows.c index 626dd784b3..5540303e49 100644 --- a/src/core/lib/iomgr/pollset_windows.c +++ b/src/core/lib/iomgr/pollset_windows.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINSOCK_SOCKET +#ifdef GRPC_WINSOCK_SOCKET #include <grpc/support/log.h> #include <grpc/support/thd.h> @@ -241,4 +241,4 @@ grpc_error *grpc_pollset_kick(grpc_pollset *p, void grpc_kick_poller(void) { grpc_iocp_kick(); } -#endif /* GPR_WINSOCK_SOCKET */ +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/port.h b/src/core/lib/iomgr/port.h new file mode 100644 index 0000000000..c0bb3b5a23 --- /dev/null +++ b/src/core/lib/iomgr/port.h @@ -0,0 +1,131 @@ +/* + * + * 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> + +#ifndef GRPC_CORE_LIB_IOMGR_PORT_H +#define GRPC_CORE_LIB_IOMGR_PORT_H + +#if defined(GRPC_UV) +// Do nothing +#elif defined(GPR_MANYLINUX1) +#define GRPC_HAVE_IPV6_RECVPKTINFO 1 +#define GRPC_HAVE_IP_PKTINFO 1 +#define GRPC_HAVE_MSG_NOSIGNAL 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETADDR 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#define GRPC_TIMER_USE_GENERIC 1 +#elif defined(GPR_WINDOWS) +#define GRPC_TIMER_USE_GENERIC 1 +#define GRPC_WINSOCK_SOCKET 1 +#define GRPC_WINDOWS_SOCKETUTILS 1 +#elif defined(GPR_ANDROID) +#define GRPC_HAVE_IPV6_RECVPKTINFO 1 +#define GRPC_HAVE_IP_PKTINFO 1 +#define GRPC_HAVE_MSG_NOSIGNAL 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_LINUX_EVENTFD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETADDR 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#define GRPC_TIMER_USE_GENERIC 1 +#elif defined(GPR_LINUX) +#define GRPC_HAVE_IPV6_RECVPKTINFO 1 +#define GRPC_HAVE_IP_PKTINFO 1 +#define GRPC_HAVE_MSG_NOSIGNAL 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_LINUX_MULTIPOLL_WITH_EPOLL 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETADDR 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#define GRPC_TIMER_USE_GENERIC 1 +#ifdef __GLIBC_PREREQ +#if __GLIBC_PREREQ(2, 9) +#define GRPC_LINUX_EPOLL 1 +#define GRPC_LINUX_EVENTFD 1 +#endif +#if __GLIBC_PREREQ(2, 10) +#define GRPC_LINUX_SOCKETUTILS 1 +#endif +#endif +#ifndef GRPC_LINUX_EVENTFD +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#endif +#ifndef GRPC_LINUX_SOCKETUTILS +#define GRPC_POSIX_SOCKETUTILS +#endif +#elif defined(GPR_APPLE) +#define GRPC_HAVE_IP_PKTINFO 1 +#define GRPC_HAVE_SO_NOSIGPIPE 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_MSG_IOVLEN_TYPE int +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETADDR 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#define GRPC_TIMER_USE_GENERIC 1 +#elif defined(GPR_FREEBSD) +#define GRPC_HAVE_IPV6_RECVPKTINFO 1 +#define GRPC_HAVE_IP_PKTINFO 1 +#define GRPC_HAVE_SO_NOSIGPIPE 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETADDR 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#define GRPC_TIMER_USE_GENERIC 1 +#elif defined(GPR_NACL) +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETADDR 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#define GRPC_TIMER_USE_GENERIC 1 +#elif !defined(GPR_NO_AUTODETECT_PLATFORM) +#error "Platform not recognized" +#endif + +#if defined(GRPC_POSIX_SOCKET) + defined(GRPC_WINSOCK_SOCKET) + \ + defined(GRPC_CUSTOM_SOCKET) + defined(GRPC_UV) != \ + 1 +#error Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GPR_CUSTOM_SOCKET +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_PORT_H */ diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h index ddbe375755..275924448a 100644 --- a/src/core/lib/iomgr/resolve_address.h +++ b/src/core/lib/iomgr/resolve_address.h @@ -36,7 +36,6 @@ #include <stddef.h> #include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/iomgr.h" #define GRPC_MAX_SOCKADDR_SIZE 128 diff --git a/src/core/lib/iomgr/resolve_address_posix.c b/src/core/lib/iomgr/resolve_address_posix.c index 4e9f978584..de791b2b67 100644 --- a/src/core/lib/iomgr/resolve_address_posix.c +++ b/src/core/lib/iomgr/resolve_address_posix.c @@ -31,12 +31,13 @@ * */ -#include <grpc/support/port_platform.h> -#ifdef GPR_POSIX_SOCKET +#include "src/core/lib/iomgr/port.h" +#ifdef GRPC_POSIX_SOCKET -#include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/resolve_address.h" + #include <string.h> #include <sys/types.h> @@ -49,7 +50,6 @@ #include <grpc/support/useful.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/iomgr/unix_sockets_posix.h" #include "src/core/lib/support/block_annotate.h" #include "src/core/lib/support/string.h" diff --git a/src/core/lib/iomgr/resolve_address_uv.c b/src/core/lib/iomgr/resolve_address_uv.c new file mode 100644 index 0000000000..b8295acfa1 --- /dev/null +++ b/src/core/lib/iomgr/resolve_address_uv.c @@ -0,0 +1,231 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" +#ifdef GRPC_UV + +#include <uv.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/host_port.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +#include <string.h> + +typedef struct request { + grpc_closure *on_done; + grpc_resolved_addresses **addresses; + struct addrinfo *hints; +} request; + +static grpc_error *handle_addrinfo_result(int status, struct addrinfo *result, + grpc_resolved_addresses **addresses) { + struct addrinfo *resp; + size_t i; + if (status != 0) { + grpc_error *error; + *addresses = NULL; + error = GRPC_ERROR_CREATE("getaddrinfo failed"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, uv_strerror(status)); + return error; + } + (*addresses) = gpr_malloc(sizeof(grpc_resolved_addresses)); + (*addresses)->naddrs = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + (*addresses)->naddrs++; + } + (*addresses)->addrs = + gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs); + i = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + (*addresses)->addrs[i].len = resp->ai_addrlen; + i++; + } + + { + for (i = 0; i < (*addresses)->naddrs; i++) { + char *buf; + grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0); + gpr_free(buf); + } + } + return GRPC_ERROR_NONE; +} + +static void getaddrinfo_callback(uv_getaddrinfo_t *req, int status, + struct addrinfo *res) { + request *r = (request *)req->data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_error *error; + error = handle_addrinfo_result(status, res, r->addresses); + grpc_exec_ctx_sched(&exec_ctx, r->on_done, error, NULL); + grpc_exec_ctx_finish(&exec_ctx); + + gpr_free(r->hints); + gpr_free(r); + gpr_free(req); + uv_freeaddrinfo(res); +} + +static grpc_error *try_split_host_port(const char *name, + const char *default_port, char **host, + char **port) { + /* parse name, splitting it into host and port parts */ + grpc_error *error; + gpr_split_host_port(name, host, port); + if (host == NULL) { + char *msg; + gpr_asprintf(&msg, "unparseable host:port: '%s'", name); + error = GRPC_ERROR_CREATE(msg); + gpr_free(msg); + return error; + } + if (port == NULL) { + if (default_port == NULL) { + char *msg; + gpr_asprintf(&msg, "no port in name '%s'", name); + error = GRPC_ERROR_CREATE(msg); + gpr_free(msg); + return error; + } + *port = gpr_strdup(default_port); + } + return GRPC_ERROR_NONE; +} + +static grpc_error *blocking_resolve_address_impl( + const char *name, const char *default_port, + grpc_resolved_addresses **addresses) { + char *host; + char *port; + struct addrinfo hints; + uv_getaddrinfo_t req; + int s; + grpc_error *err; + + req.addrinfo = NULL; + + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + goto done; + } + + /* Call getaddrinfo */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + + s = uv_getaddrinfo(uv_default_loop(), &req, NULL, host, port, &hints); + err = handle_addrinfo_result(s, req.addrinfo, addresses); + +done: + gpr_free(host); + gpr_free(port); + if (req.addrinfo) { + uv_freeaddrinfo(req.addrinfo); + } + return err; +} + +grpc_error *(*grpc_blocking_resolve_address)( + const char *name, const char *default_port, + grpc_resolved_addresses **addresses) = blocking_resolve_address_impl; + +void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { + if (addrs != NULL) { + gpr_free(addrs->addrs); + } + gpr_free(addrs); +} + +static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name, + const char *default_port, + grpc_closure *on_done, + grpc_resolved_addresses **addrs) { + uv_getaddrinfo_t *req; + request *r; + struct addrinfo *hints; + char *host; + char *port; + grpc_error *err; + int s; + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + grpc_exec_ctx_sched(exec_ctx, on_done, err, NULL); + return; + } + r = gpr_malloc(sizeof(request)); + r->on_done = on_done; + r->addresses = addrs; + req = gpr_malloc(sizeof(uv_getaddrinfo_t)); + req->data = r; + + /* Call getaddrinfo */ + hints = gpr_malloc(sizeof(struct addrinfo)); + memset(hints, 0, sizeof(struct addrinfo)); + hints->ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints->ai_socktype = SOCK_STREAM; /* stream socket */ + hints->ai_flags = AI_PASSIVE; /* for wildcard IP address */ + r->hints = hints; + + s = uv_getaddrinfo(uv_default_loop(), req, getaddrinfo_callback, host, port, + hints); + + if (s != 0) { + *addrs = NULL; + err = GRPC_ERROR_CREATE("getaddrinfo failed"); + err = grpc_error_set_str(err, GRPC_ERROR_STR_OS_ERROR, uv_strerror(s)); + grpc_exec_ctx_sched(exec_ctx, on_done, err, NULL); + gpr_free(r); + gpr_free(req); + gpr_free(hints); + } +} + +void (*grpc_resolve_address)(grpc_exec_ctx *exec_ctx, const char *name, + const char *default_port, grpc_closure *on_done, + grpc_resolved_addresses **addrs) = + resolve_address_impl; + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/resolve_address_windows.c b/src/core/lib/iomgr/resolve_address_windows.c index 2af8af82dc..e139293c03 100644 --- a/src/core/lib/iomgr/resolve_address_windows.c +++ b/src/core/lib/iomgr/resolve_address_windows.c @@ -31,12 +31,13 @@ * */ -#include <grpc/support/port_platform.h> -#ifdef GPR_WINSOCK_SOCKET +#include "src/core/lib/iomgr/port.h" +#ifdef GRPC_WINSOCK_SOCKET -#include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/resolve_address.h" + #include <string.h> #include <sys/types.h> @@ -124,8 +125,7 @@ static grpc_error *blocking_resolve_address_impl( { for (i = 0; i < (*addresses)->naddrs; i++) { char *buf; - grpc_sockaddr_to_string( - &buf, (struct sockaddr *)&(*addresses)->addrs[i].addr, 0); + grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0); gpr_free(buf); } } diff --git a/src/core/lib/iomgr/sockaddr.h b/src/core/lib/iomgr/sockaddr.h index 5563d0b8a6..52b504390d 100644 --- a/src/core/lib/iomgr/sockaddr.h +++ b/src/core/lib/iomgr/sockaddr.h @@ -31,16 +31,24 @@ * */ +/* This header transitively includes other headers that care about include + * order, so it should be included first. As a consequence, it should not be + * included in any other header. */ + #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_H #define GRPC_CORE_LIB_IOMGR_SOCKADDR_H -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV +#include <uv.h> +#endif #ifdef GPR_WINDOWS #include "src/core/lib/iomgr/sockaddr_windows.h" #endif -#ifdef GPR_POSIX_SOCKETADDR +#ifdef GRPC_POSIX_SOCKETADDR #include "src/core/lib/iomgr/sockaddr_posix.h" #endif diff --git a/src/core/lib/iomgr/sockaddr_utils.c b/src/core/lib/iomgr/sockaddr_utils.c index 127d95c618..44bc2f968b 100644 --- a/src/core/lib/iomgr/sockaddr_utils.c +++ b/src/core/lib/iomgr/sockaddr_utils.c @@ -42,26 +42,32 @@ #include <grpc/support/port_platform.h> #include <grpc/support/string_util.h> +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" #include "src/core/lib/iomgr/unix_sockets_posix.h" #include "src/core/lib/support/string.h" static const uint8_t kV4MappedPrefix[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; -int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr, - struct sockaddr_in *addr4_out) { - GPR_ASSERT(addr != (struct sockaddr *)addr4_out); +int grpc_sockaddr_is_v4mapped(const grpc_resolved_address *resolved_addr, + grpc_resolved_address *resolved_addr4_out) { + GPR_ASSERT(resolved_addr != resolved_addr4_out); + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + struct sockaddr_in *addr4_out = + (struct sockaddr_in *)resolved_addr4_out->addr; if (addr->sa_family == AF_INET6) { const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix, sizeof(kV4MappedPrefix)) == 0) { - if (addr4_out != NULL) { + if (resolved_addr4_out != NULL) { /* Normalize ::ffff:0.0.0.0/96 to IPv4. */ - memset(addr4_out, 0, sizeof(*addr4_out)); + memset(resolved_addr4_out, 0, sizeof(*resolved_addr4_out)); addr4_out->sin_family = AF_INET; /* s6_addr32 would be nice, but it's non-standard. */ memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4); addr4_out->sin_port = addr6->sin6_port; + resolved_addr4_out->len = sizeof(struct sockaddr_in); } return 1; } @@ -69,26 +75,33 @@ int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr, return 0; } -int grpc_sockaddr_to_v4mapped(const struct sockaddr *addr, - struct sockaddr_in6 *addr6_out) { - GPR_ASSERT(addr != (struct sockaddr *)addr6_out); +int grpc_sockaddr_to_v4mapped(const grpc_resolved_address *resolved_addr, + grpc_resolved_address *resolved_addr6_out) { + GPR_ASSERT(resolved_addr != resolved_addr6_out); + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + struct sockaddr_in6 *addr6_out = + (struct sockaddr_in6 *)resolved_addr6_out->addr; if (addr->sa_family == AF_INET) { const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; - memset(addr6_out, 0, sizeof(*addr6_out)); + memset(resolved_addr6_out, 0, sizeof(*resolved_addr6_out)); addr6_out->sin6_family = AF_INET6; memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12); memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4); addr6_out->sin6_port = addr4->sin_port; + resolved_addr6_out->len = sizeof(struct sockaddr_in6); return 1; } return 0; } -int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out) { - struct sockaddr_in addr4_normalized; - if (grpc_sockaddr_is_v4mapped(addr, &addr4_normalized)) { - addr = (struct sockaddr *)&addr4_normalized; +int grpc_sockaddr_is_wildcard(const grpc_resolved_address *resolved_addr, + int *port_out) { + const struct sockaddr *addr; + grpc_resolved_address addr4_normalized; + if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr4_normalized)) { + resolved_addr = &addr4_normalized; } + addr = (const struct sockaddr *)resolved_addr->addr; if (addr->sa_family == AF_INET) { /* Check for 0.0.0.0 */ const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; @@ -113,39 +126,49 @@ int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out) { } } -void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out, - struct sockaddr_in6 *wild6_out) { +void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address *wild4_out, + grpc_resolved_address *wild6_out) { grpc_sockaddr_make_wildcard4(port, wild4_out); grpc_sockaddr_make_wildcard6(port, wild6_out); } -void grpc_sockaddr_make_wildcard4(int port, struct sockaddr_in *wild_out) { +void grpc_sockaddr_make_wildcard4(int port, + grpc_resolved_address *resolved_wild_out) { + struct sockaddr_in *wild_out = (struct sockaddr_in *)resolved_wild_out->addr; GPR_ASSERT(port >= 0 && port < 65536); - memset(wild_out, 0, sizeof(*wild_out)); + memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); wild_out->sin_family = AF_INET; wild_out->sin_port = htons((uint16_t)port); + resolved_wild_out->len = sizeof(struct sockaddr_in); } -void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out) { +void grpc_sockaddr_make_wildcard6(int port, + grpc_resolved_address *resolved_wild_out) { + struct sockaddr_in6 *wild_out = + (struct sockaddr_in6 *)resolved_wild_out->addr; GPR_ASSERT(port >= 0 && port < 65536); - memset(wild_out, 0, sizeof(*wild_out)); + memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); wild_out->sin6_family = AF_INET6; wild_out->sin6_port = htons((uint16_t)port); + resolved_wild_out->len = sizeof(struct sockaddr_in6); } -int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr, +int grpc_sockaddr_to_string(char **out, + const grpc_resolved_address *resolved_addr, int normalize) { + const struct sockaddr *addr; const int save_errno = errno; - struct sockaddr_in addr_normalized; + grpc_resolved_address addr_normalized; char ntop_buf[INET6_ADDRSTRLEN]; const void *ip = NULL; int port; int ret; *out = NULL; - if (normalize && grpc_sockaddr_is_v4mapped(addr, &addr_normalized)) { - addr = (const struct sockaddr *)&addr_normalized; + if (normalize && grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { + resolved_addr = &addr_normalized; } + addr = (const struct sockaddr *)resolved_addr->addr; if (addr->sa_family == AF_INET) { const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; ip = &addr4->sin_addr; @@ -155,10 +178,8 @@ int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr, ip = &addr6->sin6_addr; port = ntohs(addr6->sin6_port); } - /* Windows inet_ntop wants a mutable ip pointer */ if (ip != NULL && - inet_ntop(addr->sa_family, (void *)ip, ntop_buf, sizeof(ntop_buf)) != - NULL) { + grpc_inet_ntop(addr->sa_family, ip, ntop_buf, sizeof(ntop_buf)) != NULL) { ret = gpr_join_host_port(out, ntop_buf, port); } else { ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family); @@ -168,39 +189,43 @@ int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr, return ret; } -char *grpc_sockaddr_to_uri(const struct sockaddr *addr) { +char *grpc_sockaddr_to_uri(const grpc_resolved_address *resolved_addr) { char *temp; char *result; - struct sockaddr_in addr_normalized; + grpc_resolved_address addr_normalized; + const struct sockaddr *addr; - if (grpc_sockaddr_is_v4mapped(addr, &addr_normalized)) { - addr = (const struct sockaddr *)&addr_normalized; + if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { + resolved_addr = &addr_normalized; } + addr = (const struct sockaddr *)resolved_addr->addr; + switch (addr->sa_family) { case AF_INET: - grpc_sockaddr_to_string(&temp, addr, 0); + grpc_sockaddr_to_string(&temp, resolved_addr, 0); gpr_asprintf(&result, "ipv4:%s", temp); gpr_free(temp); return result; case AF_INET6: - grpc_sockaddr_to_string(&temp, addr, 0); + grpc_sockaddr_to_string(&temp, resolved_addr, 0); gpr_asprintf(&result, "ipv6:%s", temp); gpr_free(temp); return result; default: - return grpc_sockaddr_to_uri_unix_if_possible(addr); + return grpc_sockaddr_to_uri_unix_if_possible(resolved_addr); } } -int grpc_sockaddr_get_port(const struct sockaddr *addr) { +int grpc_sockaddr_get_port(const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; switch (addr->sa_family) { case AF_INET: return ntohs(((struct sockaddr_in *)addr)->sin_port); case AF_INET6: return ntohs(((struct sockaddr_in6 *)addr)->sin6_port); default: - if (grpc_is_unix_socket(addr)) { + if (grpc_is_unix_socket(resolved_addr)) { return 1; } gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_get_port", @@ -209,7 +234,9 @@ int grpc_sockaddr_get_port(const struct sockaddr *addr) { } } -int grpc_sockaddr_set_port(const struct sockaddr *addr, int port) { +int grpc_sockaddr_set_port(const grpc_resolved_address *resolved_addr, + int port) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; switch (addr->sa_family) { case AF_INET: GPR_ASSERT(port >= 0 && port < 65536); diff --git a/src/core/lib/iomgr/sockaddr_utils.h b/src/core/lib/iomgr/sockaddr_utils.h index 9f81992e6b..5371e360c5 100644 --- a/src/core/lib/iomgr/sockaddr_utils.h +++ b/src/core/lib/iomgr/sockaddr_utils.h @@ -34,40 +34,40 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H #define GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H -#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/resolve_address.h" /* Returns true if addr is an IPv4-mapped IPv6 address within the ::ffff:0.0.0.0/96 range, or false otherwise. If addr4_out is non-NULL, the inner IPv4 address will be copied here when returning true. */ -int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr, - struct sockaddr_in *addr4_out); +int grpc_sockaddr_is_v4mapped(const grpc_resolved_address *addr, + grpc_resolved_address *addr4_out); /* If addr is an AF_INET address, writes the corresponding ::ffff:0.0.0.0/96 address to addr6_out and returns true. Otherwise returns false. */ -int grpc_sockaddr_to_v4mapped(const struct sockaddr *addr, - struct sockaddr_in6 *addr6_out); +int grpc_sockaddr_to_v4mapped(const grpc_resolved_address *addr, + grpc_resolved_address *addr6_out); /* If addr is ::, 0.0.0.0, or ::ffff:0.0.0.0, writes the port number to *port_out (if not NULL) and returns true, otherwise returns false. */ -int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out); +int grpc_sockaddr_is_wildcard(const grpc_resolved_address *addr, int *port_out); /* Writes 0.0.0.0:port and [::]:port to separate sockaddrs. */ -void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out, - struct sockaddr_in6 *wild6_out); +void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address *wild4_out, + grpc_resolved_address *wild6_out); /* Writes 0.0.0.0:port. */ -void grpc_sockaddr_make_wildcard4(int port, struct sockaddr_in *wild_out); +void grpc_sockaddr_make_wildcard4(int port, grpc_resolved_address *wild_out); /* Writes [::]:port. */ -void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out); +void grpc_sockaddr_make_wildcard6(int port, grpc_resolved_address *wild_out); /* Return the IP port number of a sockaddr */ -int grpc_sockaddr_get_port(const struct sockaddr *addr); +int grpc_sockaddr_get_port(const grpc_resolved_address *addr); /* Set IP port number of a sockaddr */ -int grpc_sockaddr_set_port(const struct sockaddr *addr, int port); +int grpc_sockaddr_set_port(const grpc_resolved_address *addr, int port); /* Converts a sockaddr into a newly-allocated human-readable string. @@ -81,9 +81,9 @@ int grpc_sockaddr_set_port(const struct sockaddr *addr, int port); In the unlikely event of an error, returns -1 and sets *out to NULL. The existing value of errno is always preserved. */ -int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr, +int grpc_sockaddr_to_string(char **out, const grpc_resolved_address *addr, int normalize); -char *grpc_sockaddr_to_uri(const struct sockaddr *addr); +char *grpc_sockaddr_to_uri(const grpc_resolved_address *addr); #endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H */ diff --git a/src/core/lib/iomgr/socket_utils.h b/src/core/lib/iomgr/socket_utils.h new file mode 100644 index 0000000000..cc3ee2e30c --- /dev/null +++ b/src/core/lib/iomgr/socket_utils.h @@ -0,0 +1,42 @@ +/* + * + * 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. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H +#define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H + +#include <stddef.h> + +/* A wrapper for inet_ntop on POSIX systems and InetNtop on Windows systems */ +const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size); + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H */ diff --git a/src/core/lib/iomgr/socket_utils_common_posix.c b/src/core/lib/iomgr/socket_utils_common_posix.c index d2f6261e2a..bc28bbe316 100644 --- a/src/core/lib/iomgr/socket_utils_common_posix.c +++ b/src/core/lib/iomgr/socket_utils_common_posix.c @@ -31,10 +31,11 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET +#include "src/core/lib/iomgr/socket_utils.h" #include "src/core/lib/iomgr/socket_utils_posix.h" #include <arpa/inet.h> @@ -78,7 +79,7 @@ grpc_error *grpc_set_socket_nonblocking(int fd, int non_blocking) { } grpc_error *grpc_set_socket_no_sigpipe_if_possible(int fd) { -#ifdef GPR_HAVE_SO_NOSIGPIPE +#ifdef GRPC_HAVE_SO_NOSIGPIPE int val = 1; int newval; socklen_t intlen = sizeof(newval); @@ -96,7 +97,7 @@ grpc_error *grpc_set_socket_no_sigpipe_if_possible(int fd) { } grpc_error *grpc_set_socket_ip_pktinfo_if_possible(int fd) { -#ifdef GPR_HAVE_IP_PKTINFO +#ifdef GRPC_HAVE_IP_PKTINFO int get_local_ip = 1; if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip, sizeof(get_local_ip))) { @@ -107,7 +108,7 @@ grpc_error *grpc_set_socket_ip_pktinfo_if_possible(int fd) { } grpc_error *grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) { -#ifdef GPR_HAVE_IPV6_RECVPKTINFO +#ifdef GRPC_HAVE_IPV6_RECVPKTINFO int get_local_ip = 1; if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip, sizeof(get_local_ip))) { @@ -253,7 +254,7 @@ static int set_socket_dualstack(int fd) { } } -static grpc_error *error_for_fd(int fd, const struct sockaddr *addr) { +static grpc_error *error_for_fd(int fd, const grpc_resolved_address *addr) { if (fd >= 0) return GRPC_ERROR_NONE; char *addr_str; grpc_sockaddr_to_string(&addr_str, addr, 0); @@ -263,10 +264,10 @@ static grpc_error *error_for_fd(int fd, const struct sockaddr *addr) { return err; } -grpc_error *grpc_create_dualstack_socket(const struct sockaddr *addr, int type, - int protocol, - grpc_dualstack_mode *dsmode, - int *newfd) { +grpc_error *grpc_create_dualstack_socket( + const grpc_resolved_address *resolved_addr, int type, int protocol, + grpc_dualstack_mode *dsmode, int *newfd) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; int family = addr->sa_family; if (family == AF_INET6) { if (grpc_ipv6_loopback_available()) { @@ -281,9 +282,9 @@ grpc_error *grpc_create_dualstack_socket(const struct sockaddr *addr, int type, return GRPC_ERROR_NONE; } /* If this isn't an IPv4 address, then return whatever we've got. */ - if (!grpc_sockaddr_is_v4mapped(addr, NULL)) { + if (!grpc_sockaddr_is_v4mapped(resolved_addr, NULL)) { *dsmode = GRPC_DSMODE_IPV6; - return error_for_fd(*newfd, addr); + return error_for_fd(*newfd, resolved_addr); } /* Fall back to AF_INET. */ if (*newfd >= 0) { @@ -293,7 +294,12 @@ grpc_error *grpc_create_dualstack_socket(const struct sockaddr *addr, int type, } *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE; *newfd = socket(family, type, protocol); - return error_for_fd(*newfd, addr); + return error_for_fd(*newfd, resolved_addr); +} + +const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) { + GPR_ASSERT(size <= (socklen_t)-1); + return inet_ntop(af, src, dst, (socklen_t)size); } #endif diff --git a/src/core/lib/iomgr/socket_utils_linux.c b/src/core/lib/iomgr/socket_utils_linux.c index 144e3110c8..bf6e9e4f55 100644 --- a/src/core/lib/iomgr/socket_utils_linux.c +++ b/src/core/lib/iomgr/socket_utils_linux.c @@ -31,21 +31,27 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_LINUX_SOCKETUTILS +#ifdef GRPC_LINUX_SOCKETUTILS +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/socket_utils_posix.h" +#include <grpc/support/log.h> + #include <sys/socket.h> #include <sys/types.h> -int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, - int nonblock, int cloexec) { +int grpc_accept4(int sockfd, grpc_resolved_address *resolved_addr, int nonblock, + int cloexec) { int flags = 0; + GPR_ASSERT(sizeof(socklen_t) <= sizeof(size_t)); + GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); flags |= nonblock ? SOCK_NONBLOCK : 0; flags |= cloexec ? SOCK_CLOEXEC : 0; - return accept4(sockfd, addr, addrlen, flags); + return accept4(sockfd, (struct sockaddr *)resolved_addr->addr, + (socklen_t *)&resolved_addr->len, flags); } #endif diff --git a/src/core/lib/iomgr/socket_utils_posix.c b/src/core/lib/iomgr/socket_utils_posix.c index 57ae64c103..9dea0c0cd8 100644 --- a/src/core/lib/iomgr/socket_utils_posix.c +++ b/src/core/lib/iomgr/socket_utils_posix.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKETUTILS +#ifdef GRPC_POSIX_SOCKETUTILS #include "src/core/lib/iomgr/socket_utils_posix.h" @@ -42,12 +42,15 @@ #include <unistd.h> #include <grpc/support/log.h> +#include "src/core/lib/iomgr/sockaddr.h" -int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, - int nonblock, int cloexec) { +int grpc_accept4(int sockfd, grpc_resolved_address *resolved_addr, int nonblock, + int cloexec) { int fd, flags; - - fd = accept(sockfd, addr, addrlen); + GPR_ASSERT(sizeof(socklen_t) <= sizeof(size_t)); + GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); + fd = accept(sockfd, (struct sockaddr *)resolved_addr->addr, + (socklen_t *)&resolved_addr->len); if (fd >= 0) { if (nonblock) { flags = fcntl(fd, F_GETFL, 0); @@ -67,4 +70,4 @@ close_and_error: return -1; } -#endif /* GPR_POSIX_SOCKETUTILS */ +#endif /* GRPC_POSIX_SOCKETUTILS */ diff --git a/src/core/lib/iomgr/socket_utils_posix.h b/src/core/lib/iomgr/socket_utils_posix.h index 7bcc2219ae..175fb2b717 100644 --- a/src/core/lib/iomgr/socket_utils_posix.h +++ b/src/core/lib/iomgr/socket_utils_posix.h @@ -34,14 +34,16 @@ #ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H #define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H +#include "src/core/lib/iomgr/resolve_address.h" + #include <sys/socket.h> #include <unistd.h> #include "src/core/lib/iomgr/error.h" /* a wrapper for accept or accept4 */ -int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, - int nonblock, int cloexec); +int grpc_accept4(int sockfd, grpc_resolved_address *resolved_addr, int nonblock, + int cloexec); /* set a socket to non blocking mode */ grpc_error *grpc_set_socket_nonblocking(int fd, int non_blocking); @@ -125,8 +127,8 @@ extern int grpc_forbid_dualstack_sockets_for_testing; IPv4, so that bind() or connect() see the correct family. Also, it's important to distinguish between DUALSTACK and IPV6 when listening on the [::] wildcard address. */ -grpc_error *grpc_create_dualstack_socket(const struct sockaddr *addr, int type, - int protocol, +grpc_error *grpc_create_dualstack_socket(const grpc_resolved_address *addr, + int type, int protocol, grpc_dualstack_mode *dsmode, int *newfd); diff --git a/src/core/lib/iomgr/socket_utils_uv.c b/src/core/lib/iomgr/socket_utils_uv.c new file mode 100644 index 0000000000..741bf28969 --- /dev/null +++ b/src/core/lib/iomgr/socket_utils_uv.c @@ -0,0 +1,49 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include <uv.h> + +#include "src/core/lib/iomgr/socket_utils.h" + +#include <grpc/support/log.h> + +const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) { + uv_inet_ntop(af, src, dst, size); + return dst; +} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/socket_utils_windows.c b/src/core/lib/iomgr/socket_utils_windows.c new file mode 100644 index 0000000000..628ad4a45b --- /dev/null +++ b/src/core/lib/iomgr/socket_utils_windows.c @@ -0,0 +1,48 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINDOWS_SOCKETUTILS + +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" + +#include <grpc/support/log.h> + +const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) { + /* Windows InetNtopA wants a mutable ip pointer */ + return InetNtopA(af, (void *)src, dst, size); +} + +#endif /* GRPC_WINDOWS_SOCKETUTILS */ diff --git a/src/core/lib/iomgr/socket_windows.c b/src/core/lib/iomgr/socket_windows.c index 78ef46d042..35f23300dc 100644 --- a/src/core/lib/iomgr/socket_windows.c +++ b/src/core/lib/iomgr/socket_windows.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINSOCK_SOCKET +#ifdef GRPC_WINSOCK_SOCKET #include <winsock2.h> @@ -156,4 +156,4 @@ void grpc_socket_become_ready(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, if (should_destroy) destroy(socket); } -#endif /* GPR_WINSOCK_SOCKET */ +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_client.h b/src/core/lib/iomgr/tcp_client.h index a07e0b9f0c..fe1e7f4d3e 100644 --- a/src/core/lib/iomgr/tcp_client.h +++ b/src/core/lib/iomgr/tcp_client.h @@ -37,7 +37,7 @@ #include <grpc/support/time.h> #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/pollset_set.h" -#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/resolve_address.h" /* Asynchronously connect to an address (specified as (addr, len)), and call cb with arg and the completed connection when done (or call cb with arg and @@ -47,7 +47,7 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_connect, grpc_endpoint **endpoint, grpc_pollset_set *interested_parties, - const struct sockaddr *addr, size_t addr_len, + const grpc_resolved_address *addr, gpr_timespec deadline); #endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H */ diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c index 3496b6094f..e7ae1ef695 100644 --- a/src/core/lib/iomgr/tcp_client_posix.c +++ b/src/core/lib/iomgr/tcp_client_posix.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET #include "src/core/lib/iomgr/tcp_client.h" @@ -71,7 +71,7 @@ typedef struct { grpc_closure *closure; } async_connect; -static grpc_error *prepare_socket(const struct sockaddr *addr, int fd) { +static grpc_error *prepare_socket(const grpc_resolved_address *addr, int fd) { grpc_error *err = GRPC_ERROR_NONE; GPR_ASSERT(fd >= 0); @@ -223,14 +223,14 @@ finish: static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, grpc_pollset_set *interested_parties, - const struct sockaddr *addr, - size_t addr_len, gpr_timespec deadline) { + const grpc_resolved_address *addr, + gpr_timespec deadline) { int fd; grpc_dualstack_mode dsmode; int err; async_connect *ac; - struct sockaddr_in6 addr6_v4mapped; - struct sockaddr_in addr4_copy; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address addr4_copy; grpc_fd *fdobj; char *name; char *addr_str; @@ -240,8 +240,7 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, /* Use dualstack sockets where available. */ if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = (const struct sockaddr *)&addr6_v4mapped; - addr_len = sizeof(addr6_v4mapped); + addr = &addr6_v4mapped; } error = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd); @@ -252,8 +251,7 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, if (dsmode == GRPC_DSMODE_IPV4) { /* If we got an AF_INET socket, map the address back to IPv4. */ GPR_ASSERT(grpc_sockaddr_is_v4mapped(addr, &addr4_copy)); - addr = (struct sockaddr *)&addr4_copy; - addr_len = sizeof(addr4_copy); + addr = &addr4_copy; } if ((error = prepare_socket(addr, fd)) != GRPC_ERROR_NONE) { grpc_exec_ctx_sched(exec_ctx, closure, error, NULL); @@ -261,8 +259,9 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, } do { - GPR_ASSERT(addr_len < ~(socklen_t)0); - err = connect(fd, addr, (socklen_t)addr_len); + GPR_ASSERT(addr->len < ~(socklen_t)0); + err = + connect(fd, (const struct sockaddr *)addr->addr, (socklen_t)addr->len); } while (err < 0 && errno == EINTR); addr_str = grpc_sockaddr_to_uri(addr); @@ -317,16 +316,16 @@ done: // overridden by api_fuzzer.c void (*grpc_tcp_client_connect_impl)( grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, - grpc_pollset_set *interested_parties, const struct sockaddr *addr, - size_t addr_len, gpr_timespec deadline) = tcp_client_connect_impl; + grpc_pollset_set *interested_parties, const grpc_resolved_address *addr, + gpr_timespec deadline) = tcp_client_connect_impl; void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, grpc_pollset_set *interested_parties, - const struct sockaddr *addr, size_t addr_len, + const grpc_resolved_address *addr, gpr_timespec deadline) { grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties, addr, - addr_len, deadline); + deadline); } #endif diff --git a/src/core/lib/iomgr/tcp_client_uv.c b/src/core/lib/iomgr/tcp_client_uv.c new file mode 100644 index 0000000000..6274667042 --- /dev/null +++ b/src/core/lib/iomgr/tcp_client_uv.c @@ -0,0 +1,153 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_uv.h" +#include "src/core/lib/iomgr/timer.h" + +typedef struct grpc_uv_tcp_connect { + uv_connect_t connect_req; + grpc_timer alarm; + uv_tcp_t *tcp_handle; + grpc_closure *closure; + grpc_endpoint **endpoint; + int refs; + char *addr_name; +} grpc_uv_tcp_connect; + +static void uv_tcp_connect_cleanup(grpc_uv_tcp_connect *connect) { + gpr_free(connect); +} + +static void tcp_close_callback(uv_handle_t *handle) { gpr_free(handle); } + +static void uv_tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, + grpc_error *error) { + int done; + grpc_uv_tcp_connect *connect = acp; + if (error == GRPC_ERROR_NONE) { + /* error == NONE implies that the timer ran out, and wasn't cancelled. If + it was cancelled, then the handler that cancelled it also should close + the handle, if applicable */ + uv_close((uv_handle_t *)connect->tcp_handle, tcp_close_callback); + } + done = (--connect->refs == 0); + if (done) { + uv_tcp_connect_cleanup(connect); + } +} + +static void uv_tc_on_connect(uv_connect_t *req, int status) { + grpc_uv_tcp_connect *connect = req->data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_error *error = GRPC_ERROR_NONE; + int done; + grpc_closure *closure = connect->closure; + grpc_timer_cancel(&exec_ctx, &connect->alarm); + if (status == 0) { + *connect->endpoint = + grpc_tcp_create(connect->tcp_handle, connect->addr_name); + } else { + error = GRPC_ERROR_CREATE("Failed to connect to remote host"); + error = grpc_error_set_int(error, GRPC_ERROR_INT_ERRNO, -status); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, uv_strerror(status)); + if (status == UV_ECANCELED) { + error = grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + "Timeout occurred"); + // This should only happen if the handle is already closed + } else { + error = grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + uv_strerror(status)); + uv_close((uv_handle_t *)connect->tcp_handle, tcp_close_callback); + } + } + done = (--connect->refs == 0); + if (done) { + uv_tcp_connect_cleanup(connect); + } + grpc_exec_ctx_sched(&exec_ctx, closure, error, NULL); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, + grpc_closure *closure, grpc_endpoint **ep, + grpc_pollset_set *interested_parties, + const grpc_resolved_address *resolved_addr, + gpr_timespec deadline) { + grpc_uv_tcp_connect *connect; + (void)interested_parties; + connect = gpr_malloc(sizeof(grpc_uv_tcp_connect)); + memset(connect, 0, sizeof(grpc_uv_tcp_connect)); + connect->closure = closure; + connect->endpoint = ep; + connect->tcp_handle = gpr_malloc(sizeof(uv_tcp_t)); + connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); + uv_tcp_init(uv_default_loop(), connect->tcp_handle); + connect->connect_req.data = connect; + // TODO(murgatroid99): figure out what the return value here means + uv_tcp_connect(&connect->connect_req, connect->tcp_handle, + (const struct sockaddr *)resolved_addr->addr, + uv_tc_on_connect); + grpc_timer_init(exec_ctx, &connect->alarm, + gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), + uv_tc_on_alarm, connect, gpr_now(GPR_CLOCK_MONOTONIC)); +} + +// overridden by api_fuzzer.c +void (*grpc_tcp_client_connect_impl)( + grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, + grpc_pollset_set *interested_parties, const grpc_resolved_address *addr, + gpr_timespec deadline) = tcp_client_connect_impl; + +void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_endpoint **ep, + grpc_pollset_set *interested_parties, + const grpc_resolved_address *addr, + gpr_timespec deadline) { + grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties, addr, + deadline); +} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_client_windows.c b/src/core/lib/iomgr/tcp_client_windows.c index 562cb9c6bf..fdd8c1a1f8 100644 --- a/src/core/lib/iomgr/tcp_client_windows.c +++ b/src/core/lib/iomgr/tcp_client_windows.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINSOCK_SOCKET +#ifdef GRPC_WINSOCK_SOCKET #include "src/core/lib/iomgr/sockaddr_windows.h" @@ -129,13 +129,13 @@ static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done, grpc_endpoint **endpoint, grpc_pollset_set *interested_parties, - const struct sockaddr *addr, size_t addr_len, + const grpc_resolved_address *addr, gpr_timespec deadline) { SOCKET sock = INVALID_SOCKET; BOOL success; int status; - struct sockaddr_in6 addr6_v4mapped; - struct sockaddr_in6 local_address; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address local_address; async_connect *ac; grpc_winsocket *socket = NULL; LPFN_CONNECTEX ConnectEx; @@ -148,8 +148,7 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done, /* Use dualstack sockets where available. */ if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = (const struct sockaddr *)&addr6_v4mapped; - addr_len = sizeof(addr6_v4mapped); + addr = &addr6_v4mapped; } sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, @@ -178,7 +177,8 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done, grpc_sockaddr_make_wildcard6(0, &local_address); - status = bind(sock, (struct sockaddr *)&local_address, sizeof(local_address)); + status = + bind(sock, (struct sockaddr *)&local_address.addr, local_address.len); if (status != 0) { error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); goto failure; @@ -186,8 +186,8 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_done, socket = grpc_winsocket_create(sock, "client"); info = &socket->write_info; - success = - ConnectEx(sock, addr, (int)addr_len, NULL, 0, NULL, &info->overlapped); + success = ConnectEx(sock, (struct sockaddr *)&addr->addr, (int)addr->len, + NULL, 0, NULL, &info->overlapped); /* It wouldn't be unusual to get a success immediately. But we'll still get an IOCP notification, so let's ignore it. */ @@ -228,4 +228,4 @@ failure: grpc_exec_ctx_sched(exec_ctx, on_done, final_error, NULL); } -#endif /* GPR_WINSOCK_SOCKET */ +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c index 00fd77679a..18c63cfbae 100644 --- a/src/core/lib/iomgr/tcp_posix.c +++ b/src/core/lib/iomgr/tcp_posix.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET #include "src/core/lib/iomgr/network_status_tracker.h" #include "src/core/lib/iomgr/tcp_posix.h" @@ -58,14 +58,14 @@ #include "src/core/lib/profiling/timers.h" #include "src/core/lib/support/string.h" -#ifdef GPR_HAVE_MSG_NOSIGNAL +#ifdef GRPC_HAVE_MSG_NOSIGNAL #define SENDMSG_FLAGS MSG_NOSIGNAL #else #define SENDMSG_FLAGS 0 #endif -#ifdef GPR_MSG_IOVLEN_TYPE -typedef GPR_MSG_IOVLEN_TYPE msg_iovlen_type; +#ifdef GRPC_MSG_IOVLEN_TYPE +typedef GRPC_MSG_IOVLEN_TYPE msg_iovlen_type; #else typedef size_t msg_iovlen_type; #endif diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h index 9a390699b4..0c2a4f41da 100644 --- a/src/core/lib/iomgr/tcp_server.h +++ b/src/core/lib/iomgr/tcp_server.h @@ -38,6 +38,7 @@ #include "src/core/lib/iomgr/closure.h" #include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/resolve_address.h" /* Forward decl of grpc_tcp_server */ typedef struct grpc_tcp_server grpc_tcp_server; @@ -78,8 +79,9 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server, but not dualstack sockets. */ /* TODO(ctiller): deprecate this, and make grpc_tcp_server_add_ports to handle all of the multiple socket port matching logic in one place */ -grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, - size_t addr_len, int *out_port); +grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, + const grpc_resolved_address *addr, + int *out_port); /* Number of fds at the given port_index, or 0 if port_index is out of bounds. */ diff --git a/src/core/lib/iomgr/tcp_server_posix.c b/src/core/lib/iomgr/tcp_server_posix.c index 73df5477e6..127f8595d1 100644 --- a/src/core/lib/iomgr/tcp_server_posix.c +++ b/src/core/lib/iomgr/tcp_server_posix.c @@ -36,9 +36,9 @@ #define _GNU_SOURCE #endif -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET #include "src/core/lib/iomgr/tcp_server.h" @@ -62,6 +62,7 @@ #include <grpc/support/useful.h> #include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/iomgr/socket_utils_posix.h" #include "src/core/lib/iomgr/tcp_posix.h" @@ -79,11 +80,7 @@ struct grpc_tcp_listener { int fd; grpc_fd *emfd; grpc_tcp_server *server; - union { - uint8_t untyped[GRPC_MAX_SOCKADDR_SIZE]; - struct sockaddr sockaddr; - } addr; - size_t addr_len; + grpc_resolved_address addr; int port; unsigned port_index; unsigned fd_index; @@ -238,7 +235,7 @@ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { if (s->head) { grpc_tcp_listener *sp; for (sp = s->head; sp; sp = sp->next) { - grpc_unlink_if_unix_domain_socket(&sp->addr.sockaddr); + grpc_unlink_if_unix_domain_socket(&sp->addr); sp->destroyed_closure.cb = destroyed_port; sp->destroyed_closure.cb_arg = s; grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL, @@ -304,11 +301,9 @@ static int get_max_accept_queue_size(void) { } /* Prepare a recently-created socket for listening. */ -static grpc_error *prepare_socket(int fd, const struct sockaddr *addr, - size_t addr_len, bool so_reuseport, - int *port) { - struct sockaddr_storage sockname_temp; - socklen_t sockname_len; +static grpc_error *prepare_socket(int fd, const grpc_resolved_address *addr, + bool so_reuseport, int *port) { + grpc_resolved_address sockname_temp; grpc_error *err = GRPC_ERROR_NONE; GPR_ASSERT(fd >= 0); @@ -331,8 +326,8 @@ static grpc_error *prepare_socket(int fd, const struct sockaddr *addr, err = grpc_set_socket_no_sigpipe_if_possible(fd); if (err != GRPC_ERROR_NONE) goto error; - GPR_ASSERT(addr_len < ~(socklen_t)0); - if (bind(fd, addr, (socklen_t)addr_len) < 0) { + GPR_ASSERT(addr->len < ~(socklen_t)0); + if (bind(fd, (struct sockaddr *)addr->addr, (socklen_t)addr->len) < 0) { err = GRPC_OS_ERROR(errno, "bind"); goto error; } @@ -342,13 +337,15 @@ static grpc_error *prepare_socket(int fd, const struct sockaddr *addr, goto error; } - sockname_len = sizeof(sockname_temp); - if (getsockname(fd, (struct sockaddr *)&sockname_temp, &sockname_len) < 0) { + sockname_temp.len = sizeof(struct sockaddr_storage); + + if (getsockname(fd, (struct sockaddr *)sockname_temp.addr, + (socklen_t *)&sockname_temp.len) < 0) { err = GRPC_OS_ERROR(errno, "getsockname"); goto error; } - *port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + *port = grpc_sockaddr_get_port(&sockname_temp); return GRPC_ERROR_NONE; error: @@ -382,13 +379,13 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) { /* loop until accept4 returns EAGAIN, and then re-arm notification */ for (;;) { - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); + grpc_resolved_address addr; char *addr_str; char *name; + addr.len = sizeof(struct sockaddr_storage); /* Note: If we ever decide to return this address to the user, remember to strip off the ::ffff:0.0.0.0/96 prefix first. */ - int fd = grpc_accept4(sp->fd, (struct sockaddr *)&addr, &addrlen, 1, 1); + int fd = grpc_accept4(sp->fd, &addr, 1, 1); if (fd < 0) { switch (errno) { case EINTR: @@ -404,7 +401,7 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) { grpc_set_socket_no_sigpipe_if_possible(fd); - addr_str = grpc_sockaddr_to_uri((struct sockaddr *)&addr); + addr_str = grpc_sockaddr_to_uri(&addr); gpr_asprintf(&name, "tcp-server-connection:%s", addr_str); if (grpc_tcp_trace) { @@ -442,19 +439,18 @@ error: } static grpc_error *add_socket_to_server(grpc_tcp_server *s, int fd, - const struct sockaddr *addr, - size_t addr_len, unsigned port_index, - unsigned fd_index, + const grpc_resolved_address *addr, + unsigned port_index, unsigned fd_index, grpc_tcp_listener **listener) { grpc_tcp_listener *sp = NULL; int port = -1; char *addr_str; char *name; - grpc_error *err = prepare_socket(fd, addr, addr_len, s->so_reuseport, &port); + grpc_error *err = prepare_socket(fd, addr, s->so_reuseport, &port); if (err == GRPC_ERROR_NONE) { GPR_ASSERT(port > 0); - grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1); + grpc_sockaddr_to_string(&addr_str, addr, 1); gpr_asprintf(&name, "tcp-server-listener:%s", addr_str); gpr_mu_lock(&s->mu); s->nports++; @@ -470,8 +466,7 @@ static grpc_error *add_socket_to_server(grpc_tcp_server *s, int fd, sp->server = s; sp->fd = fd; sp->emfd = grpc_fd_create(fd, name); - memcpy(sp->addr.untyped, addr, addr_len); - sp->addr_len = addr_len; + memcpy(&sp->addr, addr, sizeof(grpc_resolved_address)); sp->port = port; sp->port_index = port_index; sp->fd_index = fd_index; @@ -504,14 +499,13 @@ static grpc_error *clone_port(grpc_tcp_listener *listener, unsigned count) { int fd = -1; int port = -1; grpc_dualstack_mode dsmode; - err = grpc_create_dualstack_socket(&listener->addr.sockaddr, SOCK_STREAM, 0, - &dsmode, &fd); + err = grpc_create_dualstack_socket(&listener->addr, SOCK_STREAM, 0, &dsmode, + &fd); if (err != GRPC_ERROR_NONE) return err; - err = prepare_socket(fd, &listener->addr.sockaddr, listener->addr_len, true, - &port); + err = prepare_socket(fd, &listener->addr, true, &port); if (err != GRPC_ERROR_NONE) return err; listener->server->nports++; - grpc_sockaddr_to_string(&addr_str, &listener->addr.sockaddr, 1); + grpc_sockaddr_to_string(&addr_str, &listener->addr, 1); gpr_asprintf(&name, "tcp-server-listener:%s/clone-%d", addr_str, i); sp = gpr_malloc(sizeof(grpc_tcp_listener)); sp->next = listener->next; @@ -524,8 +518,7 @@ static grpc_error *clone_port(grpc_tcp_listener *listener, unsigned count) { sp->server = listener->server; sp->fd = fd; sp->emfd = grpc_fd_create(fd, name); - memcpy(sp->addr.untyped, listener->addr.untyped, listener->addr_len); - sp->addr_len = listener->addr_len; + memcpy(&sp->addr, &listener->addr, sizeof(grpc_resolved_address)); sp->port = port; sp->port_index = listener->port_index; sp->fd_index = listener->fd_index + count - i; @@ -540,19 +533,19 @@ static grpc_error *clone_port(grpc_tcp_listener *listener, unsigned count) { return GRPC_ERROR_NONE; } -grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, - size_t addr_len, int *out_port) { +grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, + const grpc_resolved_address *addr, + int *out_port) { grpc_tcp_listener *sp; grpc_tcp_listener *sp2 = NULL; int fd; grpc_dualstack_mode dsmode; - struct sockaddr_in6 addr6_v4mapped; - struct sockaddr_in wild4; - struct sockaddr_in6 wild6; - struct sockaddr_in addr4_copy; - struct sockaddr *allocated_addr = NULL; - struct sockaddr_storage sockname_temp; - socklen_t sockname_len; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wild4; + grpc_resolved_address wild6; + grpc_resolved_address addr4_copy; + grpc_resolved_address *allocated_addr = NULL; + grpc_resolved_address sockname_temp; int port; unsigned port_index = 0; unsigned fd_index = 0; @@ -560,19 +553,19 @@ grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, if (s->tail != NULL) { port_index = s->tail->port_index + 1; } - grpc_unlink_if_unix_domain_socket((struct sockaddr *)addr); + grpc_unlink_if_unix_domain_socket(addr); /* Check if this is a wildcard port, and if so, try to keep the port the same as some previously created listener. */ if (grpc_sockaddr_get_port(addr) == 0) { for (sp = s->head; sp; sp = sp->next) { - sockname_len = sizeof(sockname_temp); - if (0 == getsockname(sp->fd, (struct sockaddr *)&sockname_temp, - &sockname_len)) { - port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + sockname_temp.len = sizeof(struct sockaddr_storage); + if (0 == getsockname(sp->fd, (struct sockaddr *)sockname_temp.addr, + (socklen_t *)&sockname_temp.len)) { + port = grpc_sockaddr_get_port(&sockname_temp); if (port > 0) { - allocated_addr = gpr_malloc(addr_len); - memcpy(allocated_addr, addr, addr_len); + allocated_addr = gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, addr->len); grpc_sockaddr_set_port(allocated_addr, port); addr = allocated_addr; break; @@ -584,8 +577,7 @@ grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, sp = NULL; if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = (const struct sockaddr *)&addr6_v4mapped; - addr_len = sizeof(addr6_v4mapped); + addr = &addr6_v4mapped; } /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ @@ -593,12 +585,10 @@ grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, grpc_sockaddr_make_wildcards(port, &wild4, &wild6); /* Try listening on IPv6 first. */ - addr = (struct sockaddr *)&wild6; - addr_len = sizeof(wild6); + addr = &wild6; errs[0] = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd); if (errs[0] == GRPC_ERROR_NONE) { - errs[0] = add_socket_to_server(s, fd, addr, addr_len, port_index, - fd_index, &sp); + errs[0] = add_socket_to_server(s, fd, addr, port_index, fd_index, &sp); if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { goto done; } @@ -607,23 +597,20 @@ grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, } /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ if (port == 0 && sp != NULL) { - grpc_sockaddr_set_port((struct sockaddr *)&wild4, sp->port); + grpc_sockaddr_set_port(&wild4, sp->port); } } - addr = (struct sockaddr *)&wild4; - addr_len = sizeof(wild4); + addr = &wild4; } errs[1] = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd); if (errs[1] == GRPC_ERROR_NONE) { if (dsmode == GRPC_DSMODE_IPV4 && grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) { - addr = (struct sockaddr *)&addr4_copy; - addr_len = sizeof(addr4_copy); + addr = &addr4_copy; } sp2 = sp; - errs[1] = - add_socket_to_server(s, fd, addr, addr_len, port_index, fd_index, &sp); + errs[1] = add_socket_to_server(s, fd, addr, port_index, fd_index, &sp); if (sp2 != NULL && sp != NULL) { sp2->sibling = sp; sp->is_sibling = 1; @@ -704,7 +691,7 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, s->pollset_count = pollset_count; sp = s->head; while (sp != NULL) { - if (s->so_reuseport && !grpc_is_unix_socket(&sp->addr.sockaddr) && + if (s->so_reuseport && !grpc_is_unix_socket(&sp->addr) && pollset_count > 1) { GPR_ASSERT(GRPC_LOG_IF_ERROR( "clone_port", clone_port(sp, (unsigned)(pollset_count - 1)))); diff --git a/src/core/lib/iomgr/tcp_server_uv.c b/src/core/lib/iomgr/tcp_server_uv.c new file mode 100644 index 0000000000..73e4db3d65 --- /dev/null +++ b/src/core/lib/iomgr/tcp_server_uv.c @@ -0,0 +1,365 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/tcp_uv.h" + +/* one listening port */ +typedef struct grpc_tcp_listener grpc_tcp_listener; +struct grpc_tcp_listener { + uv_tcp_t *handle; + grpc_tcp_server *server; + unsigned port_index; + int port; + /* linked list */ + struct grpc_tcp_listener *next; +}; + +struct grpc_tcp_server { + gpr_refcount refs; + + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void *on_accept_cb_arg; + + int open_ports; + + /* linked list of server ports */ + grpc_tcp_listener *head; + grpc_tcp_listener *tail; + + /* List of closures passed to shutdown_starting_add(). */ + grpc_closure_list shutdown_starting; + + /* shutdown callback */ + grpc_closure *shutdown_complete; +}; + +grpc_error *grpc_tcp_server_create(grpc_closure *shutdown_complete, + const grpc_channel_args *args, + grpc_tcp_server **server) { + grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); + (void)args; + gpr_ref_init(&s->refs, 1); + s->on_accept_cb = NULL; + s->on_accept_cb_arg = NULL; + s->open_ports = 0; + s->head = NULL; + s->tail = NULL; + s->shutdown_starting.head = NULL; + s->shutdown_starting.tail = NULL; + s->shutdown_complete = shutdown_complete; + *server = s; + return GRPC_ERROR_NONE; +} + +grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) { + gpr_ref(&s->refs); + return s; +} + +void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, + grpc_closure *shutdown_starting) { + grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, + GRPC_ERROR_NONE); +} + +static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + if (s->shutdown_complete != NULL) { + grpc_exec_ctx_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL); + } + + while (s->head) { + grpc_tcp_listener *sp = s->head; + s->head = sp->next; + sp->next = NULL; + gpr_free(sp->handle); + gpr_free(sp); + } + gpr_free(s); +} + +static void handle_close_callback(uv_handle_t *handle) { + grpc_tcp_listener *sp = (grpc_tcp_listener *)handle->data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + sp->server->open_ports--; + if (sp->server->open_ports == 0) { + finish_shutdown(&exec_ctx, sp->server); + } + grpc_exec_ctx_finish(&exec_ctx); +} + +static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + int immediately_done = 0; + grpc_tcp_listener *sp; + + if (s->open_ports == 0) { + immediately_done = 1; + } + for (sp = s->head; sp; sp = sp->next) { + uv_close((uv_handle_t *)sp->handle, handle_close_callback); + } + + if (immediately_done) { + finish_shutdown(exec_ctx, s); + } +} + +void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + if (gpr_unref(&s->refs)) { + /* Complete shutdown_starting work before destroying. */ + grpc_exec_ctx local_exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_exec_ctx_enqueue_list(&local_exec_ctx, &s->shutdown_starting, NULL); + if (exec_ctx == NULL) { + grpc_exec_ctx_flush(&local_exec_ctx); + tcp_server_destroy(&local_exec_ctx, s); + grpc_exec_ctx_finish(&local_exec_ctx); + } else { + grpc_exec_ctx_finish(&local_exec_ctx); + tcp_server_destroy(exec_ctx, s); + } + } +} + +static void accepted_connection_close_cb(uv_handle_t *handle) { + gpr_free(handle); +} + +static void on_connect(uv_stream_t *server, int status) { + grpc_tcp_listener *sp = (grpc_tcp_listener *)server->data; + grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index, 0}; + uv_tcp_t *client; + grpc_endpoint *ep = NULL; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_resolved_address peer_name; + char *peer_name_string; + int err; + + if (status < 0) { + gpr_log(GPR_INFO, "Skipping on_accept due to error: %s", + uv_strerror(status)); + return; + } + client = gpr_malloc(sizeof(uv_tcp_t)); + uv_tcp_init(uv_default_loop(), client); + // UV documentation says this is guaranteed to succeed + uv_accept((uv_stream_t *)server, (uv_stream_t *)client); + // If the server has not been started, we discard incoming connections + if (sp->server->on_accept_cb == NULL) { + uv_close((uv_handle_t *)client, accepted_connection_close_cb); + } else { + peer_name_string = NULL; + memset(&peer_name, 0, sizeof(grpc_resolved_address)); + peer_name.len = sizeof(struct sockaddr_storage); + err = uv_tcp_getpeername(client, (struct sockaddr *)&peer_name.addr, + (int *)&peer_name.len); + if (err == 0) { + peer_name_string = grpc_sockaddr_to_uri(&peer_name); + } else { + gpr_log(GPR_INFO, "uv_tcp_getpeername error: %s", uv_strerror(status)); + } + ep = grpc_tcp_create(client, peer_name_string); + sp->server->on_accept_cb(&exec_ctx, sp->server->on_accept_cb_arg, ep, NULL, + &acceptor); + grpc_exec_ctx_finish(&exec_ctx); + } +} + +static grpc_error *add_socket_to_server(grpc_tcp_server *s, uv_tcp_t *handle, + const grpc_resolved_address *addr, + unsigned port_index, + grpc_tcp_listener **listener) { + grpc_tcp_listener *sp = NULL; + int port = -1; + int status; + grpc_error *error; + grpc_resolved_address sockname_temp; + + // The last argument to uv_tcp_bind is flags + status = uv_tcp_bind(handle, (struct sockaddr *)addr->addr, 0); + if (status != 0) { + error = GRPC_ERROR_CREATE("Failed to bind to port"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, uv_strerror(status)); + return error; + } + + status = uv_listen((uv_stream_t *)handle, SOMAXCONN, on_connect); + if (status != 0) { + error = GRPC_ERROR_CREATE("Failed to listen to port"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, uv_strerror(status)); + return error; + } + + sockname_temp.len = (int)sizeof(struct sockaddr_storage); + status = uv_tcp_getsockname(handle, (struct sockaddr *)&sockname_temp.addr, + (int *)&sockname_temp.len); + if (status != 0) { + error = GRPC_ERROR_CREATE("getsockname failed"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, uv_strerror(status)); + return error; + } + + port = grpc_sockaddr_get_port(&sockname_temp); + + GPR_ASSERT(port >= 0); + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); + sp = gpr_malloc(sizeof(grpc_tcp_listener)); + sp->next = NULL; + if (s->head == NULL) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->handle = handle; + sp->port = port; + sp->port_index = port_index; + handle->data = sp; + s->open_ports++; + GPR_ASSERT(sp->handle); + *listener = sp; + + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, + const grpc_resolved_address *addr, + int *port) { + // This function is mostly copied from tcp_server_windows.c + grpc_tcp_listener *sp = NULL; + uv_tcp_t *handle; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wildcard; + grpc_resolved_address *allocated_addr = NULL; + grpc_resolved_address sockname_temp; + unsigned port_index = 0; + int status; + grpc_error *error = GRPC_ERROR_NONE; + + if (s->tail != NULL) { + port_index = s->tail->port_index + 1; + } + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (sp = s->head; sp; sp = sp->next) { + sockname_temp.len = sizeof(struct sockaddr_storage); + if (0 == uv_tcp_getsockname(sp->handle, + (struct sockaddr *)&sockname_temp.addr, + (int *)&sockname_temp.len)) { + *port = grpc_sockaddr_get_port(&sockname_temp); + if (*port > 0) { + allocated_addr = gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(allocated_addr, *port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ + if (grpc_sockaddr_is_wildcard(addr, port)) { + grpc_sockaddr_make_wildcard6(*port, &wildcard); + + addr = &wildcard; + } + + handle = gpr_malloc(sizeof(uv_tcp_t)); + status = uv_tcp_init(uv_default_loop(), handle); + if (status == 0) { + error = add_socket_to_server(s, handle, addr, port_index, &sp); + } else { + error = GRPC_ERROR_CREATE("Failed to initialize UV tcp handle"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, uv_strerror(status)); + } + + gpr_free(allocated_addr); + + if (error != GRPC_ERROR_NONE) { + grpc_error *error_out = GRPC_ERROR_CREATE_REFERENCING( + "Failed to add port to server", &error, 1); + GRPC_ERROR_UNREF(error); + error = error_out; + *port = -1; + } else { + GPR_ASSERT(sp != NULL); + *port = sp->port; + } + return error; +} + +void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server, + grpc_pollset **pollsets, size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, void *cb_arg) { + grpc_tcp_listener *sp; + (void)pollsets; + (void)pollset_count; + GPR_ASSERT(on_accept_cb); + GPR_ASSERT(!server->on_accept_cb); + server->on_accept_cb = on_accept_cb; + server->on_accept_cb_arg = cb_arg; + for (sp = server->head; sp; sp = sp->next) { + GPR_ASSERT(uv_listen((uv_stream_t *)sp->handle, SOMAXCONN, on_connect) == + 0); + } +} + +void grpc_tcp_server_shutdown_listeners(grpc_exec_ctx *exec_ctx, + grpc_tcp_server *s) {} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_server_windows.c b/src/core/lib/iomgr/tcp_server_windows.c index 4ff05601fa..ad6769a6ba 100644 --- a/src/core/lib/iomgr/tcp_server_windows.c +++ b/src/core/lib/iomgr/tcp_server_windows.c @@ -31,13 +31,13 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINSOCK_SOCKET +#ifdef GRPC_WINSOCK_SOCKET -#include <io.h> +#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" +#include <io.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -48,6 +48,8 @@ #include "src/core/lib/iomgr/iocp_windows.h" #include "src/core/lib/iomgr/pollset_windows.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/iomgr/socket_windows.h" #include "src/core/lib/iomgr/tcp_server.h" #include "src/core/lib/iomgr/tcp_windows.h" @@ -183,10 +185,10 @@ void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { } /* Prepare (bind) a recently-created socket for listening. */ -static grpc_error *prepare_socket(SOCKET sock, const struct sockaddr *addr, - size_t addr_len, int *port) { - struct sockaddr_storage sockname_temp; - socklen_t sockname_len; +static grpc_error *prepare_socket(SOCKET sock, + const grpc_resolved_address *addr, + int *port) { + grpc_resolved_address sockname_temp; grpc_error *error = GRPC_ERROR_NONE; error = grpc_tcp_prepare_socket(sock); @@ -194,7 +196,8 @@ static grpc_error *prepare_socket(SOCKET sock, const struct sockaddr *addr, goto failure; } - if (bind(sock, addr, (int)addr_len) == SOCKET_ERROR) { + if (bind(sock, (const struct sockaddr *)addr->addr, (int)addr->len) == + SOCKET_ERROR) { error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); goto failure; } @@ -204,14 +207,14 @@ static grpc_error *prepare_socket(SOCKET sock, const struct sockaddr *addr, goto failure; } - sockname_len = sizeof(sockname_temp); - if (getsockname(sock, (struct sockaddr *)&sockname_temp, &sockname_len) == - SOCKET_ERROR) { + sockname_temp.len = sizeof(struct sockaddr_storage); + if (getsockname(sock, (struct sockaddr *)sockname_temp.addr, + &sockname_temp.len) == SOCKET_ERROR) { error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname"); goto failure; } - *port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + *port = grpc_sockaddr_get_port(&sockname_temp); return GRPC_ERROR_NONE; failure: @@ -307,15 +310,16 @@ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { SOCKET sock = sp->new_socket; grpc_winsocket_callback_info *info = &sp->socket->read_info; grpc_endpoint *ep = NULL; - struct sockaddr_storage peer_name; + grpc_resolved_address peer_name; char *peer_name_string; char *fd_name; - int peer_name_len = sizeof(peer_name); DWORD transfered_bytes; DWORD flags; BOOL wsa_success; int err; + peer_name.len = sizeof(struct sockaddr_storage); + /* The general mechanism for shutting down is to queue abortion calls. While this is necessary in the read/write case, it's useless for the accept case. We only need to adjust the pending callback count */ @@ -353,9 +357,10 @@ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { gpr_log(GPR_ERROR, "setsockopt error: %s", utf8_message); gpr_free(utf8_message); } - err = getpeername(sock, (struct sockaddr *)&peer_name, &peer_name_len); + err = + getpeername(sock, (struct sockaddr *)peer_name.addr, &peer_name.len); if (!err) { - peer_name_string = grpc_sockaddr_to_uri((struct sockaddr *)&peer_name); + peer_name_string = grpc_sockaddr_to_uri(&peer_name); } else { char *utf8_message = gpr_format_message(WSAGetLastError()); gpr_log(GPR_ERROR, "getpeername error: %s", utf8_message); @@ -385,8 +390,8 @@ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { } static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock, - const struct sockaddr *addr, - size_t addr_len, unsigned port_index, + const grpc_resolved_address *addr, + unsigned port_index, grpc_tcp_listener **listener) { grpc_tcp_listener *sp = NULL; int port = -1; @@ -410,7 +415,7 @@ static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock, return NULL; } - error = prepare_socket(sock, addr, addr_len, &port); + error = prepare_socket(sock, addr, &port); if (error != GRPC_ERROR_NONE) { return error; } @@ -441,15 +446,15 @@ static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock, return GRPC_ERROR_NONE; } -grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, - size_t addr_len, int *port) { +grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, + const grpc_resolved_address *addr, + int *port) { grpc_tcp_listener *sp = NULL; SOCKET sock; - struct sockaddr_in6 addr6_v4mapped; - struct sockaddr_in6 wildcard; - struct sockaddr *allocated_addr = NULL; - struct sockaddr_storage sockname_temp; - socklen_t sockname_len; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wildcard; + grpc_resolved_address *allocated_addr = NULL; + grpc_resolved_address sockname_temp; unsigned port_index = 0; grpc_error *error = GRPC_ERROR_NONE; @@ -461,13 +466,14 @@ grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, as some previously created listener. */ if (grpc_sockaddr_get_port(addr) == 0) { for (sp = s->head; sp; sp = sp->next) { - sockname_len = sizeof(sockname_temp); + sockname_temp.len = sizeof(struct sockaddr_storage); if (0 == getsockname(sp->socket->socket, - (struct sockaddr *)&sockname_temp, &sockname_len)) { - *port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + (struct sockaddr *)sockname_temp.addr, + &sockname_temp.len)) { + *port = grpc_sockaddr_get_port(&sockname_temp); if (*port > 0) { - allocated_addr = gpr_malloc(addr_len); - memcpy(allocated_addr, addr, addr_len); + allocated_addr = gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); grpc_sockaddr_set_port(allocated_addr, *port); addr = allocated_addr; break; @@ -477,16 +483,14 @@ grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, } if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = (const struct sockaddr *)&addr6_v4mapped; - addr_len = sizeof(addr6_v4mapped); + addr = &addr6_v4mapped; } /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ if (grpc_sockaddr_is_wildcard(addr, port)) { grpc_sockaddr_make_wildcard6(*port, &wildcard); - addr = (struct sockaddr *)&wildcard; - addr_len = sizeof(wildcard); + addr = &wildcard; } sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, @@ -496,7 +500,7 @@ grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, goto done; } - error = add_socket_to_server(s, sock, addr, addr_len, port_index, &sp); + error = add_socket_to_server(s, sock, addr, port_index, &sp); done: gpr_free(allocated_addr); @@ -535,4 +539,4 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, void grpc_tcp_server_shutdown_listeners(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {} -#endif /* GPR_WINSOCK_SOCKET */ +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_uv.c b/src/core/lib/iomgr/tcp_uv.c new file mode 100644 index 0000000000..05d6eeb240 --- /dev/null +++ b/src/core/lib/iomgr/tcp_uv.c @@ -0,0 +1,335 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include <limits.h> +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/slice_buffer.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/tcp_uv.h" +#include "src/core/lib/support/string.h" + +int grpc_tcp_trace = 0; + +typedef struct { + grpc_endpoint base; + gpr_refcount refcount; + + uv_tcp_t *handle; + + grpc_closure *read_cb; + grpc_closure *write_cb; + + gpr_slice read_slice; + gpr_slice_buffer *read_slices; + gpr_slice_buffer *write_slices; + uv_buf_t *write_buffers; + + bool shutting_down; + char *peer_string; + grpc_pollset *pollset; +} grpc_tcp; + +static void uv_close_callback(uv_handle_t *handle) { gpr_free(handle); } + +static void tcp_free(grpc_tcp *tcp) { gpr_free(tcp); } + +/*#define GRPC_TCP_REFCOUNT_DEBUG*/ +#ifdef GRPC_TCP_REFCOUNT_DEBUG +#define TCP_UNREF(tcp, reason) tcp_unref((tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP unref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count - 1); + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "TCP ref %p : %s %d -> %d", tcp, + reason, tcp->refcount.count, tcp->refcount.count + 1); + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(tcp, reason) tcp_unref((tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) +static void tcp_unref(grpc_tcp *tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } +#endif + +static void alloc_uv_buf(uv_handle_t *handle, size_t suggested_size, + uv_buf_t *buf) { + grpc_tcp *tcp = handle->data; + (void)suggested_size; + tcp->read_slice = gpr_slice_malloc(GRPC_TCP_DEFAULT_READ_SLICE_SIZE); + buf->base = (char *)GPR_SLICE_START_PTR(tcp->read_slice); + buf->len = GPR_SLICE_LENGTH(tcp->read_slice); +} + +static void read_callback(uv_stream_t *stream, ssize_t nread, + const uv_buf_t *buf) { + gpr_slice sub; + grpc_error *error; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_tcp *tcp = stream->data; + grpc_closure *cb = tcp->read_cb; + if (nread == 0) { + // Nothing happened. Wait for the next callback + return; + } + TCP_UNREF(tcp, "read"); + tcp->read_cb = NULL; + // TODO(murgatroid99): figure out what the return value here means + uv_read_stop(stream); + if (nread == UV_EOF) { + error = GRPC_ERROR_CREATE("EOF"); + } else if (nread > 0) { + // Successful read + sub = gpr_slice_sub_no_ref(tcp->read_slice, 0, (size_t)nread); + gpr_slice_buffer_add(tcp->read_slices, sub); + error = GRPC_ERROR_NONE; + if (grpc_tcp_trace) { + size_t i; + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "read: error=%s", str); + grpc_error_free_string(str); + for (i = 0; i < tcp->read_slices->count; i++) { + char *dump = gpr_dump_slice(tcp->read_slices->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, + dump); + gpr_free(dump); + } + } + } else { + // nread < 0: Error + error = GRPC_ERROR_CREATE("TCP Read failed"); + } + grpc_exec_ctx_sched(&exec_ctx, cb, error, NULL); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void uv_endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *read_slices, grpc_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + int status; + grpc_error *error = GRPC_ERROR_NONE; + GPR_ASSERT(tcp->read_cb == NULL); + tcp->read_cb = cb; + tcp->read_slices = read_slices; + gpr_slice_buffer_reset_and_unref(read_slices); + TCP_REF(tcp, "read"); + // TODO(murgatroid99): figure out what the return value here means + status = + uv_read_start((uv_stream_t *)tcp->handle, alloc_uv_buf, read_callback); + if (status != 0) { + error = GRPC_ERROR_CREATE("TCP Read failed at start"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, uv_strerror(status)); + grpc_exec_ctx_sched(exec_ctx, cb, error, NULL); + } + if (grpc_tcp_trace) { + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp, str); + } +} + +static void write_callback(uv_write_t *req, int status) { + grpc_tcp *tcp = req->data; + grpc_error *error; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_closure *cb = tcp->write_cb; + tcp->write_cb = NULL; + TCP_UNREF(tcp, "write"); + if (status == 0) { + error = GRPC_ERROR_NONE; + } else { + error = GRPC_ERROR_CREATE("TCP Write failed"); + } + if (grpc_tcp_trace) { + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "write complete on %p: error=%s", tcp, str); + } + gpr_free(tcp->write_buffers); + gpr_free(req); + grpc_exec_ctx_sched(&exec_ctx, cb, error, NULL); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void uv_endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + gpr_slice_buffer *write_slices, + grpc_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + uv_buf_t *buffers; + unsigned int buffer_count; + unsigned int i; + gpr_slice *slice; + uv_write_t *write_req; + + if (grpc_tcp_trace) { + size_t j; + + for (j = 0; j < write_slices->count; j++) { + char *data = gpr_dump_slice(write_slices->slices[j], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data); + gpr_free(data); + } + } + + if (tcp->shutting_down) { + grpc_exec_ctx_sched(exec_ctx, cb, + GRPC_ERROR_CREATE("TCP socket is shutting down"), NULL); + return; + } + + GPR_ASSERT(tcp->write_cb == NULL); + tcp->write_slices = write_slices; + GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); + if (tcp->write_slices->count == 0) { + // No slices means we don't have to do anything, + // and libuv doesn't like empty writes + grpc_exec_ctx_sched(exec_ctx, cb, GRPC_ERROR_NONE, NULL); + return; + } + + tcp->write_cb = cb; + buffer_count = (unsigned int)tcp->write_slices->count; + buffers = gpr_malloc(sizeof(uv_buf_t) * buffer_count); + for (i = 0; i < buffer_count; i++) { + slice = &tcp->write_slices->slices[i]; + buffers[i].base = (char *)GPR_SLICE_START_PTR(*slice); + buffers[i].len = GPR_SLICE_LENGTH(*slice); + } + write_req = gpr_malloc(sizeof(uv_write_t)); + write_req->data = tcp; + TCP_REF(tcp, "write"); + // TODO(murgatroid99): figure out what the return value here means + uv_write(write_req, (uv_stream_t *)tcp->handle, buffers, buffer_count, + write_callback); +} + +static void uv_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset *pollset) { + // No-op. We're ignoring pollsets currently + (void)exec_ctx; + (void)ep; + (void)pollset; + grpc_tcp *tcp = (grpc_tcp *)ep; + tcp->pollset = pollset; +} + +static void uv_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset_set *pollset) { + // No-op. We're ignoring pollsets currently + (void)exec_ctx; + (void)ep; + (void)pollset; +} + +static void shutdown_callback(uv_shutdown_t *req, int status) { gpr_free(req); } + +static void uv_endpoint_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + if (!tcp->shutting_down) { + tcp->shutting_down = true; + uv_shutdown_t *req = gpr_malloc(sizeof(uv_shutdown_t)); + uv_shutdown(req, (uv_stream_t *)tcp->handle, shutdown_callback); + } +} + +static void uv_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { + grpc_network_status_unregister_endpoint(ep); + grpc_tcp *tcp = (grpc_tcp *)ep; + uv_close((uv_handle_t *)tcp->handle, uv_close_callback); + TCP_UNREF(tcp, "destroy"); +} + +static char *uv_get_peer(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return gpr_strdup(tcp->peer_string); +} + +static grpc_workqueue *uv_get_workqueue(grpc_endpoint *ep) { return NULL; } + +static grpc_endpoint_vtable vtable = {uv_endpoint_read, + uv_endpoint_write, + uv_get_workqueue, + uv_add_to_pollset, + uv_add_to_pollset_set, + uv_endpoint_shutdown, + uv_destroy, + uv_get_peer}; + +grpc_endpoint *grpc_tcp_create(uv_tcp_t *handle, char *peer_string) { + grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); + + if (grpc_tcp_trace) { + gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", tcp); + } + + memset(tcp, 0, sizeof(grpc_tcp)); + tcp->base.vtable = &vtable; + tcp->handle = handle; + handle->data = tcp; + gpr_ref_init(&tcp->refcount, 1); + tcp->peer_string = gpr_strdup(peer_string); + tcp->shutting_down = false; + /* Tell network status tracking code about the new endpoint */ + grpc_network_status_register_endpoint(&tcp->base); + +#ifndef GRPC_UV_TCP_HOLD_LOOP + uv_unref((uv_handle_t *)handle); +#endif + + return &tcp->base; +} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_uv.h b/src/core/lib/iomgr/tcp_uv.h new file mode 100644 index 0000000000..eed41151ea --- /dev/null +++ b/src/core/lib/iomgr/tcp_uv.h @@ -0,0 +1,57 @@ +/* + * + * 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_LIB_IOMGR_TCP_UV_H +#define GRPC_CORE_LIB_IOMGR_TCP_UV_H +/* + Low level TCP "bottom half" implementation, for use by transports built on + top of a TCP connection. + + Note that this file does not (yet) include APIs for creating the socket in + the first place. + + All calls passing slice transfer ownership of a slice refcount unless + otherwise specified. +*/ + +#include "src/core/lib/iomgr/endpoint.h" + +#include <uv.h> + +extern int grpc_tcp_trace; + +#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192 + +grpc_endpoint *grpc_tcp_create(uv_tcp_t *handle, char *peer_string); + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_UV_H */ diff --git a/src/core/lib/iomgr/tcp_windows.c b/src/core/lib/iomgr/tcp_windows.c index 448a72671c..a5f508a2c3 100644 --- a/src/core/lib/iomgr/tcp_windows.c +++ b/src/core/lib/iomgr/tcp_windows.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_WINSOCK_SOCKET +#ifdef GRPC_WINSOCK_SOCKET #include <limits.h> @@ -417,4 +417,4 @@ grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, char *peer_string) { return &tcp->base; } -#endif /* GPR_WINSOCK_SOCKET */ +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/timer.h b/src/core/lib/iomgr/timer.h index 5a9a177963..20fe98c4a7 100644 --- a/src/core/lib/iomgr/timer.h +++ b/src/core/lib/iomgr/timer.h @@ -34,19 +34,20 @@ #ifndef GRPC_CORE_LIB_IOMGR_TIMER_H #define GRPC_CORE_LIB_IOMGR_TIMER_H +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV +#include "src/core/lib/iomgr/timer_uv.h" +#else +#include "src/core/lib/iomgr/timer_generic.h" +#endif /* GRPC_UV */ + #include <grpc/support/port_platform.h> #include <grpc/support/time.h> #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/iomgr.h" -typedef struct grpc_timer { - gpr_timespec deadline; - uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */ - int triggered; - struct grpc_timer *next; - struct grpc_timer *prev; - grpc_closure closure; -} grpc_timer; +typedef struct grpc_timer grpc_timer; /* Initialize *timer. When expired or canceled, timer_cb will be called with *timer_cb_arg and error set to indicate if it expired (GRPC_ERROR_NONE) or diff --git a/src/core/lib/iomgr/timer.c b/src/core/lib/iomgr/timer_generic.c index 9975fa1671..00058f9d86 100644 --- a/src/core/lib/iomgr/timer.c +++ b/src/core/lib/iomgr/timer_generic.c @@ -31,6 +31,10 @@ * */ +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_TIMER_USE_GENERIC + #include "src/core/lib/iomgr/timer.h" #include <grpc/support/log.h> @@ -382,3 +386,5 @@ bool grpc_timer_check(grpc_exec_ctx *exec_ctx, gpr_timespec now, ? GRPC_ERROR_NONE : GRPC_ERROR_CREATE("Shutting down timer system")); } + +#endif /* GRPC_TIMER_USE_GENERIC */ diff --git a/src/core/lib/iomgr/timer_generic.h b/src/core/lib/iomgr/timer_generic.h new file mode 100644 index 0000000000..e4494adb5f --- /dev/null +++ b/src/core/lib/iomgr/timer_generic.h @@ -0,0 +1,49 @@ +/* + * + * 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_LIB_IOMGR_TIMER_GENERIC_H +#define GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H + +#include <grpc/support/time.h> +#include "src/core/lib/iomgr/exec_ctx.h" + +struct grpc_timer { + gpr_timespec deadline; + uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */ + int triggered; + struct grpc_timer *next; + struct grpc_timer *prev; + grpc_closure closure; +}; + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H */ diff --git a/src/core/lib/iomgr/timer_heap.c b/src/core/lib/iomgr/timer_heap.c index 2ad9bb9cd2..f736d335e6 100644 --- a/src/core/lib/iomgr/timer_heap.c +++ b/src/core/lib/iomgr/timer_heap.c @@ -31,6 +31,10 @@ * */ +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_TIMER_USE_GENERIC + #include "src/core/lib/iomgr/timer_heap.h" #include <string.h> @@ -144,3 +148,5 @@ grpc_timer *grpc_timer_heap_top(grpc_timer_heap *heap) { void grpc_timer_heap_pop(grpc_timer_heap *heap) { grpc_timer_heap_remove(heap, grpc_timer_heap_top(heap)); } + +#endif /* GRPC_TIMER_USE_GENERIC */ diff --git a/src/core/lib/iomgr/timer_uv.c b/src/core/lib/iomgr/timer_uv.c new file mode 100644 index 0000000000..cfcb89268b --- /dev/null +++ b/src/core/lib/iomgr/timer_uv.c @@ -0,0 +1,99 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#if GRPC_UV + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/lib/iomgr/timer.h" + +#include <uv.h> + +static void timer_close_callback(uv_handle_t *handle) { gpr_free(handle); } + +static void stop_uv_timer(uv_timer_t *handle) { + uv_timer_stop(handle); + uv_unref((uv_handle_t *)handle); + uv_close((uv_handle_t *)handle, timer_close_callback); +} + +void run_expired_timer(uv_timer_t *handle) { + grpc_timer *timer = (grpc_timer *)handle->data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GPR_ASSERT(!timer->triggered); + timer->triggered = 1; + grpc_exec_ctx_sched(&exec_ctx, &timer->closure, GRPC_ERROR_NONE, NULL); + stop_uv_timer(handle); + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, + gpr_timespec deadline, grpc_iomgr_cb_func timer_cb, + void *timer_cb_arg, gpr_timespec now) { + uint64_t timeout; + uv_timer_t *uv_timer; + grpc_closure_init(&timer->closure, timer_cb, timer_cb_arg); + if (gpr_time_cmp(deadline, now) <= 0) { + timer->triggered = 1; + grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_NONE, NULL); + return; + } + timer->triggered = 0; + timeout = (uint64_t)gpr_time_to_millis(gpr_time_sub(deadline, now)); + uv_timer = gpr_malloc(sizeof(uv_timer_t)); + uv_timer_init(uv_default_loop(), uv_timer); + uv_timer->data = timer; + timer->uv_timer = uv_timer; + uv_timer_start(uv_timer, run_expired_timer, timeout, 0); +} + +void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) { + if (!timer->triggered) { + timer->triggered = 1; + grpc_exec_ctx_sched(exec_ctx, &timer->closure, GRPC_ERROR_CANCELLED, NULL); + stop_uv_timer((uv_timer_t *)timer->uv_timer); + } +} + +bool grpc_timer_check(grpc_exec_ctx *exec_ctx, gpr_timespec now, + gpr_timespec *next) { + return false; +} + +void grpc_timer_list_init(gpr_timespec now) {} +void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) {} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/timer_uv.h b/src/core/lib/iomgr/timer_uv.h new file mode 100644 index 0000000000..3de383ebd5 --- /dev/null +++ b/src/core/lib/iomgr/timer_uv.h @@ -0,0 +1,47 @@ +/* + * + * 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_LIB_IOMGR_TIMER_UV_H +#define GRPC_CORE_LIB_IOMGR_TIMER_UV_H + +#include "src/core/lib/iomgr/exec_ctx.h" + +struct grpc_timer { + grpc_closure closure; + /* This is actually a uv_timer_t*, but we want to keep platform-specific + types out of headers */ + void *uv_timer; + int triggered; +}; + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_UV_H */ diff --git a/src/core/lib/iomgr/udp_server.c b/src/core/lib/iomgr/udp_server.c index edf7b133e9..fd0c7a0f9d 100644 --- a/src/core/lib/iomgr/udp_server.c +++ b/src/core/lib/iomgr/udp_server.c @@ -36,9 +36,9 @@ #define _GNU_SOURCE #endif -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_SOCKET +#ifdef GRPC_POSIX_SOCKET #include "src/core/lib/iomgr/udp_server.h" @@ -62,32 +62,30 @@ #include "src/core/lib/iomgr/error.h" #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" #include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" #include "src/core/lib/support/string.h" -#define INIT_PORT_CAP 2 - /* one listening port */ -typedef struct { +typedef struct grpc_udp_listener grpc_udp_listener; +struct grpc_udp_listener { int fd; grpc_fd *emfd; grpc_udp_server *server; - union { - uint8_t untyped[GRPC_MAX_SOCKADDR_SIZE]; - struct sockaddr sockaddr; - } addr; - size_t addr_len; + grpc_resolved_address addr; grpc_closure read_closure; grpc_closure destroyed_closure; grpc_udp_server_read_cb read_cb; grpc_udp_server_orphan_cb orphan_cb; -} server_port; + + struct grpc_udp_listener *next; +}; /* the overall server */ struct grpc_udp_server { gpr_mu mu; - gpr_cv cv; /* active port count: how many ports are actually still listening */ size_t active_ports; @@ -97,10 +95,10 @@ struct grpc_udp_server { /* is this server shutting down? (boolean) */ int shutdown; - /* all listening ports */ - server_port *ports; - size_t nports; - size_t port_capacity; + /* linked list of server ports */ + grpc_udp_listener *head; + grpc_udp_listener *tail; + unsigned nports; /* shutdown callback */ grpc_closure *shutdown_complete; @@ -116,24 +114,29 @@ struct grpc_udp_server { grpc_udp_server *grpc_udp_server_create(void) { grpc_udp_server *s = gpr_malloc(sizeof(grpc_udp_server)); gpr_mu_init(&s->mu); - gpr_cv_init(&s->cv); s->active_ports = 0; s->destroyed_ports = 0; s->shutdown = 0; - s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); + s->head = NULL; + s->tail = NULL; s->nports = 0; - s->port_capacity = INIT_PORT_CAP; return s; } static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { - grpc_exec_ctx_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL); + if (s->shutdown_complete != NULL) { + grpc_exec_ctx_sched(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE, NULL); + } gpr_mu_destroy(&s->mu); - gpr_cv_destroy(&s->cv); - gpr_free(s->ports); + while (s->head) { + grpc_udp_listener *sp = s->head; + s->head = sp->next; + gpr_free(sp); + } + gpr_free(s); } @@ -154,8 +157,6 @@ static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, events will be received on them - at this point it's safe to destroy things */ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { - size_t i; - /* delete ALL the things */ gpr_mu_lock(&s->mu); @@ -164,9 +165,11 @@ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { return; } - if (s->nports) { - for (i = 0; i < s->nports; i++) { - server_port *sp = &s->ports[i]; + if (s->head) { + grpc_udp_listener *sp; + for (sp = s->head; sp; sp = sp->next) { + grpc_unlink_if_unix_domain_socket(&sp->addr); + sp->destroyed_closure.cb = destroyed_port; sp->destroyed_closure.cb_arg = s; @@ -187,7 +190,7 @@ static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, grpc_closure *on_done) { - size_t i; + grpc_udp_listener *sp; gpr_mu_lock(&s->mu); GPR_ASSERT(!s->shutdown); @@ -197,14 +200,10 @@ void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, /* shutdown all fd's */ if (s->active_ports) { - for (i = 0; i < s->nports; i++) { - server_port *sp = &s->ports[i]; - /* Call the orphan_cb to signal that the FD is about to be closed and - * should no longer be used. */ + for (sp = s->head; sp; sp = sp->next) { GPR_ASSERT(sp->orphan_cb); sp->orphan_cb(sp->emfd); - - grpc_fd_shutdown(exec_ctx, s->ports[i].emfd); + grpc_fd_shutdown(exec_ctx, sp->emfd); } gpr_mu_unlock(&s->mu); } else { @@ -214,10 +213,9 @@ void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, } /* Prepare a recently-created socket for listening. */ -static int prepare_socket(int fd, const struct sockaddr *addr, - size_t addr_len) { - struct sockaddr_storage sockname_temp; - socklen_t sockname_len; +static int prepare_socket(int fd, const grpc_resolved_address *addr) { + grpc_resolved_address sockname_temp; + struct sockaddr *addr_ptr = (struct sockaddr *)addr->addr; /* Set send/receive socket buffers to 1 MB */ int buffer_size_bytes = 1024 * 1024; @@ -237,15 +235,15 @@ static int prepare_socket(int fd, const struct sockaddr *addr, if (grpc_set_socket_ip_pktinfo_if_possible(fd) != GRPC_ERROR_NONE) { gpr_log(GPR_ERROR, "Unable to set ip_pktinfo."); goto error; - } else if (addr->sa_family == AF_INET6) { + } else if (addr_ptr->sa_family == AF_INET6) { if (grpc_set_socket_ipv6_recvpktinfo_if_possible(fd) != GRPC_ERROR_NONE) { gpr_log(GPR_ERROR, "Unable to set ipv6_recvpktinfo."); goto error; } } - GPR_ASSERT(addr_len < ~(socklen_t)0); - if (bind(fd, addr, (socklen_t)addr_len) < 0) { + GPR_ASSERT(addr->len < ~(socklen_t)0); + if (bind(fd, (struct sockaddr *)addr, (socklen_t)addr->len) < 0) { char *addr_str; grpc_sockaddr_to_string(&addr_str, addr, 0); gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno)); @@ -253,8 +251,10 @@ static int prepare_socket(int fd, const struct sockaddr *addr, goto error; } - sockname_len = sizeof(sockname_temp); - if (getsockname(fd, (struct sockaddr *)&sockname_temp, &sockname_len) < 0) { + sockname_temp.len = sizeof(struct sockaddr_storage); + + if (getsockname(fd, (struct sockaddr *)sockname_temp.addr, + (socklen_t *)&sockname_temp.len) < 0) { goto error; } @@ -270,7 +270,7 @@ static int prepare_socket(int fd, const struct sockaddr *addr, goto error; } - return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + return grpc_sockaddr_get_port(&sockname_temp); error: if (fd >= 0) { @@ -281,10 +281,10 @@ error: /* event manager callback when reads are ready */ static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - server_port *sp = arg; + grpc_udp_listener *sp = arg; + gpr_mu_lock(&sp->server->mu); if (error != GRPC_ERROR_NONE) { - gpr_mu_lock(&sp->server->mu); if (0 == --sp->server->active_ports) { gpr_mu_unlock(&sp->server->mu); deactivated_all_ports(exec_ctx, sp->server); @@ -300,34 +300,37 @@ static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { /* Re-arm the notification event so we get another chance to read. */ grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); + gpr_mu_unlock(&sp->server->mu); } static int add_socket_to_server(grpc_udp_server *s, int fd, - const struct sockaddr *addr, size_t addr_len, + const grpc_resolved_address *addr, grpc_udp_server_read_cb read_cb, grpc_udp_server_orphan_cb orphan_cb) { - server_port *sp; + grpc_udp_listener *sp; int port; char *addr_str; char *name; - port = prepare_socket(fd, addr, addr_len); + port = prepare_socket(fd, addr); if (port >= 0) { - grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1); + grpc_sockaddr_to_string(&addr_str, addr, 1); gpr_asprintf(&name, "udp-server-listener:%s", addr_str); gpr_free(addr_str); gpr_mu_lock(&s->mu); - /* append it to the list under a lock */ - if (s->nports == s->port_capacity) { - s->port_capacity *= 2; - s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity); + s->nports++; + sp = gpr_malloc(sizeof(grpc_udp_listener)); + sp->next = NULL; + if (s->head == NULL) { + s->head = sp; + } else { + s->tail->next = sp; } - sp = &s->ports[s->nports++]; + s->tail = sp; sp->server = s; sp->fd = fd; sp->emfd = grpc_fd_create(fd, name); - memcpy(sp->addr.untyped, addr, addr_len); - sp->addr_len = addr_len; + memcpy(&sp->addr, addr, sizeof(grpc_resolved_address)); sp->read_cb = read_cb; sp->orphan_cb = orphan_cb; GPR_ASSERT(sp->emfd); @@ -338,34 +341,34 @@ static int add_socket_to_server(grpc_udp_server *s, int fd, return port; } -int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, - size_t addr_len, grpc_udp_server_read_cb read_cb, +int grpc_udp_server_add_port(grpc_udp_server *s, + const grpc_resolved_address *addr, + grpc_udp_server_read_cb read_cb, grpc_udp_server_orphan_cb orphan_cb) { + grpc_udp_listener *sp; int allocated_port1 = -1; int allocated_port2 = -1; - unsigned i; int fd; grpc_dualstack_mode dsmode; - struct sockaddr_in6 addr6_v4mapped; - struct sockaddr_in wild4; - struct sockaddr_in6 wild6; - struct sockaddr_in addr4_copy; - struct sockaddr *allocated_addr = NULL; - struct sockaddr_storage sockname_temp; - socklen_t sockname_len; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wild4; + grpc_resolved_address wild6; + grpc_resolved_address addr4_copy; + grpc_resolved_address *allocated_addr = NULL; + grpc_resolved_address sockname_temp; int port; /* Check if this is a wildcard port, and if so, try to keep the port the same as some previously created listener. */ if (grpc_sockaddr_get_port(addr) == 0) { - for (i = 0; i < s->nports; i++) { - sockname_len = sizeof(sockname_temp); - if (0 == getsockname(s->ports[i].fd, (struct sockaddr *)&sockname_temp, - &sockname_len)) { - port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp); + for (sp = s->head; sp; sp = sp->next) { + sockname_temp.len = sizeof(struct sockaddr_storage); + if (0 == getsockname(sp->fd, (struct sockaddr *)sockname_temp.addr, + (socklen_t *)&sockname_temp.len)) { + port = grpc_sockaddr_get_port(&sockname_temp); if (port > 0) { - allocated_addr = gpr_malloc(addr_len); - memcpy(allocated_addr, addr, addr_len); + allocated_addr = gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); grpc_sockaddr_set_port(allocated_addr, port); addr = allocated_addr; break; @@ -375,8 +378,7 @@ int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, } if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = (const struct sockaddr *)&addr6_v4mapped; - addr_len = sizeof(addr6_v4mapped); + addr = &addr6_v4mapped; } /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ @@ -384,22 +386,19 @@ int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, grpc_sockaddr_make_wildcards(port, &wild4, &wild6); /* Try listening on IPv6 first. */ - addr = (struct sockaddr *)&wild6; - addr_len = sizeof(wild6); + addr = &wild6; // TODO(rjshade): Test and propagate the returned grpc_error*: grpc_create_dualstack_socket(addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd); - allocated_port1 = - add_socket_to_server(s, fd, addr, addr_len, read_cb, orphan_cb); + allocated_port1 = add_socket_to_server(s, fd, addr, read_cb, orphan_cb); if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { goto done; } /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ if (port == 0 && allocated_port1 > 0) { - grpc_sockaddr_set_port((struct sockaddr *)&wild4, allocated_port1); + grpc_sockaddr_set_port(&wild4, allocated_port1); } - addr = (struct sockaddr *)&wild4; - addr_len = sizeof(wild4); + addr = &wild4; } // TODO(rjshade): Test and propagate the returned grpc_error*: @@ -409,11 +408,9 @@ int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, } if (dsmode == GRPC_DSMODE_IPV4 && grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) { - addr = (struct sockaddr *)&addr4_copy; - addr_len = sizeof(addr4_copy); + addr = &addr4_copy; } - allocated_port2 = - add_socket_to_server(s, fd, addr, addr_len, read_cb, orphan_cb); + allocated_port2 = add_socket_to_server(s, fd, addr, read_cb, orphan_cb); done: gpr_free(allocated_addr); @@ -421,27 +418,40 @@ done: } int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned port_index) { - return (port_index < s->nports) ? s->ports[port_index].fd : -1; + grpc_udp_listener *sp; + if (port_index >= s->nports) { + return -1; + } + + for (sp = s->head; sp && port_index != 0; sp = sp->next) { + --port_index; + } + return sp->fd; } void grpc_udp_server_start(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, grpc_pollset **pollsets, size_t pollset_count, grpc_server *server) { - size_t i, j; + size_t i; gpr_mu_lock(&s->mu); + grpc_udp_listener *sp; GPR_ASSERT(s->active_ports == 0); s->pollsets = pollsets; s->grpc_server = server; - for (i = 0; i < s->nports; i++) { - for (j = 0; j < pollset_count; j++) { - grpc_pollset_add_fd(exec_ctx, pollsets[j], s->ports[i].emfd); + + sp = s->head; + while (sp != NULL) { + for (i = 0; i < pollset_count; i++) { + grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd); } - s->ports[i].read_closure.cb = on_read; - s->ports[i].read_closure.cb_arg = &s->ports[i]; - grpc_fd_notify_on_read(exec_ctx, s->ports[i].emfd, - &s->ports[i].read_closure); + sp->read_closure.cb = on_read; + sp->read_closure.cb_arg = sp; + grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); + s->active_ports++; + sp = sp->next; } + gpr_mu_unlock(&s->mu); } diff --git a/src/core/lib/iomgr/udp_server.h b/src/core/lib/iomgr/udp_server.h index 33c5ce11cd..f3c466a031 100644 --- a/src/core/lib/iomgr/udp_server.h +++ b/src/core/lib/iomgr/udp_server.h @@ -36,6 +36,7 @@ #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/resolve_address.h" /* Forward decl of struct grpc_server */ /* This is not typedef'ed to avoid a typedef-redefinition error */ @@ -59,7 +60,7 @@ void grpc_udp_server_start(grpc_exec_ctx *exec_ctx, grpc_udp_server *udp_server, grpc_pollset **pollsets, size_t pollset_count, struct grpc_server *server); -int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned index); +int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned port_index); /* Add a port to the server, returning port number on success, or negative on failure. @@ -71,8 +72,9 @@ int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned index); /* TODO(ctiller): deprecate this, and make grpc_udp_server_add_ports to handle all of the multiple socket port matching logic in one place */ -int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, - size_t addr_len, grpc_udp_server_read_cb read_cb, +int grpc_udp_server_add_port(grpc_udp_server *s, + const grpc_resolved_address *addr, + grpc_udp_server_read_cb read_cb, grpc_udp_server_orphan_cb orphan_cb); void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *server, diff --git a/src/core/lib/iomgr/unix_sockets_posix.c b/src/core/lib/iomgr/unix_sockets_posix.c index 0e7670e5a5..030acd9811 100644 --- a/src/core/lib/iomgr/unix_sockets_posix.c +++ b/src/core/lib/iomgr/unix_sockets_posix.c @@ -30,16 +30,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ +#include "src/core/lib/iomgr/port.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" +#ifdef GRPC_HAVE_UNIX_SOCKET -#ifdef GPR_HAVE_UNIX_SOCKET +#include "src/core/lib/iomgr/sockaddr.h" #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/un.h> +#include "src/core/lib/iomgr/unix_sockets_posix.h" + #include <grpc/support/alloc.h> #include <grpc/support/log.h> @@ -61,15 +64,18 @@ grpc_error *grpc_resolve_unix_domain_address(const char *name, return GRPC_ERROR_NONE; } -int grpc_is_unix_socket(const struct sockaddr *addr) { +int grpc_is_unix_socket(const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; return addr->sa_family == AF_UNIX; } -void grpc_unlink_if_unix_domain_socket(const struct sockaddr *addr) { +void grpc_unlink_if_unix_domain_socket( + const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; if (addr->sa_family != AF_UNIX) { return; } - struct sockaddr_un *un = (struct sockaddr_un *)addr; + struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr; struct stat st; if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) { @@ -77,7 +83,9 @@ void grpc_unlink_if_unix_domain_socket(const struct sockaddr *addr) { } } -char *grpc_sockaddr_to_uri_unix_if_possible(const struct sockaddr *addr) { +char *grpc_sockaddr_to_uri_unix_if_possible( + const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; if (addr->sa_family != AF_UNIX) { return NULL; } diff --git a/src/core/lib/iomgr/unix_sockets_posix.h b/src/core/lib/iomgr/unix_sockets_posix.h index db0516d945..21afd3aa15 100644 --- a/src/core/lib/iomgr/unix_sockets_posix.h +++ b/src/core/lib/iomgr/unix_sockets_posix.h @@ -34,22 +34,23 @@ #ifndef GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H #define GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" #include <grpc/support/string_util.h> #include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/sockaddr.h" void grpc_create_socketpair_if_unix(int sv[2]); grpc_error *grpc_resolve_unix_domain_address( const char *name, grpc_resolved_addresses **addresses); -int grpc_is_unix_socket(const struct sockaddr *addr); +int grpc_is_unix_socket(const grpc_resolved_address *resolved_addr); -void grpc_unlink_if_unix_domain_socket(const struct sockaddr *addr); +void grpc_unlink_if_unix_domain_socket( + const grpc_resolved_address *resolved_addr); -char *grpc_sockaddr_to_uri_unix_if_possible(const struct sockaddr *addr); +char *grpc_sockaddr_to_uri_unix_if_possible( + const grpc_resolved_address *resolved_addr); #endif /* GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H */ diff --git a/src/core/lib/iomgr/unix_sockets_posix_noop.c b/src/core/lib/iomgr/unix_sockets_posix_noop.c index 56b47c3daf..1daf5152c1 100644 --- a/src/core/lib/iomgr/unix_sockets_posix_noop.c +++ b/src/core/lib/iomgr/unix_sockets_posix_noop.c @@ -33,7 +33,7 @@ #include "src/core/lib/iomgr/unix_sockets_posix.h" -#ifndef GPR_HAVE_UNIX_SOCKET +#ifndef GRPC_HAVE_UNIX_SOCKET #include <grpc/support/log.h> @@ -50,11 +50,11 @@ grpc_error *grpc_resolve_unix_domain_address( return GRPC_ERROR_CREATE("Unix domain sockets are not supported on Windows"); } -int grpc_is_unix_socket(const struct sockaddr *addr) { return false; } +int grpc_is_unix_socket(const grpc_resolved_address *addr) { return false; } -void grpc_unlink_if_unix_domain_socket(const struct sockaddr *addr) {} +void grpc_unlink_if_unix_domain_socket(const grpc_resolved_address *addr) {} -char *grpc_sockaddr_to_uri_unix_if_possible(const struct sockaddr *addr) { +char *grpc_sockaddr_to_uri_unix_if_possible(const grpc_resolved_address *addr) { return NULL; } diff --git a/src/core/lib/iomgr/wakeup_fd_cv.c b/src/core/lib/iomgr/wakeup_fd_cv.c index b4165208ed..da4c2870cd 100644 --- a/src/core/lib/iomgr/wakeup_fd_cv.c +++ b/src/core/lib/iomgr/wakeup_fd_cv.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_WAKEUP_FD +#ifdef GRPC_POSIX_WAKEUP_FD #include "src/core/lib/iomgr/wakeup_fd_cv.h" @@ -115,4 +115,4 @@ const grpc_wakeup_fd_vtable grpc_cv_wakeup_fd_vtable = { cv_fd_init, cv_fd_consume, cv_fd_wakeup, cv_fd_destroy, cv_check_availability}; -#endif /* GPR_POSIX_WAKUP_FD */ +#endif /* GRPC_POSIX_WAKUP_FD */ diff --git a/src/core/lib/iomgr/wakeup_fd_eventfd.c b/src/core/lib/iomgr/wakeup_fd_eventfd.c index 95f6102330..373e21d3e1 100644 --- a/src/core/lib/iomgr/wakeup_fd_eventfd.c +++ b/src/core/lib/iomgr/wakeup_fd_eventfd.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_LINUX_EVENTFD +#ifdef GRPC_LINUX_EVENTFD #include <errno.h> #include <sys/eventfd.h> @@ -94,4 +94,4 @@ const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy, eventfd_check_availability}; -#endif /* GPR_LINUX_EVENTFD */ +#endif /* GRPC_LINUX_EVENTFD */ diff --git a/src/core/lib/iomgr/wakeup_fd_nospecial.c b/src/core/lib/iomgr/wakeup_fd_nospecial.c index cb2f707dc5..611bced029 100644 --- a/src/core/lib/iomgr/wakeup_fd_nospecial.c +++ b/src/core/lib/iomgr/wakeup_fd_nospecial.c @@ -36,9 +36,9 @@ * systems without anything better than pipe. */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_NO_SPECIAL_WAKEUP_FD +#ifdef GRPC_POSIX_NO_SPECIAL_WAKEUP_FD #include <stddef.h> #include "src/core/lib/iomgr/wakeup_fd_posix.h" @@ -48,4 +48,4 @@ static int check_availability_invalid(void) { return 0; } const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { NULL, NULL, NULL, NULL, check_availability_invalid}; -#endif /* GPR_POSIX_NO_SPECIAL_WAKEUP_FD */ +#endif /* GRPC_POSIX_NO_SPECIAL_WAKEUP_FD */ diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.c b/src/core/lib/iomgr/wakeup_fd_pipe.c index d0ea216aa0..183f0eb930 100644 --- a/src/core/lib/iomgr/wakeup_fd_pipe.c +++ b/src/core/lib/iomgr/wakeup_fd_pipe.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_WAKEUP_FD +#ifdef GRPC_POSIX_WAKEUP_FD #include "src/core/lib/iomgr/wakeup_fd_posix.h" diff --git a/src/core/lib/iomgr/wakeup_fd_posix.c b/src/core/lib/iomgr/wakeup_fd_posix.c index 5c894bef37..85526402bd 100644 --- a/src/core/lib/iomgr/wakeup_fd_posix.c +++ b/src/core/lib/iomgr/wakeup_fd_posix.c @@ -31,9 +31,9 @@ * */ -#include <grpc/support/port_platform.h> +#include "src/core/lib/iomgr/port.h" -#ifdef GPR_POSIX_WAKEUP_FD +#ifdef GRPC_POSIX_WAKEUP_FD #include <stddef.h> #include "src/core/lib/iomgr/wakeup_fd_cv.h" @@ -98,4 +98,4 @@ void grpc_wakeup_fd_destroy(grpc_wakeup_fd *fd_info) { } } -#endif /* GPR_POSIX_WAKEUP_FD */ +#endif /* GRPC_POSIX_WAKEUP_FD */ diff --git a/src/core/lib/iomgr/workqueue.h b/src/core/lib/iomgr/workqueue.h index 5b96d1d851..73d9849843 100644 --- a/src/core/lib/iomgr/workqueue.h +++ b/src/core/lib/iomgr/workqueue.h @@ -39,6 +39,7 @@ #include "src/core/lib/iomgr/iomgr.h" #include "src/core/lib/iomgr/pollset.h" #include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/lib/iomgr/port.h" #ifdef GPR_WINDOWS #include "src/core/lib/iomgr/workqueue_windows.h" diff --git a/src/core/lib/iomgr/workqueue_uv.c b/src/core/lib/iomgr/workqueue_uv.c new file mode 100644 index 0000000000..e58ca476cc --- /dev/null +++ b/src/core/lib/iomgr/workqueue_uv.c @@ -0,0 +1,66 @@ +/* + * + * 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 "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include "src/core/lib/iomgr/workqueue.h" + +// Minimal implementation of grpc_workqueue for libuv +// Works by directly enqueuing workqueue items onto the current execution +// context, which is at least correct, if not performant or in the spirit of +// workqueues. + +void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {} + +#ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG +grpc_workqueue *grpc_workqueue_ref(grpc_workqueue *workqueue, const char *file, + int line, const char *reason) { + return workqueue; +} +void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue, + const char *file, int line, const char *reason) {} +#else +grpc_workqueue *grpc_workqueue_ref(grpc_workqueue *workqueue) { + return workqueue; +} +void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) {} +#endif + +void grpc_workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue, + grpc_closure *closure, grpc_error *error) { + grpc_exec_ctx_sched(exec_ctx, closure, error, NULL); +} + +#endif /* GPR_UV */ diff --git a/src/core/lib/iomgr/workqueue_uv.h b/src/core/lib/iomgr/workqueue_uv.h new file mode 100644 index 0000000000..be3f8e4d93 --- /dev/null +++ b/src/core/lib/iomgr/workqueue_uv.h @@ -0,0 +1,37 @@ +/* + * + * 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_LIB_IOMGR_WORKQUEUE_UV_H +#define GRPC_CORE_LIB_IOMGR_WORKQUEUE_UV_H + +#endif /* GRPC_CORE_LIB_IOMGR_WORKQUEUE_UV_H */ diff --git a/src/core/lib/security/credentials/google_default/credentials_posix.c b/src/core/lib/security/credentials/google_default/credentials_generic.c index 42c9d7f997..d13d8c5200 100644 --- a/src/core/lib/security/credentials/google_default/credentials_posix.c +++ b/src/core/lib/security/credentials/google_default/credentials_generic.c @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,10 +31,6 @@ * */ -#include <grpc/support/port_platform.h> - -#ifdef GPR_POSIX_FILE - #include "src/core/lib/security/credentials/google_default/google_default_credentials.h" #include <grpc/support/alloc.h> @@ -46,16 +42,13 @@ char *grpc_get_well_known_google_credentials_file_path_impl(void) { char *result = NULL; - char *home = gpr_getenv("HOME"); - if (home == NULL) { - gpr_log(GPR_ERROR, "Could not get HOME environment variable."); + char *base = gpr_getenv(GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR); + if (base == NULL) { + gpr_log(GPR_ERROR, "Could not get " GRPC_GOOGLE_CREDENTIALS_ENV_VAR + " environment variable."); return NULL; } - gpr_asprintf(&result, "%s/.config/%s/%s", home, - GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY, - GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE); - gpr_free(home); + gpr_asprintf(&result, "%s/%s", base, GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX); + gpr_free(base); return result; } - -#endif /* GPR_POSIX_FILE */ diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.h b/src/core/lib/security/credentials/google_default/google_default_credentials.h index fac4377e2c..b55546ded0 100644 --- a/src/core/lib/security/credentials/google_default/google_default_credentials.h +++ b/src/core/lib/security/credentials/google_default/google_default_credentials.h @@ -34,12 +34,26 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_GOOGLE_DEFAULT_CREDENTIALS_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_GOOGLE_DEFAULT_CREDENTIALS_H +#include <grpc/support/port_platform.h> + #include "src/core/lib/security/credentials/credentials.h" #define GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY "gcloud" #define GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE \ "application_default_credentials.json" +#ifdef GPR_WINDOWS +#define GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR "APPDATA" +#define GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX \ + GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY \ + "/" GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE +#else +#define GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR "HOME" +#define GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX \ + ".config/" GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY \ + "/" GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE +#endif + void grpc_flush_cached_google_default_credentials(void); #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_GOOGLE_DEFAULT_CREDENTIALS_H \ diff --git a/src/core/lib/support/thd.c b/src/core/lib/support/thd.c index 41daeb5d0e..40f53a18e5 100644 --- a/src/core/lib/support/thd.c +++ b/src/core/lib/support/thd.c @@ -33,7 +33,7 @@ /* Posix implementation for gpr threads. */ -#include <memory.h> +#include <string.h> #include <grpc/support/thd.h> diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c index 30559304c6..02a5de0857 100644 --- a/src/core/lib/surface/call.c +++ b/src/core/lib/surface/call.c @@ -30,6 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ + #include <assert.h> #include <limits.h> #include <stdio.h> diff --git a/src/core/lib/transport/mdstr_hash_table.c b/src/core/lib/transport/mdstr_hash_table.c index 4be0536dd7..8e914c420b 100644 --- a/src/core/lib/transport/mdstr_hash_table.c +++ b/src/core/lib/transport/mdstr_hash_table.c @@ -42,6 +42,7 @@ struct grpc_mdstr_hash_table { gpr_refcount refs; size_t num_entries; + size_t size; grpc_mdstr_hash_table_entry* entries; }; @@ -50,13 +51,12 @@ struct grpc_mdstr_hash_table { static size_t grpc_mdstr_hash_table_find_index( const grpc_mdstr_hash_table* table, const grpc_mdstr* key, bool find_empty) { - for (size_t i = 0; i < table->num_entries; ++i) { - const size_t idx = (key->hash + i * i) % table->num_entries; - if (table->entries[idx].key == NULL) - return find_empty ? idx : table->num_entries; + for (size_t i = 0; i < table->size; ++i) { + const size_t idx = (key->hash + i * i) % table->size; + if (table->entries[idx].key == NULL) return find_empty ? idx : table->size; if (table->entries[idx].key == key) return idx; } - return table->num_entries; // Not found. + return table->size; // Not found. } static void grpc_mdstr_hash_table_add( @@ -65,7 +65,7 @@ static void grpc_mdstr_hash_table_add( GPR_ASSERT(value != NULL); const size_t idx = grpc_mdstr_hash_table_find_index(table, key, true /* find_empty */); - GPR_ASSERT(idx != table->num_entries); // Table should never be full. + GPR_ASSERT(idx != table->size); // Table should never be full. grpc_mdstr_hash_table_entry* entry = &table->entries[idx]; entry->key = GRPC_MDSTR_REF(key); entry->value = vtable->copy_value(value); @@ -77,11 +77,11 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_create( grpc_mdstr_hash_table* table = gpr_malloc(sizeof(*table)); memset(table, 0, sizeof(*table)); gpr_ref_init(&table->refs, 1); + table->num_entries = num_entries; // Quadratic probing gets best performance when the table is no more // than half full. - table->num_entries = num_entries * 2; - const size_t entry_size = - sizeof(grpc_mdstr_hash_table_entry) * table->num_entries; + table->size = num_entries * 2; + const size_t entry_size = sizeof(grpc_mdstr_hash_table_entry) * table->size; table->entries = gpr_malloc(entry_size); memset(table->entries, 0, entry_size); for (size_t i = 0; i < num_entries; ++i) { @@ -98,7 +98,7 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table) { int grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table) { if (table != NULL && gpr_unref(&table->refs)) { - for (size_t i = 0; i < table->num_entries; ++i) { + for (size_t i = 0; i < table->size; ++i) { grpc_mdstr_hash_table_entry* entry = &table->entries[i]; if (entry->key != NULL) { GRPC_MDSTR_UNREF(entry->key); @@ -112,11 +112,15 @@ int grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table) { return 0; } +size_t grpc_mdstr_hash_table_num_entries(const grpc_mdstr_hash_table* table) { + return table->num_entries; +} + void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table, const grpc_mdstr* key) { const size_t idx = grpc_mdstr_hash_table_find_index(table, key, false /* find_empty */); - if (idx == table->num_entries) return NULL; // Not found. + if (idx == table->size) return NULL; // Not found. return table->entries[idx].value; } @@ -140,3 +144,14 @@ int grpc_mdstr_hash_table_cmp(const grpc_mdstr_hash_table* table1, } return 0; } + +void grpc_mdstr_hash_table_iterate( + const grpc_mdstr_hash_table* table, + void (*func)(const grpc_mdstr_hash_table_entry* entry, void* user_data), + void* user_data) { + for (size_t i = 0; i < table->size; ++i) { + if (table->entries[i].key != NULL) { + func(&table->entries[i], user_data); + } + } +} diff --git a/src/core/lib/transport/mdstr_hash_table.h b/src/core/lib/transport/mdstr_hash_table.h index 52e5b023db..bceb4df93d 100644 --- a/src/core/lib/transport/mdstr_hash_table.h +++ b/src/core/lib/transport/mdstr_hash_table.h @@ -70,6 +70,9 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table); /** Returns 1 when \a table is destroyed. */ int grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table); +/** Returns the number of entries in \a table. */ +size_t grpc_mdstr_hash_table_num_entries(const grpc_mdstr_hash_table* table); + /** Returns the value from \a table associated with \a key. Returns NULL if \a key is not found. */ void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table, @@ -80,4 +83,10 @@ void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table, int grpc_mdstr_hash_table_cmp(const grpc_mdstr_hash_table* table1, const grpc_mdstr_hash_table* table2); +/** Iterates over the entries in \a table, calling \a func for each entry. */ +void grpc_mdstr_hash_table_iterate( + const grpc_mdstr_hash_table* table, + void (*func)(const grpc_mdstr_hash_table_entry* entry, void* user_data), + void* user_data); + #endif /* GRPC_CORE_LIB_TRANSPORT_MDSTR_HASH_TABLE_H */ diff --git a/src/core/ext/client_config/method_config.c b/src/core/lib/transport/method_config.c index f8a82323e7..57d97700bf 100644 --- a/src/core/ext/client_config/method_config.c +++ b/src/core/lib/transport/method_config.c @@ -29,7 +29,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#include "src/core/ext/client_config/method_config.h" +#include "src/core/lib/transport/method_config.h" #include <string.h> @@ -254,12 +254,12 @@ int grpc_method_config_table_cmp(const grpc_method_config_table* table1, return grpc_mdstr_hash_table_cmp(table1, table2); } -grpc_method_config* grpc_method_config_table_get_method_config( - const grpc_method_config_table* table, const grpc_mdstr* path) { - grpc_method_config* method_config = grpc_mdstr_hash_table_get(table, path); +void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table, + const grpc_mdstr* path) { + void* value = grpc_mdstr_hash_table_get(table, path); // If we didn't find a match for the path, try looking for a wildcard // entry (i.e., change "/service/method" to "/service/*"). - if (method_config == NULL) { + if (value == NULL) { const char* path_str = grpc_mdstr_as_c_string(path); const char* sep = strrchr(path_str, '/') + 1; const size_t len = (size_t)(sep - path_str); @@ -269,10 +269,10 @@ grpc_method_config* grpc_method_config_table_get_method_config( buf[len + 1] = '\0'; grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf); gpr_free(buf); - method_config = grpc_mdstr_hash_table_get(table, wildcard_path); + value = grpc_mdstr_hash_table_get(table, wildcard_path); GRPC_MDSTR_UNREF(wildcard_path); } - return method_config; + return value; } static void* copy_arg(void* p) { return grpc_method_config_table_ref(p); } @@ -294,3 +294,47 @@ grpc_arg grpc_method_config_table_create_channel_arg( arg.value.pointer.vtable = &arg_vtable; return arg; } + +// State used by convert_entry() below. +typedef struct conversion_state { + void* (*convert_value)(const grpc_method_config* method_config); + const grpc_mdstr_hash_table_vtable* vtable; + size_t num_entries; + grpc_mdstr_hash_table_entry* entries; +} conversion_state; + +// A function to be passed to grpc_mdstr_hash_table_iterate() to create +// a copy of the entries. +static void convert_entry(const grpc_mdstr_hash_table_entry* entry, + void* user_data) { + conversion_state* state = user_data; + state->entries[state->num_entries].key = GRPC_MDSTR_REF(entry->key); + state->entries[state->num_entries].value = state->convert_value(entry->value); + state->entries[state->num_entries].vtable = state->vtable; + ++state->num_entries; +} + +grpc_mdstr_hash_table* grpc_method_config_table_convert( + const grpc_method_config_table* table, + void* (*convert_value)(const grpc_method_config* method_config), + const grpc_mdstr_hash_table_vtable* vtable) { + // Create an array of the entries in the table with converted values. + conversion_state state; + state.convert_value = convert_value; + state.vtable = vtable; + state.num_entries = 0; + state.entries = gpr_malloc(sizeof(grpc_mdstr_hash_table_entry) * + grpc_mdstr_hash_table_num_entries(table)); + grpc_mdstr_hash_table_iterate(table, convert_entry, &state); + // Create a new table based on the array we just constructed. + grpc_mdstr_hash_table* new_table = + grpc_mdstr_hash_table_create(state.num_entries, state.entries); + // Clean up the array. + for (size_t i = 0; i < state.num_entries; ++i) { + GRPC_MDSTR_UNREF(state.entries[i].key); + vtable->destroy_value(state.entries[i].value); + } + gpr_free(state.entries); + // Return the new table. + return new_table; +} diff --git a/src/core/ext/client_config/method_config.h b/src/core/lib/transport/method_config.h index 1302ac425d..58fedd9436 100644 --- a/src/core/ext/client_config/method_config.h +++ b/src/core/lib/transport/method_config.h @@ -29,8 +29,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // -#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_METHOD_CONFIG_H -#define GRPC_CORE_EXT_CLIENT_CONFIG_METHOD_CONFIG_H +#ifndef GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H +#define GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H #include <stdbool.h> @@ -106,11 +106,31 @@ int grpc_method_config_table_cmp(const grpc_method_config_table* table1, /// the form "/service/method". /// Returns NULL if the method has no config. /// Caller does NOT own a reference to the result. -grpc_method_config* grpc_method_config_table_get_method_config( - const grpc_method_config_table* table, const grpc_mdstr* path); +/// +/// Note: This returns a void* instead of a grpc_method_config* so that +/// it can also be used for tables constructed via +/// grpc_method_config_table_convert(). +void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table, + const grpc_mdstr* path); /// Returns a channel arg containing \a table. grpc_arg grpc_method_config_table_create_channel_arg( grpc_method_config_table* table); -#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_METHOD_CONFIG_H */ +/// Generates a new table from \a table whose values are converted to a +/// new form via the \a convert_value function. The new table will use +/// \a vtable for its values. +/// +/// This is generally used to convert the table's value type from +/// grpc_method_config to a simple struct containing only the parameters +/// relevant to a particular filter, thus avoiding the need for a hash +/// table lookup on the fast path. In that scenario, \a convert_value +/// will return a new instance of the struct containing the values from +/// the grpc_method_config, and \a vtable provides the methods for +/// operating on the struct type. +grpc_mdstr_hash_table* grpc_method_config_table_convert( + const grpc_method_config_table* table, + void* (*convert_value)(const grpc_method_config* method_config), + const grpc_mdstr_hash_table_vtable* vtable); + +#endif /* GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H */ diff --git a/src/core/lib/transport/static_metadata.c b/src/core/lib/transport/static_metadata.c index f019ef156a..8b22592b45 100644 --- a/src/core/lib/transport/static_metadata.c +++ b/src/core/lib/transport/static_metadata.c @@ -126,7 +126,7 @@ const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = { "if-range", "if-unmodified-since", "last-modified", - "lb-cost", + "lb-cost-bin", "lb-token", "link", "location", diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h index e0a8196419..28ad6f2961 100644 --- a/src/core/lib/transport/static_metadata.h +++ b/src/core/lib/transport/static_metadata.h @@ -175,8 +175,8 @@ extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT]; #define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[62]) /* "last-modified" */ #define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[63]) -/* "lb-cost" */ -#define GRPC_MDSTR_LB_COST (&grpc_static_mdstr_table[64]) +/* "lb-cost-bin" */ +#define GRPC_MDSTR_LB_COST_BIN (&grpc_static_mdstr_table[64]) /* "lb-token" */ #define GRPC_MDSTR_LB_TOKEN (&grpc_static_mdstr_table[65]) /* "link" */ @@ -337,8 +337,8 @@ extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT]; #define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[44]) /* "last-modified": "" */ #define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[45]) -/* "lb-cost": "" */ -#define GRPC_MDELEM_LB_COST_EMPTY (&grpc_static_mdelem_table[46]) +/* "lb-cost-bin": "" */ +#define GRPC_MDELEM_LB_COST_BIN_EMPTY (&grpc_static_mdelem_table[46]) /* "lb-token": "" */ #define GRPC_MDELEM_LB_TOKEN_EMPTY (&grpc_static_mdelem_table[47]) /* "link": "" */ diff --git a/src/core/lib/tsi/ssl_transport_security.c b/src/core/lib/tsi/ssl_transport_security.c index e91c6316e7..749b46e19f 100644 --- a/src/core/lib/tsi/ssl_transport_security.c +++ b/src/core/lib/tsi/ssl_transport_security.c @@ -31,6 +31,9 @@ * */ +#include "src/core/lib/iomgr/sockaddr.h" + +#include "src/core/lib/iomgr/socket_utils.h" #include "src/core/lib/tsi/ssl_transport_security.h" #include <grpc/support/port_platform.h> @@ -38,13 +41,6 @@ #include <limits.h> #include <string.h> -/* TODO(jboeuf): refactor inet_ntop into a portability header. */ -#ifdef GPR_WINSOCK_SOCKET -#include <ws2tcpip.h> -#else -#include <arpa/inet.h> -#endif - #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/sync.h> @@ -353,8 +349,8 @@ static tsi_result add_subject_alt_names_properties_to_peer( result = TSI_INTERNAL_ERROR; break; } - const char *name = inet_ntop(af, subject_alt_name->d.iPAddress->data, - ntop_buf, INET6_ADDRSTRLEN); + const char *name = grpc_inet_ntop(af, subject_alt_name->d.iPAddress->data, + ntop_buf, INET6_ADDRSTRLEN); if (name == NULL) { gpr_log(GPR_ERROR, "Could not get IP string from asn1 octet."); result = TSI_INTERNAL_ERROR; diff --git a/src/core/plugin_registry/grpc_cronet_plugin_registry.c b/src/core/plugin_registry/grpc_cronet_plugin_registry.c index d0b5f5c702..d339ed327f 100644 --- a/src/core/plugin_registry/grpc_cronet_plugin_registry.c +++ b/src/core/plugin_registry/grpc_cronet_plugin_registry.c @@ -35,12 +35,12 @@ extern void grpc_chttp2_plugin_init(void); extern void grpc_chttp2_plugin_shutdown(void); -extern void grpc_client_config_init(void); -extern void grpc_client_config_shutdown(void); +extern void grpc_client_channel_init(void); +extern void grpc_client_channel_shutdown(void); void grpc_register_built_in_plugins(void) { grpc_register_plugin(grpc_chttp2_plugin_init, grpc_chttp2_plugin_shutdown); - grpc_register_plugin(grpc_client_config_init, - grpc_client_config_shutdown); + grpc_register_plugin(grpc_client_channel_init, + grpc_client_channel_shutdown); } diff --git a/src/core/plugin_registry/grpc_plugin_registry.c b/src/core/plugin_registry/grpc_plugin_registry.c index 7a7a9ce477..2efd9cd1ad 100644 --- a/src/core/plugin_registry/grpc_plugin_registry.c +++ b/src/core/plugin_registry/grpc_plugin_registry.c @@ -35,8 +35,8 @@ extern void grpc_chttp2_plugin_init(void); extern void grpc_chttp2_plugin_shutdown(void); -extern void grpc_client_config_init(void); -extern void grpc_client_config_shutdown(void); +extern void grpc_client_channel_init(void); +extern void grpc_client_channel_shutdown(void); extern void grpc_lb_policy_grpclb_init(void); extern void grpc_lb_policy_grpclb_shutdown(void); extern void grpc_lb_policy_pick_first_init(void); @@ -55,8 +55,8 @@ extern void census_grpc_plugin_shutdown(void); void grpc_register_built_in_plugins(void) { grpc_register_plugin(grpc_chttp2_plugin_init, grpc_chttp2_plugin_shutdown); - grpc_register_plugin(grpc_client_config_init, - grpc_client_config_shutdown); + grpc_register_plugin(grpc_client_channel_init, + grpc_client_channel_shutdown); grpc_register_plugin(grpc_lb_policy_grpclb_init, grpc_lb_policy_grpclb_shutdown); grpc_register_plugin(grpc_lb_policy_pick_first_init, diff --git a/src/core/plugin_registry/grpc_unsecure_plugin_registry.c b/src/core/plugin_registry/grpc_unsecure_plugin_registry.c index ad4ddf0ff4..8b18af699d 100644 --- a/src/core/plugin_registry/grpc_unsecure_plugin_registry.c +++ b/src/core/plugin_registry/grpc_unsecure_plugin_registry.c @@ -35,8 +35,8 @@ extern void grpc_chttp2_plugin_init(void); extern void grpc_chttp2_plugin_shutdown(void); -extern void grpc_client_config_init(void); -extern void grpc_client_config_shutdown(void); +extern void grpc_client_channel_init(void); +extern void grpc_client_channel_shutdown(void); extern void grpc_resolver_dns_native_init(void); extern void grpc_resolver_dns_native_shutdown(void); extern void grpc_resolver_sockaddr_init(void); @@ -55,8 +55,8 @@ extern void census_grpc_plugin_shutdown(void); void grpc_register_built_in_plugins(void) { grpc_register_plugin(grpc_chttp2_plugin_init, grpc_chttp2_plugin_shutdown); - grpc_register_plugin(grpc_client_config_init, - grpc_client_config_shutdown); + grpc_register_plugin(grpc_client_channel_init, + grpc_client_channel_shutdown); grpc_register_plugin(grpc_resolver_dns_native_init, grpc_resolver_dns_native_shutdown); grpc_register_plugin(grpc_resolver_sockaddr_init, diff --git a/src/cpp/client/cronet_credentials.cc b/src/cpp/client/cronet_credentials.cc new file mode 100644 index 0000000000..60cad097db --- /dev/null +++ b/src/cpp/client/cronet_credentials.cc @@ -0,0 +1,69 @@ +/* + * + * 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++/security/credentials.h> + +#include <grpc++/channel.h> +#include <grpc++/support/channel_arguments.h> +#include <grpc/grpc_cronet.h> +#include "src/cpp/client/create_channel_internal.h" + +namespace grpc { + +class CronetChannelCredentialsImpl GRPC_FINAL : public ChannelCredentials { + public: + CronetChannelCredentialsImpl(void* engine) : engine_(engine) {} + + std::shared_ptr<grpc::Channel> CreateChannel( + const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE { + grpc_channel_args channel_args; + args.SetChannelArgs(&channel_args); + return CreateChannelInternal( + "", grpc_cronet_secure_channel_create(engine_, target.c_str(), + &channel_args, nullptr)); + } + + SecureChannelCredentials* AsSecureCredentials() GRPC_OVERRIDE { + return nullptr; + } + + private: + void* engine_; +}; + +std::shared_ptr<ChannelCredentials> CronetChannelCredentials(void* engine) { + return std::shared_ptr<ChannelCredentials>( + new CronetChannelCredentialsImpl(engine)); +} + +} // namespace grpc diff --git a/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs b/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs index 96d6ee87ae..c5bca444b4 100644 --- a/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs +++ b/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs @@ -33,6 +33,7 @@ using System; using System.Threading; +using System.Threading.Tasks; using Google.Apis.Auth.OAuth2; using Grpc.Core; @@ -72,9 +73,10 @@ namespace Grpc.Auth public static AsyncAuthInterceptor FromAccessToken(string accessToken) { GrpcPreconditions.CheckNotNull(accessToken); - return new AsyncAuthInterceptor(async (context, metadata) => + return new AsyncAuthInterceptor((context, metadata) => { metadata.Add(CreateBearerTokenHeader(accessToken)); + return TaskUtils.CompletedTask; }); } diff --git a/src/csharp/Grpc.Core/DefaultCallInvoker.cs b/src/csharp/Grpc.Core/DefaultCallInvoker.cs index 1a99e41153..b15aaefcd6 100644 --- a/src/csharp/Grpc.Core/DefaultCallInvoker.cs +++ b/src/csharp/Grpc.Core/DefaultCallInvoker.cs @@ -102,6 +102,7 @@ namespace Grpc.Core return Calls.AsyncDuplexStreamingCall(call); } + /// <summary>Creates call invocation details for given method.</summary> protected virtual CallInvocationDetails<TRequest, TResponse> CreateCall<TRequest, TResponse>(Method<TRequest, TResponse> method, string host, CallOptions options) where TRequest : class where TResponse : class diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 622813fb38..d315e6d667 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -138,6 +138,8 @@ <Compile Include="Internal\CallError.cs" /> <Compile Include="Logging\LogLevel.cs" /> <Compile Include="Logging\LogLevelFilterLogger.cs" /> + <Compile Include="Internal\RequestCallContextSafeHandle.cs" /> + <Compile Include="Utils\TaskUtils.cs" /> </ItemGroup> <ItemGroup> <None Include="Grpc.Core.nuspec" /> diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs index 3ed2df203d..c57c904eb9 100644 --- a/src/csharp/Grpc.Core/GrpcEnvironment.cs +++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs @@ -59,7 +59,6 @@ namespace Grpc.Core static ILogger logger = new NullLogger(); - readonly object myLock = new object(); readonly GrpcThreadPool threadPool; readonly DebugStats debugStats = new DebugStats(); readonly AtomicCounter cqPickerCounter = new AtomicCounter(); diff --git a/src/csharp/Grpc.Core/Internal/AsyncCall.cs b/src/csharp/Grpc.Core/Internal/AsyncCall.cs index 9abaf1120f..49d0a111ef 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCall.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCall.cs @@ -265,7 +265,7 @@ namespace Grpc.Core.Internal // the halfclose has already been done implicitly, so just return // completed task here. halfcloseRequested = true; - return Task.FromResult<object>(null); + return TaskUtils.CompletedTask; } call.StartSendCloseFromClient(HandleSendFinished); diff --git a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs index c28a6f64d3..26449ee539 100644 --- a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs @@ -93,21 +93,6 @@ namespace Grpc.Core.Internal return data; } - // Gets data of server_rpc_new completion. - public ServerRpcNew GetServerRpcNew(Server server) - { - var call = Native.grpcsharp_batch_context_server_rpc_new_call(this); - - var method = Marshal.PtrToStringAnsi(Native.grpcsharp_batch_context_server_rpc_new_method(this)); - var host = Marshal.PtrToStringAnsi(Native.grpcsharp_batch_context_server_rpc_new_host(this)); - var deadline = Native.grpcsharp_batch_context_server_rpc_new_deadline(this); - - IntPtr metadataArrayPtr = Native.grpcsharp_batch_context_server_rpc_new_request_metadata(this); - var metadata = MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr); - - return new ServerRpcNew(server, call, method, host, deadline, metadata); - } - // Gets data of receive_close_on_server completion. public bool GetReceivedCloseOnServerCancelled() { diff --git a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs index 628844f242..7e2f0e9c6c 100644 --- a/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs +++ b/src/csharp/Grpc.Core/Internal/CompletionRegistry.cs @@ -44,6 +44,8 @@ namespace Grpc.Core.Internal internal delegate void BatchCompletionDelegate(bool success, BatchContextSafeHandle ctx); + internal delegate void RequestCallCompletionDelegate(bool success, RequestCallContextSafeHandle ctx); + internal class CompletionRegistry { static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<CompletionRegistry>(); @@ -68,6 +70,12 @@ namespace Grpc.Core.Internal Register(ctx.Handle, opCallback); } + public void RegisterRequestCallCompletion(RequestCallContextSafeHandle ctx, RequestCallCompletionDelegate callback) + { + OpCompletionDelegate opCallback = ((success) => HandleRequestCallCompletion(success, ctx, callback)); + Register(ctx.Handle, opCallback); + } + public OpCompletionDelegate Extract(IntPtr key) { OpCompletionDelegate value; @@ -84,7 +92,26 @@ namespace Grpc.Core.Internal } catch (Exception e) { - Logger.Error(e, "Exception occured while invoking completion delegate."); + Logger.Error(e, "Exception occured while invoking batch completion delegate."); + } + finally + { + if (ctx != null) + { + ctx.Dispose(); + } + } + } + + private static void HandleRequestCallCompletion(bool success, RequestCallContextSafeHandle ctx, RequestCallCompletionDelegate callback) + { + try + { + callback(success, ctx); + } + catch (Exception e) + { + Logger.Error(e, "Exception occured while invoking request call completion delegate."); } finally { diff --git a/src/csharp/Grpc.Core/Internal/InterceptingCallInvoker.cs b/src/csharp/Grpc.Core/Internal/InterceptingCallInvoker.cs index ef48dc7121..0c63e2092a 100644 --- a/src/csharp/Grpc.Core/Internal/InterceptingCallInvoker.cs +++ b/src/csharp/Grpc.Core/Internal/InterceptingCallInvoker.cs @@ -48,7 +48,7 @@ namespace Grpc.Core.Internal readonly Func<CallOptions, CallOptions> callOptionsInterceptor; /// <summary> - /// Initializes a new instance of the <see cref="Grpc.Core.InterceptingCallInvoker"/> class. + /// Initializes a new instance of the <see cref="Grpc.Core.Internal.InterceptingCallInvoker"/> class. /// </summary> public InterceptingCallInvoker(CallInvoker callInvoker, Func<string, string> hostInterceptor = null, diff --git a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs index 26af6311d5..b3714481eb 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs @@ -78,7 +78,10 @@ namespace Grpc.Core.Internal { var context = new AuthInterceptorContext(Marshal.PtrToStringAnsi(serviceUrlPtr), Marshal.PtrToStringAnsi(methodNamePtr)); - StartGetMetadata(context, callbackPtr, userDataPtr); + // Don't await, we are in a native callback and need to return. + #pragma warning disable 4014 + GetMetadataAsync(context, callbackPtr, userDataPtr); + #pragma warning restore 4014 } catch (Exception e) { @@ -87,7 +90,7 @@ namespace Grpc.Core.Internal } } - private async Task StartGetMetadata(AuthInterceptorContext context, IntPtr callbackPtr, IntPtr userDataPtr) + private async Task GetMetadataAsync(AuthInterceptorContext context, IntPtr callbackPtr, IntPtr userDataPtr) { try { diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.cs index f457c9dbf1..40ba7e30cb 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMethods.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMethods.cs @@ -64,14 +64,17 @@ namespace Grpc.Core.Internal public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate grpcsharp_batch_context_recv_status_on_client_status; public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate grpcsharp_batch_context_recv_status_on_client_details; public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate grpcsharp_batch_context_recv_status_on_client_trailing_metadata; - public readonly Delegates.grpcsharp_batch_context_server_rpc_new_call_delegate grpcsharp_batch_context_server_rpc_new_call; - public readonly Delegates.grpcsharp_batch_context_server_rpc_new_method_delegate grpcsharp_batch_context_server_rpc_new_method; - public readonly Delegates.grpcsharp_batch_context_server_rpc_new_host_delegate grpcsharp_batch_context_server_rpc_new_host; - public readonly Delegates.grpcsharp_batch_context_server_rpc_new_deadline_delegate grpcsharp_batch_context_server_rpc_new_deadline; - public readonly Delegates.grpcsharp_batch_context_server_rpc_new_request_metadata_delegate grpcsharp_batch_context_server_rpc_new_request_metadata; public readonly Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate grpcsharp_batch_context_recv_close_on_server_cancelled; public readonly Delegates.grpcsharp_batch_context_destroy_delegate grpcsharp_batch_context_destroy; + public readonly Delegates.grpcsharp_request_call_context_create_delegate grpcsharp_request_call_context_create; + public readonly Delegates.grpcsharp_request_call_context_call_delegate grpcsharp_request_call_context_call; + public readonly Delegates.grpcsharp_request_call_context_method_delegate grpcsharp_request_call_context_method; + public readonly Delegates.grpcsharp_request_call_context_host_delegate grpcsharp_request_call_context_host; + public readonly Delegates.grpcsharp_request_call_context_deadline_delegate grpcsharp_request_call_context_deadline; + public readonly Delegates.grpcsharp_request_call_context_request_metadata_delegate grpcsharp_request_call_context_request_metadata; + public readonly Delegates.grpcsharp_request_call_context_destroy_delegate grpcsharp_request_call_context_destroy; + public readonly Delegates.grpcsharp_composite_call_credentials_create_delegate grpcsharp_composite_call_credentials_create; public readonly Delegates.grpcsharp_call_credentials_release_delegate grpcsharp_call_credentials_release; @@ -170,14 +173,17 @@ namespace Grpc.Core.Internal this.grpcsharp_batch_context_recv_status_on_client_status = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate>(library); this.grpcsharp_batch_context_recv_status_on_client_details = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate>(library); this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate>(library); - this.grpcsharp_batch_context_server_rpc_new_call = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_call_delegate>(library); - this.grpcsharp_batch_context_server_rpc_new_method = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_method_delegate>(library); - this.grpcsharp_batch_context_server_rpc_new_host = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_host_delegate>(library); - this.grpcsharp_batch_context_server_rpc_new_deadline = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_deadline_delegate>(library); - this.grpcsharp_batch_context_server_rpc_new_request_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_request_metadata_delegate>(library); this.grpcsharp_batch_context_recv_close_on_server_cancelled = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate>(library); this.grpcsharp_batch_context_destroy = GetMethodDelegate<Delegates.grpcsharp_batch_context_destroy_delegate>(library); + this.grpcsharp_request_call_context_create = GetMethodDelegate<Delegates.grpcsharp_request_call_context_create_delegate>(library); + this.grpcsharp_request_call_context_call = GetMethodDelegate<Delegates.grpcsharp_request_call_context_call_delegate>(library); + this.grpcsharp_request_call_context_method = GetMethodDelegate<Delegates.grpcsharp_request_call_context_method_delegate>(library); + this.grpcsharp_request_call_context_host = GetMethodDelegate<Delegates.grpcsharp_request_call_context_host_delegate>(library); + this.grpcsharp_request_call_context_deadline = GetMethodDelegate<Delegates.grpcsharp_request_call_context_deadline_delegate>(library); + this.grpcsharp_request_call_context_request_metadata = GetMethodDelegate<Delegates.grpcsharp_request_call_context_request_metadata_delegate>(library); + this.grpcsharp_request_call_context_destroy = GetMethodDelegate<Delegates.grpcsharp_request_call_context_destroy_delegate>(library); + this.grpcsharp_composite_call_credentials_create = GetMethodDelegate<Delegates.grpcsharp_composite_call_credentials_create_delegate>(library); this.grpcsharp_call_credentials_release = GetMethodDelegate<Delegates.grpcsharp_call_credentials_release_delegate>(library); @@ -302,14 +308,17 @@ namespace Grpc.Core.Internal public delegate StatusCode grpcsharp_batch_context_recv_status_on_client_status_delegate(BatchContextSafeHandle ctx); public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx); // returns const char* public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate(BatchContextSafeHandle ctx); - public delegate CallSafeHandle grpcsharp_batch_context_server_rpc_new_call_delegate(BatchContextSafeHandle ctx); - public delegate IntPtr grpcsharp_batch_context_server_rpc_new_method_delegate(BatchContextSafeHandle ctx); // returns const char* - public delegate IntPtr grpcsharp_batch_context_server_rpc_new_host_delegate(BatchContextSafeHandle ctx); // returns const char* - public delegate Timespec grpcsharp_batch_context_server_rpc_new_deadline_delegate(BatchContextSafeHandle ctx); - public delegate IntPtr grpcsharp_batch_context_server_rpc_new_request_metadata_delegate(BatchContextSafeHandle ctx); public delegate int grpcsharp_batch_context_recv_close_on_server_cancelled_delegate(BatchContextSafeHandle ctx); public delegate void grpcsharp_batch_context_destroy_delegate(IntPtr ctx); + public delegate RequestCallContextSafeHandle grpcsharp_request_call_context_create_delegate(); + public delegate CallSafeHandle grpcsharp_request_call_context_call_delegate(RequestCallContextSafeHandle ctx); + public delegate IntPtr grpcsharp_request_call_context_method_delegate(RequestCallContextSafeHandle ctx); // returns const char* + public delegate IntPtr grpcsharp_request_call_context_host_delegate(RequestCallContextSafeHandle ctx); // returns const char* + public delegate Timespec grpcsharp_request_call_context_deadline_delegate(RequestCallContextSafeHandle ctx); + public delegate IntPtr grpcsharp_request_call_context_request_metadata_delegate(RequestCallContextSafeHandle ctx); + public delegate void grpcsharp_request_call_context_destroy_delegate(IntPtr ctx); + public delegate CallCredentialsSafeHandle grpcsharp_composite_call_credentials_create_delegate(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2); public delegate void grpcsharp_call_credentials_release_delegate(IntPtr credentials); @@ -393,7 +402,7 @@ namespace Grpc.Core.Internal public delegate int grpcsharp_server_add_insecure_http2_port_delegate(ServerSafeHandle server, string addr); public delegate int grpcsharp_server_add_secure_http2_port_delegate(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds); public delegate void grpcsharp_server_start_delegate(ServerSafeHandle server); - public delegate CallError grpcsharp_server_request_call_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); + public delegate CallError grpcsharp_server_request_call_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, RequestCallContextSafeHandle ctx); public delegate void grpcsharp_server_cancel_all_calls_delegate(ServerSafeHandle server); public delegate void grpcsharp_server_shutdown_and_notify_callback_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx); public delegate void grpcsharp_server_destroy_delegate(IntPtr server); diff --git a/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs new file mode 100644 index 0000000000..ea7819d7b1 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/RequestCallContextSafeHandle.cs @@ -0,0 +1,85 @@ +#region Copyright notice and license + +// 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. + +#endregion + +using System; +using System.Runtime.InteropServices; +using Grpc.Core; + +namespace Grpc.Core.Internal +{ + /// <summary> + /// grpcsharp_request_call_context + /// </summary> + internal class RequestCallContextSafeHandle : SafeHandleZeroIsInvalid + { + static readonly NativeMethods Native = NativeMethods.Get(); + + private RequestCallContextSafeHandle() + { + } + + public static RequestCallContextSafeHandle Create() + { + return Native.grpcsharp_request_call_context_create(); + } + + public IntPtr Handle + { + get + { + return handle; + } + } + + // Gets data of server_rpc_new completion. + public ServerRpcNew GetServerRpcNew(Server server) + { + var call = Native.grpcsharp_request_call_context_call(this); + + var method = Marshal.PtrToStringAnsi(Native.grpcsharp_request_call_context_method(this)); + var host = Marshal.PtrToStringAnsi(Native.grpcsharp_request_call_context_host(this)); + var deadline = Native.grpcsharp_request_call_context_deadline(this); + + IntPtr metadataArrayPtr = Native.grpcsharp_request_call_context_request_metadata(this); + var metadata = MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr); + + return new ServerRpcNew(server, call, method, host, deadline, metadata); + } + + protected override bool ReleaseHandle() + { + Native.grpcsharp_request_call_context_destroy(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs b/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs index 230faacff6..a637a54f58 100644 --- a/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs +++ b/src/csharp/Grpc.Core/Internal/SafeHandleZeroIsInvalid.cs @@ -45,10 +45,6 @@ namespace Grpc.Core.Internal { } - public SafeHandleZeroIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) - { - } - public override bool IsInvalid { get @@ -56,11 +52,5 @@ namespace Grpc.Core.Internal return handle == IntPtr.Zero; } } - - protected override bool ReleaseHandle() - { - // handle is not owned. - return true; - } } } diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs index ebb7e0021e..600811ddd6 100644 --- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs @@ -76,7 +76,7 @@ namespace Grpc.Core.Internal Status status; Tuple<TResponse,WriteFlags> responseTuple = null; - var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); + var context = HandlerUtils.NewContext(newRpc, responseStream, asyncCall.CancellationToken); try { GrpcPreconditions.CheckArgument(await requestStream.MoveNext().ConfigureAwait(false)); @@ -134,7 +134,7 @@ namespace Grpc.Core.Internal var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall); Status status; - var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); + var context = HandlerUtils.NewContext(newRpc, responseStream, asyncCall.CancellationToken); try { GrpcPreconditions.CheckArgument(await requestStream.MoveNext().ConfigureAwait(false)); @@ -193,7 +193,7 @@ namespace Grpc.Core.Internal Status status; Tuple<TResponse,WriteFlags> responseTuple = null; - var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); + var context = HandlerUtils.NewContext(newRpc, responseStream, asyncCall.CancellationToken); try { var response = await handler(requestStream, context).ConfigureAwait(false); @@ -250,7 +250,7 @@ namespace Grpc.Core.Internal var responseStream = new ServerResponseStream<TRequest, TResponse>(asyncCall); Status status; - var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); + var context = HandlerUtils.NewContext(newRpc, responseStream, asyncCall.CancellationToken); try { await handler(requestStream, responseStream, context).ConfigureAwait(false); @@ -296,7 +296,7 @@ namespace Grpc.Core.Internal private Task UnimplementedMethod(IAsyncStreamReader<byte[]> requestStream, IServerStreamWriter<byte[]> responseStream, ServerCallContext ctx) { ctx.Status = new Status(StatusCode.Unimplemented, ""); - return Task.FromResult<object>(null); + return TaskUtils.CompletedTask; } public Task HandleCall(ServerRpcNew newRpc, CompletionQueueSafeHandle cq) @@ -324,13 +324,13 @@ namespace Grpc.Core.Internal return writeOptions != null ? writeOptions.Flags : default(WriteFlags); } - public static ServerCallContext NewContext<TRequest, TResponse>(ServerRpcNew newRpc, string peer, ServerResponseStream<TRequest, TResponse> serverResponseStream, CancellationToken cancellationToken) + public static ServerCallContext NewContext<TRequest, TResponse>(ServerRpcNew newRpc, ServerResponseStream<TRequest, TResponse> serverResponseStream, CancellationToken cancellationToken) where TRequest : class where TResponse : class { DateTime realtimeDeadline = newRpc.Deadline.ToClockType(ClockType.Realtime).ToDateTime(); - return new ServerCallContext(newRpc.Call, newRpc.Method, newRpc.Host, peer, realtimeDeadline, + return new ServerCallContext(newRpc.Call, newRpc.Method, newRpc.Host, realtimeDeadline, newRpc.RequestMetadata, cancellationToken, serverResponseStream.WriteResponseHeadersAsync, serverResponseStream); } } diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs index 014a8db78f..7d7b838616 100644 --- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs @@ -85,12 +85,12 @@ namespace Grpc.Core.Internal } } - public void RequestCall(BatchCompletionDelegate callback, CompletionQueueSafeHandle completionQueue) + public void RequestCall(RequestCallCompletionDelegate callback, CompletionQueueSafeHandle completionQueue) { using (completionQueue.NewScope()) { - var ctx = BatchContextSafeHandle.Create(); - completionQueue.CompletionRegistry.RegisterBatchCompletion(ctx, callback); + var ctx = RequestCallContextSafeHandle.Create(); + completionQueue.CompletionRegistry.RegisterRequestCallCompletion(ctx, callback); Native.grpcsharp_server_request_call(this, completionQueue, ctx).CheckOk(); } } diff --git a/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs index 31e1402849..1553bdd687 100644 --- a/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs +++ b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs @@ -134,7 +134,11 @@ namespace Grpc.Core.Internal { throw new MissingMethodException(string.Format("The native method \"{0}\" does not exist", methodName)); } - return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T; +#if NETSTANDARD1_5 + return Marshal.GetDelegateForFunctionPointer<T>(ptr); // non-generic version is obsolete +#else + return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T; // generic version not available in .NET45 +#endif } /// <summary> diff --git a/src/csharp/Grpc.Core/Metadata.cs b/src/csharp/Grpc.Core/Metadata.cs index 915bec146c..6fc715d6ee 100644 --- a/src/csharp/Grpc.Core/Metadata.cs +++ b/src/csharp/Grpc.Core/Metadata.cs @@ -95,11 +95,18 @@ namespace Grpc.Core #region IList members + + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public int IndexOf(Metadata.Entry item) { return entries.IndexOf(item); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public void Insert(int index, Metadata.Entry item) { GrpcPreconditions.CheckNotNull(item); @@ -107,12 +114,18 @@ namespace Grpc.Core entries.Insert(index, item); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public void RemoveAt(int index) { CheckWriteable(); entries.RemoveAt(index); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public Metadata.Entry this[int index] { get @@ -128,6 +141,9 @@ namespace Grpc.Core } } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public void Add(Metadata.Entry item) { GrpcPreconditions.CheckNotNull(item); @@ -135,48 +151,75 @@ namespace Grpc.Core entries.Add(item); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public void Add(string key, string value) { Add(new Entry(key, value)); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public void Add(string key, byte[] valueBytes) { Add(new Entry(key, valueBytes)); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public void Clear() { CheckWriteable(); entries.Clear(); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public bool Contains(Metadata.Entry item) { return entries.Contains(item); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public void CopyTo(Metadata.Entry[] array, int arrayIndex) { entries.CopyTo(array, arrayIndex); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public int Count { get { return entries.Count; } } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public bool IsReadOnly { get { return readOnly; } } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public bool Remove(Metadata.Entry item) { CheckWriteable(); return entries.Remove(item); } + /// <summary> + /// <see cref="T:IList`1"/> + /// </summary> public IEnumerator<Metadata.Entry> GetEnumerator() { return entries.GetEnumerator(); @@ -221,7 +264,7 @@ namespace Grpc.Core public Entry(string key, byte[] valueBytes) { this.key = NormalizeKey(key); - GrpcPreconditions.CheckArgument(this.key.EndsWith(BinaryHeaderSuffix), + GrpcPreconditions.CheckArgument(HasBinaryHeaderSuffix(this.key), "Key for binary valued metadata entry needs to have suffix indicating binary value."); this.value = null; GrpcPreconditions.CheckNotNull(valueBytes, "valueBytes"); @@ -237,7 +280,7 @@ namespace Grpc.Core public Entry(string key, string value) { this.key = NormalizeKey(key); - GrpcPreconditions.CheckArgument(!this.key.EndsWith(BinaryHeaderSuffix), + GrpcPreconditions.CheckArgument(!HasBinaryHeaderSuffix(this.key), "Key for ASCII valued metadata entry cannot have suffix indicating binary value."); this.value = GrpcPreconditions.CheckNotNull(value, "value"); this.valueBytes = null; @@ -324,7 +367,7 @@ namespace Grpc.Core /// </summary> internal static Entry CreateUnsafe(string key, byte[] valueBytes) { - if (key.EndsWith(BinaryHeaderSuffix)) + if (HasBinaryHeaderSuffix(key)) { return new Entry(key, null, valueBytes); } @@ -338,6 +381,27 @@ namespace Grpc.Core "Metadata entry key not valid. Keys can only contain lowercase alphanumeric characters, underscores and hyphens."); return normalized; } + + /// <summary> + /// Returns <c>true</c> if the key has "-bin" binary header suffix. + /// </summary> + private static bool HasBinaryHeaderSuffix(string key) + { + // We don't use just string.EndsWith because its implementation is extremely slow + // on CoreCLR and we've seen significant differences in gRPC benchmarks caused by it. + // See https://github.com/dotnet/coreclr/issues/5612 + + int len = key.Length; + if (len >= 4 && + key[len - 4] == '-' && + key[len - 3] == 'b' && + key[len - 2] == 'i' && + key[len - 1] == 'n') + { + return true; + } + return false; + } } } } diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index dd4a405ed9..63c1d9cd00 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -47,7 +47,7 @@ namespace Grpc.Core /// </summary> public class Server { - const int InitialAllowRpcTokenCountPerCq = 10; + const int DefaultRequestCallTokensPerCq = 2000; static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<Server>(); readonly AtomicCounter activeCallCounter = new AtomicCounter(); @@ -66,7 +66,7 @@ namespace Grpc.Core bool startRequested; volatile bool shutdownRequested; - + int requestCallTokensPerCq = DefaultRequestCallTokensPerCq; /// <summary> /// Creates a new server. @@ -133,6 +133,27 @@ namespace Grpc.Core } /// <summary> + /// Experimental API. Might anytime change without prior notice. + /// Number or calls requested via grpc_server_request_call at any given time for each completion queue. + /// </summary> + public int RequestCallTokensPerCompletionQueue + { + get + { + return requestCallTokensPerCq; + } + set + { + lock (myLock) + { + GrpcPreconditions.CheckState(!startRequested); + GrpcPreconditions.CheckArgument(value > 0); + requestCallTokensPerCq = value; + } + } + } + + /// <summary> /// Starts the server. /// </summary> public void Start() @@ -145,9 +166,7 @@ namespace Grpc.Core handle.Start(); - // Starting with more than one AllowOneRpc tokens can significantly increase - // unary RPC throughput. - for (int i = 0; i < InitialAllowRpcTokenCountPerCq; i++) + for (int i = 0; i < requestCallTokensPerCq; i++) { foreach (var cq in environment.CompletionQueues) { @@ -310,7 +329,7 @@ namespace Grpc.Core /// <summary> /// Selects corresponding handler for given call and handles the call. /// </summary> - private async Task HandleCallAsync(ServerRpcNew newRpc, CompletionQueueSafeHandle cq) + private async Task HandleCallAsync(ServerRpcNew newRpc, CompletionQueueSafeHandle cq, Action continuation) { try { @@ -325,25 +344,40 @@ namespace Grpc.Core { Logger.Warning(e, "Exception while handling RPC."); } + finally + { + continuation(); + } } /// <summary> /// Handles the native callback. /// </summary> - private void HandleNewServerRpc(bool success, BatchContextSafeHandle ctx, CompletionQueueSafeHandle cq) + private void HandleNewServerRpc(bool success, RequestCallContextSafeHandle ctx, CompletionQueueSafeHandle cq) { - Task.Run(() => AllowOneRpc(cq)); - + bool nextRpcRequested = false; if (success) { - ServerRpcNew newRpc = ctx.GetServerRpcNew(this); + var newRpc = ctx.GetServerRpcNew(this); // after server shutdown, the callback returns with null call if (!newRpc.Call.IsInvalid) { - HandleCallAsync(newRpc, cq); // we don't need to await. + nextRpcRequested = true; + + // Start asynchronous handler for the call. + // Don't await, the continuations will run on gRPC thread pool once triggered + // by cq.Next(). + #pragma warning disable 4014 + HandleCallAsync(newRpc, cq, () => AllowOneRpc(cq)); + #pragma warning restore 4014 } } + + if (!nextRpcRequested) + { + AllowOneRpc(cq); + } } /// <summary> diff --git a/src/csharp/Grpc.Core/ServerCallContext.cs b/src/csharp/Grpc.Core/ServerCallContext.cs index 09a6b882a6..8f28fbc045 100644 --- a/src/csharp/Grpc.Core/ServerCallContext.cs +++ b/src/csharp/Grpc.Core/ServerCallContext.cs @@ -48,7 +48,6 @@ namespace Grpc.Core private readonly CallSafeHandle callHandle; private readonly string method; private readonly string host; - private readonly string peer; private readonly DateTime deadline; private readonly Metadata requestHeaders; private readonly CancellationToken cancellationToken; @@ -58,13 +57,12 @@ namespace Grpc.Core private Func<Metadata, Task> writeHeadersFunc; private IHasWriteOptions writeOptionsHolder; - internal ServerCallContext(CallSafeHandle callHandle, string method, string host, string peer, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken, + internal ServerCallContext(CallSafeHandle callHandle, string method, string host, DateTime deadline, Metadata requestHeaders, CancellationToken cancellationToken, Func<Metadata, Task> writeHeadersFunc, IHasWriteOptions writeOptionsHolder) { this.callHandle = callHandle; this.method = method; this.host = host; - this.peer = peer; this.deadline = deadline; this.requestHeaders = requestHeaders; this.cancellationToken = cancellationToken; @@ -115,7 +113,10 @@ namespace Grpc.Core { get { - return this.peer; + // Getting the peer lazily is fine as the native call is guaranteed + // not to be disposed before user-supplied server side handler returns. + // Most users won't need to read this field anyway. + return this.callHandle.GetPeer(); } } diff --git a/src/csharp/Grpc.Core/Utils/TaskUtils.cs b/src/csharp/Grpc.Core/Utils/TaskUtils.cs new file mode 100644 index 0000000000..2cf1144143 --- /dev/null +++ b/src/csharp/Grpc.Core/Utils/TaskUtils.cs @@ -0,0 +1,59 @@ +#region Copyright notice and license + +// 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. + +#endregion + +using System; +using System.Threading.Tasks; + +namespace Grpc.Core.Utils +{ + /// <summary> + /// Utility methods for task parallel library. + /// </summary> + public static class TaskUtils + { + /// <summary> + /// Framework independent equivalent of <c>Task.CompletedTask</c>. + /// </summary> + public static Task CompletedTask + { + get + { +#if NETSTANDARD1_5 + return Task.CompletedTask; +#else + return Task.FromResult<object>(null); // for .NET45, emulate the functionality +#endif + } + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json b/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json index 1b900c8af3..fe200f8d44 100644 --- a/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json +++ b/src/csharp/Grpc.IntegrationTesting.QpsWorker/project.json @@ -67,5 +67,10 @@ } } } + }, + "runtimeOptions": { + "configProperties": { + "System.GC.Server": true + } } } diff --git a/src/csharp/Grpc.IntegrationTesting/InterarrivalTimers.cs b/src/csharp/Grpc.IntegrationTesting/InterarrivalTimers.cs index 6492d34890..8bea083bc2 100644 --- a/src/csharp/Grpc.IntegrationTesting/InterarrivalTimers.cs +++ b/src/csharp/Grpc.IntegrationTesting/InterarrivalTimers.cs @@ -69,7 +69,7 @@ namespace Grpc.IntegrationTesting public Task WaitForNextAsync() { - return Task.FromResult<object>(null); + return TaskUtils.CompletedTask; } } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index cb926328e0..5ba83b143e 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -195,8 +195,11 @@ namespace Grpc.IntegrationTesting case "status_code_and_message": await RunStatusCodeAndMessageAsync(client); break; + case "unimplemented_service": + RunUnimplementedService(new UnimplementedService.UnimplementedServiceClient(channel)); + break; case "unimplemented_method": - RunUnimplementedMethod(new UnimplementedService.UnimplementedServiceClient(channel)); + RunUnimplementedMethod(client); break; case "client_compressed_unary": RunClientCompressedUnary(client); @@ -577,13 +580,21 @@ namespace Grpc.IntegrationTesting Console.WriteLine("Passed!"); } - public static void RunUnimplementedMethod(UnimplementedService.UnimplementedServiceClient client) + public static void RunUnimplementedService(UnimplementedService.UnimplementedServiceClient client) + { + Console.WriteLine("running unimplemented_service"); + var e = Assert.Throws<RpcException>(() => client.UnimplementedCall(new Empty())); + + Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode); + Console.WriteLine("Passed!"); + } + + public static void RunUnimplementedMethod(TestService.TestServiceClient client) { Console.WriteLine("running unimplemented_method"); var e = Assert.Throws<RpcException>(() => client.UnimplementedCall(new Empty())); Assert.AreEqual(StatusCode.Unimplemented, e.Status.StatusCode); - Assert.AreEqual("", e.Status.Detail); Console.WriteLine("Passed!"); } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index f907f630da..4960a53f92 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -146,9 +146,15 @@ namespace Grpc.IntegrationTesting } [Test] + public void UnimplementedService() + { + InteropClient.RunUnimplementedService(new UnimplementedService.UnimplementedServiceClient(channel)); + } + + [Test] public void UnimplementedMethod() { - InteropClient.RunUnimplementedMethod(new UnimplementedService.UnimplementedServiceClient(channel)); + InteropClient.RunUnimplementedMethod(client); } } } diff --git a/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs b/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs index 865556c242..62a7347d42 100644 --- a/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs +++ b/src/csharp/Grpc.IntegrationTesting/QpsWorker.cs @@ -76,6 +76,11 @@ namespace Grpc.IntegrationTesting private async Task RunAsync() { + // (ThreadPoolSize == ProcessorCount) gives best throughput in benchmarks + // and doesn't seem to harm performance even when server and client + // are running on the same machine. + GrpcEnvironment.SetThreadPoolSize(Environment.ProcessorCount); + string host = "0.0.0.0"; int port = options.DriverPort; diff --git a/src/csharp/Grpc.IntegrationTesting/Test.cs b/src/csharp/Grpc.IntegrationTesting/Test.cs index 88c2b8a921..d2fa9f8013 100644 --- a/src/csharp/Grpc.IntegrationTesting/Test.cs +++ b/src/csharp/Grpc.IntegrationTesting/Test.cs @@ -24,25 +24,28 @@ namespace Grpc.Testing { string.Concat( "CiFzcmMvcHJvdG8vZ3JwYy90ZXN0aW5nL3Rlc3QucHJvdG8SDGdycGMudGVz", "dGluZxoic3JjL3Byb3RvL2dycGMvdGVzdGluZy9lbXB0eS5wcm90bxolc3Jj", - "L3Byb3RvL2dycGMvdGVzdGluZy9tZXNzYWdlcy5wcm90bzK7BAoLVGVzdFNl", + "L3Byb3RvL2dycGMvdGVzdGluZy9tZXNzYWdlcy5wcm90bzLLBQoLVGVzdFNl", "cnZpY2USNQoJRW1wdHlDYWxsEhMuZ3JwYy50ZXN0aW5nLkVtcHR5GhMuZ3Jw", "Yy50ZXN0aW5nLkVtcHR5EkYKCVVuYXJ5Q2FsbBIbLmdycGMudGVzdGluZy5T", - "aW1wbGVSZXF1ZXN0GhwuZ3JwYy50ZXN0aW5nLlNpbXBsZVJlc3BvbnNlEmwK", - "E1N0cmVhbWluZ091dHB1dENhbGwSKC5ncnBjLnRlc3RpbmcuU3RyZWFtaW5n", - "T3V0cHV0Q2FsbFJlcXVlc3QaKS5ncnBjLnRlc3RpbmcuU3RyZWFtaW5nT3V0", - "cHV0Q2FsbFJlc3BvbnNlMAESaQoSU3RyZWFtaW5nSW5wdXRDYWxsEicuZ3Jw", - "Yy50ZXN0aW5nLlN0cmVhbWluZ0lucHV0Q2FsbFJlcXVlc3QaKC5ncnBjLnRl", - "c3RpbmcuU3RyZWFtaW5nSW5wdXRDYWxsUmVzcG9uc2UoARJpCg5GdWxsRHVw", - "bGV4Q2FsbBIoLmdycGMudGVzdGluZy5TdHJlYW1pbmdPdXRwdXRDYWxsUmVx", - "dWVzdBopLmdycGMudGVzdGluZy5TdHJlYW1pbmdPdXRwdXRDYWxsUmVzcG9u", - "c2UoATABEmkKDkhhbGZEdXBsZXhDYWxsEiguZ3JwYy50ZXN0aW5nLlN0cmVh", - "bWluZ091dHB1dENhbGxSZXF1ZXN0GikuZ3JwYy50ZXN0aW5nLlN0cmVhbWlu", - "Z091dHB1dENhbGxSZXNwb25zZSgBMAEyVQoUVW5pbXBsZW1lbnRlZFNlcnZp", - "Y2USPQoRVW5pbXBsZW1lbnRlZENhbGwSEy5ncnBjLnRlc3RpbmcuRW1wdHka", - "Ey5ncnBjLnRlc3RpbmcuRW1wdHkyiQEKEFJlY29ubmVjdFNlcnZpY2USOwoF", - "U3RhcnQSHS5ncnBjLnRlc3RpbmcuUmVjb25uZWN0UGFyYW1zGhMuZ3JwYy50", - "ZXN0aW5nLkVtcHR5EjgKBFN0b3ASEy5ncnBjLnRlc3RpbmcuRW1wdHkaGy5n", - "cnBjLnRlc3RpbmcuUmVjb25uZWN0SW5mb2IGcHJvdG8z")); + "aW1wbGVSZXF1ZXN0GhwuZ3JwYy50ZXN0aW5nLlNpbXBsZVJlc3BvbnNlEk8K", + "EkNhY2hlYWJsZVVuYXJ5Q2FsbBIbLmdycGMudGVzdGluZy5TaW1wbGVSZXF1", + "ZXN0GhwuZ3JwYy50ZXN0aW5nLlNpbXBsZVJlc3BvbnNlEmwKE1N0cmVhbWlu", + "Z091dHB1dENhbGwSKC5ncnBjLnRlc3RpbmcuU3RyZWFtaW5nT3V0cHV0Q2Fs", + "bFJlcXVlc3QaKS5ncnBjLnRlc3RpbmcuU3RyZWFtaW5nT3V0cHV0Q2FsbFJl", + "c3BvbnNlMAESaQoSU3RyZWFtaW5nSW5wdXRDYWxsEicuZ3JwYy50ZXN0aW5n", + "LlN0cmVhbWluZ0lucHV0Q2FsbFJlcXVlc3QaKC5ncnBjLnRlc3RpbmcuU3Ry", + "ZWFtaW5nSW5wdXRDYWxsUmVzcG9uc2UoARJpCg5GdWxsRHVwbGV4Q2FsbBIo", + "LmdycGMudGVzdGluZy5TdHJlYW1pbmdPdXRwdXRDYWxsUmVxdWVzdBopLmdy", + "cGMudGVzdGluZy5TdHJlYW1pbmdPdXRwdXRDYWxsUmVzcG9uc2UoATABEmkK", + "DkhhbGZEdXBsZXhDYWxsEiguZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ091dHB1", + "dENhbGxSZXF1ZXN0GikuZ3JwYy50ZXN0aW5nLlN0cmVhbWluZ091dHB1dENh", + "bGxSZXNwb25zZSgBMAESPQoRVW5pbXBsZW1lbnRlZENhbGwSEy5ncnBjLnRl", + "c3RpbmcuRW1wdHkaEy5ncnBjLnRlc3RpbmcuRW1wdHkyVQoUVW5pbXBsZW1l", + "bnRlZFNlcnZpY2USPQoRVW5pbXBsZW1lbnRlZENhbGwSEy5ncnBjLnRlc3Rp", + "bmcuRW1wdHkaEy5ncnBjLnRlc3RpbmcuRW1wdHkyiQEKEFJlY29ubmVjdFNl", + "cnZpY2USOwoFU3RhcnQSHS5ncnBjLnRlc3RpbmcuUmVjb25uZWN0UGFyYW1z", + "GhMuZ3JwYy50ZXN0aW5nLkVtcHR5EjgKBFN0b3ASEy5ncnBjLnRlc3Rpbmcu", + "RW1wdHkaGy5ncnBjLnRlc3RpbmcuUmVjb25uZWN0SW5mb2IGcHJvdG8z")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { global::Grpc.Testing.EmptyReflection.Descriptor, global::Grpc.Testing.MessagesReflection.Descriptor, }, new pbr::GeneratedClrTypeInfo(null, null)); diff --git a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs index 61f2ed4015..8d649bf5c5 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestGrpc.cs @@ -71,6 +71,13 @@ namespace Grpc.Testing { __Marshaller_SimpleRequest, __Marshaller_SimpleResponse); + static readonly Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse> __Method_CacheableUnaryCall = new Method<global::Grpc.Testing.SimpleRequest, global::Grpc.Testing.SimpleResponse>( + MethodType.Unary, + __ServiceName, + "CacheableUnaryCall", + __Marshaller_SimpleRequest, + __Marshaller_SimpleResponse); + static readonly Method<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse> __Method_StreamingOutputCall = new Method<global::Grpc.Testing.StreamingOutputCallRequest, global::Grpc.Testing.StreamingOutputCallResponse>( MethodType.ServerStreaming, __ServiceName, @@ -99,6 +106,13 @@ namespace Grpc.Testing { __Marshaller_StreamingOutputCallRequest, __Marshaller_StreamingOutputCallResponse); + static readonly Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty> __Method_UnimplementedCall = new Method<global::Grpc.Testing.Empty, global::Grpc.Testing.Empty>( + MethodType.Unary, + __ServiceName, + "UnimplementedCall", + __Marshaller_Empty, + __Marshaller_Empty); + /// <summary>Service descriptor</summary> public static global::Google.Protobuf.Reflection.ServiceDescriptor Descriptor { @@ -125,6 +139,16 @@ namespace Grpc.Testing { } /// <summary> + /// One request followed by one response. Response has cache control + /// headers set such that a caching HTTP proxy (such as GFE) can + /// satisfy subsequent requests. + /// </summary> + public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.SimpleResponse> CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, ServerCallContext context) + { + throw new RpcException(new Status(StatusCode.Unimplemented, "")); + } + + /// <summary> /// One request followed by a sequence of responses (streamed download). /// The server returns the payload with client desired type and sizes. /// </summary> @@ -163,6 +187,15 @@ namespace Grpc.Testing { throw new RpcException(new Status(StatusCode.Unimplemented, "")); } + /// <summary> + /// The test server will not implement this method. It will be used + /// to test the behavior when clients call unimplemented methods. + /// </summary> + public virtual global::System.Threading.Tasks.Task<global::Grpc.Testing.Empty> UnimplementedCall(global::Grpc.Testing.Empty request, ServerCallContext context) + { + throw new RpcException(new Status(StatusCode.Unimplemented, "")); + } + } /// <summary>Client for TestService</summary> @@ -245,6 +278,42 @@ namespace Grpc.Testing { return CallInvoker.AsyncUnaryCall(__Method_UnaryCall, null, options, request); } /// <summary> + /// One request followed by one response. Response has cache control + /// headers set such that a caching HTTP proxy (such as GFE) can + /// satisfy subsequent requests. + /// </summary> + public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + return CacheableUnaryCall(request, new CallOptions(headers, deadline, cancellationToken)); + } + /// <summary> + /// One request followed by one response. Response has cache control + /// headers set such that a caching HTTP proxy (such as GFE) can + /// satisfy subsequent requests. + /// </summary> + public virtual global::Grpc.Testing.SimpleResponse CacheableUnaryCall(global::Grpc.Testing.SimpleRequest request, CallOptions options) + { + return CallInvoker.BlockingUnaryCall(__Method_CacheableUnaryCall, null, options, request); + } + /// <summary> + /// One request followed by one response. Response has cache control + /// headers set such that a caching HTTP proxy (such as GFE) can + /// satisfy subsequent requests. + /// </summary> + public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + return CacheableUnaryCallAsync(request, new CallOptions(headers, deadline, cancellationToken)); + } + /// <summary> + /// One request followed by one response. Response has cache control + /// headers set such that a caching HTTP proxy (such as GFE) can + /// satisfy subsequent requests. + /// </summary> + public virtual AsyncUnaryCall<global::Grpc.Testing.SimpleResponse> CacheableUnaryCallAsync(global::Grpc.Testing.SimpleRequest request, CallOptions options) + { + return CallInvoker.AsyncUnaryCall(__Method_CacheableUnaryCall, null, options, request); + } + /// <summary> /// One request followed by a sequence of responses (streamed download). /// The server returns the payload with client desired type and sizes. /// </summary> @@ -314,6 +383,38 @@ namespace Grpc.Testing { { return CallInvoker.AsyncDuplexStreamingCall(__Method_HalfDuplexCall, null, options); } + /// <summary> + /// The test server will not implement this method. It will be used + /// to test the behavior when clients call unimplemented methods. + /// </summary> + public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + return UnimplementedCall(request, new CallOptions(headers, deadline, cancellationToken)); + } + /// <summary> + /// The test server will not implement this method. It will be used + /// to test the behavior when clients call unimplemented methods. + /// </summary> + public virtual global::Grpc.Testing.Empty UnimplementedCall(global::Grpc.Testing.Empty request, CallOptions options) + { + return CallInvoker.BlockingUnaryCall(__Method_UnimplementedCall, null, options, request); + } + /// <summary> + /// The test server will not implement this method. It will be used + /// to test the behavior when clients call unimplemented methods. + /// </summary> + public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken)) + { + return UnimplementedCallAsync(request, new CallOptions(headers, deadline, cancellationToken)); + } + /// <summary> + /// The test server will not implement this method. It will be used + /// to test the behavior when clients call unimplemented methods. + /// </summary> + public virtual AsyncUnaryCall<global::Grpc.Testing.Empty> UnimplementedCallAsync(global::Grpc.Testing.Empty request, CallOptions options) + { + return CallInvoker.AsyncUnaryCall(__Method_UnimplementedCall, null, options, request); + } /// <summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary> protected override TestServiceClient NewInstance(ClientBaseConfiguration configuration) { @@ -327,10 +428,12 @@ namespace Grpc.Testing { return ServerServiceDefinition.CreateBuilder() .AddMethod(__Method_EmptyCall, serviceImpl.EmptyCall) .AddMethod(__Method_UnaryCall, serviceImpl.UnaryCall) + .AddMethod(__Method_CacheableUnaryCall, serviceImpl.CacheableUnaryCall) .AddMethod(__Method_StreamingOutputCall, serviceImpl.StreamingOutputCall) .AddMethod(__Method_StreamingInputCall, serviceImpl.StreamingInputCall) .AddMethod(__Method_FullDuplexCall, serviceImpl.FullDuplexCall) - .AddMethod(__Method_HalfDuplexCall, serviceImpl.HalfDuplexCall).Build(); + .AddMethod(__Method_HalfDuplexCall, serviceImpl.HalfDuplexCall) + .AddMethod(__Method_UnimplementedCall, serviceImpl.UnimplementedCall).Build(); } } diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 068bf709b8..9a5d7869d3 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -84,11 +84,6 @@ typedef struct grpcsharp_batch_context { size_t status_details_capacity; } recv_status_on_client; int recv_close_on_server_cancelled; - struct { - grpc_call *call; - grpc_call_details call_details; - grpc_metadata_array request_metadata; - } server_rpc_new; } grpcsharp_batch_context; GPR_EXPORT grpcsharp_batch_context *GPR_CALLTYPE grpcsharp_batch_context_create() { @@ -97,6 +92,18 @@ GPR_EXPORT grpcsharp_batch_context *GPR_CALLTYPE grpcsharp_batch_context_create( return ctx; } +typedef struct { + grpc_call *call; + grpc_call_details call_details; + grpc_metadata_array request_metadata; +} grpcsharp_request_call_context; + +GPR_EXPORT grpcsharp_request_call_context *GPR_CALLTYPE grpcsharp_request_call_context_create() { + grpcsharp_request_call_context *ctx = gpr_malloc(sizeof(grpcsharp_request_call_context)); + memset(ctx, 0, sizeof(grpcsharp_request_call_context)); + return ctx; +} + /* * Destroys array->metadata. * The array pointer itself is not freed. @@ -230,13 +237,20 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_batch_context_destroy(grpcsharp_batch_con &(ctx->recv_status_on_client.trailing_metadata)); gpr_free((void *)ctx->recv_status_on_client.status_details); + gpr_free(ctx); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_request_call_context_destroy(grpcsharp_request_call_context *ctx) { + if (!ctx) { + return; + } /* NOTE: ctx->server_rpc_new.call is not destroyed because callback handler is supposed to take its ownership. */ - grpc_call_details_destroy(&(ctx->server_rpc_new.call_details)); + grpc_call_details_destroy(&(ctx->call_details)); grpcsharp_metadata_array_destroy_metadata_only( - &(ctx->server_rpc_new.request_metadata)); + &(ctx->request_metadata)); gpr_free(ctx); } @@ -303,32 +317,32 @@ grpcsharp_batch_context_recv_status_on_client_trailing_metadata( return &(ctx->recv_status_on_client.trailing_metadata); } -GPR_EXPORT grpc_call *GPR_CALLTYPE grpcsharp_batch_context_server_rpc_new_call( - const grpcsharp_batch_context *ctx) { - return ctx->server_rpc_new.call; +GPR_EXPORT grpc_call *GPR_CALLTYPE grpcsharp_request_call_context_call( + const grpcsharp_request_call_context *ctx) { + return ctx->call; } GPR_EXPORT const char *GPR_CALLTYPE -grpcsharp_batch_context_server_rpc_new_method( - const grpcsharp_batch_context *ctx) { - return ctx->server_rpc_new.call_details.method; +grpcsharp_request_call_context_method( + const grpcsharp_request_call_context *ctx) { + return ctx->call_details.method; } -GPR_EXPORT const char *GPR_CALLTYPE grpcsharp_batch_context_server_rpc_new_host( - const grpcsharp_batch_context *ctx) { - return ctx->server_rpc_new.call_details.host; +GPR_EXPORT const char *GPR_CALLTYPE grpcsharp_request_call_context_host( + const grpcsharp_request_call_context *ctx) { + return ctx->call_details.host; } GPR_EXPORT gpr_timespec GPR_CALLTYPE -grpcsharp_batch_context_server_rpc_new_deadline( - const grpcsharp_batch_context *ctx) { - return ctx->server_rpc_new.call_details.deadline; +grpcsharp_request_call_context_deadline( + const grpcsharp_request_call_context *ctx) { + return ctx->call_details.deadline; } GPR_EXPORT const grpc_metadata_array *GPR_CALLTYPE -grpcsharp_batch_context_server_rpc_new_request_metadata( - const grpcsharp_batch_context *ctx) { - return &(ctx->server_rpc_new.request_metadata); +grpcsharp_request_call_context_request_metadata( + const grpcsharp_request_call_context *ctx) { + return &(ctx->request_metadata); } GPR_EXPORT int32_t GPR_CALLTYPE @@ -853,10 +867,10 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_destroy(grpc_server *server) { GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_server_request_call(grpc_server *server, grpc_completion_queue *cq, - grpcsharp_batch_context *ctx) { + grpcsharp_request_call_context *ctx) { return grpc_server_request_call( - server, &(ctx->server_rpc_new.call), &(ctx->server_rpc_new.call_details), - &(ctx->server_rpc_new.request_metadata), cq, cq, ctx); + server, &(ctx->call), &(ctx->call_details), + &(ctx->request_metadata), cq, cq, ctx); } /* Security */ diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc index 9f023b5883..b48a7bd698 100644 --- a/src/node/ext/call.cc +++ b/src/node/ext/call.cc @@ -45,6 +45,7 @@ #include "byte_buffer.h" #include "call.h" #include "channel.h" +#include "completion_queue.h" #include "completion_queue_async_worker.h" #include "call_credentials.h" #include "timeval.h" @@ -222,6 +223,9 @@ class SendMetadataOp : public Op { out->data.send_initial_metadata.metadata = array.metadata; return true; } + bool IsFinalOp() { + return false; + } protected: std::string GetTypeString() const { return "send_metadata"; @@ -263,6 +267,9 @@ class SendMessageOp : public Op { resources->handles.push_back(unique_ptr<PersistentValue>(handle)); return true; } + bool IsFinalOp() { + return false; + } protected: std::string GetTypeString() const { return "send_message"; @@ -281,6 +288,9 @@ class SendClientCloseOp : public Op { shared_ptr<Resources> resources) { return true; } + bool IsFinalOp() { + return false; + } protected: std::string GetTypeString() const { return "client_close"; @@ -341,6 +351,9 @@ class SendServerStatusOp : public Op { out->data.send_status_from_server.status_details = **str; return true; } + bool IsFinalOp() { + return true; + } protected: std::string GetTypeString() const { return "send_status"; @@ -367,6 +380,9 @@ class GetMetadataOp : public Op { out->data.recv_initial_metadata = &recv_metadata; return true; } + bool IsFinalOp() { + return false; + } protected: std::string GetTypeString() const { @@ -397,6 +413,9 @@ class ReadMessageOp : public Op { out->data.recv_message = &recv_message; return true; } + bool IsFinalOp() { + return false; + } protected: std::string GetTypeString() const { @@ -442,6 +461,9 @@ class ClientStatusOp : public Op { ParseMetadata(&metadata_array)); return scope.Escape(status_obj); } + bool IsFinalOp() { + return true; + } protected: std::string GetTypeString() const { return "status"; @@ -465,6 +487,9 @@ class ServerCloseResponseOp : public Op { out->data.recv_close_on_server.cancelled = &cancelled; return true; } + bool IsFinalOp() { + return false; + } protected: std::string GetTypeString() const { @@ -476,8 +501,8 @@ class ServerCloseResponseOp : public Op { }; tag::tag(Callback *callback, OpVec *ops, - shared_ptr<Resources> resources) : - callback(callback), ops(ops), resources(resources){ + shared_ptr<Resources> resources, Call *call) : + callback(callback), ops(ops), resources(resources), call(call){ } tag::~tag() { @@ -502,16 +527,36 @@ Callback *GetTagCallback(void *tag) { return tag_struct->callback; } +void CompleteTag(void *tag) { + struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); + bool is_final_op = false; + if (tag_struct->call == NULL) { + return; + } + for (vector<unique_ptr<Op> >::iterator it = tag_struct->ops->begin(); + it != tag_struct->ops->end(); ++it) { + Op *op_ptr = it->get(); + if (op_ptr->IsFinalOp()) { + is_final_op = true; + } + } + tag_struct->call->CompleteBatch(is_final_op); +} + void DestroyTag(void *tag) { struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); delete tag_struct; } -Call::Call(grpc_call *call) : wrapped_call(call) { +Call::Call(grpc_call *call) : wrapped_call(call), + pending_batches(0), + has_final_op_completed(false) { } Call::~Call() { - grpc_call_destroy(wrapped_call); + if (wrapped_call != NULL) { + grpc_call_destroy(wrapped_call); + } } void Call::Init(Local<Object> exports) { @@ -552,6 +597,17 @@ Local<Value> Call::WrapStruct(grpc_call *call) { } } +void Call::CompleteBatch(bool is_final_op) { + if (is_final_op) { + this->has_final_op_completed = true; + } + this->pending_batches--; + if (this->has_final_op_completed && this->pending_batches == 0) { + grpc_call_destroy(this->wrapped_call); + this->wrapped_call = NULL; + } +} + NAN_METHOD(Call::New) { if (info.IsConstructCall()) { Call *call; @@ -602,12 +658,12 @@ NAN_METHOD(Call::New) { Utf8String host_override(info[3]); wrapped_call = grpc_channel_create_call( wrapped_channel, parent_call, propagate_flags, - CompletionQueueAsyncWorker::GetQueue(), *method, + GetCompletionQueue(), *method, *host_override, MillisecondsToTimespec(deadline), NULL); } else if (info[3]->IsUndefined() || info[3]->IsNull()) { wrapped_call = grpc_channel_create_call( wrapped_channel, parent_call, propagate_flags, - CompletionQueueAsyncWorker::GetQueue(), *method, + GetCompletionQueue(), *method, NULL, MillisecondsToTimespec(deadline), NULL); } else { return Nan::ThrowTypeError("Call's fourth argument must be a string"); @@ -697,11 +753,12 @@ NAN_METHOD(Call::StartBatch) { Callback *callback = new Callback(callback_func); grpc_call_error error = grpc_call_start_batch( call->wrapped_call, &ops[0], nops, new struct tag( - callback, op_vector.release(), resources), NULL); + callback, op_vector.release(), resources, call), NULL); if (error != GRPC_CALL_OK) { return Nan::ThrowError(nanErrorWithCode("startBatch failed", error)); } - CompletionQueueAsyncWorker::Next(); + call->pending_batches++; + CompletionQueueNext(); } NAN_METHOD(Call::Cancel) { diff --git a/src/node/ext/call.h b/src/node/ext/call.h index 1e3c3ba18d..31c6566d14 100644 --- a/src/node/ext/call.h +++ b/src/node/ext/call.h @@ -66,34 +66,6 @@ bool CreateMetadataArray(v8::Local<v8::Object> metadata, grpc_metadata_array *array, shared_ptr<Resources> resources); -class Op { - public: - virtual v8::Local<v8::Value> GetNodeValue() const = 0; - virtual bool ParseOp(v8::Local<v8::Value> value, grpc_op *out, - shared_ptr<Resources> resources) = 0; - virtual ~Op(); - v8::Local<v8::Value> GetOpType() const; - - protected: - virtual std::string GetTypeString() const = 0; -}; - -typedef std::vector<unique_ptr<Op>> OpVec; -struct tag { - tag(Nan::Callback *callback, OpVec *ops, - shared_ptr<Resources> resources); - ~tag(); - Nan::Callback *callback; - OpVec *ops; - shared_ptr<Resources> resources; -}; - -v8::Local<v8::Value> GetTagNodeValue(void *tag); - -Nan::Callback *GetTagCallback(void *tag); - -void DestroyTag(void *tag); - /* Wrapper class for grpc_call structs. */ class Call : public Nan::ObjectWrap { public: @@ -102,6 +74,8 @@ class Call : public Nan::ObjectWrap { /* Wrap a grpc_call struct in a javascript object */ static v8::Local<v8::Value> WrapStruct(grpc_call *call); + void CompleteBatch(bool is_final_op); + private: explicit Call(grpc_call *call); ~Call(); @@ -121,8 +95,46 @@ class Call : public Nan::ObjectWrap { static Nan::Persistent<v8::FunctionTemplate> fun_tpl; grpc_call *wrapped_call; + // The number of ops that were started but not completed on this call + int pending_batches; + /* Indicates whether the "final" op on a call has completed. For a client + call, this is GRPC_OP_RECV_STATUS_ON_CLIENT and for a server call, this + is GRPC_OP_SEND_STATUS_FROM_SERVER */ + bool has_final_op_completed; }; +class Op { + public: + virtual v8::Local<v8::Value> GetNodeValue() const = 0; + virtual bool ParseOp(v8::Local<v8::Value> value, grpc_op *out, + shared_ptr<Resources> resources) = 0; + virtual ~Op(); + v8::Local<v8::Value> GetOpType() const; + virtual bool IsFinalOp() = 0; + + protected: + virtual std::string GetTypeString() const = 0; +}; + +typedef std::vector<unique_ptr<Op>> OpVec; +struct tag { + tag(Nan::Callback *callback, OpVec *ops, + shared_ptr<Resources> resources, Call *call); + ~tag(); + Nan::Callback *callback; + OpVec *ops; + shared_ptr<Resources> resources; + Call *call; +}; + +v8::Local<v8::Value> GetTagNodeValue(void *tag); + +Nan::Callback *GetTagCallback(void *tag); + +void DestroyTag(void *tag); + +void CompleteTag(void *tag); + } // namespace node } // namespace grpc diff --git a/src/node/ext/channel.cc b/src/node/ext/channel.cc index 00fcca6dc8..c4028170e7 100644 --- a/src/node/ext/channel.cc +++ b/src/node/ext/channel.cc @@ -41,6 +41,7 @@ #include "grpc/grpc_security.h" #include "call.h" #include "channel.h" +#include "completion_queue.h" #include "completion_queue_async_worker.h" #include "channel_credentials.h" #include "timeval.h" @@ -140,6 +141,7 @@ void DeallocateChannelArgs(grpc_channel_args *channel_args) { Channel::Channel(grpc_channel *channel) : wrapped_channel(channel) {} Channel::~Channel() { + gpr_log(GPR_DEBUG, "Destroying channel"); if (wrapped_channel != NULL) { grpc_channel_destroy(wrapped_channel); } @@ -276,11 +278,11 @@ NAN_METHOD(Channel::WatchConnectivityState) { unique_ptr<OpVec> ops(new OpVec()); grpc_channel_watch_connectivity_state( channel->wrapped_channel, last_state, MillisecondsToTimespec(deadline), - CompletionQueueAsyncWorker::GetQueue(), + GetCompletionQueue(), new struct tag(callback, ops.release(), - shared_ptr<Resources>(nullptr))); - CompletionQueueAsyncWorker::Next(); + shared_ptr<Resources>(nullptr), NULL)); + CompletionQueueNext(); } } // namespace node diff --git a/src/node/ext/completion_queue.cc b/src/node/ext/completion_queue.cc new file mode 100644 index 0000000000..fcfa77b39c --- /dev/null +++ b/src/node/ext/completion_queue.cc @@ -0,0 +1,114 @@ +/* + * + * 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 <uv.h> +#include <node.h> +#include <v8.h> +#include <grpc/grpc.h> + +#include "call.h" +#include "completion_queue.h" +#include "completion_queue_async_worker.h" + +namespace grpc { +namespace node { + +using v8::Local; +using v8::Object; +using v8::Value; + +grpc_completion_queue *queue; +uv_prepare_t prepare; +int pending_batches; + +void drain_completion_queue(uv_prepare_t *handle) { + Nan::HandleScope scope; + grpc_event event; + (void)handle; + do { + event = grpc_completion_queue_next( + queue, gpr_inf_past(GPR_CLOCK_MONOTONIC), NULL); + + if (event.type == GRPC_OP_COMPLETE) { + Nan::Callback *callback = grpc::node::GetTagCallback(event.tag); + if (event.success) { + Local<Value> argv[] = {Nan::Null(), + grpc::node::GetTagNodeValue(event.tag)}; + callback->Call(2, argv); + } else { + Local<Value> argv[] = {Nan::Error( + "The async function encountered an error")}; + callback->Call(1, argv); + } + grpc::node::CompleteTag(event.tag); + grpc::node::DestroyTag(event.tag); + pending_batches--; + if (pending_batches == 0) { + uv_prepare_stop(&prepare); + } + } + } while (event.type != GRPC_QUEUE_TIMEOUT); +} + +grpc_completion_queue *GetCompletionQueue() { +#ifdef GRPC_UV + return queue; +#else + return CompletionQueueAsyncWorker::GetQueue(); +#endif +} + +void CompletionQueueNext() { +#ifdef GRPC_UV + if (pending_batches == 0) { + GPR_ASSERT(!uv_is_active((uv_handle_t *)&prepare)); + uv_prepare_start(&prepare, drain_completion_queue); + } + pending_batches++; +#else + CompletionQueueAsyncWorker::Next(); +#endif +} + +void CompletionQueueInit(Local<Object> exports) { +#ifdef GRPC_UV + queue = grpc_completion_queue_create(NULL); + uv_prepare_init(uv_default_loop(), &prepare); + pending_batches = 0; +#else + CompletionQueueAsyncWorker::Init(exports); +#endif +} + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/completion_queue.h b/src/node/ext/completion_queue.h new file mode 100644 index 0000000000..bf280f768b --- /dev/null +++ b/src/node/ext/completion_queue.h @@ -0,0 +1,46 @@ +/* + * + * 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 <v8.h> + +namespace grpc { +namespace node { + +grpc_completion_queue *GetCompletionQueue(); + +void CompletionQueueNext(); + +void CompletionQueueInit(v8::Local<v8::Object> exports); + +} // namespace node +} // namespace grpc diff --git a/src/node/ext/completion_queue_async_worker.cc b/src/node/ext/completion_queue_async_worker.cc index 619ea41515..f5e03b277b 100644 --- a/src/node/ext/completion_queue_async_worker.cc +++ b/src/node/ext/completion_queue_async_worker.cc @@ -74,6 +74,7 @@ void CompletionQueueAsyncWorker::Execute() { grpc_completion_queue *CompletionQueueAsyncWorker::GetQueue() { return queue; } void CompletionQueueAsyncWorker::Next() { +#ifndef GRPC_UV Nan::HandleScope scope; if (current_threads < max_queue_threads) { current_threads += 1; @@ -85,6 +86,7 @@ void CompletionQueueAsyncWorker::Next() { GPR_ASSERT(current_threads <= max_queue_threads); GPR_ASSERT((current_threads == max_queue_threads) || (waiting_next_calls == 0)); +#endif } void CompletionQueueAsyncWorker::Init(Local<Object> exports) { diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc index 745b5023d5..9b9eee85b7 100644 --- a/src/node/ext/node_grpc.cc +++ b/src/node/ext/node_grpc.cc @@ -42,6 +42,13 @@ #include "grpc/support/log.h" #include "grpc/support/time.h" +// TODO(murgatroid99): Remove this when the endpoint API becomes public +#ifdef GRPC_UV +extern "C" { +#include "src/core/lib/iomgr/pollset_uv.h" +} +#endif + #include "call.h" #include "call_credentials.h" #include "channel.h" @@ -50,6 +57,7 @@ #include "completion_queue_async_worker.h" #include "server_credentials.h" #include "timeval.h" +#include "completion_queue.h" using v8::FunctionTemplate; using v8::Local; @@ -261,10 +269,10 @@ void InitLogConstants(Local<Object> exports) { Nan::HandleScope scope; Local<Object> log_verbosity = Nan::New<Object>(); Nan::Set(exports, Nan::New("logVerbosity").ToLocalChecked(), log_verbosity); - Local<Value> DEBUG(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_DEBUG)); - Nan::Set(log_verbosity, Nan::New("DEBUG").ToLocalChecked(), DEBUG); - Local<Value> INFO(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_INFO)); - Nan::Set(log_verbosity, Nan::New("INFO").ToLocalChecked(), INFO); + Local<Value> LOG_DEBUG(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_DEBUG)); + Nan::Set(log_verbosity, Nan::New("DEBUG").ToLocalChecked(), LOG_DEBUG); + Local<Value> LOG_INFO(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_INFO)); + Nan::Set(log_verbosity, Nan::New("INFO").ToLocalChecked(), LOG_INFO); Local<Value> LOG_ERROR(Nan::New<Uint32, uint32_t>(GPR_LOG_SEVERITY_ERROR)); Nan::Set(log_verbosity, Nan::New("ERROR").ToLocalChecked(), LOG_ERROR); } @@ -428,14 +436,19 @@ void init(Local<Object> exports) { InitWriteFlags(exports); InitLogConstants(exports); +#ifdef GRPC_UV + grpc_pollset_work_run_loop = 0; +#endif + grpc::node::Call::Init(exports); grpc::node::CallCredentials::Init(exports); grpc::node::Channel::Init(exports); grpc::node::ChannelCredentials::Init(exports); grpc::node::Server::Init(exports); - grpc::node::CompletionQueueAsyncWorker::Init(exports); grpc::node::ServerCredentials::Init(exports); + grpc::node::CompletionQueueInit(exports); + // Attach a few utility functions directly to the module Nan::Set(exports, Nan::New("metadataKeyIsLegal").ToLocalChecked(), Nan::GetFunction( diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc index dd1b777ac8..29f31ff15e 100644 --- a/src/node/ext/server.cc +++ b/src/node/ext/server.cc @@ -40,6 +40,7 @@ #include <vector> #include "call.h" +#include "completion_queue.h" #include "completion_queue_async_worker.h" #include "grpc/grpc.h" #include "grpc/grpc_security.h" @@ -64,6 +65,7 @@ using v8::Array; using v8::Boolean; using v8::Date; using v8::Exception; +using v8::External; using v8::Function; using v8::FunctionTemplate; using v8::Local; @@ -75,6 +77,8 @@ using v8::Value; Nan::Callback *Server::constructor; Persistent<FunctionTemplate> Server::fun_tpl; +static Callback *shutdown_callback; + class NewCallOp : public Op { public: NewCallOp() { @@ -111,6 +115,9 @@ class NewCallOp : public Op { shared_ptr<Resources> resources) { return true; } + bool IsFinalOp() { + return false; + } grpc_call *call; grpc_call_details details; @@ -120,17 +127,50 @@ class NewCallOp : public Op { std::string GetTypeString() const { return "new_call"; } }; +class ServerShutdownOp : public Op { + public: + ServerShutdownOp(grpc_server *server): server(server) { + } + + ~ServerShutdownOp() { + } + + Local<Value> GetNodeValue() const { + return Nan::New<External>(reinterpret_cast<void *>(server)); + } + + bool ParseOp(Local<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + return true; + } + bool IsFinalOp() { + return false; + } + + grpc_server *server; + + protected: + std::string GetTypeString() const { return "shutdown"; } +}; + +NAN_METHOD(ServerShutdownCallback) { + if (!info[0]->IsNull()) { + return Nan::ThrowError("forceShutdown failed somehow"); + } + MaybeLocal<Object> maybe_result = Nan::To<Object>(info[1]); + Local<Object> result = maybe_result.ToLocalChecked(); + Local<Value> server_val = Nan::Get( + result, Nan::New("shutdown").ToLocalChecked()).ToLocalChecked(); + Local<External> server_extern = server_val.As<External>(); + grpc_server *server = reinterpret_cast<grpc_server *>(server_extern->Value()); + grpc_server_destroy(server); +} + Server::Server(grpc_server *server) : wrapped_server(server) { - shutdown_queue = grpc_completion_queue_create(NULL); - grpc_server_register_non_listening_completion_queue(server, shutdown_queue, - NULL); } Server::~Server() { this->ShutdownServer(); - grpc_completion_queue_shutdown(this->shutdown_queue); - grpc_server_destroy(this->wrapped_server); - grpc_completion_queue_destroy(this->shutdown_queue); } void Server::Init(Local<Object> exports) { @@ -147,6 +187,11 @@ void Server::Init(Local<Object> exports) { Local<Function> ctr = Nan::GetFunction(tpl).ToLocalChecked(); Nan::Set(exports, Nan::New("Server").ToLocalChecked(), ctr); constructor = new Callback(ctr); + + Local<FunctionTemplate>callback_tpl = + Nan::New<FunctionTemplate>(ServerShutdownCallback); + shutdown_callback = new Callback( + Nan::GetFunction(callback_tpl).ToLocalChecked()); } bool Server::HasInstance(Local<Value> val) { @@ -155,11 +200,19 @@ bool Server::HasInstance(Local<Value> val) { } void Server::ShutdownServer() { - grpc_server_shutdown_and_notify(this->wrapped_server, this->shutdown_queue, - NULL); - grpc_server_cancel_all_calls(this->wrapped_server); - grpc_completion_queue_pluck(this->shutdown_queue, NULL, - gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + if (this->wrapped_server != NULL) { + ServerShutdownOp *op = new ServerShutdownOp(this->wrapped_server); + unique_ptr<OpVec> ops(new OpVec()); + ops->push_back(unique_ptr<Op>(op)); + + grpc_server_shutdown_and_notify( + this->wrapped_server, GetCompletionQueue(), + new struct tag(new Callback(**shutdown_callback), ops.release(), + shared_ptr<Resources>(nullptr), NULL)); + grpc_server_cancel_all_calls(this->wrapped_server); + CompletionQueueNext(); + this->wrapped_server = NULL; + } } NAN_METHOD(Server::New) { @@ -179,7 +232,7 @@ NAN_METHOD(Server::New) { } } grpc_server *wrapped_server; - grpc_completion_queue *queue = CompletionQueueAsyncWorker::GetQueue(); + grpc_completion_queue *queue = GetCompletionQueue(); grpc_channel_args *channel_args; if (!ParseChannelArgs(info[0], &channel_args)) { DeallocateChannelArgs(channel_args); @@ -205,14 +258,14 @@ NAN_METHOD(Server::RequestCall) { ops->push_back(unique_ptr<Op>(op)); grpc_call_error error = grpc_server_request_call( server->wrapped_server, &op->call, &op->details, &op->request_metadata, - CompletionQueueAsyncWorker::GetQueue(), - CompletionQueueAsyncWorker::GetQueue(), + GetCompletionQueue(), + GetCompletionQueue(), new struct tag(new Callback(info[0].As<Function>()), ops.release(), - shared_ptr<Resources>(nullptr))); + shared_ptr<Resources>(nullptr), NULL)); if (error != GRPC_CALL_OK) { return Nan::ThrowError(nanErrorWithCode("requestCall failed", error)); } - CompletionQueueAsyncWorker::Next(); + CompletionQueueNext(); } NAN_METHOD(Server::AddHttp2Port) { @@ -259,10 +312,10 @@ NAN_METHOD(Server::TryShutdown) { Server *server = ObjectWrap::Unwrap<Server>(info.This()); unique_ptr<OpVec> ops(new OpVec()); grpc_server_shutdown_and_notify( - server->wrapped_server, CompletionQueueAsyncWorker::GetQueue(), + server->wrapped_server, GetCompletionQueue(), new struct tag(new Nan::Callback(info[0].As<Function>()), ops.release(), - shared_ptr<Resources>(nullptr))); - CompletionQueueAsyncWorker::Next(); + shared_ptr<Resources>(nullptr), NULL)); + CompletionQueueNext(); } NAN_METHOD(Server::ForceShutdown) { diff --git a/src/node/ext/server.h b/src/node/ext/server.h index ab5fc210e8..9e6a7bd1e0 100644 --- a/src/node/ext/server.h +++ b/src/node/ext/server.h @@ -73,7 +73,6 @@ class Server : public Nan::ObjectWrap { static Nan::Persistent<v8::FunctionTemplate> fun_tpl; grpc_server *wrapped_server; - grpc_completion_queue *shutdown_queue; }; } // namespace node diff --git a/src/node/index.js b/src/node/index.js index 9fb6faa5d7..a294aad8ee 100644 --- a/src/node/index.js +++ b/src/node/index.js @@ -219,3 +219,7 @@ exports.getClientChannel = client.getClientChannel; * @see module:src/client.waitForClientReady */ exports.waitForClientReady = client.waitForClientReady; + +exports.closeClient = function closeClient(client_obj) { + client.getClientChannel(client_obj).close(); +}; diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js index e8f2d37bd8..46ddecfb1f 100644 --- a/src/node/interop/interop_client.js +++ b/src/node/interop/interop_client.js @@ -375,11 +375,20 @@ function statusCodeAndMessage(client, done) { duplex.end(); } +// NOTE: the client param to this function is from UnimplementedService +function unimplementedService(client, done) { + client.unimplementedCall({}, function(err, resp) { + assert(err); + assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED); + done(); + }); +} + +// NOTE: the client param to this function is from TestService function unimplementedMethod(client, done) { client.unimplementedCall({}, function(err, resp) { assert(err); assert.strictEqual(err.code, grpc.status.UNIMPLEMENTED); - assert(!err.message); done(); }); } @@ -527,8 +536,10 @@ var test_cases = { Client: testProto.TestService}, status_code_and_message: {run: statusCodeAndMessage, Client: testProto.TestService}, - unimplemented_method: {run: unimplementedMethod, + unimplemented_service: {run: unimplementedService, Client: testProto.UnimplementedService}, + unimplemented_method: {run: unimplementedMethod, + Client: testProto.TestService}, compute_engine_creds: {run: computeEngineCreds, Client: testProto.TestService, getCreds: getApplicationCreds}, diff --git a/src/node/src/client.js b/src/node/src/client.js index f75f951eb8..9c1562e8b8 100644 --- a/src/node/src/client.js +++ b/src/node/src/client.js @@ -382,6 +382,7 @@ function makeUnaryRequestFunction(method, serialize, deserialize) { if (args.options) { message.grpcWriteFlags = args.options.flags; } + client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata._getCoreRepresentation(); client_batch[grpc.opType.SEND_MESSAGE] = message; diff --git a/src/node/src/grpc_extension.js b/src/node/src/grpc_extension.js index 6a8fe2c03c..63a281ddbc 100644 --- a/src/node/src/grpc_extension.js +++ b/src/node/src/grpc_extension.js @@ -31,7 +31,7 @@ * */ -var binary = require('node-pre-gyp'); +var binary = require('node-pre-gyp/lib/pre-binding'); var path = require('path'); var binding_path = binary.find(path.resolve(path.join(__dirname, '../../../package.json'))); diff --git a/src/node/test/async_test.js b/src/node/test/async_test.js index c46e745116..7b467e5475 100644 --- a/src/node/test/async_test.js +++ b/src/node/test/async_test.js @@ -61,6 +61,7 @@ describe('Async functionality', function() { done(); }); after(function() { + grpc.closeClient(math_client); server.forceShutdown(); }); it('should not hang', function(done) { diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js index f008a87585..58f8842c0d 100644 --- a/src/node/test/interop_sanity_test.js +++ b/src/node/test/interop_sanity_test.js @@ -98,6 +98,10 @@ describe('Interop tests', function() { interop_client.runTest(port, name_override, 'status_code_and_message', true, true, done); }); + it('should pass unimplemented_service', function(done) { + interop_client.runTest(port, name_override, 'unimplemented_service', + true, true, done); + }); it('should pass unimplemented_method', function(done) { interop_client.runTest(port, name_override, 'unimplemented_method', true, true, done); diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h index 4a3f3fa4a1..c1623a0068 100644 --- a/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h +++ b/src/objective-c/GRPCClient/GRPCCall+ChannelArg.h @@ -40,15 +40,18 @@ @interface GRPCCall (ChannelArg) /** - * Use the provided @c userAgentPrefix at the beginning of the HTTP User Agent string for all calls - * to the specified @c host. + * Use the provided @c userAgentPrefix at the beginning of the HTTP User Agent + * string for all calls to the specified @c host. */ -+ (void)setUserAgentPrefix:(nonnull NSString *)userAgentPrefix forHost:(nonnull NSString *)host; ++ (void)setUserAgentPrefix:(nonnull NSString *)userAgentPrefix + forHost:(nonnull NSString *)host; -/** The default response size limit is 4MB. Set this to override that default. */ +/** The default response size limit is 4MB. Set this to override that default. + */ + (void)setResponseSizeLimit:(NSUInteger)limit forHost:(nonnull NSString *)host; -+ (void)closeOpenConnections DEPRECATED_MSG_ATTRIBUTE("The API for this feature is experimental, " - "and might be removed or modified at any " - "time."); ++ (void)closeOpenConnections DEPRECATED_MSG_ATTRIBUTE( + "The API for this feature is experimental, " + "and might be removed or modified at any " + "time."); @end diff --git a/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h b/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h index ac2a37d75f..beae0d11a2 100644 --- a/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h +++ b/src/objective-c/GRPCClient/GRPCCall+ChannelCredentials.h @@ -33,24 +33,26 @@ #import "GRPCCall.h" -/** Helpers for setting TLS Trusted Roots, Client Certificates, and Private Key */ +/** Helpers for setting TLS Trusted Roots, Client Certificates, and Private Key + */ @interface GRPCCall (ChannelCredentials) /** - * Use the provided @c pemRootCert as the set of trusted root Certificate Authorities for @c host. + * Use the provided @c pemRootCert as the set of trusted root Certificate + * Authorities for @c host. */ + (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCert forHost:(nonnull NSString *)host - error:(NSError * _Nullable * _Nullable)errorPtr; + error:(NSError *_Nullable *_Nullable)errorPtr; /** - * Configures @c host with TLS/SSL Client Credentials and optionally trusted root Certificate - * Authorities. If @c pemRootCerts is nil, the default CA Certificates bundled with gRPC will be - * used. + * Configures @c host with TLS/SSL Client Credentials and optionally trusted + * root Certificate Authorities. If @c pemRootCerts is nil, the default CA + * Certificates bundled with gRPC will be used. */ + (BOOL)setTLSPEMRootCerts:(nullable NSString *)pemRootCerts withPrivateKey:(nullable NSString *)pemPrivateKey withCertChain:(nullable NSString *)pemCertChain forHost:(nonnull NSString *)host - error:(NSError * _Nullable * _Nullable)errorPtr; + error:(NSError *_Nullable *_Nullable)errorPtr; @end diff --git a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h index 6b443877e9..467c6332c1 100644 --- a/src/objective-c/GRPCClient/GRPCCall+OAuth2.h +++ b/src/objective-c/GRPCClient/GRPCCall+OAuth2.h @@ -37,15 +37,17 @@ @interface GRPCCall (OAuth2) /** - * Setting this property is equivalent to setting "Bearer <passed token>" as the value of the - * request header with key "authorization" (the authorization header). Setting it to nil removes the - * authorization header from the request. - * The value obtained by getting the property is the OAuth2 bearer token if the authorization header - * of the request has the form "Bearer <token>", or nil otherwise. + * Setting this property is equivalent to setting "Bearer <passed token>" as the + * value of the request header with key "authorization" (the authorization + * header). Setting it to nil removes the authorization header from the request. + * The value obtained by getting the property is the OAuth2 bearer token if the + * authorization header of the request has the form "Bearer <token>", or nil + * otherwise. */ @property(atomic, copy) NSString *oauth2AccessToken; -/** Returns the value (if any) of the "www-authenticate" response header (the challenge header). */ +/** Returns the value (if any) of the "www-authenticate" response header (the + * challenge header). */ @property(atomic, readonly) NSString *oauth2ChallengeHeader; @end diff --git a/src/objective-c/GRPCClient/GRPCCall+Tests.h b/src/objective-c/GRPCClient/GRPCCall+Tests.h index 184ad09c5c..f517f3aac8 100644 --- a/src/objective-c/GRPCClient/GRPCCall+Tests.h +++ b/src/objective-c/GRPCClient/GRPCCall+Tests.h @@ -34,33 +34,36 @@ #import "GRPCCall.h" /** - * Methods to let tune down the security of gRPC connections for specific hosts. These shouldn't be - * used in releases, but are sometimes needed for testing. + * Methods to let tune down the security of gRPC connections for specific hosts. + * These shouldn't be used in releases, but are sometimes needed for testing. */ @interface GRPCCall (Tests) /** - * Establish all SSL connections to the provided host using the passed SSL target name and the root - * certificates found in the file at |certsPath|. + * Establish all SSL connections to the provided host using the passed SSL + * target name and the root certificates found in the file at |certsPath|. * - * Must be called before any gRPC call to that host is made. It's illegal to pass the same host to - * more than one invocation of the methods of this category. + * Must be called before any gRPC call to that host is made. It's illegal to + * pass the same host to more than one invocation of the methods of this + * category. */ + (void)useTestCertsPath:(NSString *)certsPath testName:(NSString *)testName forHost:(NSString *)host; /** - * Establish all connections to the provided host using cleartext instead of SSL. + * Establish all connections to the provided host using cleartext instead of + * SSL. * - * Must be called before any gRPC call to that host is made. It's illegal to pass the same host to - * more than one invocation of the methods of this category. + * Must be called before any gRPC call to that host is made. It's illegal to + * pass the same host to more than one invocation of the methods of this + * category. */ + (void)useInsecureConnectionsForHost:(NSString *)host; /** - * Resets all host configurations to their default values, and flushes all connections from the - * cache. + * Resets all host configurations to their default values, and flushes all + * connections from the cache. */ + (void)resetHostSettings; @end diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 7645bb1d34..5ed160d7a0 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -34,17 +34,18 @@ /** * The gRPC protocol is an RPC protocol on top of HTTP2. * - * While the most common type of RPC receives only one request message and returns only one response - * message, the protocol also supports RPCs that return multiple individual messages in a streaming - * fashion, RPCs that accept a stream of request messages, or RPCs with both streaming requests and + * While the most common type of RPC receives only one request message and + * returns only one response message, the protocol also supports RPCs that + * return multiple individual messages in a streaming fashion, RPCs that accept + * a stream of request messages, or RPCs with both streaming requests and * responses. * - * Conceptually, each gRPC call consists of a bidirectional stream of binary messages, with RPCs of - * the "non-streaming type" sending only one message in the corresponding direction (the protocol - * doesn't make any distinction). + * Conceptually, each gRPC call consists of a bidirectional stream of binary + * messages, with RPCs of the "non-streaming type" sending only one message in + * the corresponding direction (the protocol doesn't make any distinction). * - * Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs can be multiplexed - * transparently on the same TCP connection. + * Each RPC uses a different HTTP2 stream, and thus multiple simultaneous RPCs + * can be multiplexed transparently on the same TCP connection. */ #import <Foundation/Foundation.h> @@ -59,51 +60,56 @@ extern NSString *const kGRPCErrorDomain; /** * gRPC error codes. - * Note that a few of these are never produced by the gRPC libraries, but are of general utility for - * server applications to produce. + * Note that a few of these are never produced by the gRPC libraries, but are of + * general utility for server applications to produce. */ typedef NS_ENUM(NSUInteger, GRPCErrorCode) { /** The operation was cancelled (typically by the caller). */ GRPCErrorCodeCancelled = 1, /** - * Unknown error. Errors raised by APIs that do not return enough error information may be + * Unknown error. Errors raised by APIs that do not return enough error + * information may be * converted to this error. */ GRPCErrorCodeUnknown = 2, /** - * The client specified an invalid argument. Note that this differs from FAILED_PRECONDITION. - * INVALID_ARGUMENT indicates arguments that are problematic regardless of the state of the - * server (e.g., a malformed file name). + * The client specified an invalid argument. Note that this differs from + * FAILED_PRECONDITION. + * INVALID_ARGUMENT indicates arguments that are problematic regardless of the + * state of the server (e.g., a malformed file name). */ GRPCErrorCodeInvalidArgument = 3, /** - * Deadline expired before operation could complete. For operations that change the state of the - * server, this error may be returned even if the operation has completed successfully. For - * example, a successful response from the server could have been delayed long enough for the - * deadline to expire. + * Deadline expired before operation could complete. For operations that + * change the state of the server, this error may be returned even if the + * operation has completed successfully. For example, a successful response + * from the server could have been delayed long enough for the deadline to + * expire. */ GRPCErrorCodeDeadlineExceeded = 4, /** Some requested entity (e.g., file or directory) was not found. */ GRPCErrorCodeNotFound = 5, - /** Some entity that we attempted to create (e.g., file or directory) already exists. */ + /** Some entity that we attempted to create (e.g., file or directory) already + exists. */ GRPCErrorCodeAlreadyExists = 6, /** - * The caller does not have permission to execute the specified operation. PERMISSION_DENIED isn't - * used for rejections caused by exhausting some resource (RESOURCE_EXHAUSTED is used instead for - * those errors). PERMISSION_DENIED doesn't indicate a failure to identify the caller + * The caller does not have permission to execute the specified operation. + * PERMISSION_DENIED isn't used for rejections caused by exhausting some + * resource (RESOURCE_EXHAUSTED is used instead for those errors). + * PERMISSION_DENIED doesn't indicate a failure to identify the caller * (UNAUTHENTICATED is used instead for those errors). */ GRPCErrorCodePermissionDenied = 7, /** - * The request does not have valid authentication credentials for the operation (e.g. the caller's - * identity can't be verified). + * The request does not have valid authentication credentials for the + * operation (e.g. the caller's identity can't be verified). */ GRPCErrorCodeUnauthenticated = 16, @@ -111,42 +117,47 @@ typedef NS_ENUM(NSUInteger, GRPCErrorCode) { GRPCErrorCodeResourceExhausted = 8, /** - * The RPC was rejected because the server is not in a state required for the procedure's + * The RPC was rejected because the server is not in a state required for the + * procedure's * execution. For example, a directory to be deleted may be non-empty, etc. - * The client should not retry until the server state has been explicitly fixed (e.g. by - * performing another RPC). The details depend on the service being called, and should be found in - * the NSError's userInfo. + * The client should not retry until the server state has been explicitly + * fixed (e.g. by + * performing another RPC). The details depend on the service being called, + * and should be found in the NSError's userInfo. */ GRPCErrorCodeFailedPrecondition = 9, /** - * The RPC was aborted, typically due to a concurrency issue like sequencer check failures, - * transaction aborts, etc. The client should retry at a higher-level (e.g., restarting a read- - * modify-write sequence). + * The RPC was aborted, typically due to a concurrency issue like sequencer + * check failures, transaction aborts, etc. The client should retry at a + * higher-level (e.g., restarting a read-modify-write sequence). */ GRPCErrorCodeAborted = 10, /** - * The RPC was attempted past the valid range. E.g., enumerating past the end of a list. - * Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed if the system state - * changes. For example, an RPC to get elements of a list will generate INVALID_ARGUMENT if asked - * to return the element at a negative index, but it will generate OUT_OF_RANGE if asked to return - * the element at an index past the current size of the list. + * The RPC was attempted past the valid range. E.g., enumerating past the end + * of a list. + * Unlike INVALID_ARGUMENT, this error indicates a problem that may be fixed + * if the system state changes. For example, an RPC to get elements of a list + * will generate INVALID_ARGUMENT if asked to return the element at a negative + * index, but it will generate OUT_OF_RANGE if asked to return the element at + * an index past the current size of the list. */ GRPCErrorCodeOutOfRange = 11, - /** The procedure is not implemented or not supported/enabled in this server. */ + /** The procedure is not implemented or not supported/enabled in this server. + */ GRPCErrorCodeUnimplemented = 12, /** - * Internal error. Means some invariant expected by the server application or the gRPC library has - * been broken. + * Internal error. Means some invariant expected by the server application or + * the gRPC library has been broken. */ GRPCErrorCodeInternal = 13, /** - * The server is currently unavailable. This is most likely a transient condition and may be - * corrected by retrying with a backoff. + * The server is currently unavailable. This is most likely a transient + * condition and may be corrected by retrying with a backoff. */ GRPCErrorCodeUnavailable = 14, @@ -158,17 +169,19 @@ typedef NS_ENUM(NSUInteger, GRPCErrorCode) { * Safety remark of a gRPC method as defined in RFC 2616 Section 9.1 */ typedef NS_ENUM(NSUInteger, GRPCCallSafety) { - /** Signal that there is no guarantees on how the call affects the server state. */ + /** Signal that there is no guarantees on how the call affects the server + state. */ GRPCCallSafetyDefault = 0, /** Signal that the call is idempotent. gRPC is free to use PUT verb. */ GRPCCallSafetyIdempotentRequest = 1, - /** Signal that the call is cacheable and will not affect server state. gRPC is free to use GET verb. */ + /** Signal that the call is cacheable and will not affect server state. gRPC + is free to use GET verb. */ GRPCCallSafetyCacheableRequest = 2, }; /** - * Keys used in |NSError|'s |userInfo| dictionary to store the response headers and trailers sent by - * the server. + * Keys used in |NSError|'s |userInfo| dictionary to store the response headers + * and trailers sent by the server. */ extern id const kGRPCHeadersKey; extern id const kGRPCTrailersKey; @@ -179,20 +192,24 @@ extern id const kGRPCTrailersKey; @interface GRPCCall : GRXWriter /** - * The container of the request headers of an RPC conforms to this protocol, which is a subset of - * NSMutableDictionary's interface. It will become a NSMutableDictionary later on. - * The keys of this container are the header names, which per the HTTP standard are case- - * insensitive. They are stored in lowercase (which is how HTTP/2 mandates them on the wire), and - * can only consist of ASCII characters. - * A header value is a NSString object (with only ASCII characters), unless the header name has the - * suffix "-bin", in which case the value has to be a NSData object. + * The container of the request headers of an RPC conforms to this protocol, + * which is a subset of NSMutableDictionary's interface. It will become a + * NSMutableDictionary later on. The keys of this container are the header + * names, which per the HTTP standard are case-insensitive. They are stored in + * lowercase (which is how HTTP/2 mandates them on the wire), and can only + * consist of ASCII characters. + * A header value is a NSString object (with only ASCII characters), unless the + * header name has the suffix "-bin", in which case the value has to be a NSData + * object. */ /** - * These HTTP headers will be passed to the server as part of this call. Each HTTP header is a - * name-value pair with string names and either string or binary values. + * These HTTP headers will be passed to the server as part of this call. Each + * HTTP header is a name-value pair with string names and either string or + * binary values. * - * The passed dictionary has to use NSString keys, corresponding to the header names. The value - * associated to each can be a NSString object or a NSData object. E.g.: + * The passed dictionary has to use NSString keys, corresponding to the header + * names. The value associated to each can be a NSString object or a NSData + * object. E.g.: * * call.requestHeaders = @{@"authorization": @"Bearer ..."}; * @@ -205,53 +222,61 @@ extern id const kGRPCTrailersKey; @property(atomic, readonly) NSMutableDictionary *requestHeaders; /** - * This dictionary is populated with the HTTP headers received from the server. This happens before - * any response message is received from the server. It has the same structure as the request - * headers dictionary: Keys are NSString header names; names ending with the suffix "-bin" have a - * NSData value; the others have a NSString value. + * This dictionary is populated with the HTTP headers received from the server. + * This happens before any response message is received from the server. It has + * the same structure as the request headers dictionary: Keys are NSString + * header names; names ending with the suffix "-bin" have a NSData value; the + * others have a NSString value. * - * The value of this property is nil until all response headers are received, and will change before - * any of -writeValue: or -writesFinishedWithError: are sent to the writeable. + * The value of this property is nil until all response headers are received, + * and will change before any of -writeValue: or -writesFinishedWithError: are + * sent to the writeable. */ @property(atomic, readonly) NSDictionary *responseHeaders; /** - * Same as responseHeaders, but populated with the HTTP trailers received from the server before the - * call finishes. + * Same as responseHeaders, but populated with the HTTP trailers received from + * the server before the call finishes. * - * The value of this property is nil until all response trailers are received, and will change - * before -writesFinishedWithError: is sent to the writeable. + * The value of this property is nil until all response trailers are received, + * and will change before -writesFinishedWithError: is sent to the writeable. */ @property(atomic, readonly) NSDictionary *responseTrailers; /** - * The request writer has to write NSData objects into the provided Writeable. The server will - * receive each of those separately and in order as distinct messages. - * A gRPC call might not complete until the request writer finishes. On the other hand, the request - * finishing doesn't necessarily make the call to finish, as the server might continue sending - * messages to the response side of the call indefinitely (depending on the semantics of the - * specific remote method called). + * The request writer has to write NSData objects into the provided Writeable. + * The server will receive each of those separately and in order as distinct + * messages. + * A gRPC call might not complete until the request writer finishes. On the + * other hand, the request finishing doesn't necessarily make the call to + * finish, as the server might continue sending messages to the response side of + * the call indefinitely (depending on the semantics of the specific remote + * method called). * To finish a call right away, invoke cancel. - * host parameter should not contain the scheme (http:// or https://), only the name or IP addr - * and the port number, for example @"localhost:5050". + * host parameter should not contain the scheme (http:// or https://), only the + * name or IP addr and the port number, for example @"localhost:5050". */ - (instancetype)initWithHost:(NSString *)host path:(NSString *)path - requestsWriter:(GRXWriter *)requestsWriter NS_DESIGNATED_INITIALIZER; + requestsWriter:(GRXWriter *)requestsWriter + NS_DESIGNATED_INITIALIZER; /** - * Finishes the request side of this call, notifies the server that the RPC should be cancelled, and - * finishes the response side of the call with an error of code CANCELED. + * Finishes the request side of this call, notifies the server that the RPC + * should be cancelled, and finishes the response side of the call with an error + * of code CANCELED. */ - (void)cancel; /** * Set the call flag for a specific host path. * - * Host parameter should not contain the scheme (http:// or https://), only the name or IP addr - * and the port number, for example @"localhost:5050". + * Host parameter should not contain the scheme (http:// or https://), only the + * name or IP addr and the port number, for example @"localhost:5050". */ -+ (void)setCallSafety:(GRPCCallSafety)callSafety host:(NSString *)host path:(NSString *)path; ++ (void)setCallSafety:(GRPCCallSafety)callSafety + host:(NSString *)host + path:(NSString *)path; // TODO(jcanizales): Let specify a deadline. As a category of GRXWriter? @end @@ -260,7 +285,7 @@ extern id const kGRPCTrailersKey; /** This protocol is kept for backwards compatibility with existing code. */ DEPRECATED_MSG_ATTRIBUTE("Use NSDictionary or NSMutableDictionary instead.") -@protocol GRPCRequestHeaders <NSObject> +@protocol GRPCRequestHeaders<NSObject> @property(nonatomic, readonly) NSUInteger count; - (id)objectForKeyedSubscript:(id)key; @@ -273,6 +298,6 @@ DEPRECATED_MSG_ATTRIBUTE("Use NSDictionary or NSMutableDictionary instead.") #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated" /** This is only needed for backwards-compatibility. */ -@interface NSMutableDictionary (GRPCRequestHeaders) <GRPCRequestHeaders> +@interface NSMutableDictionary (GRPCRequestHeaders)<GRPCRequestHeaders> @end #pragma clang diagnostic pop diff --git a/src/objective-c/GRPCClient/GRPCCall.m b/src/objective-c/GRPCClient/GRPCCall.m index 43204345f5..85d141aa09 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -33,9 +33,9 @@ #import "GRPCCall.h" +#import <RxLibrary/GRXConcurrentWriteable.h> #include <grpc/grpc.h> #include <grpc/support/time.h> -#import <RxLibrary/GRXConcurrentWriteable.h> #import "private/GRPCConnectivityMonitor.h" #import "private/GRPCHost.h" @@ -45,11 +45,11 @@ #import "private/NSDictionary+GRPC.h" #import "private/NSError+GRPC.h" -NSString * const kGRPCHeadersKey = @"io.grpc.HeadersKey"; -NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; +NSString *const kGRPCHeadersKey = @"io.grpc.HeadersKey"; +NSString *const kGRPCTrailersKey = @"io.grpc.TrailersKey"; static NSMutableDictionary *callFlags; -@interface GRPCCall () <GRXWriteable> +@interface GRPCCall ()<GRXWriteable> // Make them read-write. @property(atomic, strong) NSDictionary *responseHeaders; @property(atomic, strong) NSDictionary *responseTrailers; @@ -85,17 +85,21 @@ static NSMutableDictionary *callFlags; // correct ordering. GRXConcurrentWriteable *_responseWriteable; - // The network thread wants the requestWriter to resume (when the server is ready for more input), - // or to stop (on errors), concurrently with user threads that want to start it, pause it or stop - // it. Because a writer isn't thread-safe, we'll synchronize those operations on it. - // We don't use a dispatch queue for that purpose, because the writer can call writeValue: or - // writesFinishedWithError: on this GRPCCall as part of those operations. We want to be able to - // pause the writer immediately on writeValue:, so we need our locking to be recursive. + // The network thread wants the requestWriter to resume (when the server is + // ready for more input), or to stop (on errors), concurrently with user + // threads that want to start it, pause it or stop it. Because a writer isn't + // thread-safe, we'll synchronize those operations on it. + // We don't use a dispatch queue for that purpose, because the writer can call + // writeValue: or writesFinishedWithError: on this GRPCCall as part of those + // operations. We want to be able to pause the writer immediately on + // writeValue:, so we need our locking to be recursive. GRXWriter *_requestWriter; // To create a retain cycle when a call is started, up until it finishes. See - // |startWithWriteable:| and |finishWithError:|. This saves users from having to retain a - // reference to the call object if all they're interested in is the handler being executed when + // |startWithWriteable:| and |finishWithError:|. This saves users from having + // to retain a + // reference to the call object if all they're interested in is the handler + // being executed when // the response arrives. GRPCCall *_retainSelf; @@ -104,13 +108,16 @@ static NSMutableDictionary *callFlags; @synthesize state = _state; -// TODO(jcanizales): If grpc_init is idempotent, this should be changed from load to initialize. +// TODO(jcanizales): If grpc_init is idempotent, this should be changed from +// load to initialize. + (void)load { grpc_init(); callFlags = [NSMutableDictionary dictionary]; } -+ (void)setCallSafety:(GRPCCallSafety)callSafety host:(NSString *)host path:(NSString *)path { ++ (void)setCallSafety:(GRPCCallSafety)callSafety + host:(NSString *)host + path:(NSString *)path { NSString *hostAndPath = [NSString stringWithFormat:@"%@/%@", host, path]; switch (callSafety) { case GRPCCallSafetyDefault: @@ -141,7 +148,8 @@ static NSMutableDictionary *callFlags; path:(NSString *)path requestsWriter:(GRXWriter *)requestWriter { if (!host || !path) { - [NSException raise:NSInvalidArgumentException format:@"Neither host nor path can be nil."]; + [NSException raise:NSInvalidArgumentException + format:@"Neither host nor path can be nil."]; } if (requestWriter.state != GRXWriterStateNotStarted) { [NSException raise:NSInvalidArgumentException @@ -191,7 +199,10 @@ static NSMutableDictionary *callFlags; - (void)cancel { [self finishWithError:[NSError errorWithDomain:kGRPCErrorDomain code:GRPCErrorCodeCancelled - userInfo:@{NSLocalizedDescriptionKey: @"Canceled by app"}]]; + userInfo:@{ + NSLocalizedDescriptionKey : + @"Canceled by app" + }]]; [self cancelCall]; } @@ -206,15 +217,18 @@ static NSMutableDictionary *callFlags; // Only called from the call queue. // The handler will be called from the network queue. -- (void)startReadWithHandler:(void(^)(grpc_byte_buffer *))handler { +- (void)startReadWithHandler:(void (^)(grpc_byte_buffer *))handler { // TODO(jcanizales): Add error handlers for async failures - [_wrappedCall startBatchWithOperations:@[[[GRPCOpRecvMessage alloc] initWithHandler:handler]]]; + [_wrappedCall startBatchWithOperations:@[ [[GRPCOpRecvMessage alloc] + initWithHandler:handler] ]]; } // Called initially from the network queue once response headers are received, -// then "recursively" from the responseWriteable queue after each response from the +// then "recursively" from the responseWriteable queue after each response from +// the // server has been written. -// If the call is currently paused, this is a noop. Restarting the call will invoke this +// If the call is currently paused, this is a noop. Restarting the call will +// invoke this // method. // TODO(jcanizales): Rename to readResponseIfNotPaused. - (void)startNextRead { @@ -237,15 +251,23 @@ static NSMutableDictionary *callFlags; // don't want to throw, because the app shouldn't crash for a behavior // that's on the hands of any server to have. Instead we finish and ask // the server to cancel. - [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeResourceExhausted - userInfo:@{NSLocalizedDescriptionKey: @"Client does not have enough memory to hold the server response."}]]; + [weakSelf + finishWithError:[NSError + errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeResourceExhausted + userInfo:@{ + NSLocalizedDescriptionKey : + @"Client does not have enough " + @"memory to hold the server " + @"response." + }]]; [weakSelf cancelCall]; return; } - [weakWriteable enqueueValue:data completionHandler:^{ - [weakSelf startNextRead]; - }]; + [weakWriteable enqueueValue:data + completionHandler:^{ + [weakSelf startNextRead]; + }]; }]; }); } @@ -254,19 +276,22 @@ static NSMutableDictionary *callFlags; - (void)sendHeaders:(NSDictionary *)headers { // TODO(jcanizales): Add error handlers for async failures - [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers - flags:[GRPCCall callFlagsForHost:_host path:_path] - handler:nil]]]; + [_wrappedCall startBatchWithOperations:@[ + [[GRPCOpSendMetadata alloc] + initWithMetadata:headers + flags:[GRPCCall callFlagsForHost:_host path:_path] + handler:nil] + ]]; } #pragma mark GRXWriteable implementation // Only called from the call queue. The error handler will be called from the // network queue if the write didn't succeed. -- (void)writeMessage:(NSData *)message withErrorHandler:(void (^)())errorHandler { - +- (void)writeMessage:(NSData *)message + withErrorHandler:(void (^)())errorHandler { __weak GRPCCall *weakSelf = self; - void(^resumingHandler)(void) = ^{ + void (^resumingHandler)(void) = ^{ // Resume the request writer. GRPCCall *strongSelf = weakSelf; if (strongSelf) { @@ -275,8 +300,9 @@ static NSMutableDictionary *callFlags; } } }; - [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMessage alloc] initWithMessage:message - handler:resumingHandler]] + [_wrappedCall startBatchWithOperations:@[ [[GRPCOpSendMessage alloc] + initWithMessage:message + handler:resumingHandler] ] errorHandler:errorHandler]; } @@ -291,18 +317,20 @@ static NSMutableDictionary *callFlags; __weak GRPCCall *weakSelf = self; dispatch_async(_callQueue, ^{ - [weakSelf writeMessage:value withErrorHandler:^{ - [weakSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain + [weakSelf writeMessage:value + withErrorHandler:^{ + [weakSelf + finishWithError:[NSError errorWithDomain:kGRPCErrorDomain code:GRPCErrorCodeInternal userInfo:nil]]; - }]; + }]; }); } // Only called from the call queue. The error handler will be called from the // network queue if the requests stream couldn't be closed successfully. - (void)finishRequestWithErrorHandler:(void (^)())errorHandler { - [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendClose alloc] init]] + [_wrappedCall startBatchWithOperations:@[ [[GRPCOpSendClose alloc] init] ] errorHandler:errorHandler]; } @@ -323,17 +351,19 @@ static NSMutableDictionary *callFlags; #pragma mark Invoke -// Both handlers will eventually be called, from the network queue. Writes can start immediately -// after this. +// Both handlers will eventually be called, from the network queue. Writes can +// start immediately after this. // The first one (headersHandler), when the response headers are received. // The second one (completionHandler), whenever the RPC finishes for any reason. -- (void)invokeCallWithHeadersHandler:(void(^)(NSDictionary *))headersHandler - completionHandler:(void(^)(NSError *, NSDictionary *))completionHandler { +- (void)invokeCallWithHeadersHandler:(void (^)(NSDictionary *))headersHandler + completionHandler: + (void (^)(NSError *, NSDictionary *))completionHandler { // TODO(jcanizales): Add error handlers for async failures - [_wrappedCall startBatchWithOperations:@[[[GRPCOpRecvMetadata alloc] - initWithHandler:headersHandler]]]; - [_wrappedCall startBatchWithOperations:@[[[GRPCOpRecvStatus alloc] - initWithHandler:completionHandler]]]; + [_wrappedCall startBatchWithOperations:@[ [[GRPCOpRecvMetadata alloc] + initWithHandler:headersHandler] ]]; + [_wrappedCall + startBatchWithOperations:@[ [[GRPCOpRecvStatus alloc] + initWithHandler:completionHandler] ]]; } - (void)invokeCall { @@ -341,27 +371,31 @@ static NSMutableDictionary *callFlags; // Response headers received. self.responseHeaders = headers; [self startNextRead]; - } completionHandler:^(NSError *error, NSDictionary *trailers) { - self.responseTrailers = trailers; - - if (error) { - NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; - if (error.userInfo) { - [userInfo addEntriesFromDictionary:error.userInfo]; - } - userInfo[kGRPCTrailersKey] = self.responseTrailers; - // TODO(jcanizales): The C gRPC library doesn't guarantee that the headers block will be - // called before this one, so an error might end up with trailers but no headers. We - // shouldn't call finishWithError until ater both blocks are called. It is also when this is - // done that we can provide a merged view of response headers and trailers in a thread-safe - // way. - if (self.responseHeaders) { - userInfo[kGRPCHeadersKey] = self.responseHeaders; - } - error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo]; - } - [self finishWithError:error]; - }]; + } + completionHandler:^(NSError *error, NSDictionary *trailers) { + self.responseTrailers = trailers; + + if (error) { + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + if (error.userInfo) { + [userInfo addEntriesFromDictionary:error.userInfo]; + } + userInfo[kGRPCTrailersKey] = self.responseTrailers; + // TODO(jcanizales): The C gRPC library doesn't guarantee that the + // headers block will be called before this one, so an error might end + // up with trailers but no headers. We shouldn't call finishWithError + // until ater both blocks are called. It is also when this is done + // that we can provide a merged view of response headers and trailers + // in a thread-safe way. + if (self.responseHeaders) { + userInfo[kGRPCHeadersKey] = self.responseHeaders; + } + error = [NSError errorWithDomain:error.domain + code:error.code + userInfo:userInfo]; + } + [self finishWithError:error]; + }]; // Now that the RPC has been initiated, request writes can start. @synchronized(_requestWriter) { [_requestWriter startWithWriteable:self]; @@ -375,43 +409,55 @@ static NSMutableDictionary *callFlags; _state = GRXWriterStateStarted; } - // Create a retain cycle so that this instance lives until the RPC finishes (or is cancelled). - // This makes RPCs in which the call isn't externally retained possible (as long as it is started - // before being autoreleased). - // Care is taken not to retain self strongly in any of the blocks used in this implementation, so - // that the life of the instance is determined by this retain cycle. + // Create a retain cycle so that this instance lives until the RPC finishes + // (or is cancelled). This makes RPCs in which the call isn't externally + // retained possible (as long as it is started before being autoreleased). + // Care is taken not to retain self strongly in any of the blocks used in this + // implementation, so that the life of the instance is determined by this + // retain cycle. _retainSelf = self; - _responseWriteable = [[GRXConcurrentWriteable alloc] initWithWriteable:writeable]; + _responseWriteable = + [[GRXConcurrentWriteable alloc] initWithWriteable:writeable]; _wrappedCall = [[GRPCWrappedCall alloc] initWithHost:_host path:_path]; NSAssert(_wrappedCall, @"Error allocating RPC objects. Low memory?"); [self sendHeaders:_requestHeaders]; [self invokeCall]; + // TODO(jcanizales): Extract this logic somewhere common. - NSString *host = [NSURL URLWithString:[@"https://" stringByAppendingString:_host]].host; + NSString *host = + [NSURL URLWithString:[@"https://" stringByAppendingString:_host]].host; if (!host) { // TODO(jcanizales): Check this on init. - [NSException raise:NSInvalidArgumentException format:@"host of %@ is nil", _host]; + [NSException raise:NSInvalidArgumentException + format:@"host of %@ is nil", _host]; } __weak typeof(self) weakSelf = self; _connectivityMonitor = [GRPCConnectivityMonitor monitorWithHost:host]; - [_connectivityMonitor handleLossWithHandler:^{ + void (^handler)() = ^{ typeof(self) strongSelf = weakSelf; if (strongSelf) { - [strongSelf finishWithError:[NSError errorWithDomain:kGRPCErrorDomain - code:GRPCErrorCodeUnavailable - userInfo:@{NSLocalizedDescriptionKey: @"Connectivity lost."}]]; - [[GRPCHost hostWithAddress:strongSelf->_host] disconnect]; + [strongSelf + finishWithError:[NSError errorWithDomain:kGRPCErrorDomain + code:GRPCErrorCodeUnavailable + userInfo:@{ + NSLocalizedDescriptionKey : + @"Connectivity lost." + }]]; } - }]; + }; + [_connectivityMonitor handleLossWithHandler:handler + wifiStatusChangeHandler:^{ + }]; } - (void)setState:(GRXWriterState)newState { @synchronized(self) { // Manual transitions are only allowed from the started or paused states. - if (_state == GRXWriterStateNotStarted || _state == GRXWriterStateFinished) { + if (_state == GRXWriterStateNotStarted || + _state == GRXWriterStateFinished) { return; } diff --git a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h index fe3b8f39d1..c0bbf22c74 100644 --- a/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h +++ b/src/objective-c/GRPCClient/private/GRPCCompletionQueue.h @@ -34,18 +34,19 @@ #import <Foundation/Foundation.h> #include <grpc/grpc.h> -typedef void(^GRPCQueueCompletionHandler)(bool success); +typedef void (^GRPCQueueCompletionHandler)(bool success); /** - * This class lets one more easily use |grpc_completion_queue|. To use it, pass the value of the - * |unmanagedQueue| property of an instance of this class to |grpc_channel_create_call|. Then for - * every |grpc_call_*| method that accepts a tag, you can pass a block of type - * |GRPCQueueCompletionHandler| (remembering to cast it using |__bridge_retained|). The block is - * guaranteed to eventually be called, by a concurrent queue, and then released. Each such block is + * This class lets one more easily use |grpc_completion_queue|. To use it, pass + * the value of the |unmanagedQueue| property of an instance of this class to + * |grpc_channel_create_call|. Then for every |grpc_call_*| method that accepts + * a tag, you can pass a block of type |GRPCQueueCompletionHandler| (remembering + * to cast it using |__bridge_retained|). The block is guaranteed to eventually + * be called, by a concurrent queue, and then released. Each such block is * passed a |bool| that tells if the operation was successful. * - * Release the GRPCCompletionQueue object only after you are not going to pass any more blocks to - * the |grpc_call| that's using it. + * Release the GRPCCompletionQueue object only after you are not going to pass + * any more blocks to the |grpc_call| that's using it. */ @interface GRPCCompletionQueue : NSObject @property(nonatomic, readonly) grpc_completion_queue *unmanagedQueue; diff --git a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h index 2fae410331..941b596d2c 100644 --- a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h +++ b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.h @@ -45,7 +45,7 @@ */ #define GRPC_XMACRO_ITEM(methodName, FlagName) \ -@property(nonatomic, readonly) BOOL methodName; + @property(nonatomic, readonly) BOOL methodName; #include "GRPCReachabilityFlagNames.xmacro.h" #undef GRPC_XMACRO_ITEM @@ -53,7 +53,6 @@ @property(nonatomic, readonly) BOOL isHostReachable; @end - @interface GRPCConnectivityMonitor : NSObject + (nullable instancetype)monitorWithHost:(nonnull NSString *)hostName; @@ -61,17 +60,19 @@ - (nonnull instancetype)init NS_UNAVAILABLE; /** - * Queue on which callbacks will be dispatched. Default is the main queue. Set it before calling - * handleLossWithHandler:. + * Queue on which callbacks will be dispatched. Default is the main queue. Set + * it before calling handleLossWithHandler:. */ // TODO(jcanizales): Default to a serial background queue instead. @property(nonatomic, strong, null_resettable) dispatch_queue_t queue; /** - * Calls handler every time the connectivity to this instance's host is lost. If this instance is - * released before that happens, the handler won't be called. - * Only one handler is active at a time, so if this method is called again before the previous - * handler has been called, it might never be called at all (or yes, if it has already been queued). + * Calls handler every time the connectivity to this instance's host is lost. If + * this instance is released before that happens, the handler won't be called. + * Only one handler is active at a time, so if this method is called again + * before the previous handler has been called, it might never be called at all + * (or yes, if it has already been queued). */ -- (void)handleLossWithHandler:(nonnull void (^)())handler; +- (void)handleLossWithHandler:(nonnull void (^)())handler + wifiStatusChangeHandler:(nonnull void (^)())wifiStatusChangeHandler; @end diff --git a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m index b4061bd5ef..fec6391d39 100644 --- a/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m +++ b/src/objective-c/GRPCClient/private/GRPCConnectivityMonitor.m @@ -58,19 +58,20 @@ } */ -#define GRPC_XMACRO_ITEM(methodName, FlagName) \ -- (BOOL)methodName { \ - return !!(_flags & kSCNetworkReachabilityFlags ## FlagName); \ -} +#define GRPC_XMACRO_ITEM(methodName, FlagName) \ + -(BOOL)methodName { \ + return !!(_flags & kSCNetworkReachabilityFlags##FlagName); \ + } #include "GRPCReachabilityFlagNames.xmacro.h" #undef GRPC_XMACRO_ITEM - (BOOL)isHostReachable { - // Note: connectionOnDemand means it'll be reachable only if using the CFSocketStream API or APIs - // on top of it. - // connectionRequired means we can't tell until a connection is attempted (e.g. for VPN on - // demand). - return self.reachable && !self.interventionRequired && !self.connectionOnDemand; + // Note: connectionOnDemand means it'll be reachable only if using the + // CFSocketStream API or APIs on top of it. + // connectionRequired means we can't tell until a connection is attempted + // (e.g. for VPN on demand). + return self.reachable && !self.interventionRequired && + !self.connectionOnDemand; } - (NSString *)description { @@ -79,24 +80,26 @@ /* * For each flag, add its name to the array if it's ON. Example: - if (self.isCell) { - [activeOptions addObject:@"isCell"]; - } + if (self.isCell) { + [activeOptions addObject:@"isCell"]; + } */ -#define GRPC_XMACRO_ITEM(methodName, FlagName) \ - if (self.methodName) { \ - [activeOptions addObject:@#methodName]; \ - } -#include "GRPCReachabilityFlagNames.xmacro.h" -#undef GRPC_XMACRO_ITEM + #define GRPC_XMACRO_ITEM(methodName, FlagName) \ + if (self.methodName) { \ + [activeOptions addObject:@ #methodName]; \ + } + #include "GRPCReachabilityFlagNames.xmacro.h" + #undef GRPC_XMACRO_ITEM - return activeOptions.count == 0 ? @"(none)" : [activeOptions componentsJoinedByString:@", "]; + return activeOptions.count == 0 + ? @"(none)" + : [activeOptions componentsJoinedByString:@", "]; } - (BOOL)isEqual:(id)object { return [object isKindOfClass:[GRPCReachabilityFlags class]] && - _flags == ((GRPCReachabilityFlags *)object)->_flags; + _flags == ((GRPCReachabilityFlags *)object)->_flags; } - (NSUInteger)hash { @@ -106,29 +109,33 @@ #pragma mark Connectivity Monitor -// Assumes the third argument is a block that accepts a GRPCReachabilityFlags object, and passes the -// received ones to it. +// Assumes the third argument is a block that accepts a GRPCReachabilityFlags +// object, and passes the received ones to it. static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info) { - #pragma unused (target) - // This can be called many times with the same info. The info is retained by SCNetworkReachability - // while this function is being executed. - void (^handler)(GRPCReachabilityFlags *) = (__bridge void (^)(GRPCReachabilityFlags *))info; +#pragma unused(target) + // This can be called many times with the same info. The info is retained by + // SCNetworkReachability while this function is being executed. + void (^handler)(GRPCReachabilityFlags *) = + (__bridge void (^)(GRPCReachabilityFlags *))info; handler([[GRPCReachabilityFlags alloc] initWithFlags:flags]); } @implementation GRPCConnectivityMonitor { SCNetworkReachabilityRef _reachabilityRef; + GRPCReachabilityFlags *_previousReachabilityFlags; } -- (nullable instancetype)initWithReachability:(nullable SCNetworkReachabilityRef)reachability { +- (nullable instancetype)initWithReachability: + (nullable SCNetworkReachabilityRef)reachability { if (!reachability) { return nil; } if ((self = [super init])) { _reachabilityRef = CFRetain(reachability); _queue = dispatch_get_main_queue(); + _previousReachabilityFlags = nil; } return self; } @@ -142,33 +149,46 @@ static void PassFlagsToContextInfoBlock(SCNetworkReachabilityRef target, SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, hostName); - GRPCConnectivityMonitor *returnValue = [[self alloc] initWithReachability:reachability]; + GRPCConnectivityMonitor *returnValue = + [[self alloc] initWithReachability:reachability]; if (reachability) { CFRelease(reachability); } return returnValue; } -- (void)handleLossWithHandler:(void (^)())handler { +- (void)handleLossWithHandler:(void (^)())handler + wifiStatusChangeHandler:(nonnull void (^)())wifiStatusChangeHandler { + __weak typeof(self) weakSelf = self; [self startListeningWithHandler:^(GRPCReachabilityFlags *flags) { - if (!flags.isHostReachable) { - handler(); + typeof(self) strongSelf = weakSelf; + if (strongSelf) { + if (!flags.reachable) { + handler(); + } else if (strongSelf->_previousReachabilityFlags && + (flags.isWWAN ^ + strongSelf->_previousReachabilityFlags.isWWAN)) { + wifiStatusChangeHandler(); + } + strongSelf->_previousReachabilityFlags = flags; } }]; } - (void)startListeningWithHandler:(void (^)(GRPCReachabilityFlags *))handler { - // Copy to ensure the handler block is in the heap (and so can't be deallocated when this method - // returns). + // Copy to ensure the handler block is in the heap (and so can't be + // deallocated when this method returns). void (^copiedHandler)(GRPCReachabilityFlags *) = [handler copy]; SCNetworkReachabilityContext context = { - .version = 0, - .info = (__bridge void *)copiedHandler, - .retain = CFRetain, - .release = CFRelease, + .version = 0, + .info = (__bridge void *)copiedHandler, + .retain = CFRetain, + .release = CFRelease, }; - // The following will retain context.info, and release it when the callback is set to NULL. - SCNetworkReachabilitySetCallback(_reachabilityRef, PassFlagsToContextInfoBlock, &context); + // The following will retain context.info, and release it when the callback is + // set to NULL. + SCNetworkReachabilitySetCallback(_reachabilityRef, + PassFlagsToContextInfoBlock, &context); SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _queue); } diff --git a/src/objective-c/GRPCClient/private/GRPCHost.m b/src/objective-c/GRPCClient/private/GRPCHost.m index f8634b448e..0524472f53 100644 --- a/src/objective-c/GRPCClient/private/GRPCHost.m +++ b/src/objective-c/GRPCClient/private/GRPCHost.m @@ -33,9 +33,9 @@ #import "GRPCHost.h" +#import <GRPCClient/GRPCCall.h> #include <grpc/grpc.h> #include <grpc/grpc_security.h> -#import <GRPCClient/GRPCCall.h> #ifdef GRPC_COMPILE_WITH_CRONET #import <GRPCClient/GRPCCall+ChannelArg.h> #import <GRPCClient/GRPCCall+Cronet.h> @@ -43,18 +43,27 @@ #import "GRPCChannel.h" #import "GRPCCompletionQueue.h" +#import "GRPCConnectivityMonitor.h" #import "NSDictionary+GRPC.h" NS_ASSUME_NONNULL_BEGIN -// TODO(jcanizales): Generate the version in a standalone header, from templates. Like +// TODO(jcanizales): Generate the version in a standalone header, from +// templates. Like // templates/src/core/surface/version.c.template . #define GRPC_OBJC_VERSION_STRING @"1.0.0" static NSMutableDictionary *kHostCache; +// This connectivity monitor flushes the host cache when connectivity status +// changes or when connection switch between Wifi and Cellular data, so that a +// new call will use a new channel. Otherwise, a new call will still use the +// cached channel which is no longer available and will cause gRPC to hang. +static GRPCConnectivityMonitor *connectivityMonitor = nil; + @implementation GRPCHost { - // TODO(mlumish): Investigate whether caching channels with strong links is a good idea. + // TODO(mlumish): Investigate whether caching channels with strong links is a + // good idea. GRPCChannel *_channel; } @@ -74,11 +83,13 @@ static NSMutableDictionary *kHostCache; return nil; } - // To provide a default port, we try to interpret the address. If it's just a host name without - // scheme and without port, we'll use port 443. If it has a scheme, we pass it untouched to the C - // gRPC library. - // TODO(jcanizales): Add unit tests for the types of addresses we want to let pass untouched. - NSURL *hostURL = [NSURL URLWithString:[@"https://" stringByAppendingString:address]]; + // To provide a default port, we try to interpret the address. If it's just a + // host name without scheme and without port, we'll use port 443. If it has a + // scheme, we pass it untouched to the C gRPC library. + // TODO(jcanizales): Add unit tests for the types of addresses we want to let + // pass untouched. + NSURL *hostURL = + [NSURL URLWithString:[@"https://" stringByAppendingString:address]]; if (hostURL.host && !hostURL.port) { address = [hostURL.host stringByAppendingString:@":443"]; } @@ -88,6 +99,7 @@ static NSMutableDictionary *kHostCache; dispatch_once(&cacheInitialization, ^{ kHostCache = [NSMutableDictionary dictionary]; }); + @synchronized(kHostCache) { GRPCHost *cachedHost = kHostCache[address]; if (cachedHost) { @@ -99,6 +111,17 @@ static NSMutableDictionary *kHostCache; _secure = YES; kHostCache[address] = self; } + // Keep a single monitor to flush the cache if the connectivity status changes + // Thread safety guarded by @synchronized(kHostCache) + if (!connectivityMonitor) { + connectivityMonitor = + [GRPCConnectivityMonitor monitorWithHost:hostURL.host]; + void (^handler)() = ^{ + [GRPCHost flushChannelCache]; + }; + [connectivityMonitor handleLossWithHandler:handler + wifiStatusChangeHandler:handler]; + } } return self; } @@ -114,7 +137,7 @@ static NSMutableDictionary *kHostCache; } + (void)resetAllHostSettings { - @synchronized (kHostCache) { + @synchronized(kHostCache) { kHostCache = [NSMutableDictionary dictionary]; } } @@ -140,16 +163,19 @@ static NSMutableDictionary *kHostCache; static NSError *kDefaultRootsError; static dispatch_once_t loading; dispatch_once(&loading, ^{ - NSString *defaultPath = @"gRPCCertificates.bundle/roots"; // .pem - // Do not use NSBundle.mainBundle, as it's nil for tests of library projects. + NSString *defaultPath = @"gRPCCertificates.bundle/roots"; // .pem + // Do not use NSBundle.mainBundle, as it's nil for tests of library + // projects. NSBundle *bundle = [NSBundle bundleForClass:self.class]; NSString *path = [bundle pathForResource:defaultPath ofType:@"pem"]; NSError *error; - // Files in PEM format can have non-ASCII characters in their comments (e.g. for the name of the - // issuer). Load them as UTF8 and produce an ASCII equivalent. - NSString *contentInUTF8 = [NSString stringWithContentsOfFile:path - encoding:NSUTF8StringEncoding - error:&error]; + // Files in PEM format can have non-ASCII characters in their comments (e.g. + // for the name of the issuer). Load them as UTF8 and produce an ASCII + // equivalent. + NSString *contentInUTF8 = + [NSString stringWithContentsOfFile:path + encoding:NSUTF8StringEncoding + error:&error]; if (contentInUTF8 == nil) { kDefaultRootsError = error; return; @@ -161,17 +187,21 @@ static NSMutableDictionary *kHostCache; NSData *rootsASCII; if (pemRootCerts != nil) { rootsASCII = [pemRootCerts dataUsingEncoding:NSASCIIStringEncoding - allowLossyConversion:YES]; + allowLossyConversion:YES]; } else { if (kDefaultRootsASCII == nil) { if (errorPtr) { *errorPtr = kDefaultRootsError; } - NSAssert(kDefaultRootsASCII, @"Could not read gRPCCertificates.bundle/roots.pem. This file, " - "with the root certificates, is needed to establish secure (TLS) connections. " - "Because the file is distributed with the gRPC library, this error is usually a sign " - "that the library wasn't configured correctly for your project. Error: %@", - kDefaultRootsError); + NSAssert(kDefaultRootsASCII, + @"Could not read gRPCCertificates.bundle/roots.pem. This file, " + "with the root certificates, is needed to establish secure " + "(TLS) connections. " + "Because the file is distributed with the gRPC library, this " + "error is usually a sign " + "that the library wasn't configured correctly for your " + "project. Error: %@", + kDefaultRootsError); return NO; } rootsASCII = kDefaultRootsASCII; @@ -182,10 +212,12 @@ static NSMutableDictionary *kHostCache; creds = grpc_ssl_credentials_create(rootsASCII.bytes, NULL, NULL); } else { grpc_ssl_pem_key_cert_pair key_cert_pair; - NSData *privateKeyASCII = [pemPrivateKey dataUsingEncoding:NSASCIIStringEncoding - allowLossyConversion:YES]; - NSData *certChainASCII = [pemCertChain dataUsingEncoding:NSASCIIStringEncoding - allowLossyConversion:YES]; + NSData *privateKeyASCII = + [pemPrivateKey dataUsingEncoding:NSASCIIStringEncoding + allowLossyConversion:YES]; + NSData *certChainASCII = + [pemCertChain dataUsingEncoding:NSASCIIStringEncoding + allowLossyConversion:YES]; key_cert_pair.private_key = privateKeyASCII.bytes; key_cert_pair.cert_chain = certChainASCII.bytes; creds = grpc_ssl_credentials_create(rootsASCII.bytes, &key_cert_pair, NULL); @@ -205,7 +237,8 @@ static NSMutableDictionary *kHostCache; NSMutableDictionary *args = [NSMutableDictionary dictionary]; // TODO(jcanizales): Add OS and device information (see - // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#user-agents ). + // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#user-agents + // ). NSString *userAgent = @"grpc-objc/" GRPC_OBJC_VERSION_STRING; if (_userAgentPrefix) { userAgent = [_userAgentPrefix stringByAppendingFormat:@" %@", userAgent]; @@ -219,7 +252,7 @@ static NSMutableDictionary *kHostCache; if (_responseSizeLimitOverride) { args[@GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH] = _responseSizeLimitOverride; } - // Use 10000ms initial backoff time for correct behavior on bad/slow networks + // Use 10000ms initial backoff time for correct behavior on bad/slow networks args[@GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS] = @10000; return args; } @@ -230,31 +263,35 @@ static NSMutableDictionary *kHostCache; BOOL useCronet = [GRPCCall isUsingCronet]; #endif if (_secure) { - GRPCChannel *channel; - @synchronized(self) { - if (_channelCreds == nil) { - [self setTLSPEMRootCerts:nil withPrivateKey:nil withCertChain:nil error:nil]; - } + GRPCChannel *channel; + @synchronized(self) { + if (_channelCreds == nil) { + [self setTLSPEMRootCerts:nil + withPrivateKey:nil + withCertChain:nil + error:nil]; + } #ifdef GRPC_COMPILE_WITH_CRONET - if (useCronet) { - channel = [GRPCChannel secureCronetChannelWithHost:_address - channelArgs:args]; - } else + if (useCronet) { + channel = + [GRPCChannel secureCronetChannelWithHost:_address channelArgs:args]; + } else #endif - { - channel = [GRPCChannel secureChannelWithHost:_address - credentials:_channelCreds - channelArgs:args]; - } + { + channel = [GRPCChannel secureChannelWithHost:_address + credentials:_channelCreds + channelArgs:args]; } - return channel; + } + return channel; } else { return [GRPCChannel insecureChannelWithHost:_address channelArgs:args]; } } - (NSString *)hostName { - // TODO(jcanizales): Default to nil instead of _address when Issue #2635 is clarified. + // TODO(jcanizales): Default to nil instead of _address when Issue #2635 is + // clarified. return _hostNameOverride ?: _address; } diff --git a/src/objective-c/GRPCClient/private/GRPCReachabilityFlagNames.xmacro.h b/src/objective-c/GRPCClient/private/GRPCReachabilityFlagNames.xmacro.h index 4b92504b55..f5b9e7e64c 100644 --- a/src/objective-c/GRPCClient/private/GRPCReachabilityFlagNames.xmacro.h +++ b/src/objective-c/GRPCClient/private/GRPCReachabilityFlagNames.xmacro.h @@ -55,7 +55,7 @@ #endif #if TARGET_OS_IPHONE -GRPC_XMACRO_ITEM(isCell, IsWWAN) +GRPC_XMACRO_ITEM(isWWAN, IsWWAN) #endif GRPC_XMACRO_ITEM(reachable, Reachable) GRPC_XMACRO_ITEM(transientConnection, TransientConnection) diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index c6a03c145e..58c6032e80 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -38,9 +38,10 @@ #import "NSDictionary+GRPC.h" // Used by the setter. -static void CheckIsNonNilASCII(NSString *name, NSString* value) { +static void CheckIsNonNilASCII(NSString *name, NSString *value) { if (!value) { - [NSException raise:NSInvalidArgumentException format:@"%@ cannot be nil", name]; + [NSException raise:NSInvalidArgumentException + format:@"%@ cannot be nil", name]; } if (![value canBeConvertedToEncoding:NSASCIIStringEncoding]) { [NSException raise:NSInvalidArgumentException @@ -52,15 +53,20 @@ static void CheckIsNonNilASCII(NSString *name, NSString* value) { static void CheckKeyValuePairIsValid(NSString *key, id value) { if ([key hasSuffix:@"-bin"]) { if (![value isKindOfClass:NSData.class]) { - [NSException raise:NSInvalidArgumentException - format:@"Expected NSData value for header %@ ending in \"-bin\", " - @"instead got %@", key, value]; + [NSException + raise:NSInvalidArgumentException + format:@"Expected NSData value for header %@ ending in \"-bin\", " + @"instead got %@", + key, value]; } } else { if (![value isKindOfClass:NSString.class]) { - [NSException raise:NSInvalidArgumentException - format:@"Expected NSString value for header %@ not ending in \"-bin\", " - @"instead got %@", key, value]; + [NSException + raise:NSInvalidArgumentException + format: + @"Expected NSString value for header %@ not ending in \"-bin\", " + @"instead got %@", + key, value]; } CheckIsNonNilASCII(@"Text header value", (NSString *)value); } @@ -68,9 +74,10 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { @implementation GRPCRequestHeaders { __weak GRPCCall *_call; - // The NSMutableDictionary superclass doesn't hold any storage (so that people can implement their - // own in subclasses). As that's not the reason we're subclassing, we just delegate storage to the - // default NSMutableDictionary subclass returned by the cluster (e.g. __NSDictionaryM on iOS 9). + // The NSMutableDictionary superclass doesn't hold any storage (so that people + // can implement their own in subclasses). As that's not the reason we're + // subclassing, we just delegate storage to the default NSMutableDictionary + // subclass returned by the cluster (e.g. __NSDictionaryM on iOS 9). NSMutableDictionary *_delegate; } @@ -91,7 +98,8 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { } // Designated initializer -- (instancetype)initWithCall:(GRPCCall *)call storage:(NSMutableDictionary *)storage { +- (instancetype)initWithCall:(GRPCCall *)call + storage:(NSMutableDictionary *)storage { // TODO(jcanizales): Throw if call or storage are nil. if ((self = [super init])) { _call = call; @@ -100,9 +108,10 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { return self; } -- (instancetype)initWithObjects:(const id _Nonnull __unsafe_unretained *)objects - forKeys:(const id<NSCopying> _Nonnull __unsafe_unretained *)keys - count:(NSUInteger)cnt { +- (instancetype) +initWithObjects:(const id _Nonnull __unsafe_unretained *)objects + forKeys:(const id<NSCopying> _Nonnull __unsafe_unretained *)keys + count:(NSUInteger)cnt { return [self init]; } @@ -134,7 +143,7 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { return _delegate.count; } -- (NSEnumerator * _Nonnull)keyEnumerator { +- (NSEnumerator *_Nonnull)keyEnumerator { return [_delegate keyEnumerator]; } diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index 627b6aa86d..bbda9f2f64 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -34,27 +34,27 @@ #import "GRPCWrappedCall.h" #import <Foundation/Foundation.h> -#include <grpc/grpc.h> #include <grpc/byte_buffer.h> +#include <grpc/grpc.h> #include <grpc/support/alloc.h> #import "GRPCCompletionQueue.h" #import "GRPCHost.h" -#import "NSDictionary+GRPC.h" #import "NSData+GRPC.h" +#import "NSDictionary+GRPC.h" #import "NSError+GRPC.h" @implementation GRPCOperation { -@protected - // Most operation subclasses don't set any flags in the grpc_op, and rely on the flag member being - // initialized to zero. + @protected + // Most operation subclasses don't set any flags in the grpc_op, and rely on + // the flag member being initialized to zero. grpc_op _op; - void(^_handler)(); + void (^_handler)(); } - (void)finish { if (_handler) { - void(^handler)() = _handler; + void (^handler)() = _handler; _handler = nil; handler(); } @@ -101,7 +101,8 @@ - (instancetype)initWithMessage:(NSData *)message handler:(void (^)())handler { if (!message) { - [NSException raise:NSInvalidArgumentException format:@"message cannot be nil"]; + [NSException raise:NSInvalidArgumentException + format:@"message cannot be nil"]; } if (self = [super init]) { _op.op = GRPC_OP_SEND_MESSAGE; @@ -137,11 +138,11 @@ grpc_metadata_array _headers; } -- (instancetype) init { +- (instancetype)init { return [self initWithHandler:nil]; } -- (instancetype) initWithHandler:(void (^)(NSDictionary *))handler { +- (instancetype)initWithHandler:(void (^)(NSDictionary *))handler { if (self = [super init]) { _op.op = GRPC_OP_RECV_INITIAL_METADATA; grpc_metadata_array_init(&_headers); @@ -152,7 +153,7 @@ _handler = ^{ __strong typeof(self) strongSelf = weakSelf; NSDictionary *metadata = [NSDictionary - grpc_dictionaryFromMetadataArray:strongSelf->_headers]; + grpc_dictionaryFromMetadataArray:strongSelf->_headers]; handler(metadata); }; } @@ -166,7 +167,7 @@ @end -@implementation GRPCOpRecvMessage{ +@implementation GRPCOpRecvMessage { grpc_byte_buffer *_receivedMessage; } @@ -192,18 +193,18 @@ @end -@implementation GRPCOpRecvStatus{ +@implementation GRPCOpRecvStatus { grpc_status_code _statusCode; char *_details; size_t _detailsCapacity; grpc_metadata_array _trailers; } -- (instancetype) init { +- (instancetype)init { return [self initWithHandler:nil]; } -- (instancetype) initWithHandler:(void (^)(NSError *, NSDictionary *))handler { +- (instancetype)initWithHandler:(void (^)(NSError *, NSDictionary *))handler { if (self = [super init]) { _op.op = GRPC_OP_RECV_STATUS_ON_CLIENT; _op.data.recv_status_on_client.status = &_statusCode; @@ -216,10 +217,11 @@ __weak typeof(self) weakSelf = self; _handler = ^{ __strong typeof(self) strongSelf = weakSelf; - NSError *error = [NSError grpc_errorFromStatusCode:strongSelf->_statusCode - details:strongSelf->_details]; + NSError *error = + [NSError grpc_errorFromStatusCode:strongSelf->_statusCode + details:strongSelf->_details]; NSDictionary *trailers = [NSDictionary - grpc_dictionaryFromMetadataArray:strongSelf->_trailers]; + grpc_dictionaryFromMetadataArray:strongSelf->_trailers]; handler(error, trailers); }; } @@ -245,20 +247,21 @@ return [self initWithHost:nil path:nil]; } -- (instancetype)initWithHost:(NSString *)host - path:(NSString *)path { +- (instancetype)initWithHost:(NSString *)host path:(NSString *)path { if (!path || !host) { [NSException raise:NSInvalidArgumentException format:@"path and host cannot be nil."]; } if (self = [super init]) { - // Each completion queue consumes one thread. There's a trade to be made between creating and - // consuming too many threads and having contention of multiple calls in a single completion - // queue. Currently we use a singleton queue. + // Each completion queue consumes one thread. There's a trade to be made + // between creating and consuming too many threads and having contention of + // multiple calls in a single completion queue. Currently we use a singleton + // queue. _queue = [GRPCCompletionQueue completionQueue]; - _call = [[GRPCHost hostWithAddress:host] unmanagedCallWithPath:path completionQueue:_queue]; + _call = [[GRPCHost hostWithAddress:host] unmanagedCallWithPath:path + completionQueue:_queue]; if (_call == NULL) { return nil; } @@ -270,32 +273,35 @@ [self startBatchWithOperations:operations errorHandler:nil]; } -- (void)startBatchWithOperations:(NSArray *)operations errorHandler:(void (^)())errorHandler { +- (void)startBatchWithOperations:(NSArray *)operations + errorHandler:(void (^)())errorHandler { size_t nops = operations.count; grpc_op *ops_array = gpr_malloc(nops * sizeof(grpc_op)); size_t i = 0; for (GRPCOperation *operation in operations) { ops_array[i++] = operation.op; } - grpc_call_error error = grpc_call_start_batch(_call, ops_array, nops, - (__bridge_retained void *)(^(bool success){ - if (!success) { - if (errorHandler) { - errorHandler(); - } else { - return; - } - } - for (GRPCOperation *operation in operations) { - [operation finish]; - } - }), NULL); + grpc_call_error error = grpc_call_start_batch( + _call, ops_array, nops, (__bridge_retained void *)(^(bool success) { + if (!success) { + if (errorHandler) { + errorHandler(); + } else { + return; + } + } + for (GRPCOperation *operation in operations) { + [operation finish]; + } + }), + NULL); gpr_free(ops_array); if (error != GRPC_CALL_OK) { [NSException raise:NSInternalInconsistencyException - format:@"A precondition for calling grpc_call_start_batch wasn't met. Error %i", - error]; + format:@"A precondition for calling grpc_call_start_batch " + @"wasn't met. Error %i", + error]; } } diff --git a/src/objective-c/GRPCClient/private/NSError+GRPC.h b/src/objective-c/GRPCClient/private/NSError+GRPC.h index e0c1efc1f9..a9a321470c 100644 --- a/src/objective-c/GRPCClient/private/NSError+GRPC.h +++ b/src/objective-c/GRPCClient/private/NSError+GRPC.h @@ -36,8 +36,9 @@ @interface NSError (GRPC) /** - * Returns nil if the status code is OK. Otherwise, a NSError whose code is one of |GRPCErrorCode| - * and whose domain is |kGRPCErrorDomain|. + * Returns nil if the status code is OK. Otherwise, a NSError whose code is one + * of |GRPCErrorCode| and whose domain is |kGRPCErrorDomain|. */ -+ (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode details:(char *)details; ++ (instancetype)grpc_errorFromStatusCode:(grpc_status_code)statusCode + details:(char *)details; @end diff --git a/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj b/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj index 8d34b26c4f..ab7159cda2 100644 --- a/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj +++ b/src/objective-c/examples/Sample/Sample.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -129,7 +129,7 @@ 6369A2621A9322E20015FC5C /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0730; ORGANIZATIONNAME = gRPC; TargetAttributes = { 6369A2691A9322E20015FC5C = { @@ -138,7 +138,7 @@ }; }; buildConfigurationList = 6369A2651A9322E20015FC5C /* Build configuration list for PBXProject "Sample" */; - compatibilityVersion = "Xcode 8.0"; + compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( @@ -253,10 +253,8 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -265,7 +263,6 @@ ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -299,10 +296,8 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -310,7 +305,6 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; diff --git a/src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme b/src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme index 881474b111..d399e22e46 100644 --- a/src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme +++ b/src/objective-c/examples/Sample/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0800" + LastUpgradeVersion = "0730" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj index 55b392e778..afc3da7116 100644 --- a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj +++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -123,17 +123,16 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0730; ORGANIZATIONNAME = gRPC; TargetAttributes = { 633BFFC11B950B210007E424 = { CreatedOnToolsVersion = 6.4; - LastSwiftMigration = 0800; }; }; }; buildConfigurationList = 633BFFBD1B950B210007E424 /* Build configuration list for PBXProject "SwiftSample" */; - compatibilityVersion = "Xcode 8.0"; + compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( @@ -247,10 +246,8 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -295,10 +292,8 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -317,7 +312,6 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.4; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -333,7 +327,6 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.grpc.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ""; }; name = Debug; @@ -348,7 +341,6 @@ PRODUCT_BUNDLE_IDENTIFIER = "io.grpc.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_VERSION = 3.0; USER_HEADER_SEARCH_PATHS = ""; }; name = Release; @@ -363,7 +355,7 @@ 633BFFE01B950B210007E424 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Debug; + defaultConfigurationName = Release; }; 633BFFE11B950B210007E424 /* Build configuration list for PBXNativeTarget "SwiftSample" */ = { isa = XCConfigurationList; @@ -372,7 +364,7 @@ 633BFFE31B950B210007E424 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Debug; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/xcshareddata/xcschemes/SwiftSample.xcscheme b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/xcshareddata/xcschemes/SwiftSample.xcscheme index 87bca5ec68..bba6a02b2b 100644 --- a/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/xcshareddata/xcschemes/SwiftSample.xcscheme +++ b/src/objective-c/examples/SwiftSample/SwiftSample.xcodeproj/xcshareddata/xcschemes/SwiftSample.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0800" + LastUpgradeVersion = "0730" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/src/objective-c/examples/SwiftSample/ViewController.swift b/src/objective-c/examples/SwiftSample/ViewController.swift index 6b25e71f2d..66d4fa9412 100644 --- a/src/objective-c/examples/SwiftSample/ViewController.swift +++ b/src/objective-c/examples/SwiftSample/ViewController.swift @@ -51,7 +51,7 @@ class ViewController: UIViewController { // Example gRPC call using a generated proto client library: let service = RMTTestService(host: RemoteHost) - service.unaryCall(with: request) { response, error in + service.unaryCallWithRequest(request) { response, error in if let response = response { NSLog("1. Finished successfully with response:\n\(response)") } else { @@ -63,39 +63,40 @@ class ViewController: UIViewController { // Same but manipulating headers: var RPC : GRPCProtoCall! // Needed to convince Swift to capture by reference (__block) - RPC = service.rpcToUnaryCall(with: request) { response, error in + RPC = service.RPCToUnaryCallWithRequest(request) { response, error in if let response = response { NSLog("2. Finished successfully with response:\n\(response)") } else { NSLog("2. Finished with error: \(error!)") } - NSLog("2. Response headers: \(RPC.responseHeaders!)") - NSLog("2. Response trailers: \(RPC.responseTrailers!)") + NSLog("2. Response headers: \(RPC.responseHeaders)") + NSLog("2. Response trailers: \(RPC.responseTrailers)") } - RPC.requestHeaders["My-Header"] = "My value"; + // TODO(jcanizales): Revert to using subscript syntax once XCode 8 is released. + RPC.requestHeaders.setObject("My value", forKey: "My-Header") RPC.start() // Same example call using the generic gRPC client library: - let method = GRPCProtoMethod(package: "grpc.testing", service: "TestService", method: "UnaryCall")! + let method = GRPCProtoMethod(package: "grpc.testing", service: "TestService", method: "UnaryCall") let requestsWriter = GRXWriter(value: request.data()) - let call = GRPCCall(host: RemoteHost, path: method.httpPath, requestsWriter: requestsWriter)! + let call = GRPCCall(host: RemoteHost, path: method.HTTPPath, requestsWriter: requestsWriter) - call.requestHeaders["My-Header"] = "My value"; + call.requestHeaders.setObject("My value", forKey: "My-Header") - call.start(with: GRXWriteable { response, error in - if let response = response as? Data { + call.startWithWriteable(GRXWriteable { response, error in + if let response = response as? NSData { NSLog("3. Received response:\n\(try! RMTSimpleResponse(data: response))") } else { NSLog("3. Finished with error: \(error!)") } - NSLog("3. Response headers: \(call.responseHeaders!)") - NSLog("3. Response trailers: \(call.responseTrailers!)") + NSLog("3. Response headers: \(call.responseHeaders)") + NSLog("3. Response trailers: \(call.responseTrailers)") }) } } diff --git a/src/objective-c/tests/Connectivity/ConnectivityTestingApp.xcodeproj/project.pbxproj b/src/objective-c/tests/Connectivity/ConnectivityTestingApp.xcodeproj/project.pbxproj index 2a9466c03f..3f26c98564 100644 --- a/src/objective-c/tests/Connectivity/ConnectivityTestingApp.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Connectivity/ConnectivityTestingApp.xcodeproj/project.pbxproj @@ -156,7 +156,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; 5347BF6C41E7888C1C05CD88 /* [CP] Copy Pods Resources */ = { diff --git a/src/objective-c/tests/Connectivity/Podfile b/src/objective-c/tests/Connectivity/Podfile index f9224d9e4e..27ff935c54 100644 --- a/src/objective-c/tests/Connectivity/Podfile +++ b/src/objective-c/tests/Connectivity/Podfile @@ -1,10 +1,32 @@ install! 'cocoapods', :deterministic_uuids => false +platform :ios, '8.0' # Location of gRPC's repo root relative to this file. GRPC_LOCAL_SRC = '../../../..' target 'ConnectivityTestingApp' do pod 'gRPC', :path => GRPC_LOCAL_SRC + pod 'gRPC-Core', :path => GRPC_LOCAL_SRC + pod 'gRPC-ProtoRPC', :path => GRPC_LOCAL_SRC + pod 'gRPC-RxLibrary', :path => GRPC_LOCAL_SRC pod 'Protobuf', :path => "#{GRPC_LOCAL_SRC}/third_party/protobuf" pod 'BoringSSL', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c" end + +pre_install do |installer| + # This is the gRPC-Core podspec object, as initialized by its podspec file. + grpc_core_spec = installer.pod_targets.find{|t| t.name == 'gRPC-Core'}.root_spec + + # Copied from gRPC-Core.podspec, except for the adjusted src_root: + src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}" + grpc_core_spec.pod_target_xcconfig = { + 'GRPC_SRC_ROOT' => src_root, + 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', + 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', + # If we don't set these two settings, `include/grpc/support/time.h` and + # `src/core/lib/support/string.h` shadow the system `<time.h>` and `<string.h>`, breaking the + # build. + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + } +end diff --git a/src/objective-c/tests/Info.plist b/src/objective-c/tests/Info.plist index ba72822e87..fbeeb96ba6 100644 --- a/src/objective-c/tests/Info.plist +++ b/src/objective-c/tests/Info.plist @@ -7,7 +7,7 @@ <key>CFBundleExecutable</key> <string>$(EXECUTABLE_NAME)</string> <key>CFBundleIdentifier</key> - <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> + <string>gRPC.$(PRODUCT_NAME:rfc1034identifier)</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 8dccbf0f72..c4a6567ae0 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ @@ -535,7 +535,7 @@ 635697BF1B14FC11007A7283 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0630; ORGANIZATIONNAME = gRPC; TargetAttributes = { 5E8A5DA31D3840B4000F8BC4 = { @@ -565,7 +565,7 @@ }; }; buildConfigurationList = 635697C21B14FC11007A7283 /* Build configuration list for PBXProject "Tests" */; - compatibilityVersion = "Xcode 8.0"; + compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( @@ -1151,10 +1151,8 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; @@ -1207,7 +1205,6 @@ ); INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "gRPC.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Cronet; @@ -1365,7 +1362,6 @@ ); INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "gRPC.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -1380,7 +1376,6 @@ ); INFOPLIST_FILE = Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "gRPC.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -1398,16 +1393,13 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -1444,10 +1436,8 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme index 740a1c0672..d1d616c4cf 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/AllTests.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0800" + LastUpgradeVersion = "0630" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme index 538149c4ee..a1da2e0c97 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0800" + LastUpgradeVersion = "0730" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme index acf6f38ef2..ce358bf69f 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalCleartext.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0800" + LastUpgradeVersion = "0700" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme index 95cc317093..f268da1fb0 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsLocalSSL.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0800" + LastUpgradeVersion = "0700" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme index 8504845da4..186d7208e0 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemote.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0800" + LastUpgradeVersion = "0700" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemoteWithCronet.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemoteWithCronet.xcscheme index 08955e1e03..1d211115f7 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemoteWithCronet.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/InteropTestsRemoteWithCronet.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0800" + LastUpgradeVersion = "0730" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme index b5672fce02..3abc1d42e4 100644 --- a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/RxLibraryUnitTests.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - LastUpgradeVersion = "0800" + LastUpgradeVersion = "0700" version = "1.3"> <BuildAction parallelizeBuildables = "YES" diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php index 72012a49d0..3d62e86ab0 100755 --- a/src/php/tests/interop/interop_client.php +++ b/src/php/tests/interop/interop_client.php @@ -500,6 +500,16 @@ function statusCodeAndMessage($stub) $status->details); } +# NOTE: the stub input to this function is from UnimplementedService +function unimplementedService($stub) +{ + $call = $stub->UnimplementedCall(new grpc\testing\EmptyMessage()); + list($result, $status) = $call->wait(); + hardAssert($status->code === Grpc\STATUS_UNIMPLEMENTED, + 'Received unexpected status code'); +} + +# NOTE: the stub input to this function is from TestService function unimplementedMethod($stub) { $call = $stub->UnimplementedCall(new grpc\testing\EmptyMessage()); @@ -592,7 +602,7 @@ function _makeStub($args) $opts['update_metadata'] = $update_metadata; } - if ($test_case === 'unimplemented_method') { + if ($test_case === 'unimplemented_service') { $stub = new grpc\testing\UnimplementedServiceClient($server_address, $opts); } else { @@ -645,6 +655,9 @@ function interop_main($args, $stub = false) case 'status_code_and_message': statusCodeAndMessage($stub); break; + case 'unimplemented_service': + unimplementedService($stub); + break; case 'unimplemented_method': unimplementedMethod($stub); break; diff --git a/src/proto/grpc/testing/duplicate/echo_duplicate.proto b/src/proto/grpc/testing/duplicate/echo_duplicate.proto index 94130ea767..97fdbc4fd3 100644 --- a/src/proto/grpc/testing/duplicate/echo_duplicate.proto +++ b/src/proto/grpc/testing/duplicate/echo_duplicate.proto @@ -38,4 +38,5 @@ package grpc.testing.duplicate; service EchoTestService { rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse); + rpc ResponseStream(EchoRequest) returns (stream EchoResponse); } diff --git a/src/proto/grpc/testing/test.proto b/src/proto/grpc/testing/test.proto index d6ef58ab12..2b0dcde5a9 100644 --- a/src/proto/grpc/testing/test.proto +++ b/src/proto/grpc/testing/test.proto @@ -77,7 +77,7 @@ service TestService { // The test server will not implement this method. It will be used // to test the behavior when clients call unimplemented methods. - rpc UnimplementedMethod(grpc.testing.Empty) returns (grpc.testing.Empty); + rpc UnimplementedCall(grpc.testing.Empty) returns (grpc.testing.Empty); } // A simple service NOT implemented at servers so clients can test for diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index a40edfb090..7ffe0f558a 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -98,6 +98,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/combiner.c', 'src/core/lib/iomgr/endpoint.c', 'src/core/lib/iomgr/endpoint_pair_posix.c', + 'src/core/lib/iomgr/endpoint_pair_uv.c', 'src/core/lib/iomgr/endpoint_pair_windows.c', 'src/core/lib/iomgr/error.c', 'src/core/lib/iomgr/ev_epoll_linux.c', @@ -109,28 +110,38 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/iocp_windows.c', 'src/core/lib/iomgr/iomgr.c', 'src/core/lib/iomgr/iomgr_posix.c', + 'src/core/lib/iomgr/iomgr_uv.c', 'src/core/lib/iomgr/iomgr_windows.c', 'src/core/lib/iomgr/load_file.c', 'src/core/lib/iomgr/network_status_tracker.c', 'src/core/lib/iomgr/polling_entity.c', + 'src/core/lib/iomgr/pollset_set_uv.c', 'src/core/lib/iomgr/pollset_set_windows.c', + 'src/core/lib/iomgr/pollset_uv.c', 'src/core/lib/iomgr/pollset_windows.c', 'src/core/lib/iomgr/resolve_address_posix.c', + 'src/core/lib/iomgr/resolve_address_uv.c', 'src/core/lib/iomgr/resolve_address_windows.c', 'src/core/lib/iomgr/sockaddr_utils.c', 'src/core/lib/iomgr/socket_utils_common_posix.c', 'src/core/lib/iomgr/socket_utils_linux.c', 'src/core/lib/iomgr/socket_utils_posix.c', + 'src/core/lib/iomgr/socket_utils_uv.c', + 'src/core/lib/iomgr/socket_utils_windows.c', 'src/core/lib/iomgr/socket_windows.c', 'src/core/lib/iomgr/tcp_client_posix.c', + 'src/core/lib/iomgr/tcp_client_uv.c', 'src/core/lib/iomgr/tcp_client_windows.c', 'src/core/lib/iomgr/tcp_posix.c', 'src/core/lib/iomgr/tcp_server_posix.c', + 'src/core/lib/iomgr/tcp_server_uv.c', 'src/core/lib/iomgr/tcp_server_windows.c', + 'src/core/lib/iomgr/tcp_uv.c', 'src/core/lib/iomgr/tcp_windows.c', 'src/core/lib/iomgr/time_averaged_stats.c', - 'src/core/lib/iomgr/timer.c', + 'src/core/lib/iomgr/timer_generic.c', 'src/core/lib/iomgr/timer_heap.c', + 'src/core/lib/iomgr/timer_uv.c', 'src/core/lib/iomgr/udp_server.c', 'src/core/lib/iomgr/unix_sockets_posix.c', 'src/core/lib/iomgr/unix_sockets_posix_noop.c', @@ -139,6 +150,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/iomgr/wakeup_fd_nospecial.c', 'src/core/lib/iomgr/wakeup_fd_pipe.c', 'src/core/lib/iomgr/wakeup_fd_posix.c', + 'src/core/lib/iomgr/workqueue_uv.c', 'src/core/lib/iomgr/workqueue_windows.c', 'src/core/lib/json/json.c', 'src/core/lib/json/json_reader.c', @@ -167,6 +179,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/transport/mdstr_hash_table.c', 'src/core/lib/transport/metadata.c', 'src/core/lib/transport/metadata_batch.c', + 'src/core/lib/transport/method_config.c', 'src/core/lib/transport/static_metadata.c', 'src/core/lib/transport/timeout_encoding.c', 'src/core/lib/transport/transport.c', @@ -200,8 +213,7 @@ CORE_SOURCE_FILES = [ 'src/core/lib/security/credentials/credentials.c', 'src/core/lib/security/credentials/credentials_metadata.c', 'src/core/lib/security/credentials/fake/fake_credentials.c', - 'src/core/lib/security/credentials/google_default/credentials_posix.c', - 'src/core/lib/security/credentials/google_default/credentials_windows.c', + 'src/core/lib/security/credentials/google_default/credentials_generic.c', 'src/core/lib/security/credentials/google_default/google_default_credentials.c', 'src/core/lib/security/credentials/iam/iam_credentials.c', 'src/core/lib/security/credentials/jwt/json_token.c', @@ -223,26 +235,24 @@ CORE_SOURCE_FILES = [ 'src/core/lib/tsi/ssl_transport_security.c', 'src/core/lib/tsi/transport_security.c', 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.c', - 'src/core/ext/client_config/channel_connectivity.c', - 'src/core/ext/client_config/client_channel.c', - 'src/core/ext/client_config/client_channel_factory.c', - 'src/core/ext/client_config/client_config_plugin.c', - 'src/core/ext/client_config/connector.c', - 'src/core/ext/client_config/default_initial_connect_string.c', - 'src/core/ext/client_config/http_connect_handshaker.c', - 'src/core/ext/client_config/initial_connect_string.c', - 'src/core/ext/client_config/lb_policy.c', - 'src/core/ext/client_config/lb_policy_factory.c', - 'src/core/ext/client_config/lb_policy_registry.c', - 'src/core/ext/client_config/method_config.c', - 'src/core/ext/client_config/parse_address.c', - 'src/core/ext/client_config/resolver.c', - 'src/core/ext/client_config/resolver_factory.c', - 'src/core/ext/client_config/resolver_registry.c', - 'src/core/ext/client_config/resolver_result.c', - 'src/core/ext/client_config/subchannel.c', - 'src/core/ext/client_config/subchannel_index.c', - 'src/core/ext/client_config/uri_parser.c', + 'src/core/ext/client_channel/channel_connectivity.c', + 'src/core/ext/client_channel/client_channel.c', + 'src/core/ext/client_channel/client_channel_factory.c', + 'src/core/ext/client_channel/client_channel_plugin.c', + 'src/core/ext/client_channel/connector.c', + 'src/core/ext/client_channel/default_initial_connect_string.c', + 'src/core/ext/client_channel/http_connect_handshaker.c', + 'src/core/ext/client_channel/initial_connect_string.c', + 'src/core/ext/client_channel/lb_policy.c', + 'src/core/ext/client_channel/lb_policy_factory.c', + 'src/core/ext/client_channel/lb_policy_registry.c', + 'src/core/ext/client_channel/parse_address.c', + 'src/core/ext/client_channel/resolver.c', + 'src/core/ext/client_channel/resolver_factory.c', + 'src/core/ext/client_channel/resolver_registry.c', + 'src/core/ext/client_channel/subchannel.c', + 'src/core/ext/client_channel/subchannel_index.c', + 'src/core/ext/client_channel/uri_parser.c', 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c', 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c', 'src/core/ext/transport/chttp2/client/insecure/channel_create.c', diff --git a/src/python/grpcio/support.py b/src/python/grpcio/support.py index 7730374df0..f363f5fdc5 100644 --- a/src/python/grpcio/support.py +++ b/src/python/grpcio/support.py @@ -45,7 +45,10 @@ int main(int argc, char **argv) { return 0; } """ C_PYTHON_DEV_ERROR_MESSAGE = """ Could not find <Python.h>. This could mean the following: - * You're on Ubuntu and haven't `apt-get install`ed `python-dev`. + * You're on Ubuntu and haven't run `apt-get install python-dev`. + * You're on RHEL/Fedora and haven't run `yum install python-devel` or + `dnf install python-devel` (make sure you also have redhat-rpm-config + installed) * You're on Mac OS X and the usual Python framework was somehow corrupted (check your environment variables or try re-installing?) * You're on Windows and your Python installation was somehow corrupted diff --git a/src/python/grpcio_tests/tests/interop/client.py b/src/python/grpcio_tests/tests/interop/client.py index 9d61d18975..4fbf58f7d9 100644 --- a/src/python/grpcio_tests/tests/interop/client.py +++ b/src/python/grpcio_tests/tests/interop/client.py @@ -106,7 +106,10 @@ def _stub(args): (('grpc.ssl_target_name_override', args.server_host_override,),)) else: channel = grpc.insecure_channel(target) - return test_pb2.TestServiceStub(channel) + if args.test_case == "unimplemented_service": + return test_pb2.UnimplementedServiceStub(channel) + else: + return test_pb2.TestServiceStub(channel) def _test_case_from_arg(test_case_arg): diff --git a/src/python/grpcio_tests/tests/interop/methods.py b/src/python/grpcio_tests/tests/interop/methods.py index 7edd75c56c..52e56f3502 100644 --- a/src/python/grpcio_tests/tests/interop/methods.py +++ b/src/python/grpcio_tests/tests/interop/methods.py @@ -44,25 +44,43 @@ from src.proto.grpc.testing import empty_pb2 from src.proto.grpc.testing import messages_pb2 from src.proto.grpc.testing import test_pb2 +_INITIAL_METADATA_KEY = "x-grpc-test-echo-initial" +_TRAILING_METADATA_KEY = "x-grpc-test-echo-trailing-bin" + +def _maybe_echo_metadata(servicer_context): + """Copies metadata from request to response if it is present.""" + invocation_metadata = dict(servicer_context.invocation_metadata()) + if _INITIAL_METADATA_KEY in invocation_metadata: + initial_metadatum = ( + _INITIAL_METADATA_KEY, invocation_metadata[_INITIAL_METADATA_KEY]) + servicer_context.send_initial_metadata((initial_metadatum,)) + if _TRAILING_METADATA_KEY in invocation_metadata: + trailing_metadatum = ( + _TRAILING_METADATA_KEY, invocation_metadata[_TRAILING_METADATA_KEY]) + servicer_context.set_trailing_metadata((trailing_metadatum,)) + +def _maybe_echo_status_and_message(request, servicer_context): + """Sets the response context code and details if the request asks for them""" + if request.HasField('response_status'): + servicer_context.set_code(request.response_status.code) + servicer_context.set_details(request.response_status.message) class TestService(test_pb2.TestServiceServicer): def EmptyCall(self, request, context): + _maybe_echo_metadata(context) return empty_pb2.Empty() def UnaryCall(self, request, context): - if request.HasField('response_status'): - context.set_code(request.response_status.code) - context.set_details(request.response_status.message) + _maybe_echo_metadata(context) + _maybe_echo_status_and_message(request, context) return messages_pb2.SimpleResponse( payload=messages_pb2.Payload( type=messages_pb2.COMPRESSABLE, body=b'\x00' * request.response_size)) def StreamingOutputCall(self, request, context): - if request.HasField('response_status'): - context.set_code(request.response_status.code) - context.set_details(request.response_status.message) + _maybe_echo_status_and_message(request, context) for response_parameters in request.response_parameters: yield messages_pb2.StreamingOutputCallResponse( payload=messages_pb2.Payload( @@ -78,10 +96,9 @@ class TestService(test_pb2.TestServiceServicer): aggregated_payload_size=aggregate_size) def FullDuplexCall(self, request_iterator, context): + _maybe_echo_metadata(context) for request in request_iterator: - if request.HasField('response_status'): - context.set_code(request.response_status.code) - context.set_details(request.response_status.message) + _maybe_echo_status_and_message(request, context) for response_parameters in request.response_parameters: yield messages_pb2.StreamingOutputCallResponse( payload=messages_pb2.Payload( @@ -94,23 +111,46 @@ class TestService(test_pb2.TestServiceServicer): return self.FullDuplexCall(request_iterator, context) +def _expect_status_code(call, expected_code): + if call.code() != expected_code: + raise ValueError( + 'expected code %s, got %s' % (expected_code, call.code())) + + +def _expect_status_details(call, expected_details): + if call.details() != expected_details: + raise ValueError( + 'expected message %s, got %s' % (expected_details, call.details())) + + +def _validate_status_code_and_details(call, expected_code, expected_details): + _expect_status_code(call, expected_code) + _expect_status_details(call, expected_details) + + +def _validate_payload_type_and_length(response, expected_type, expected_length): + if response.payload.type is not expected_type: + raise ValueError( + 'expected payload type %s, got %s' % + (expected_type, type(response.payload.type))) + elif len(response.payload.body) != expected_length: + raise ValueError( + 'expected payload body size %d, got %d' % + (expected_length, len(response.payload.body))) + + def _large_unary_common_behavior( stub, fill_username, fill_oauth_scope, call_credentials): + size = 314159 request = messages_pb2.SimpleRequest( - response_type=messages_pb2.COMPRESSABLE, response_size=314159, + response_type=messages_pb2.COMPRESSABLE, response_size=size, payload=messages_pb2.Payload(body=b'\x00' * 271828), fill_username=fill_username, fill_oauth_scope=fill_oauth_scope) response_future = stub.UnaryCall.future( request, credentials=call_credentials) response = response_future.result() - if response.payload.type is not messages_pb2.COMPRESSABLE: - raise ValueError( - 'response payload type is "%s"!' % type(response.payload.type)) - elif len(response.payload.body) != 314159: - raise ValueError( - 'response body of incorrect size %d!' % len(response.payload.body)) - else: - return response + _validate_payload_type_and_length(response, messages_pb2.COMPRESSABLE, size) + return response def _empty_unary(stub): @@ -152,12 +192,9 @@ def _server_streaming(stub): ) response_iterator = stub.StreamingOutputCall(request) for index, response in enumerate(response_iterator): - if response.payload.type != messages_pb2.COMPRESSABLE: - raise ValueError( - 'response body of invalid type %s!' % response.payload.type) - elif len(response.payload.body) != sizes[index]: - raise ValueError( - 'response body of invalid size %d!' % len(response.payload.body)) + _validate_payload_type_and_length( + response, messages_pb2.COMPRESSABLE, sizes[index]) + def _cancel_after_begin(stub): sizes = (27182, 8, 1828, 45904,) @@ -224,12 +261,8 @@ def _ping_pong(stub): payload=messages_pb2.Payload(body=b'\x00' * payload_size)) pipe.add(request) response = next(response_iterator) - if response.payload.type != messages_pb2.COMPRESSABLE: - raise ValueError( - 'response body of invalid type %s!' % response.payload.type) - if len(response.payload.body) != response_size: - raise ValueError( - 'response body of invalid size %d!' % len(response.payload.body)) + _validate_payload_type_and_length( + response, messages_pb2.COMPRESSABLE, response_size) def _cancel_after_first_response(stub): @@ -291,36 +324,84 @@ def _empty_stream(stub): def _status_code_and_message(stub): - message = 'test status message' + details = 'test status message' code = 2 status = grpc.StatusCode.UNKNOWN # code = 2 + + # Test with a UnaryCall request = messages_pb2.SimpleRequest( response_type=messages_pb2.COMPRESSABLE, response_size=1, payload=messages_pb2.Payload(body=b'\x00'), - response_status=messages_pb2.EchoStatus(code=code, message=message) + response_status=messages_pb2.EchoStatus(code=code, message=details) ) response_future = stub.UnaryCall.future(request) - if response_future.code() != status: - raise ValueError( - 'expected code %s, got %s' % (status, response_future.code())) - elif response_future.details() != message: - raise ValueError( - 'expected message %s, got %s' % (message, response_future.details())) + _validate_status_code_and_details(response_future, status, details) - request = messages_pb2.StreamingOutputCallRequest( + # Test with a FullDuplexCall + with _Pipe() as pipe: + response_iterator = stub.FullDuplexCall(pipe) + request = messages_pb2.StreamingOutputCallRequest( + response_type=messages_pb2.COMPRESSABLE, + response_parameters=( + messages_pb2.ResponseParameters(size=1),), + payload=messages_pb2.Payload(body=b'\x00'), + response_status=messages_pb2.EchoStatus(code=code, message=details)) + pipe.add(request) # sends the initial request. + # Dropping out of with block closes the pipe + _validate_status_code_and_details(response_iterator, status, details) + + +def _unimplemented_method(test_service_stub): + response_future = ( + test_service_stub.UnimplementedCall.future(empty_pb2.Empty())) + _expect_status_code(response_future, grpc.StatusCode.UNIMPLEMENTED) + + +def _unimplemented_service(unimplemented_service_stub): + response_future = ( + unimplemented_service_stub.UnimplementedCall.future(empty_pb2.Empty())) + _expect_status_code(response_future, grpc.StatusCode.UNIMPLEMENTED) + + +def _custom_metadata(stub): + initial_metadata_value = "test_initial_metadata_value" + trailing_metadata_value = "\x0a\x0b\x0a\x0b\x0a\x0b" + metadata = ( + (_INITIAL_METADATA_KEY, initial_metadata_value), + (_TRAILING_METADATA_KEY, trailing_metadata_value)) + + def _validate_metadata(response): + initial_metadata = dict(response.initial_metadata()) + if initial_metadata[_INITIAL_METADATA_KEY] != initial_metadata_value: + raise ValueError( + 'expected initial metadata %s, got %s' % ( + initial_metadata_value, initial_metadata[_INITIAL_METADATA_KEY])) + trailing_metadata = dict(response.trailing_metadata()) + if trailing_metadata[_TRAILING_METADATA_KEY] != trailing_metadata_value: + raise ValueError( + 'expected trailing metadata %s, got %s' % ( + trailing_metadata_value, initial_metadata[_TRAILING_METADATA_KEY])) + + # Testing with UnaryCall + request = messages_pb2.SimpleRequest( response_type=messages_pb2.COMPRESSABLE, - response_parameters=( - messages_pb2.ResponseParameters(size=1),), - response_status=messages_pb2.EchoStatus(code=code, message=message)) - response_iterator = stub.StreamingOutputCall(request) - if response_future.code() != status: - raise ValueError( - 'expected code %s, got %s' % (status, response_iterator.code())) - elif response_future.details() != message: - raise ValueError( - 'expected message %s, got %s' % (message, response_iterator.details())) + response_size=1, + payload=messages_pb2.Payload(body=b'\x00')) + response_future = stub.UnaryCall.future(request, metadata=metadata) + _validate_metadata(response_future) + # Testing with FullDuplexCall + with _Pipe() as pipe: + response_iterator = stub.FullDuplexCall(pipe, metadata=metadata) + request = messages_pb2.StreamingOutputCallRequest( + response_type=messages_pb2.COMPRESSABLE, + response_parameters=( + messages_pb2.ResponseParameters(size=1),)) + pipe.add(request) # Sends the request + next(response_iterator) # Causes server to send trailing metadata + # Dropping out of the with block closes the pipe + _validate_metadata(response_iterator) def _compute_engine_creds(stub, args): response = _large_unary_common_behavior(stub, True, True, None) @@ -381,6 +462,9 @@ class TestCase(enum.Enum): CANCEL_AFTER_FIRST_RESPONSE = 'cancel_after_first_response' EMPTY_STREAM = 'empty_stream' STATUS_CODE_AND_MESSAGE = 'status_code_and_message' + UNIMPLEMENTED_METHOD = 'unimplemented_method' + UNIMPLEMENTED_SERVICE = 'unimplemented_service' + CUSTOM_METADATA = "custom_metadata" COMPUTE_ENGINE_CREDS = 'compute_engine_creds' OAUTH2_AUTH_TOKEN = 'oauth2_auth_token' JWT_TOKEN_CREDS = 'jwt_token_creds' @@ -408,6 +492,12 @@ class TestCase(enum.Enum): _empty_stream(stub) elif self is TestCase.STATUS_CODE_AND_MESSAGE: _status_code_and_message(stub) + elif self is TestCase.UNIMPLEMENTED_METHOD: + _unimplemented_method(stub) + elif self is TestCase.UNIMPLEMENTED_SERVICE: + _unimplemented_service(stub) + elif self is TestCase.CUSTOM_METADATA: + _custom_metadata(stub) elif self is TestCase.COMPUTE_ENGINE_CREDS: _compute_engine_creds(stub, args) elif self is TestCase.OAUTH2_AUTH_TOKEN: diff --git a/src/ruby/README.md b/src/ruby/README.md index f476fe72be..266d6b2c16 100644 --- a/src/ruby/README.md +++ b/src/ruby/README.md @@ -59,7 +59,7 @@ Directory structure is the layout for [ruby extensions][] - bin: example gRPC clients and servers, e.g, ```ruby - stub = Math::Math::Stub.new('my.test.math.server.com:8080') + stub = Math::Math::Stub.new('my.test.math.server.com:8080', :this_channel_is_insecure) req = Math::DivArgs.new(dividend: 7, divisor: 3) GRPC.logger.info("div(7/3): req=#{req.inspect}") resp = stub.div(req) |