diff options
author | David Garcia Quintas <dgq@google.com> | 2015-12-16 17:36:04 -0800 |
---|---|---|
committer | David Garcia Quintas <dgq@google.com> | 2015-12-16 17:36:04 -0800 |
commit | 7052ac25e60e137514d9a201a86eeb9b29b03d24 (patch) | |
tree | 2ce8f32319129e346a27d3b29a9b8d6b440cdd6c /src | |
parent | 886b7d19bafbb61e84141e66a040da8c27781c44 (diff) | |
parent | 788767a18f918131268ca88985b3547a8257e973 (diff) |
Merge branch 'master' of github.com:grpc/grpc into grpclb_api
Diffstat (limited to 'src')
395 files changed, 9295 insertions, 6697 deletions
diff --git a/src/core/census/context.h b/src/core/census/context.h index d9907d4da7..700bcf86cf 100644 --- a/src/core/census/context.h +++ b/src/core/census/context.h @@ -36,14 +36,12 @@ #include <grpc/census.h> +#define GRPC_CENSUS_MAX_ON_THE_WIRE_TAG_BYTES 2048 + /* census_context is the in-memory representation of information needed to * maintain tracing, RPC statistics and resource usage information. */ struct census_context { - gpr_uint64 op_id; /* Operation identifier - unique per-context */ - gpr_uint64 trace_id; /* Globally unique trace identifier */ - /* TODO(aveitch) Add census tags: - const census_tag_set *tags; - */ + census_tag_set *tags; /* Opaque data structure for census tags. */ }; #endif /* GRPC_INTERNAL_CORE_CENSUS_CONTEXT_H */ diff --git a/src/core/census/grpc_filter.c b/src/core/census/grpc_filter.c index 61a95ec765..4529ae9bd7 100644 --- a/src/core/census/grpc_filter.c +++ b/src/core/census/grpc_filter.c @@ -43,7 +43,6 @@ #include <grpc/support/time.h> #include "src/core/channel/channel_stack.h" -#include "src/core/channel/noop_filter.h" #include "src/core/statistics/census_interface.h" #include "src/core/statistics/census_rpc_stats.h" #include "src/core/transport/static_metadata.h" @@ -60,9 +59,7 @@ typedef struct call_data { grpc_closure finish_recv; } call_data; -typedef struct channel_data { - gpr_uint8 unused; -} channel_data; +typedef struct channel_data { gpr_uint8 unused; } channel_data; static void extract_and_annotate_method_tag(grpc_metadata_batch *md, call_data *calld, @@ -118,8 +115,11 @@ static void server_mutate_op(grpc_call_element *elem, static void server_start_transport_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_transport_stream_op *op) { + /* TODO(ctiller): this code fails. I don't know why. I expect it's + incomplete, and someone should look at it soon. + call_data *calld = elem->call_data; - GPR_ASSERT((calld->op_id.upper != 0) || (calld->op_id.lower != 0)); + GPR_ASSERT((calld->op_id.upper != 0) || (calld->op_id.lower != 0)); */ server_mutate_op(elem, op); grpc_call_next_op(exec_ctx, elem, op); } diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c index 7f7fbf420f..5e09a050ee 100644 --- a/src/core/channel/channel_stack.c +++ b/src/core/channel/channel_stack.c @@ -101,11 +101,12 @@ grpc_call_element *grpc_call_stack_element(grpc_call_stack *call_stack, return CALL_ELEMS_FROM_STACK(call_stack) + index; } -void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, +void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs, + grpc_iomgr_cb_func destroy, void *destroy_arg, const grpc_channel_filter **filters, - size_t filter_count, grpc_channel *master, + size_t filter_count, const grpc_channel_args *channel_args, - grpc_channel_stack *stack) { + const char *name, grpc_channel_stack *stack) { size_t call_size = ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) + ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_call_element)); @@ -115,6 +116,8 @@ void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, size_t i; stack->count = filter_count; + GRPC_STREAM_REF_INIT(&stack->refcount, initial_refs, destroy, destroy_arg, + name); elems = CHANNEL_ELEMS_FROM_STACK(stack); user_data = ((char *)elems) + @@ -122,7 +125,7 @@ void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, /* init per-filter data */ for (i = 0; i < filter_count; i++) { - args.master = master; + args.channel_stack = stack; args.channel_args = channel_args; args.is_first = i == 0; args.is_last = i == (filter_count - 1); @@ -166,15 +169,15 @@ void grpc_call_stack_init(grpc_exec_ctx *exec_ctx, size_t i; call_stack->count = count; - gpr_ref_init(&call_stack->refcount.refs, initial_refs); - grpc_closure_init(&call_stack->refcount.destroy, destroy, destroy_arg); + GRPC_STREAM_REF_INIT(&call_stack->refcount, initial_refs, destroy, + destroy_arg, "CALL_STACK"); call_elems = CALL_ELEMS_FROM_STACK(call_stack); user_data = ((char *)call_elems) + ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element)); /* init per-filter data */ for (i = 0; i < count; i++) { - args.refcount = &call_stack->refcount; + args.call_stack = call_stack; args.server_transport_data = transport_server_data; args.context = context; call_elems[i].filter = channel_elems[i].filter; diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h index 1db12ead7e..c01050e717 100644 --- a/src/core/channel/channel_stack.h +++ b/src/core/channel/channel_stack.h @@ -51,15 +51,18 @@ typedef struct grpc_channel_element grpc_channel_element; typedef struct grpc_call_element grpc_call_element; +typedef struct grpc_channel_stack grpc_channel_stack; +typedef struct grpc_call_stack grpc_call_stack; + typedef struct { - grpc_channel *master; + grpc_channel_stack *channel_stack; const grpc_channel_args *channel_args; int is_first; int is_last; } grpc_channel_element_args; typedef struct { - grpc_stream_refcount *refcount; + grpc_call_stack *call_stack; const void *server_transport_data; grpc_call_context_element *context; } grpc_call_element_args; @@ -144,23 +147,24 @@ struct grpc_call_element { /* A channel stack tracks a set of related filters for one channel, and guarantees they live within a single malloc() allocation */ -typedef struct { +struct grpc_channel_stack { + grpc_stream_refcount refcount; size_t count; /* Memory required for a call stack (computed at channel stack initialization) */ size_t call_stack_size; -} grpc_channel_stack; +}; /* A call stack tracks a set of related filters for one call, and guarantees they live within a single malloc() allocation */ -typedef struct { +struct grpc_call_stack { /* shared refcount for this channel stack. MUST be the first element: the underlying code calls destroy with the address of the refcount, but higher layers prefer to think about the address of the call stack itself. */ grpc_stream_refcount refcount; size_t count; -} grpc_call_stack; +}; /* Get a channel element given a channel stack and its index */ grpc_channel_element *grpc_channel_stack_element(grpc_channel_stack *stack, @@ -175,11 +179,11 @@ grpc_call_element *grpc_call_stack_element(grpc_call_stack *stack, size_t i); size_t grpc_channel_stack_size(const grpc_channel_filter **filters, size_t filter_count); /* Initialize a channel stack given some filters */ -void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, +void grpc_channel_stack_init(grpc_exec_ctx *exec_ctx, int initial_refs, + grpc_iomgr_cb_func destroy, void *destroy_arg, const grpc_channel_filter **filters, - size_t filter_count, grpc_channel *master, - const grpc_channel_args *args, - grpc_channel_stack *stack); + size_t filter_count, const grpc_channel_args *args, + const char *name, grpc_channel_stack *stack); /* Destroy a channel stack */ void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_channel_stack *stack); @@ -199,14 +203,23 @@ void grpc_call_stack_set_pollset(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset); #ifdef GRPC_STREAM_REFCOUNT_DEBUG -#define grpc_call_stack_ref(call_stack, reason) \ +#define GRPC_CALL_STACK_REF(call_stack, reason) \ grpc_stream_ref(&(call_stack)->refcount, reason) -#define grpc_call_stack_unref(exec_ctx, call_stack, reason) \ +#define GRPC_CALL_STACK_UNREF(exec_ctx, call_stack, reason) \ grpc_stream_unref(exec_ctx, &(call_stack)->refcount, reason) +#define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \ + grpc_stream_ref(&(channel_stack)->refcount, reason) +#define GRPC_CHANNEL_STACK_UNREF(exec_ctx, channel_stack, reason) \ + grpc_stream_unref(exec_ctx, &(channel_stack)->refcount, reason) #else -#define grpc_call_stack_ref(call_stack) grpc_stream_ref(&(call_stack)->refcount) -#define grpc_call_stack_unref(exec_ctx, call_stack) \ +#define GRPC_CALL_STACK_REF(call_stack, reason) \ + grpc_stream_ref(&(call_stack)->refcount) +#define GRPC_CALL_STACK_UNREF(exec_ctx, call_stack, reason) \ grpc_stream_unref(exec_ctx, &(call_stack)->refcount) +#define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \ + grpc_stream_ref(&(channel_stack)->refcount) +#define GRPC_CHANNEL_STACK_UNREF(exec_ctx, channel_stack, reason) \ + grpc_stream_unref(exec_ctx, &(channel_stack)->refcount) #endif /* Destroy a call stack */ diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c index 020138bf15..385ae3be9b 100644 --- a/src/core/channel/client_channel.c +++ b/src/core/channel/client_channel.c @@ -59,11 +59,6 @@ typedef struct client_channel_channel_data { grpc_resolver *resolver; /** have we started resolving this channel */ int started_resolving; - /** master channel - the grpc_channel instance that ultimately owns - this channel_data via its channel stack. - We occasionally use this to bump the refcount on the master channel - to keep ourselves alive through an asynchronous operation. */ - grpc_channel *master; /** mutex protecting client configuration, including all variables below in this data structure */ @@ -81,8 +76,10 @@ typedef struct client_channel_channel_data { grpc_connectivity_state_tracker state_tracker; /** when an lb_policy arrives, should we try to exit idle */ int exit_idle_when_lb_policy_arrives; - /** pollset_set of interested parties in a new connection */ - grpc_pollset_set pollset_set; + /** owning stack */ + grpc_channel_stack *owning_stack; + /** interested parties */ + grpc_pollset_set interested_parties; } channel_data; /** We create one watcher for each new lb_policy that is returned from a @@ -103,9 +100,7 @@ typedef struct { } waiting_call; static char *cc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { - channel_data *chand = elem->channel_data; - return grpc_subchannel_call_holder_get_peer(exec_ctx, elem->call_data, - chand->master); + return grpc_subchannel_call_holder_get_peer(exec_ctx, elem->call_data); } static void cc_start_transport_stream_op(grpc_exec_ctx *exec_ctx, @@ -121,10 +116,18 @@ static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand, static void on_lb_policy_state_changed_locked( grpc_exec_ctx *exec_ctx, lb_policy_connectivity_watcher *w) { + grpc_connectivity_state publish_state = w->state; /* check if the notification is for a stale policy */ if (w->lb_policy != w->chand->lb_policy) return; - grpc_connectivity_state_set(exec_ctx, &w->chand->state_tracker, w->state, + if (publish_state == GRPC_CHANNEL_FATAL_FAILURE && + w->chand->resolver != NULL) { + publish_state = GRPC_CHANNEL_TRANSIENT_FAILURE; + grpc_resolver_channel_saw_error(exec_ctx, w->chand->resolver); + GRPC_LB_POLICY_UNREF(exec_ctx, w->chand->lb_policy, "channel"); + w->chand->lb_policy = NULL; + } + grpc_connectivity_state_set(exec_ctx, &w->chand->state_tracker, publish_state, "lb_changed"); if (w->state != GRPC_CHANNEL_FATAL_FAILURE) { watch_lb_policy(exec_ctx, w->chand, w->lb_policy, w->state); @@ -139,7 +142,7 @@ static void on_lb_policy_state_changed(grpc_exec_ctx *exec_ctx, void *arg, on_lb_policy_state_changed_locked(exec_ctx, w); gpr_mu_unlock(&w->chand->mu_config); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->chand->master, "watch_lb_policy"); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, "watch_lb_policy"); gpr_free(w); } @@ -147,7 +150,7 @@ static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand, grpc_lb_policy *lb_policy, grpc_connectivity_state current_state) { lb_policy_connectivity_watcher *w = gpr_malloc(sizeof(*w)); - GRPC_CHANNEL_INTERNAL_REF(chand->master, "watch_lb_policy"); + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "watch_lb_policy"); w->chand = chand; grpc_closure_init(&w->on_changed, on_lb_policy_state_changed, w); @@ -179,6 +182,11 @@ static void cc_on_config_changed(grpc_exec_ctx *exec_ctx, void *arg, chand->incoming_configuration = NULL; + if (lb_policy != NULL) { + grpc_pollset_set_add_pollset_set(exec_ctx, &lb_policy->interested_parties, + &chand->interested_parties); + } + gpr_mu_lock(&chand->mu_config); old_lb_policy = chand->lb_policy; chand->lb_policy = lb_policy; @@ -200,7 +208,7 @@ static void cc_on_config_changed(grpc_exec_ctx *exec_ctx, void *arg, watch_lb_policy(exec_ctx, chand, lb_policy, state); } gpr_mu_unlock(&chand->mu_config); - GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); grpc_resolver_next(exec_ctx, resolver, &chand->incoming_configuration, &chand->on_config_changed); GRPC_RESOLVER_UNREF(exec_ctx, resolver, "channel-next"); @@ -222,7 +230,9 @@ static void cc_on_config_changed(grpc_exec_ctx *exec_ctx, void *arg, } if (old_lb_policy != NULL) { - grpc_lb_policy_shutdown(exec_ctx, old_lb_policy); + grpc_pollset_set_del_pollset_set(exec_ctx, + &old_lb_policy->interested_parties, + &chand->interested_parties); GRPC_LB_POLICY_UNREF(exec_ctx, old_lb_policy, "channel"); } @@ -230,20 +240,22 @@ static void cc_on_config_changed(grpc_exec_ctx *exec_ctx, void *arg, GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "config_change"); } - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->master, "resolver"); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->owning_stack, "resolver"); } static void cc_start_transport_op(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_transport_op *op) { - grpc_lb_policy *lb_policy = NULL; channel_data *chand = elem->channel_data; grpc_resolver *destroy_resolver = NULL; grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, 1); GPR_ASSERT(op->set_accept_stream == NULL); - GPR_ASSERT(op->bind_pollset == NULL); + if (op->bind_pollset != NULL) { + grpc_pollset_set_add_pollset(exec_ctx, &chand->interested_parties, + op->bind_pollset); + } gpr_mu_lock(&chand->mu_config); if (op->on_connectivity_state_change != NULL) { @@ -254,9 +266,14 @@ static void cc_start_transport_op(grpc_exec_ctx *exec_ctx, op->connectivity_state = NULL; } - lb_policy = chand->lb_policy; - if (lb_policy) { - GRPC_LB_POLICY_REF(lb_policy, "broadcast"); + if (op->send_ping != NULL) { + if (chand->lb_policy == NULL) { + grpc_exec_ctx_enqueue(exec_ctx, op->send_ping, 0); + } else { + grpc_lb_policy_ping_one(exec_ctx, chand->lb_policy, op->send_ping); + op->bind_pollset = NULL; + } + op->send_ping = NULL; } if (op->disconnect && chand->resolver != NULL) { @@ -265,7 +282,9 @@ static void cc_start_transport_op(grpc_exec_ctx *exec_ctx, destroy_resolver = chand->resolver; chand->resolver = NULL; if (chand->lb_policy != NULL) { - grpc_lb_policy_shutdown(exec_ctx, chand->lb_policy); + grpc_pollset_set_del_pollset_set(exec_ctx, + &chand->lb_policy->interested_parties, + &chand->interested_parties); GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); chand->lb_policy = NULL; } @@ -276,16 +295,11 @@ static void cc_start_transport_op(grpc_exec_ctx *exec_ctx, grpc_resolver_shutdown(exec_ctx, destroy_resolver); GRPC_RESOLVER_UNREF(exec_ctx, destroy_resolver, "channel"); } - - if (lb_policy) { - grpc_lb_policy_broadcast(exec_ctx, lb_policy, op); - GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "broadcast"); - } } typedef struct { grpc_metadata_batch *initial_metadata; - grpc_subchannel **subchannel; + grpc_connected_subchannel **connected_subchannel; grpc_closure *on_ready; grpc_call_element *elem; grpc_closure closure; @@ -293,17 +307,17 @@ typedef struct { static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *arg, grpc_metadata_batch *initial_metadata, - grpc_subchannel **subchannel, + grpc_connected_subchannel **connected_subchannel, grpc_closure *on_ready); static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg, int success) { continue_picking_args *cpa = arg; if (!success) { grpc_exec_ctx_enqueue(exec_ctx, cpa->on_ready, 0); - } else if (cpa->subchannel == NULL) { + } else if (cpa->connected_subchannel == NULL) { /* cancelled, do nothing */ } else if (cc_pick_subchannel(exec_ctx, cpa->elem, cpa->initial_metadata, - cpa->subchannel, cpa->on_ready)) { + cpa->connected_subchannel, cpa->on_ready)) { grpc_exec_ctx_enqueue(exec_ctx, cpa->on_ready, 1); } gpr_free(cpa); @@ -311,7 +325,7 @@ static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg, int success) { static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp, grpc_metadata_batch *initial_metadata, - grpc_subchannel **subchannel, + grpc_connected_subchannel **connected_subchannel, grpc_closure *on_ready) { grpc_call_element *elem = elemp; channel_data *chand = elem->channel_data; @@ -319,18 +333,19 @@ static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp, continue_picking_args *cpa; grpc_closure *closure; - GPR_ASSERT(subchannel); + GPR_ASSERT(connected_subchannel); gpr_mu_lock(&chand->mu_config); if (initial_metadata == NULL) { if (chand->lb_policy != NULL) { - grpc_lb_policy_cancel_pick(exec_ctx, chand->lb_policy, subchannel); + grpc_lb_policy_cancel_pick(exec_ctx, chand->lb_policy, + connected_subchannel); } for (closure = chand->waiting_for_config_closures.head; closure != NULL; closure = grpc_closure_next(closure)) { cpa = closure->cb_arg; - if (cpa->subchannel == subchannel) { - cpa->subchannel = NULL; + if (cpa->connected_subchannel == connected_subchannel) { + cpa->connected_subchannel = NULL; grpc_exec_ctx_enqueue(exec_ctx, cpa->on_ready, 0); } } @@ -338,21 +353,22 @@ static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp, return 1; } if (chand->lb_policy != NULL) { - int r = grpc_lb_policy_pick(exec_ctx, chand->lb_policy, calld->pollset, - initial_metadata, subchannel, on_ready); + int r = + grpc_lb_policy_pick(exec_ctx, chand->lb_policy, calld->pollset, + initial_metadata, connected_subchannel, on_ready); gpr_mu_unlock(&chand->mu_config); return r; } if (chand->resolver != NULL && !chand->started_resolving) { chand->started_resolving = 1; - GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); grpc_resolver_next(exec_ctx, chand->resolver, &chand->incoming_configuration, &chand->on_config_changed); } cpa = gpr_malloc(sizeof(*cpa)); cpa->initial_metadata = initial_metadata; - cpa->subchannel = subchannel; + cpa->connected_subchannel = connected_subchannel; cpa->on_ready = on_ready; cpa->elem = elem; grpc_closure_init(&cpa->closure, continue_picking, cpa); @@ -364,7 +380,8 @@ static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp, /* Constructor for call_data */ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_call_element_args *args) { - grpc_subchannel_call_holder_init(elem->call_data, cc_pick_subchannel, elem); + grpc_subchannel_call_holder_init(elem->call_data, cc_pick_subchannel, elem, + args->call_stack); } /* Destructor for call_data */ @@ -385,12 +402,12 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx, GPR_ASSERT(elem->filter == &grpc_client_channel_filter); gpr_mu_init(&chand->mu_config); - chand->master = args->master; - grpc_pollset_set_init(&chand->pollset_set); grpc_closure_init(&chand->on_config_changed, cc_on_config_changed, chand); + chand->owning_stack = args->channel_stack; grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, "client_channel"); + grpc_pollset_set_init(&chand->interested_parties); } /* Destructor for channel_data */ @@ -403,10 +420,13 @@ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel"); } if (chand->lb_policy != NULL) { + grpc_pollset_set_del_pollset_set(exec_ctx, + &chand->lb_policy->interested_parties, + &chand->interested_parties); GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); } grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker); - grpc_pollset_set_destroy(&chand->pollset_set); + grpc_pollset_set_destroy(&chand->interested_parties); gpr_mu_destroy(&chand->mu_config); } @@ -435,7 +455,7 @@ void grpc_client_channel_set_resolver(grpc_exec_ctx *exec_ctx, if (!grpc_closure_list_empty(chand->waiting_for_config_closures) || chand->exit_idle_when_lb_policy_arrives) { chand->started_resolving = 1; - GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); grpc_resolver_next(exec_ctx, resolver, &chand->incoming_configuration, &chand->on_config_changed); } @@ -454,7 +474,7 @@ grpc_connectivity_state grpc_client_channel_check_connectivity_state( } else { chand->exit_idle_when_lb_policy_arrives = 1; if (!chand->started_resolving && chand->resolver != NULL) { - GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); chand->started_resolving = 1; grpc_resolver_next(exec_ctx, chand->resolver, &chand->incoming_configuration, @@ -466,32 +486,39 @@ grpc_connectivity_state grpc_client_channel_check_connectivity_state( return out; } +typedef struct { + channel_data *chand; + grpc_pollset *pollset; + grpc_closure *on_complete; + grpc_closure my_closure; +} external_connectivity_watcher; + +static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { + external_connectivity_watcher *w = arg; + grpc_closure *follow_up = w->on_complete; + grpc_pollset_set_del_pollset(exec_ctx, &w->chand->interested_parties, + w->pollset); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, + "external_connectivity_watcher"); + gpr_free(w); + follow_up->cb(exec_ctx, follow_up->cb_arg, iomgr_success); +} + void grpc_client_channel_watch_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, grpc_connectivity_state *state, grpc_closure *on_complete) { channel_data *chand = elem->channel_data; + external_connectivity_watcher *w = gpr_malloc(sizeof(*w)); + w->chand = chand; + w->pollset = pollset; + w->on_complete = on_complete; + grpc_pollset_set_add_pollset(exec_ctx, &chand->interested_parties, pollset); + grpc_closure_init(&w->my_closure, on_external_watch_complete, w); + GRPC_CHANNEL_STACK_REF(w->chand->owning_stack, + "external_connectivity_watcher"); gpr_mu_lock(&chand->mu_config); grpc_connectivity_state_notify_on_state_change( - exec_ctx, &chand->state_tracker, state, on_complete); + exec_ctx, &chand->state_tracker, state, &w->my_closure); gpr_mu_unlock(&chand->mu_config); } - -grpc_pollset_set *grpc_client_channel_get_connecting_pollset_set( - grpc_channel_element *elem) { - channel_data *chand = elem->channel_data; - return &chand->pollset_set; -} - -void grpc_client_channel_add_interested_party(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_pollset *pollset) { - channel_data *chand = elem->channel_data; - grpc_pollset_set_add_pollset(exec_ctx, &chand->pollset_set, pollset); -} - -void grpc_client_channel_del_interested_party(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_pollset *pollset) { - channel_data *chand = elem->channel_data; - grpc_pollset_set_del_pollset(exec_ctx, &chand->pollset_set, pollset); -} diff --git a/src/core/channel/client_channel.h b/src/core/channel/client_channel.h index 5103f07a43..d9bc4971f1 100644 --- a/src/core/channel/client_channel.h +++ b/src/core/channel/client_channel.h @@ -57,17 +57,7 @@ grpc_connectivity_state grpc_client_channel_check_connectivity_state( grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect); void grpc_client_channel_watch_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, grpc_connectivity_state *state, grpc_closure *on_complete); -grpc_pollset_set *grpc_client_channel_get_connecting_pollset_set( - grpc_channel_element *elem); - -void grpc_client_channel_add_interested_party(grpc_exec_ctx *exec_ctx, - grpc_channel_element *channel, - grpc_pollset *pollset); -void grpc_client_channel_del_interested_party(grpc_exec_ctx *exec_ctx, - grpc_channel_element *channel, - grpc_pollset *pollset); - #endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_CHANNEL_H */ diff --git a/src/core/channel/client_uchannel.c b/src/core/channel/client_uchannel.c index 456ffb7371..2c0b07d8bf 100644 --- a/src/core/channel/client_uchannel.c +++ b/src/core/channel/client_uchannel.c @@ -58,13 +58,13 @@ typedef struct client_uchannel_channel_data { this channel_data via its channel stack. We occasionally use this to bump the refcount on the master channel to keep ourselves alive through an asynchronous operation. */ - grpc_channel *master; + grpc_channel_stack *owning_stack; /** connectivity state being tracked */ grpc_connectivity_state_tracker state_tracker; /** the subchannel wrapped by the microchannel */ - grpc_subchannel *subchannel; + grpc_connected_subchannel *connected_subchannel; /** the callback used to stay subscribed to subchannel connectivity * notifications */ @@ -84,15 +84,13 @@ static void monitor_subchannel(grpc_exec_ctx *exec_ctx, void *arg, grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, chand->subchannel_connectivity, "uchannel_monitor_subchannel"); - grpc_subchannel_notify_on_state_change(exec_ctx, chand->subchannel, - &chand->subchannel_connectivity, - &chand->connectivity_cb); + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, chand->connected_subchannel, NULL, + &chand->subchannel_connectivity, &chand->connectivity_cb); } static char *cuc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { - channel_data *chand = elem->channel_data; - return grpc_subchannel_call_holder_get_peer(exec_ctx, elem->call_data, - chand->master); + return grpc_subchannel_call_holder_get_peer(exec_ctx, elem->call_data); } static void cuc_start_transport_stream_op(grpc_exec_ctx *exec_ctx, @@ -128,11 +126,11 @@ static void cuc_start_transport_op(grpc_exec_ctx *exec_ctx, static int cuc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *arg, grpc_metadata_batch *initial_metadata, - grpc_subchannel **subchannel, + grpc_connected_subchannel **connected_subchannel, grpc_closure *on_ready) { channel_data *chand = arg; GPR_ASSERT(initial_metadata != NULL); - *subchannel = chand->subchannel; + *connected_subchannel = chand->connected_subchannel; return 1; } @@ -140,7 +138,7 @@ static int cuc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *arg, static void cuc_init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_call_element_args *args) { grpc_subchannel_call_holder_init(elem->call_data, cuc_pick_subchannel, - elem->channel_data); + elem->channel_data, args->call_stack); } /* Destructor for call_data */ @@ -158,7 +156,7 @@ static void cuc_init_channel_elem(grpc_exec_ctx *exec_ctx, grpc_closure_init(&chand->connectivity_cb, monitor_subchannel, chand); GPR_ASSERT(args->is_last); GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter); - chand->master = args->master; + chand->owning_stack = args->channel_stack; grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, "client_uchannel"); gpr_mu_init(&chand->mu_state); @@ -168,10 +166,14 @@ static void cuc_init_channel_elem(grpc_exec_ctx *exec_ctx, static void cuc_destroy_channel_elem(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem) { channel_data *chand = elem->channel_data; - grpc_subchannel_state_change_unsubscribe(exec_ctx, chand->subchannel, - &chand->connectivity_cb); + /* cancel subscription */ + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, chand->connected_subchannel, NULL, NULL, + &chand->connectivity_cb); grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker); gpr_mu_destroy(&chand->mu_state); + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, chand->connected_subchannel, + "uchannel"); } static void cuc_set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, @@ -191,23 +193,14 @@ grpc_connectivity_state grpc_client_uchannel_check_connectivity_state( grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect) { channel_data *chand = elem->channel_data; grpc_connectivity_state out; - out = grpc_connectivity_state_check(&chand->state_tracker); gpr_mu_lock(&chand->mu_state); - if (out == GRPC_CHANNEL_IDLE && try_to_connect) { - grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, - GRPC_CHANNEL_CONNECTING, - "uchannel_connecting_changed"); - chand->subchannel_connectivity = out; - grpc_subchannel_notify_on_state_change(exec_ctx, chand->subchannel, - &chand->subchannel_connectivity, - &chand->connectivity_cb); - } + out = grpc_connectivity_state_check(&chand->state_tracker); gpr_mu_unlock(&chand->mu_state); return out; } void grpc_client_uchannel_watch_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, grpc_connectivity_state *state, grpc_closure *on_complete) { channel_data *chand = elem->channel_data; gpr_mu_lock(&chand->mu_state); @@ -216,40 +209,11 @@ void grpc_client_uchannel_watch_connectivity_state( gpr_mu_unlock(&chand->mu_state); } -grpc_pollset_set *grpc_client_uchannel_get_connecting_pollset_set( - grpc_channel_element *elem) { - channel_data *chand = elem->channel_data; - grpc_channel_element *parent_elem; - gpr_mu_lock(&chand->mu_state); - parent_elem = grpc_channel_stack_last_element(grpc_channel_get_channel_stack( - grpc_subchannel_get_master(chand->subchannel))); - gpr_mu_unlock(&chand->mu_state); - return grpc_client_channel_get_connecting_pollset_set(parent_elem); -} - -void grpc_client_uchannel_add_interested_party(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_pollset *pollset) { - grpc_pollset_set *master_pollset_set = - grpc_client_uchannel_get_connecting_pollset_set(elem); - grpc_pollset_set_add_pollset(exec_ctx, master_pollset_set, pollset); -} - -void grpc_client_uchannel_del_interested_party(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_pollset *pollset) { - grpc_pollset_set *master_pollset_set = - grpc_client_uchannel_get_connecting_pollset_set(elem); - grpc_pollset_set_del_pollset(exec_ctx, master_pollset_set, pollset); -} - grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel, grpc_channel_args *args) { grpc_channel *channel = NULL; #define MAX_FILTERS 3 const grpc_channel_filter *filters[MAX_FILTERS]; - grpc_channel *master = grpc_subchannel_get_master(subchannel); - char *target = grpc_channel_get_target(master); grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; size_t n = 0; @@ -261,19 +225,19 @@ grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel, GPR_ASSERT(n <= MAX_FILTERS); channel = - grpc_channel_create_from_filters(&exec_ctx, target, filters, n, args, 1); + grpc_channel_create_from_filters(&exec_ctx, NULL, filters, n, args, 1); - gpr_free(target); return channel; } -void grpc_client_uchannel_set_subchannel(grpc_channel *uchannel, - grpc_subchannel *subchannel) { +void grpc_client_uchannel_set_connected_subchannel( + grpc_channel *uchannel, grpc_connected_subchannel *connected_subchannel) { grpc_channel_element *elem = grpc_channel_stack_last_element(grpc_channel_get_channel_stack(uchannel)); channel_data *chand = elem->channel_data; GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter); gpr_mu_lock(&chand->mu_state); - chand->subchannel = subchannel; + chand->connected_subchannel = connected_subchannel; + GRPC_CONNECTED_SUBCHANNEL_REF(connected_subchannel, "uchannel"); gpr_mu_unlock(&chand->mu_state); } diff --git a/src/core/channel/client_uchannel.h b/src/core/channel/client_uchannel.h index dfe6695ae3..92a831493c 100644 --- a/src/core/channel/client_uchannel.h +++ b/src/core/channel/client_uchannel.h @@ -48,23 +48,13 @@ grpc_connectivity_state grpc_client_uchannel_check_connectivity_state( grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect); void grpc_client_uchannel_watch_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, grpc_connectivity_state *state, grpc_closure *on_complete); -grpc_pollset_set *grpc_client_uchannel_get_connecting_pollset_set( - grpc_channel_element *elem); - -void grpc_client_uchannel_add_interested_party(grpc_exec_ctx *exec_ctx, - grpc_channel_element *channel, - grpc_pollset *pollset); -void grpc_client_uchannel_del_interested_party(grpc_exec_ctx *exec_ctx, - grpc_channel_element *channel, - grpc_pollset *pollset); - grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel, grpc_channel_args *args); -void grpc_client_uchannel_set_subchannel(grpc_channel *uchannel, - grpc_subchannel *subchannel); +void grpc_client_uchannel_set_connected_subchannel( + grpc_channel *uchannel, grpc_connected_subchannel *connected_subchannel); #endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_MICROCHANNEL_H */ diff --git a/src/core/channel/compress_filter.c b/src/core/channel/compress_filter.c index fc8b425e47..cc8e191628 100644 --- a/src/core/channel/compress_filter.c +++ b/src/core/channel/compress_filter.c @@ -39,11 +39,11 @@ #include <grpc/support/log.h> #include <grpc/support/slice_buffer.h> -#include "src/core/channel/compress_filter.h" #include "src/core/channel/channel_args.h" -#include "src/core/profiling/timers.h" +#include "src/core/channel/compress_filter.h" #include "src/core/compression/algorithm_metadata.h" #include "src/core/compression/message_compress.h" +#include "src/core/profiling/timers.h" #include "src/core/support/string.h" #include "src/core/transport/static_metadata.h" @@ -288,8 +288,7 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx, /* Destructor for channel data */ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { -} + grpc_channel_element *elem) {} const grpc_channel_filter grpc_compress_filter = { compress_start_transport_stream_op, grpc_channel_next_op, sizeof(call_data), diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index 0e1efd965a..e8eb9dcfc5 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -89,9 +89,9 @@ static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, int r; GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - r = grpc_transport_init_stream(exec_ctx, chand->transport, - TRANSPORT_STREAM_FROM_CALL_DATA(calld), - args->refcount, args->server_transport_data); + r = grpc_transport_init_stream( + exec_ctx, chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld), + &args->call_stack->refcount, args->server_transport_data); GPR_ASSERT(r == 0); } diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c index b9a30cdaf2..65cfb778bb 100644 --- a/src/core/channel/http_client_filter.c +++ b/src/core/channel/http_client_filter.c @@ -31,12 +31,12 @@ */ #include "src/core/channel/http_client_filter.h" -#include <string.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/string_util.h> -#include "src/core/support/string.h" +#include <string.h> #include "src/core/profiling/timers.h" +#include "src/core/support/string.h" #include "src/core/transport/static_metadata.h" typedef struct call_data { diff --git a/src/core/channel/http_server_filter.c b/src/core/channel/http_server_filter.c index c1645c2ba0..ae8660da92 100644 --- a/src/core/channel/http_server_filter.c +++ b/src/core/channel/http_server_filter.c @@ -33,9 +33,9 @@ #include "src/core/channel/http_server_filter.h" -#include <string.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include <string.h> #include "src/core/profiling/timers.h" #include "src/core/transport/static_metadata.h" @@ -124,7 +124,6 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { omitted */ grpc_mdelem *authority = grpc_mdelem_from_metadata_strings( GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value)); - GRPC_MDELEM_UNREF(md); calld->seen_authority = 1; return authority; } else { @@ -225,8 +224,7 @@ static void init_channel_elem(grpc_exec_ctx *exec_ctx, /* Destructor for channel data */ static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { -} + grpc_channel_element *elem) {} const grpc_channel_filter grpc_http_server_filter = { hs_start_transport_op, grpc_channel_next_op, sizeof(call_data), diff --git a/src/core/channel/noop_filter.c b/src/core/channel/noop_filter.c deleted file mode 100644 index 2fbf1c06bb..0000000000 --- a/src/core/channel/noop_filter.c +++ /dev/null @@ -1,118 +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/channel/noop_filter.h" -#include <grpc/support/log.h> - -typedef struct call_data { - int unused; /* C89 requires at least one struct element */ -} call_data; - -typedef struct channel_data { - int unused; /* C89 requires at least one struct element */ -} channel_data; - -/* used to silence 'variable not used' warnings */ -static void ignore_unused(void *ignored) {} - -static void noop_mutate_op(grpc_call_element *elem, - grpc_transport_stream_op *op) { - /* grab pointers to our data from the call element */ - call_data *calld = elem->call_data; - channel_data *channeld = elem->channel_data; - - ignore_unused(calld); - ignore_unused(channeld); - - /* do nothing */ -} - -/* Called either: - - in response to an API call (or similar) from above, to send something - - a network event (or similar) from below, to receive something - op contains type and call direction information, in addition to the data - that is being sent or received. */ -static void noop_start_transport_stream_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - noop_mutate_op(elem, op); - - /* pass control down the stack */ - grpc_call_next_op(exec_ctx, elem, op); -} - -/* Constructor for call_data */ -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - /* grab pointers to our data from the call element */ - call_data *calld = elem->call_data; - channel_data *channeld = elem->channel_data; - - /* initialize members */ - calld->unused = channeld->unused; -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) {} - -/* Constructor for channel_data */ -static void init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - /* grab pointers to our data from the channel element */ - channel_data *channeld = elem->channel_data; - - /* The last filter tends to be implemented differently to - handle the case that there's no 'next' filter to call on the down - path */ - GPR_ASSERT(!args->is_last); - - /* initialize members */ - channeld->unused = 0; -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - /* grab pointers to our data from the channel element */ - channel_data *channeld = elem->channel_data; - - ignore_unused(channeld); -} - -const grpc_channel_filter grpc_no_op_filter = { - noop_start_transport_stream_op, grpc_channel_next_op, sizeof(call_data), - init_call_elem, grpc_call_stack_ignore_set_pollset, destroy_call_elem, - sizeof(channel_data), init_channel_elem, destroy_channel_elem, - grpc_call_next_get_peer, "no-op"}; diff --git a/src/core/channel/subchannel_call_holder.c b/src/core/channel/subchannel_call_holder.c index 7251714519..f5da41f3cd 100644 --- a/src/core/channel/subchannel_call_holder.c +++ b/src/core/channel/subchannel_call_holder.c @@ -44,7 +44,6 @@ static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *holder, int success); -static void call_ready(grpc_exec_ctx *exec_ctx, void *holder, int success); static void retry_ops(grpc_exec_ctx *exec_ctx, void *retry_ops_args, int success); @@ -58,16 +57,17 @@ static void retry_waiting_locked(grpc_exec_ctx *exec_ctx, void grpc_subchannel_call_holder_init( grpc_subchannel_call_holder *holder, grpc_subchannel_call_holder_pick_subchannel pick_subchannel, - void *pick_subchannel_arg) { + void *pick_subchannel_arg, grpc_call_stack *owning_call) { gpr_atm_rel_store(&holder->subchannel_call, 0); holder->pick_subchannel = pick_subchannel; holder->pick_subchannel_arg = pick_subchannel_arg; gpr_mu_init(&holder->mu); - holder->subchannel = NULL; + holder->connected_subchannel = NULL; holder->waiting_ops = NULL; holder->waiting_ops_count = 0; holder->waiting_ops_capacity = 0; holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; + holder->owning_call = owning_call; } void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx, @@ -125,13 +125,9 @@ retry: case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING: fail_locked(exec_ctx, holder); break; - case GRPC_SUBCHANNEL_CALL_HOLDER_CREATING_CALL: - grpc_subchannel_cancel_create_call(exec_ctx, holder->subchannel, - &holder->subchannel_call); - break; case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL: holder->pick_subchannel(exec_ctx, holder->pick_subchannel_arg, NULL, - &holder->subchannel, NULL); + &holder->connected_subchannel, NULL); break; } gpr_mu_unlock(&holder->mu); @@ -142,28 +138,27 @@ retry: } /* if we don't have a subchannel, try to get one */ if (holder->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING && - holder->subchannel == NULL && op->send_initial_metadata != NULL) { + holder->connected_subchannel == NULL && + op->send_initial_metadata != NULL) { holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL; grpc_closure_init(&holder->next_step, subchannel_ready, holder); - if (holder->pick_subchannel(exec_ctx, holder->pick_subchannel_arg, - op->send_initial_metadata, &holder->subchannel, - &holder->next_step)) { + GRPC_CALL_STACK_REF(holder->owning_call, "pick_subchannel"); + if (holder->pick_subchannel( + exec_ctx, holder->pick_subchannel_arg, op->send_initial_metadata, + &holder->connected_subchannel, &holder->next_step)) { holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; + GRPC_CALL_STACK_UNREF(exec_ctx, holder->owning_call, "pick_subchannel"); } } /* if we've got a subchannel, then let's ask it to create a call */ if (holder->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING && - holder->subchannel != NULL) { - holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_CREATING_CALL; - grpc_closure_init(&holder->next_step, call_ready, holder); - if (grpc_subchannel_create_call(exec_ctx, holder->subchannel, - holder->pollset, &holder->subchannel_call, - &holder->next_step)) { - /* got one immediately - continue the op (and any waiting ops) */ - holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; - retry_waiting_locked(exec_ctx, holder); - goto retry; - } + holder->connected_subchannel != NULL) { + gpr_atm_rel_store( + &holder->subchannel_call, + (gpr_atm)(gpr_uintptr)grpc_connected_subchannel_create_call( + exec_ctx, holder->connected_subchannel, holder->pollset)); + retry_waiting_locked(exec_ctx, holder); + goto retry; } /* nothing to be done but wait */ add_waiting_locked(holder, op); @@ -179,36 +174,18 @@ static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg, int success) { GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL); call = GET_CALL(holder); GPR_ASSERT(call == NULL || call == CANCELLED_CALL); - if (holder->subchannel == NULL) { - holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; + holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; + if (holder->connected_subchannel == NULL) { fail_locked(exec_ctx, holder); } else { - grpc_closure_init(&holder->next_step, call_ready, holder); - if (grpc_subchannel_create_call(exec_ctx, holder->subchannel, - holder->pollset, &holder->subchannel_call, - &holder->next_step)) { - holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; - /* got one immediately - continue the op (and any waiting ops) */ - retry_waiting_locked(exec_ctx, holder); - } - } - gpr_mu_unlock(&holder->mu); -} - -static void call_ready(grpc_exec_ctx *exec_ctx, void *arg, int success) { - grpc_subchannel_call_holder *holder = arg; - GPR_TIMER_BEGIN("call_ready", 0); - gpr_mu_lock(&holder->mu); - GPR_ASSERT(holder->creation_phase == - GRPC_SUBCHANNEL_CALL_HOLDER_CREATING_CALL); - holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; - if (GET_CALL(holder) != NULL) { + gpr_atm_rel_store( + &holder->subchannel_call, + (gpr_atm)(gpr_uintptr)grpc_connected_subchannel_create_call( + exec_ctx, holder->connected_subchannel, holder->pollset)); retry_waiting_locked(exec_ctx, holder); - } else { - fail_locked(exec_ctx, holder); } gpr_mu_unlock(&holder->mu); - GPR_TIMER_END("call_ready", 0); + GRPC_CALL_STACK_UNREF(exec_ctx, holder->owning_call, "pick_subchannel"); } typedef struct { @@ -270,14 +247,13 @@ static void fail_locked(grpc_exec_ctx *exec_ctx, holder->waiting_ops_count = 0; } -char *grpc_subchannel_call_holder_get_peer(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder, - grpc_channel *master) { +char *grpc_subchannel_call_holder_get_peer( + grpc_exec_ctx *exec_ctx, grpc_subchannel_call_holder *holder) { grpc_subchannel_call *subchannel_call = GET_CALL(holder); if (subchannel_call) { return grpc_subchannel_call_get_peer(exec_ctx, subchannel_call); } else { - return grpc_channel_get_target(master); + return NULL; } } diff --git a/src/core/channel/subchannel_call_holder.h b/src/core/channel/subchannel_call_holder.h index bda051c566..9cf72c6cf7 100644 --- a/src/core/channel/subchannel_call_holder.h +++ b/src/core/channel/subchannel_call_holder.h @@ -42,12 +42,11 @@ called when the subchannel is available) */ typedef int (*grpc_subchannel_call_holder_pick_subchannel)( grpc_exec_ctx *exec_ctx, void *arg, grpc_metadata_batch *initial_metadata, - grpc_subchannel **subchannel, grpc_closure *on_ready); + grpc_connected_subchannel **connected_subchannel, grpc_closure *on_ready); typedef enum { GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING, - GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL, - GRPC_SUBCHANNEL_CALL_HOLDER_CREATING_CALL + GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL } grpc_subchannel_call_holder_creation_phase; /** Wrapper for holding a pointer to grpc_subchannel_call, and the @@ -71,7 +70,7 @@ typedef struct grpc_subchannel_call_holder { gpr_mu mu; grpc_subchannel_call_holder_creation_phase creation_phase; - grpc_subchannel *subchannel; + grpc_connected_subchannel *connected_subchannel; grpc_pollset *pollset; grpc_transport_stream_op *waiting_ops; @@ -79,12 +78,14 @@ typedef struct grpc_subchannel_call_holder { size_t waiting_ops_capacity; grpc_closure next_step; + + grpc_call_stack *owning_call; } grpc_subchannel_call_holder; void grpc_subchannel_call_holder_init( grpc_subchannel_call_holder *holder, grpc_subchannel_call_holder_pick_subchannel pick_subchannel, - void *pick_subchannel_arg); + void *pick_subchannel_arg, grpc_call_stack *owning_call); void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx, grpc_subchannel_call_holder *holder); @@ -92,7 +93,6 @@ void grpc_subchannel_call_holder_perform_op(grpc_exec_ctx *exec_ctx, grpc_subchannel_call_holder *holder, grpc_transport_stream_op *op); char *grpc_subchannel_call_holder_get_peer(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder, - grpc_channel *master); + grpc_subchannel_call_holder *holder); #endif diff --git a/src/core/client_config/lb_policies/pick_first.c b/src/core/client_config/lb_policies/pick_first.c index 93312abb00..37de3e9f68 100644 --- a/src/core/client_config/lb_policies/pick_first.c +++ b/src/core/client_config/lb_policies/pick_first.c @@ -42,7 +42,7 @@ typedef struct pending_pick { struct pending_pick *next; grpc_pollset *pollset; - grpc_subchannel **target; + grpc_connected_subchannel **target; grpc_closure *on_complete; } pending_pick; @@ -60,7 +60,7 @@ typedef struct { /** the selected channel TODO(ctiller): this should be atomically set so we don't need to take a mutex in the common case */ - grpc_subchannel *selected; + grpc_connected_subchannel *selected; /** have we started picking? */ int started_picking; /** are we shut down? */ @@ -76,24 +76,6 @@ typedef struct { grpc_connectivity_state_tracker state_tracker; } pick_first_lb_policy; -static void del_interested_parties_locked(grpc_exec_ctx *exec_ctx, - pick_first_lb_policy *p) { - pending_pick *pp; - for (pp = p->pending_picks; pp; pp = pp->next) { - grpc_subchannel_del_interested_party( - exec_ctx, p->subchannels[p->checking_subchannel], pp->pollset); - } -} - -static void add_interested_parties_locked(grpc_exec_ctx *exec_ctx, - pick_first_lb_policy *p) { - pending_pick *pp; - for (pp = p->pending_picks; pp; pp = pp->next) { - grpc_subchannel_add_interested_party( - exec_ctx, p->subchannels[p->checking_subchannel], pp->pollset); - } -} - void pf_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; size_t i; @@ -102,7 +84,7 @@ void pf_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "pick_first"); } if (p->selected) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, p->selected, "picked_first"); + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, p->selected, "picked_first"); } grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); gpr_free(p->subchannels); @@ -114,16 +96,26 @@ void pf_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; pending_pick *pp; gpr_mu_lock(&p->mu); - del_interested_parties_locked(exec_ctx, p); p->shutdown = 1; pp = p->pending_picks; p->pending_picks = NULL; grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, "shutdown"); + /* cancel subscription */ + if (p->selected != NULL) { + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, p->selected, NULL, NULL, &p->connectivity_changed); + } else { + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[p->checking_subchannel], NULL, NULL, + &p->connectivity_changed); + } gpr_mu_unlock(&p->mu); while (pp != NULL) { pending_pick *next = pp->next; *pp->target = NULL; + grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, + pp->pollset); grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); pp = next; @@ -131,7 +123,7 @@ void pf_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { } static void pf_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_subchannel **target) { + grpc_connected_subchannel **target) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; pending_pick *pp; gpr_mu_lock(&p->mu); @@ -140,8 +132,8 @@ static void pf_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, while (pp != NULL) { pending_pick *next = pp->next; if (pp->target == target) { - grpc_subchannel_del_interested_party( - exec_ctx, p->subchannels[p->checking_subchannel], pp->pollset); + grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, + pp->pollset); *target = NULL; grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 0); gpr_free(pp); @@ -158,10 +150,11 @@ static void start_picking(grpc_exec_ctx *exec_ctx, pick_first_lb_policy *p) { p->started_picking = 1; p->checking_subchannel = 0; p->checking_connectivity = GRPC_CHANNEL_IDLE; - GRPC_LB_POLICY_REF(&p->base, "pick_first_connectivity"); + GRPC_LB_POLICY_WEAK_REF(&p->base, "pick_first_connectivity"); grpc_subchannel_notify_on_state_change( exec_ctx, p->subchannels[p->checking_subchannel], - &p->checking_connectivity, &p->connectivity_changed); + &p->base.interested_parties, &p->checking_connectivity, + &p->connectivity_changed); } void pf_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { @@ -174,8 +167,8 @@ void pf_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { } int pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_pollset *pollset, - grpc_metadata_batch *initial_metadata, grpc_subchannel **target, - grpc_closure *on_complete) { + grpc_metadata_batch *initial_metadata, + grpc_connected_subchannel **target, grpc_closure *on_complete) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; pending_pick *pp; gpr_mu_lock(&p->mu); @@ -187,8 +180,8 @@ int pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_pollset *pollset, if (!p->started_picking) { start_picking(exec_ctx, p); } - grpc_subchannel_add_interested_party( - exec_ctx, p->subchannels[p->checking_subchannel], pollset); + grpc_pollset_set_add_pollset(exec_ctx, &p->base.interested_parties, + pollset); pp = gpr_malloc(sizeof(*pp)); pp->next = p->pending_picks; pp->pollset = pollset; @@ -204,25 +197,17 @@ static void destroy_subchannels(grpc_exec_ctx *exec_ctx, void *arg, int iomgr_success) { pick_first_lb_policy *p = arg; size_t i; - grpc_transport_op op; size_t num_subchannels = p->num_subchannels; grpc_subchannel **subchannels; - grpc_subchannel *exclude_subchannel; gpr_mu_lock(&p->mu); subchannels = p->subchannels; p->num_subchannels = 0; p->subchannels = NULL; - exclude_subchannel = p->selected; gpr_mu_unlock(&p->mu); - GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "destroy_subchannels"); + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "destroy_subchannels"); for (i = 0; i < num_subchannels; i++) { - if (subchannels[i] != exclude_subchannel) { - memset(&op, 0, sizeof(op)); - op.disconnect = 1; - grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], &op); - } GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pick_first"); } @@ -232,23 +217,28 @@ static void destroy_subchannels(grpc_exec_ctx *exec_ctx, void *arg, static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, int iomgr_success) { pick_first_lb_policy *p = arg; + grpc_subchannel *selected_subchannel; pending_pick *pp; gpr_mu_lock(&p->mu); if (p->shutdown) { gpr_mu_unlock(&p->mu); - GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); return; } else if (p->selected != NULL) { + if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { + /* if the selected channel goes bad, we're done */ + p->checking_connectivity = GRPC_CHANNEL_FATAL_FAILURE; + } grpc_connectivity_state_set(exec_ctx, &p->state_tracker, p->checking_connectivity, "selected_changed"); if (p->checking_connectivity != GRPC_CHANNEL_FATAL_FAILURE) { - grpc_subchannel_notify_on_state_change(exec_ctx, p->selected, - &p->checking_connectivity, - &p->connectivity_changed); + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, p->selected, &p->base.interested_parties, + &p->checking_connectivity, &p->connectivity_changed); } else { - GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); } } else { loop: @@ -256,39 +246,41 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, case GRPC_CHANNEL_READY: grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_READY, "connecting_ready"); - p->selected = p->subchannels[p->checking_subchannel]; - GRPC_SUBCHANNEL_REF(p->selected, "picked_first"); + selected_subchannel = p->subchannels[p->checking_subchannel]; + p->selected = + grpc_subchannel_get_connected_subchannel(selected_subchannel); + GPR_ASSERT(p->selected); + GRPC_CONNECTED_SUBCHANNEL_REF(p->selected, "picked_first"); /* drop the pick list: we are connected now */ - GRPC_LB_POLICY_REF(&p->base, "destroy_subchannels"); + GRPC_LB_POLICY_WEAK_REF(&p->base, "destroy_subchannels"); grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(destroy_subchannels, p), 1); /* update any calls that were waiting for a pick */ while ((pp = p->pending_picks)) { p->pending_picks = pp->next; *pp->target = p->selected; - grpc_subchannel_del_interested_party(exec_ctx, p->selected, - pp->pollset); + grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, + pp->pollset); grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); } - grpc_subchannel_notify_on_state_change(exec_ctx, p->selected, - &p->checking_connectivity, - &p->connectivity_changed); + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, p->selected, &p->base.interested_parties, + &p->checking_connectivity, &p->connectivity_changed); break; case GRPC_CHANNEL_TRANSIENT_FAILURE: grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, "connecting_transient_failure"); - del_interested_parties_locked(exec_ctx, p); p->checking_subchannel = (p->checking_subchannel + 1) % p->num_subchannels; p->checking_connectivity = grpc_subchannel_check_connectivity( p->subchannels[p->checking_subchannel]); - add_interested_parties_locked(exec_ctx, p); if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { grpc_subchannel_notify_on_state_change( exec_ctx, p->subchannels[p->checking_subchannel], - &p->checking_connectivity, &p->connectivity_changed); + &p->base.interested_parties, &p->checking_connectivity, + &p->connectivity_changed); } else { goto loop; } @@ -300,13 +292,13 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, "connecting_changed"); grpc_subchannel_notify_on_state_change( exec_ctx, p->subchannels[p->checking_subchannel], - &p->checking_connectivity, &p->connectivity_changed); + &p->base.interested_parties, &p->checking_connectivity, + &p->connectivity_changed); break; case GRPC_CHANNEL_FATAL_FAILURE: - del_interested_parties_locked(exec_ctx, p); - GPR_SWAP(grpc_subchannel *, p->subchannels[p->checking_subchannel], - p->subchannels[p->num_subchannels - 1]); p->num_subchannels--; + GPR_SWAP(grpc_subchannel *, p->subchannels[p->checking_subchannel], + p->subchannels[p->num_subchannels]); GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels], "pick_first"); if (p->num_subchannels == 0) { @@ -319,7 +311,8 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); } - GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, + "pick_first_connectivity"); } else { grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, @@ -327,7 +320,6 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, p->checking_subchannel %= p->num_subchannels; p->checking_connectivity = grpc_subchannel_check_connectivity( p->subchannels[p->checking_subchannel]); - add_interested_parties_locked(exec_ctx, p); goto loop; } } @@ -336,39 +328,6 @@ static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, gpr_mu_unlock(&p->mu); } -static void pf_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_transport_op *op) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - size_t i; - size_t n; - grpc_subchannel **subchannels; - grpc_subchannel *selected; - - gpr_mu_lock(&p->mu); - n = p->num_subchannels; - subchannels = gpr_malloc(n * sizeof(*subchannels)); - selected = p->selected; - if (selected) { - GRPC_SUBCHANNEL_REF(selected, "pf_broadcast_to_selected"); - } - for (i = 0; i < n; i++) { - subchannels[i] = p->subchannels[i]; - GRPC_SUBCHANNEL_REF(subchannels[i], "pf_broadcast"); - } - gpr_mu_unlock(&p->mu); - - for (i = 0; i < n; i++) { - if (selected == subchannels[i]) continue; - grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], op); - GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pf_broadcast"); - } - if (p->selected) { - grpc_subchannel_process_transport_op(exec_ctx, selected, op); - GRPC_SUBCHANNEL_UNREF(exec_ctx, selected, "pf_broadcast_to_selected"); - } - gpr_free(subchannels); -} - static grpc_connectivity_state pf_check_connectivity(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { pick_first_lb_policy *p = (pick_first_lb_policy *)pol; @@ -389,9 +348,21 @@ void pf_notify_on_state_change(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, gpr_mu_unlock(&p->mu); } +void pf_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_closure *closure) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + gpr_mu_lock(&p->mu); + if (p->selected) { + grpc_connected_subchannel_ping(exec_ctx, p->selected, closure); + } else { + grpc_exec_ctx_enqueue(exec_ctx, closure, 0); + } + gpr_mu_unlock(&p->mu); +} + static const grpc_lb_policy_vtable pick_first_lb_policy_vtable = { - pf_destroy, pf_shutdown, pf_pick, pf_cancel_pick, pf_exit_idle, - pf_broadcast, pf_check_connectivity, pf_notify_on_state_change}; + pf_destroy, pf_shutdown, pf_pick, pf_cancel_pick, pf_ping_one, pf_exit_idle, + pf_check_connectivity, pf_notify_on_state_change}; static void pick_first_factory_ref(grpc_lb_policy_factory *factory) {} diff --git a/src/core/client_config/lb_policies/round_robin.c b/src/core/client_config/lb_policies/round_robin.c index 1ffe32fff2..d487456363 100644 --- a/src/core/client_config/lb_policies/round_robin.c +++ b/src/core/client_config/lb_policies/round_robin.c @@ -38,6 +38,8 @@ #include <grpc/support/alloc.h> #include "src/core/transport/connectivity_state.h" +typedef struct round_robin_lb_policy round_robin_lb_policy; + int grpc_lb_round_robin_trace = 0; /** List of entities waiting for a pick. @@ -46,7 +48,7 @@ int grpc_lb_round_robin_trace = 0; typedef struct pending_pick { struct pending_pick *next; grpc_pollset *pollset; - grpc_subchannel **target; + grpc_connected_subchannel **target; grpc_closure *on_complete; } pending_pick; @@ -58,22 +60,27 @@ typedef struct ready_list { } ready_list; typedef struct { - size_t subchannel_idx; /**< Index over p->subchannels */ - void *p; /**< round_robin_lb_policy instance */ -} connectivity_changed_cb_arg; - -typedef struct { + /** index within policy->subchannels */ + size_t index; + /** backpointer to owning policy */ + round_robin_lb_policy *policy; + /** subchannel itself */ + grpc_subchannel *subchannel; + /** notification that connectivity has changed on subchannel */ + grpc_closure connectivity_changed_closure; + /** this subchannels current position in subchannel->ready_list */ + ready_list *ready_list_node; + /** last observed connectivity */ + grpc_connectivity_state connectivity_state; +} subchannel_data; + +struct round_robin_lb_policy { /** base policy: must be first */ grpc_lb_policy base; /** all our subchannels */ - grpc_subchannel **subchannels; size_t num_subchannels; - - /** Callbacks, one per subchannel being watched, to be called when their - * respective connectivity changes */ - grpc_closure *connectivity_changed_cbs; - connectivity_changed_cb_arg *cb_args; + subchannel_data **subchannels; /** mutex protecting remaining members */ gpr_mu mu; @@ -81,8 +88,6 @@ typedef struct { int started_picking; /** are we shutting down? */ int shutdown; - /** Connectivity state of the subchannels being watched */ - grpc_connectivity_state *subchannel_connectivity; /** List of picks that are waiting on connectivity */ pending_pick *pending_picks; @@ -93,13 +98,7 @@ typedef struct { ready_list ready_list; /** Last pick from the ready list. */ ready_list *ready_list_last_pick; - - /** Subchannel index to ready_list node. - * - * Kept in order to remove nodes from the ready list associated with a - * subchannel */ - ready_list **subchannel_index_to_readylist_node; -} round_robin_lb_policy; +}; /** Returns the next subchannel from the connected list or NULL if the list is * empty. @@ -144,9 +143,9 @@ static void advance_last_picked_locked(round_robin_lb_policy *p) { /** Prepends (relative to the root at p->ready_list) the connected subchannel \a * csc to the list of ready subchannels. */ static ready_list *add_connected_sc_locked(round_robin_lb_policy *p, - grpc_subchannel *csc) { + grpc_subchannel *sc) { ready_list *new_elem = gpr_malloc(sizeof(ready_list)); - new_elem->subchannel = csc; + new_elem->subchannel = sc; if (p->ready_list.prev == NULL) { /* first element */ new_elem->next = &p->ready_list; @@ -160,7 +159,7 @@ static ready_list *add_connected_sc_locked(round_robin_lb_policy *p, p->ready_list.prev = new_elem; } if (grpc_lb_round_robin_trace) { - gpr_log(GPR_DEBUG, "[READYLIST] ADDING NODE %p (SC %p)", new_elem, csc); + gpr_log(GPR_DEBUG, "[READYLIST] ADDING NODE %p (SC %p)", new_elem, sc); } return new_elem; } @@ -200,28 +199,15 @@ static void remove_disconnected_sc_locked(round_robin_lb_policy *p, gpr_free(node); } -static void del_interested_parties_locked(grpc_exec_ctx *exec_ctx, - round_robin_lb_policy *p, - const size_t subchannel_idx) { - pending_pick *pp; - for (pp = p->pending_picks; pp; pp = pp->next) { - grpc_subchannel_del_interested_party( - exec_ctx, p->subchannels[subchannel_idx], pp->pollset); - } -} - void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { round_robin_lb_policy *p = (round_robin_lb_policy *)pol; size_t i; ready_list *elem; for (i = 0; i < p->num_subchannels; i++) { - del_interested_parties_locked(exec_ctx, p, i); - } - for (i = 0; i < p->num_subchannels; i++) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "round_robin"); + subchannel_data *sd = p->subchannels[i]; + GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "round_robin"); + gpr_free(sd); } - gpr_free(p->connectivity_changed_cbs); - gpr_free(p->subchannel_connectivity); grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); gpr_free(p->subchannels); @@ -237,20 +223,15 @@ void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { gpr_free(elem); elem = tmp; } - gpr_free(p->subchannel_index_to_readylist_node); - gpr_free(p->cb_args); gpr_free(p); } void rr_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - size_t i; round_robin_lb_policy *p = (round_robin_lb_policy *)pol; pending_pick *pp; - gpr_mu_lock(&p->mu); + size_t i; - for (i = 0; i < p->num_subchannels; i++) { - del_interested_parties_locked(exec_ctx, p, i); - } + gpr_mu_lock(&p->mu); p->shutdown = 1; while ((pp = p->pending_picks)) { @@ -261,24 +242,26 @@ void rr_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { } grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, "shutdown"); + for (i = 0; i < p->num_subchannels; i++) { + subchannel_data *sd = p->subchannels[i]; + grpc_subchannel_notify_on_state_change(exec_ctx, sd->subchannel, NULL, NULL, + &sd->connectivity_changed_closure); + } gpr_mu_unlock(&p->mu); } static void rr_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_subchannel **target) { + grpc_connected_subchannel **target) { round_robin_lb_policy *p = (round_robin_lb_policy *)pol; pending_pick *pp; - size_t i; gpr_mu_lock(&p->mu); pp = p->pending_picks; p->pending_picks = NULL; while (pp != NULL) { pending_pick *next = pp->next; if (pp->target == target) { - for (i = 0; i < p->num_subchannels; i++) { - grpc_subchannel_add_interested_party(exec_ctx, p->subchannels[i], - pp->pollset); - } + grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, + pp->pollset); *target = NULL; grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 0); gpr_free(pp); @@ -295,12 +278,16 @@ static void start_picking(grpc_exec_ctx *exec_ctx, round_robin_lb_policy *p) { size_t i; p->started_picking = 1; + gpr_log(GPR_DEBUG, "LB_POLICY: p=%p num_subchannels=%d", p, + p->num_subchannels); + for (i = 0; i < p->num_subchannels; i++) { - p->subchannel_connectivity[i] = GRPC_CHANNEL_IDLE; - grpc_subchannel_notify_on_state_change(exec_ctx, p->subchannels[i], - &p->subchannel_connectivity[i], - &p->connectivity_changed_cbs[i]); - GRPC_LB_POLICY_REF(&p->base, "round_robin_connectivity"); + subchannel_data *sd = p->subchannels[i]; + sd->connectivity_state = GRPC_CHANNEL_IDLE; + grpc_subchannel_notify_on_state_change( + exec_ctx, sd->subchannel, &p->base.interested_parties, + &sd->connectivity_state, &sd->connectivity_changed_closure); + GRPC_LB_POLICY_WEAK_REF(&p->base, "round_robin_connectivity"); } } @@ -314,18 +301,18 @@ void rr_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { } int rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_pollset *pollset, - grpc_metadata_batch *initial_metadata, grpc_subchannel **target, - grpc_closure *on_complete) { - size_t i; + grpc_metadata_batch *initial_metadata, + grpc_connected_subchannel **target, grpc_closure *on_complete) { round_robin_lb_policy *p = (round_robin_lb_policy *)pol; pending_pick *pp; ready_list *selected; gpr_mu_lock(&p->mu); if ((selected = peek_next_connected_locked(p))) { gpr_mu_unlock(&p->mu); - *target = selected->subchannel; + *target = grpc_subchannel_get_connected_subchannel(selected->subchannel); if (grpc_lb_round_robin_trace) { - gpr_log(GPR_DEBUG, "[RR PICK] TARGET <-- SUBCHANNEL %p (NODE %p)", + gpr_log(GPR_DEBUG, + "[RR PICK] TARGET <-- CONNECTED SUBCHANNEL %p (NODE %p)", selected->subchannel, selected); } /* only advance the last picked pointer if the selection was used */ @@ -335,10 +322,8 @@ int rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_pollset *pollset, if (!p->started_picking) { start_picking(exec_ctx, p); } - for (i = 0; i < p->num_subchannels; i++) { - grpc_subchannel_add_interested_party(exec_ctx, p->subchannels[i], - pollset); - } + grpc_pollset_set_add_pollset(exec_ctx, &p->base.interested_parties, + pollset); pp = gpr_malloc(sizeof(*pp)); pp->next = p->pending_picks; pp->pollset = pollset; @@ -352,33 +337,25 @@ int rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_pollset *pollset, static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, int iomgr_success) { - connectivity_changed_cb_arg *cb_arg = arg; - round_robin_lb_policy *p = cb_arg->p; - /* index over p->subchannels of this cb's subchannel */ - const size_t this_idx = cb_arg->subchannel_idx; + subchannel_data *sd = arg; + round_robin_lb_policy *p = sd->policy; pending_pick *pp; ready_list *selected; int unref = 0; - /* connectivity state of this cb's subchannel */ - grpc_connectivity_state *this_connectivity; - gpr_mu_lock(&p->mu); - this_connectivity = &p->subchannel_connectivity[this_idx]; - if (p->shutdown) { unref = 1; } else { - switch (*this_connectivity) { + switch (sd->connectivity_state) { case GRPC_CHANNEL_READY: grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_READY, "connecting_ready"); /* add the newly connected subchannel to the list of connected ones. * Note that it goes to the "end of the line". */ - p->subchannel_index_to_readylist_node[this_idx] = - add_connected_sc_locked(p, p->subchannels[this_idx]); + sd->ready_list_node = add_connected_sc_locked(p, sd->subchannel); /* at this point we know there's at least one suitable subchannel. Go * ahead and pick one and notify the pending suitors in * p->pending_picks. This preemtively replicates rr_pick()'s actions. */ @@ -390,60 +367,60 @@ static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, } while ((pp = p->pending_picks)) { p->pending_picks = pp->next; - *pp->target = selected->subchannel; + *pp->target = + grpc_subchannel_get_connected_subchannel(selected->subchannel); if (grpc_lb_round_robin_trace) { gpr_log(GPR_DEBUG, "[RR CONN CHANGED] TARGET <-- SUBCHANNEL %p (NODE %p)", selected->subchannel, selected); } - grpc_subchannel_del_interested_party(exec_ctx, selected->subchannel, - pp->pollset); + grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, + pp->pollset); grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); } grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[this_idx], this_connectivity, - &p->connectivity_changed_cbs[this_idx]); + exec_ctx, sd->subchannel, &p->base.interested_parties, + &sd->connectivity_state, &sd->connectivity_changed_closure); break; case GRPC_CHANNEL_CONNECTING: case GRPC_CHANNEL_IDLE: grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - *this_connectivity, "connecting_changed"); + sd->connectivity_state, + "connecting_changed"); grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[this_idx], this_connectivity, - &p->connectivity_changed_cbs[this_idx]); + exec_ctx, sd->subchannel, &p->base.interested_parties, + &sd->connectivity_state, &sd->connectivity_changed_closure); break; case GRPC_CHANNEL_TRANSIENT_FAILURE: - del_interested_parties_locked(exec_ctx, p, this_idx); /* renew state notification */ grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[this_idx], this_connectivity, - &p->connectivity_changed_cbs[this_idx]); + exec_ctx, sd->subchannel, &p->base.interested_parties, + &sd->connectivity_state, &sd->connectivity_changed_closure); /* remove from ready list if still present */ - if (p->subchannel_index_to_readylist_node[this_idx] != NULL) { - remove_disconnected_sc_locked( - p, p->subchannel_index_to_readylist_node[this_idx]); - p->subchannel_index_to_readylist_node[this_idx] = NULL; + if (sd->ready_list_node != NULL) { + remove_disconnected_sc_locked(p, sd->ready_list_node); + sd->ready_list_node = NULL; } grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, "connecting_transient_failure"); break; case GRPC_CHANNEL_FATAL_FAILURE: - del_interested_parties_locked(exec_ctx, p, this_idx); - if (p->subchannel_index_to_readylist_node[this_idx] != NULL) { - remove_disconnected_sc_locked( - p, p->subchannel_index_to_readylist_node[this_idx]); - p->subchannel_index_to_readylist_node[this_idx] = NULL; + if (sd->ready_list_node != NULL) { + remove_disconnected_sc_locked(p, sd->ready_list_node); + sd->ready_list_node = NULL; } - GPR_SWAP(grpc_subchannel *, p->subchannels[this_idx], - p->subchannels[p->num_subchannels - 1]); p->num_subchannels--; - GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels], - "round_robin"); + GPR_SWAP(subchannel_data *, p->subchannels[sd->index], + p->subchannels[p->num_subchannels]); + GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "round_robin"); + p->subchannels[sd->index]->index = sd->index; + gpr_free(sd); + unref = 1; if (p->num_subchannels == 0) { grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_FATAL_FAILURE, @@ -454,7 +431,6 @@ static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, 1); gpr_free(pp); } - unref = 1; } else { grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, @@ -466,31 +442,8 @@ static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, gpr_mu_unlock(&p->mu); if (unref) { - GRPC_LB_POLICY_UNREF(exec_ctx, &p->base, "round_robin_connectivity"); - } -} - -static void rr_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_transport_op *op) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - size_t i; - size_t n; - grpc_subchannel **subchannels; - - gpr_mu_lock(&p->mu); - n = p->num_subchannels; - subchannels = gpr_malloc(n * sizeof(*subchannels)); - for (i = 0; i < n; i++) { - subchannels[i] = p->subchannels[i]; - GRPC_SUBCHANNEL_REF(subchannels[i], "rr_broadcast"); - } - gpr_mu_unlock(&p->mu); - - for (i = 0; i < n; i++) { - grpc_subchannel_process_transport_op(exec_ctx, subchannels[i], op); - GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "rr_broadcast"); + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "round_robin_connectivity"); } - gpr_free(subchannels); } static grpc_connectivity_state rr_check_connectivity(grpc_exec_ctx *exec_ctx, @@ -514,9 +467,25 @@ static void rr_notify_on_state_change(grpc_exec_ctx *exec_ctx, gpr_mu_unlock(&p->mu); } +static void rr_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_closure *closure) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + ready_list *selected; + grpc_connected_subchannel *target; + gpr_mu_lock(&p->mu); + if ((selected = peek_next_connected_locked(p))) { + gpr_mu_unlock(&p->mu); + target = grpc_subchannel_get_connected_subchannel(selected->subchannel); + grpc_connected_subchannel_ping(exec_ctx, target, closure); + } else { + gpr_mu_unlock(&p->mu); + grpc_exec_ctx_enqueue(exec_ctx, closure, 0); + } +} + static const grpc_lb_policy_vtable round_robin_lb_policy_vtable = { - rr_destroy, rr_shutdown, rr_pick, rr_cancel_pick, rr_exit_idle, - rr_broadcast, rr_check_connectivity, rr_notify_on_state_change}; + rr_destroy, rr_shutdown, rr_pick, rr_cancel_pick, rr_ping_one, rr_exit_idle, + rr_check_connectivity, rr_notify_on_state_change}; static void round_robin_factory_ref(grpc_lb_policy_factory *factory) {} @@ -529,27 +498,22 @@ static grpc_lb_policy *create_round_robin(grpc_lb_policy_factory *factory, GPR_ASSERT(args->num_subchannels > 0); memset(p, 0, sizeof(*p)); grpc_lb_policy_init(&p->base, &round_robin_lb_policy_vtable); - p->subchannels = - gpr_malloc(sizeof(grpc_subchannel *) * args->num_subchannels); p->num_subchannels = args->num_subchannels; + p->subchannels = gpr_malloc(sizeof(*p->subchannels) * p->num_subchannels); + memset(p->subchannels, 0, sizeof(*p->subchannels) * p->num_subchannels); grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE, "round_robin"); - memcpy(p->subchannels, args->subchannels, - sizeof(grpc_subchannel *) * args->num_subchannels); gpr_mu_init(&p->mu); - p->connectivity_changed_cbs = - gpr_malloc(sizeof(grpc_closure) * args->num_subchannels); - p->subchannel_connectivity = - gpr_malloc(sizeof(grpc_connectivity_state) * args->num_subchannels); - - p->cb_args = - gpr_malloc(sizeof(connectivity_changed_cb_arg) * args->num_subchannels); for (i = 0; i < args->num_subchannels; i++) { - p->cb_args[i].subchannel_idx = i; - p->cb_args[i].p = p; - grpc_closure_init(&p->connectivity_changed_cbs[i], rr_connectivity_changed, - &p->cb_args[i]); + subchannel_data *sd = gpr_malloc(sizeof(*sd)); + memset(sd, 0, sizeof(*sd)); + p->subchannels[i] = sd; + sd->policy = p; + sd->index = i; + sd->subchannel = args->subchannels[i]; + grpc_closure_init(&sd->connectivity_changed_closure, + rr_connectivity_changed, sd); } /* The (dummy node) root of the ready list */ @@ -558,10 +522,6 @@ static grpc_lb_policy *create_round_robin(grpc_lb_policy_factory *factory, p->ready_list.next = NULL; p->ready_list_last_pick = &p->ready_list; - p->subchannel_index_to_readylist_node = - gpr_malloc(sizeof(grpc_subchannel *) * args->num_subchannels); - memset(p->subchannel_index_to_readylist_node, 0, - sizeof(grpc_subchannel *) * args->num_subchannels); return &p->base; } diff --git a/src/core/client_config/lb_policy.c b/src/core/client_config/lb_policy.c index 36a2454309..d4672f6b25 100644 --- a/src/core/client_config/lb_policy.c +++ b/src/core/client_config/lb_policy.c @@ -33,63 +33,94 @@ #include "src/core/client_config/lb_policy.h" +#define WEAK_REF_BITS 16 + void grpc_lb_policy_init(grpc_lb_policy *policy, const grpc_lb_policy_vtable *vtable) { policy->vtable = vtable; - gpr_ref_init(&policy->refs, 1); + gpr_atm_no_barrier_store(&policy->ref_pair, 1 << WEAK_REF_BITS); + grpc_pollset_set_init(&policy->interested_parties); } #ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG -void grpc_lb_policy_ref(grpc_lb_policy *policy, const char *file, int line, - const char *reason) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "LB_POLICY:%p ref %d -> %d %s", - policy, (int)policy->refs.count, (int)policy->refs.count + 1, reason); +#define REF_FUNC_EXTRA_ARGS , const char *file, int line, const char *reason +#define REF_MUTATE_EXTRA_ARGS REF_FUNC_EXTRA_ARGS, const char *purpose +#define REF_FUNC_PASS_ARGS(new_reason) , file, line, new_reason +#define REF_MUTATE_PASS_ARGS(purpose) , file, line, reason, purpose #else -void grpc_lb_policy_ref(grpc_lb_policy *policy) { +#define REF_FUNC_EXTRA_ARGS +#define REF_MUTATE_EXTRA_ARGS +#define REF_FUNC_PASS_ARGS(new_reason) +#define REF_MUTATE_PASS_ARGS(x) #endif - gpr_ref(&policy->refs); -} +static gpr_atm ref_mutate(grpc_lb_policy *c, gpr_atm delta, + int barrier REF_MUTATE_EXTRA_ARGS) { + gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta) + : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta); #ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG -void grpc_lb_policy_unref(grpc_lb_policy *policy, - grpc_closure_list *closure_list, const char *file, - int line, const char *reason) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "LB_POLICY:%p unref %d -> %d %s", - policy, (int)policy->refs.count, (int)policy->refs.count - 1, reason); -#else -void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "LB_POLICY: %p % 12s 0x%08x -> 0x%08x [%s]", c, purpose, old_val, + old_val + delta, reason); #endif - if (gpr_unref(&policy->refs)) { - policy->vtable->destroy(exec_ctx, policy); + return old_val; +} + +void grpc_lb_policy_ref(grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { + ref_mutate(policy, 1 << WEAK_REF_BITS, 0 REF_MUTATE_PASS_ARGS("STRONG_REF")); +} + +void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { + gpr_atm old_val = + ref_mutate(policy, (gpr_atm)1 - (gpr_atm)(1 << WEAK_REF_BITS), + 1 REF_MUTATE_PASS_ARGS("STRONG_UNREF")); + gpr_atm mask = ~(gpr_atm)((1 << WEAK_REF_BITS) - 1); + gpr_atm check = 1 << WEAK_REF_BITS; + if ((old_val & mask) == check) { + policy->vtable->shutdown(exec_ctx, policy); } + grpc_lb_policy_weak_unref(exec_ctx, + policy REF_FUNC_PASS_ARGS("strong-unref")); } -void grpc_lb_policy_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) { - policy->vtable->shutdown(exec_ctx, policy); +void grpc_lb_policy_weak_ref(grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { + ref_mutate(policy, 1, 0 REF_MUTATE_PASS_ARGS("WEAK_REF")); +} + +void grpc_lb_policy_weak_unref(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { + gpr_atm old_val = + ref_mutate(policy, -(gpr_atm)1, 1 REF_MUTATE_PASS_ARGS("WEAK_UNREF")); + if (old_val == 1) { + grpc_pollset_set_destroy(&policy->interested_parties); + policy->vtable->destroy(exec_ctx, policy); + } } int grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, grpc_pollset *pollset, grpc_metadata_batch *initial_metadata, - grpc_subchannel **target, grpc_closure *on_complete) { + grpc_connected_subchannel **target, + grpc_closure *on_complete) { return policy->vtable->pick(exec_ctx, policy, pollset, initial_metadata, target, on_complete); } void grpc_lb_policy_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_subchannel **target) { + grpc_connected_subchannel **target) { policy->vtable->cancel_pick(exec_ctx, policy, target); } -void grpc_lb_policy_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_transport_op *op) { - policy->vtable->broadcast(exec_ctx, policy, op); -} - void grpc_lb_policy_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) { policy->vtable->exit_idle(exec_ctx, policy); } +void grpc_lb_policy_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_closure *closure) { + policy->vtable->ping_one(exec_ctx, policy, closure); +} + void grpc_lb_policy_notify_on_state_change(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, grpc_connectivity_state *state, diff --git a/src/core/client_config/lb_policy.h b/src/core/client_config/lb_policy.h index a696c3ce64..db5238c8ca 100644 --- a/src/core/client_config/lb_policy.h +++ b/src/core/client_config/lb_policy.h @@ -47,7 +47,8 @@ typedef void (*grpc_lb_completion)(void *cb_arg, grpc_subchannel *subchannel, struct grpc_lb_policy { const grpc_lb_policy_vtable *vtable; - gpr_refcount refs; + gpr_atm ref_pair; + grpc_pollset_set interested_parties; }; struct grpc_lb_policy_vtable { @@ -58,17 +59,16 @@ struct grpc_lb_policy_vtable { /** implement grpc_lb_policy_pick */ int (*pick)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, grpc_pollset *pollset, grpc_metadata_batch *initial_metadata, - grpc_subchannel **target, grpc_closure *on_complete); + grpc_connected_subchannel **target, grpc_closure *on_complete); void (*cancel_pick)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_subchannel **target); + grpc_connected_subchannel **target); + + void (*ping_one)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_closure *closure); /** try to enter a READY connectivity state */ void (*exit_idle)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); - /** broadcast a transport op to all subchannels */ - void (*broadcast)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_transport_op *op); - /** check the current connectivity of the lb_policy */ grpc_connectivity_state (*check_connectivity)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); @@ -81,29 +81,39 @@ struct grpc_lb_policy_vtable { grpc_closure *closure); }; +/*#define GRPC_LB_POLICY_REFCOUNT_DEBUG*/ #ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG #define GRPC_LB_POLICY_REF(p, r) \ grpc_lb_policy_ref((p), __FILE__, __LINE__, (r)) #define GRPC_LB_POLICY_UNREF(exec_ctx, p, r) \ grpc_lb_policy_unref((exec_ctx), (p), __FILE__, __LINE__, (r)) +#define GRPC_LB_POLICY_WEAK_REF(p, r) \ + grpc_lb_policy_weak_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, p, r) \ + grpc_lb_policy_weak_unref((exec_ctx), (p), __FILE__, __LINE__, (r)) void grpc_lb_policy_ref(grpc_lb_policy *policy, const char *file, int line, const char *reason); void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, const char *file, int line, const char *reason); +void grpc_lb_policy_weak_ref(grpc_lb_policy *policy, const char *file, int line, + const char *reason); +void grpc_lb_policy_weak_unref(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + const char *file, int line, const char *reason); #else #define GRPC_LB_POLICY_REF(p, r) grpc_lb_policy_ref((p)) #define GRPC_LB_POLICY_UNREF(cl, p, r) grpc_lb_policy_unref((cl), (p)) +#define GRPC_LB_POLICY_WEAK_REF(p, r) grpc_lb_policy_weak_ref((p)) +#define GRPC_LB_POLICY_WEAK_UNREF(cl, p, r) grpc_lb_policy_weak_unref((cl), (p)) void grpc_lb_policy_ref(grpc_lb_policy *policy); void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); +void grpc_lb_policy_weak_ref(grpc_lb_policy *policy); +void grpc_lb_policy_weak_unref(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); #endif /** called by concrete implementations to initialize the base struct */ void grpc_lb_policy_init(grpc_lb_policy *policy, const grpc_lb_policy_vtable *vtable); -/** Start shutting down (fail any pending picks) */ -void grpc_lb_policy_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); - /** Given initial metadata in \a initial_metadata, find an appropriate target for this rpc, and 'return' it by calling \a on_complete after setting \a target. @@ -111,13 +121,14 @@ void grpc_lb_policy_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); int grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, grpc_pollset *pollset, grpc_metadata_batch *initial_metadata, - grpc_subchannel **target, grpc_closure *on_complete); + grpc_connected_subchannel **target, + grpc_closure *on_complete); -void grpc_lb_policy_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_subchannel **target); +void grpc_lb_policy_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_closure *closure); -void grpc_lb_policy_broadcast(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_transport_op *op); +void grpc_lb_policy_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_connected_subchannel **target); void grpc_lb_policy_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); diff --git a/src/core/client_config/resolver.c b/src/core/client_config/resolver.c index 081097eb19..eda01e72ba 100644 --- a/src/core/client_config/resolver.c +++ b/src/core/client_config/resolver.c @@ -71,11 +71,8 @@ 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, - struct sockaddr *failing_address, - int failing_address_len) { - resolver->vtable->channel_saw_error(exec_ctx, resolver, failing_address, - failing_address_len); + grpc_resolver *resolver) { + resolver->vtable->channel_saw_error(exec_ctx, resolver); } void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, diff --git a/src/core/client_config/resolver.h b/src/core/client_config/resolver.h index 7ba0cd5bd4..e612eaf3b3 100644 --- a/src/core/client_config/resolver.h +++ b/src/core/client_config/resolver.h @@ -35,8 +35,8 @@ #define GRPC_INTERNAL_CORE_CLIENT_CONFIG_RESOLVER_H #include "src/core/client_config/client_config.h" +#include "src/core/client_config/subchannel.h" #include "src/core/iomgr/iomgr.h" -#include "src/core/iomgr/sockaddr.h" typedef struct grpc_resolver grpc_resolver; typedef struct grpc_resolver_vtable grpc_resolver_vtable; @@ -51,9 +51,7 @@ struct grpc_resolver { struct grpc_resolver_vtable { void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); - void (*channel_saw_error)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - struct sockaddr *failing_address, - int failing_address_len); + void (*channel_saw_error)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); void (*next)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, grpc_client_config **target_config, grpc_closure *on_complete); }; @@ -81,9 +79,7 @@ void grpc_resolver_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); /** Notification that the channel has seen an error on some address. Can be used as a hint that re-resolution is desirable soon. */ void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver, - struct sockaddr *failing_address, - int failing_address_len); + grpc_resolver *resolver); /** Get the next client config. Called by the channel to fetch a new configuration. Expected to set *target_config with a new configuration, diff --git a/src/core/client_config/resolvers/dns_resolver.c b/src/core/client_config/resolvers/dns_resolver.c index 7f9dd2543f..28ca30b946 100644 --- a/src/core/client_config/resolvers/dns_resolver.c +++ b/src/core/client_config/resolvers/dns_resolver.c @@ -40,7 +40,6 @@ #include <grpc/support/string_util.h> #include "src/core/client_config/lb_policy_registry.h" -#include "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h" #include "src/core/iomgr/resolve_address.h" #include "src/core/support/string.h" @@ -81,9 +80,7 @@ static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, dns_resolver *r); 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, - struct sockaddr *failing_address, - int failing_address_len); +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_client_config **target_config, grpc_closure *on_complete); @@ -103,8 +100,7 @@ static void dns_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver) { } static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver, struct sockaddr *sa, - int len) { + grpc_resolver *resolver) { dns_resolver *r = (dns_resolver *)resolver; gpr_mu_lock(&r->mu); if (!r->resolving) { diff --git a/src/core/client_config/resolvers/sockaddr_resolver.c b/src/core/client_config/resolvers/sockaddr_resolver.c index 0b017f06c7..81d6627ecc 100644 --- a/src/core/client_config/resolvers/sockaddr_resolver.c +++ b/src/core/client_config/resolvers/sockaddr_resolver.c @@ -83,9 +83,7 @@ static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, 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, - struct sockaddr *failing_address, - int failing_address_len); + grpc_resolver *r); static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, grpc_client_config **target_config, grpc_closure *on_complete); @@ -107,8 +105,13 @@ static void sockaddr_shutdown(grpc_exec_ctx *exec_ctx, } static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver, - struct sockaddr *sa, int len) {} + grpc_resolver *resolver) { + sockaddr_resolver *r = (sockaddr_resolver *)resolver; + gpr_mu_lock(&r->mu); + r->published = 0; + sockaddr_maybe_finish_next_locked(exec_ctx, r); + gpr_mu_unlock(&r->mu); +} static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, grpc_client_config **target_config, @@ -344,6 +347,9 @@ static grpc_resolver *sockaddr_create( gpr_slice_buffer_destroy(&path_parts); gpr_slice_unref(path_slice); if (errors_found) { + gpr_free(r->lb_policy_name); + gpr_free(r->addrs); + gpr_free(r->addrs_len); gpr_free(r); return NULL; } diff --git a/src/core/client_config/resolvers/zookeeper_resolver.c b/src/core/client_config/resolvers/zookeeper_resolver.c index 136197d4c6..4924ca77d6 100644 --- a/src/core/client_config/resolvers/zookeeper_resolver.c +++ b/src/core/client_config/resolvers/zookeeper_resolver.c @@ -96,9 +96,7 @@ static void zookeeper_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, static void zookeeper_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *r, - struct sockaddr *failing_address, - int failing_address_len); + grpc_resolver *r); static void zookeeper_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, grpc_client_config **target_config, grpc_closure *on_complete); @@ -125,8 +123,7 @@ static void zookeeper_shutdown(grpc_exec_ctx *exec_ctx, } static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver, - struct sockaddr *sa, int len) { + grpc_resolver *resolver) { zookeeper_resolver *r = (zookeeper_resolver *)resolver; gpr_mu_lock(&r->mu); if (r->resolving == 0) { diff --git a/src/core/client_config/subchannel.c b/src/core/client_config/subchannel.c index 28496ac2c9..afb1cdbd6d 100644 --- a/src/core/client_config/subchannel.c +++ b/src/core/client_config/subchannel.c @@ -47,39 +47,44 @@ #include "src/core/transport/connectivity_state.h" #include "src/core/transport/connectivity_state.h" +#define INTERNAL_REF_BITS 16 +#define STRONG_REF_MASK (~(gpr_atm)((1 << INTERNAL_REF_BITS) - 1)) + #define GRPC_SUBCHANNEL_MIN_CONNECT_TIMEOUT_SECONDS 20 #define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 1 #define GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER 1.6 #define GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS 120 #define GRPC_SUBCHANNEL_RECONNECT_JITTER 0.2 -typedef struct { - /* all fields protected by subchannel->mu */ - /** refcount */ - int refs; - /** parent subchannel */ - grpc_subchannel *subchannel; -} connection; +#define GET_CONNECTED_SUBCHANNEL(subchannel, barrier) \ + ((grpc_connected_subchannel *)(gpr_atm_##barrier##_load( \ + &(subchannel)->connected_subchannel))) typedef struct { grpc_closure closure; - size_t version; grpc_subchannel *subchannel; grpc_connectivity_state connectivity_state; } state_watcher; -typedef struct waiting_for_connect { - struct waiting_for_connect *next; - grpc_closure *notify; - grpc_pollset *pollset; - gpr_atm *target; +typedef struct external_state_watcher { grpc_subchannel *subchannel; - grpc_closure continuation; -} waiting_for_connect; + grpc_pollset_set *pollset_set; + grpc_closure *notify; + grpc_closure closure; + struct external_state_watcher *next; + struct external_state_watcher *prev; +} external_state_watcher; struct grpc_subchannel { grpc_connector *connector; + /** refcount + - lower INTERNAL_REF_BITS bits are for internal references: + these do not keep the subchannel open. + - upper remaining bits are for public references: these do + keep the subchannel open */ + gpr_atm ref_pair; + /** non-transport related channel filters */ const grpc_channel_filter **filters; size_t num_filters; @@ -88,15 +93,9 @@ struct grpc_subchannel { /** address to connect to */ struct sockaddr *addr; size_t addr_len; + /** initial string to send to peer */ gpr_slice initial_connect_string; - /** master channel - the grpc_channel instance that ultimately owns - this channel_data via its channel stack. - We occasionally use this to bump the refcount on the master channel - to keep ourselves alive through an asynchronous operation. */ - grpc_channel *master; - /** have we seen a disconnection? */ - int disconnected; /** set during connection */ grpc_connect_out_args connecting_result; @@ -105,27 +104,24 @@ struct grpc_subchannel { grpc_closure connected; /** pollset_set tracking who's interested in a connection - being setup - owned by the master channel (in particular the - client_channel - filter there-in) */ - grpc_pollset_set *pollset_set; + being setup */ + grpc_pollset_set pollset_set; + + /** active connection, or null; of type grpc_connected_subchannel */ + gpr_atm connected_subchannel; /** mutex protecting remaining elements */ gpr_mu mu; - /** active connection */ - connection *active; - /** version number for the active connection */ - size_t active_version; - /** refcount */ - int refs; + /** have we seen a disconnection? */ + int disconnected; /** are we connecting */ int connecting; - /** things waiting for a connection */ - waiting_for_connect *waiting; /** connectivity state tracking */ grpc_connectivity_state_tracker state_tracker; + external_state_watcher root_external_state_watcher; + /** next connect attempt time */ gpr_timespec next_attempt; /** amount to backoff each failure */ @@ -139,151 +135,141 @@ struct grpc_subchannel { }; struct grpc_subchannel_call { - connection *connection; + grpc_connected_subchannel *connection; }; #define SUBCHANNEL_CALL_TO_CALL_STACK(call) ((grpc_call_stack *)((call) + 1)) -#define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)((con) + 1)) +#define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)(con)) #define CALLSTACK_TO_SUBCHANNEL_CALL(callstack) \ (((grpc_subchannel_call *)(callstack)) - 1) -static grpc_subchannel_call *create_call(grpc_exec_ctx *exec_ctx, - connection *con, - grpc_pollset *pollset); -static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c, - const char *reason); -static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c); static gpr_timespec compute_connect_deadline(grpc_subchannel *c); static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *subchannel, int iomgr_success); -static void subchannel_ref_locked(grpc_subchannel *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -static int subchannel_unref_locked( - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT; -static void connection_ref_locked(connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -static grpc_subchannel *connection_unref_locked( - grpc_exec_ctx *exec_ctx, - connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) GRPC_MUST_USE_RESULT; -static void subchannel_destroy(grpc_exec_ctx *exec_ctx, grpc_subchannel *c); - #ifdef GRPC_STREAM_REFCOUNT_DEBUG -#define SUBCHANNEL_REF_LOCKED(p, r) \ - subchannel_ref_locked((p), __FILE__, __LINE__, (r)) -#define SUBCHANNEL_UNREF_LOCKED(p, r) \ - subchannel_unref_locked((p), __FILE__, __LINE__, (r)) -#define CONNECTION_REF_LOCKED(p, r) \ - connection_ref_locked((p), __FILE__, __LINE__, (r)) -#define CONNECTION_UNREF_LOCKED(cl, p, r) \ - connection_unref_locked((cl), (p), __FILE__, __LINE__, (r)) -#define REF_PASS_ARGS , file, line, reason -#define REF_PASS_REASON , reason +#define REF_REASON reason #define REF_LOG(name, p) \ gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p ref %d -> %d %s", \ - (name), (p), (p)->refs, (p)->refs + 1, reason) + (name), (p), (p)->refs.count, (p)->refs.count + 1, reason) #define UNREF_LOG(name, p) \ gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p unref %d -> %d %s", \ - (name), (p), (p)->refs, (p)->refs - 1, reason) + (name), (p), (p)->refs.count, (p)->refs.count - 1, reason) +#define REF_MUTATE_EXTRA_ARGS \ + GRPC_SUBCHANNEL_REF_EXTRA_ARGS, const char *purpose +#define REF_MUTATE_PURPOSE(x) , file, line, reason, x #else -#define SUBCHANNEL_REF_LOCKED(p, r) subchannel_ref_locked((p)) -#define SUBCHANNEL_UNREF_LOCKED(p, r) subchannel_unref_locked((p)) -#define CONNECTION_REF_LOCKED(p, r) connection_ref_locked((p)) -#define CONNECTION_UNREF_LOCKED(cl, p, r) connection_unref_locked((cl), (p)) -#define REF_PASS_ARGS -#define REF_PASS_REASON +#define REF_REASON "" #define REF_LOG(name, p) \ do { \ } while (0) #define UNREF_LOG(name, p) \ do { \ } while (0) +#define REF_MUTATE_EXTRA_ARGS +#define REF_MUTATE_PURPOSE(x) #endif /* * connection implementation */ -static void connection_destroy(grpc_exec_ctx *exec_ctx, connection *c) { - GPR_ASSERT(c->refs == 0); +static void connection_destroy(grpc_exec_ctx *exec_ctx, void *arg, + int success) { + grpc_connected_subchannel *c = arg; grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c)); gpr_free(c); } -static void connection_ref_locked(connection *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - REF_LOG("CONNECTION", c); - subchannel_ref_locked(c->subchannel REF_PASS_ARGS); - ++c->refs; +void grpc_connected_subchannel_ref(grpc_connected_subchannel *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CONNECTION(c), REF_REASON); } -static grpc_subchannel *connection_unref_locked( - grpc_exec_ctx *exec_ctx, connection *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - grpc_subchannel *destroy = NULL; - UNREF_LOG("CONNECTION", c); - if (subchannel_unref_locked(c->subchannel REF_PASS_ARGS)) { - destroy = c->subchannel; - } - if (--c->refs == 0 && c->subchannel->active != c) { - connection_destroy(exec_ctx, c); - } - return destroy; +void grpc_connected_subchannel_unref(grpc_exec_ctx *exec_ctx, + grpc_connected_subchannel *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + GRPC_CHANNEL_STACK_UNREF(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c), + REF_REASON); } /* * grpc_subchannel implementation */ -static void subchannel_ref_locked(grpc_subchannel *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - REF_LOG("SUBCHANNEL", c); - ++c->refs; +static void subchannel_destroy(grpc_exec_ctx *exec_ctx, void *arg, + int success) { + grpc_subchannel *c = arg; + gpr_free((void *)c->filters); + grpc_channel_args_destroy(c->args); + gpr_free(c->addr); + gpr_slice_unref(c->initial_connect_string); + grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker); + grpc_connector_unref(exec_ctx, c->connector); + grpc_pollset_set_destroy(&c->pollset_set); + gpr_free(c); } -static int subchannel_unref_locked(grpc_subchannel *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - UNREF_LOG("SUBCHANNEL", c); - return --c->refs == 0; +static gpr_atm ref_mutate(grpc_subchannel *c, gpr_atm delta, + int barrier REF_MUTATE_EXTRA_ARGS) { + gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta) + : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta); +#ifdef GRPC_STREAM_REFCOUNT_DEBUG + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SUBCHANNEL: %p % 12s 0x%08x -> 0x%08x [%s]", c, purpose, old_val, + old_val + delta, reason); +#endif + return old_val; } void grpc_subchannel_ref(grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - gpr_mu_lock(&c->mu); - subchannel_ref_locked(c REF_PASS_ARGS); - gpr_mu_unlock(&c->mu); + gpr_atm old_refs; + old_refs = ref_mutate(c, (1 << INTERNAL_REF_BITS), + 0 REF_MUTATE_PURPOSE("STRONG_REF")); + GPR_ASSERT((old_refs & STRONG_REF_MASK) != 0); } -void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - int destroy; - gpr_mu_lock(&c->mu); - destroy = subchannel_unref_locked(c REF_PASS_ARGS); - gpr_mu_unlock(&c->mu); - if (destroy) subchannel_destroy(exec_ctx, c); +void grpc_subchannel_weak_ref(grpc_subchannel *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + old_refs = ref_mutate(c, 1, 0 REF_MUTATE_PURPOSE("WEAK_REF")); + GPR_ASSERT(old_refs != 0); } -static void subchannel_destroy(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { - if (c->active != NULL) { - connection_destroy(exec_ctx, c->active); +static void disconnect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { + grpc_connected_subchannel *con; + gpr_mu_lock(&c->mu); + GPR_ASSERT(!c->disconnected); + c->disconnected = 1; + grpc_connector_shutdown(exec_ctx, c->connector); + con = GET_CONNECTED_SUBCHANNEL(c, no_barrier); + if (con != NULL) { + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, con, "connection"); + gpr_atm_no_barrier_store(&c->connected_subchannel, 0xdeadbeef); } - gpr_free((void *)c->filters); - grpc_channel_args_destroy(c->args); - gpr_free(c->addr); - gpr_slice_unref(c->initial_connect_string); - grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker); - grpc_connector_unref(exec_ctx, c->connector); - gpr_free(c); + gpr_mu_unlock(&c->mu); } -void grpc_subchannel_add_interested_party(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c, - grpc_pollset *pollset) { - grpc_pollset_set_add_pollset(exec_ctx, c->pollset_set, pollset); +void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + old_refs = ref_mutate(c, (gpr_atm)1 - (gpr_atm)(1 << INTERNAL_REF_BITS), + 1 REF_MUTATE_PURPOSE("STRONG_UNREF")); + if ((old_refs & STRONG_REF_MASK) == (1 << INTERNAL_REF_BITS)) { + disconnect(exec_ctx, c); + } + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "strong-unref"); } -void grpc_subchannel_del_interested_party(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c, - grpc_pollset *pollset) { - grpc_pollset_set_del_pollset(exec_ctx, c->pollset_set, pollset); +void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + old_refs = ref_mutate(c, -(gpr_atm)1, 1 REF_MUTATE_PURPOSE("WEAK_UNREF")); + if (old_refs == 1) { + grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(subchannel_destroy, c), + 1); + } } static gpr_uint32 random_seed() { @@ -293,10 +279,8 @@ static gpr_uint32 random_seed() { grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, grpc_subchannel_args *args) { grpc_subchannel *c = gpr_malloc(sizeof(*c)); - grpc_channel_element *parent_elem = grpc_channel_stack_last_element( - grpc_channel_get_channel_stack(args->master)); memset(c, 0, sizeof(*c)); - c->refs = 1; + gpr_atm_no_barrier_store(&c->ref_pair, 1 << INTERNAL_REF_BITS); c->connector = connector; grpc_connector_ref(c->connector); c->num_filters = args->filter_count; @@ -305,13 +289,14 @@ grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, sizeof(grpc_channel_filter *) * c->num_filters); c->addr = gpr_malloc(args->addr_len); memcpy(c->addr, args->addr, args->addr_len); + grpc_pollset_set_init(&c->pollset_set); c->addr_len = args->addr_len; grpc_set_initial_connect_string(&c->addr, &c->addr_len, &c->initial_connect_string); c->args = grpc_channel_args_copy(args->args); - c->master = args->master; - c->pollset_set = grpc_client_channel_get_connecting_pollset_set(parent_elem); c->random = random_seed(); + c->root_external_state_watcher.next = c->root_external_state_watcher.prev = + &c->root_external_state_watcher; grpc_closure_init(&c->connected, subchannel_connected, c); grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE, "subchannel"); @@ -319,70 +304,18 @@ grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, return c; } -static void cancel_waiting_calls(grpc_exec_ctx *exec_ctx, - grpc_subchannel *subchannel, - int iomgr_success) { - waiting_for_connect *w4c; - gpr_mu_lock(&subchannel->mu); - w4c = subchannel->waiting; - subchannel->waiting = NULL; - gpr_mu_unlock(&subchannel->mu); - while (w4c != NULL) { - waiting_for_connect *next = w4c->next; - grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel, - w4c->pollset); - if (w4c->notify) { - w4c->notify->cb(exec_ctx, w4c->notify->cb_arg, iomgr_success); - } - - GRPC_SUBCHANNEL_UNREF(exec_ctx, w4c->subchannel, "waiting_for_connect"); - gpr_free(w4c); - - w4c = next; - } -} - -void grpc_subchannel_cancel_create_call(grpc_exec_ctx *exec_ctx, - grpc_subchannel *subchannel, - gpr_atm *target) { - waiting_for_connect *w4c; - int unref_count = 0; - gpr_mu_lock(&subchannel->mu); - w4c = subchannel->waiting; - subchannel->waiting = NULL; - while (w4c != NULL) { - waiting_for_connect *next = w4c->next; - if (w4c->target == target) { - grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel, - w4c->pollset); - grpc_exec_ctx_enqueue(exec_ctx, w4c->notify, 0); - - unref_count++; - gpr_free(w4c); - } else { - w4c->next = subchannel->waiting; - subchannel->waiting = w4c; - } - - w4c = next; - } - gpr_mu_unlock(&subchannel->mu); - - while (unref_count-- > 0) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannel, "waiting_for_connect"); - } -} - static void continue_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { grpc_connect_in_args args; - args.interested_parties = c->pollset_set; + args.interested_parties = &c->pollset_set; args.addr = c->addr; args.addr_len = c->addr_len; args.deadline = compute_connect_deadline(c); args.channel_args = c->args; args.initial_connect_string = c->initial_connect_string; + grpc_connectivity_state_set(exec_ctx, &c->state_tracker, + GRPC_CHANNEL_CONNECTING, "state_change"); grpc_connector_connect(exec_ctx, c->connector, &args, &c->connecting_result, &c->connected); } @@ -395,66 +328,6 @@ static void start_connect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { continue_connect(exec_ctx, c); } -static void continue_creating_call(grpc_exec_ctx *exec_ctx, void *arg, - int iomgr_success) { - int call_creation_finished_ok; - waiting_for_connect *w4c = arg; - grpc_subchannel_del_interested_party(exec_ctx, w4c->subchannel, w4c->pollset); - call_creation_finished_ok = grpc_subchannel_create_call( - exec_ctx, w4c->subchannel, w4c->pollset, w4c->target, w4c->notify); - GPR_ASSERT(call_creation_finished_ok == 1); - w4c->notify->cb(exec_ctx, w4c->notify->cb_arg, iomgr_success); - GRPC_SUBCHANNEL_UNREF(exec_ctx, w4c->subchannel, "waiting_for_connect"); - gpr_free(w4c); -} - -int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx, grpc_subchannel *c, - grpc_pollset *pollset, gpr_atm *target, - grpc_closure *notify) { - connection *con; - grpc_subchannel_call *call; - GPR_TIMER_BEGIN("grpc_subchannel_create_call", 0); - gpr_mu_lock(&c->mu); - if (c->active != NULL) { - con = c->active; - CONNECTION_REF_LOCKED(con, "call"); - gpr_mu_unlock(&c->mu); - - call = create_call(exec_ctx, con, pollset); - if (!gpr_atm_rel_cas(target, 0, (gpr_atm)(gpr_uintptr)call)) { - GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, call, "failed to set"); - } - GPR_TIMER_END("grpc_subchannel_create_call", 0); - return 1; - } else { - waiting_for_connect *w4c = gpr_malloc(sizeof(*w4c)); - w4c->next = c->waiting; - w4c->notify = notify; - w4c->pollset = pollset; - w4c->target = target; - w4c->subchannel = c; - /* released when clearing w4c */ - SUBCHANNEL_REF_LOCKED(c, "waiting_for_connect"); - grpc_closure_init(&w4c->continuation, continue_creating_call, w4c); - c->waiting = w4c; - grpc_subchannel_add_interested_party(exec_ctx, c, pollset); - if (!c->connecting) { - c->connecting = 1; - connectivity_state_changed_locked(exec_ctx, c, "create_call"); - /* released by connection */ - SUBCHANNEL_REF_LOCKED(c, "connecting"); - GRPC_CHANNEL_INTERNAL_REF(c->master, "connecting"); - gpr_mu_unlock(&c->mu); - - start_connect(exec_ctx, c); - } else { - gpr_mu_unlock(&c->mu); - } - GPR_TIMER_END("grpc_subchannel_create_call", 0); - return 0; - } -} - grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c) { grpc_connectivity_state state; gpr_mu_lock(&c->mu); @@ -463,153 +336,149 @@ grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c) { return state; } -void grpc_subchannel_notify_on_state_change(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c, - grpc_connectivity_state *state, - grpc_closure *notify) { +static void on_external_state_watcher_done(grpc_exec_ctx *exec_ctx, void *arg, + int success) { + external_state_watcher *w = arg; + grpc_closure *follow_up = w->notify; + if (w->pollset_set != NULL) { + grpc_pollset_set_del_pollset_set(exec_ctx, &w->subchannel->pollset_set, + w->pollset_set); + } + gpr_mu_lock(&w->subchannel->mu); + w->next->prev = w->prev; + w->prev->next = w->next; + gpr_mu_unlock(&w->subchannel->mu); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, w->subchannel, "external_state_watcher"); + gpr_free(w); + follow_up->cb(exec_ctx, follow_up->cb_arg, success); +} + +void grpc_subchannel_notify_on_state_change( + grpc_exec_ctx *exec_ctx, grpc_subchannel *c, + grpc_pollset_set *interested_parties, grpc_connectivity_state *state, + grpc_closure *notify) { int do_connect = 0; - gpr_mu_lock(&c->mu); - if (grpc_connectivity_state_notify_on_state_change( - exec_ctx, &c->state_tracker, state, notify)) { - do_connect = 1; - c->connecting = 1; - /* released by connection */ - SUBCHANNEL_REF_LOCKED(c, "connecting"); - GRPC_CHANNEL_INTERNAL_REF(c->master, "connecting"); - connectivity_state_changed_locked(exec_ctx, c, "state_change"); + external_state_watcher *w; + + if (state == NULL) { + gpr_mu_lock(&c->mu); + for (w = c->root_external_state_watcher.next; + w != &c->root_external_state_watcher; w = w->next) { + if (w->notify == notify) { + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &c->state_tracker, NULL, &w->closure); + } + } + gpr_mu_unlock(&c->mu); + } else { + w = gpr_malloc(sizeof(*w)); + w->subchannel = c; + w->pollset_set = interested_parties; + w->notify = notify; + grpc_closure_init(&w->closure, on_external_state_watcher_done, w); + if (interested_parties != NULL) { + grpc_pollset_set_add_pollset_set(exec_ctx, &c->pollset_set, + interested_parties); + } + GRPC_SUBCHANNEL_WEAK_REF(c, "external_state_watcher"); + gpr_mu_lock(&c->mu); + w->next = &c->root_external_state_watcher; + w->prev = w->next->prev; + w->next->prev = w->prev->next = w; + if (grpc_connectivity_state_notify_on_state_change( + exec_ctx, &c->state_tracker, state, &w->closure)) { + do_connect = 1; + c->connecting = 1; + /* released by connection */ + GRPC_SUBCHANNEL_WEAK_REF(c, "connecting"); + } + gpr_mu_unlock(&c->mu); } - gpr_mu_unlock(&c->mu); if (do_connect) { start_connect(exec_ctx, c); } } -int grpc_subchannel_state_change_unsubscribe(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c, - grpc_closure *subscribed_notify) { - int success; - gpr_mu_lock(&c->mu); - success = grpc_connectivity_state_change_unsubscribe( - exec_ctx, &c->state_tracker, subscribed_notify); - gpr_mu_unlock(&c->mu); - return success; +void grpc_connected_subchannel_process_transport_op( + grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, + grpc_transport_op *op) { + grpc_channel_stack *channel_stack = CHANNEL_STACK_FROM_CONNECTION(con); + grpc_channel_element *top_elem = grpc_channel_stack_element(channel_stack, 0); + top_elem->filter->start_transport_op(exec_ctx, top_elem, op); } -void grpc_subchannel_process_transport_op(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c, - grpc_transport_op *op) { - connection *con = NULL; - grpc_subchannel *destroy; - int cancel_alarm = 0; - gpr_mu_lock(&c->mu); - if (c->active != NULL) { - con = c->active; - CONNECTION_REF_LOCKED(con, "transport-op"); - } - if (op->disconnect) { - c->disconnected = 1; - connectivity_state_changed_locked(exec_ctx, c, "disconnect"); - if (c->have_alarm) { - cancel_alarm = 1; - } - } - gpr_mu_unlock(&c->mu); +static void subchannel_on_child_state_changed(grpc_exec_ctx *exec_ctx, void *p, + int iomgr_success) { + state_watcher *sw = p; + grpc_subchannel *c = sw->subchannel; + gpr_mu *mu = &c->mu; - if (con != NULL) { - grpc_channel_stack *channel_stack = CHANNEL_STACK_FROM_CONNECTION(con); - grpc_channel_element *top_elem = - grpc_channel_stack_element(channel_stack, 0); - top_elem->filter->start_transport_op(exec_ctx, top_elem, op); + gpr_mu_lock(mu); - gpr_mu_lock(&c->mu); - destroy = CONNECTION_UNREF_LOCKED(exec_ctx, con, "transport-op"); - gpr_mu_unlock(&c->mu); - if (destroy) { - subchannel_destroy(exec_ctx, destroy); + /* if we failed just leave this closure */ + if (iomgr_success) { + if (sw->connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + /* any errors on a subchannel ==> we're done, create a new one */ + sw->connectivity_state = GRPC_CHANNEL_FATAL_FAILURE; + } + grpc_connectivity_state_set(exec_ctx, &c->state_tracker, + sw->connectivity_state, "reflect_child"); + if (sw->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) { + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, GET_CONNECTED_SUBCHANNEL(c, no_barrier), NULL, + &sw->connectivity_state, &sw->closure); + GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher"); + sw = NULL; } } - if (cancel_alarm) { - grpc_timer_cancel(exec_ctx, &c->alarm); - } - - if (op->disconnect) { - grpc_connector_shutdown(exec_ctx, c->connector); - } + gpr_mu_unlock(mu); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "state_watcher"); + gpr_free(sw); } -static void on_state_changed(grpc_exec_ctx *exec_ctx, void *p, - int iomgr_success) { - state_watcher *sw = p; - grpc_subchannel *c = sw->subchannel; - gpr_mu *mu = &c->mu; - int destroy; +static void connected_subchannel_state_op(grpc_exec_ctx *exec_ctx, + grpc_connected_subchannel *con, + grpc_pollset_set *interested_parties, + grpc_connectivity_state *state, + grpc_closure *closure) { grpc_transport_op op; grpc_channel_element *elem; - connection *destroy_connection = NULL; - - gpr_mu_lock(mu); - - /* if we failed or there is a version number mismatch, just leave - this closure */ - if (!iomgr_success || sw->subchannel->active_version != sw->version) { - goto done; - } + memset(&op, 0, sizeof(op)); + op.connectivity_state = state; + op.on_connectivity_state_change = closure; + op.bind_pollset_set = interested_parties; + elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0); + elem->filter->start_transport_op(exec_ctx, elem, &op); +} - switch (sw->connectivity_state) { - case GRPC_CHANNEL_CONNECTING: - case GRPC_CHANNEL_READY: - case GRPC_CHANNEL_IDLE: - /* all is still good: keep watching */ - memset(&op, 0, sizeof(op)); - op.connectivity_state = &sw->connectivity_state; - op.on_connectivity_state_change = &sw->closure; - elem = grpc_channel_stack_element( - CHANNEL_STACK_FROM_CONNECTION(c->active), 0); - elem->filter->start_transport_op(exec_ctx, elem, &op); - /* early out */ - gpr_mu_unlock(mu); - return; - case GRPC_CHANNEL_FATAL_FAILURE: - case GRPC_CHANNEL_TRANSIENT_FAILURE: - /* things have gone wrong, deactivate and enter idle */ - if (sw->subchannel->active->refs == 0) { - destroy_connection = sw->subchannel->active; - } - sw->subchannel->active = NULL; - grpc_connectivity_state_set(exec_ctx, &c->state_tracker, - c->disconnected - ? GRPC_CHANNEL_FATAL_FAILURE - : GRPC_CHANNEL_TRANSIENT_FAILURE, - "connection_failed"); - break; - } +void grpc_connected_subchannel_notify_on_state_change( + grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, + grpc_pollset_set *interested_parties, grpc_connectivity_state *state, + grpc_closure *closure) { + connected_subchannel_state_op(exec_ctx, con, interested_parties, state, + closure); +} -done: - connectivity_state_changed_locked(exec_ctx, c, "transport_state_changed"); - destroy = SUBCHANNEL_UNREF_LOCKED(c, "state_watcher"); - gpr_free(sw); - gpr_mu_unlock(mu); - if (destroy) { - subchannel_destroy(exec_ctx, c); - } - if (destroy_connection != NULL) { - connection_destroy(exec_ctx, destroy_connection); - } +void grpc_connected_subchannel_ping(grpc_exec_ctx *exec_ctx, + grpc_connected_subchannel *con, + grpc_closure *closure) { + grpc_transport_op op; + grpc_channel_element *elem; + memset(&op, 0, sizeof(op)); + op.send_ping = closure; + elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0); + elem->filter->start_transport_op(exec_ctx, elem, &op); } static void publish_transport(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { size_t channel_stack_size; - connection *con; + grpc_connected_subchannel *con; grpc_channel_stack *stk; size_t num_filters; const grpc_channel_filter **filters; - waiting_for_connect *w4c; - grpc_transport_op op; - state_watcher *sw; - connection *destroy_connection = NULL; - grpc_channel_element *elem; + state_watcher *sw_subchannel; /* build final filter list */ num_filters = c->num_filters + c->connecting_result.num_filters + 1; @@ -621,74 +490,52 @@ static void publish_transport(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { /* construct channel stack */ channel_stack_size = grpc_channel_stack_size(filters, num_filters); - con = gpr_malloc(sizeof(connection) + channel_stack_size); - stk = (grpc_channel_stack *)(con + 1); - con->refs = 0; - con->subchannel = c; - grpc_channel_stack_init(exec_ctx, filters, num_filters, c->master, c->args, - stk); + con = gpr_malloc(channel_stack_size); + stk = CHANNEL_STACK_FROM_CONNECTION(con); + grpc_channel_stack_init(exec_ctx, 1, connection_destroy, con, filters, + num_filters, c->args, "CONNECTED_SUBCHANNEL", stk); grpc_connected_channel_bind_transport(stk, c->connecting_result.transport); gpr_free((void *)c->connecting_result.filters); memset(&c->connecting_result, 0, sizeof(c->connecting_result)); /* initialize state watcher */ - sw = gpr_malloc(sizeof(*sw)); - grpc_closure_init(&sw->closure, on_state_changed, sw); - sw->subchannel = c; - sw->connectivity_state = GRPC_CHANNEL_READY; + sw_subchannel = gpr_malloc(sizeof(*sw_subchannel)); + sw_subchannel->subchannel = c; + sw_subchannel->connectivity_state = GRPC_CHANNEL_READY; + grpc_closure_init(&sw_subchannel->closure, subchannel_on_child_state_changed, + sw_subchannel); gpr_mu_lock(&c->mu); if (c->disconnected) { gpr_mu_unlock(&c->mu); - gpr_free(sw); + gpr_free(sw_subchannel); gpr_free((void *)filters); grpc_channel_stack_destroy(exec_ctx, stk); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting"); - GRPC_SUBCHANNEL_UNREF(exec_ctx, c, "connecting"); + gpr_free(con); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); return; } /* publish */ - if (c->active != NULL && c->active->refs == 0) { - destroy_connection = c->active; - } - c->active = con; - c->active_version++; - sw->version = c->active_version; + GPR_ASSERT(gpr_atm_no_barrier_cas(&c->connected_subchannel, 0, (gpr_atm)con)); c->connecting = 0; - /* watch for changes; subchannel ref for connecting is donated + /* setup subchannel watching connected subchannel for changes; subchannel ref + for connecting is donated to the state watcher */ - memset(&op, 0, sizeof(op)); - op.connectivity_state = &sw->connectivity_state; - op.on_connectivity_state_change = &sw->closure; - op.bind_pollset_set = c->pollset_set; - SUBCHANNEL_REF_LOCKED(c, "state_watcher"); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting"); - GPR_ASSERT(!SUBCHANNEL_UNREF_LOCKED(c, "connecting")); - elem = - grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(c->active), 0); - elem->filter->start_transport_op(exec_ctx, elem, &op); + GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher"); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, con, &c->pollset_set, &sw_subchannel->connectivity_state, + &sw_subchannel->closure); /* signal completion */ - connectivity_state_changed_locked(exec_ctx, c, "connected"); - w4c = c->waiting; - c->waiting = NULL; + grpc_connectivity_state_set(exec_ctx, &c->state_tracker, GRPC_CHANNEL_READY, + "connected"); gpr_mu_unlock(&c->mu); - - while (w4c != NULL) { - waiting_for_connect *next = w4c->next; - grpc_exec_ctx_enqueue(exec_ctx, &w4c->continuation, 1); - w4c = next; - } - gpr_free((void *)filters); - - if (destroy_connection != NULL) { - connection_destroy(exec_ctx, destroy_connection); - } } /* Generate a random number between 0 and 1. */ @@ -742,29 +589,31 @@ static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, int iomgr_success) { if (c->disconnected) { iomgr_success = 0; } - connectivity_state_changed_locked(exec_ctx, c, "alarm"); gpr_mu_unlock(&c->mu); if (iomgr_success) { update_reconnect_parameters(c); continue_connect(exec_ctx, c); } else { - cancel_waiting_calls(exec_ctx, c, iomgr_success); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->master, "connecting"); - GRPC_SUBCHANNEL_UNREF(exec_ctx, c, "connecting"); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); } } static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *arg, int iomgr_success) { grpc_subchannel *c = arg; + if (c->connecting_result.transport != NULL) { publish_transport(exec_ctx, c); + } else if (c->disconnected) { + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); } else { gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); gpr_mu_lock(&c->mu); GPR_ASSERT(!c->have_alarm); c->have_alarm = 1; - connectivity_state_changed_locked(exec_ctx, c, "connect_failed"); + grpc_connectivity_state_set(exec_ctx, &c->state_tracker, + GRPC_CHANNEL_TRANSIENT_FAILURE, + "connect_failed"); grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, on_alarm, c, now); gpr_mu_unlock(&c->mu); } @@ -781,29 +630,6 @@ static gpr_timespec compute_connect_deadline(grpc_subchannel *c) { : min_deadline; } -static grpc_connectivity_state compute_connectivity_locked(grpc_subchannel *c) { - if (c->disconnected) { - return GRPC_CHANNEL_FATAL_FAILURE; - } - if (c->connecting) { - if (c->have_alarm) { - return GRPC_CHANNEL_TRANSIENT_FAILURE; - } - return GRPC_CHANNEL_CONNECTING; - } - if (c->active) { - return GRPC_CHANNEL_READY; - } - return GRPC_CHANNEL_IDLE; -} - -static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c, - const char *reason) { - grpc_connectivity_state current = compute_connectivity_locked(c); - grpc_connectivity_state_set(exec_ctx, &c->state_tracker, current, reason); -} - /* * grpc_subchannel_call implementation */ @@ -811,37 +637,22 @@ static void connectivity_state_changed_locked(grpc_exec_ctx *exec_ctx, static void subchannel_call_destroy(grpc_exec_ctx *exec_ctx, void *call, int success) { grpc_subchannel_call *c = call; - gpr_mu *mu = &c->connection->subchannel->mu; - grpc_subchannel *destroy; GPR_TIMER_BEGIN("grpc_subchannel_call_unref.destroy", 0); grpc_call_stack_destroy(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c)); - gpr_mu_lock(mu); - destroy = CONNECTION_UNREF_LOCKED(exec_ctx, c->connection, "call"); - gpr_mu_unlock(mu); + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, c->connection, "subchannel_call"); gpr_free(c); - if (destroy != NULL) { - subchannel_destroy(exec_ctx, destroy); - } GPR_TIMER_END("grpc_subchannel_call_unref.destroy", 0); } void grpc_subchannel_call_ref(grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { -#ifdef GRPC_STREAM_REFCOUNT_DEBUG - grpc_call_stack_ref(SUBCHANNEL_CALL_TO_CALL_STACK(c), reason); -#else - grpc_call_stack_ref(SUBCHANNEL_CALL_TO_CALL_STACK(c)); -#endif + GRPC_CALL_STACK_REF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); } void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx, grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { -#ifdef GRPC_STREAM_REFCOUNT_DEBUG - grpc_call_stack_unref(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), reason); -#else - grpc_call_stack_unref(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c)); -#endif + GRPC_CALL_STACK_UNREF(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); } char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx, @@ -859,24 +670,26 @@ void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, top_elem->filter->start_transport_stream_op(exec_ctx, top_elem, op); } -static grpc_subchannel_call *create_call(grpc_exec_ctx *exec_ctx, - connection *con, - grpc_pollset *pollset) { +grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel( + grpc_subchannel *c) { + return GET_CONNECTED_SUBCHANNEL(c, acq); +} + +grpc_subchannel_call *grpc_connected_subchannel_create_call( + grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, + grpc_pollset *pollset) { grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con); grpc_subchannel_call *call = gpr_malloc(sizeof(grpc_subchannel_call) + chanstk->call_stack_size); grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(call); call->connection = con; + GRPC_CONNECTED_SUBCHANNEL_REF(con, "subchannel_call"); grpc_call_stack_init(exec_ctx, chanstk, 1, subchannel_call_destroy, call, NULL, NULL, callstk); grpc_call_stack_set_pollset(exec_ctx, callstk, pollset); return call; } -grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel) { - return subchannel->master; -} - grpc_call_stack *grpc_subchannel_call_get_call_stack( grpc_subchannel_call *subchannel_call) { return SUBCHANNEL_CALL_TO_CALL_STACK(subchannel_call); diff --git a/src/core/client_config/subchannel.h b/src/core/client_config/subchannel.h index 85ea3739e4..57c7c9dc67 100644 --- a/src/core/client_config/subchannel.h +++ b/src/core/client_config/subchannel.h @@ -41,6 +41,7 @@ /** A (sub-)channel that knows how to connect to exactly one target address. Provides a target for load balancing. */ typedef struct grpc_subchannel grpc_subchannel; +typedef struct grpc_connected_subchannel grpc_connected_subchannel; typedef struct grpc_subchannel_call grpc_subchannel_call; typedef struct grpc_subchannel_args grpc_subchannel_args; @@ -49,6 +50,14 @@ typedef struct grpc_subchannel_args grpc_subchannel_args; grpc_subchannel_ref((p), __FILE__, __LINE__, (r)) #define GRPC_SUBCHANNEL_UNREF(cl, p, r) \ grpc_subchannel_unref((cl), (p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_WEAK_REF(p, r) \ + grpc_subchannel_weak_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_WEAK_UNREF(cl, p, r) \ + grpc_subchannel_weak_unref((cl), (p), __FILE__, __LINE__, (r)) +#define GRPC_CONNECTED_SUBCHANNEL_REF(p, r) \ + grpc_connected_subchannel_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_CONNECTED_SUBCHANNEL_UNREF(cl, p, r) \ + grpc_connected_subchannel_unref((cl), (p), __FILE__, __LINE__, (r)) #define GRPC_SUBCHANNEL_CALL_REF(p, r) \ grpc_subchannel_call_ref((p), __FILE__, __LINE__, (r)) #define GRPC_SUBCHANNEL_CALL_UNREF(cl, p, r) \ @@ -58,6 +67,12 @@ typedef struct grpc_subchannel_args grpc_subchannel_args; #else #define GRPC_SUBCHANNEL_REF(p, r) grpc_subchannel_ref((p)) #define GRPC_SUBCHANNEL_UNREF(cl, p, r) grpc_subchannel_unref((cl), (p)) +#define GRPC_SUBCHANNEL_WEAK_REF(p, r) grpc_subchannel_weak_ref((p)) +#define GRPC_SUBCHANNEL_WEAK_UNREF(cl, p, r) \ + grpc_subchannel_weak_unref((cl), (p)) +#define GRPC_CONNECTED_SUBCHANNEL_REF(p, r) grpc_connected_subchannel_ref((p)) +#define GRPC_CONNECTED_SUBCHANNEL_UNREF(cl, p, r) \ + grpc_connected_subchannel_unref((cl), (p)) #define GRPC_SUBCHANNEL_CALL_REF(p, r) grpc_subchannel_call_ref((p)) #define GRPC_SUBCHANNEL_CALL_UNREF(cl, p, r) \ grpc_subchannel_call_unref((cl), (p)) @@ -69,33 +84,31 @@ void grpc_subchannel_ref(grpc_subchannel *channel void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx, grpc_subchannel *channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_weak_ref(grpc_subchannel *channel + GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel *channel + GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_connected_subchannel_ref(grpc_connected_subchannel *channel + GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_connected_subchannel_unref(grpc_exec_ctx *exec_ctx, + grpc_connected_subchannel *channel + GRPC_SUBCHANNEL_REF_EXTRA_ARGS); void grpc_subchannel_call_ref(grpc_subchannel_call *call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx, grpc_subchannel_call *call GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -/** construct a subchannel call (possibly asynchronously). - * - * If the returned status is 1, the call will return immediately and \a target - * will point to a connected \a subchannel_call instance. Note that \a notify - * will \em not be invoked in this case. - * Otherwise, if the returned status is 0, the subchannel call will be created - * asynchronously, invoking the \a notify callback upon completion. */ -int grpc_subchannel_create_call(grpc_exec_ctx *exec_ctx, - grpc_subchannel *subchannel, - grpc_pollset *pollset, gpr_atm *target, - grpc_closure *notify); - -/** cancel \a call in the waiting state. */ -void grpc_subchannel_cancel_create_call(grpc_exec_ctx *exec_ctx, - grpc_subchannel *subchannel, - gpr_atm *target); +/** construct a subchannel call */ +grpc_subchannel_call *grpc_connected_subchannel_create_call( + grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *connected_subchannel, + grpc_pollset *pollset); /** process a transport level op */ -void grpc_subchannel_process_transport_op(grpc_exec_ctx *exec_ctx, - grpc_subchannel *subchannel, - grpc_transport_op *op); +void grpc_connected_subchannel_process_transport_op( + grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *subchannel, + grpc_transport_op *op); /** poll the current connectivity state of a channel */ grpc_connectivity_state grpc_subchannel_check_connectivity( @@ -103,26 +116,22 @@ grpc_connectivity_state grpc_subchannel_check_connectivity( /** call notify when the connectivity state of a channel changes from *state. Updates *state with the new state of the channel */ -void grpc_subchannel_notify_on_state_change(grpc_exec_ctx *exec_ctx, - grpc_subchannel *channel, - grpc_connectivity_state *state, - grpc_closure *notify); - -/** Remove \a subscribed_notify from the list of closures to be called on a - * state change if present, returning 1. Otherwise, nothing is done and return - * 0. */ -int grpc_subchannel_state_change_unsubscribe(grpc_exec_ctx *exec_ctx, - grpc_subchannel *channel, - grpc_closure *subscribed_notify); - -/** express interest in \a channel's activities through \a pollset. */ -void grpc_subchannel_add_interested_party(grpc_exec_ctx *exec_ctx, - grpc_subchannel *channel, - grpc_pollset *pollset); -/** stop following \a channel's activity through \a pollset. */ -void grpc_subchannel_del_interested_party(grpc_exec_ctx *exec_ctx, - grpc_subchannel *channel, - grpc_pollset *pollset); +void grpc_subchannel_notify_on_state_change( + grpc_exec_ctx *exec_ctx, grpc_subchannel *channel, + grpc_pollset_set *interested_parties, grpc_connectivity_state *state, + grpc_closure *notify); +void grpc_connected_subchannel_notify_on_state_change( + grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *channel, + grpc_pollset_set *interested_parties, grpc_connectivity_state *state, + grpc_closure *notify); +void grpc_connected_subchannel_ping(grpc_exec_ctx *exec_ctx, + grpc_connected_subchannel *channel, + grpc_closure *notify); + +/** retrieve the grpc_connected_subchannel - or NULL if called before + the subchannel becomes connected */ +grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel( + grpc_subchannel *subchannel); /** continue processing a transport op */ void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, @@ -147,15 +156,10 @@ struct grpc_subchannel_args { /** Address to connect to */ struct sockaddr *addr; size_t addr_len; - /** master channel */ - grpc_channel *master; }; /** create a subchannel given a connector */ grpc_subchannel *grpc_subchannel_create(grpc_connector *connector, grpc_subchannel_args *args); -/** Return the master channel associated with the subchannel */ -grpc_channel *grpc_subchannel_get_master(grpc_subchannel *subchannel); - #endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_H */ diff --git a/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h b/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h deleted file mode 100644 index 76a535ebed..0000000000 --- a/src/core/client_config/subchannel_factory_decorators/add_channel_arg.h +++ /dev/null @@ -1,46 +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_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_ADD_CHANNEL_ARG_H -#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_ADD_CHANNEL_ARG_H - -#include "src/core/client_config/subchannel_factory.h" - -/** Takes a subchannel factory, returns a new one that mutates incoming - channel_args by adding a new argument; ownership of input, arg is retained - by the caller. */ -grpc_subchannel_factory *grpc_subchannel_factory_add_channel_arg( - grpc_subchannel_factory *input, const grpc_arg *arg); - -#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_ADD_CHANNEL_ARG_H \ - */ diff --git a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c deleted file mode 100644 index cd25fdcf0f..0000000000 --- a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.c +++ /dev/null @@ -1,86 +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/client_config/subchannel_factory_decorators/merge_channel_args.h" -#include <grpc/support/alloc.h> -#include "src/core/channel/channel_args.h" - -typedef struct { - grpc_subchannel_factory base; - gpr_refcount refs; - grpc_subchannel_factory *wrapped; - grpc_channel_args *merge_args; -} merge_args_factory; - -static void merge_args_factory_ref(grpc_subchannel_factory *scf) { - merge_args_factory *f = (merge_args_factory *)scf; - gpr_ref(&f->refs); -} - -static void merge_args_factory_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel_factory *scf) { - merge_args_factory *f = (merge_args_factory *)scf; - if (gpr_unref(&f->refs)) { - grpc_subchannel_factory_unref(exec_ctx, f->wrapped); - grpc_channel_args_destroy(f->merge_args); - gpr_free(f); - } -} - -static grpc_subchannel *merge_args_factory_create_subchannel( - grpc_exec_ctx *exec_ctx, grpc_subchannel_factory *scf, - grpc_subchannel_args *args) { - merge_args_factory *f = (merge_args_factory *)scf; - grpc_channel_args *final_args = - grpc_channel_args_merge(args->args, f->merge_args); - grpc_subchannel *s; - args->args = final_args; - s = grpc_subchannel_factory_create_subchannel(exec_ctx, f->wrapped, args); - grpc_channel_args_destroy(final_args); - return s; -} - -static const grpc_subchannel_factory_vtable merge_args_factory_vtable = { - merge_args_factory_ref, merge_args_factory_unref, - merge_args_factory_create_subchannel}; - -grpc_subchannel_factory *grpc_subchannel_factory_merge_channel_args( - grpc_subchannel_factory *input, const grpc_channel_args *args) { - merge_args_factory *f = gpr_malloc(sizeof(*f)); - f->base.vtable = &merge_args_factory_vtable; - gpr_ref_init(&f->refs, 1); - grpc_subchannel_factory_ref(input); - f->wrapped = input; - f->merge_args = grpc_channel_args_copy(args); - return &f->base; -} diff --git a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h b/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h deleted file mode 100644 index a9e1691871..0000000000 --- a/src/core/client_config/subchannel_factory_decorators/merge_channel_args.h +++ /dev/null @@ -1,46 +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_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_MERGE_CHANNEL_ARGS_H -#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_MERGE_CHANNEL_ARGS_H - -#include "src/core/client_config/subchannel_factory.h" - -/** Takes a subchannel factory, returns a new one that mutates incoming - channel_args by adding a new argument; ownership of input, args is retained - by the caller. */ -grpc_subchannel_factory *grpc_subchannel_factory_merge_channel_args( - grpc_subchannel_factory *input, const grpc_channel_args *args); - -#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_DECORATORS_MERGE_CHANNEL_ARGS_H \ - */ diff --git a/src/core/compression/algorithm.c b/src/core/compression/algorithm.c index 73d91fa8ea..8e4e5c91d4 100644 --- a/src/core/compression/algorithm.c +++ b/src/core/compression/algorithm.c @@ -119,8 +119,8 @@ grpc_mdelem *grpc_compression_encoding_mdelem( return GRPC_MDELEM_GRPC_ENCODING_DEFLATE; case GRPC_COMPRESS_GZIP: return GRPC_MDELEM_GRPC_ENCODING_GZIP; - case GRPC_COMPRESS_ALGORITHMS_COUNT: - return NULL; + default: + break; } return NULL; } @@ -139,25 +139,9 @@ grpc_compression_algorithm grpc_compression_algorithm_for_level( case GRPC_COMPRESS_LEVEL_HIGH: return GRPC_COMPRESS_DEFLATE; default: - /* we shouldn't be making it here */ - abort(); - return GRPC_COMPRESS_NONE; - } -} - -grpc_compression_level grpc_compression_level_for_algorithm( - grpc_compression_algorithm algorithm) { - grpc_compression_level clevel; - GRPC_API_TRACE("grpc_compression_level_for_algorithm(algorithm=%d)", 1, - ((int)algorithm)); - for (clevel = GRPC_COMPRESS_LEVEL_NONE; clevel < GRPC_COMPRESS_LEVEL_COUNT; - ++clevel) { - if (grpc_compression_algorithm_for_level(clevel) == algorithm) { - return clevel; - } + break; } - abort(); - return GRPC_COMPRESS_LEVEL_NONE; + GPR_UNREACHABLE_CODE(return GRPC_COMPRESS_NONE); } void grpc_compression_options_init(grpc_compression_options *opts) { diff --git a/src/core/compression/message_compress.c b/src/core/compression/message_compress.c index 209c1f0ff1..edc21a9eb7 100644 --- a/src/core/compression/message_compress.c +++ b/src/core/compression/message_compress.c @@ -69,8 +69,8 @@ static int zlib_body(z_stream* zs, gpr_slice_buffer* input, zs->next_out = GPR_SLICE_START_PTR(outbuf); } r = flate(zs, flush); - if (r == Z_STREAM_ERROR) { - gpr_log(GPR_INFO, "zlib: stream error"); + if (r < 0 && r != Z_BUF_ERROR /* not fatal */) { + gpr_log(GPR_INFO, "zlib error (%d)", r); goto error; } } while (zs->avail_out == 0); @@ -91,6 +91,12 @@ error: return 0; } +static void* zalloc_gpr(void* opaque, unsigned int items, unsigned int size) { + return gpr_malloc(items * size); +} + +static void zfree_gpr(void* opaque, void* address) { gpr_free(address); } + static int zlib_compress(gpr_slice_buffer* input, gpr_slice_buffer* output, int gzip) { z_stream zs; @@ -99,12 +105,11 @@ static int zlib_compress(gpr_slice_buffer* input, gpr_slice_buffer* output, size_t count_before = output->count; size_t length_before = output->length; memset(&zs, 0, sizeof(zs)); + zs.zalloc = zalloc_gpr; + zs.zfree = zfree_gpr; r = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | (gzip ? 16 : 0), 8, Z_DEFAULT_STRATEGY); - if (r != Z_OK) { - gpr_log(GPR_ERROR, "deflateInit2 returns %d", r); - return 0; - } + GPR_ASSERT(r == Z_OK); r = zlib_body(&zs, input, output, deflate) && output->length < input->length; if (!r) { for (i = count_before; i < output->count; i++) { @@ -125,11 +130,10 @@ static int zlib_decompress(gpr_slice_buffer* input, gpr_slice_buffer* output, size_t count_before = output->count; size_t length_before = output->length; memset(&zs, 0, sizeof(zs)); + zs.zalloc = zalloc_gpr; + zs.zfree = zfree_gpr; r = inflateInit2(&zs, 15 | (gzip ? 16 : 0)); - if (r != Z_OK) { - gpr_log(GPR_ERROR, "inflateInit2 returns %d", r); - return 0; - } + GPR_ASSERT(r == Z_OK); r = zlib_body(&zs, input, output, inflate); if (!r) { for (i = count_before; i < output->count; i++) { @@ -150,8 +154,8 @@ static int copy(gpr_slice_buffer* input, gpr_slice_buffer* output) { return 1; } -int compress_inner(grpc_compression_algorithm algorithm, - gpr_slice_buffer* input, gpr_slice_buffer* output) { +static int compress_inner(grpc_compression_algorithm algorithm, + gpr_slice_buffer* input, gpr_slice_buffer* output) { switch (algorithm) { case GRPC_COMPRESS_NONE: /* the fallback path always needs to be send uncompressed: we simply diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c index a87f1aa87b..b5cd8d8d2a 100644 --- a/src/core/httpcli/httpcli.c +++ b/src/core/httpcli/httpcli.c @@ -53,6 +53,7 @@ typedef struct { size_t next_address; grpc_endpoint *ep; char *host; + char *ssl_host_override; gpr_timespec deadline; int have_read_byte; const grpc_httpcli_handshaker *handshaker; @@ -106,6 +107,7 @@ static void finish(grpc_exec_ctx *exec_ctx, internal_request *req, } gpr_slice_unref(req->request_text); gpr_free(req->host); + gpr_free(req->ssl_host_override); grpc_iomgr_unregister_object(&req->iomgr_obj); gpr_slice_buffer_destroy(&req->incoming); gpr_slice_buffer_destroy(&req->outgoing); @@ -180,8 +182,10 @@ static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, int success) { next_address(exec_ctx, req); return; } - req->handshaker->handshake(exec_ctx, req, req->ep, req->host, - on_handshake_done); + req->handshaker->handshake( + exec_ctx, req, req->ep, + req->ssl_host_override ? req->ssl_host_override : req->host, + on_handshake_done); } static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) { @@ -231,6 +235,7 @@ static void internal_request_begin( gpr_slice_buffer_init(&req->outgoing); grpc_iomgr_register_object(&req->iomgr_obj, name); req->host = gpr_strdup(request->host); + req->ssl_host_override = gpr_strdup(request->ssl_host_override); grpc_pollset_set_add_pollset(exec_ctx, &req->context->pollset_set, req->pollset); diff --git a/src/core/httpcli/httpcli.h b/src/core/httpcli/httpcli.h index 6469c2f03e..30875d71f1 100644 --- a/src/core/httpcli/httpcli.h +++ b/src/core/httpcli/httpcli.h @@ -74,6 +74,8 @@ extern const grpc_httpcli_handshaker grpc_httpcli_ssl; typedef struct grpc_httpcli_request { /* The host name to connect to */ char *host; + /* The host to verify in the SSL handshake (or NULL) */ + char *ssl_host_override; /* The path of the resource to fetch */ char *path; /* Additional headers: count and key/values; the following are supplied diff --git a/src/core/httpcli/httpcli_security_connector.c b/src/core/httpcli/httpcli_security_connector.c index fc6699c918..a5aa551373 100644 --- a/src/core/httpcli/httpcli_security_connector.c +++ b/src/core/httpcli/httpcli_security_connector.c @@ -68,7 +68,7 @@ static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx, tsi_result result = TSI_OK; tsi_handshaker *handshaker; if (c->handshaker_factory == NULL) { - cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, nonsecure_endpoint, NULL); + cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL); return; } result = tsi_ssl_handshaker_factory_create_handshaker( @@ -76,7 +76,7 @@ static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx, if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", tsi_result_to_string(result)); - cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, nonsecure_endpoint, NULL); + cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL); } else { grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, user_data); @@ -149,7 +149,6 @@ typedef struct { static void on_secure_transport_setup_done(grpc_exec_ctx *exec_ctx, void *rp, grpc_security_status status, - grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint) { on_done_closure *c = rp; if (status != GRPC_SECURITY_OK) { diff --git a/src/core/iomgr/endpoint_pair_posix.c b/src/core/iomgr/endpoint_pair_posix.c index deae9c6875..56f6f146fd 100644 --- a/src/core/iomgr/endpoint_pair_posix.c +++ b/src/core/iomgr/endpoint_pair_posix.c @@ -36,6 +36,7 @@ #ifdef GPR_POSIX_SOCKET #include "src/core/iomgr/endpoint_pair.h" +#include "src/core/iomgr/socket_utils_posix.h" #include <errno.h> #include <fcntl.h> @@ -56,6 +57,8 @@ static void create_sockets(int sv[2]) { GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0); flags = fcntl(sv[1], F_GETFL, 0); GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0); + GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0])); + GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1])); } grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c index 2be0ea235f..00710d83bd 100644 --- a/src/core/iomgr/fd_posix.c +++ b/src/core/iomgr/fd_posix.c @@ -43,6 +43,7 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include <grpc/support/string_util.h> #include <grpc/support/useful.h> #define CLOSURE_NOT_READY ((grpc_closure *)0) @@ -158,7 +159,10 @@ void grpc_fd_global_shutdown(void) { grpc_fd *grpc_fd_create(int fd, const char *name) { grpc_fd *r = alloc_fd(fd); - grpc_iomgr_register_object(&r->iomgr_object, name); + char *name2; + gpr_asprintf(&name2, "%s fd=%d", name, fd); + grpc_iomgr_register_object(&r->iomgr_object, name2); + gpr_free(name2); #ifdef GRPC_FD_REF_COUNT_DEBUG gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, r, name); #endif diff --git a/src/core/iomgr/fd_posix.h b/src/core/iomgr/fd_posix.h index d628ef3aaf..df4eb64d4c 100644 --- a/src/core/iomgr/fd_posix.h +++ b/src/core/iomgr/fd_posix.h @@ -170,6 +170,7 @@ void grpc_fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd); void grpc_fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd); /* Reference counting for fds */ +/*#define GRPC_FD_REF_COUNT_DEBUG*/ #ifdef GRPC_FD_REF_COUNT_DEBUG void grpc_fd_ref(grpc_fd *fd, const char *reason, const char *file, int line); void grpc_fd_unref(grpc_fd *fd, const char *reason, const char *file, int line); diff --git a/src/core/iomgr/pollset_multipoller_with_epoll.c b/src/core/iomgr/pollset_multipoller_with_epoll.c index 1f1bf47e98..6e31efa013 100644 --- a/src/core/iomgr/pollset_multipoller_with_epoll.c +++ b/src/core/iomgr/pollset_multipoller_with_epoll.c @@ -123,26 +123,6 @@ static void multipoll_with_epoll_pollset_add_fd(grpc_exec_ctx *exec_ctx, } } -static void multipoll_with_epoll_pollset_del_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, - grpc_fd *fd, - int and_unlock_pollset) { - pollset_hdr *h = pollset->data.ptr; - int err; - - if (and_unlock_pollset) { - gpr_mu_unlock(&pollset->mu); - } - - /* Note that this can race with concurrent poll, but that should be fine since - * at worst it creates a spurious read event on a reused grpc_fd object. */ - err = epoll_ctl(h->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); - if (err < 0) { - gpr_log(GPR_ERROR, "epoll_ctl del for %d failed: %s", fd->fd, - strerror(errno)); - } -} - /* TODO(klempner): We probably want to turn this down a bit */ #define GRPC_EPOLL_MAX_EVENTS 1000 @@ -235,7 +215,7 @@ static void multipoll_with_epoll_pollset_destroy(grpc_pollset *pollset) { } static const grpc_pollset_vtable multipoll_with_epoll_pollset = { - multipoll_with_epoll_pollset_add_fd, multipoll_with_epoll_pollset_del_fd, + multipoll_with_epoll_pollset_add_fd, multipoll_with_epoll_pollset_maybe_work_and_unlock, multipoll_with_epoll_pollset_finish_shutdown, multipoll_with_epoll_pollset_destroy}; diff --git a/src/core/iomgr/pollset_multipoller_with_poll_posix.c b/src/core/iomgr/pollset_multipoller_with_poll_posix.c index 09f04b64b9..b619b8c3db 100644 --- a/src/core/iomgr/pollset_multipoller_with_poll_posix.c +++ b/src/core/iomgr/pollset_multipoller_with_poll_posix.c @@ -82,23 +82,6 @@ exit: } } -static void multipoll_with_poll_pollset_del_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, - grpc_fd *fd, - int and_unlock_pollset) { - /* will get removed next poll cycle */ - pollset_hdr *h = pollset->data.ptr; - if (h->del_count == h->del_capacity) { - h->del_capacity = GPR_MAX(h->del_capacity + 8, h->del_count * 3 / 2); - h->dels = gpr_realloc(h->dels, sizeof(grpc_fd *) * h->del_capacity); - } - h->dels[h->del_count++] = fd; - GRPC_FD_REF(fd, "multipoller_del"); - if (and_unlock_pollset) { - gpr_mu_unlock(&pollset->mu); - } -} - static void multipoll_with_poll_pollset_maybe_work_and_unlock( grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker, gpr_timespec deadline, gpr_timespec now) { @@ -212,7 +195,7 @@ static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) { } static const grpc_pollset_vtable multipoll_with_poll_pollset = { - multipoll_with_poll_pollset_add_fd, multipoll_with_poll_pollset_del_fd, + multipoll_with_poll_pollset_add_fd, multipoll_with_poll_pollset_maybe_work_and_unlock, multipoll_with_poll_pollset_finish_shutdown, multipoll_with_poll_pollset_destroy}; diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c index 0a5577baea..9195344758 100644 --- a/src/core/iomgr/pollset_posix.c +++ b/src/core/iomgr/pollset_posix.c @@ -232,21 +232,7 @@ void grpc_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, gpr_mu_lock(&pollset->mu); pollset->vtable->add_fd(exec_ctx, pollset, fd, 1); /* the following (enabled only in debug) will reacquire and then release - our lock - meaning that if the unlocking flag passed to del_fd above is - not respected, the code will deadlock (in a way that we have a chance of - debugging) */ -#ifndef NDEBUG - gpr_mu_lock(&pollset->mu); - gpr_mu_unlock(&pollset->mu); -#endif -} - -void grpc_pollset_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_fd *fd) { - gpr_mu_lock(&pollset->mu); - pollset->vtable->del_fd(exec_ctx, pollset, fd, 1); -/* the following (enabled only in debug) will reacquire and then release - our lock - meaning that if the unlocking flag passed to del_fd above is + our lock - meaning that if the unlocking flag passed to add_fd above is not respected, the code will deadlock (in a way that we have a chance of debugging) */ #ifndef NDEBUG @@ -547,19 +533,6 @@ exit: } } -static void basic_pollset_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_fd *fd, int and_unlock_pollset) { - GPR_ASSERT(fd); - if (fd == pollset->data.ptr) { - GRPC_FD_UNREF(pollset->data.ptr, "basicpoll"); - pollset->data.ptr = NULL; - } - - if (and_unlock_pollset) { - gpr_mu_unlock(&pollset->mu); - } -} - static void basic_pollset_maybe_work_and_unlock(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker, @@ -651,9 +624,8 @@ static void basic_pollset_destroy(grpc_pollset *pollset) { } static const grpc_pollset_vtable basic_pollset = { - basic_pollset_add_fd, basic_pollset_del_fd, - basic_pollset_maybe_work_and_unlock, basic_pollset_destroy, - basic_pollset_destroy}; + basic_pollset_add_fd, basic_pollset_maybe_work_and_unlock, + basic_pollset_destroy, basic_pollset_destroy}; static void become_basic_pollset(grpc_pollset *pollset, grpc_fd *fd_or_null) { pollset->vtable = &basic_pollset; diff --git a/src/core/iomgr/pollset_posix.h b/src/core/iomgr/pollset_posix.h index e4593728bd..29de4a2026 100644 --- a/src/core/iomgr/pollset_posix.h +++ b/src/core/iomgr/pollset_posix.h @@ -86,8 +86,6 @@ typedef struct grpc_pollset { struct grpc_pollset_vtable { void (*add_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, struct grpc_fd *fd, int and_unlock_pollset); - void (*del_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - struct grpc_fd *fd, int and_unlock_pollset); void (*maybe_work_and_unlock)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker, gpr_timespec deadline, gpr_timespec now); @@ -100,10 +98,6 @@ struct grpc_pollset_vtable { /* Add an fd to a pollset */ void grpc_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, struct grpc_fd *fd); -/* Force remove an fd from a pollset (normally they are removed on the next - poll after an fd is orphaned) */ -void grpc_pollset_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - struct grpc_fd *fd); /* Returns the fd to listen on for kicks */ int grpc_kick_read_fd(grpc_pollset *p); diff --git a/src/core/iomgr/pollset_set.h b/src/core/iomgr/pollset_set.h index 0fdcba01a4..09c04438f7 100644 --- a/src/core/iomgr/pollset_set.h +++ b/src/core/iomgr/pollset_set.h @@ -49,13 +49,19 @@ #include "src/core/iomgr/pollset_set_windows.h" #endif -void grpc_pollset_set_init(grpc_pollset_set* pollset_set); -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_init(grpc_pollset_set *pollset_set); +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_INTERNAL_CORE_IOMGR_POLLSET_H */ diff --git a/src/core/iomgr/pollset_set_posix.c b/src/core/iomgr/pollset_set_posix.c index c86ed3d5da..4ec92202e3 100644 --- a/src/core/iomgr/pollset_set_posix.c +++ b/src/core/iomgr/pollset_set_posix.c @@ -52,9 +52,10 @@ void grpc_pollset_set_destroy(grpc_pollset_set *pollset_set) { size_t i; gpr_mu_destroy(&pollset_set->mu); for (i = 0; i < pollset_set->fd_count; i++) { - GRPC_FD_UNREF(pollset_set->fds[i], "pollset"); + GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set"); } gpr_free(pollset_set->pollsets); + gpr_free(pollset_set->pollset_sets); gpr_free(pollset_set->fds); } @@ -73,7 +74,7 @@ void grpc_pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, pollset_set->pollsets[pollset_set->pollset_count++] = pollset; for (i = 0, j = 0; i < pollset_set->fd_count; i++) { if (grpc_fd_is_orphaned(pollset_set->fds[i])) { - GRPC_FD_UNREF(pollset_set->fds[i], "pollset"); + GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set"); } else { grpc_pollset_add_fd(exec_ctx, pollset, pollset_set->fds[i]); pollset_set->fds[j++] = pollset_set->fds[i]; @@ -99,6 +100,46 @@ void grpc_pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, gpr_mu_unlock(&pollset_set->mu); } +void grpc_pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) { + size_t i, j; + gpr_mu_lock(&bag->mu); + if (bag->pollset_set_count == bag->pollset_set_capacity) { + bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity); + bag->pollset_sets = + gpr_realloc(bag->pollset_sets, + bag->pollset_set_capacity * sizeof(*bag->pollset_sets)); + } + bag->pollset_sets[bag->pollset_set_count++] = item; + for (i = 0, j = 0; i < bag->fd_count; i++) { + if (grpc_fd_is_orphaned(bag->fds[i])) { + GRPC_FD_UNREF(bag->fds[i], "pollset_set"); + } else { + grpc_pollset_set_add_fd(exec_ctx, item, bag->fds[i]); + bag->fds[j++] = bag->fds[i]; + } + } + bag->fd_count = j; + gpr_mu_unlock(&bag->mu); +} + +void grpc_pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) { + size_t i; + gpr_mu_lock(&bag->mu); + for (i = 0; i < bag->pollset_set_count; i++) { + if (bag->pollset_sets[i] == item) { + bag->pollset_set_count--; + GPR_SWAP(grpc_pollset_set *, bag->pollset_sets[i], + bag->pollset_sets[bag->pollset_set_count]); + break; + } + } + gpr_mu_unlock(&bag->mu); +} + void grpc_pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pollset_set, grpc_fd *fd) { size_t i; @@ -113,6 +154,9 @@ void grpc_pollset_set_add_fd(grpc_exec_ctx *exec_ctx, for (i = 0; i < pollset_set->pollset_count; i++) { grpc_pollset_add_fd(exec_ctx, pollset_set->pollsets[i], fd); } + for (i = 0; i < pollset_set->pollset_set_count; i++) { + grpc_pollset_set_add_fd(exec_ctx, pollset_set->pollset_sets[i], fd); + } gpr_mu_unlock(&pollset_set->mu); } @@ -129,6 +173,9 @@ void grpc_pollset_set_del_fd(grpc_exec_ctx *exec_ctx, break; } } + for (i = 0; i < pollset_set->pollset_set_count; i++) { + grpc_pollset_set_del_fd(exec_ctx, pollset_set->pollset_sets[i], fd); + } gpr_mu_unlock(&pollset_set->mu); } diff --git a/src/core/iomgr/pollset_set_posix.h b/src/core/iomgr/pollset_set_posix.h index 05234fb642..4820a61e4b 100644 --- a/src/core/iomgr/pollset_set_posix.h +++ b/src/core/iomgr/pollset_set_posix.h @@ -44,6 +44,10 @@ typedef struct grpc_pollset_set { size_t pollset_capacity; grpc_pollset **pollsets; + size_t pollset_set_count; + size_t pollset_set_capacity; + struct grpc_pollset_set **pollset_sets; + size_t fd_count; size_t fd_capacity; grpc_fd **fds; diff --git a/src/core/iomgr/pollset_set_windows.c b/src/core/iomgr/pollset_set_windows.c index 53d5d3dcd4..157b46ec32 100644 --- a/src/core/iomgr/pollset_set_windows.c +++ b/src/core/iomgr/pollset_set_windows.c @@ -49,4 +49,12 @@ 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 /* GPR_WINSOCK_SOCKET */ diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c index a89ee02d34..835675c390 100644 --- a/src/core/iomgr/tcp_server_posix.c +++ b/src/core/iomgr/tcp_server_posix.c @@ -411,7 +411,6 @@ static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, int fd, grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, size_t addr_len) { - int allocated_port = -1; grpc_tcp_listener *sp; grpc_tcp_listener *sp2 = NULL; int fd; @@ -464,14 +463,13 @@ grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s, addr_len = sizeof(wild6); fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode); sp = add_socket_to_server(s, fd, addr, addr_len); - allocated_port = sp->port; 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_port > 0) { - grpc_sockaddr_set_port((struct sockaddr *)&wild4, allocated_port); + if (port == 0 && sp != NULL) { + grpc_sockaddr_set_port((struct sockaddr *)&wild4, sp->port); sp2 = sp; } addr = (struct sockaddr *)&wild4; @@ -488,8 +486,8 @@ grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s, addr_len = sizeof(addr4_copy); } sp = add_socket_to_server(s, fd, addr, addr_len); - sp->sibling = sp2; - if (sp2) sp2->is_sibling = 1; + if (sp != NULL) sp->sibling = sp2; + if (sp2 != NULL) sp2->is_sibling = 1; done: gpr_free(allocated_addr); @@ -534,8 +532,12 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, } int grpc_tcp_listener_get_port(grpc_tcp_listener *listener) { - grpc_tcp_listener *sp = listener; - return sp->port; + if (listener != NULL) { + grpc_tcp_listener *sp = listener; + return sp->port; + } else { + return 0; + } } void grpc_tcp_listener_ref(grpc_tcp_listener *listener) { diff --git a/src/core/iomgr/tcp_server_windows.c b/src/core/iomgr/tcp_server_windows.c index a2425cd4d2..583cab4890 100644 --- a/src/core/iomgr/tcp_server_windows.c +++ b/src/core/iomgr/tcp_server_windows.c @@ -486,8 +486,12 @@ void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, } int grpc_tcp_listener_get_port(grpc_tcp_listener *listener) { - grpc_tcp_listener *sp = listener; - return sp->port; + if (listener != NULL) { + grpc_tcp_listener *sp = listener; + return sp->port; + } else { + return 0; + } } void grpc_tcp_listener_ref(grpc_tcp_listener *listener) { diff --git a/src/core/iomgr/tcp_windows.c b/src/core/iomgr/tcp_windows.c index 5ff78231bd..cc7f7ff8d2 100644 --- a/src/core/iomgr/tcp_windows.c +++ b/src/core/iomgr/tcp_windows.c @@ -197,7 +197,8 @@ static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, tcp->read_slice = gpr_slice_malloc(8192); - buffer.len = GPR_SLICE_LENGTH(tcp->read_slice); + buffer.len = (ULONG)GPR_SLICE_LENGTH( + tcp->read_slice); // we know slice size fits in 32bit. buffer.buf = (char *)GPR_SLICE_START_PTR(tcp->read_slice); TCP_REF(tcp, "read"); @@ -273,6 +274,7 @@ static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, WSABUF local_buffers[16]; WSABUF *allocated = NULL; WSABUF *buffers = local_buffers; + size_t len; if (tcp->shutting_down) { grpc_exec_ctx_enqueue(exec_ctx, cb, 0); @@ -281,19 +283,21 @@ static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, tcp->write_cb = cb; tcp->write_slices = slices; - + GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); if (tcp->write_slices->count > GPR_ARRAY_SIZE(local_buffers)) { buffers = (WSABUF *)gpr_malloc(sizeof(WSABUF) * tcp->write_slices->count); allocated = buffers; } for (i = 0; i < tcp->write_slices->count; i++) { - buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices->slices[i]); + len = GPR_SLICE_LENGTH(tcp->write_slices->slices[i]); + GPR_ASSERT(len <= ULONG_MAX); + buffers[i].len = (ULONG)len; buffers[i].buf = (char *)GPR_SLICE_START_PTR(tcp->write_slices->slices[i]); } /* First, let's try a synchronous, non-blocking write. */ - status = WSASend(socket->socket, buffers, tcp->write_slices->count, + status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count, &bytes_sent, 0, NULL, NULL); info->wsa_error = status == 0 ? 0 : WSAGetLastError(); @@ -322,7 +326,7 @@ static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same operation, this time asynchronously. */ memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED)); - status = WSASend(socket->socket, buffers, tcp->write_slices->count, + status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count, &bytes_sent, 0, &socket->write_info.overlapped, NULL); if (allocated) gpr_free(allocated); diff --git a/src/core/iomgr/timer.c b/src/core/iomgr/timer.c index 66fafe75ad..bbf9800049 100644 --- a/src/core/iomgr/timer.c +++ b/src/core/iomgr/timer.c @@ -126,8 +126,8 @@ static double ts_to_dbl(gpr_timespec ts) { static gpr_timespec dbl_to_ts(double d) { gpr_timespec ts; - ts.tv_sec = (time_t)d; - ts.tv_nsec = (int)(1e9 * (d - (double)ts.tv_sec)); + ts.tv_sec = (gpr_int64)d; + ts.tv_nsec = (gpr_int32)(1e9 * (d - (double)ts.tv_sec)); ts.clock_type = GPR_TIMESPAN; return ts; } @@ -343,11 +343,3 @@ int grpc_timer_check(grpc_exec_ctx *exec_ctx, gpr_timespec now, exec_ctx, now, next, gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0); } - -gpr_timespec grpc_timer_list_next_timeout(void) { - gpr_timespec out; - gpr_mu_lock(&g_mu); - out = g_shard_queue[0]->min_deadline; - gpr_mu_unlock(&g_mu); - return out; -} diff --git a/src/core/iomgr/timer_internal.h b/src/core/iomgr/timer_internal.h index f180eca36e..f182e73764 100644 --- a/src/core/iomgr/timer_internal.h +++ b/src/core/iomgr/timer_internal.h @@ -54,8 +54,6 @@ int grpc_timer_check(grpc_exec_ctx* exec_ctx, gpr_timespec now, void grpc_timer_list_init(gpr_timespec now); void grpc_timer_list_shutdown(grpc_exec_ctx* exec_ctx); -gpr_timespec grpc_timer_list_next_timeout(void); - /* the following must be implemented by each iomgr implementation */ void grpc_kick_poller(void); diff --git a/src/core/iomgr/udp_server.c b/src/core/iomgr/udp_server.c index 782fbd9f46..28f1bfae26 100644 --- a/src/core/iomgr/udp_server.c +++ b/src/core/iomgr/udp_server.c @@ -38,6 +38,7 @@ #include <grpc/support/port_platform.h> +#ifdef GRPC_NEED_UDP #ifdef GPR_POSIX_SOCKET #include "src/core/iomgr/udp_server.h" @@ -435,3 +436,4 @@ void grpc_udp_server_write(server_port *sp, const char *buffer, size_t buf_len, } #endif +#endif diff --git a/src/core/iomgr/wakeup_fd_posix.c b/src/core/iomgr/wakeup_fd_posix.c index d09fb78d12..f40be081b0 100644 --- a/src/core/iomgr/wakeup_fd_posix.c +++ b/src/core/iomgr/wakeup_fd_posix.c @@ -40,19 +40,17 @@ #include <stddef.h> static const grpc_wakeup_fd_vtable *wakeup_fd_vtable = NULL; +int grpc_allow_specialized_wakeup_fd = 1; void grpc_wakeup_fd_global_init(void) { - if (grpc_specialized_wakeup_fd_vtable.check_availability()) { + if (grpc_allow_specialized_wakeup_fd && + grpc_specialized_wakeup_fd_vtable.check_availability()) { wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable; } else { wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable; } } -void grpc_wakeup_fd_global_init_force_fallback(void) { - wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable; -} - void grpc_wakeup_fd_global_destroy(void) { wakeup_fd_vtable = NULL; } void grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info) { diff --git a/src/core/iomgr/wakeup_fd_posix.h b/src/core/iomgr/wakeup_fd_posix.h index fe71b5abe9..ffd60d1d4e 100644 --- a/src/core/iomgr/wakeup_fd_posix.h +++ b/src/core/iomgr/wakeup_fd_posix.h @@ -85,6 +85,8 @@ struct grpc_wakeup_fd { int write_fd; }; +extern int grpc_allow_specialized_wakeup_fd; + #define GRPC_WAKEUP_FD_GET_READ_FD(fd_info) ((fd_info)->read_fd) void grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info); diff --git a/src/core/iomgr/workqueue_posix.c b/src/core/iomgr/workqueue_posix.c index 2e30178131..d2a1c34612 100644 --- a/src/core/iomgr/workqueue_posix.c +++ b/src/core/iomgr/workqueue_posix.c @@ -103,6 +103,9 @@ void grpc_workqueue_add_to_pollset(grpc_exec_ctx *exec_ctx, void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) { gpr_mu_lock(&workqueue->mu); + if (grpc_closure_list_empty(workqueue->closure_list)) { + grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd); + } grpc_closure_list_move(&exec_ctx->closure_list, &workqueue->closure_list); gpr_mu_unlock(&workqueue->mu); } diff --git a/src/core/json/json_reader.c b/src/core/json/json_reader.c index 8abad01252..256995240a 100644 --- a/src/core/json/json_reader.c +++ b/src/core/json/json_reader.c @@ -35,6 +35,8 @@ #include <grpc/support/port_platform.h> +#include <grpc/support/log.h> + #include "src/core/json/json_reader.h" static void json_reader_string_clear(grpc_json_reader *reader) { @@ -224,13 +226,13 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { reader->in_array = 1; break; case GRPC_JSON_TOP_LEVEL: - if (reader->depth != 0) return GRPC_JSON_INTERNAL_ERROR; + GPR_ASSERT(reader->depth == 0); reader->in_object = 0; reader->in_array = 0; reader->state = GRPC_JSON_STATE_END; break; default: - return GRPC_JSON_INTERNAL_ERROR; + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); } } break; @@ -279,8 +281,7 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { break; case GRPC_JSON_STATE_OBJECT_KEY_STRING: - if (reader->unicode_high_surrogate != 0) - return GRPC_JSON_PARSE_ERROR; + GPR_ASSERT(reader->unicode_high_surrogate == 0); if (c == '"') { reader->state = GRPC_JSON_STATE_OBJECT_KEY_END; json_reader_set_key(reader); @@ -461,7 +462,7 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { } break; default: - return GRPC_JSON_INTERNAL_ERROR; + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); } break; @@ -641,7 +642,7 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { case ',': case '}': case ']': - return GRPC_JSON_INTERNAL_ERROR; + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); break; default: @@ -655,5 +656,5 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { } } - return GRPC_JSON_INTERNAL_ERROR; + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); } diff --git a/src/core/json/json_string.c b/src/core/json/json_string.c index 0461c2703f..06c157dc98 100644 --- a/src/core/json/json_string.c +++ b/src/core/json/json_string.c @@ -353,7 +353,7 @@ static void json_dump_recursive(grpc_json_writer *writer, grpc_json *json, grpc_json_writer_value_raw_with_len(writer, "null", 4); break; default: - abort(); + GPR_UNREACHABLE_CODE(abort()); } json = json->next; } diff --git a/src/core/profiling/basic_timers.c b/src/core/profiling/basic_timers.c index f0fce7858d..eedd387ebc 100644 --- a/src/core/profiling/basic_timers.c +++ b/src/core/profiling/basic_timers.c @@ -141,10 +141,11 @@ static void write_log(gpr_timer_log *log) { entry->tm = gpr_time_0(entry->tm.clock_type); } fprintf(output_file, - "{\"t\": %ld.%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": " + "{\"t\": %lld.%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": " "\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n", - entry->tm.tv_sec, entry->tm.tv_nsec, entry->thd, entry->type, - entry->tagstr, entry->file, entry->line, entry->important); + (long long)entry->tm.tv_sec, (int)entry->tm.tv_nsec, entry->thd, + entry->type, entry->tagstr, entry->file, entry->line, + entry->important); } } diff --git a/src/core/security/client_auth_filter.c b/src/core/security/client_auth_filter.c index 4e5be03d85..b1fd733c91 100644 --- a/src/core/security/client_auth_filter.c +++ b/src/core/security/client_auth_filter.c @@ -39,11 +39,11 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> -#include "src/core/support/string.h" #include "src/core/channel/channel_stack.h" -#include "src/core/security/security_context.h" -#include "src/core/security/security_connector.h" #include "src/core/security/credentials.h" +#include "src/core/security/security_connector.h" +#include "src/core/security/security_context.h" +#include "src/core/support/string.h" #include "src/core/surface/call.h" #include "src/core/transport/static_metadata.h" diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c index 543c75044b..a0054741ad 100644 --- a/src/core/security/credentials.c +++ b/src/core/security/credentials.c @@ -39,7 +39,7 @@ #include "src/core/channel/channel_args.h" #include "src/core/channel/http_client_filter.h" #include "src/core/httpcli/httpcli.h" -#include "src/core/iomgr/iomgr.h" +#include "src/core/iomgr/executor.h" #include "src/core/json/json.h" #include "src/core/support/string.h" #include "src/core/surface/api_trace.h" @@ -48,7 +48,6 @@ #include <grpc/support/log.h> #include <grpc/support/string_util.h> #include <grpc/support/sync.h> -#include <grpc/support/thd.h> #include <grpc/support/time.h> /* -- Common. -- */ @@ -511,10 +510,11 @@ grpc_call_credentials *grpc_service_account_jwt_access_credentials_create( "grpc_service_account_jwt_access_credentials_create(" "json_key=%s, " "token_lifetime=" - "gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " "reserved=%p)", - 5, (json_key, (long)token_lifetime.tv_sec, token_lifetime.tv_nsec, - (int)token_lifetime.clock_type, reserved)); + 5, + (json_key, (long long)token_lifetime.tv_sec, (int)token_lifetime.tv_nsec, + (int)token_lifetime.clock_type, reserved)); GPR_ASSERT(reserved == NULL); return grpc_service_account_jwt_access_credentials_create_from_auth_json_key( grpc_auth_json_key_create_from_string(json_key), token_lifetime); @@ -792,15 +792,14 @@ static void md_only_test_destruct(grpc_call_credentials *creds) { grpc_credentials_md_store_unref(c->md_store); } -static void on_simulated_token_fetch_done(void *user_data) { +static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx, + void *user_data, int success) { grpc_credentials_metadata_request *r = (grpc_credentials_metadata_request *)user_data; grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)r->creds; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - r->cb(&exec_ctx, r->user_data, c->md_store->entries, c->md_store->num_entries, + r->cb(exec_ctx, r->user_data, c->md_store->entries, c->md_store->num_entries, GRPC_CREDENTIALS_OK); grpc_credentials_metadata_request_destroy(r); - grpc_exec_ctx_finish(&exec_ctx); } static void md_only_test_get_request_metadata( @@ -810,10 +809,10 @@ static void md_only_test_get_request_metadata( grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; if (c->is_async) { - gpr_thd_id thd_id; grpc_credentials_metadata_request *cb_arg = grpc_credentials_metadata_request_create(creds, cb, user_data); - gpr_thd_new(&thd_id, on_simulated_token_fetch_done, cb_arg, NULL); + grpc_executor_enqueue( + grpc_closure_create(on_simulated_token_fetch_done, cb_arg), 1); } else { cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK); } diff --git a/src/core/security/credentials.h b/src/core/security/credentials.h index 6d45895e77..3cd652cd57 100644 --- a/src/core/security/credentials.h +++ b/src/core/security/credentials.h @@ -93,6 +93,14 @@ typedef enum { /* It is the caller's responsibility to gpr_free the result if not NULL. */ char *grpc_get_well_known_google_credentials_file_path(void); +/* Implementation function for the different platforms. */ +char *grpc_get_well_known_google_credentials_file_path_impl(void); + +/* Override for testing only. Not thread-safe */ +typedef char *(*grpc_well_known_credentials_path_getter)(void); +void grpc_override_well_known_credentials_path_getter( + grpc_well_known_credentials_path_getter getter); + /* --- grpc_channel_credentials. --- */ typedef struct { @@ -201,6 +209,7 @@ grpc_credentials_status grpc_oauth2_token_fetcher_credentials_parse_server_response( const struct grpc_httpcli_response *response, grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime); + void grpc_flush_cached_google_default_credentials(void); /* Metadata-only credentials with the specified key and value where diff --git a/src/core/security/credentials_posix.c b/src/core/security/credentials_posix.c index 20f67a7f14..0c92bd4a96 100644 --- a/src/core/security/credentials_posix.c +++ b/src/core/security/credentials_posix.c @@ -44,7 +44,7 @@ #include "src/core/support/env.h" #include "src/core/support/string.h" -char *grpc_get_well_known_google_credentials_file_path(void) { +char *grpc_get_well_known_google_credentials_file_path_impl(void) { char *result = NULL; char *home = gpr_getenv("HOME"); if (home == NULL) { diff --git a/src/core/security/credentials_win32.c b/src/core/security/credentials_win32.c index 92dfd9bdfe..8ee9f706a1 100644 --- a/src/core/security/credentials_win32.c +++ b/src/core/security/credentials_win32.c @@ -44,7 +44,7 @@ #include "src/core/support/env.h" #include "src/core/support/string.h" -char *grpc_get_well_known_google_credentials_file_path(void) { +char *grpc_get_well_known_google_credentials_file_path_impl(void) { char *result = NULL; char *appdata_path = gpr_getenv("APPDATA"); if (appdata_path == NULL) { diff --git a/src/core/security/google_default_credentials.c b/src/core/security/google_default_credentials.c index 6a54fe4e47..5385e41130 100644 --- a/src/core/security/google_default_credentials.c +++ b/src/core/security/google_default_credentials.c @@ -241,5 +241,20 @@ void grpc_flush_cached_google_default_credentials(void) { grpc_channel_credentials_unref(default_credentials); default_credentials = NULL; } + compute_engine_detection_done = 0; gpr_mu_unlock(&g_mu); } + +/* -- Well known credentials path. -- */ + +static grpc_well_known_credentials_path_getter creds_path_getter = NULL; + +char *grpc_get_well_known_google_credentials_file_path(void) { + if (creds_path_getter != NULL) return creds_path_getter(); + return grpc_get_well_known_google_credentials_file_path_impl(); +} + +void grpc_override_well_known_credentials_path_getter( + grpc_well_known_credentials_path_getter getter) { + creds_path_getter = getter; +} diff --git a/src/core/security/handshake.c b/src/core/security/handshake.c index adbdd0b40e..6734187fce 100644 --- a/src/core/security/handshake.c +++ b/src/core/security/handshake.c @@ -64,12 +64,39 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *setup, int success); +static void security_connector_remove_handshake(grpc_security_handshake *h) { + grpc_security_connector_handshake_list *node; + grpc_security_connector_handshake_list *tmp; + grpc_security_connector *sc = h->connector; + gpr_mu_lock(&sc->mu); + node = sc->handshaking_handshakes; + if (node && node->handshake == h) { + sc->handshaking_handshakes = node->next; + gpr_free(node); + gpr_mu_unlock(&sc->mu); + return; + } + while (node) { + if (node->next->handshake == h) { + tmp = node->next; + node->next = node->next->next; + gpr_free(tmp); + gpr_mu_unlock(&sc->mu); + return; + } + node = node->next; + } + gpr_mu_unlock(&sc->mu); +} + static void security_handshake_done(grpc_exec_ctx *exec_ctx, grpc_security_handshake *h, int is_success) { + if (!h->connector->is_client_side) { + security_connector_remove_handshake(h); + } if (is_success) { - h->cb(exec_ctx, h->user_data, GRPC_SECURITY_OK, h->wrapped_endpoint, - h->secure_endpoint); + h->cb(exec_ctx, h->user_data, GRPC_SECURITY_OK, h->secure_endpoint); } else { if (h->secure_endpoint != NULL) { grpc_endpoint_shutdown(exec_ctx, h->secure_endpoint); @@ -77,8 +104,7 @@ static void security_handshake_done(grpc_exec_ctx *exec_ctx, } else { grpc_endpoint_destroy(exec_ctx, h->wrapped_endpoint); } - h->cb(exec_ctx, h->user_data, GRPC_SECURITY_ERROR, h->wrapped_endpoint, - NULL); + h->cb(exec_ctx, h->user_data, GRPC_SECURITY_ERROR, NULL); } if (h->handshaker != NULL) tsi_handshaker_destroy(h->handshaker); if (h->handshake_buffer != NULL) gpr_free(h->handshake_buffer); @@ -268,6 +294,7 @@ void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx, grpc_endpoint *nonsecure_endpoint, grpc_security_handshake_done_cb cb, void *user_data) { + grpc_security_connector_handshake_list *handshake_node; grpc_security_handshake *h = gpr_malloc(sizeof(grpc_security_handshake)); memset(h, 0, sizeof(grpc_security_handshake)); h->handshaker = handshaker; @@ -284,5 +311,19 @@ void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx, gpr_slice_buffer_init(&h->left_overs); gpr_slice_buffer_init(&h->outgoing); gpr_slice_buffer_init(&h->incoming); + if (!connector->is_client_side) { + handshake_node = gpr_malloc(sizeof(grpc_security_connector_handshake_list)); + handshake_node->handshake = h; + gpr_mu_lock(&connector->mu); + handshake_node->next = connector->handshaking_handshakes; + connector->handshaking_handshakes = handshake_node; + gpr_mu_unlock(&connector->mu); + } send_handshake_bytes_to_peer(exec_ctx, h); } + +void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, + void *handshake) { + grpc_security_handshake *h = handshake; + grpc_endpoint_shutdown(exec_ctx, h->wrapped_endpoint); +} diff --git a/src/core/security/handshake.h b/src/core/security/handshake.h index 28eaa79dc3..44215d16ef 100644 --- a/src/core/security/handshake.h +++ b/src/core/security/handshake.h @@ -45,4 +45,6 @@ void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx, grpc_security_handshake_done_cb cb, void *user_data); +void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, void *handshake); + #endif /* GRPC_INTERNAL_CORE_SECURITY_HANDSHAKE_H */ diff --git a/src/core/security/json_token.c b/src/core/security/json_token.c index 021912f333..92775d885d 100644 --- a/src/core/security/json_token.c +++ b/src/core/security/json_token.c @@ -215,8 +215,8 @@ static char *encoded_jwt_claim(const grpc_auth_json_key *json_key, gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value."); expiration = gpr_time_add(now, grpc_max_auth_token_lifetime); } - gpr_ltoa(now.tv_sec, now_str); - gpr_ltoa(expiration.tv_sec, expiration_str); + gpr_int64toa(now.tv_sec, now_str); + gpr_int64toa(expiration.tv_sec, expiration_str); child = create_child(NULL, json, "iss", json_key->client_email, GRPC_JSON_STRING); diff --git a/src/core/security/security_connector.c b/src/core/security/security_connector.c index 3c54a4deae..8c6ab0b8a4 100644 --- a/src/core/security/security_connector.c +++ b/src/core/security/security_connector.c @@ -102,13 +102,29 @@ const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, return NULL; } +void grpc_security_connector_shutdown(grpc_exec_ctx *exec_ctx, + grpc_security_connector *connector) { + grpc_security_connector_handshake_list *tmp; + if (!connector->is_client_side) { + gpr_mu_lock(&connector->mu); + while (connector->handshaking_handshakes) { + tmp = connector->handshaking_handshakes; + grpc_security_handshake_shutdown( + exec_ctx, connector->handshaking_handshakes->handshake); + connector->handshaking_handshakes = tmp->next; + gpr_free(tmp); + } + gpr_mu_unlock(&connector->mu); + } +} + void grpc_security_connector_do_handshake(grpc_exec_ctx *exec_ctx, grpc_security_connector *sc, grpc_endpoint *nonsecure_endpoint, grpc_security_handshake_done_cb cb, void *user_data) { if (sc == NULL || nonsecure_endpoint == NULL) { - cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, nonsecure_endpoint, NULL); + cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL); } else { sc->vtable->do_handshake(exec_ctx, sc, nonsecure_endpoint, cb, user_data); } @@ -219,6 +235,7 @@ static void fake_channel_destroy(grpc_security_connector *sc) { static void fake_server_destroy(grpc_security_connector *sc) { GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + gpr_mu_destroy(&sc->mu); gpr_free(sc); } @@ -319,6 +336,7 @@ grpc_security_connector *grpc_fake_server_security_connector_create(void) { c->is_client_side = 0; c->vtable = &fake_server_vtable; c->url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; + gpr_mu_init(&c->mu); return c; } @@ -354,10 +372,12 @@ static void ssl_channel_destroy(grpc_security_connector *sc) { static void ssl_server_destroy(grpc_security_connector *sc) { grpc_ssl_server_security_connector *c = (grpc_ssl_server_security_connector *)sc; + if (c->handshaker_factory != NULL) { tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); } GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + gpr_mu_destroy(&sc->mu); gpr_free(sc); } @@ -390,7 +410,7 @@ static void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx, : c->target_name, &handshaker); if (status != GRPC_SECURITY_OK) { - cb(exec_ctx, user_data, status, nonsecure_endpoint, NULL); + cb(exec_ctx, user_data, status, NULL); } else { grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, user_data); @@ -408,7 +428,7 @@ static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx, grpc_security_status status = ssl_create_handshaker(c->handshaker_factory, 0, NULL, &handshaker); if (status != GRPC_SECURITY_OK) { - cb(exec_ctx, user_data, status, nonsecure_endpoint, NULL); + cb(exec_ctx, user_data, status, NULL); } else { grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, user_data); @@ -691,6 +711,7 @@ grpc_security_status grpc_ssl_server_security_connector_create( *sc = NULL; goto error; } + gpr_mu_init(&c->base.mu); *sc = &c->base; gpr_free((void *)alpn_protocol_strings); gpr_free(alpn_protocol_string_lengths); diff --git a/src/core/security/security_connector.h b/src/core/security/security_connector.h index 24095cddde..7edb05a662 100644 --- a/src/core/security/security_connector.h +++ b/src/core/security/security_connector.h @@ -67,7 +67,6 @@ typedef void (*grpc_security_check_cb)(grpc_exec_ctx *exec_ctx, void *user_data, typedef void (*grpc_security_handshake_done_cb)(grpc_exec_ctx *exec_ctx, void *user_data, grpc_security_status status, - grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint); typedef struct { @@ -80,13 +79,22 @@ typedef struct { void *user_data); } grpc_security_connector_vtable; +typedef struct grpc_security_connector_handshake_list { + void *handshake; + struct grpc_security_connector_handshake_list *next; +} grpc_security_connector_handshake_list; + struct grpc_security_connector { const grpc_security_connector_vtable *vtable; gpr_refcount refcount; int is_client_side; const char *url_scheme; grpc_auth_context *auth_context; /* Populated after the peer is checked. */ - const grpc_channel_args *channel_args; /* Server side only. */ + /* Used on server side only. */ + /* TODO(yangg) maybe create a grpc_server_security_connector with these */ + gpr_mu mu; + grpc_security_connector_handshake_list *handshaking_handshakes; + const grpc_channel_args *channel_args; }; /* Refcounting. */ @@ -127,6 +135,9 @@ grpc_security_status grpc_security_connector_check_peer( grpc_security_connector *sc, tsi_peer peer, grpc_security_check_cb cb, void *user_data); +void grpc_security_connector_shutdown(grpc_exec_ctx *exec_ctx, + grpc_security_connector *connector); + /* Util to encapsulate the connector in a channel arg. */ grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc); diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c index 71965f7d96..d1468e40e0 100644 --- a/src/core/security/server_secure_chttp2.c +++ b/src/core/security/server_secure_chttp2.c @@ -52,17 +52,11 @@ #include <grpc/support/sync.h> #include <grpc/support/useful.h> -typedef struct tcp_endpoint_list { - grpc_endpoint *tcp_endpoint; - struct tcp_endpoint_list *next; -} tcp_endpoint_list; - typedef struct grpc_server_secure_state { grpc_server *server; grpc_tcp_server *tcp; grpc_security_connector *sc; grpc_server_credentials *creds; - tcp_endpoint_list *handshaking_tcp_endpoints; int is_shutdown; gpr_mu mu; gpr_refcount refcount; @@ -103,52 +97,28 @@ static void setup_transport(grpc_exec_ctx *exec_ctx, void *statep, grpc_channel_args_destroy(args_copy); } -static int remove_tcp_from_list_locked(grpc_server_secure_state *state, - grpc_endpoint *tcp) { - tcp_endpoint_list *node = state->handshaking_tcp_endpoints; - tcp_endpoint_list *tmp = NULL; - if (node && node->tcp_endpoint == tcp) { - state->handshaking_tcp_endpoints = state->handshaking_tcp_endpoints->next; - gpr_free(node); - return 0; - } - while (node) { - if (node->next->tcp_endpoint == tcp) { - tmp = node->next; - node->next = node->next->next; - gpr_free(tmp); - return 0; - } - node = node->next; - } - return -1; -} - static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *statep, grpc_security_status status, - grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint) { grpc_server_secure_state *state = statep; grpc_transport *transport; if (status == GRPC_SECURITY_OK) { - gpr_mu_lock(&state->mu); - remove_tcp_from_list_locked(state, wrapped_endpoint); - if (!state->is_shutdown) { - transport = grpc_create_chttp2_transport( - exec_ctx, grpc_server_get_channel_args(state->server), - secure_endpoint, 0); - setup_transport(exec_ctx, state, transport); - grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0); - } else { - /* We need to consume this here, because the server may already have gone - * away. */ - grpc_endpoint_destroy(exec_ctx, secure_endpoint); + if (secure_endpoint) { + gpr_mu_lock(&state->mu); + if (!state->is_shutdown) { + transport = grpc_create_chttp2_transport( + exec_ctx, grpc_server_get_channel_args(state->server), + secure_endpoint, 0); + setup_transport(exec_ctx, state, transport); + grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0); + } else { + /* We need to consume this here, because the server may already have + * gone away. */ + grpc_endpoint_destroy(exec_ctx, secure_endpoint); + } + gpr_mu_unlock(&state->mu); } - gpr_mu_unlock(&state->mu); } else { - gpr_mu_lock(&state->mu); - remove_tcp_from_list_locked(state, wrapped_endpoint); - gpr_mu_unlock(&state->mu); gpr_log(GPR_ERROR, "Secure transport failed with error %d", status); } state_unref(state); @@ -157,14 +127,7 @@ static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *statep, static void on_accept(grpc_exec_ctx *exec_ctx, void *statep, grpc_endpoint *tcp) { grpc_server_secure_state *state = statep; - tcp_endpoint_list *node; state_ref(state); - node = gpr_malloc(sizeof(tcp_endpoint_list)); - node->tcp_endpoint = tcp; - gpr_mu_lock(&state->mu); - node->next = state->handshaking_tcp_endpoints; - state->handshaking_tcp_endpoints = node; - gpr_mu_unlock(&state->mu); grpc_security_connector_do_handshake(exec_ctx, state->sc, tcp, on_secure_handshake_done, state); } @@ -181,14 +144,7 @@ static void destroy_done(grpc_exec_ctx *exec_ctx, void *statep, int success) { grpc_server_secure_state *state = statep; state->destroy_callback->cb(exec_ctx, state->destroy_callback->cb_arg, success); - gpr_mu_lock(&state->mu); - while (state->handshaking_tcp_endpoints != NULL) { - grpc_endpoint_shutdown(exec_ctx, - state->handshaking_tcp_endpoints->tcp_endpoint); - remove_tcp_from_list_locked(state, - state->handshaking_tcp_endpoints->tcp_endpoint); - } - gpr_mu_unlock(&state->mu); + grpc_security_connector_shutdown(exec_ctx, state->sc); state_unref(state); } @@ -253,7 +209,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, tcp, (struct sockaddr *)&resolved->addrs[i].addr, resolved->addrs[i].len); port_temp = grpc_tcp_listener_get_port(listener); - if (port_temp >= 0) { + if (port_temp > 0) { if (port_num == -1) { port_num = port_temp; } else { @@ -281,7 +237,6 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, state->sc = sc; state->creds = grpc_server_credentials_ref(creds); - state->handshaking_tcp_endpoints = NULL; state->is_shutdown = 0; gpr_mu_init(&state->mu); gpr_ref_init(&state->refcount, 1); diff --git a/src/core/statistics/window_stats.c b/src/core/statistics/window_stats.c index 4d0d3cca4a..e744006bb5 100644 --- a/src/core/statistics/window_stats.c +++ b/src/core/statistics/window_stats.c @@ -94,7 +94,7 @@ static gpr_int64 timespec_to_ns(const gpr_timespec ts) { if (ts.tv_sec > max_seconds) { return GPR_INT64_MAX - 1; } - return (gpr_int64)ts.tv_sec * GPR_NS_PER_SEC + ts.tv_nsec; + return ts.tv_sec * GPR_NS_PER_SEC + ts.tv_nsec; } static void cws_initialize_statistic(void *statistic, diff --git a/src/core/support/alloc.c b/src/core/support/alloc.c index bfcb77956b..ca72379b1d 100644 --- a/src/core/support/alloc.c +++ b/src/core/support/alloc.c @@ -34,13 +34,27 @@ #include <grpc/support/alloc.h> #include <stdlib.h> +#include <grpc/support/log.h> #include <grpc/support/port_platform.h> #include "src/core/profiling/timers.h" +static gpr_allocation_functions g_alloc_functions = {malloc, realloc, free}; + +gpr_allocation_functions gpr_get_allocation_functions() { + return g_alloc_functions; +} + +void gpr_set_allocation_functions(gpr_allocation_functions functions) { + GPR_ASSERT(functions.malloc_fn != NULL); + GPR_ASSERT(functions.realloc_fn != NULL); + GPR_ASSERT(functions.free_fn != NULL); + g_alloc_functions = functions; +} + void *gpr_malloc(size_t size) { void *p; GPR_TIMER_BEGIN("gpr_malloc", 0); - p = malloc(size); + p = g_alloc_functions.malloc_fn(size); if (!p) { abort(); } @@ -50,13 +64,13 @@ void *gpr_malloc(size_t size) { void gpr_free(void *p) { GPR_TIMER_BEGIN("gpr_free", 0); - free(p); + g_alloc_functions.free_fn(p); GPR_TIMER_END("gpr_free", 0); } void *gpr_realloc(void *p, size_t size) { GPR_TIMER_BEGIN("gpr_realloc", 0); - p = realloc(p, size); + p = g_alloc_functions.realloc_fn(p, size); if (!p) { abort(); } diff --git a/src/core/support/cmdline.c b/src/core/support/cmdline.c index 87f60bca2e..b517f30b2d 100644 --- a/src/core/support/cmdline.c +++ b/src/core/support/cmdline.c @@ -62,11 +62,13 @@ struct gpr_cmdline { void (*extra_arg)(void *user_data, const char *arg); void *extra_arg_user_data; - void (*state)(gpr_cmdline *cl, char *arg); + int (*state)(gpr_cmdline *cl, char *arg); arg *cur_arg; + + int survive_failure; }; -static void normal_state(gpr_cmdline *cl, char *arg); +static int normal_state(gpr_cmdline *cl, char *arg); gpr_cmdline *gpr_cmdline_create(const char *description) { gpr_cmdline *cl = gpr_malloc(sizeof(gpr_cmdline)); @@ -78,6 +80,10 @@ gpr_cmdline *gpr_cmdline_create(const char *description) { return cl; } +void gpr_cmdline_set_survive_failure(gpr_cmdline *cl) { + cl->survive_failure = 1; +} + void gpr_cmdline_destroy(gpr_cmdline *cl) { while (cl->args) { arg *a = cl->args; @@ -185,16 +191,22 @@ char *gpr_cmdline_usage_string(gpr_cmdline *cl, const char *argv0) { return tmp; } -static void print_usage_and_die(gpr_cmdline *cl) { +static int print_usage_and_die(gpr_cmdline *cl) { char *usage = gpr_cmdline_usage_string(cl, cl->argv0); fprintf(stderr, "%s", usage); gpr_free(usage); - exit(1); + if (!cl->survive_failure) { + exit(1); + } + return 0; } -static void extra_state(gpr_cmdline *cl, char *str) { - if (!cl->extra_arg) print_usage_and_die(cl); +static int extra_state(gpr_cmdline *cl, char *str) { + if (!cl->extra_arg) { + return print_usage_and_die(cl); + } cl->extra_arg(cl->extra_arg_user_data, str); + return 1; } static arg *find_arg(gpr_cmdline *cl, char *name) { @@ -208,13 +220,13 @@ static arg *find_arg(gpr_cmdline *cl, char *name) { if (!a) { fprintf(stderr, "Unknown argument: %s\n", name); - print_usage_and_die(cl); + return NULL; } return a; } -static void value_state(gpr_cmdline *cl, char *str) { +static int value_state(gpr_cmdline *cl, char *str) { long intval; char *end; @@ -226,7 +238,7 @@ static void value_state(gpr_cmdline *cl, char *str) { if (*end || intval < INT_MIN || intval > INT_MAX) { fprintf(stderr, "expected integer, got '%s' for %s\n", str, cl->cur_arg->name); - print_usage_and_die(cl); + return print_usage_and_die(cl); } *(int *)cl->cur_arg->value = (int)intval; break; @@ -238,7 +250,7 @@ static void value_state(gpr_cmdline *cl, char *str) { } else { fprintf(stderr, "expected boolean, got '%s' for %s\n", str, cl->cur_arg->name); - print_usage_and_die(cl); + return print_usage_and_die(cl); } break; case ARGTYPE_STRING: @@ -247,16 +259,18 @@ static void value_state(gpr_cmdline *cl, char *str) { } cl->state = normal_state; + return 1; } -static void normal_state(gpr_cmdline *cl, char *str) { +static int normal_state(gpr_cmdline *cl, char *str) { char *eq = NULL; char *tmp = NULL; char *arg_name = NULL; + int r = 1; if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") || 0 == strcmp(str, "-h")) { - print_usage_and_die(cl); + return print_usage_and_die(cl); } cl->cur_arg = NULL; @@ -266,7 +280,7 @@ static void normal_state(gpr_cmdline *cl, char *str) { if (str[2] == 0) { /* handle '--' to move to just extra args */ cl->state = extra_state; - return; + return 1; } str += 2; } else { @@ -277,12 +291,15 @@ static void normal_state(gpr_cmdline *cl, char *str) { /* str is of the form '--no-foo' - it's a flag disable */ str += 3; cl->cur_arg = find_arg(cl, str); + if (cl->cur_arg == NULL) { + return print_usage_and_die(cl); + } if (cl->cur_arg->type != ARGTYPE_BOOL) { fprintf(stderr, "%s is not a flag argument\n", str); - print_usage_and_die(cl); + return print_usage_and_die(cl); } *(int *)cl->cur_arg->value = 0; - return; /* early out */ + return 1; /* early out */ } eq = strchr(str, '='); if (eq != NULL) { @@ -294,9 +311,12 @@ static void normal_state(gpr_cmdline *cl, char *str) { arg_name = str; } cl->cur_arg = find_arg(cl, arg_name); + if (cl->cur_arg == NULL) { + return print_usage_and_die(cl); + } if (eq != NULL) { /* str was of the type --foo=value, parse the value */ - value_state(cl, eq + 1); + r = value_state(cl, eq + 1); } else if (cl->cur_arg->type != ARGTYPE_BOOL) { /* flag types don't have a '--foo value' variant, other types do */ cl->state = value_state; @@ -305,19 +325,23 @@ static void normal_state(gpr_cmdline *cl, char *str) { *(int *)cl->cur_arg->value = 1; } } else { - extra_state(cl, str); + r = extra_state(cl, str); } gpr_free(tmp); + return r; } -void gpr_cmdline_parse(gpr_cmdline *cl, int argc, char **argv) { +int gpr_cmdline_parse(gpr_cmdline *cl, int argc, char **argv) { int i; GPR_ASSERT(argc >= 1); cl->argv0 = argv[0]; for (i = 1; i < argc; i++) { - cl->state(cl, argv[i]); + if (!cl->state(cl, argv[i])) { + return 0; + } } + return 1; } diff --git a/src/core/support/log.c b/src/core/support/log.c index f52c2035b9..04156a5b1f 100644 --- a/src/core/support/log.c +++ b/src/core/support/log.c @@ -32,6 +32,7 @@ */ #include <grpc/support/log.h> +#include <grpc/support/port_platform.h> #include <stdio.h> #include <string.h> @@ -48,7 +49,7 @@ const char *gpr_log_severity_string(gpr_log_severity severity) { case GPR_LOG_SEVERITY_ERROR: return "E"; } - return "UNKNOWN"; + GPR_UNREACHABLE_CODE(return "UNKNOWN"); } void gpr_log_message(const char *file, int line, gpr_log_severity severity, diff --git a/src/core/support/log_linux.c b/src/core/support/log_linux.c index 02f64d8b7e..d66b7a3cc0 100644 --- a/src/core/support/log_linux.c +++ b/src/core/support/log_linux.c @@ -76,16 +76,18 @@ void gpr_default_log(gpr_log_func_args *args) { char *prefix; const char *display_file; char time_buffer[64]; + time_t timer; gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); struct tm tm; + timer = (time_t)now.tv_sec; final_slash = strrchr(args->file, '/'); if (final_slash == NULL) display_file = args->file; else display_file = final_slash + 1; - if (!localtime_r(&now.tv_sec, &tm)) { + if (!localtime_r(&timer, &tm)) { strcpy(time_buffer, "error:localtime"); } else if (0 == strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { diff --git a/src/core/support/log_posix.c b/src/core/support/log_posix.c index 8b050dbee7..8986254e4e 100644 --- a/src/core/support/log_posix.c +++ b/src/core/support/log_posix.c @@ -75,16 +75,18 @@ void gpr_default_log(gpr_log_func_args *args) { char *final_slash; const char *display_file; char time_buffer[64]; + time_t timer; gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); struct tm tm; + timer = (time_t)now.tv_sec; final_slash = strrchr(args->file, '/'); if (final_slash == NULL) display_file = args->file; else display_file = final_slash + 1; - if (!localtime_r(&now.tv_sec, &tm)) { + if (!localtime_r(&timer, &tm)) { strcpy(time_buffer, "error:localtime"); } else if (0 == strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { diff --git a/src/core/support/log_win32.c b/src/core/support/log_win32.c index b68239f8f5..40adcd1b50 100644 --- a/src/core/support/log_win32.c +++ b/src/core/support/log_win32.c @@ -84,16 +84,18 @@ void gpr_default_log(gpr_log_func_args *args) { char *final_slash; const char *display_file; char time_buffer[64]; + time_t timer; gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); struct tm tm; + timer = (time_t)now.tv_sec; final_slash = strrchr(args->file, '\\'); if (final_slash == NULL) display_file = args->file; else display_file = final_slash + 1; - if (localtime_s(&tm, &now.tv_sec)) { + if (localtime_s(&tm, &timer)) { strcpy(time_buffer, "error:localtime"); } else if (0 == strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { @@ -104,6 +106,7 @@ void gpr_default_log(gpr_log_func_args *args) { gpr_log_severity_string(args->severity), time_buffer, (int)(now.tv_nsec), GetCurrentThreadId(), display_file, args->line, args->message); + fflush(stderr); } char *gpr_format_message(DWORD messageid) { diff --git a/src/core/support/slice.c b/src/core/support/slice.c index 5b091f17b0..9f0ded4932 100644 --- a/src/core/support/slice.c +++ b/src/core/support/slice.c @@ -341,10 +341,3 @@ int gpr_slice_str_cmp(gpr_slice a, const char *b) { if (d != 0) return d; return memcmp(GPR_SLICE_START_PTR(a), b, b_length); } - -char *gpr_slice_to_cstring(gpr_slice slice) { - char *result = gpr_malloc(GPR_SLICE_LENGTH(slice) + 1); - memcpy(result, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice)); - result[GPR_SLICE_LENGTH(slice)] = '\0'; - return result; -} diff --git a/src/core/support/stack_lockfree.c b/src/core/support/stack_lockfree.c index df9a09894c..fc934d404c 100644 --- a/src/core/support/stack_lockfree.c +++ b/src/core/support/stack_lockfree.c @@ -128,8 +128,8 @@ int gpr_stack_lockfree_push(gpr_stack_lockfree *stack, int entry) { gpr_atm old_val; old_val = gpr_atm_no_barrier_fetch_add(&stack->pushed[pushed_index], - (gpr_atm)(1UL << pushed_bit)); - GPR_ASSERT((old_val & (gpr_atm)(1UL << pushed_bit)) == 0); + ((gpr_atm)1 << pushed_bit)); + GPR_ASSERT((old_val & (((gpr_atm)1) << pushed_bit)) == 0); } #endif @@ -166,8 +166,8 @@ int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack) { gpr_atm old_val; old_val = gpr_atm_no_barrier_fetch_add(&stack->pushed[pushed_index], - -(gpr_atm)(1UL << pushed_bit)); - GPR_ASSERT((old_val & (gpr_atm)(1UL << pushed_bit)) != 0); + -((gpr_atm)1 << pushed_bit)); + GPR_ASSERT((old_val & (((gpr_atm)1) << pushed_bit)) != 0); } #endif diff --git a/src/core/support/string.c b/src/core/support/string.c index e0ffeb8a4a..46a7ca3d46 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -153,8 +153,8 @@ void gpr_reverse_bytes(char *str, int len) { } int gpr_ltoa(long value, char *string) { + long sign; int i = 0; - int neg = value < 0; if (value == 0) { string[0] = '0'; @@ -162,12 +162,33 @@ int gpr_ltoa(long value, char *string) { return 1; } - if (neg) value = -value; + sign = value < 0 ? -1 : 1; while (value) { - string[i++] = (char)('0' + value % 10); + string[i++] = (char)('0' + sign * (value % 10)); value /= 10; } - if (neg) string[i++] = '-'; + if (sign < 0) string[i++] = '-'; + gpr_reverse_bytes(string, i); + string[i] = 0; + return i; +} + +int gpr_int64toa(gpr_int64 value, char *string) { + gpr_int64 sign; + int i = 0; + + if (value == 0) { + string[0] = '0'; + string[1] = 0; + return 1; + } + + sign = value < 0 ? -1 : 1; + while (value) { + string[i++] = (char)('0' + sign * (value % 10)); + value /= 10; + } + if (sign < 0) string[i++] = '-'; gpr_reverse_bytes(string, i); string[i] = 0; return i; diff --git a/src/core/support/string.h b/src/core/support/string.h index a28e00fd3e..9b604ac5bf 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -70,6 +70,16 @@ int gpr_parse_bytes_to_uint32(const char *data, size_t length, output must be at least GPR_LTOA_MIN_BUFSIZE bytes long. */ int gpr_ltoa(long value, char *output); +/* Minimum buffer size for calling int64toa */ +#define GPR_INT64TOA_MIN_BUFSIZE (3 * sizeof(gpr_int64)) + +/* Convert an int64 to a string in base 10; returns the length of the +output string (or 0 on failure). +output must be at least GPR_INT64TOA_MIN_BUFSIZE bytes long. +NOTE: This function ensures sufficient bit width even on Win x64, +where long is 32bit is size.*/ +int gpr_int64toa(gpr_int64 value, char *output); + /* Reverse a run of bytes */ void gpr_reverse_bytes(char *str, int len); diff --git a/src/core/support/time.c b/src/core/support/time.c index 929adac918..197fa9ad44 100644 --- a/src/core/support/time.c +++ b/src/core/support/time.c @@ -56,22 +56,6 @@ gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b) { return gpr_time_cmp(a, b) > 0 ? a : b; } -/* There's no standard TIME_T_MIN and TIME_T_MAX, so we construct them. The - following assumes that signed types are two's-complement and that bytes are - 8 bits. */ - -/* The top bit of integral type t. */ -#define TOP_BIT_OF_TYPE(t) (((gpr_uintmax)1) << ((8 * sizeof(t)) - 1)) - -/* Return whether integral type t is signed. */ -#define TYPE_IS_SIGNED(t) (((t)1) > (t) ~(t)0) - -/* The minimum and maximum value of integral type t. */ -#define TYPE_MIN(t) ((t)(TYPE_IS_SIGNED(t) ? TOP_BIT_OF_TYPE(t) : 0)) -#define TYPE_MAX(t) \ - ((t)(TYPE_IS_SIGNED(t) ? (TOP_BIT_OF_TYPE(t) - 1) \ - : ((TOP_BIT_OF_TYPE(t) - 1) << 1) + 1)) - gpr_timespec gpr_time_0(gpr_clock_type type) { gpr_timespec out; out.tv_sec = 0; @@ -82,7 +66,7 @@ gpr_timespec gpr_time_0(gpr_clock_type type) { gpr_timespec gpr_inf_future(gpr_clock_type type) { gpr_timespec out; - out.tv_sec = TYPE_MAX(time_t); + out.tv_sec = INT64_MAX; out.tv_nsec = 0; out.clock_type = type; return out; @@ -90,7 +74,7 @@ gpr_timespec gpr_inf_future(gpr_clock_type type) { gpr_timespec gpr_inf_past(gpr_clock_type type) { gpr_timespec out; - out.tv_sec = TYPE_MIN(time_t); + out.tv_sec = INT64_MIN; out.tv_nsec = 0; out.clock_type = type; return out; @@ -108,11 +92,11 @@ gpr_timespec gpr_time_from_nanos(long ns, gpr_clock_type type) { result = gpr_inf_past(type); } else if (ns >= 0) { result.tv_sec = ns / GPR_NS_PER_SEC; - result.tv_nsec = (int)(ns - result.tv_sec * GPR_NS_PER_SEC); + result.tv_nsec = (gpr_int32)(ns - result.tv_sec * GPR_NS_PER_SEC); } else { /* Calculation carefully formulated to avoid any possible under/overflow. */ result.tv_sec = (-(999999999 - (ns + GPR_NS_PER_SEC)) / GPR_NS_PER_SEC) - 1; - result.tv_nsec = (int)(ns - result.tv_sec * GPR_NS_PER_SEC); + result.tv_nsec = (gpr_int32)(ns - result.tv_sec * GPR_NS_PER_SEC); } return result; } @@ -126,11 +110,11 @@ gpr_timespec gpr_time_from_micros(long us, gpr_clock_type type) { result = gpr_inf_past(type); } else if (us >= 0) { result.tv_sec = us / 1000000; - result.tv_nsec = (int)((us - result.tv_sec * 1000000) * 1000); + result.tv_nsec = (gpr_int32)((us - result.tv_sec * 1000000) * 1000); } else { /* Calculation carefully formulated to avoid any possible under/overflow. */ result.tv_sec = (-(999999 - (us + 1000000)) / 1000000) - 1; - result.tv_nsec = (int)((us - result.tv_sec * 1000000) * 1000); + result.tv_nsec = (gpr_int32)((us - result.tv_sec * 1000000) * 1000); } return result; } @@ -144,11 +128,11 @@ gpr_timespec gpr_time_from_millis(long ms, gpr_clock_type type) { result = gpr_inf_past(type); } else if (ms >= 0) { result.tv_sec = ms / 1000; - result.tv_nsec = (int)((ms - result.tv_sec * 1000) * 1000000); + result.tv_nsec = (gpr_int32)((ms - result.tv_sec * 1000) * 1000000); } else { /* Calculation carefully formulated to avoid any possible under/overflow. */ result.tv_sec = (-(999 - (ms + 1000)) / 1000) - 1; - result.tv_nsec = (int)((ms - result.tv_sec * 1000) * 1000000); + result.tv_nsec = (gpr_int32)((ms - result.tv_sec * 1000) * 1000000); } return result; } @@ -197,7 +181,7 @@ gpr_timespec gpr_time_from_hours(long h, gpr_clock_type type) { gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { gpr_timespec sum; - int inc = 0; + gpr_int64 inc = 0; GPR_ASSERT(b.clock_type == GPR_TIMESPAN); sum.clock_type = a.clock_type; sum.tv_nsec = a.tv_nsec + b.tv_nsec; @@ -205,17 +189,17 @@ gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { sum.tv_nsec -= GPR_NS_PER_SEC; inc++; } - if (a.tv_sec == TYPE_MAX(time_t) || a.tv_sec == TYPE_MIN(time_t)) { + if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) { sum = a; - } else if (b.tv_sec == TYPE_MAX(time_t) || - (b.tv_sec >= 0 && a.tv_sec >= TYPE_MAX(time_t) - b.tv_sec)) { + } else if (b.tv_sec == INT64_MAX || + (b.tv_sec >= 0 && a.tv_sec >= INT64_MAX - b.tv_sec)) { sum = gpr_inf_future(sum.clock_type); - } else if (b.tv_sec == TYPE_MIN(time_t) || - (b.tv_sec <= 0 && a.tv_sec <= TYPE_MIN(time_t) - b.tv_sec)) { + } else if (b.tv_sec == INT64_MIN || + (b.tv_sec <= 0 && a.tv_sec <= INT64_MIN - b.tv_sec)) { sum = gpr_inf_past(sum.clock_type); } else { sum.tv_sec = a.tv_sec + b.tv_sec; - if (inc != 0 && sum.tv_sec == TYPE_MAX(time_t) - 1) { + if (inc != 0 && sum.tv_sec == INT64_MAX - 1) { sum = gpr_inf_future(sum.clock_type); } else { sum.tv_sec += inc; @@ -226,7 +210,7 @@ gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) { gpr_timespec diff; - int dec = 0; + gpr_int64 dec = 0; if (b.clock_type == GPR_TIMESPAN) { diff.clock_type = a.clock_type; } else { @@ -238,17 +222,17 @@ gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) { diff.tv_nsec += GPR_NS_PER_SEC; dec++; } - if (a.tv_sec == TYPE_MAX(time_t) || a.tv_sec == TYPE_MIN(time_t)) { + if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) { diff = a; - } else if (b.tv_sec == TYPE_MIN(time_t) || - (b.tv_sec <= 0 && a.tv_sec >= TYPE_MAX(time_t) + b.tv_sec)) { + } else if (b.tv_sec == INT64_MIN || + (b.tv_sec <= 0 && a.tv_sec >= INT64_MAX + b.tv_sec)) { diff = gpr_inf_future(GPR_CLOCK_REALTIME); - } else if (b.tv_sec == TYPE_MAX(time_t) || - (b.tv_sec >= 0 && a.tv_sec <= TYPE_MIN(time_t) + b.tv_sec)) { + } else if (b.tv_sec == INT64_MAX || + (b.tv_sec >= 0 && a.tv_sec <= INT64_MIN + b.tv_sec)) { diff = gpr_inf_past(GPR_CLOCK_REALTIME); } else { diff.tv_sec = a.tv_sec - b.tv_sec; - if (dec != 0 && diff.tv_sec == TYPE_MIN(time_t) + 1) { + if (dec != 0 && diff.tv_sec == INT64_MIN + 1) { diff = gpr_inf_past(GPR_CLOCK_REALTIME); } else { diff.tv_sec -= dec; @@ -297,11 +281,11 @@ gpr_timespec gpr_convert_clock_type(gpr_timespec t, gpr_clock_type clock_type) { } if (t.tv_nsec == 0) { - if (t.tv_sec == TYPE_MAX(time_t)) { + if (t.tv_sec == INT64_MAX) { t.clock_type = clock_type; return t; } - if (t.tv_sec == TYPE_MIN(time_t)) { + if (t.tv_sec == INT64_MIN) { t.clock_type = clock_type; return t; } diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c index 02cfca8555..ba72572e05 100644 --- a/src/core/support/time_posix.c +++ b/src/core/support/time_posix.c @@ -45,7 +45,11 @@ static struct timespec timespec_from_gpr(gpr_timespec gts) { struct timespec rv; - rv.tv_sec = gts.tv_sec; + if (sizeof(time_t) < sizeof(gpr_int64)) { + /* fine to assert, as this is only used in gpr_sleep_until */ + GPR_ASSERT(gts.tv_sec <= INT32_MAX && gts.tv_sec >= INT32_MIN); + } + rv.tv_sec = (time_t)gts.tv_sec; rv.tv_nsec = gts.tv_nsec; return rv; } @@ -53,9 +57,14 @@ static struct timespec timespec_from_gpr(gpr_timespec gts) { #if _POSIX_TIMERS > 0 static gpr_timespec gpr_from_timespec(struct timespec ts, gpr_clock_type clock_type) { + /* + * timespec.tv_sec can have smaller size than gpr_timespec.tv_sec, + * but we are only using this function to implement gpr_now + * so there's no need to handle "infinity" values. + */ gpr_timespec rv; rv.tv_sec = ts.tv_sec; - rv.tv_nsec = (int)ts.tv_nsec; + rv.tv_nsec = (gpr_int32)ts.tv_nsec; rv.clock_type = clock_type; return rv; } @@ -110,8 +119,8 @@ gpr_timespec gpr_now(gpr_clock_type clock) { break; case GPR_CLOCK_MONOTONIC: now_dbl = (mach_absolute_time() - g_time_start) * g_time_scale; - now.tv_sec = (time_t)(now_dbl * 1e-9); - now.tv_nsec = (int)(now_dbl - ((double)now.tv_sec) * 1e9); + now.tv_sec = (gpr_int64)(now_dbl * 1e-9); + now.tv_nsec = (gpr_int32)(now_dbl - ((double)now.tv_sec) * 1e9); break; case GPR_CLOCK_PRECISE: gpr_precise_clock_now(&now); diff --git a/src/core/support/time_precise.c b/src/core/support/time_precise.c index b37517e639..4de1d9b071 100644 --- a/src/core/support/time_precise.c +++ b/src/core/support/time_precise.c @@ -75,8 +75,8 @@ void gpr_precise_clock_now(gpr_timespec *clk) { gpr_get_cycle_counter(&counter); secs = (double)(counter - start_cycle) / cycles_per_second; clk->clock_type = GPR_CLOCK_PRECISE; - clk->tv_sec = (time_t)secs; - clk->tv_nsec = (int)(1e9 * (secs - (double)clk->tv_sec)); + clk->tv_sec = (gpr_int64)secs; + clk->tv_nsec = (gpr_int32)(1e9 * (secs - (double)clk->tv_sec)); } #else /* GRPC_TIMERS_RDTSC */ diff --git a/src/core/support/time_win32.c b/src/core/support/time_win32.c index 623a8d9233..7ccaaa248d 100644 --- a/src/core/support/time_win32.c +++ b/src/core/support/time_win32.c @@ -62,15 +62,15 @@ gpr_timespec gpr_now(gpr_clock_type clock) { switch (clock) { case GPR_CLOCK_REALTIME: _ftime_s(&now_tb); - now_tv.tv_sec = now_tb.time; + now_tv.tv_sec = (gpr_int64)now_tb.time; now_tv.tv_nsec = now_tb.millitm * 1000000; break; case GPR_CLOCK_MONOTONIC: case GPR_CLOCK_PRECISE: QueryPerformanceCounter(×tamp); now_dbl = (timestamp.QuadPart - g_start_time.QuadPart) * g_time_scale; - now_tv.tv_sec = (time_t)now_dbl; - now_tv.tv_nsec = (int)((now_dbl - (double)now_tv.tv_sec) * 1e9); + now_tv.tv_sec = (gpr_int64)now_dbl; + now_tv.tv_nsec = (gpr_int32)((now_dbl - (double)now_tv.tv_sec) * 1e9); break; } return now_tv; diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 4affafa585..f8dde0748b 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -336,26 +336,19 @@ void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call, grpc_cq_pollset(cq)); } -grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call) { - return call->cq; -} - #ifdef GRPC_STREAM_REFCOUNT_DEBUG -void grpc_call_internal_ref(grpc_call *c, const char *reason) { - grpc_call_stack_ref(CALL_STACK_FROM_CALL(c), reason); -} -void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c, - const char *reason) { - grpc_call_stack_unref(exec_ctx, CALL_STACK_FROM_CALL(c), reason); -} +#define REF_REASON reason +#define REF_ARG , const char *reason #else -void grpc_call_internal_ref(grpc_call *c) { - grpc_call_stack_ref(CALL_STACK_FROM_CALL(c)); +#define REF_REASON "" +#define REF_ARG +#endif +void grpc_call_internal_ref(grpc_call *c REF_ARG) { + GRPC_CALL_STACK_REF(CALL_STACK_FROM_CALL(c), REF_REASON); } -void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c) { - grpc_call_stack_unref(exec_ctx, CALL_STACK_FROM_CALL(c)); +void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c REF_ARG) { + GRPC_CALL_STACK_UNREF(exec_ctx, CALL_STACK_FROM_CALL(c), REF_REASON); } -#endif static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, int success) { size_t i; @@ -742,8 +735,15 @@ static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call, char *grpc_call_get_peer(grpc_call *call) { grpc_call_element *elem = CALL_ELEM_FROM_CALL(call, 0); grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - char *result = elem->filter->get_peer(&exec_ctx, elem); + char *result; GRPC_API_TRACE("grpc_call_get_peer(%p)", 1, (call)); + result = elem->filter->get_peer(&exec_ctx, elem); + if (result == NULL) { + result = grpc_channel_get_target(call->channel); + } + if (result == NULL) { + result = gpr_strdup("unknown"); + } grpc_exec_ctx_finish(&exec_ctx); return result; } @@ -974,11 +974,19 @@ static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp, batch_control *bctl = bctlp; grpc_call *call = bctl->call; - GPR_ASSERT(success); - gpr_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, - call->receiving_slice); - - continue_receiving_slices(exec_ctx, bctl); + if (success) { + gpr_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, + call->receiving_slice); + continue_receiving_slices(exec_ctx, bctl); + } else { + grpc_byte_stream_destroy(call->receiving_stream); + call->receiving_stream = NULL; + grpc_byte_buffer_destroy(*call->receiving_buffer); + *call->receiving_buffer = NULL; + if (gpr_unref(&bctl->steps_to_complete)) { + post_batch_completion(exec_ctx, bctl); + } + } } static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp, int success) { @@ -1060,6 +1068,7 @@ static void receiving_stream_ready(grpc_exec_ctx *exec_ctx, void *bctlp, if (call->receiving_stream == NULL) { *call->receiving_buffer = NULL; + call->receiving_message = 0; if (gpr_unref(&bctl->steps_to_complete)) { post_batch_completion(exec_ctx, bctl); } @@ -1070,6 +1079,7 @@ static void receiving_stream_ready(grpc_exec_ctx *exec_ctx, void *bctlp, grpc_byte_stream_destroy(call->receiving_stream); call->receiving_stream = NULL; *call->receiving_buffer = NULL; + call->receiving_message = 0; if (gpr_unref(&bctl->steps_to_complete)) { post_batch_completion(exec_ctx, bctl); } @@ -1119,11 +1129,12 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx, GRPC_CALL_INTERNAL_REF(call, "completion"); bctl->success = 1; if (!is_notify_tag_closure) { - grpc_cq_begin_op(call->cq); + grpc_cq_begin_op(call->cq, notify_tag); } gpr_mu_unlock(&call->mu); post_batch_completion(exec_ctx, bctl); - return GRPC_CALL_OK; + error = GRPC_CALL_OK; + goto done; } /* rewrite batch ops into a transport op */ @@ -1270,6 +1281,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx, } if (call->receiving_message) { error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; } call->receiving_message = 1; bctl->recv_message = 1; @@ -1332,7 +1344,7 @@ static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx, GRPC_CALL_INTERNAL_REF(call, "completion"); if (!is_notify_tag_closure) { - grpc_cq_begin_op(call->cq); + grpc_cq_begin_op(call->cq, notify_tag); } gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed); diff --git a/src/core/surface/call.h b/src/core/surface/call.h index 24ab9c33bb..b53340df8e 100644 --- a/src/core/surface/call.h +++ b/src/core/surface/call.h @@ -58,7 +58,6 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call, grpc_completion_queue *cq); -grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call); #ifdef GRPC_STREAM_REFCOUNT_DEBUG void grpc_call_internal_ref(grpc_call *call, const char *reason); @@ -91,19 +90,6 @@ void grpc_call_log_batch(char *file, int line, gpr_log_severity severity, grpc_call *call, const grpc_op *ops, size_t nops, void *tag); -void grpc_server_log_request_call(char *file, int line, - gpr_log_severity severity, - grpc_server *server, grpc_call **call, - grpc_call_details *details, - grpc_metadata_array *initial_metadata, - grpc_completion_queue *cq_bound_to_call, - grpc_completion_queue *cq_for_notification, - void *tag); - -void grpc_server_log_shutdown(char *file, int line, gpr_log_severity severity, - grpc_server *server, grpc_completion_queue *cq, - void *tag); - /* Set a context pointer. No thread safety guarantees are made wrt this value. */ void grpc_call_context_set(grpc_call *call, grpc_context_index elem, diff --git a/src/core/surface/call_log_batch.c b/src/core/surface/call_log_batch.c index 16212f6386..46756f418b 100644 --- a/src/core/surface/call_log_batch.c +++ b/src/core/surface/call_log_batch.c @@ -116,27 +116,3 @@ void grpc_call_log_batch(char *file, int line, gpr_log_severity severity, gpr_free(tmp); } } - -void grpc_server_log_request_call(char *file, int line, - gpr_log_severity severity, - grpc_server *server, grpc_call **call, - grpc_call_details *details, - grpc_metadata_array *initial_metadata, - grpc_completion_queue *cq_bound_to_call, - grpc_completion_queue *cq_for_notification, - void *tag) { - gpr_log(file, line, severity, - "grpc_server_request_call(server=%p, call=%p, details=%p, " - "initial_metadata=%p, cq_bound_to_call=%p, cq_for_notification=%p, " - "tag=%p)", - server, call, details, initial_metadata, cq_bound_to_call, - cq_for_notification, tag); -} - -void grpc_server_log_shutdown(char *file, int line, gpr_log_severity severity, - grpc_server *server, grpc_completion_queue *cq, - void *tag) { - gpr_log(file, line, severity, - "grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", server, - cq, tag); -} diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c index 859197412b..d0a8b0be09 100644 --- a/src/core/surface/channel.c +++ b/src/core/surface/channel.c @@ -63,7 +63,6 @@ typedef struct registered_call { struct grpc_channel { int is_client; - gpr_refcount refs; gpr_uint32 max_message_length; grpc_mdelem *default_authority; @@ -81,6 +80,8 @@ struct grpc_channel { /* the protobuf library will (by default) start warning at 100megs */ #define DEFAULT_MAX_MESSAGE_LENGTH (100 * 1024 * 1024) +static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, int success); + grpc_channel *grpc_channel_create_from_filters( grpc_exec_ctx *exec_ctx, const char *target, const grpc_channel_filter **filters, size_t num_filters, @@ -93,8 +94,6 @@ grpc_channel *grpc_channel_create_from_filters( channel->target = gpr_strdup(target); GPR_ASSERT(grpc_is_initialized() && "call grpc_init()"); channel->is_client = is_client; - /* decremented by grpc_channel_destroy */ - gpr_ref_init(&channel->refs, 1); gpr_mu_init(&channel->registered_call_mu); channel->registered_calls = NULL; @@ -113,7 +112,7 @@ grpc_channel *grpc_channel_create_from_filters( } } else if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) { if (args->args[i].type != GRPC_ARG_STRING) { - gpr_log(GPR_ERROR, "%s: must be an string", + gpr_log(GPR_ERROR, "%s ignored: it must be a string", GRPC_ARG_DEFAULT_AUTHORITY); } else { if (channel->default_authority) { @@ -126,13 +125,14 @@ grpc_channel *grpc_channel_create_from_filters( } else if (0 == strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) { if (args->args[i].type != GRPC_ARG_STRING) { - gpr_log(GPR_ERROR, "%s: must be an string", + gpr_log(GPR_ERROR, "%s ignored: it must be a string", GRPC_SSL_TARGET_NAME_OVERRIDE_ARG); } else { if (channel->default_authority) { /* other ways of setting this (notably ssl) take precedence */ - gpr_log(GPR_ERROR, "%s: default host already set some other way", - GRPC_ARG_DEFAULT_AUTHORITY); + gpr_log(GPR_ERROR, + "%s ignored: default host already set some other way", + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG); } else { channel->default_authority = grpc_mdelem_from_strings( ":authority", args->args[i].value.string); @@ -152,7 +152,9 @@ grpc_channel *grpc_channel_create_from_filters( gpr_free(default_authority); } - grpc_channel_stack_init(exec_ctx, filters, num_filters, channel, args, + grpc_channel_stack_init(exec_ctx, 1, destroy_channel, channel, filters, + num_filters, args, + is_client ? "CLIENT_CHANNEL" : "SERVER_CHANNEL", CHANNEL_STACK_FROM_CHANNEL(channel)); return channel; @@ -193,11 +195,11 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel, "grpc_channel_create_call(" "channel=%p, parent_call=%p, propagation_mask=%x, cq=%p, method=%s, " "host=%s, " - "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " "reserved=%p)", 10, (channel, parent_call, (unsigned)propagation_mask, cq, method, host, - (long)deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, - reserved)); + (long long)deadline.tv_sec, (int)deadline.tv_nsec, + (int)deadline.clock_type, reserved)); GPR_ASSERT(!reserved); return grpc_channel_create_call_internal( channel, parent_call, propagation_mask, cq, @@ -237,11 +239,11 @@ grpc_call *grpc_channel_create_registered_call( "grpc_channel_create_registered_call(" "channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, " "registered_call_handle=%p, " - "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " "reserved=%p)", 9, (channel, parent_call, (unsigned)propagation_mask, completion_queue, - registered_call_handle, (long)deadline.tv_sec, deadline.tv_nsec, - (int)deadline.clock_type, reserved)); + registered_call_handle, (long long)deadline.tv_sec, + (int)deadline.tv_nsec, (int)deadline.clock_type, reserved)); GPR_ASSERT(!reserved); return grpc_channel_create_call_internal( channel, parent_call, propagation_mask, completion_queue, @@ -249,17 +251,25 @@ grpc_call *grpc_channel_create_registered_call( rc->authority ? GRPC_MDELEM_REF(rc->authority) : NULL, deadline); } -#ifdef GRPC_CHANNEL_REF_COUNT_DEBUG -void grpc_channel_internal_ref(grpc_channel *c, const char *reason) { - gpr_log(GPR_DEBUG, "CHANNEL: ref %p %d -> %d [%s]", c, c->refs.count, - c->refs.count + 1, reason); +#ifdef GRPC_STREAM_REFCOUNT_DEBUG +#define REF_REASON reason +#define REF_ARG , const char *reason #else -void grpc_channel_internal_ref(grpc_channel *c) { +#define REF_REASON "" +#define REF_ARG #endif - gpr_ref(&c->refs); +void grpc_channel_internal_ref(grpc_channel *c REF_ARG) { + GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); } -static void destroy_channel(grpc_exec_ctx *exec_ctx, grpc_channel *channel) { +void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, + grpc_channel *c REF_ARG) { + GRPC_CHANNEL_STACK_UNREF(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); +} + +static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, + int iomgr_success) { + grpc_channel *channel = arg; grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(channel)); while (channel->registered_calls) { registered_call *rc = channel->registered_calls; @@ -278,20 +288,6 @@ static void destroy_channel(grpc_exec_ctx *exec_ctx, grpc_channel *channel) { gpr_free(channel); } -#ifdef GRPC_CHANNEL_REF_COUNT_DEBUG -void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, grpc_channel *channel, - const char *reason) { - gpr_log(GPR_DEBUG, "CHANNEL: unref %p %d -> %d [%s]", channel, - channel->refs.count, channel->refs.count - 1, reason); -#else -void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, - grpc_channel *channel) { -#endif - if (gpr_unref(&channel->refs)) { - destroy_channel(exec_ctx, channel); - } -} - void grpc_channel_destroy(grpc_channel *channel) { grpc_transport_op op; grpc_channel_element *elem; diff --git a/src/core/surface/channel.h b/src/core/surface/channel.h index 7dea609ebc..3d2ff23542 100644 --- a/src/core/surface/channel.h +++ b/src/core/surface/channel.h @@ -53,7 +53,7 @@ grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int status_code); gpr_uint32 grpc_channel_get_max_message_length(grpc_channel *channel); -#ifdef GRPC_CHANNEL_REF_COUNT_DEBUG +#ifdef GRPC_STREAM_REFCOUNT_DEBUG void grpc_channel_internal_ref(grpc_channel *channel, const char *reason); void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, grpc_channel *channel, const char *reason); diff --git a/src/core/surface/channel_connectivity.c b/src/core/surface/channel_connectivity.c index df2774b527..10f5c4da4d 100644 --- a/src/core/surface/channel_connectivity.c +++ b/src/core/surface/channel_connectivity.c @@ -83,7 +83,6 @@ typedef struct { gpr_mu mu; callback_phase phase; int success; - int removed; grpc_closure on_complete; grpc_timer alarm; grpc_connectivity_state state; @@ -135,30 +134,15 @@ static void finished_completion(grpc_exec_ctx *exec_ctx, void *pw, static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w, int due_to_completion) { int delete = 0; - grpc_channel_element *client_channel_elem = NULL; - gpr_mu_lock(&w->mu); - if (w->removed == 0) { - w->removed = 1; - client_channel_elem = grpc_channel_stack_last_element( - grpc_channel_get_channel_stack(w->channel)); - if (client_channel_elem->filter == &grpc_client_channel_filter) { - grpc_client_channel_del_interested_party(exec_ctx, client_channel_elem, - grpc_cq_pollset(w->cq)); - } else { - grpc_client_uchannel_del_interested_party(exec_ctx, client_channel_elem, - grpc_cq_pollset(w->cq)); - } - } - gpr_mu_unlock(&w->mu); if (due_to_completion) { - gpr_mu_lock(&w->mu); - w->success = 1; - gpr_mu_unlock(&w->mu); grpc_timer_cancel(exec_ctx, &w->alarm); } gpr_mu_lock(&w->mu); + if (due_to_completion) { + w->success = 1; + } switch (w->phase) { case WAITING: w->phase = CALLING_BACK; @@ -200,19 +184,18 @@ void grpc_channel_watch_connectivity_state( GRPC_API_TRACE( "grpc_channel_watch_connectivity_state(" "channel=%p, last_observed_state=%d, " - "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " "cq=%p, tag=%p)", - 7, (channel, (int)last_observed_state, (long)deadline.tv_sec, - deadline.tv_nsec, (int)deadline.clock_type, cq, tag)); + 7, (channel, (int)last_observed_state, (long long)deadline.tv_sec, + (int)deadline.tv_nsec, (int)deadline.clock_type, cq, tag)); - grpc_cq_begin_op(cq); + grpc_cq_begin_op(cq, tag); gpr_mu_init(&w->mu); grpc_closure_init(&w->on_complete, watch_complete, w); w->phase = WAITING; w->state = last_observed_state; w->success = 0; - w->removed = 0; w->cq = cq; w->tag = tag; w->channel = channel; @@ -223,16 +206,14 @@ void grpc_channel_watch_connectivity_state( if (client_channel_elem->filter == &grpc_client_channel_filter) { GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity"); - grpc_client_channel_add_interested_party(&exec_ctx, client_channel_elem, - grpc_cq_pollset(cq)); grpc_client_channel_watch_connectivity_state(&exec_ctx, client_channel_elem, - &w->state, &w->on_complete); + grpc_cq_pollset(cq), &w->state, + &w->on_complete); } else if (client_channel_elem->filter == &grpc_client_uchannel_filter) { GRPC_CHANNEL_INTERNAL_REF(channel, "watch_uchannel_connectivity"); - grpc_client_uchannel_add_interested_party(&exec_ctx, client_channel_elem, - grpc_cq_pollset(cq)); grpc_client_uchannel_watch_connectivity_state( - &exec_ctx, client_channel_elem, &w->state, &w->on_complete); + &exec_ctx, client_channel_elem, grpc_cq_pollset(cq), &w->state, + &w->on_complete); } grpc_exec_ctx_finish(&exec_ctx); diff --git a/src/core/surface/channel_create.c b/src/core/surface/channel_create.c index 6d40780497..97ec23408f 100644 --- a/src/core/surface/channel_create.c +++ b/src/core/surface/channel_create.c @@ -99,8 +99,8 @@ static void connected(grpc_exec_ctx *exec_ctx, void *arg, int success) { grpc_endpoint_write(exec_ctx, tcp, &c->initial_string_buffer, &c->initial_string_sent); } - c->result->transport = grpc_create_chttp2_transport( - exec_ctx, c->args.channel_args, tcp, 1); + c->result->transport = + grpc_create_chttp2_transport(exec_ctx, c->args.channel_args, tcp, 1); grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL, 0); GPR_ASSERT(c->result->transport); @@ -171,7 +171,6 @@ static grpc_subchannel *subchannel_factory_create_subchannel( c->base.vtable = &connector_vtable; gpr_ref_init(&c->refs, 1); args->args = final_args; - args->master = f->master; s = grpc_subchannel_create(&c->base, args); grpc_connector_unref(exec_ctx, &c->base); grpc_channel_args_destroy(final_args); @@ -218,6 +217,9 @@ grpc_channel *grpc_insecure_channel_create(const char *target, GRPC_CHANNEL_INTERNAL_REF(f->master, "subchannel_factory"); resolver = grpc_resolver_create(target, &f->base); if (!resolver) { + GRPC_CHANNEL_INTERNAL_UNREF(&exec_ctx, f->master, "subchannel_factory"); + grpc_subchannel_factory_unref(&exec_ctx, &f->base); + grpc_exec_ctx_finish(&exec_ctx); return NULL; } diff --git a/src/core/client_config/subchannel_factory_decorators/add_channel_arg.c b/src/core/surface/channel_ping.c index 585e465fa4..b4ce282787 100644 --- a/src/core/client_config/subchannel_factory_decorators/add_channel_arg.c +++ b/src/core/surface/channel_ping.c @@ -31,13 +31,49 @@ * */ -#include "src/core/client_config/subchannel_factory_decorators/add_channel_arg.h" -#include "src/core/client_config/subchannel_factory_decorators/merge_channel_args.h" - -grpc_subchannel_factory *grpc_subchannel_factory_add_channel_arg( - grpc_subchannel_factory *input, const grpc_arg *arg) { - grpc_channel_args args; - args.num_args = 1; - args.args = (grpc_arg *)arg; - return grpc_subchannel_factory_merge_channel_args(input, &args); +#include "src/core/surface/channel.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +#include "src/core/surface/api_trace.h" +#include "src/core/surface/completion_queue.h" + +typedef struct { + grpc_closure closure; + void *tag; + grpc_completion_queue *cq; + grpc_cq_completion completion_storage; +} ping_result; + +static void ping_destroy(grpc_exec_ctx *exec_ctx, void *arg, + grpc_cq_completion *storage) { + gpr_free(arg); +} + +static void ping_done(grpc_exec_ctx *exec_ctx, void *arg, int success) { + ping_result *pr = arg; + grpc_cq_end_op(exec_ctx, pr->cq, pr->tag, success, ping_destroy, pr, + &pr->completion_storage); +} + +void grpc_channel_ping(grpc_channel *channel, grpc_completion_queue *cq, + void *tag, void *reserved) { + grpc_transport_op op; + ping_result *pr = gpr_malloc(sizeof(*pr)); + grpc_channel_element *top_elem = + grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GPR_ASSERT(reserved == NULL); + memset(&op, 0, sizeof(op)); + pr->tag = tag; + pr->cq = cq; + grpc_closure_init(&pr->closure, ping_done, pr); + op.send_ping = &pr->closure; + op.bind_pollset = grpc_cq_pollset(cq); + grpc_cq_begin_op(cq, tag); + top_elem->filter->start_transport_op(&exec_ctx, top_elem, &op); + grpc_exec_ctx_finish(&exec_ctx); } diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c index d56e5cbe84..848a33adc3 100644 --- a/src/core/surface/completion_queue.c +++ b/src/core/surface/completion_queue.c @@ -73,6 +73,12 @@ struct grpc_completion_queue { plucker pluckers[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS]; grpc_closure pollset_shutdown_done; +#ifndef NDEBUG + void **outstanding_tags; + size_t outstanding_tag_count; + size_t outstanding_tag_capacity; +#endif + grpc_completion_queue *next_free; }; @@ -89,6 +95,9 @@ void grpc_cq_global_shutdown(void) { while (g_freelist) { grpc_completion_queue *next = g_freelist->next_free; grpc_pollset_destroy(&g_freelist->pollset); +#ifndef NDEBUG + gpr_free(g_freelist->outstanding_tags); +#endif gpr_free(g_freelist); g_freelist = next; } @@ -117,6 +126,10 @@ grpc_completion_queue *grpc_completion_queue_create(void *reserved) { cc = gpr_malloc(sizeof(grpc_completion_queue)); grpc_pollset_init(&cc->pollset); +#ifndef NDEBUG + cc->outstanding_tags = NULL; + cc->outstanding_tag_capacity = 0; +#endif } else { cc = g_freelist; g_freelist = g_freelist->next_free; @@ -134,6 +147,9 @@ grpc_completion_queue *grpc_completion_queue_create(void *reserved) { cc->shutdown_called = 0; cc->is_server_cq = 0; cc->num_pluckers = 0; +#ifndef NDEBUG + cc->outstanding_tag_count = 0; +#endif grpc_closure_init(&cc->pollset_shutdown_done, on_pollset_shutdown_done, cc); GPR_TIMER_END("grpc_completion_queue_create", 0); @@ -176,10 +192,17 @@ void grpc_cq_internal_unref(grpc_completion_queue *cc) { } } -void grpc_cq_begin_op(grpc_completion_queue *cc) { +void grpc_cq_begin_op(grpc_completion_queue *cc, void *tag) { #ifndef NDEBUG gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); GPR_ASSERT(!cc->shutdown_called); + if (cc->outstanding_tag_count == cc->outstanding_tag_capacity) { + cc->outstanding_tag_capacity = GPR_MAX(4, 2 * cc->outstanding_tag_capacity); + cc->outstanding_tags = + gpr_realloc(cc->outstanding_tags, sizeof(*cc->outstanding_tags) * + cc->outstanding_tag_capacity); + } + cc->outstanding_tags[cc->outstanding_tag_count++] = tag; gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); #endif gpr_ref(&cc->pending_events); @@ -196,6 +219,9 @@ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, int shutdown; int i; grpc_pollset_worker *pluck_worker; +#ifndef NDEBUG + int found = 0; +#endif GPR_TIMER_BEGIN("grpc_cq_end_op", 0); @@ -206,6 +232,18 @@ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, ((gpr_uintptr)&cc->completed_head) | ((gpr_uintptr)(success != 0)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); +#ifndef NDEBUG + for (i = 0; i < (int)cc->outstanding_tag_count; i++) { + if (cc->outstanding_tags[i] == tag) { + cc->outstanding_tag_count--; + GPR_SWAP(void *, cc->outstanding_tags[i], + cc->outstanding_tags[cc->outstanding_tag_count]); + found = 1; + break; + } + } + GPR_ASSERT(found); +#endif shutdown = gpr_unref(&cc->pending_events); if (!shutdown) { cc->completed_tail->next = @@ -247,10 +285,10 @@ grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, GRPC_API_TRACE( "grpc_completion_queue_next(" "cc=%p, " - "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " "reserved=%p)", - 5, (cc, (long)deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, - reserved)); + 5, (cc, (long long)deadline.tv_sec, (int)deadline.tv_nsec, + (int)deadline.clock_type, reserved)); GPR_ASSERT(!reserved); deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); @@ -335,9 +373,9 @@ grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, GRPC_API_TRACE( "grpc_completion_queue_pluck(" "cc=%p, tag=%p, " - "deadline=gpr_timespec { tv_sec: %ld, tv_nsec: %d, clock_type: %d }, " + "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " "reserved=%p)", - 6, (cc, tag, (long)deadline.tv_sec, deadline.tv_nsec, + 6, (cc, tag, (long long)deadline.tv_sec, (int)deadline.tv_nsec, (int)deadline.clock_type, reserved)); GPR_ASSERT(!reserved); diff --git a/src/core/surface/completion_queue.h b/src/core/surface/completion_queue.h index a40bb048ac..1e40c48bea 100644 --- a/src/core/surface/completion_queue.h +++ b/src/core/surface/completion_queue.h @@ -68,10 +68,12 @@ void grpc_cq_internal_unref(grpc_completion_queue *cc); #endif /* Flag that an operation is beginning: the completion channel will not finish - shutdown until a corrensponding grpc_cq_end_* call is made */ -void grpc_cq_begin_op(grpc_completion_queue *cc); + shutdown until a corrensponding grpc_cq_end_* call is made. + \a tag is currently used only in debug builds. */ +void grpc_cq_begin_op(grpc_completion_queue *cc, void *tag); -/* Queue a GRPC_OP_COMPLETED operation */ +/* Queue a GRPC_OP_COMPLETED operation; tag must correspond to the tag passed to + grpc_cq_begin_op */ void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, void *tag, int success, void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, diff --git a/src/core/surface/init.c b/src/core/surface/init.c index 04d68620f1..82027af651 100644 --- a/src/core/surface/init.c +++ b/src/core/surface/init.c @@ -58,6 +58,10 @@ #include "src/core/transport/chttp2_transport.h" #include "src/core/transport/connectivity_state.h" +#ifndef GRPC_DEFAULT_NAME_PREFIX +#define GRPC_DEFAULT_NAME_PREFIX "dns:///" +#endif + #define MAX_PLUGINS 128 static gpr_once g_basic_init = GPR_ONCE_INIT; @@ -97,7 +101,7 @@ void grpc_init(void) { grpc_lb_policy_registry_init(grpc_pick_first_lb_factory_create()); grpc_register_lb_policy(grpc_pick_first_lb_factory_create()); grpc_register_lb_policy(grpc_round_robin_lb_factory_create()); - grpc_resolver_registry_init("dns:///"); + grpc_resolver_registry_init(GRPC_DEFAULT_NAME_PREFIX); grpc_register_resolver_type(grpc_dns_resolver_factory_create()); grpc_register_resolver_type(grpc_ipv4_resolver_factory_create()); grpc_register_resolver_type(grpc_ipv6_resolver_factory_create()); @@ -143,6 +147,7 @@ void grpc_shutdown(void) { gpr_timers_global_destroy(); grpc_tracer_shutdown(); grpc_resolver_registry_shutdown(); + grpc_lb_policy_registry_shutdown(); for (i = 0; i < g_number_of_plugins; i++) { if (g_all_of_the_plugins[i].destroy != NULL) { g_all_of_the_plugins[i].destroy(); diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c index 0247116ebb..a60e9d20da 100644 --- a/src/core/surface/lame_client.c +++ b/src/core/surface/lame_client.c @@ -49,7 +49,6 @@ typedef struct { } call_data; typedef struct { - grpc_channel *master; grpc_status_code error_code; const char *error_message; } channel_data; @@ -84,8 +83,7 @@ static void lame_start_transport_stream_op(grpc_exec_ctx *exec_ctx, } static char *lame_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { - channel_data *chand = elem->channel_data; - return grpc_channel_get_target(chand->master); + return NULL; } static void lame_start_transport_op(grpc_exec_ctx *exec_ctx, @@ -103,8 +101,7 @@ static void lame_start_transport_op(grpc_exec_ctx *exec_ctx, } static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { -} + grpc_call_element_args *args) {} static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {} @@ -112,10 +109,8 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, static void init_channel_elem(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_channel_element_args *args) { - channel_data *chand = elem->channel_data; GPR_ASSERT(args->is_first); GPR_ASSERT(args->is_last); - chand->master = args->master; } static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c index 374b44b406..92bd53411d 100644 --- a/src/core/surface/secure_channel_create.c +++ b/src/core/surface/secure_channel_create.c @@ -88,7 +88,6 @@ static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) { static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, grpc_security_status status, - grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint) { connector *c = arg; grpc_closure *notify; @@ -97,13 +96,11 @@ static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, memset(c->result, 0, sizeof(*c->result)); gpr_mu_unlock(&c->mu); } else if (status != GRPC_SECURITY_OK) { - GPR_ASSERT(c->connecting_endpoint == wrapped_endpoint); gpr_log(GPR_ERROR, "Secure handshake failed with error %d.", status); memset(c->result, 0, sizeof(*c->result)); c->connecting_endpoint = NULL; gpr_mu_unlock(&c->mu); } else { - GPR_ASSERT(c->connecting_endpoint == wrapped_endpoint); c->connecting_endpoint = NULL; gpr_mu_unlock(&c->mu); c->result->transport = grpc_create_chttp2_transport( @@ -231,7 +228,6 @@ static grpc_subchannel *subchannel_factory_create_subchannel( gpr_mu_init(&c->mu); gpr_ref_init(&c->refs, 1); args->args = final_args; - args->master = f->master; s = grpc_subchannel_create(&c->base, args); grpc_connector_unref(exec_ctx, &c->base); grpc_channel_args_destroy(final_args); @@ -308,22 +304,22 @@ grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, f->master = channel; GRPC_CHANNEL_INTERNAL_REF(channel, "subchannel_factory"); resolver = grpc_resolver_create(target, &f->base); - if (!resolver) { - grpc_exec_ctx_finish(&exec_ctx); - return NULL; + if (resolver) { + grpc_client_channel_set_resolver( + &exec_ctx, grpc_channel_get_channel_stack(channel), resolver); + GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "create"); } - - grpc_client_channel_set_resolver( - &exec_ctx, grpc_channel_get_channel_stack(channel), resolver); - GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "create"); grpc_subchannel_factory_unref(&exec_ctx, &f->base); GRPC_SECURITY_CONNECTOR_UNREF(&security_connector->base, "channel_create"); - grpc_channel_args_destroy(args_copy); if (new_args_from_connector != NULL) { grpc_channel_args_destroy(new_args_from_connector); } + if (!resolver) { + GRPC_CHANNEL_INTERNAL_UNREF(&exec_ctx, channel, "subchannel_factory"); + channel = NULL; + } grpc_exec_ctx_finish(&exec_ctx); return channel; diff --git a/src/core/surface/server.c b/src/core/surface/server.c index cdbd542d9a..1e1cde3648 100644 --- a/src/core/surface/server.c +++ b/src/core/surface/server.c @@ -1007,7 +1007,7 @@ void grpc_server_shutdown_and_notify(grpc_server *server, /* lock, and gather up some stuff to do */ gpr_mu_lock(&server->mu_global); - grpc_cq_begin_op(cq); + grpc_cq_begin_op(cq, tag); if (server->shutdown_published) { grpc_cq_end_op(&exec_ctx, cq, tag, 1, done_published_shutdown, NULL, gpr_malloc(sizeof(grpc_cq_completion))); @@ -1176,7 +1176,7 @@ grpc_call_error grpc_server_request_call( error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; goto done; } - grpc_cq_begin_op(cq_for_notification); + grpc_cq_begin_op(cq_for_notification, tag); details->reserved = NULL; rc->type = BATCH_CALL; rc->server = server; @@ -1213,7 +1213,7 @@ grpc_call_error grpc_server_request_registered_call( error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; goto done; } - grpc_cq_begin_op(cq_for_notification); + grpc_cq_begin_op(cq_for_notification, tag); rc->type = REGISTERED_CALL; rc->server = server; rc->tag = tag; diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c index 990bc4aa23..5ce7c1955b 100644 --- a/src/core/surface/server_chttp2.c +++ b/src/core/surface/server_chttp2.c @@ -101,9 +101,7 @@ int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { } tcp = grpc_tcp_server_create(); - if (!tcp) { - goto error; - } + GPR_ASSERT(tcp); for (i = 0; i < resolved->naddrs; i++) { grpc_tcp_listener *listener; @@ -111,7 +109,7 @@ int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { tcp, (struct sockaddr *)&resolved->addrs[i].addr, resolved->addrs[i].len); port_temp = grpc_tcp_listener_get_port(listener); - if (port_temp >= 0) { + if (port_temp > 0) { if (port_num == -1) { port_num = port_temp; } else { diff --git a/src/core/surface/server_create.c b/src/core/surface/server_create.c index c7811a6d88..f30093e06b 100644 --- a/src/core/surface/server_create.c +++ b/src/core/surface/server_create.c @@ -32,14 +32,20 @@ */ #include <grpc/grpc.h> +#include "src/core/census/grpc_filter.h" +#include "src/core/channel/channel_args.h" +#include "src/core/channel/compress_filter.h" #include "src/core/surface/api_trace.h" #include "src/core/surface/completion_queue.h" #include "src/core/surface/server.h" -#include "src/core/channel/compress_filter.h" grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) { - const grpc_channel_filter *filters[] = {&grpc_compress_filter}; + const grpc_channel_filter *filters[3]; + size_t num_filters = 0; + filters[num_filters++] = &grpc_compress_filter; + if (grpc_channel_args_is_census_enabled(args)) { + filters[num_filters++] = &grpc_server_census_filter; + } GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved)); - return grpc_server_create_from_filters(filters, GPR_ARRAY_SIZE(filters), - args); + return grpc_server_create_from_filters(filters, num_filters, args); } diff --git a/src/core/transport/chttp2/frame_data.c b/src/core/transport/chttp2/frame_data.c index e07fbb2cc7..60a3ce23d5 100644 --- a/src/core/transport/chttp2/frame_data.c +++ b/src/core/transport/chttp2/frame_data.c @@ -53,7 +53,8 @@ void grpc_chttp2_data_parser_destroy(grpc_exec_ctx *exec_ctx, grpc_chttp2_data_parser *parser) { grpc_byte_stream *bs; if (parser->parsing_frame) { - grpc_chttp2_incoming_byte_stream_finished(exec_ctx, parser->parsing_frame); + grpc_chttp2_incoming_byte_stream_finished(exec_ctx, parser->parsing_frame, + 0, 1); } while ( (bs = grpc_chttp2_incoming_frame_queue_pop(&parser->incoming_frames))) { @@ -118,7 +119,7 @@ void grpc_chttp2_encode_data(gpr_uint32 id, gpr_slice_buffer *inbuf, hdr = gpr_slice_malloc(9); p = GPR_SLICE_START_PTR(hdr); - GPR_ASSERT(write_bytes < 16777316); + GPR_ASSERT(write_bytes < (1 << 24)); *p++ = (gpr_uint8)(write_bytes >> 16); *p++ = (gpr_uint8)(write_bytes >> 8); *p++ = (gpr_uint8)(write_bytes); @@ -218,7 +219,8 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( grpc_chttp2_incoming_byte_stream_push( exec_ctx, p->parsing_frame, gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg))); - grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame); + grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame, 1, + 1); p->parsing_frame = NULL; p->state = GRPC_CHTTP2_DATA_FH_0; return GRPC_CHTTP2_PARSE_OK; @@ -227,7 +229,8 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( exec_ctx, p->parsing_frame, gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(cur + p->frame_size - beg))); - grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame); + grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame, 1, + 1); p->parsing_frame = NULL; cur += p->frame_size; goto fh_0; /* loop */ diff --git a/src/core/transport/chttp2/frame_data.h b/src/core/transport/chttp2/frame_data.h index 472f9cebdb..27d4d0043b 100644 --- a/src/core/transport/chttp2/frame_data.h +++ b/src/core/transport/chttp2/frame_data.h @@ -94,9 +94,6 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( grpc_chttp2_transport_parsing *transport_parsing, grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); -/* create a slice with an empty data frame and is_last set */ -gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id); - void grpc_chttp2_encode_data(gpr_uint32 id, gpr_slice_buffer *inbuf, gpr_uint32 write_bytes, int is_eof, gpr_slice_buffer *outbuf); diff --git a/src/core/transport/chttp2/frame_ping.c b/src/core/transport/chttp2/frame_ping.c index 4d2c54269d..8e763278ff 100644 --- a/src/core/transport/chttp2/frame_ping.c +++ b/src/core/transport/chttp2/frame_ping.c @@ -76,7 +76,6 @@ grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( gpr_uint8 *const end = GPR_SLICE_END_PTR(slice); gpr_uint8 *cur = beg; grpc_chttp2_ping_parser *p = parser; - grpc_chttp2_outstanding_ping *ping; while (p->byte != 8 && cur != end) { p->opaque_8bytes[p->byte] = *cur; @@ -87,15 +86,7 @@ grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( if (p->byte == 8) { GPR_ASSERT(is_last); if (p->is_ack) { - for (ping = transport_parsing->pings.next; - ping != &transport_parsing->pings; ping = ping->next) { - if (0 == memcmp(p->opaque_8bytes, ping->id, 8)) { - grpc_exec_ctx_enqueue(exec_ctx, ping->on_recv, 1); - } - ping->next->prev = ping->prev; - ping->prev->next = ping->next; - gpr_free(ping); - } + grpc_chttp2_ack_ping(exec_ctx, transport_parsing, p->opaque_8bytes); } else { gpr_slice_buffer_add(&transport_parsing->qbuf, grpc_chttp2_ping_create(1, p->opaque_8bytes)); diff --git a/src/core/transport/chttp2/frame_settings.c b/src/core/transport/chttp2/frame_settings.c index d7c9f7ed69..383b6e7f93 100644 --- a/src/core/transport/chttp2/frame_settings.c +++ b/src/core/transport/chttp2/frame_settings.c @@ -44,6 +44,8 @@ #include "src/core/transport/chttp2/http2_errors.h" #include "src/core/transport/chttp2_transport.h" +#define MAX_MAX_HEADER_LIST_SIZE (1024 * 1024 * 1024) + /* HTTP/2 mandated initial connection settings */ const grpc_chttp2_setting_parameters grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = { @@ -60,8 +62,9 @@ const grpc_chttp2_setting_parameters GRPC_CHTTP2_FLOW_CONTROL_ERROR}, {"MAX_FRAME_SIZE", 16384, 16384, 16777215, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR}, - {"MAX_HEADER_LIST_SIZE", 0xffffffffu, 0, 0xffffffffu, - GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR}, + {"MAX_HEADER_LIST_SIZE", MAX_MAX_HEADER_LIST_SIZE, 0, + MAX_MAX_HEADER_LIST_SIZE, GRPC_CHTTP2_CLAMP_INVALID_VALUE, + GRPC_CHTTP2_PROTOCOL_ERROR}, }; static gpr_uint8 *fill_header(gpr_uint8 *out, gpr_uint32 length, diff --git a/src/core/transport/chttp2/hpack_encoder.c b/src/core/transport/chttp2/hpack_encoder.c index 28d5433d15..6c558bc1cb 100644 --- a/src/core/transport/chttp2/hpack_encoder.c +++ b/src/core/transport/chttp2/hpack_encoder.c @@ -365,10 +365,10 @@ static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem, GPR_ASSERT(GPR_SLICE_LENGTH(elem->key->slice) > 0); if (GPR_SLICE_START_PTR(elem->key->slice)[0] != ':') { /* regular header */ st->seen_regular_header = 1; - } else if (st->seen_regular_header != 0) { /* reserved header */ - gpr_log(GPR_ERROR, - "Reserved header (colon-prefixed) happening after regular ones."); - abort(); + } else { + GPR_ASSERT( + st->seen_regular_header == 0 && + "Reserved header (colon-prefixed) happening after regular ones."); } inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum, c->filter_elems); @@ -458,12 +458,6 @@ static void deadline_enc(grpc_chttp2_hpack_compressor *c, gpr_timespec deadline, GRPC_MDELEM_UNREF(mdelem); } -gpr_slice grpc_chttp2_data_frame_create_empty_close(gpr_uint32 id) { - gpr_slice slice = gpr_slice_malloc(9); - fill_header(GPR_SLICE_START_PTR(slice), GRPC_CHTTP2_FRAME_DATA, id, 0, 1); - return slice; -} - static gpr_uint32 elems_for_bytes(gpr_uint32 bytes) { return (bytes + 31) / 32; } diff --git a/src/core/transport/chttp2/hpack_parser.c b/src/core/transport/chttp2/hpack_parser.c index d38ff68754..fea0000896 100644 --- a/src/core/transport/chttp2/hpack_parser.c +++ b/src/core/transport/chttp2/hpack_parser.c @@ -728,6 +728,7 @@ static int finish_indexed_field(grpc_chttp2_hpack_parser *p, /* parse an indexed field with index < 127 */ static int parse_indexed_field(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { + p->dynamic_table_update_allowed = 0; p->index = (*cur) & 0x7f; return finish_indexed_field(p, cur + 1, end); } @@ -737,6 +738,7 @@ static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { finish_indexed_field}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; p->index = 0x7f; p->parsing.value = &p->index; @@ -748,6 +750,7 @@ static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p, static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GPR_ASSERT(md != NULL); /* handled in string parsing */ return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key), take_string(p, &p->value)), 1) && @@ -768,6 +771,7 @@ static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { parse_value_string_with_indexed_key, finish_lithdr_incidx}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; p->index = (*cur) & 0x3f; return parse_string_prefix(p, cur + 1, end); @@ -779,6 +783,7 @@ static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, static const grpc_chttp2_hpack_parser_state and_then[] = { parse_string_prefix, parse_value_string_with_indexed_key, finish_lithdr_incidx}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; p->index = 0x3f; p->parsing.value = &p->index; @@ -791,6 +796,7 @@ static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, static const grpc_chttp2_hpack_parser_state and_then[] = { parse_key_string, parse_string_prefix, parse_value_string_with_literal_key, finish_lithdr_incidx_v}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; return parse_string_prefix(p, cur + 1, end); } @@ -799,6 +805,7 @@ static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GPR_ASSERT(md != NULL); /* handled in string parsing */ return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key), take_string(p, &p->value)), 0) && @@ -819,6 +826,7 @@ static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { parse_value_string_with_indexed_key, finish_lithdr_notidx}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; p->index = (*cur) & 0xf; return parse_string_prefix(p, cur + 1, end); @@ -830,6 +838,7 @@ static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, static const grpc_chttp2_hpack_parser_state and_then[] = { parse_string_prefix, parse_value_string_with_indexed_key, finish_lithdr_notidx}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; p->index = 0xf; p->parsing.value = &p->index; @@ -842,6 +851,7 @@ static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, static const grpc_chttp2_hpack_parser_state and_then[] = { parse_key_string, parse_string_prefix, parse_value_string_with_literal_key, finish_lithdr_notidx_v}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; return parse_string_prefix(p, cur + 1, end); } @@ -850,6 +860,7 @@ static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GPR_ASSERT(md != NULL); /* handled in string parsing */ return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key), take_string(p, &p->value)), 0) && @@ -870,6 +881,7 @@ static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { parse_value_string_with_indexed_key, finish_lithdr_nvridx}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; p->index = (*cur) & 0xf; return parse_string_prefix(p, cur + 1, end); @@ -881,6 +893,7 @@ static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, static const grpc_chttp2_hpack_parser_state and_then[] = { parse_string_prefix, parse_value_string_with_indexed_key, finish_lithdr_nvridx}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; p->index = 0xf; p->parsing.value = &p->index; @@ -893,6 +906,7 @@ static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, static const grpc_chttp2_hpack_parser_state and_then[] = { parse_key_string, parse_string_prefix, parse_value_string_with_literal_key, finish_lithdr_nvridx_v}; + p->dynamic_table_update_allowed = 0; p->next_state = and_then; return parse_string_prefix(p, cur + 1, end); } @@ -908,6 +922,10 @@ static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p, /* parse a max table size change, max size < 15 */ static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { + if (p->dynamic_table_update_allowed == 0) { + return 0; + } + p->dynamic_table_update_allowed--; p->index = (*cur) & 0x1f; return finish_max_tbl_size(p, cur + 1, end); } @@ -917,6 +935,10 @@ static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, const gpr_uint8 *end) { static const grpc_chttp2_hpack_parser_state and_then[] = { finish_max_tbl_size}; + if (p->dynamic_table_update_allowed == 0) { + return 0; + } + p->dynamic_table_update_allowed--; p->next_state = and_then; p->index = 0x1f; p->parsing.value = &p->index; @@ -1044,7 +1066,7 @@ static int parse_value4(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, error: gpr_log(GPR_ERROR, "integer overflow in hpack integer decoding: have 0x%08x, " - "got byte 0x%02x", + "got byte 0x%02x on byte 5", *p->parsing.value, *cur); return parse_error(p, cur, end); } @@ -1069,7 +1091,8 @@ static int parse_value5up(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur, gpr_log(GPR_ERROR, "integer overflow in hpack integer decoding: have 0x%08x, " - "got byte 0x%02x sometime after byte 4"); + "got byte 0x%02x sometime after byte 5", + *p->parsing.value, *cur); return parse_error(p, cur, end); } @@ -1300,7 +1323,10 @@ static is_binary_header is_binary_literal_header(grpc_chttp2_hpack_parser *p) { static is_binary_header is_binary_indexed_header(grpc_chttp2_hpack_parser *p) { grpc_mdelem *elem = grpc_chttp2_hptbl_lookup(&p->table, p->index); - if (!elem) return ERROR_HEADER; + if (!elem) { + gpr_log(GPR_ERROR, "Invalid HPACK index received: %d", p->index); + return ERROR_HEADER; + } return grpc_is_binary_header( (const char *)GPR_SLICE_START_PTR(elem->key->slice), GPR_SLICE_LENGTH(elem->key->slice)) @@ -1338,15 +1364,7 @@ static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p, /* PUBLIC INTERFACE */ static void on_header_not_set(void *user_data, grpc_mdelem *md) { - char *keyhex = gpr_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); - char *valuehex = - gpr_dump_slice(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_ERROR, "on_header callback not set; key=%s value=%s", keyhex, - valuehex); - gpr_free(keyhex); - gpr_free(valuehex); - GRPC_MDELEM_UNREF(md); - abort(); + GPR_UNREACHABLE_CODE(return ); } void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) { @@ -1359,6 +1377,7 @@ void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) { p->value.str = NULL; p->value.capacity = 0; p->value.length = 0; + p->dynamic_table_update_allowed = 2; grpc_chttp2_hptbl_init(&p->table); } @@ -1400,20 +1419,25 @@ grpc_chttp2_parse_error grpc_chttp2_header_parser_parse( GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); return GRPC_CHTTP2_CONNECTION_ERROR; } - if (parser->is_boundary) { - stream_parsing - ->got_metadata_on_parse[stream_parsing->header_frames_received] = 1; - stream_parsing->header_frames_received++; - grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, - stream_parsing); - } - if (parser->is_eof) { - stream_parsing->received_close = 1; + /* need to check for null stream: this can occur if we receive an invalid + stream id on a header */ + if (stream_parsing != NULL) { + if (parser->is_boundary) { + stream_parsing + ->got_metadata_on_parse[stream_parsing->header_frames_received] = 1; + stream_parsing->header_frames_received++; + grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, + stream_parsing); + } + if (parser->is_eof) { + stream_parsing->received_close = 1; + } } parser->on_header = on_header_not_set; parser->on_header_user_data = NULL; parser->is_boundary = 0xde; parser->is_eof = 0xde; + parser->dynamic_table_update_allowed = 2; } GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); return GRPC_CHTTP2_PARSE_OK; diff --git a/src/core/transport/chttp2/hpack_parser.h b/src/core/transport/chttp2/hpack_parser.h index fb894b5735..bd36357124 100644 --- a/src/core/transport/chttp2/hpack_parser.h +++ b/src/core/transport/chttp2/hpack_parser.h @@ -85,6 +85,8 @@ struct grpc_chttp2_hpack_parser { gpr_uint8 binary; /* is the current string huffman encoded? */ gpr_uint8 huff; + /* is a dynamic table update allowed? */ + gpr_uint8 dynamic_table_update_allowed; /* set by higher layers, used by grpc_chttp2_header_parser_parse to signal it should append a metadata boundary at the end of frame */ gpr_uint8 is_boundary; diff --git a/src/core/transport/chttp2/incoming_metadata.c b/src/core/transport/chttp2/incoming_metadata.c index 956afc8e9d..315bc2faa1 100644 --- a/src/core/transport/chttp2/incoming_metadata.c +++ b/src/core/transport/chttp2/incoming_metadata.c @@ -56,16 +56,6 @@ void grpc_chttp2_incoming_metadata_buffer_destroy( gpr_free(buffer->elems); } -void grpc_chttp2_incoming_metadata_buffer_reset( - grpc_chttp2_incoming_metadata_buffer *buffer) { - size_t i; - GPR_ASSERT(!buffer->published); - for (i = 0; i < buffer->count; i++) { - GRPC_MDELEM_UNREF(buffer->elems[i].md); - } - buffer->count = 0; -} - void grpc_chttp2_incoming_metadata_buffer_add( grpc_chttp2_incoming_metadata_buffer *buffer, grpc_mdelem *elem) { GPR_ASSERT(!buffer->published); @@ -83,14 +73,6 @@ void grpc_chttp2_incoming_metadata_buffer_set_deadline( buffer->deadline = deadline; } -void grpc_chttp2_incoming_metadata_buffer_swap( - grpc_chttp2_incoming_metadata_buffer *a, - grpc_chttp2_incoming_metadata_buffer *b) { - GPR_ASSERT(!a->published); - GPR_ASSERT(!b->published); - GPR_SWAP(grpc_chttp2_incoming_metadata_buffer, *a, *b); -} - void grpc_chttp2_incoming_metadata_buffer_publish( grpc_chttp2_incoming_metadata_buffer *buffer, grpc_metadata_batch *batch) { GPR_ASSERT(!buffer->published); diff --git a/src/core/transport/chttp2/incoming_metadata.h b/src/core/transport/chttp2/incoming_metadata.h index 0e1dabe825..ea74cfc64b 100644 --- a/src/core/transport/chttp2/incoming_metadata.h +++ b/src/core/transport/chttp2/incoming_metadata.h @@ -49,8 +49,6 @@ void grpc_chttp2_incoming_metadata_buffer_init( grpc_chttp2_incoming_metadata_buffer *buffer); void grpc_chttp2_incoming_metadata_buffer_destroy( grpc_chttp2_incoming_metadata_buffer *buffer); -void grpc_chttp2_incoming_metadata_buffer_reset( - grpc_chttp2_incoming_metadata_buffer *buffer); void grpc_chttp2_incoming_metadata_buffer_publish( grpc_chttp2_incoming_metadata_buffer *buffer, grpc_metadata_batch *batch); diff --git a/src/core/transport/chttp2/internal.h b/src/core/transport/chttp2/internal.h index 3952f8a0e8..4ad900378b 100644 --- a/src/core/transport/chttp2/internal.h +++ b/src/core/transport/chttp2/internal.h @@ -65,6 +65,7 @@ typedef enum { GRPC_CHTTP2_LIST_WRITTEN, GRPC_CHTTP2_LIST_PARSING_SEEN, GRPC_CHTTP2_LIST_CLOSED_WAITING_FOR_PARSING, + GRPC_CHTTP2_LIST_CLOSED_WAITING_FOR_WRITING, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT, /** streams that are waiting to start because there are too many concurrent streams on the connection */ @@ -151,6 +152,7 @@ struct grpc_chttp2_incoming_byte_stream { grpc_byte_stream base; gpr_refcount refs; struct grpc_chttp2_incoming_byte_stream *next_message; + int failed; grpc_chttp2_transport *transport; grpc_chttp2_stream *stream; @@ -283,9 +285,6 @@ struct grpc_chttp2_transport_parsing { gpr_slice goaway_text; gpr_int64 outgoing_window; - - /** pings awaiting responses */ - grpc_chttp2_outstanding_ping pings; }; struct grpc_chttp2_transport { @@ -394,8 +393,6 @@ typedef struct { gpr_uint8 write_closed; /** is this stream reading half-closed (boolean) */ gpr_uint8 read_closed; - /** is this stream finished closing (and reportably closed) */ - gpr_uint8 finished_close; /** is this stream in the stream map? (boolean) */ gpr_uint8 in_stream_map; /** has this stream seen an error? if 1, then pending incoming frames @@ -512,9 +509,6 @@ void grpc_chttp2_publish_reads(grpc_exec_ctx *exec_ctx, void grpc_chttp2_list_add_writable_stream( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global); -void grpc_chttp2_list_add_first_writable_stream( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global); int grpc_chttp2_list_pop_writable_stream( grpc_chttp2_transport_global *transport_global, grpc_chttp2_transport_writing *transport_writing, @@ -592,6 +586,13 @@ int grpc_chttp2_list_pop_closed_waiting_for_parsing( grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global **stream_global); +void grpc_chttp2_list_add_closed_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global); +int grpc_chttp2_list_pop_closed_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global); + grpc_chttp2_stream_parsing *grpc_chttp2_parsing_lookup_stream( grpc_chttp2_transport_parsing *transport_parsing, gpr_uint32 id); grpc_chttp2_stream_parsing *grpc_chttp2_parsing_accept_stream( @@ -748,6 +749,11 @@ void grpc_chttp2_incoming_byte_stream_push(grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, gpr_slice slice); void grpc_chttp2_incoming_byte_stream_finished( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs); + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, int success, + int from_parsing_thread); + +void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport_parsing *parsing, + const gpr_uint8 *opaque_8bytes); #endif diff --git a/src/core/transport/chttp2/parsing.c b/src/core/transport/chttp2/parsing.c index eb2cb6b4c2..7604e7b681 100644 --- a/src/core/transport/chttp2/parsing.c +++ b/src/core/transport/chttp2/parsing.c @@ -604,7 +604,7 @@ static void on_initial_header(void *tp, grpc_mdelem *md) { cached_timeout)) { gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", grpc_mdstr_as_c_string(md->value)); - *cached_timeout = gpr_inf_future(GPR_CLOCK_REALTIME); + *cached_timeout = gpr_inf_future(GPR_TIMESPAN); } grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); } diff --git a/src/core/transport/chttp2/stream_lists.c b/src/core/transport/chttp2/stream_lists.c index a81ffcce79..49f951d08b 100644 --- a/src/core/transport/chttp2/stream_lists.c +++ b/src/core/transport/chttp2/stream_lists.c @@ -108,23 +108,6 @@ static void stream_list_maybe_remove(grpc_chttp2_transport *t, } } -static void stream_list_add_head(grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_chttp2_stream_list_id id) { - grpc_chttp2_stream *old_head; - GPR_ASSERT(!s->included[id]); - old_head = t->lists[id].head; - s->links[id].next = old_head; - s->links[id].prev = NULL; - if (old_head) { - old_head->links[id].prev = s; - } else { - t->lists[id].tail = s; - } - t->lists[id].head = s; - s->included[id] = 1; -} - static void stream_list_add_tail(grpc_chttp2_transport *t, grpc_chttp2_stream *s, grpc_chttp2_stream_list_id id) { @@ -161,15 +144,6 @@ void grpc_chttp2_list_add_writable_stream( STREAM_FROM_GLOBAL(stream_global), GRPC_CHTTP2_LIST_WRITABLE); } -void grpc_chttp2_list_add_first_writable_stream( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global) { - GPR_ASSERT(stream_global->id != 0); - stream_list_add_head(TRANSPORT_FROM_GLOBAL(transport_global), - STREAM_FROM_GLOBAL(stream_global), - GRPC_CHTTP2_LIST_WRITABLE); -} - int grpc_chttp2_list_pop_writable_stream( grpc_chttp2_transport_global *transport_global, grpc_chttp2_transport_writing *transport_writing, @@ -379,6 +353,26 @@ int grpc_chttp2_list_pop_closed_waiting_for_parsing( return r; } +void grpc_chttp2_list_add_closed_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global *stream_global) { + stream_list_add(TRANSPORT_FROM_GLOBAL(transport_global), + STREAM_FROM_GLOBAL(stream_global), + GRPC_CHTTP2_LIST_CLOSED_WAITING_FOR_WRITING); +} + +int grpc_chttp2_list_pop_closed_waiting_for_writing( + grpc_chttp2_transport_global *transport_global, + grpc_chttp2_stream_global **stream_global) { + grpc_chttp2_stream *stream; + int r = stream_list_pop(TRANSPORT_FROM_GLOBAL(transport_global), &stream, + GRPC_CHTTP2_LIST_CLOSED_WAITING_FOR_WRITING); + if (r != 0) { + *stream_global = &stream->global; + } + return r; +} + void grpc_chttp2_register_stream(grpc_chttp2_transport *t, grpc_chttp2_stream *s) { stream_list_add_tail(t, s, GRPC_CHTTP2_LIST_ALL_STREAMS); diff --git a/src/core/transport/chttp2/timeout_encoding.c b/src/core/transport/chttp2/timeout_encoding.c index 8a9b290ecb..7ec8b4e8bf 100644 --- a/src/core/transport/chttp2/timeout_encoding.c +++ b/src/core/transport/chttp2/timeout_encoding.c @@ -36,14 +36,15 @@ #include <stdio.h> #include <string.h> +#include <grpc/support/port_platform.h> #include "src/core/support/string.h" -static int round_up(int x, int divisor) { +static gpr_int64 round_up(gpr_int64 x, gpr_int64 divisor) { return (x / divisor + (x % divisor != 0)) * divisor; } /* round an integer up to the next value with three significant figures */ -static int round_up_to_three_sig_figs(int x) { +static gpr_int64 round_up_to_three_sig_figs(gpr_int64 x) { if (x < 1000) return x; if (x < 10000) return round_up(x, 10); if (x < 100000) return round_up(x, 100); @@ -57,13 +58,13 @@ static int round_up_to_three_sig_figs(int x) { /* encode our minimum viable timeout value */ static void enc_tiny(char *buffer) { memcpy(buffer, "1n", 3); } -static void enc_ext(char *buffer, long value, char ext) { - int n = gpr_ltoa(value, buffer); +static void enc_ext(char *buffer, gpr_int64 value, char ext) { + int n = gpr_int64toa(value, buffer); buffer[n] = ext; buffer[n + 1] = 0; } -static void enc_seconds(char *buffer, long sec) { +static void enc_seconds(char *buffer, gpr_int64 sec) { if (sec % 3600 == 0) { enc_ext(buffer, sec / 3600, 'H'); } else if (sec % 60 == 0) { @@ -73,7 +74,7 @@ static void enc_seconds(char *buffer, long sec) { } } -static void enc_nanos(char *buffer, int x) { +static void enc_nanos(char *buffer, gpr_int64 x) { x = round_up_to_three_sig_figs(x); if (x < 100000) { if (x % 1000 == 0) { @@ -97,7 +98,7 @@ static void enc_nanos(char *buffer, int x) { } } -static void enc_micros(char *buffer, int x) { +static void enc_micros(char *buffer, gpr_int64 x) { x = round_up_to_three_sig_figs(x); if (x < 100000) { if (x % 1000 == 0) { @@ -123,7 +124,7 @@ void grpc_chttp2_encode_timeout(gpr_timespec timeout, char *buffer) { enc_nanos(buffer, timeout.tv_nsec); } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) { enc_micros(buffer, - (int)(timeout.tv_sec * 1000000) + + (gpr_int64)(timeout.tv_sec * 1000000) + (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0))); } else { enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0)); diff --git a/src/core/transport/chttp2/varint.h b/src/core/transport/chttp2/varint.h index 4dfcc76773..5acb15d032 100644 --- a/src/core/transport/chttp2/varint.h +++ b/src/core/transport/chttp2/varint.h @@ -50,7 +50,8 @@ void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, /* maximum value that can be bitpacked with the opcode if the opcode has a prefix of length prefix_bits */ -#define GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) ((1 << (8 - (prefix_bits))) - 1) +#define GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) \ + ((gpr_uint32)((1 << (8 - (prefix_bits))) - 1)) /* length required to bitpack a value */ #define GRPC_CHTTP2_VARINT_LENGTH(n, prefix_bits) \ @@ -65,7 +66,8 @@ void grpc_chttp2_hpack_write_varint_tail(gpr_uint32 tail_value, if ((length) == 1u) { \ (tgt)[0] = (gpr_uint8)((prefix_or) | (n)); \ } else { \ - (tgt)[0] = (prefix_or) | GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits); \ + (tgt)[0] = \ + (prefix_or) | (gpr_uint8)GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits); \ grpc_chttp2_hpack_write_varint_tail( \ (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits), (tgt) + 1, (length)-1); \ } \ diff --git a/src/core/transport/chttp2/writing.c b/src/core/transport/chttp2/writing.c index 805d05222d..b5ca42d69c 100644 --- a/src/core/transport/chttp2/writing.c +++ b/src/core/transport/chttp2/writing.c @@ -332,17 +332,12 @@ void grpc_chttp2_cleanup_writing( while (grpc_chttp2_list_pop_written_stream( transport_global, transport_writing, &stream_global, &stream_writing)) { - if (stream_writing->sent_trailing_metadata) { - grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, - !transport_global->is_client, 1); - } if (stream_writing->sent_initial_metadata) { grpc_chttp2_complete_closure_step( exec_ctx, &stream_global->send_initial_metadata_finished, 1); } if (stream_writing->sent_message) { GPR_ASSERT(stream_writing->send_message == NULL); - GPR_ASSERT(stream_global->send_message_finished); grpc_chttp2_complete_closure_step( exec_ctx, &stream_global->send_message_finished, 1); stream_writing->sent_message = 0; @@ -351,6 +346,10 @@ void grpc_chttp2_cleanup_writing( grpc_chttp2_complete_closure_step( exec_ctx, &stream_global->send_trailing_metadata_finished, 1); } + if (stream_writing->sent_trailing_metadata) { + grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, + !transport_global->is_client, 1); + } GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2_writing"); } gpr_slice_buffer_reset_and_unref(&transport_writing->outbuf); diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index 3e88f69abf..c3847bb6c5 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -134,6 +134,9 @@ static void connectivity_state_set( static void check_read_ops(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global); +static void fail_pending_writes(grpc_exec_ctx *exec_ctx, + grpc_chttp2_stream_global *stream_global); + /* * CONSTRUCTION/DESTRUCTION/REFCOUNTING */ @@ -625,6 +628,7 @@ void grpc_chttp2_terminate_writing(grpc_exec_ctx *exec_ctx, void *transport_writing_ptr, int success) { grpc_chttp2_transport_writing *transport_writing = transport_writing_ptr; grpc_chttp2_transport *t = TRANSPORT_FROM_WRITING(transport_writing); + grpc_chttp2_stream_global *stream_global; GPR_TIMER_BEGIN("grpc_chttp2_terminate_writing", 0); @@ -638,6 +642,11 @@ void grpc_chttp2_terminate_writing(grpc_exec_ctx *exec_ctx, grpc_chttp2_cleanup_writing(exec_ctx, &t->global, &t->writing); + while (grpc_chttp2_list_pop_closed_waiting_for_writing(&t->global, &stream_global)) { + fail_pending_writes(exec_ctx, stream_global); + GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "finish_writes"); + } + /* leave the writing flag up on shutdown to prevent further writes in unlock() from starting */ t->writing_active = 0; @@ -901,6 +910,26 @@ static void send_ping_locked(grpc_chttp2_transport *t, grpc_closure *on_recv) { gpr_slice_buffer_add(&t->global.qbuf, grpc_chttp2_ping_create(0, p->id)); } +void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport_parsing *transport_parsing, + const gpr_uint8 *opaque_8bytes) { + grpc_chttp2_outstanding_ping *ping; + grpc_chttp2_transport *t = TRANSPORT_FROM_PARSING(transport_parsing); + grpc_chttp2_transport_global *transport_global = &t->global; + lock(t); + for (ping = transport_global->pings.next; ping != &transport_global->pings; + ping = ping->next) { + if (0 == memcmp(opaque_8bytes, ping->id, 8)) { + grpc_exec_ctx_enqueue(exec_ctx, ping->on_recv, 1); + ping->next->prev = ping->prev; + ping->prev->next = ping->next; + gpr_free(ping); + break; + } + } + unlock(exec_ctx, t); +} + static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, grpc_transport_op *op) { grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; @@ -910,7 +939,7 @@ static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, 1); - if (op->on_connectivity_state_change) { + if (op->on_connectivity_state_change != NULL) { grpc_connectivity_state_notify_on_state_change( exec_ctx, &t->channel_callback.state_tracker, op->connectivity_state, op->on_connectivity_state_change); @@ -1020,6 +1049,12 @@ static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, t->parsing.incoming_stream = NULL; grpc_chttp2_parsing_become_skip_parser(exec_ctx, &t->parsing); } + if (s->parsing.data_parser.parsing_frame != NULL) { + grpc_chttp2_incoming_byte_stream_finished( + exec_ctx, s->parsing.data_parser.parsing_frame, 0, 0); + s->parsing.data_parser.parsing_frame = NULL; + } + if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) { close_transport_locked(exec_ctx, t); } @@ -1087,6 +1122,16 @@ void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, } } +static void fail_pending_writes(grpc_exec_ctx *exec_ctx, + grpc_chttp2_stream_global *stream_global) { + grpc_chttp2_complete_closure_step( + exec_ctx, &stream_global->send_initial_metadata_finished, 0); + grpc_chttp2_complete_closure_step( + exec_ctx, &stream_global->send_trailing_metadata_finished, 0); + grpc_chttp2_complete_closure_step(exec_ctx, + &stream_global->send_message_finished, 0); +} + void grpc_chttp2_mark_stream_closed( grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, grpc_chttp2_stream_global *stream_global, int close_reads, @@ -1103,6 +1148,13 @@ void grpc_chttp2_mark_stream_closed( } if (close_writes && !stream_global->write_closed) { stream_global->write_closed = 1; + if (TRANSPORT_FROM_GLOBAL(transport_global)->writing_active) { + GRPC_CHTTP2_STREAM_REF(stream_global, "finish_writes"); + grpc_chttp2_list_add_closed_waiting_for_writing(transport_global, + stream_global); + } else { + fail_pending_writes(exec_ctx, stream_global); + } } if (stream_global->read_closed && stream_global->write_closed) { if (stream_global->id != 0 && @@ -1114,7 +1166,6 @@ void grpc_chttp2_mark_stream_closed( remove_stream(exec_ctx, TRANSPORT_FROM_GLOBAL(transport_global), stream_global->id); } - stream_global->finished_close = 1; GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2"); } } @@ -1328,7 +1379,6 @@ static void recv_data(grpc_exec_ctx *exec_ctx, void *tp, int success) { GPR_ASSERT(stream_global->write_closed); GPR_ASSERT(stream_global->read_closed); remove_stream(exec_ctx, t, stream_global->id); - stream_global->finished_close = 1; GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2"); } } @@ -1459,6 +1509,10 @@ static int incoming_byte_stream_next(grpc_exec_ctx *exec_ctx, *slice = gpr_slice_buffer_take_first(&bs->slices); unlock(exec_ctx, bs->transport); return 1; + } else if (bs->failed) { + grpc_exec_ctx_enqueue(exec_ctx, on_complete, 0); + unlock(exec_ctx, bs->transport); + return 0; } else { bs->on_next = on_complete; bs->next = slice; @@ -1493,7 +1547,29 @@ void grpc_chttp2_incoming_byte_stream_push(grpc_exec_ctx *exec_ctx, } void grpc_chttp2_incoming_byte_stream_finished( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs) { + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, int success, + int from_parsing_thread) { + if (!success) { + if (from_parsing_thread) { + gpr_mu_lock(&bs->transport->mu); + } + grpc_exec_ctx_enqueue(exec_ctx, bs->on_next, 0); + bs->on_next = NULL; + bs->failed = 1; + if (from_parsing_thread) { + gpr_mu_unlock(&bs->transport->mu); + } + } else { +#ifndef NDEBUG + if (from_parsing_thread) { + gpr_mu_lock(&bs->transport->mu); + } + GPR_ASSERT(bs->on_next == NULL); + if (from_parsing_thread) { + gpr_mu_unlock(&bs->transport->mu); + } +#endif + } incoming_byte_stream_unref(bs); } @@ -1514,6 +1590,7 @@ grpc_chttp2_incoming_byte_stream *grpc_chttp2_incoming_byte_stream_create( gpr_slice_buffer_init(&incoming_byte_stream->slices); incoming_byte_stream->on_next = NULL; incoming_byte_stream->is_tail = 1; + incoming_byte_stream->failed = 0; if (add_to_queue->head == NULL) { add_to_queue->head = incoming_byte_stream; } else { diff --git a/src/core/transport/connectivity_state.c b/src/core/transport/connectivity_state.c index 09b298c131..3c3fd4671d 100644 --- a/src/core/transport/connectivity_state.c +++ b/src/core/transport/connectivity_state.c @@ -54,8 +54,7 @@ const char *grpc_connectivity_state_name(grpc_connectivity_state state) { case GRPC_CHANNEL_FATAL_FAILURE: return "FATAL_FAILURE"; } - abort(); - return "UNKNOWN"; + GPR_UNREACHABLE_CODE(return "UNKNOWN"); } void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, @@ -88,7 +87,7 @@ void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx, grpc_connectivity_state grpc_connectivity_state_check( grpc_connectivity_state_tracker *tracker) { if (grpc_connectivity_state_trace) { - gpr_log(GPR_DEBUG, "CONWATCH: %s: get %s", tracker->name, + gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name, grpc_connectivity_state_name(tracker->current_state)); } return tracker->current_state; @@ -98,42 +97,47 @@ int grpc_connectivity_state_notify_on_state_change( grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, grpc_connectivity_state *current, grpc_closure *notify) { if (grpc_connectivity_state_trace) { - gpr_log(GPR_DEBUG, "CONWATCH: %s: from %s [cur=%s] notify=%p", - tracker->name, grpc_connectivity_state_name(*current), - grpc_connectivity_state_name(tracker->current_state), notify); + if (current == NULL) { + gpr_log(GPR_DEBUG, "CONWATCH: %p %s: unsubscribe notify=%p", tracker, + tracker->name, notify); + } else { + gpr_log(GPR_DEBUG, "CONWATCH: %p %s: from %s [cur=%s] notify=%p", tracker, + tracker->name, grpc_connectivity_state_name(*current), + grpc_connectivity_state_name(tracker->current_state), notify); + } } - if (tracker->current_state != *current) { - *current = tracker->current_state; - grpc_exec_ctx_enqueue(exec_ctx, notify, 1); + if (current == NULL) { + grpc_connectivity_state_watcher *w = tracker->watchers; + if (w != NULL && w->notify == notify) { + grpc_exec_ctx_enqueue(exec_ctx, notify, 0); + tracker->watchers = w->next; + gpr_free(w); + return 0; + } + while (w != NULL) { + grpc_connectivity_state_watcher *rm_candidate = w->next; + if (rm_candidate != NULL && rm_candidate->notify == notify) { + grpc_exec_ctx_enqueue(exec_ctx, notify, 0); + w->next = w->next->next; + gpr_free(rm_candidate); + return 0; + } + w = w->next; + } + return 0; } else { - grpc_connectivity_state_watcher *w = gpr_malloc(sizeof(*w)); - w->current = current; - w->notify = notify; - w->next = tracker->watchers; - tracker->watchers = w; - } - return tracker->current_state == GRPC_CHANNEL_IDLE; -} - -int grpc_connectivity_state_change_unsubscribe( - grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, - grpc_closure *subscribed_notify) { - grpc_connectivity_state_watcher *w = tracker->watchers; - if (w != NULL && w->notify == subscribed_notify) { - tracker->watchers = w->next; - gpr_free(w); - return 1; - } - while (w != NULL) { - grpc_connectivity_state_watcher *rm_candidate = w->next; - if (rm_candidate != NULL && rm_candidate->notify == subscribed_notify) { - w->next = w->next->next; - gpr_free(rm_candidate); - return 1; + if (tracker->current_state != *current) { + *current = tracker->current_state; + grpc_exec_ctx_enqueue(exec_ctx, notify, 1); + } else { + grpc_connectivity_state_watcher *w = gpr_malloc(sizeof(*w)); + w->current = current; + w->notify = notify; + w->next = tracker->watchers; + tracker->watchers = w; } - w = w->next; + return tracker->current_state == GRPC_CHANNEL_IDLE; } - return 0; } void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx, @@ -142,7 +146,7 @@ void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx, const char *reason) { grpc_connectivity_state_watcher *w; if (grpc_connectivity_state_trace) { - gpr_log(GPR_DEBUG, "SET: %s: %s --> %s [%s]", tracker->name, + gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s]", tracker, tracker->name, grpc_connectivity_state_name(tracker->current_state), grpc_connectivity_state_name(state), reason); } diff --git a/src/core/transport/connectivity_state.h b/src/core/transport/connectivity_state.h index 119b1c1554..a4eb6652e5 100644 --- a/src/core/transport/connectivity_state.h +++ b/src/core/transport/connectivity_state.h @@ -57,6 +57,8 @@ typedef struct { extern int grpc_connectivity_state_trace; +const char *grpc_connectivity_state_name(grpc_connectivity_state state); + void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, grpc_connectivity_state init_state, const char *name); @@ -73,16 +75,11 @@ void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx, grpc_connectivity_state grpc_connectivity_state_check( grpc_connectivity_state_tracker *tracker); -/** Return 1 if the channel should start connecting, 0 otherwise */ +/** Return 1 if the channel should start connecting, 0 otherwise. + If current==NULL cancel notify if it is already queued (success==0 in that + case) */ int grpc_connectivity_state_notify_on_state_change( grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, grpc_connectivity_state *current, grpc_closure *notify); -/** Remove \a subscribed_notify from the list of closures to be called on a - * state change if present, returning 1. Otherwise, nothing is done and return - * 0. */ -int grpc_connectivity_state_change_unsubscribe( - grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, - grpc_closure *subscribed_notify); - #endif /* GRPC_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H */ diff --git a/src/core/transport/metadata.c b/src/core/transport/metadata.c index d6fb255eb6..02b2402820 100644 --- a/src/core/transport/metadata.c +++ b/src/core/transport/metadata.c @@ -225,7 +225,8 @@ void grpc_mdctx_global_shutdown(void) { gc_mdtab(shard); /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ if (shard->count != 0) { - gpr_log(GPR_DEBUG, "WARNING: %d metadata elements were leaked", shard->count); + gpr_log(GPR_DEBUG, "WARNING: %d metadata elements were leaked", + shard->count); } gpr_free(shard->elems); } @@ -234,7 +235,8 @@ void grpc_mdctx_global_shutdown(void) { gpr_mu_destroy(&shard->mu); /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ if (shard->count != 0) { - gpr_log(GPR_DEBUG, "WARNING: %d metadata strings were leaked", shard->count); + gpr_log(GPR_DEBUG, "WARNING: %d metadata strings were leaked", + shard->count); } gpr_free(shard->strs); } @@ -701,7 +703,7 @@ static int conforms_to(grpc_mdstr *s, const gpr_uint8 *legal_bits) { int grpc_mdstr_is_legal_header(grpc_mdstr *s) { static const gpr_uint8 legal_header_bits[256 / 8] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0x00, 0x00, 0x00, 0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; return conforms_to(s, legal_header_bits); @@ -709,7 +711,7 @@ int grpc_mdstr_is_legal_header(grpc_mdstr *s) { int grpc_mdstr_is_legal_nonbin_header(grpc_mdstr *s) { static const gpr_uint8 legal_header_bits[256 / 8] = { - 0x00, 0x00, 0x00, 0x00, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; return conforms_to(s, legal_header_bits); diff --git a/src/core/transport/metadata_batch.c b/src/core/transport/metadata_batch.c index c5d39e0c9f..1266862f82 100644 --- a/src/core/transport/metadata_batch.c +++ b/src/core/transport/metadata_batch.c @@ -133,16 +133,6 @@ void grpc_metadata_batch_link_tail(grpc_metadata_batch *batch, link_tail(&batch->list, storage); } -void grpc_metadata_batch_merge(grpc_metadata_batch *target, - grpc_metadata_batch *to_add) { - grpc_linked_mdelem *l; - grpc_linked_mdelem *next; - for (l = to_add->list.head; l; l = next) { - next = l->next; - link_tail(&target->list, l); - } -} - void grpc_metadata_batch_move(grpc_metadata_batch *dst, grpc_metadata_batch *src) { *dst = *src; diff --git a/src/core/transport/metadata_batch.h b/src/core/transport/metadata_batch.h index fc3a46004f..1b0d1fda3e 100644 --- a/src/core/transport/metadata_batch.h +++ b/src/core/transport/metadata_batch.h @@ -63,8 +63,6 @@ typedef struct grpc_metadata_batch { void grpc_metadata_batch_init(grpc_metadata_batch *batch); void grpc_metadata_batch_destroy(grpc_metadata_batch *batch); -void grpc_metadata_batch_merge(grpc_metadata_batch *target, - grpc_metadata_batch *add); void grpc_metadata_batch_clear(grpc_metadata_batch *batch); int grpc_metadata_batch_is_empty(grpc_metadata_batch *batch); diff --git a/src/core/transport/static_metadata.c b/src/core/transport/static_metadata.c index e7aff325c2..6e42379eee 100644 --- a/src/core/transport/static_metadata.c +++ b/src/core/transport/static_metadata.c @@ -33,11 +33,11 @@ * WARNING: Auto-generated code. * * To make changes to this file, change - * tools/codegen/core/gen_static_metadata.py, + *tools/codegen/core/gen_static_metadata.py, * and then re-run it. * * See metadata.h for an explanation of the interface here, and metadata.c for - * an + *an * explanation of what's going on. */ @@ -54,103 +54,34 @@ gpr_uintptr grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = { const gpr_uint8 grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] = { - 11, 33, 10, 33, 12, 33, 12, 47, 13, 33, 14, 33, 15, 33, 16, 33, 17, 33, - 19, 33, 20, 33, 21, 33, 22, 33, 23, 33, 24, 33, 25, 33, 26, 33, 27, 33, - 28, 18, 28, 33, 29, 33, 30, 33, 34, 33, 35, 33, 36, 33, 37, 33, 40, 31, - 40, 32, 40, 46, 40, 51, 40, 52, 40, 53, 40, 54, 41, 31, 41, 46, 41, 51, - 44, 0, 44, 1, 44, 2, 48, 33, 55, 33, 56, 33, 57, 33, 58, 33, 59, 33, - 60, 33, 61, 33, 62, 33, 63, 33, 64, 38, 64, 66, 65, 76, 65, 77, 67, 33, - 68, 33, 69, 33, 70, 33, 71, 33, 72, 33, 73, 39, 73, 49, 73, 50, 74, 33, - 75, 33, 78, 3, 78, 4, 78, 5, 78, 6, 78, 7, 78, 8, 78, 9, 79, 33, - 80, 81, 82, 33, 83, 33, 84, 33, 85, 33, 86, 33}; + 11, 35, 10, 35, 12, 35, 12, 49, 13, 35, 14, 35, 15, 35, 16, 35, 17, 35, + 19, 35, 20, 35, 21, 35, 24, 35, 25, 35, 26, 35, 27, 35, 28, 35, 29, 35, + 30, 18, 30, 35, 31, 35, 32, 35, 36, 35, 37, 35, 38, 35, 39, 35, 42, 33, + 42, 34, 42, 48, 42, 53, 42, 54, 42, 55, 42, 56, 43, 33, 43, 48, 43, 53, + 46, 0, 46, 1, 46, 2, 50, 35, 57, 35, 58, 35, 59, 35, 60, 35, 61, 35, + 62, 35, 63, 35, 64, 35, 65, 35, 66, 40, 66, 68, 67, 78, 67, 79, 69, 35, + 70, 35, 71, 35, 72, 35, 73, 35, 74, 35, 75, 41, 75, 51, 75, 52, 76, 35, + 77, 35, 80, 3, 80, 4, 80, 5, 80, 6, 80, 7, 80, 8, 80, 9, 81, 35, + 82, 83, 84, 35, 85, 35, 86, 35, 87, 35, 88, 35}; const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = { - "0", - "1", - "2", - "200", - "204", - "206", - "304", - "400", - "404", - "500", - "accept", - "accept-charset", - "accept-encoding", - "accept-language", - "accept-ranges", - "access-control-allow-origin", - "age", - "allow", - "application/grpc", - ":authority", - "authorization", - "cache-control", - "content-disposition", - "content-encoding", - "content-language", - "content-length", - "content-location", - "content-range", - "content-type", - "cookie", - "date", - "deflate", - "deflate,gzip", - "", - "etag", - "expect", - "expires", - "from", - "GET", - "grpc", - "grpc-accept-encoding", - "grpc-encoding", - "grpc-internal-encoding-request", - "grpc-message", - "grpc-status", - "grpc-timeout", - "gzip", - "gzip, deflate", - "host", - "http", - "https", - "identity", - "identity,deflate", - "identity,deflate,gzip", - "identity,gzip", - "if-match", - "if-modified-since", - "if-none-match", - "if-range", - "if-unmodified-since", - "last-modified", - "link", - "location", - "max-forwards", - ":method", - ":path", - "POST", - "proxy-authenticate", - "proxy-authorization", - "range", - "referer", - "refresh", - "retry-after", - ":scheme", - "server", - "set-cookie", - "/", - "/index.html", - ":status", - "strict-transport-security", - "te", - "trailers", - "transfer-encoding", - "user-agent", - "vary", - "via", + "0", "1", "2", "200", "204", "206", "304", "400", "404", "500", "accept", + "accept-charset", "accept-encoding", "accept-language", "accept-ranges", + "access-control-allow-origin", "age", "allow", "application/grpc", + ":authority", "authorization", "cache-control", "census", "census-bin", + "content-disposition", "content-encoding", "content-language", + "content-length", "content-location", "content-range", "content-type", + "cookie", "date", "deflate", "deflate,gzip", "", "etag", "expect", + "expires", "from", "GET", "grpc", "grpc-accept-encoding", "grpc-encoding", + "grpc-internal-encoding-request", "grpc-message", "grpc-status", + "grpc-timeout", "gzip", "gzip, deflate", "host", "http", "https", + "identity", "identity,deflate", "identity,deflate,gzip", "identity,gzip", + "if-match", "if-modified-since", "if-none-match", "if-range", + "if-unmodified-since", "last-modified", "link", "location", "max-forwards", + ":method", ":path", "POST", "proxy-authenticate", "proxy-authorization", + "range", "referer", "refresh", "retry-after", ":scheme", "server", + "set-cookie", "/", "/index.html", ":status", "strict-transport-security", + "te", "trailers", "transfer-encoding", "user-agent", "vary", "via", "www-authenticate"}; const gpr_uint8 grpc_static_accept_encoding_metadata[8] = {0, 29, 26, 30, diff --git a/src/core/transport/static_metadata.h b/src/core/transport/static_metadata.h index e9055fb45c..0e630b1b03 100644 --- a/src/core/transport/static_metadata.h +++ b/src/core/transport/static_metadata.h @@ -46,7 +46,7 @@ #include "src/core/transport/metadata.h" -#define GRPC_STATIC_MDSTR_COUNT 87 +#define GRPC_STATIC_MDSTR_COUNT 89 extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT]; /* "0" */ #define GRPC_MDSTR_0 (&grpc_static_mdstr_table[0]) @@ -92,137 +92,141 @@ extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT]; #define GRPC_MDSTR_AUTHORIZATION (&grpc_static_mdstr_table[20]) /* "cache-control" */ #define GRPC_MDSTR_CACHE_CONTROL (&grpc_static_mdstr_table[21]) +/* "census" */ +#define GRPC_MDSTR_CENSUS (&grpc_static_mdstr_table[22]) +/* "census-bin" */ +#define GRPC_MDSTR_CENSUS_BIN (&grpc_static_mdstr_table[23]) /* "content-disposition" */ -#define GRPC_MDSTR_CONTENT_DISPOSITION (&grpc_static_mdstr_table[22]) +#define GRPC_MDSTR_CONTENT_DISPOSITION (&grpc_static_mdstr_table[24]) /* "content-encoding" */ -#define GRPC_MDSTR_CONTENT_ENCODING (&grpc_static_mdstr_table[23]) +#define GRPC_MDSTR_CONTENT_ENCODING (&grpc_static_mdstr_table[25]) /* "content-language" */ -#define GRPC_MDSTR_CONTENT_LANGUAGE (&grpc_static_mdstr_table[24]) +#define GRPC_MDSTR_CONTENT_LANGUAGE (&grpc_static_mdstr_table[26]) /* "content-length" */ -#define GRPC_MDSTR_CONTENT_LENGTH (&grpc_static_mdstr_table[25]) +#define GRPC_MDSTR_CONTENT_LENGTH (&grpc_static_mdstr_table[27]) /* "content-location" */ -#define GRPC_MDSTR_CONTENT_LOCATION (&grpc_static_mdstr_table[26]) +#define GRPC_MDSTR_CONTENT_LOCATION (&grpc_static_mdstr_table[28]) /* "content-range" */ -#define GRPC_MDSTR_CONTENT_RANGE (&grpc_static_mdstr_table[27]) +#define GRPC_MDSTR_CONTENT_RANGE (&grpc_static_mdstr_table[29]) /* "content-type" */ -#define GRPC_MDSTR_CONTENT_TYPE (&grpc_static_mdstr_table[28]) +#define GRPC_MDSTR_CONTENT_TYPE (&grpc_static_mdstr_table[30]) /* "cookie" */ -#define GRPC_MDSTR_COOKIE (&grpc_static_mdstr_table[29]) +#define GRPC_MDSTR_COOKIE (&grpc_static_mdstr_table[31]) /* "date" */ -#define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[30]) +#define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[32]) /* "deflate" */ -#define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[31]) +#define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[33]) /* "deflate,gzip" */ -#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (&grpc_static_mdstr_table[32]) +#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (&grpc_static_mdstr_table[34]) /* "" */ -#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[33]) +#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[35]) /* "etag" */ -#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[34]) +#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[36]) /* "expect" */ -#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[35]) +#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[37]) /* "expires" */ -#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[36]) +#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[38]) /* "from" */ -#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[37]) +#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[39]) /* "GET" */ -#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[38]) +#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[40]) /* "grpc" */ -#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[39]) +#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[41]) /* "grpc-accept-encoding" */ -#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[40]) +#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[42]) /* "grpc-encoding" */ -#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[41]) +#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[43]) /* "grpc-internal-encoding-request" */ -#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[42]) +#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[44]) /* "grpc-message" */ -#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[43]) +#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[45]) /* "grpc-status" */ -#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[44]) +#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[46]) /* "grpc-timeout" */ -#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[45]) +#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[47]) /* "gzip" */ -#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[46]) +#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[48]) /* "gzip, deflate" */ -#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[47]) +#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[49]) /* "host" */ -#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[48]) +#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[50]) /* "http" */ -#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[49]) +#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[51]) /* "https" */ -#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[50]) +#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[52]) /* "identity" */ -#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[51]) +#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[53]) /* "identity,deflate" */ -#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (&grpc_static_mdstr_table[52]) +#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (&grpc_static_mdstr_table[54]) /* "identity,deflate,gzip" */ #define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \ - (&grpc_static_mdstr_table[53]) + (&grpc_static_mdstr_table[55]) /* "identity,gzip" */ -#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (&grpc_static_mdstr_table[54]) +#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (&grpc_static_mdstr_table[56]) /* "if-match" */ -#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[55]) +#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[57]) /* "if-modified-since" */ -#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[56]) +#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[58]) /* "if-none-match" */ -#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[57]) +#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[59]) /* "if-range" */ -#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[58]) +#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[60]) /* "if-unmodified-since" */ -#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[59]) +#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[61]) /* "last-modified" */ -#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[60]) +#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[62]) /* "link" */ -#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[61]) +#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[63]) /* "location" */ -#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[62]) +#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[64]) /* "max-forwards" */ -#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[63]) +#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[65]) /* ":method" */ -#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[64]) +#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[66]) /* ":path" */ -#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[65]) +#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[67]) /* "POST" */ -#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[66]) +#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[68]) /* "proxy-authenticate" */ -#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[67]) +#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[69]) /* "proxy-authorization" */ -#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[68]) +#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[70]) /* "range" */ -#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[69]) +#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[71]) /* "referer" */ -#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[70]) +#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[72]) /* "refresh" */ -#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[71]) +#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[73]) /* "retry-after" */ -#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[72]) +#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[74]) /* ":scheme" */ -#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[73]) +#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[75]) /* "server" */ -#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[74]) +#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[76]) /* "set-cookie" */ -#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[75]) +#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[77]) /* "/" */ -#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[76]) +#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[78]) /* "/index.html" */ -#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[77]) +#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[79]) /* ":status" */ -#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[78]) +#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[80]) /* "strict-transport-security" */ -#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[79]) +#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[81]) /* "te" */ -#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[80]) +#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[82]) /* "trailers" */ -#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[81]) +#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[83]) /* "transfer-encoding" */ -#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[82]) +#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[84]) /* "user-agent" */ -#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[83]) +#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[85]) /* "vary" */ -#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[84]) +#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[86]) /* "via" */ -#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[85]) +#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[87]) /* "www-authenticate" */ -#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[86]) +#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[88]) #define GRPC_STATIC_MDELEM_COUNT 78 extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT]; diff --git a/src/core/transport/transport.c b/src/core/transport/transport.c index f2bebc62f3..2ab978be46 100644 --- a/src/core/transport/transport.c +++ b/src/core/transport/transport.c @@ -40,8 +40,8 @@ #ifdef GRPC_STREAM_REFCOUNT_DEBUG void grpc_stream_ref(grpc_stream_refcount *refcount, const char *reason) { gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); - gpr_log(GPR_DEBUG, "STREAM %p:%p REF %d->%d %s", refcount, - refcount->destroy.cb_arg, val, val + 1, reason); + gpr_log(GPR_DEBUG, "%s %p:%p REF %d->%d %s", refcount->object_type, + refcount, refcount->destroy.cb_arg, val, val + 1, reason); #else void grpc_stream_ref(grpc_stream_refcount *refcount) { #endif @@ -52,8 +52,8 @@ void grpc_stream_ref(grpc_stream_refcount *refcount) { void grpc_stream_unref(grpc_exec_ctx *exec_ctx, grpc_stream_refcount *refcount, const char *reason) { gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); - gpr_log(GPR_DEBUG, "STREAM %p:%p UNREF %d->%d %s", refcount, - refcount->destroy.cb_arg, val, val - 1, reason); + gpr_log(GPR_DEBUG, "%s %p:%p UNREF %d->%d %s", refcount->object_type, + refcount, refcount->destroy.cb_arg, val, val - 1, reason); #else void grpc_stream_unref(grpc_exec_ctx *exec_ctx, grpc_stream_refcount *refcount) { @@ -63,6 +63,19 @@ void grpc_stream_unref(grpc_exec_ctx *exec_ctx, } } +#ifdef GRPC_STREAM_REFCOUNT_DEBUG +void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, + grpc_iomgr_cb_func cb, void *cb_arg, + const char *object_type) { + refcount->object_type = object_type; +#else +void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, + grpc_iomgr_cb_func cb, void *cb_arg) { +#endif + gpr_ref_init(&refcount->refs, initial_refs); + grpc_closure_init(&refcount->destroy, cb, cb_arg); +} + size_t grpc_transport_stream_size(grpc_transport *transport) { return transport->vtable->sizeof_stream; } diff --git a/src/core/transport/transport.h b/src/core/transport/transport.h index f296ce8251..f94f0ae76e 100644 --- a/src/core/transport/transport.h +++ b/src/core/transport/transport.h @@ -50,19 +50,32 @@ typedef struct grpc_transport grpc_transport; for a stream. */ typedef struct grpc_stream grpc_stream; +/*#define GRPC_STREAM_REFCOUNT_DEBUG*/ + typedef struct grpc_stream_refcount { gpr_refcount refs; grpc_closure destroy; +#ifdef GRPC_STREAM_REFCOUNT_DEBUG + const char *object_type; +#endif } grpc_stream_refcount; -/*#define GRPC_STREAM_REFCOUNT_DEBUG*/ #ifdef GRPC_STREAM_REFCOUNT_DEBUG +void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, + grpc_iomgr_cb_func cb, void *cb_arg, + const char *object_type); void grpc_stream_ref(grpc_stream_refcount *refcount, const char *reason); void grpc_stream_unref(grpc_exec_ctx *exec_ctx, grpc_stream_refcount *refcount, const char *reason); +#define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \ + grpc_stream_ref_init(rc, ir, cb, cb_arg, objtype) #else +void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, + grpc_iomgr_cb_func cb, void *cb_arg); void grpc_stream_ref(grpc_stream_refcount *refcount); void grpc_stream_unref(grpc_exec_ctx *exec_ctx, grpc_stream_refcount *refcount); +#define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \ + grpc_stream_ref_init(rc, ir, cb, cb_arg) #endif /* Transport stream op: a set of operations to perform on a transport @@ -96,7 +109,7 @@ typedef struct grpc_transport_stream_op { typedef struct grpc_transport_op { /** called when processing of this op is done */ grpc_closure *on_consumed; - /** connectivity monitoring */ + /** connectivity monitoring - set connectivity_state to NULL to unsubscribe */ grpc_closure *on_connectivity_state_change; grpc_connectivity_state *connectivity_state; /** should the transport be disconnected */ diff --git a/src/core/transport/transport_op_string.c b/src/core/transport/transport_op_string.c index f3b6db29d6..98b51afc88 100644 --- a/src/core/transport/transport_op_string.c +++ b/src/core/transport/transport_op_string.c @@ -63,8 +63,8 @@ static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) { } if (gpr_time_cmp(md.deadline, gpr_inf_future(md.deadline.clock_type)) != 0) { char *tmp; - gpr_asprintf(&tmp, " deadline=%d.%09d", md.deadline.tv_sec, - md.deadline.tv_nsec); + gpr_asprintf(&tmp, " deadline=%lld.%09d", (long long)md.deadline.tv_sec, + (int)md.deadline.tv_nsec); gpr_strvec_add(b, tmp); } } diff --git a/src/core/tsi/transport_security.c b/src/core/tsi/transport_security.c index c39e584496..db219a50a6 100644 --- a/src/core/tsi/transport_security.c +++ b/src/core/tsi/transport_security.c @@ -145,7 +145,9 @@ void tsi_frame_protector_destroy(tsi_frame_protector *self) { tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, unsigned char *bytes, size_t *bytes_size) { - if (self == NULL) return TSI_INVALID_ARGUMENT; + if (self == NULL || bytes == NULL || bytes_size == NULL) { + return TSI_INVALID_ARGUMENT; + } if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size); } @@ -153,7 +155,9 @@ tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker *self, const unsigned char *bytes, size_t *bytes_size) { - if (self == NULL) return TSI_INVALID_ARGUMENT; + if (self == NULL || bytes == NULL || bytes_size == NULL) { + return TSI_INVALID_ARGUMENT; + } if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; return self->vtable->process_bytes_from_peer(self, bytes, bytes_size); } diff --git a/src/cpp/client/client_context.cc b/src/cpp/client/client_context.cc index 9bb358b233..2aa532808c 100644 --- a/src/cpp/client/client_context.cc +++ b/src/cpp/client/client_context.cc @@ -45,17 +45,31 @@ namespace grpc { +class DefaultGlobalClientCallbacks GRPC_FINAL + : public ClientContext::GlobalCallbacks { + public: + void DefaultConstructor(ClientContext* context) GRPC_OVERRIDE {} + void Destructor(ClientContext* context) GRPC_OVERRIDE {} +}; + +static DefaultGlobalClientCallbacks g_default_client_callbacks; +static ClientContext::GlobalCallbacks* g_client_callbacks = + &g_default_client_callbacks; + ClientContext::ClientContext() : initial_metadata_received_(false), call_(nullptr), call_canceled_(false), deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)), - propagate_from_call_(nullptr) {} + propagate_from_call_(nullptr) { + g_client_callbacks->DefaultConstructor(this); +} ClientContext::~ClientContext() { if (call_) { grpc_call_destroy(call_); } + g_client_callbacks->Destructor(this); } std::unique_ptr<ClientContext> ClientContext::FromServerContext( @@ -124,4 +138,11 @@ grpc::string ClientContext::peer() const { return peer; } +void ClientContext::SetGlobalCallbacks(GlobalCallbacks* client_callbacks) { + GPR_ASSERT(g_client_callbacks == &g_default_client_callbacks); + GPR_ASSERT(client_callbacks != NULL); + GPR_ASSERT(client_callbacks != &g_default_client_callbacks); + g_client_callbacks = client_callbacks; +} + } // namespace grpc diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc index 6409323262..96ae25b761 100644 --- a/src/cpp/client/secure_credentials.cc +++ b/src/cpp/client/secure_credentials.cc @@ -31,10 +31,10 @@ * */ -#include <grpc/support/log.h> #include <grpc++/channel.h> #include <grpc++/impl/grpc_library.h> #include <grpc++/support/channel_arguments.h> +#include <grpc/support/log.h> #include "src/cpp/client/create_channel_internal.h" #include "src/cpp/client/secure_credentials.h" #include "src/cpp/common/secure_auth_context.h" diff --git a/src/cpp/proto/proto_utils.cc b/src/cpp/proto/proto_utils.cc index b1330fde7f..898a1d4f58 100644 --- a/src/cpp/proto/proto_utils.cc +++ b/src/cpp/proto/proto_utils.cc @@ -33,6 +33,8 @@ #include <grpc++/impl/proto_utils.h> +#include <climits> + #include <grpc/grpc.h> #include <grpc/byte_buffer.h> #include <grpc/byte_buffer_reader.h> @@ -70,7 +72,9 @@ class GrpcBufferWriter GRPC_FINAL slice_ = gpr_slice_malloc(block_size_); } *data = GPR_SLICE_START_PTR(slice_); - byte_count_ += * size = GPR_SLICE_LENGTH(slice_); + // On win x64, int is only 32bit + GPR_ASSERT(GPR_SLICE_LENGTH(slice_) <= INT_MAX); + byte_count_ += * size = (int)GPR_SLICE_LENGTH(slice_); gpr_slice_buffer_add(slice_buffer_, slice_); return true; } @@ -124,7 +128,9 @@ class GrpcBufferReader GRPC_FINAL } gpr_slice_unref(slice_); *data = GPR_SLICE_START_PTR(slice_); - byte_count_ += * size = GPR_SLICE_LENGTH(slice_); + // On win x64, int is only 32bit + GPR_ASSERT(GPR_SLICE_LENGTH(slice_) <= INT_MAX); + byte_count_ += * size = (int)GPR_SLICE_LENGTH(slice_); return true; } diff --git a/src/cpp/util/time.cc b/src/cpp/util/time.cc index 6157a37745..9c38b34879 100644 --- a/src/cpp/util/time.cc +++ b/src/cpp/util/time.cc @@ -57,8 +57,8 @@ void Timepoint2Timespec(const system_clock::time_point& from, return; } nanoseconds nsecs = duration_cast<nanoseconds>(deadline - secs); - to->tv_sec = (time_t)secs.count(); - to->tv_nsec = (int)nsecs.count(); + to->tv_sec = (gpr_int64)secs.count(); + to->tv_nsec = (gpr_int32)nsecs.count(); to->clock_type = GPR_CLOCK_REALTIME; } @@ -73,8 +73,8 @@ void TimepointHR2Timespec(const high_resolution_clock::time_point& from, return; } nanoseconds nsecs = duration_cast<nanoseconds>(deadline - secs); - to->tv_sec = (time_t)secs.count(); - to->tv_nsec = (int)nsecs.count(); + to->tv_sec = (gpr_int64)secs.count(); + to->tv_nsec = (gpr_int32)nsecs.count(); to->clock_type = GPR_CLOCK_REALTIME; } diff --git a/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs b/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs index 1c14c5bb5b..f77e9c6573 100644 --- a/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs +++ b/src/csharp/Grpc.Auth/GoogleAuthInterceptors.cs @@ -57,9 +57,9 @@ namespace Grpc.Auth /// <returns>The interceptor.</returns> public static AsyncAuthInterceptor FromCredential(ITokenAccess credential) { - return new AsyncAuthInterceptor(async (authUri, metadata) => + return new AsyncAuthInterceptor(async (context, metadata) => { - var accessToken = await credential.GetAccessTokenForRequestAsync(authUri, CancellationToken.None).ConfigureAwait(false); + var accessToken = await credential.GetAccessTokenForRequestAsync(context.ServiceUrl, CancellationToken.None).ConfigureAwait(false); metadata.Add(CreateBearerTokenHeader(accessToken)); }); } @@ -72,7 +72,7 @@ namespace Grpc.Auth public static AsyncAuthInterceptor FromAccessToken(string accessToken) { Preconditions.CheckNotNull(accessToken); - return new AsyncAuthInterceptor(async (authUri, metadata) => + return new AsyncAuthInterceptor(async (context, metadata) => { metadata.Add(CreateBearerTokenHeader(accessToken)); }); diff --git a/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs b/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs new file mode 100644 index 0000000000..a3a613be74 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/CallOptionsTest.cs @@ -0,0 +1,88 @@ +#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.Collections.Generic; +using System.Threading; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class CallOptionsTest + { + [Test] + public void WithMethods() + { + var options = new CallOptions(); + + var metadata = new Metadata(); + Assert.AreSame(metadata, options.WithHeaders(metadata).Headers); + + var deadline = DateTime.UtcNow; + Assert.AreEqual(deadline, options.WithDeadline(deadline).Deadline.Value); + + var token = new CancellationTokenSource().Token; + Assert.AreEqual(token, options.WithCancellationToken(token).CancellationToken); + + // Change original instance is unchanged. + Assert.IsNull(options.Headers); + Assert.IsNull(options.Deadline); + Assert.AreEqual(CancellationToken.None, options.CancellationToken); + Assert.IsNull(options.WriteOptions); + Assert.IsNull(options.PropagationToken); + Assert.IsNull(options.Credentials); + } + + [Test] + public void Normalize() + { + Assert.AreSame(Metadata.Empty, new CallOptions().Normalize().Headers); + Assert.AreEqual(DateTime.MaxValue, new CallOptions().Normalize().Deadline.Value); + + var deadline = DateTime.UtcNow; + var propagationToken1 = new ContextPropagationToken(CallSafeHandle.NullInstance, deadline, CancellationToken.None, + new ContextPropagationOptions(propagateDeadline: true, propagateCancellation: false)); + Assert.AreEqual(deadline, new CallOptions(propagationToken: propagationToken1).Normalize().Deadline.Value); + Assert.Throws(typeof(ArgumentException), () => new CallOptions(deadline: deadline, propagationToken: propagationToken1).Normalize()); + + var token = new CancellationTokenSource().Token; + var propagationToken2 = new ContextPropagationToken(CallSafeHandle.NullInstance, deadline, token, + new ContextPropagationOptions(propagateDeadline: false, propagateCancellation: true)); + Assert.AreEqual(token, new CallOptions(propagationToken: propagationToken2).Normalize().CancellationToken); + Assert.Throws(typeof(ArgumentException), () => new CallOptions(cancellationToken: token, propagationToken: propagationToken2).Normalize()); + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs b/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs index 52be77c846..d2b5a436fd 100644 --- a/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs +++ b/src/csharp/Grpc.Core.Tests/ChannelOptionsTest.cs @@ -38,7 +38,7 @@ using Grpc.Core.Internal; using Grpc.Core.Utils; using NUnit.Framework; -namespace Grpc.Core.Internal.Tests +namespace Grpc.Core.Tests { public class ChannelOptionsTest { diff --git a/src/csharp/Grpc.Core.Tests/ChannelTest.cs b/src/csharp/Grpc.Core.Tests/ChannelTest.cs index f4ae9abefd..ed0ec14df5 100644 --- a/src/csharp/Grpc.Core.Tests/ChannelTest.cs +++ b/src/csharp/Grpc.Core.Tests/ChannelTest.cs @@ -48,6 +48,17 @@ namespace Grpc.Core.Tests } [Test] + public void Constructor_RejectsDuplicateOptions() + { + var options = new ChannelOption[] + { + new ChannelOption(ChannelOptions.PrimaryUserAgentString, "ABC"), + new ChannelOption(ChannelOptions.PrimaryUserAgentString, "XYZ") + }; + Assert.Throws(typeof(ArgumentException), () => new Channel("127.0.0.1", ChannelCredentials.Insecure, options)); + } + + [Test] public void State_IdleAfterCreation() { var channel = new Channel("localhost", ChannelCredentials.Insecure); diff --git a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs index 25a5a27c8e..77f6a63156 100644 --- a/src/csharp/Grpc.Core.Tests/ClientServerTest.cs +++ b/src/csharp/Grpc.Core.Tests/ClientServerTest.cs @@ -32,6 +32,7 @@ #endregion using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; @@ -144,6 +145,45 @@ namespace Grpc.Core.Tests var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall()); await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" }); Assert.AreEqual("ABC", await call.ResponseAsync); + + Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); + Assert.IsNotNull(call.GetTrailers()); + } + + [Test] + public async Task ServerStreamingCall() + { + helper.ServerStreamingHandler = new ServerStreamingServerMethod<string, string>(async (request, responseStream, context) => + { + await responseStream.WriteAllAsync(request.Split(new []{' '})); + context.ResponseTrailers.Add("xyz", ""); + }); + + var call = Calls.AsyncServerStreamingCall(helper.CreateServerStreamingCall(), "A B C"); + CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync()); + + Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); + Assert.IsNotNull("xyz", call.GetTrailers()[0].Key); + } + + [Test] + public async Task DuplexStreamingCall() + { + helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) => + { + while (await requestStream.MoveNext()) + { + await responseStream.WriteAsync(requestStream.Current); + } + context.ResponseTrailers.Add("xyz", "xyz-value"); + }); + + var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall()); + await call.RequestStream.WriteAllAsync(new string[] { "A", "B", "C" }); + CollectionAssert.AreEqual(new string[] { "A", "B", "C" }, await call.ResponseStream.ToListAsync()); + + Assert.AreEqual(StatusCode.OK, call.GetStatus().StatusCode); + Assert.IsNotNull("xyz-value", call.GetTrailers()[0].Value); } [Test] @@ -201,7 +241,7 @@ namespace Grpc.Core.Tests Assert.AreEqual(headers[1].Key, trailers[1].Key); CollectionAssert.AreEqual(headers[1].ValueBytes, trailers[1].ValueBytes); } - + [Test] public void UnknownMethodHandler() { @@ -219,27 +259,27 @@ namespace Grpc.Core.Tests } [Test] - public void UserAgentStringPresent() + public void ServerCallContext_PeerInfoPresent() { helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => { - return context.RequestHeaders.Where(entry => entry.Key == "user-agent").Single().Value; + return context.Peer; }); - string userAgent = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"); - Assert.IsTrue(userAgent.StartsWith("grpc-csharp/")); + string peer = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"); + Assert.IsTrue(peer.Contains(Host)); } [Test] - public void PeerInfoPresent() + public void ServerCallContext_HostAndMethodPresent() { helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => { - return context.Peer; + Assert.IsTrue(context.Host.Contains(Host)); + Assert.AreEqual("/tests.Test/Unary", context.Method); + return "PASS"; }); - - string peer = Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc"); - Assert.IsTrue(peer.Contains(Host)); + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "abc")); } [Test] diff --git a/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs index 2db3f286f7..90c510ec61 100644 --- a/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs +++ b/src/csharp/Grpc.Core.Tests/ContextPropagationTest.cs @@ -69,11 +69,19 @@ namespace Grpc.Core.Tests [Test] public async Task PropagateCancellation() { + var readyToCancelTcs = new TaskCompletionSource<object>(); + var successTcs = new TaskCompletionSource<string>(); + helper.UnaryHandler = new UnaryServerMethod<string, string>(async (request, context) => { - // check that we didn't obtain the default cancellation token. - Assert.IsTrue(context.CancellationToken.CanBeCanceled); - return "PASS"; + readyToCancelTcs.SetResult(null); // child call running, ready to parent call + + while (!context.CancellationToken.IsCancellationRequested) + { + await Task.Delay(10); + } + successTcs.SetResult("CHILD_CALL_CANCELLED"); + return ""; }); helper.ClientStreamingHandler = new ClientStreamingServerMethod<string, string>(async (requestStream, context) => @@ -82,13 +90,23 @@ namespace Grpc.Core.Tests Assert.IsNotNull(propagationToken.ParentCall); var callOptions = new CallOptions(propagationToken: propagationToken); - return await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + try + { + await Calls.AsyncUnaryCall(helper.CreateUnaryCall(callOptions), "xyz"); + } + catch(RpcException) + { + // Child call will get cancelled, eat the exception. + } + return ""; }); var cts = new CancellationTokenSource(); - var call = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token))); - await call.RequestStream.CompleteAsync(); - Assert.AreEqual("PASS", await call); + var parentCall = Calls.AsyncClientStreamingCall(helper.CreateClientStreamingCall(new CallOptions(cancellationToken: cts.Token))); + await readyToCancelTcs.Task; + cts.Cancel(); + Assert.Throws(typeof(RpcException), async () => await parentCall); + Assert.AreEqual("CHILD_CALL_CANCELLED", await successTcs.Task); } [Test] diff --git a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj index e5ffa31989..475c792347 100644 --- a/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj +++ b/src/csharp/Grpc.Core.Tests/Grpc.Core.Tests.csproj @@ -64,6 +64,8 @@ <Link>Version.cs</Link> </Compile> <Compile Include="CallCredentialsTest.cs" /> + <Compile Include="CallOptionsTest.cs" /> + <Compile Include="UserAgentStringTest.cs" /> <Compile Include="FakeCredentials.cs" /> <Compile Include="MarshallingErrorsTest.cs" /> <Compile Include="ChannelCredentialsTest.cs" /> @@ -89,6 +91,7 @@ <Compile Include="ContextPropagationTest.cs" /> <Compile Include="MetadataTest.cs" /> <Compile Include="PerformanceTest.cs" /> + <Compile Include="SanityTest.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> diff --git a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs index 9be5450d81..74f7f2497a 100644 --- a/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs +++ b/src/csharp/Grpc.Core.Tests/Internal/TimespecTest.cs @@ -82,17 +82,17 @@ namespace Grpc.Core.Internal.Tests public void ToDateTime() { Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), - new Timespec(IntPtr.Zero, 0).ToDateTime()); + new Timespec(0, 0).ToDateTime()); Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50), - new Timespec(new IntPtr(10), 5000).ToDateTime()); + new Timespec(10, 5000).ToDateTime()); Assert.AreEqual(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc), - new Timespec(new IntPtr(1437452508), 0).ToDateTime()); + new Timespec(1437452508, 0).ToDateTime()); // before epoch Assert.AreEqual(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10), - new Timespec(new IntPtr(-5), 1000).ToDateTime()); + new Timespec(-5, 1000).ToDateTime()); // infinity Assert.AreEqual(DateTime.MaxValue, Timespec.InfFuture.ToDateTime()); @@ -100,80 +100,62 @@ namespace Grpc.Core.Internal.Tests // nanos are rounded to ticks are rounded up Assert.AreEqual(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddTicks(1), - new Timespec(IntPtr.Zero, 99).ToDateTime()); + new Timespec(0, 99).ToDateTime()); // Illegal inputs Assert.Throws(typeof(InvalidOperationException), - () => new Timespec(new IntPtr(0), -2).ToDateTime()); + () => new Timespec(0, -2).ToDateTime()); Assert.Throws(typeof(InvalidOperationException), - () => new Timespec(new IntPtr(0), 1000 * 1000 * 1000).ToDateTime()); + () => new Timespec(0, 1000 * 1000 * 1000).ToDateTime()); Assert.Throws(typeof(InvalidOperationException), - () => new Timespec(new IntPtr(0), 0, GPRClockType.Monotonic).ToDateTime()); + () => new Timespec(0, 0, GPRClockType.Monotonic).ToDateTime()); } [Test] public void ToDateTime_ReturnsUtc() { - Assert.AreEqual(DateTimeKind.Utc, new Timespec(new IntPtr(1437452508), 0).ToDateTime().Kind); - Assert.AreNotEqual(DateTimeKind.Unspecified, new Timespec(new IntPtr(1437452508), 0).ToDateTime().Kind); + Assert.AreEqual(DateTimeKind.Utc, new Timespec(1437452508, 0).ToDateTime().Kind); + Assert.AreNotEqual(DateTimeKind.Unspecified, new Timespec(1437452508, 0).ToDateTime().Kind); } [Test] public void ToDateTime_Overflow() - { - // we can only get overflow in ticks arithmetic on 64-bit - if (IntPtr.Size == 8) - { - var timespec = new Timespec(new IntPtr(long.MaxValue - 100), 0); - Assert.AreNotEqual(Timespec.InfFuture, timespec); - Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); - - Assert.AreEqual(DateTime.MinValue, new Timespec(new IntPtr(long.MinValue + 100), 0).ToDateTime()); - } - else - { - Console.WriteLine("Test cannot be run on this platform, skipping the test."); - } + { + var timespec = new Timespec(long.MaxValue - 100, 0); + Assert.AreNotEqual(Timespec.InfFuture, timespec); + Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); + + Assert.AreEqual(DateTime.MinValue, new Timespec(long.MinValue + 100, 0).ToDateTime()); } [Test] public void ToDateTime_OutOfDateTimeRange() { - // we can only get out of range on 64-bit, on 32 bit the max - // timestamp is ~ Jan 19 2038, which is far within range of DateTime - // same case for min value. - if (IntPtr.Size == 8) - { - // DateTime range goes up to year 9999, 20000 years from now should - // be out of range. - long seconds = 20000L * 365L * 24L * 3600L; - - var timespec = new Timespec(new IntPtr(seconds), 0); - Assert.AreNotEqual(Timespec.InfFuture, timespec); - Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); - - Assert.AreEqual(DateTime.MinValue, new Timespec(new IntPtr(-seconds), 0).ToDateTime()); - } - else - { - Console.WriteLine("Test cannot be run on this platform, skipping the test"); - } + // DateTime range goes up to year 9999, 20000 years from now should + // be out of range. + long seconds = 20000L * 365L * 24L * 3600L; + + var timespec = new Timespec(seconds, 0); + Assert.AreNotEqual(Timespec.InfFuture, timespec); + Assert.AreEqual(DateTime.MaxValue, timespec.ToDateTime()); + + Assert.AreEqual(DateTime.MinValue, new Timespec(-seconds, 0).ToDateTime()); } [Test] public void FromDateTime() { - Assert.AreEqual(new Timespec(IntPtr.Zero, 0), + Assert.AreEqual(new Timespec(0, 0), Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc))); - Assert.AreEqual(new Timespec(new IntPtr(10), 5000), + Assert.AreEqual(new Timespec(10, 5000), Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 10, DateTimeKind.Utc).AddTicks(50))); - Assert.AreEqual(new Timespec(new IntPtr(1437452508), 0), + Assert.AreEqual(new Timespec(1437452508, 0), Timespec.FromDateTime(new DateTime(2015, 7, 21, 4, 21, 48, DateTimeKind.Utc))); // before epoch - Assert.AreEqual(new Timespec(new IntPtr(-5), 1000), + Assert.AreEqual(new Timespec(-5, 1000), Timespec.FromDateTime(new DateTime(1969, 12, 31, 23, 59, 55, DateTimeKind.Utc).AddTicks(10))); // infinity @@ -184,34 +166,19 @@ namespace Grpc.Core.Internal.Tests Assert.Throws(typeof(ArgumentException), () => Timespec.FromDateTime(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Unspecified))); } - - [Test] - public void FromDateTime_OutOfTimespecRange() - { - // we can only get overflow in Timespec on 32-bit - if (IntPtr.Size == 4) - { - Assert.AreEqual(Timespec.InfFuture, Timespec.FromDateTime(new DateTime(2040, 1, 1, 0, 0, 0, DateTimeKind.Utc))); - Assert.AreEqual(Timespec.InfPast, Timespec.FromDateTime(new DateTime(1800, 1, 1, 0, 0, 0, DateTimeKind.Utc))); - } - else - { - Console.WriteLine("Test cannot be run on this platform, skipping the test."); - } - } - // Test attribute commented out to prevent running as part of the default test suite. - // [Test] - // [Category("Performance")] + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public void NowBenchmark() { // approx Timespec.Now latency <33ns BenchmarkUtil.RunBenchmark(10000000, 1000000000, () => { var now = Timespec.Now; }); } - - // Test attribute commented out to prevent running as part of the default test suite. - // [Test] - // [Category("Performance")] + + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public void PreciseNowBenchmark() { // approx Timespec.PreciseNow latency <18ns (when compiled with GRPC_TIMERS_RDTSC) diff --git a/src/csharp/Grpc.Core.Tests/MetadataTest.cs b/src/csharp/Grpc.Core.Tests/MetadataTest.cs index ddeb7d0926..49e9de1174 100644 --- a/src/csharp/Grpc.Core.Tests/MetadataTest.cs +++ b/src/csharp/Grpc.Core.Tests/MetadataTest.cs @@ -127,5 +127,118 @@ namespace Grpc.Core.Tests Assert.Throws(typeof(InvalidOperationException), () => { var v = entry.Value; }); CollectionAssert.AreEqual(bytes, entry.ValueBytes); } + + [Test] + public void IndexOf() + { + var metadata = CreateMetadata(); + Assert.AreEqual(0, metadata.IndexOf(metadata[0])); + Assert.AreEqual(1, metadata.IndexOf(metadata[1])); + } + + [Test] + public void Insert() + { + var metadata = CreateMetadata(); + metadata.Insert(0, new Metadata.Entry("new-key", "new-value")); + Assert.AreEqual(3, metadata.Count); + Assert.AreEqual("new-key", metadata[0].Key); + Assert.AreEqual("abc", metadata[1].Key); + } + + [Test] + public void RemoveAt() + { + var metadata = CreateMetadata(); + metadata.RemoveAt(0); + Assert.AreEqual(1, metadata.Count); + Assert.AreEqual("xyz", metadata[0].Key); + } + + [Test] + public void Remove() + { + var metadata = CreateMetadata(); + metadata.Remove(metadata[0]); + Assert.AreEqual(1, metadata.Count); + Assert.AreEqual("xyz", metadata[0].Key); + } + + [Test] + public void Indexer_Set() + { + var metadata = CreateMetadata(); + var entry = new Metadata.Entry("new-key", "new-value"); + + metadata[1] = entry; + Assert.AreEqual(entry, metadata[1]); + } + + [Test] + public void Clear() + { + var metadata = CreateMetadata(); + metadata.Clear(); + Assert.AreEqual(0, metadata.Count); + } + + [Test] + public void Contains() + { + var metadata = CreateMetadata(); + Assert.IsTrue(metadata.Contains(metadata[0])); + Assert.IsFalse(metadata.Contains(new Metadata.Entry("new-key", "new-value"))); + } + + [Test] + public void CopyTo() + { + var metadata = CreateMetadata(); + var array = new Metadata.Entry[metadata.Count + 1]; + + metadata.CopyTo(array, 1); + Assert.AreEqual(default(Metadata.Entry), array[0]); + Assert.AreEqual(metadata[0], array[1]); + } + + [Test] + public void IEnumerableGetEnumerator() + { + var metadata = CreateMetadata(); + var enumerator = (metadata as System.Collections.IEnumerable).GetEnumerator(); + + int i = 0; + while (enumerator.MoveNext()) + { + Assert.AreEqual(metadata[i], enumerator.Current); + i++; + } + } + + [Test] + public void FreezeMakesReadOnly() + { + var entry = new Metadata.Entry("new-key", "new-value"); + var metadata = CreateMetadata().Freeze(); + + Assert.IsTrue(metadata.IsReadOnly); + Assert.Throws<InvalidOperationException>(() => metadata.Insert(0, entry)); + Assert.Throws<InvalidOperationException>(() => metadata.RemoveAt(0)); + Assert.Throws<InvalidOperationException>(() => metadata[0] = entry); + Assert.Throws<InvalidOperationException>(() => metadata.Add(entry)); + Assert.Throws<InvalidOperationException>(() => metadata.Add("new-key", "new-value")); + Assert.Throws<InvalidOperationException>(() => metadata.Add("new-key-bin", new byte[] { 0xaa })); + Assert.Throws<InvalidOperationException>(() => metadata.Clear()); + Assert.Throws<InvalidOperationException>(() => metadata.Remove(metadata[0])); + } + + private Metadata CreateMetadata() + { + return new Metadata + { + { "abc", "abc-value" }, + { "xyz", "xyz-value" }, + }; + } } } diff --git a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs index 567e04eddc..3047314345 100644 --- a/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs +++ b/src/csharp/Grpc.Core.Tests/MockServiceHelper.cs @@ -32,6 +32,7 @@ #endregion using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; @@ -52,6 +53,7 @@ namespace Grpc.Core.Tests readonly string host; readonly ServerServiceDefinition serviceDefinition; + readonly IEnumerable<ChannelOption> channelOptions; readonly Method<string, string> unaryMethod; readonly Method<string, string> clientStreamingMethod; @@ -66,9 +68,10 @@ namespace Grpc.Core.Tests Server server; Channel channel; - public MockServiceHelper(string host = null, Marshaller<string> marshaller = null) + public MockServiceHelper(string host = null, Marshaller<string> marshaller = null, IEnumerable<ChannelOption> channelOptions = null) { this.host = host ?? "localhost"; + this.channelOptions = channelOptions; marshaller = marshaller ?? Marshallers.StringMarshaller; unaryMethod = new Method<string, string>( @@ -154,7 +157,7 @@ namespace Grpc.Core.Tests { if (channel == null) { - channel = new Channel(Host, GetServer().Ports.Single().BoundPort, ChannelCredentials.Insecure); + channel = new Channel(Host, GetServer().Ports.Single().BoundPort, ChannelCredentials.Insecure, channelOptions); } return channel; } diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs index 073c502daf..af55cb0566 100644 --- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs +++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs @@ -59,6 +59,8 @@ namespace Grpc.Core.Tests [Test] public void CompletionQueueCreateDestroyBenchmark() { + GrpcEnvironment.AddRef(); // completion queue requires gRPC environment being initialized. + BenchmarkUtil.RunBenchmark( 10, 10, () => @@ -66,6 +68,8 @@ namespace Grpc.Core.Tests CompletionQueueSafeHandle cq = CompletionQueueSafeHandle.Create(); cq.Dispose(); }); + + GrpcEnvironment.Release(); } /// <summary> diff --git a/src/csharp/Grpc.Core.Tests/PerformanceTest.cs b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs index 5516cd3377..6815839992 100644 --- a/src/csharp/Grpc.Core.Tests/PerformanceTest.cs +++ b/src/csharp/Grpc.Core.Tests/PerformanceTest.cs @@ -67,10 +67,10 @@ namespace Grpc.Core.Tests channel.ShutdownAsync().Wait(); server.ShutdownAsync().Wait(); } - - // Test attribute commented out to prevent running as part of the default test suite. - //[Test] - //[Category("Performance")] + + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public void UnaryCallPerformance() { var profiler = new BasicProfiler(); diff --git a/src/csharp/Grpc.Core.Tests/SanityTest.cs b/src/csharp/Grpc.Core.Tests/SanityTest.cs new file mode 100644 index 0000000000..343ab1e85a --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/SanityTest.cs @@ -0,0 +1,125 @@ +#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.Collections.Generic; +using System.IO; +using System.Reflection; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class SanityTest + { + /// <summary> + /// Because we depend on a native library, sometimes when things go wrong, the + /// entire NUnit test process crashes. To be able to track down problems better, + /// the NUnit tests are run by run_tests.py script in a separate process per test class. + /// The list of tests to run is stored in src/csharp/tests.json. + /// This test checks that the tests.json file is up to date by discovering all the + /// existing NUnit tests in all test assemblies and comparing to contents of tests.json. + /// </summary> + [Test] + public void TestsJsonUpToDate() + { + var testClasses = DiscoverAllTestClasses(); + string testsJson = GetTestsJson(); + + // we don't have a JSON parser at hand, but check that the test class + // name is contained in the file instead. + foreach (var className in testClasses) { + Assert.IsTrue(testsJson.Contains(className), + string.Format("Test class \"{0}\" is missing in C# tests.json file", className)); + } + } + + /// <summary> + /// Gets list of all test classes obtained by inspecting all the test assemblies. + /// </summary> + private List<string> DiscoverAllTestClasses() + { + var assemblies = GetTestAssemblies(); + + var testClasses = new List<string>(); + foreach (var assembly in assemblies) + { + foreach (var t in assembly.GetTypes()) + { + foreach (var m in t.GetMethods()) + { + var attributes = m.GetCustomAttributes(typeof(NUnit.Framework.TestAttribute), true); + if (attributes.Length > 0) + { + testClasses.Add(t.FullName); + break; + } + + } + } + } + testClasses.Sort(); + return testClasses; + } + + private string GetTestsJson() + { + var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var testsJsonFile = Path.Combine(assemblyDir, "..", "..", "..", "tests.json"); + + return File.ReadAllText(testsJsonFile); + } + + private List<Assembly> GetTestAssemblies() + { + var result = new List<Assembly>(); + var executingAssembly = Assembly.GetExecutingAssembly(); + + result.Add(executingAssembly); + + var otherAssemblies = new[] { + "Grpc.Examples.Tests", + "Grpc.HealthCheck.Tests", + "Grpc.IntegrationTesting" + }; + foreach (var assemblyName in otherAssemblies) + { + var location = executingAssembly.Location.Replace("Grpc.Core.Tests", assemblyName); + result.Add(Assembly.LoadFrom(location)); + } + return result; + } + } +} diff --git a/src/csharp/Grpc.Core.Tests/ShutdownTest.cs b/src/csharp/Grpc.Core.Tests/ShutdownTest.cs index a2be7ddd5e..10d666d109 100644 --- a/src/csharp/Grpc.Core.Tests/ShutdownTest.cs +++ b/src/csharp/Grpc.Core.Tests/ShutdownTest.cs @@ -61,17 +61,20 @@ namespace Grpc.Core.Tests } [Test] - public async Task AbandonedCall() + public async Task AbandonedCall_ServerKillAsync() { + var readyToShutdown = new TaskCompletionSource<object>(); helper.DuplexStreamingHandler = new DuplexStreamingServerMethod<string, string>(async (requestStream, responseStream, context) => { + readyToShutdown.SetResult(null); await requestStream.ToListAsync(); }); - var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall(new CallOptions(deadline: DateTime.UtcNow.AddMilliseconds(1)))); + var call = Calls.AsyncDuplexStreamingCall(helper.CreateDuplexStreamingCall()); + await readyToShutdown.Task; // make sure handler is running - channel.ShutdownAsync().Wait(); - server.ShutdownAsync().Wait(); + await channel.ShutdownAsync(); // channel.ShutdownAsync() works even if there's a pending call. + await server.KillAsync(); // server.ShutdownAsync() would hang waiting for the call to finish. } } } diff --git a/src/csharp/Grpc.Core.Tests/UserAgentStringTest.cs b/src/csharp/Grpc.Core.Tests/UserAgentStringTest.cs new file mode 100644 index 0000000000..cc830086a6 --- /dev/null +++ b/src/csharp/Grpc.Core.Tests/UserAgentStringTest.cs @@ -0,0 +1,101 @@ +#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.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Internal; +using Grpc.Core.Profiling; +using Grpc.Core.Utils; +using NUnit.Framework; + +namespace Grpc.Core.Tests +{ + public class UserAgentStringTest + { + const string Host = "127.0.0.1"; + + MockServiceHelper helper; + Server server; + Channel channel; + + [TearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public void DefaultUserAgentString() + { + helper = new MockServiceHelper(Host); + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + + helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => + { + var userAgentString = context.RequestHeaders.First(m => (m.Key == "user-agent")).Value; + var parts = userAgentString.Split(new [] {' '}, 2); + Assert.AreEqual(string.Format("grpc-csharp/{0}", VersionInfo.CurrentVersion), parts[0]); + Assert.IsTrue(parts[1].StartsWith("grpc-c/")); + return Task.FromResult("PASS"); + }); + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "")); + } + + [Test] + public void ApplicationUserAgentString() + { + helper = new MockServiceHelper(Host, + channelOptions: new[] { new ChannelOption(ChannelOptions.PrimaryUserAgentString, "XYZ") }); + server = helper.GetServer(); + server.Start(); + channel = helper.GetChannel(); + + channel = helper.GetChannel(); + helper.UnaryHandler = new UnaryServerMethod<string, string>((request, context) => + { + var userAgentString = context.RequestHeaders.First(m => (m.Key == "user-agent")).Value; + var parts = userAgentString.Split(new[] { ' ' }, 3); + Assert.AreEqual("XYZ", parts[0]); + return Task.FromResult("PASS"); + }); + Assert.AreEqual("PASS", Calls.BlockingUnaryCall(helper.CreateUnaryCall(), "")); + } + } +} diff --git a/src/csharp/Grpc.Core/AsyncAuthInterceptor.cs b/src/csharp/Grpc.Core/AsyncAuthInterceptor.cs new file mode 100644 index 0000000000..5c9ab04812 --- /dev/null +++ b/src/csharp/Grpc.Core/AsyncAuthInterceptor.cs @@ -0,0 +1,84 @@ +#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.Collections.Generic; +using System.Threading.Tasks; + +using Grpc.Core.Internal; +using Grpc.Core.Utils; + +namespace Grpc.Core +{ + /// <summary> + /// Asynchronous authentication interceptor for <see cref="CallCredentials"/>. + /// </summary> + /// <param name="context">The interceptor context.</param> + /// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param> + /// <returns></returns> + public delegate Task AsyncAuthInterceptor(AuthInterceptorContext context, Metadata metadata); + + /// <summary> + /// Context for an RPC being intercepted by <see cref="AsyncAuthInterceptor"/>. + /// </summary> + public class AuthInterceptorContext + { + readonly string serviceUrl; + readonly string methodName; + + /// <summary> + /// Initializes a new instance of <c>AuthInterceptorContext</c>. + /// </summary> + public AuthInterceptorContext(string serviceUrl, string methodName) + { + this.serviceUrl = Preconditions.CheckNotNull(serviceUrl); + this.methodName = Preconditions.CheckNotNull(methodName); + } + + /// <summary> + /// The fully qualified service URL for the RPC being called. + /// </summary> + public string ServiceUrl + { + get { return serviceUrl; } + } + + /// <summary> + /// The method name of the RPC being called. + /// </summary> + public string MethodName + { + get { return methodName; } + } + } +} diff --git a/src/csharp/Grpc.Core/CallCredentials.cs b/src/csharp/Grpc.Core/CallCredentials.cs index 5ea179dfea..a71c8904fe 100644 --- a/src/csharp/Grpc.Core/CallCredentials.cs +++ b/src/csharp/Grpc.Core/CallCredentials.cs @@ -41,14 +41,6 @@ using Grpc.Core.Utils; namespace Grpc.Core { /// <summary> - /// Asynchronous authentication interceptor for <see cref="CallCredentials"/>. - /// </summary> - /// <param name="authUri">URL of a service to which current remote call needs to authenticate</param> - /// <param name="metadata">Metadata to populate with entries that will be added to outgoing call's headers.</param> - /// <returns></returns> - public delegate Task AsyncAuthInterceptor(string authUri, Metadata metadata); - - /// <summary> /// Client-side call credentials. Provide authorization with per-call granularity. /// </summary> public abstract class CallCredentials diff --git a/src/csharp/Grpc.Core/CallOptions.cs b/src/csharp/Grpc.Core/CallOptions.cs index c0f94c63c2..1fda80cb90 100644 --- a/src/csharp/Grpc.Core/CallOptions.cs +++ b/src/csharp/Grpc.Core/CallOptions.cs @@ -184,6 +184,7 @@ namespace Grpc.Core { Preconditions.CheckArgument(!newOptions.cancellationToken.CanBeCanceled, "Cannot propagate cancellation token from parent call. The cancellation token has already been set to a non-default value."); + newOptions.cancellationToken = propagationToken.ParentCancellationToken; } } diff --git a/src/csharp/Grpc.Core/Channel.cs b/src/csharp/Grpc.Core/Channel.cs index f5eec969f5..d8d43c7998 100644 --- a/src/csharp/Grpc.Core/Channel.cs +++ b/src/csharp/Grpc.Core/Channel.cs @@ -32,8 +32,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; using System.Threading.Tasks; using Grpc.Core.Internal; @@ -57,7 +55,7 @@ namespace Grpc.Core readonly string target; readonly GrpcEnvironment environment; readonly ChannelSafeHandle handle; - readonly List<ChannelOption> options; + readonly Dictionary<string, ChannelOption> options; bool shutdownRequested; @@ -71,12 +69,12 @@ namespace Grpc.Core public Channel(string target, ChannelCredentials credentials, IEnumerable<ChannelOption> options = null) { this.target = Preconditions.CheckNotNull(target, "target"); + this.options = CreateOptionsDictionary(options); + EnsureUserAgentChannelOption(this.options); this.environment = GrpcEnvironment.AddRef(); - this.options = options != null ? new List<ChannelOption>(options) : new List<ChannelOption>(); - EnsureUserAgentChannelOption(this.options); using (var nativeCredentials = credentials.ToNativeCredentials()) - using (var nativeChannelArgs = ChannelOptions.CreateChannelArgs(this.options)) + using (var nativeChannelArgs = ChannelOptions.CreateChannelArgs(this.options.Values)) { if (nativeCredentials != null) { @@ -173,7 +171,7 @@ namespace Grpc.Core { throw new OperationCanceledException("Channel has reached FatalFailure state."); } - await WaitForStateChangedAsync(currentState, deadline); + await WaitForStateChangedAsync(currentState, deadline).ConfigureAwait(false); currentState = handle.CheckConnectivityState(false); } } @@ -198,7 +196,7 @@ namespace Grpc.Core handle.Dispose(); - await Task.Run(() => GrpcEnvironment.Release()); + await Task.Run(() => GrpcEnvironment.Release()).ConfigureAwait(false); } internal ChannelSafeHandle Handle @@ -233,18 +231,36 @@ namespace Grpc.Core activeCallCounter.Decrement(); } - private static void EnsureUserAgentChannelOption(List<ChannelOption> options) + private static void EnsureUserAgentChannelOption(Dictionary<string, ChannelOption> options) { - if (!options.Any((option) => option.Name == ChannelOptions.PrimaryUserAgentString)) + var key = ChannelOptions.PrimaryUserAgentString; + var userAgentString = ""; + + ChannelOption option; + if (options.TryGetValue(key, out option)) { - options.Add(new ChannelOption(ChannelOptions.PrimaryUserAgentString, GetUserAgentString())); - } + // user-provided userAgentString needs to be at the beginning + userAgentString = option.StringValue + " "; + }; + + // TODO(jtattermusch): it would be useful to also provide .NET/mono version. + userAgentString += string.Format("grpc-csharp/{0}", VersionInfo.CurrentVersion); + + options[ChannelOptions.PrimaryUserAgentString] = new ChannelOption(key, userAgentString); } - private static string GetUserAgentString() + private static Dictionary<string, ChannelOption> CreateOptionsDictionary(IEnumerable<ChannelOption> options) { - // TODO(jtattermusch): it would be useful to also provide .NET/mono version. - return string.Format("grpc-csharp/{0}", VersionInfo.CurrentVersion); + var dict = new Dictionary<string, ChannelOption>(); + if (options == null) + { + return dict; + } + foreach (var option in options) + { + dict.Add(option.Name, option); + } + return dict; } } } diff --git a/src/csharp/Grpc.Core/ChannelOptions.cs b/src/csharp/Grpc.Core/ChannelOptions.cs index f5ef63af54..d70673cf78 100644 --- a/src/csharp/Grpc.Core/ChannelOptions.cs +++ b/src/csharp/Grpc.Core/ChannelOptions.cs @@ -169,7 +169,7 @@ namespace Grpc.Core /// Creates native object for a collection of channel options. /// </summary> /// <returns>The native channel arguments.</returns> - internal static ChannelArgsSafeHandle CreateChannelArgs(List<ChannelOption> options) + internal static ChannelArgsSafeHandle CreateChannelArgs(ICollection<ChannelOption> options) { if (options == null || options.Count == 0) { @@ -179,9 +179,9 @@ namespace Grpc.Core try { nativeArgs = ChannelArgsSafeHandle.Create(options.Count); - for (int i = 0; i < options.Count; i++) + int i = 0; + foreach (var option in options) { - var option = options[i]; if (option.Type == ChannelOption.OptionType.Integer) { nativeArgs.SetInteger(i, option.Name, option.IntValue); @@ -194,6 +194,7 @@ namespace Grpc.Core { throw new InvalidOperationException("Unknown option type"); } + i++; } return nativeArgs; } diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 56a06f4a9b..5b3da7c6c9 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -46,6 +46,7 @@ <ItemGroup> <Compile Include="AsyncDuplexStreamingCall.cs" /> <Compile Include="AsyncServerStreamingCall.cs" /> + <Compile Include="AsyncAuthInterceptor.cs" /> <Compile Include="CallCredentials.cs" /> <Compile Include="IClientStreamWriter.cs" /> <Compile Include="Internal\NativeMetadataCredentialsPlugin.cs" /> @@ -148,8 +149,8 @@ <Error Condition="!Exists('..\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.openssl.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.openssl.redist.targets'))" /> <Error Condition="!Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.zlib.redist.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.zlib.redist.targets'))" /> </Target> - <ItemGroup /> - <ItemGroup /> <Import Project="..\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.openssl.redist.targets" Condition="Exists('..\packages\grpc.dependencies.openssl.redist.1.0.204.1\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.openssl.redist.targets')" /> <Import Project="..\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.zlib.redist.targets" Condition="Exists('..\packages\grpc.dependencies.zlib.redist.1.2.8.10\build\portable-net45+netcore45+wpa81+wp8\grpc.dependencies.zlib.redist.targets')" /> -</Project>
\ No newline at end of file + <ItemGroup /> + <ItemGroup /> +</Project> diff --git a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs index 953f61aa1e..92f8d77e85 100644 --- a/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs +++ b/src/csharp/Grpc.Core/Internal/AsyncCallBase.cs @@ -238,20 +238,6 @@ namespace Grpc.Core.Internal } } - protected Exception TrySerialize(TWrite msg, out byte[] payload) - { - try - { - payload = serializer(msg); - return null; - } - catch (Exception e) - { - payload = null; - return e; - } - } - protected Exception TryDeserialize(byte[] payload, out TRead msg) { using (Profilers.ForCurrentThread().NewScope("AsyncCallBase.TryDeserialize")) diff --git a/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs b/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs index b4a7335c7c..d6e34a0f04 100644 --- a/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs +++ b/src/csharp/Grpc.Core/Internal/ClientResponseStream.cs @@ -70,12 +70,12 @@ namespace Grpc.Core.Internal } var taskSource = new AsyncCompletionTaskSource<TResponse>(); call.StartReadMessage(taskSource.CompletionDelegate); - var result = await taskSource.Task; + var result = await taskSource.Task.ConfigureAwait(false); this.current = result; if (result == null) { - await call.StreamingCallFinishedTask; + await call.StreamingCallFinishedTask.ConfigureAwait(false); return false; } return true; diff --git a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs index 2a571cd6c9..8bb646d303 100644 --- a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs +++ b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs @@ -38,7 +38,7 @@ using Grpc.Core.Utils; namespace Grpc.Core.Internal { - internal delegate void NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy); + internal delegate void NativeMetadataInterceptor(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy); internal class NativeMetadataCredentialsPlugin { @@ -71,7 +71,7 @@ namespace Grpc.Core.Internal get { return credentials; } } - private void NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy) + private void NativeMetadataInterceptorHandler(IntPtr statePtr, IntPtr serviceUrlPtr, IntPtr methodNamePtr, IntPtr callbackPtr, IntPtr userDataPtr, bool isDestroy) { if (isDestroy) { @@ -81,8 +81,9 @@ namespace Grpc.Core.Internal try { - string serviceUrl = Marshal.PtrToStringAnsi(serviceUrlPtr); - StartGetMetadata(serviceUrl, callbackPtr, userDataPtr); + var context = new AuthInterceptorContext(Marshal.PtrToStringAnsi(serviceUrlPtr), + Marshal.PtrToStringAnsi(methodNamePtr)); + StartGetMetadata(context, callbackPtr, userDataPtr); } catch (Exception e) { @@ -91,12 +92,12 @@ namespace Grpc.Core.Internal } } - private async void StartGetMetadata(string serviceUrl, IntPtr callbackPtr, IntPtr userDataPtr) + private async void StartGetMetadata(AuthInterceptorContext context, IntPtr callbackPtr, IntPtr userDataPtr) { try { var metadata = new Metadata(); - await interceptor(serviceUrl, metadata); + await interceptor(context, metadata).ConfigureAwait(false); using (var metadataArray = MetadataArraySafeHandle.Create(metadata)) { diff --git a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs index 59f4c5727c..de66759b94 100644 --- a/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs +++ b/src/csharp/Grpc.Core/Internal/ServerCallHandler.cs @@ -78,13 +78,13 @@ namespace Grpc.Core.Internal var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); try { - Preconditions.CheckArgument(await requestStream.MoveNext()); + Preconditions.CheckArgument(await requestStream.MoveNext().ConfigureAwait(false)); var request = requestStream.Current; // TODO(jtattermusch): we need to read the full stream so that native callhandle gets deallocated. - Preconditions.CheckArgument(!await requestStream.MoveNext()); - var result = await handler(request, context); + Preconditions.CheckArgument(!await requestStream.MoveNext().ConfigureAwait(false)); + var result = await handler(request, context).ConfigureAwait(false); status = context.Status; - await responseStream.WriteAsync(result); + await responseStream.WriteAsync(result).ConfigureAwait(false); } catch (Exception e) { @@ -93,13 +93,13 @@ namespace Grpc.Core.Internal } try { - await responseStream.WriteStatusAsync(status, context.ResponseTrailers); + await responseStream.WriteStatusAsync(status, context.ResponseTrailers).ConfigureAwait(false); } catch (OperationCanceledException) { // Call has been already cancelled. } - await finishedTask; + await finishedTask.ConfigureAwait(false); } } @@ -134,11 +134,11 @@ namespace Grpc.Core.Internal var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); try { - Preconditions.CheckArgument(await requestStream.MoveNext()); + Preconditions.CheckArgument(await requestStream.MoveNext().ConfigureAwait(false)); var request = requestStream.Current; // TODO(jtattermusch): we need to read the full stream so that native callhandle gets deallocated. - Preconditions.CheckArgument(!await requestStream.MoveNext()); - await handler(request, responseStream, context); + Preconditions.CheckArgument(!await requestStream.MoveNext().ConfigureAwait(false)); + await handler(request, responseStream, context).ConfigureAwait(false); status = context.Status; } catch (Exception e) @@ -149,13 +149,13 @@ namespace Grpc.Core.Internal try { - await responseStream.WriteStatusAsync(status, context.ResponseTrailers); + await responseStream.WriteStatusAsync(status, context.ResponseTrailers).ConfigureAwait(false); } catch (OperationCanceledException) { // Call has been already cancelled. } - await finishedTask; + await finishedTask.ConfigureAwait(false); } } @@ -190,11 +190,11 @@ namespace Grpc.Core.Internal var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); try { - var result = await handler(requestStream, context); + var result = await handler(requestStream, context).ConfigureAwait(false); status = context.Status; try { - await responseStream.WriteAsync(result); + await responseStream.WriteAsync(result).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -209,13 +209,13 @@ namespace Grpc.Core.Internal try { - await responseStream.WriteStatusAsync(status, context.ResponseTrailers); + await responseStream.WriteStatusAsync(status, context.ResponseTrailers).ConfigureAwait(false); } catch (OperationCanceledException) { // Call has been already cancelled. } - await finishedTask; + await finishedTask.ConfigureAwait(false); } } @@ -250,7 +250,7 @@ namespace Grpc.Core.Internal var context = HandlerUtils.NewContext(newRpc, asyncCall.Peer, responseStream, asyncCall.CancellationToken); try { - await handler(requestStream, responseStream, context); + await handler(requestStream, responseStream, context).ConfigureAwait(false); status = context.Status; } catch (Exception e) @@ -260,13 +260,13 @@ namespace Grpc.Core.Internal } try { - await responseStream.WriteStatusAsync(status, context.ResponseTrailers); + await responseStream.WriteStatusAsync(status, context.ResponseTrailers).ConfigureAwait(false); } catch (OperationCanceledException) { // Call has been already cancelled. } - await finishedTask; + await finishedTask.ConfigureAwait(false); } } @@ -284,8 +284,8 @@ namespace Grpc.Core.Internal var finishedTask = asyncCall.ServerSideCallAsync(); var responseStream = new ServerResponseStream<byte[], byte[]>(asyncCall); - await responseStream.WriteStatusAsync(new Status(StatusCode.Unimplemented, "No such method."), Metadata.Empty); - await finishedTask; + await responseStream.WriteStatusAsync(new Status(StatusCode.Unimplemented, ""), Metadata.Empty).ConfigureAwait(false); + await finishedTask.ConfigureAwait(false); } } diff --git a/src/csharp/Grpc.Core/Internal/ServerRequestStream.cs b/src/csharp/Grpc.Core/Internal/ServerRequestStream.cs index 3fccb88abb..e7be82c318 100644 --- a/src/csharp/Grpc.Core/Internal/ServerRequestStream.cs +++ b/src/csharp/Grpc.Core/Internal/ServerRequestStream.cs @@ -70,7 +70,7 @@ namespace Grpc.Core.Internal } var taskSource = new AsyncCompletionTaskSource<TRequest>(); call.StartReadMessage(taskSource.CompletionDelegate); - var result = await taskSource.Task; + var result = await taskSource.Task.ConfigureAwait(false); this.current = result; return result != null; } diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs index 38fc067d9f..3031175c8a 100644 --- a/src/csharp/Grpc.Core/Internal/Timespec.cs +++ b/src/csharp/Grpc.Core/Internal/Timespec.cs @@ -63,20 +63,18 @@ namespace Grpc.Core.Internal [DllImport("grpc_csharp_ext.dll")] static extern int gprsharp_sizeof_timespec(); - public Timespec(IntPtr tv_sec, int tv_nsec) : this(tv_sec, tv_nsec, GPRClockType.Realtime) + public Timespec(long tv_sec, int tv_nsec) : this(tv_sec, tv_nsec, GPRClockType.Realtime) { } - public Timespec(IntPtr tv_sec, int tv_nsec, GPRClockType clock_type) + public Timespec(long tv_sec, int tv_nsec, GPRClockType clock_type) { this.tv_sec = tv_sec; this.tv_nsec = tv_nsec; this.clock_type = clock_type; } - // NOTE: on linux 64bit sizeof(gpr_timespec) = 16, on windows 32bit sizeof(gpr_timespec) = 8 - // so IntPtr seems to have the right size to work on both. - private System.IntPtr tv_sec; + private long tv_sec; private int tv_nsec; private GPRClockType clock_type; @@ -116,7 +114,7 @@ namespace Grpc.Core.Internal /// <summary> /// Seconds since unix epoch. /// </summary> - public IntPtr TimevalSeconds + public long TimevalSeconds { get { @@ -176,18 +174,18 @@ namespace Grpc.Core.Internal { // convert nanos to ticks, round up to the nearest tick long ticksFromNanos = tv_nsec / NanosPerTick + ((tv_nsec % NanosPerTick != 0) ? 1 : 0); - long ticksTotal = checked(tv_sec.ToInt64() * TicksPerSecond + ticksFromNanos); + long ticksTotal = checked(tv_sec * TicksPerSecond + ticksFromNanos); return UnixEpoch.AddTicks(ticksTotal); } catch (OverflowException) { // ticks out of long range - return tv_sec.ToInt64() > 0 ? DateTime.MaxValue : DateTime.MinValue; + return tv_sec > 0 ? DateTime.MaxValue : DateTime.MinValue; } catch (ArgumentOutOfRangeException) { // resulting date time would be larger than MaxValue - return tv_sec.ToInt64() > 0 ? DateTime.MaxValue : DateTime.MinValue; + return tv_sec > 0 ? DateTime.MaxValue : DateTime.MinValue; } } @@ -226,12 +224,7 @@ namespace Grpc.Core.Internal seconds--; nanos += (int)NanosPerSecond; } - // new IntPtr possibly throws OverflowException - return new Timespec(new IntPtr(seconds), nanos); - } - catch (OverflowException) - { - return dateTime > UnixEpoch ? Timespec.InfFuture : Timespec.InfPast; + return new Timespec(seconds, nanos); } catch (ArgumentOutOfRangeException) { diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 7c94d21561..d120f95fdf 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -148,10 +148,10 @@ namespace Grpc.Core } handle.ShutdownAndNotify(HandleServerShutdown, environment); - await shutdownTcs.Task; + await shutdownTcs.Task.ConfigureAwait(false); DisposeHandle(); - await Task.Run(() => GrpcEnvironment.Release()); + await Task.Run(() => GrpcEnvironment.Release()).ConfigureAwait(false); } /// <summary> @@ -169,8 +169,10 @@ namespace Grpc.Core handle.ShutdownAndNotify(HandleServerShutdown, environment); handle.CancelAllCalls(); - await shutdownTcs.Task; + await shutdownTcs.Task.ConfigureAwait(false); DisposeHandle(); + + await Task.Run(() => GrpcEnvironment.Release()).ConfigureAwait(false); } internal void AddCallReference(object call) @@ -268,7 +270,7 @@ namespace Grpc.Core { callHandler = NoSuchMethodCallHandler.Instance; } - await callHandler.HandleCall(newRpc, environment); + await callHandler.HandleCall(newRpc, environment).ConfigureAwait(false); } catch (Exception e) { @@ -288,7 +290,7 @@ namespace Grpc.Core // after server shutdown, the callback returns with null call if (!newRpc.Call.IsInvalid) { - Task.Run(async () => await HandleCallAsync(newRpc)); + Task.Run(async () => await HandleCallAsync(newRpc)).ConfigureAwait(false); } } diff --git a/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs b/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs index cdf1e51026..02a47568e7 100644 --- a/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs +++ b/src/csharp/Grpc.Core/Utils/AsyncStreamExtensions.cs @@ -48,9 +48,9 @@ namespace Grpc.Core.Utils public static async Task ForEachAsync<T>(this IAsyncStreamReader<T> streamReader, Func<T, Task> asyncAction) where T : class { - while (await streamReader.MoveNext()) + while (await streamReader.MoveNext().ConfigureAwait(false)) { - await asyncAction(streamReader.Current); + await asyncAction(streamReader.Current).ConfigureAwait(false); } } @@ -61,7 +61,7 @@ namespace Grpc.Core.Utils where T : class { var result = new List<T>(); - while (await streamReader.MoveNext()) + while (await streamReader.MoveNext().ConfigureAwait(false)) { result.Add(streamReader.Current); } @@ -77,11 +77,11 @@ namespace Grpc.Core.Utils { foreach (var element in elements) { - await streamWriter.WriteAsync(element); + await streamWriter.WriteAsync(element).ConfigureAwait(false); } if (complete) { - await streamWriter.CompleteAsync(); + await streamWriter.CompleteAsync().ConfigureAwait(false); } } @@ -93,7 +93,7 @@ namespace Grpc.Core.Utils { foreach (var element in elements) { - await streamWriter.WriteAsync(element); + await streamWriter.WriteAsync(element).ConfigureAwait(false); } } } diff --git a/src/csharp/Grpc.Examples/Grpc.Examples.csproj b/src/csharp/Grpc.Examples/Grpc.Examples.csproj index 55462e02fd..53b2bb78c4 100644 --- a/src/csharp/Grpc.Examples/Grpc.Examples.csproj +++ b/src/csharp/Grpc.Examples/Grpc.Examples.csproj @@ -66,6 +66,5 @@ </ItemGroup> <ItemGroup> <None Include="packages.config" /> - <None Include="proto\math.proto" /> </ItemGroup> -</Project>
\ No newline at end of file +</Project> diff --git a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj index 8fce5d39aa..4e775a7a0c 100644 --- a/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj +++ b/src/csharp/Grpc.HealthCheck/Grpc.HealthCheck.csproj @@ -65,7 +65,6 @@ <ItemGroup> <None Include="Grpc.HealthCheck.nuspec" /> <None Include="packages.config" /> - <None Include="proto\health.proto" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Grpc.Core\Grpc.Core.csproj"> @@ -81,4 +80,4 @@ <Target Name="AfterBuild"> </Target> --> -</Project>
\ No newline at end of file +</Project> diff --git a/src/csharp/Grpc.HealthCheck/proto/health.proto b/src/csharp/Grpc.HealthCheck/proto/health.proto deleted file mode 100644 index 01aa3fcf57..0000000000 --- a/src/csharp/Grpc.HealthCheck/proto/health.proto +++ /dev/null @@ -1,52 +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. - -// TODO(jtattermusch): switch to proto3 once C# supports that. -syntax = "proto3"; - -package grpc.health.v1alpha; -option csharp_namespace = "Grpc.Health.V1Alpha"; - -message HealthCheckRequest { - string host = 1; - string service = 2; -} - -message HealthCheckResponse { - enum ServingStatus { - UNKNOWN = 0; - SERVING = 1; - NOT_SERVING = 2; - } - ServingStatus status = 1; -} - -service Health { - rpc Check(HealthCheckRequest) returns (HealthCheckResponse); -}
\ No newline at end of file diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index 012de45524..b0d920a34c 100644 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -41,6 +41,9 @@ <Reference Include="CommandLine"> <HintPath>..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath> </Reference> + <Reference Include="Moq"> + <HintPath>..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath> + </Reference> <Reference Include="nunit.framework"> <HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath> </Reference> @@ -83,6 +86,7 @@ <Compile Include="..\Grpc.Core\Version.cs"> <Link>Version.cs</Link> </Compile> + <Compile Include="HeaderInterceptorTest.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Empty.cs" /> <Compile Include="Messages.cs" /> diff --git a/src/csharp/Grpc.IntegrationTesting/HeaderInterceptorTest.cs b/src/csharp/Grpc.IntegrationTesting/HeaderInterceptorTest.cs new file mode 100644 index 0000000000..1d758b7540 --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/HeaderInterceptorTest.cs @@ -0,0 +1,113 @@ +#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.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core; +using Grpc.Core.Utils; +using Grpc.Testing; +using NUnit.Framework; + +namespace Grpc.IntegrationTesting +{ + public class HeaderInterceptorTest + { + const string Host = "localhost"; + Server server; + Channel channel; + TestService.TestServiceClient client; + + [TestFixtureSetUp] + public void Init() + { + server = new Server + { + Services = { TestService.BindService(new TestServiceImpl()) }, + Ports = { { Host, ServerPort.PickUnused, ServerCredentials.Insecure } } + }; + server.Start(); + + channel = new Channel(Host, server.Ports.Single().BoundPort, ChannelCredentials.Insecure); + client = TestService.NewClient(channel); + } + + [TestFixtureTearDown] + public void Cleanup() + { + channel.ShutdownAsync().Wait(); + server.ShutdownAsync().Wait(); + } + + [Test] + public async Task HeaderInterceptor_CreateMetadata() + { + var key = "x-grpc-test-echo-initial"; + client.HeaderInterceptor = new HeaderInterceptor((method, metadata) => + { + metadata.Add(key, "ABC"); + }); + + var call = client.UnaryCallAsync(new SimpleRequest()); + await call; + + var responseHeaders = await call.ResponseHeadersAsync; + Assert.AreEqual("ABC", responseHeaders.First((entry) => entry.Key == key).Value); + } + + [Test] + public async Task HeaderInterceptor_AppendMetadata() + { + var initialKey = "x-grpc-test-echo-initial"; + var trailingKey = "x-grpc-test-echo-trailing-bin"; + + client.HeaderInterceptor = new HeaderInterceptor((method, metadata) => + { + metadata.Add(initialKey, "ABC"); + }); + + var headers = new Metadata + { + { trailingKey, new byte[] {0xaa} } + }; + var call = client.UnaryCallAsync(new SimpleRequest(), headers: headers); + await call; + + var responseHeaders = await call.ResponseHeadersAsync; + Assert.AreEqual("ABC", responseHeaders.First((entry) => entry.Key == initialKey).Value); + CollectionAssert.AreEqual(new byte[] {0xaa}, call.GetTrailers().First((entry) => entry.Key == trailingKey).ValueBytes); + } + } +} diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index 5eec11abf7..b0e33e49f7 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -34,6 +34,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -130,8 +131,7 @@ namespace Grpc.IntegrationTesting }; } var channel = new Channel(options.ServerHost, options.ServerPort, credentials, channelOptions); - TestService.TestServiceClient client = new TestService.TestServiceClient(channel); - await RunTestCaseAsync(client, options); + await RunTestCaseAsync(channel, options); await channel.ShutdownAsync(); } @@ -159,8 +159,9 @@ namespace Grpc.IntegrationTesting return credentials; } - private async Task RunTestCaseAsync(TestService.TestServiceClient client, ClientOptions options) + private async Task RunTestCaseAsync(Channel channel, ClientOptions options) { + var client = new TestService.TestServiceClient(channel); switch (options.TestCase) { case "empty_unary": @@ -202,8 +203,14 @@ namespace Grpc.IntegrationTesting case "timeout_on_sleeping_server": await RunTimeoutOnSleepingServerAsync(client); break; - case "benchmark_empty_unary": - RunBenchmarkEmptyUnary(client); + case "custom_metadata": + await RunCustomMetadataAsync(client); + break; + case "status_code_and_message": + await RunStatusCodeAndMessageAsync(client); + break; + case "unimplemented_method": + RunUnimplementedMethod(new UnimplementedService.UnimplementedServiceClient(channel)); break; default: throw new ArgumentException("Unknown test case " + options.TestCase); @@ -227,7 +234,6 @@ namespace Grpc.IntegrationTesting ResponseSize = 314159, Payload = CreateZerosPayload(271828) }; - var response = client.UnaryCall(request); Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); @@ -493,11 +499,95 @@ namespace Grpc.IntegrationTesting Console.WriteLine("Passed!"); } - // This is not an official interop test, but it's useful. - public static void RunBenchmarkEmptyUnary(TestService.ITestServiceClient client) + public static async Task RunCustomMetadataAsync(TestService.ITestServiceClient client) { - BenchmarkUtil.RunBenchmark(10000, 10000, - () => { client.EmptyCall(new Empty()); }); + Console.WriteLine("running custom_metadata"); + { + // step 1: test unary call + var request = new SimpleRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseSize = 314159, + Payload = CreateZerosPayload(271828) + }; + + var call = client.UnaryCallAsync(request, headers: CreateTestMetadata()); + await call.ResponseAsync; + + var responseHeaders = await call.ResponseHeadersAsync; + var responseTrailers = call.GetTrailers(); + + Assert.AreEqual("test_initial_metadata_value", responseHeaders.First((entry) => entry.Key == "x-grpc-test-echo-initial").Value); + CollectionAssert.AreEqual(new byte[] { 0xab, 0xab, 0xab }, responseTrailers.First((entry) => entry.Key == "x-grpc-test-echo-trailing-bin").ValueBytes); + } + + { + // step 2: test full duplex call + var request = new StreamingOutputCallRequest + { + ResponseType = PayloadType.COMPRESSABLE, + ResponseParameters = { new ResponseParameters { Size = 31415 } }, + Payload = CreateZerosPayload(27182) + }; + + var call = client.FullDuplexCall(headers: CreateTestMetadata()); + var responseHeaders = await call.ResponseHeadersAsync; + + await call.RequestStream.WriteAsync(request); + await call.RequestStream.CompleteAsync(); + await call.ResponseStream.ToListAsync(); + + var responseTrailers = call.GetTrailers(); + + Assert.AreEqual("test_initial_metadata_value", responseHeaders.First((entry) => entry.Key == "x-grpc-test-echo-initial").Value); + CollectionAssert.AreEqual(new byte[] { 0xab, 0xab, 0xab }, responseTrailers.First((entry) => entry.Key == "x-grpc-test-echo-trailing-bin").ValueBytes); + } + + Console.WriteLine("Passed!"); + } + + public static async Task RunStatusCodeAndMessageAsync(TestService.ITestServiceClient client) + { + Console.WriteLine("running status_code_and_message"); + var echoStatus = new EchoStatus + { + Code = 2, + Message = "test status message" + }; + + { + // step 1: test unary call + var request = new SimpleRequest { ResponseStatus = echoStatus }; + + var e = Assert.Throws<RpcException>(() => client.UnaryCall(request)); + Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode); + Assert.AreEqual(echoStatus.Message, e.Status.Detail); + } + + { + // step 2: test full duplex call + var request = new StreamingOutputCallRequest { ResponseStatus = echoStatus }; + + var call = client.FullDuplexCall(); + await call.RequestStream.WriteAsync(request); + await call.RequestStream.CompleteAsync(); + + var e = Assert.Throws<RpcException>(async () => await call.ResponseStream.ToListAsync()); + Assert.AreEqual(StatusCode.Unknown, e.Status.StatusCode); + Assert.AreEqual(echoStatus.Message, e.Status.Detail); + } + + Console.WriteLine("Passed!"); + } + + public static void RunUnimplementedMethod(UnimplementedService.IUnimplementedServiceClient 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!"); } private static Payload CreateZerosPayload(int size) @@ -516,5 +606,14 @@ namespace Grpc.IntegrationTesting Assert.IsTrue(email.Length > 0); // spec requires nonempty client email. return email; } + + private static Metadata CreateTestMetadata() + { + return new Metadata + { + {"x-grpc-test-echo-initial", "test_initial_metadata_value"}, + {"x-grpc-test-echo-trailing-bin", new byte[] {0xab, 0xab, 0xab}} + }; + } } } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index 837ae74c45..18168f9970 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -128,9 +128,29 @@ namespace Grpc.IntegrationTesting } [Test] - public async Task TimeoutOnSleepingServerAsync() + public async Task TimeoutOnSleepingServer() { await InteropClient.RunTimeoutOnSleepingServerAsync(client); } + + [Test] + public async Task CustomMetadata() + { + await InteropClient.RunCustomMetadataAsync(client); + } + + [Test] + [Ignore("TODO: see #4427")] + public async Task StatusCodeAndMessage() + { + await InteropClient.RunStatusCodeAndMessageAsync(client); + } + + [Test] + [Ignore("TODO: see #4427")] + public void UnimplementedMethod() + { + InteropClient.RunUnimplementedMethod(UnimplementedService.NewClient(channel)); + } } } diff --git a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs index 3d56678b99..1c8bfed1f6 100644 --- a/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/MetadataCredentialsTest.cs @@ -40,6 +40,7 @@ using System.Threading.Tasks; using Grpc.Core; using Grpc.Core.Utils; using Grpc.Testing; +using Moq; using NUnit.Framework; namespace Grpc.IntegrationTesting @@ -50,37 +51,37 @@ namespace Grpc.IntegrationTesting Server server; Channel channel; TestService.ITestServiceClient client; + List<ChannelOption> options; + Mock<TestService.ITestService> serviceMock; + AsyncAuthInterceptor asyncAuthInterceptor; - [TestFixtureSetUp] + [SetUp] public void Init() { - var serverCredentials = new SslServerCredentials(new[] { new KeyCertificatePair(File.ReadAllText(TestCredentials.ServerCertChainPath), File.ReadAllText(TestCredentials.ServerPrivateKeyPath)) }); + serviceMock = new Mock<TestService.ITestService>(); + serviceMock.Setup(m => m.UnaryCall(It.IsAny<SimpleRequest>(), It.IsAny<ServerCallContext>())) + .Returns(new Func<SimpleRequest, ServerCallContext, Task<SimpleResponse>>(UnaryCallHandler)); + server = new Server { - Services = { TestService.BindService(new TestServiceImpl()) }, - Ports = { { Host, ServerPort.PickUnused, serverCredentials } } + Services = { TestService.BindService(serviceMock.Object) }, + Ports = { { Host, ServerPort.PickUnused, TestCredentials.CreateSslServerCredentials() } } }; server.Start(); - var options = new List<ChannelOption> + options = new List<ChannelOption> { new ChannelOption(ChannelOptions.SslTargetNameOverride, TestCredentials.DefaultHostOverride) }; - var asyncAuthInterceptor = new AsyncAuthInterceptor(async (authUri, metadata) => + asyncAuthInterceptor = new AsyncAuthInterceptor(async (context, metadata) => { - await Task.Delay(100); // make sure the operation is asynchronous. + await Task.Delay(100).ConfigureAwait(false); // make sure the operation is asynchronous. metadata.Add("authorization", "SECRET_TOKEN"); }); - - var clientCredentials = ChannelCredentials.Create( - new SslCredentials(File.ReadAllText(TestCredentials.ClientCertAuthorityPath)), - CallCredentials.FromInterceptor(asyncAuthInterceptor)); - channel = new Channel(Host, server.Ports.Single().BoundPort, clientCredentials, options); - client = TestService.NewClient(channel); } - [TestFixtureTearDown] + [TearDown] public void Cleanup() { channel.ShutdownAsync().Wait(); @@ -90,8 +91,29 @@ namespace Grpc.IntegrationTesting [Test] public void MetadataCredentials() { - var response = client.UnaryCall(new SimpleRequest { ResponseSize = 10 }); - Assert.AreEqual(10, response.Payload.Body.Length); + var channelCredentials = ChannelCredentials.Create(TestCredentials.CreateSslCredentials(), + CallCredentials.FromInterceptor(asyncAuthInterceptor)); + channel = new Channel(Host, server.Ports.Single().BoundPort, channelCredentials, options); + client = TestService.NewClient(channel); + + client.UnaryCall(new SimpleRequest {}); + } + + [Test] + public void MetadataCredentials_PerCall() + { + channel = new Channel(Host, server.Ports.Single().BoundPort, TestCredentials.CreateSslCredentials(), options); + client = TestService.NewClient(channel); + + var callCredentials = CallCredentials.FromInterceptor(asyncAuthInterceptor); + client.UnaryCall(new SimpleRequest { }, new CallOptions(credentials: callCredentials)); + } + + private Task<SimpleResponse> UnaryCallHandler(SimpleRequest request, ServerCallContext context) + { + var authToken = context.RequestHeaders.First((entry) => entry.Key == "authorization").Value; + Assert.AreEqual("SECRET_TOKEN", authToken); + return Task.FromResult(new SimpleResponse()); } } } diff --git a/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs index 2b51526c88..3dd91b7948 100644 --- a/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/RunnerClientServerTest.cs @@ -75,9 +75,10 @@ namespace Grpc.IntegrationTesting serverRunner.StopAsync().Wait(); } - // Test attribute commented out to prevent running as part of the default test suite. - //[Test] - //[Category("Performance")] + + [Test] + [Category("Performance")] + [Ignore("Prevent running on Jenkins")] public async Task ClientServerRunner() { var config = new ClientConfig diff --git a/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs index c5bfcf08c0..5a1b4cf319 100644 --- a/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs +++ b/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Google.Protobuf; @@ -51,14 +52,20 @@ namespace Grpc.Testing return Task.FromResult(new Empty()); } - public Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context) + public async Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context) { + await EnsureEchoMetadataAsync(context); + EnsureEchoStatus(request.ResponseStatus, context); + var response = new SimpleResponse { Payload = CreateZerosPayload(request.ResponseSize) }; - return Task.FromResult(response); + return response; } public async Task StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context) { + await EnsureEchoMetadataAsync(context); + EnsureEchoStatus(request.ResponseStatus, context); + foreach (var responseParam in request.ResponseParameters) { var response = new StreamingOutputCallResponse { Payload = CreateZerosPayload(responseParam.Size) }; @@ -68,6 +75,8 @@ namespace Grpc.Testing public async Task<StreamingInputCallResponse> StreamingInputCall(IAsyncStreamReader<StreamingInputCallRequest> requestStream, ServerCallContext context) { + await EnsureEchoMetadataAsync(context); + int sum = 0; await requestStream.ForEachAsync(async request => { @@ -78,8 +87,11 @@ namespace Grpc.Testing public async Task FullDuplexCall(IAsyncStreamReader<StreamingOutputCallRequest> requestStream, IServerStreamWriter<StreamingOutputCallResponse> responseStream, ServerCallContext context) { + await EnsureEchoMetadataAsync(context); + await requestStream.ForEachAsync(async request => { + EnsureEchoStatus(request.ResponseStatus, context); foreach (var responseParam in request.ResponseParameters) { var response = new StreamingOutputCallResponse { Payload = CreateZerosPayload(responseParam.Size) }; @@ -97,5 +109,28 @@ namespace Grpc.Testing { return new Payload { Body = ByteString.CopyFrom(new byte[size]) }; } + + private static async Task EnsureEchoMetadataAsync(ServerCallContext context) + { + var echoInitialList = context.RequestHeaders.Where((entry) => entry.Key == "x-grpc-test-echo-initial").ToList(); + if (echoInitialList.Any()) { + var entry = echoInitialList.Single(); + await context.WriteResponseHeadersAsync(new Metadata { entry }); + } + + var echoTrailingList = context.RequestHeaders.Where((entry) => entry.Key == "x-grpc-test-echo-trailing-bin").ToList(); + if (echoTrailingList.Any()) { + context.ResponseTrailers.Add(echoTrailingList.Single()); + } + } + + private static void EnsureEchoStatus(EchoStatus responseStatus, ServerCallContext context) + { + if (responseStatus != null) + { + var statusCode = (StatusCode)responseStatus.Code; + context.Status = new Status(statusCode, responseStatus.Message); + } + } } } diff --git a/src/csharp/Grpc.IntegrationTesting/packages.config b/src/csharp/Grpc.IntegrationTesting/packages.config index bdb3dadf44..5c59af1b7d 100644 --- a/src/csharp/Grpc.IntegrationTesting/packages.config +++ b/src/csharp/Grpc.IntegrationTesting/packages.config @@ -11,6 +11,7 @@ <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" /> <package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" /> <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" /> + <package id="Moq" version="4.2.1510.2205" targetFramework="net45" /> <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" /> <package id="NUnit" version="2.6.4" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index b8705c49d3..0ef9be33a6 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -927,7 +927,8 @@ GPR_EXPORT void GPR_CALLTYPE grpcsharp_metadata_credentials_notify_from_plugin( } typedef void(GPR_CALLTYPE *grpcsharp_metadata_interceptor_func)( - void *state, const char *service_url, grpc_credentials_plugin_metadata_cb cb, + void *state, const char *service_url, const char *method_name, + grpc_credentials_plugin_metadata_cb cb, void *user_data, gpr_int32 is_destroy); static void grpcsharp_get_metadata_handler( @@ -935,13 +936,13 @@ static void grpcsharp_get_metadata_handler( grpc_credentials_plugin_metadata_cb cb, void *user_data) { grpcsharp_metadata_interceptor_func interceptor = (grpcsharp_metadata_interceptor_func)(gpr_intptr)state; - interceptor(state, context.service_url, cb, user_data, 0); + interceptor(state, context.service_url, context.method_name, cb, user_data, 0); } static void grpcsharp_metadata_credentials_destroy_handler(void *state) { grpcsharp_metadata_interceptor_func interceptor = (grpcsharp_metadata_interceptor_func)(gpr_intptr)state; - interceptor(state, NULL, NULL, NULL, 1); + interceptor(state, NULL, NULL, NULL, NULL, 1); } GPR_EXPORT grpc_call_credentials *GPR_CALLTYPE grpcsharp_metadata_credentials_create_from_plugin( diff --git a/src/csharp/generate_proto_csharp.sh b/src/csharp/generate_proto_csharp.sh index 92348d1394..4dbd9c3308 100755 --- a/src/csharp/generate_proto_csharp.sh +++ b/src/csharp/generate_proto_csharp.sh @@ -30,19 +30,19 @@ # Regenerates gRPC service stubs from proto files. set +e -cd $(dirname $0) +cd $(dirname $0)/../.. -PROTOC=../../bins/opt/protobuf/protoc -PLUGIN=protoc-gen-grpc=../../bins/opt/grpc_csharp_plugin -EXAMPLES_DIR=Grpc.Examples -TESTING_DIR=Grpc.IntegrationTesting -HEALTHCHECK_DIR=Grpc.HealthCheck +PROTOC=bins/opt/protobuf/protoc +PLUGIN=protoc-gen-grpc=bins/opt/grpc_csharp_plugin +EXAMPLES_DIR=src/csharp/Grpc.Examples +HEALTHCHECK_DIR=src/csharp/Grpc.HealthCheck +TESTING_DIR=src/csharp/Grpc.IntegrationTesting $PROTOC --plugin=$PLUGIN --csharp_out=$EXAMPLES_DIR --grpc_out=$EXAMPLES_DIR \ - -I $EXAMPLES_DIR/proto $EXAMPLES_DIR/proto/math.proto - -$PROTOC --plugin=$PLUGIN --csharp_out=$TESTING_DIR --grpc_out=$TESTING_DIR \ - -I ../.. ../../test/proto/*.proto ../../test/proto/benchmarks/*.proto + -I src/proto/math src/proto/math/math.proto $PROTOC --plugin=$PLUGIN --csharp_out=$HEALTHCHECK_DIR --grpc_out=$HEALTHCHECK_DIR \ - -I $HEALTHCHECK_DIR/proto $HEALTHCHECK_DIR/proto/health.proto + -I src/proto/grpc/health/v1alpha src/proto/grpc/health/v1alpha/health.proto + +$PROTOC --plugin=$PLUGIN --csharp_out=$TESTING_DIR --grpc_out=$TESTING_DIR \ + -I . test/proto/{empty,messages,test}.proto test/proto/benchmarks/*.proto diff --git a/src/csharp/tests.json b/src/csharp/tests.json new file mode 100644 index 0000000000..4aa93668ad --- /dev/null +++ b/src/csharp/tests.json @@ -0,0 +1,45 @@ +{ + "assemblies": [ + "Grpc.Core.Tests", + "Grpc.Examples.Tests", + "Grpc.HealthCheck.Tests", + "Grpc.IntegrationTesting" + ], + "tests": [ + "Grpc.Core.Internal.Tests.AsyncCallTest", + "Grpc.Core.Internal.Tests.ChannelArgsSafeHandleTest", + "Grpc.Core.Internal.Tests.CompletionQueueEventTest", + "Grpc.Core.Internal.Tests.CompletionQueueSafeHandleTest", + "Grpc.Core.Internal.Tests.MetadataArraySafeHandleTest", + "Grpc.Core.Internal.Tests.TimespecTest", + "Grpc.Core.Tests.CallCredentialsTest", + "Grpc.Core.Tests.CallOptionsTest", + "Grpc.Core.Tests.ChannelCredentialsTest", + "Grpc.Core.Tests.ChannelOptionsTest", + "Grpc.Core.Tests.ChannelTest", + "Grpc.Core.Tests.ClientServerTest", + "Grpc.Core.Tests.CompressionTest", + "Grpc.Core.Tests.ContextPropagationTest", + "Grpc.Core.Tests.GrpcEnvironmentTest", + "Grpc.Core.Tests.MarshallingErrorsTest", + "Grpc.Core.Tests.MetadataTest", + "Grpc.Core.Tests.NUnitVersionTest", + "Grpc.Core.Tests.PerformanceTest", + "Grpc.Core.Tests.PInvokeTest", + "Grpc.Core.Tests.ResponseHeadersTest", + "Grpc.Core.Tests.SanityTest", + "Grpc.Core.Tests.ServerTest", + "Grpc.Core.Tests.ShutdownTest", + "Grpc.Core.Tests.TimeoutsTest", + "Grpc.Core.Tests.UserAgentStringTest", + "Math.Tests.MathClientServerTest", + "Grpc.HealthCheck.Tests.HealthClientServerTest", + "Grpc.HealthCheck.Tests.HealthServiceImplTest", + "Grpc.IntegrationTesting.HeaderInterceptorTest", + "Grpc.IntegrationTesting.HistogramTest", + "Grpc.IntegrationTesting.InteropClientServerTest", + "Grpc.IntegrationTesting.MetadataCredentialsTest", + "Grpc.IntegrationTesting.RunnerClientServerTest", + "Grpc.IntegrationTesting.SslCredentialsTest" + ] +}
\ No newline at end of file diff --git a/src/node/ext/channel_credentials.cc b/src/node/ext/channel_credentials.cc index b77ff80af2..059bc8a890 100644 --- a/src/node/ext/channel_credentials.cc +++ b/src/node/ext/channel_credentials.cc @@ -154,6 +154,12 @@ NAN_METHOD(ChannelCredentials::CreateSsl) { return Nan::ThrowTypeError( "createSSl's third argument must be a Buffer if provided"); } + if ((key_cert_pair.private_key == NULL) != + (key_cert_pair.cert_chain == NULL)) { + return Nan::ThrowError( + "createSsl's second and third arguments must be" + " provided or omitted together"); + } grpc_channel_credentials *creds = grpc_ssl_credentials_create( root_certs, key_cert_pair.private_key == NULL ? NULL : &key_cert_pair, NULL); diff --git a/src/node/ext/server_credentials.cc b/src/node/ext/server_credentials.cc index e6c55e263c..5285d53df4 100644 --- a/src/node/ext/server_credentials.cc +++ b/src/node/ext/server_credentials.cc @@ -167,30 +167,18 @@ NAN_METHOD(ServerCredentials::CreateSsl) { return Nan::ThrowTypeError("Key/cert pairs must be objects"); } Local<Object> pair_obj = Nan::To<Object>(pair_val).ToLocalChecked(); - MaybeLocal<Value> maybe_key = Nan::Get(pair_obj, key_key); - if (maybe_key.IsEmpty()) { - delete key_cert_pairs; - return Nan::ThrowTypeError( - "Key/cert pairs must have a private_key and a cert_chain"); - } - MaybeLocal<Value> maybe_cert = Nan::Get(pair_obj, cert_key); - if (maybe_cert.IsEmpty()) { - delete key_cert_pairs; - return Nan::ThrowTypeError( - "Key/cert pairs must have a private_key and a cert_chain"); - } - if (!::node::Buffer::HasInstance(maybe_key.ToLocalChecked())) { + Local<Value> maybe_key = Nan::Get(pair_obj, key_key).ToLocalChecked(); + Local<Value> maybe_cert = Nan::Get(pair_obj, cert_key).ToLocalChecked(); + if (!::node::Buffer::HasInstance(maybe_key)) { delete key_cert_pairs; return Nan::ThrowTypeError("private_key must be a Buffer"); } - if (!::node::Buffer::HasInstance(maybe_cert.ToLocalChecked())) { + if (!::node::Buffer::HasInstance(maybe_cert)) { delete key_cert_pairs; return Nan::ThrowTypeError("cert_chain must be a Buffer"); } - key_cert_pairs[i].private_key = ::node::Buffer::Data( - maybe_key.ToLocalChecked()); - key_cert_pairs[i].cert_chain = ::node::Buffer::Data( - maybe_cert.ToLocalChecked()); + key_cert_pairs[i].private_key = ::node::Buffer::Data(maybe_key); + key_cert_pairs[i].cert_chain = ::node::Buffer::Data(maybe_cert); } grpc_server_credentials *creds = grpc_ssl_server_credentials_create( root_certs, key_cert_pairs, key_cert_pair_count, force_client_auth, NULL); diff --git a/src/node/health_check/health.js b/src/node/health_check/health.js index 84d7e0568e..1a2c036687 100644 --- a/src/node/health_check/health.js +++ b/src/node/health_check/health.js @@ -37,7 +37,8 @@ var grpc = require('../'); var _ = require('lodash'); -var health_proto = grpc.load(__dirname + '/health.proto'); +var health_proto = grpc.load(__dirname + + '/../../proto/grpc/health/v1alpha/health.proto'); var HealthClient = health_proto.grpc.health.v1alpha.Health; diff --git a/src/node/health_check/health.proto b/src/node/health_check/health.proto deleted file mode 100644 index 57f4aaa9c0..0000000000 --- a/src/node/health_check/health.proto +++ /dev/null @@ -1,49 +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. - -syntax = "proto3"; - -package grpc.health.v1alpha; - -message HealthCheckRequest { - string service = 1; -} - -message HealthCheckResponse { - enum ServingStatus { - UNKNOWN = 0; - SERVING = 1; - NOT_SERVING = 2; - } - ServingStatus status = 1; -} - -service Health { - rpc Check(HealthCheckRequest) returns (HealthCheckResponse); -} diff --git a/src/python/grpcio/grpc/_adapter/_c/types.c b/src/node/interop/async_delay_queue.js index 8dedf5902b..2bd3ca4da3 100644 --- a/src/python/grpcio/grpc/_adapter/_c/types.c +++ b/src/node/interop/async_delay_queue.js @@ -31,31 +31,49 @@ * */ -#include "grpc/_adapter/_c/types.h" +'use strict'; -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> +var _ = require('lodash'); -int pygrpc_module_add_types(PyObject *module) { - int i; - PyTypeObject *types[] = { - &pygrpc_CallCredentials_type, - &pygrpc_ChannelCredentials_type, - &pygrpc_ServerCredentials_type, - &pygrpc_CompletionQueue_type, - &pygrpc_Call_type, - &pygrpc_Channel_type, - &pygrpc_Server_type - }; - for (i = 0; i < sizeof(types)/sizeof(PyTypeObject *); ++i) { - if (PyType_Ready(types[i]) < 0) { - return -1; - } +/** + * This class represents a queue of callbacks that must happen sequentially, each + * with a specific delay after the previous event. + */ +function AsyncDelayQueue() { + this.queue = []; + + this.callback_pending = false; +} + +/** + * Run the next callback after its corresponding delay, if there are any + * remaining. + */ +AsyncDelayQueue.prototype.runNext = function() { + var next = this.queue.shift(); + var continueCallback = _.bind(this.runNext, this); + if (next) { + this.callback_pending = true; + setTimeout(function() { + next.callback(continueCallback); + }, next.delay); + } else { + this.callback_pending = false; } - for (i = 0; i < sizeof(types)/sizeof(PyTypeObject *); ++i) { - Py_INCREF(types[i]); - PyModule_AddObject(module, types[i]->tp_name, (PyObject *)types[i]); +}; + +/** + * Add a callback to be called with a specific delay after now or after the + * current last item in the queue or current pending callback, whichever is + * latest. + * @param {function(function())} callback The callback + * @param {Number} The delay to apply, in milliseconds + */ +AsyncDelayQueue.prototype.add = function(callback, delay) { + this.queue.push({callback: callback, delay: delay}); + if (!this.callback_pending) { + this.runNext(); } - return 0; -} +}; + +module.exports = AsyncDelayQueue; diff --git a/src/node/interop/interop_server.js b/src/node/interop/interop_server.js index 5321005c86..9526b5d183 100644 --- a/src/node/interop/interop_server.js +++ b/src/node/interop/interop_server.js @@ -36,6 +36,7 @@ var fs = require('fs'); var path = require('path'); var _ = require('lodash'); +var AsyncDelayQueue = require('./async_delay_queue'); var grpc = require('..'); var testProto = grpc.load({ root: __dirname + '/../../..', @@ -155,6 +156,7 @@ function handleStreamingInput(call, callback) { */ function handleStreamingOutput(call) { echoHeader(call); + var delay_queue = new AsyncDelayQueue(); var req = call.request; if (req.response_status) { var status = req.response_status; @@ -163,9 +165,15 @@ function handleStreamingOutput(call) { return; } _.each(req.response_parameters, function(resp_param) { - call.write({payload: getPayload(req.response_type, resp_param.size)}); + delay_queue.add(function(next) { + call.write({payload: getPayload(req.response_type, resp_param.size)}); + next(); + }, resp_param.interval_us); + }); + delay_queue.add(function(next) { + call.end(getEchoTrailer(call)); + next(); }); - call.end(getEchoTrailer(call)); } /** @@ -175,6 +183,7 @@ function handleStreamingOutput(call) { */ function handleFullDuplex(call) { echoHeader(call); + var delay_queue = new AsyncDelayQueue(); call.on('data', function(value) { if (value.response_status) { var status = value.response_status; @@ -183,11 +192,17 @@ function handleFullDuplex(call) { return; } _.each(value.response_parameters, function(resp_param) { - call.write({payload: getPayload(value.response_type, resp_param.size)}); + delay_queue.add(function(next) { + call.write({payload: getPayload(value.response_type, resp_param.size)}); + next(); + }, resp_param.interval_us); }); }); call.on('end', function() { - call.end(getEchoTrailer(call)); + delay_queue.add(function(next) { + call.end(getEchoTrailer(call)); + next(); + }); }); } diff --git a/src/node/performance/benchmark_client.js b/src/node/performance/benchmark_client.js new file mode 100644 index 0000000000..d97bdbbcaf --- /dev/null +++ b/src/node/performance/benchmark_client.js @@ -0,0 +1,336 @@ +/* + * + * 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. + * + */ + +/** + * Benchmark client module + * @module + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); +var util = require('util'); +var EventEmitter = require('events'); +var _ = require('lodash'); +var PoissonProcess = require('poisson-process'); +var Histogram = require('./histogram'); +var grpc = require('../../../'); +var serviceProto = grpc.load({ + root: __dirname + '/../../..', + file: 'test/proto/benchmarks/services.proto'}).grpc.testing; + +/** + * Create a buffer filled with size zeroes + * @param {number} size The length of the buffer + * @return {Buffer} The new buffer + */ +function zeroBuffer(size) { + var zeros = new Buffer(size); + zeros.fill(0); + return zeros; +} + +/** + * Convert a time difference, as returned by process.hrtime, to a number of + * nanoseconds. + * @param {Array.<number>} time_diff The time diff, represented as + * [seconds, nanoseconds] + * @return {number} The total number of nanoseconds + */ +function timeDiffToNanos(time_diff) { + return time_diff[0] * 1e9 + time_diff[1]; +} + +/** + * The BenchmarkClient class. Opens channels to servers and makes RPCs based on + * parameters from the driver, and records statistics about those RPCs. + * @param {Array.<string>} server_targets List of servers to connect to + * @param {number} channels The total number of channels to open + * @param {Object} histogram_params Options for setting up the histogram + * @param {Object=} security_params Options for TLS setup. If absent, don't use + * TLS + */ +function BenchmarkClient(server_targets, channels, histogram_params, + security_params) { + var options = {}; + var creds; + if (security_params) { + var ca_path; + if (security_params.use_test_ca) { + ca_path = path.join(__dirname, '../test/data/ca.pem'); + var ca_data = fs.readFileSync(ca_path); + creds = grpc.credentials.createSsl(ca_data); + } else { + creds = grpc.credentials.createSsl(); + } + if (security_params.server_host_override) { + var host_override = security_params.server_host_override; + options['grpc.ssl_target_name_override'] = host_override; + options['grpc.default_authority'] = host_override; + } + } else { + creds = grpc.credentials.createInsecure(); + } + + this.clients = []; + + for (var i = 0; i < channels; i++) { + this.clients[i] = new serviceProto.BenchmarkService( + server_targets[i % server_targets.length], creds, options); + } + + this.histogram = new Histogram(histogram_params.resolution, + histogram_params.max_possible); + + this.running = false; + + this.pending_calls = 0; +}; + +util.inherits(BenchmarkClient, EventEmitter); + +/** + * Start a closed-loop test. For each channel, start + * outstanding_rpcs_per_channel RPCs. Then, whenever an RPC finishes, start + * another one. + * @param {number} outstanding_rpcs_per_channel Number of RPCs to start per + * channel + * @param {string} rpc_type Which method to call. Should be 'UNARY' or + * 'STREAMING' + * @param {number} req_size The size of the payload to send with each request + * @param {number} resp_size The size of payload to request be sent in responses + */ +BenchmarkClient.prototype.startClosedLoop = function( + outstanding_rpcs_per_channel, rpc_type, req_size, resp_size) { + var self = this; + + self.running = true; + + self.last_wall_time = process.hrtime(); + + var makeCall; + + var argument = { + response_size: resp_size, + payload: { + body: zeroBuffer(req_size) + } + }; + + if (rpc_type == 'UNARY') { + makeCall = function(client) { + if (self.running) { + self.pending_calls++; + var start_time = process.hrtime(); + client.unaryCall(argument, function(error, response) { + if (error) { + self.emit('error', new Error('Client error: ' + error.message)); + self.running = false; + return; + } + var time_diff = process.hrtime(start_time); + self.histogram.add(timeDiffToNanos(time_diff)); + makeCall(client); + self.pending_calls--; + if ((!self.running) && self.pending_calls == 0) { + self.emit('finished'); + } + }); + } + }; + } else { + makeCall = function(client) { + if (self.running) { + self.pending_calls++; + var start_time = process.hrtime(); + var call = client.streamingCall(); + call.write(argument); + call.on('data', function() { + }); + call.on('end', function() { + var time_diff = process.hrtime(start_time); + self.histogram.add(timeDiffToNanos(time_diff)); + makeCall(client); + self.pending_calls--; + if ((!self.running) && self.pending_calls == 0) { + self.emit('finished'); + } + }); + call.on('error', function(error) { + self.emit('error', new Error('Client error: ' + error.message)); + self.running = false; + }); + } + }; + } + + _.each(self.clients, function(client) { + _.times(outstanding_rpcs_per_channel, function() { + makeCall(client); + }); + }); +}; + +/** + * Start a poisson test. For each channel, this initiates a number of Poisson + * processes equal to outstanding_rpcs_per_channel, where each Poisson process + * has the load parameter offered_load. + * @param {number} outstanding_rpcs_per_channel Number of RPCs to start per + * channel + * @param {string} rpc_type Which method to call. Should be 'UNARY' or + * 'STREAMING' + * @param {number} req_size The size of the payload to send with each request + * @param {number} resp_size The size of payload to request be sent in responses + * @param {number} offered_load The load parameter for the Poisson process + */ +BenchmarkClient.prototype.startPoisson = function( + outstanding_rpcs_per_channel, rpc_type, req_size, resp_size, offered_load) { + var self = this; + + self.running = true; + + self.last_wall_time = process.hrtime(); + + var makeCall; + + var argument = { + response_size: resp_size, + payload: { + body: zeroBuffer(req_size) + } + }; + + if (rpc_type == 'UNARY') { + makeCall = function(client, poisson) { + if (self.running) { + self.pending_calls++; + var start_time = process.hrtime(); + client.unaryCall(argument, function(error, response) { + if (error) { + self.emit('error', new Error('Client error: ' + error.message)); + self.running = false; + return; + } + var time_diff = process.hrtime(start_time); + self.histogram.add(timeDiffToNanos(time_diff)); + self.pending_calls--; + if ((!self.running) && self.pending_calls == 0) { + self.emit('finished'); + } + }); + } else { + poisson.stop(); + } + }; + } else { + makeCall = function(client, poisson) { + if (self.running) { + self.pending_calls++; + var start_time = process.hrtime(); + var call = client.streamingCall(); + call.write(argument); + call.on('data', function() { + }); + call.on('end', function() { + var time_diff = process.hrtime(start_time); + self.histogram.add(timeDiffToNanos(time_diff)); + self.pending_calls--; + if ((!self.running) && self.pending_calls == 0) { + self.emit('finished'); + } + }); + call.on('error', function(error) { + self.emit('error', new Error('Client error: ' + error.message)); + self.running = false; + }); + } else { + poisson.stop(); + } + }; + } + + var averageIntervalMs = (1 / offered_load) * 1000; + + _.each(self.clients, function(client) { + _.times(outstanding_rpcs_per_channel, function() { + var p = PoissonProcess.create(averageIntervalMs, function() { + makeCall(client, p); + }); + p.start(); + }); + }); +}; + +/** + * Return curent statistics for the client. If reset is set, restart + * statistic collection. + * @param {boolean} reset Indicates that statistics should be reset + * @return {object} Client statistics + */ +BenchmarkClient.prototype.mark = function(reset) { + var wall_time_diff = process.hrtime(this.last_wall_time); + var histogram = this.histogram; + if (reset) { + this.last_wall_time = process.hrtime(); + this.histogram = new Histogram(histogram.resolution, + histogram.max_possible); + } + + return { + latencies: { + bucket: histogram.getContents(), + min_seen: histogram.minimum(), + max_seen: histogram.maximum(), + sum: histogram.getSum(), + sum_of_squares: histogram.sumOfSquares(), + count: histogram.getCount() + }, + time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9, + // Not sure how to measure these values + time_user: 0, + time_system: 0 + }; +}; + +/** + * Stop the clients. + * @param {function} callback Called when the clients have finished shutting + * down + */ +BenchmarkClient.prototype.stop = function(callback) { + this.running = false; + this.on('finished', callback); +}; + +module.exports = BenchmarkClient; diff --git a/src/node/performance/benchmark_server.js b/src/node/performance/benchmark_server.js new file mode 100644 index 0000000000..ac96fc5edb --- /dev/null +++ b/src/node/performance/benchmark_server.js @@ -0,0 +1,162 @@ +/* + * + * 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. + * + */ + +/** + * Benchmark server module + * @module + */ + +'use strict'; + +var fs = require('fs'); +var path = require('path'); + +var grpc = require('../../../'); +var serviceProto = grpc.load({ + root: __dirname + '/../../..', + file: 'test/proto/benchmarks/services.proto'}).grpc.testing; + +/** + * Create a buffer filled with size zeroes + * @param {number} size The length of the buffer + * @return {Buffer} The new buffer + */ +function zeroBuffer(size) { + var zeros = new Buffer(size); + zeros.fill(0); + return zeros; +} + +/** + * Handler for the unary benchmark method. Simply responds with a payload + * containing the requested number of zero bytes. + * @param {Call} call The call object to be handled + * @param {function} callback The callback to call with the response + */ +function unaryCall(call, callback) { + var req = call.request; + var payload = {body: zeroBuffer(req.response_size)}; + callback(null, {payload: payload}); +} + +/** + * Handler for the streaming benchmark method. Simply responds to each request + * with a payload containing the requested number of zero bytes. + * @param {Call} call The call object to be handled + */ +function streamingCall(call) { + call.on('data', function(value) { + var payload = {body: zeroBuffer(value.repsonse_size)}; + call.write({payload: payload}); + }); + call.on('end', function() { + call.end(); + }); +} + +/** + * BenchmarkServer class. Constructed based on parameters from the driver and + * stores statistics. + * @param {string} host The host to serve on + * @param {number} port The port to listen to + * @param {tls} Indicates whether TLS should be used + */ +function BenchmarkServer(host, port, tls) { + var server_creds; + var host_override; + if (tls) { + var key_path = path.join(__dirname, '../test/data/server1.key'); + var pem_path = path.join(__dirname, '../test/data/server1.pem'); + + var key_data = fs.readFileSync(key_path); + var pem_data = fs.readFileSync(pem_path); + server_creds = grpc.ServerCredentials.createSsl(null, + [{private_key: key_data, + cert_chain: pem_data}]); + } else { + server_creds = grpc.ServerCredentials.createInsecure(); + } + + var server = new grpc.Server(); + this.port = server.bind(host + ':' + port, server_creds); + server.addProtoService(serviceProto.BenchmarkService.service, { + unaryCall: unaryCall, + streamingCall: streamingCall + }); + this.server = server; +} + +/** + * Start the benchmark server. + */ +BenchmarkServer.prototype.start = function() { + this.server.start(); + this.last_wall_time = process.hrtime(); +}; + +/** + * Return the port number that the server is bound to. + * @return {Number} The port number + */ +BenchmarkServer.prototype.getPort = function() { + return this.port; +}; + +/** + * Return current statistics for the server. If reset is set, restart + * statistic collection. + * @param {boolean} reset Indicates that statistics should be reset + * @return {object} Server statistics + */ +BenchmarkServer.prototype.mark = function(reset) { + var wall_time_diff = process.hrtime(this.last_wall_time); + if (reset) { + this.last_wall_time = process.hrtime(); + } + return { + time_elapsed: wall_time_diff[0] + wall_time_diff[1] / 1e9, + // Not sure how to measure these values + time_user: 0, + time_system: 0 + }; +}; + +/** + * Stop the server. + * @param {function} callback Called when the server has finished shutting down + */ +BenchmarkServer.prototype.stop = function(callback) { + this.server.tryShutdown(callback); +}; + +module.exports = BenchmarkServer; diff --git a/src/node/performance/histogram.js b/src/node/performance/histogram.js new file mode 100644 index 0000000000..204d7d47da --- /dev/null +++ b/src/node/performance/histogram.js @@ -0,0 +1,180 @@ +/* + * + * 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. + * + */ + +/** + * Histogram module. Exports the Histogram class + * @module + */ + +'use strict'; + +/** + * Histogram class. Collects data and exposes a histogram and other statistics. + * This data structure is taken directly from src/core/support/histogram.c, but + * pared down to the statistics needed for client stats in + * test/proto/benchmarks/stats.proto. + * @constructor + * @param {number} resolution The histogram's bucket resolution. Must be positive + * @param {number} max_possible The maximum allowed value. Must be greater than 1 + */ +function Histogram(resolution, max_possible) { + this.resolution = resolution; + this.max_possible = max_possible; + + this.sum = 0; + this.sum_of_squares = 0; + this.multiplier = 1 + resolution; + this.count = 0; + this.min_seen = max_possible; + this.max_seen = 0; + this.buckets = []; + for (var i = 0; i < this.bucketFor(max_possible) + 1; i++) { + this.buckets[i] = 0; + } +} + +/** + * Get the bucket index for a given value. + * @param {number} value The value to check + * @return {number} The bucket index + */ +Histogram.prototype.bucketFor = function(value) { + return Math.floor(Math.log(value) / Math.log(this.multiplier)); +}; + +/** + * Get the minimum value for a given bucket index + * @param {number} The bucket index to check + * @return {number} The minimum value for that bucket + */ +Histogram.prototype.bucketStart = function(index) { + return Math.pow(this.multiplier, index); +}; + +/** + * Add a value to the histogram. This updates all statistics with the new + * value. Those statistics should not be modified except with this function + * @param {number} value The value to add + */ +Histogram.prototype.add = function(value) { + // Ensure value is a number + value = +value; + this.sum += value; + this.sum_of_squares += value * value; + this.count++; + if (value < this.min_seen) { + this.min_seen = value; + } + if (value > this.max_seen) { + this.max_seen = value; + } + this.buckets[this.bucketFor(value)]++; +}; + +/** + * Get the mean of all added values + * @return {number} The mean + */ +Histogram.prototype.mean = function() { + return this.sum / this.count; +}; + +/** + * Get the variance of all added values. Used to calulate the standard deviation + * @return {number} The variance + */ +Histogram.prototype.variance = function() { + if (this.count == 0) { + return 0; + } + return (this.sum_of_squares * this.count - this.sum * this.sum) / + (this.count * this.count); +}; + +/** + * Get the standard deviation of all added values + * @return {number} The standard deviation + */ +Histogram.prototype.stddev = function() { + return Math.sqrt(this.variance); +}; + +/** + * Get the maximum among all added values + * @return {number} The maximum + */ +Histogram.prototype.maximum = function() { + return this.max_seen; +}; + +/** + * Get the minimum among all added values + * @return {number} The minimum + */ +Histogram.prototype.minimum = function() { + return this.min_seen; +}; + +/** + * Get the number of all added values + * @return {number} The count + */ +Histogram.prototype.getCount = function() { + return this.count; +}; + +/** + * Get the sum of all added values + * @return {number} The sum + */ +Histogram.prototype.getSum = function() { + return this.sum; +}; + +/** + * Get the sum of squares of all added values + * @return {number} The sum of squares + */ +Histogram.prototype.sumOfSquares = function() { + return this.sum_of_squares; +}; + +/** + * Get the raw histogram as a list of bucket sizes + * @return {Array.<number>} The buckets + */ +Histogram.prototype.getContents = function() { + return this.buckets; +}; + +module.exports = Histogram; diff --git a/src/node/performance/perf_test.js b/src/node/performance/perf_test.js deleted file mode 100644 index fe51e4a603..0000000000 --- a/src/node/performance/perf_test.js +++ /dev/null @@ -1,119 +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. - * - */ - -'use strict'; - -var grpc = require('..'); -var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing; -var _ = require('lodash'); -var interop_server = require('../interop/interop_server.js'); - -function runTest(iterations, callback) { - var testServer = interop_server.getServer(0, false); - testServer.server.start(); - var client = new testProto.TestService('localhost:' + testServer.port, - grpc.credentials.createInsecure()); - - function runIterations(finish) { - var start = process.hrtime(); - var intervals = []; - function next(i) { - if (i >= iterations) { - testServer.server.shutdown(); - var totalDiff = process.hrtime(start); - finish({ - total: totalDiff[0] * 1000000 + totalDiff[1] / 1000, - intervals: intervals - }); - } else{ - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + 3); - var startTime = process.hrtime(); - client.emptyCall({}, function(err, resp) { - var timeDiff = process.hrtime(startTime); - intervals[i] = timeDiff[0] * 1000000 + timeDiff[1] / 1000; - next(i+1); - }, {}, {deadline: deadline}); - } - } - next(0); - } - - function warmUp(num) { - var pending = num; - function startCall() { - client.emptyCall({}, function(err, resp) { - pending--; - if (pending === 0) { - runIterations(callback); - } - }); - } - for (var i = 0; i < num; i++) { - startCall(); - } - } - warmUp(100); -} - -function percentile(arr, pct) { - if (pct > 99) { - pct = 99; - } - if (pct < 0) { - pct = 0; - } - var index = Math.floor(arr.length * pct / 100); - return arr[index]; -} - -if (require.main === module) { - var count; - if (process.argv.length >= 3) { - count = process.argv[2]; - } else { - count = 100; - } - runTest(count, function(results) { - var sorted_intervals = _.sortBy(results.intervals, _.identity); - console.log('count:', count); - console.log('total time:', results.total, 'us'); - console.log('median:', percentile(sorted_intervals, 50), 'us'); - console.log('90th percentile:', percentile(sorted_intervals, 90), 'us'); - console.log('95th percentile:', percentile(sorted_intervals, 95), 'us'); - console.log('99th percentile:', percentile(sorted_intervals, 99), 'us'); - console.log('QPS:', (count / results.total) * 1000000); - }); -} - -module.exports = runTest; diff --git a/src/node/performance/qps_test.js b/src/node/performance/qps_test.js deleted file mode 100644 index 491f47364c..0000000000 --- a/src/node/performance/qps_test.js +++ /dev/null @@ -1,137 +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. - * - */ - -/** - * This script runs a QPS test. It sends requests for a specified length of time - * with a specified number pending at any one time. It then outputs the measured - * QPS. Usage: - * node qps_test.js [--concurrent=count] [--time=seconds] - * concurrent defaults to 100 and time defaults to 10 - */ - -'use strict'; - -var async = require('async'); -var parseArgs = require('minimist'); - -var grpc = require('..'); -var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing; -var interop_server = require('../interop/interop_server.js'); - -/** - * Runs the QPS test. Sends requests constantly for the given number of seconds, - * and keeps concurrent_calls requests pending at all times. When the test ends, - * the callback is called with the number of calls that completed within the - * time limit. - * @param {number} concurrent_calls The number of calls to have pending - * simultaneously - * @param {number} seconds The number of seconds to run the test for - * @param {function(Error, number)} callback Callback for test completion - */ -function runTest(concurrent_calls, seconds, callback) { - var testServer = interop_server.getServer(0, false); - testServer.server.start(); - var client = new testProto.TestService('localhost:' + testServer.port, - grpc.credentials.createInsecure()); - - var warmup_num = 100; - - /** - * Warms up the client to avoid counting startup time in the test result - * @param {function(Error)} callback Called when warmup is complete - */ - function warmUp(callback) { - var pending = warmup_num; - function startCall() { - client.emptyCall({}, function(err, resp) { - if (err) { - callback(err); - return; - } - pending--; - if (pending === 0) { - callback(null); - } - }); - } - for (var i = 0; i < warmup_num; i++) { - startCall(); - } - } - /** - * Run the QPS test. Starts concurrent_calls requests, then starts a new - * request whenever one completes until time runs out. - * @param {function(Error, number)} callback Called when the test is complete. - * The second argument is the number of calls that finished within the - * time limit - */ - function run(callback) { - var running = 0; - var count = 0; - var start = process.hrtime(); - function responseCallback(err, resp) { - if (process.hrtime(start)[0] < seconds) { - count += 1; - client.emptyCall({}, responseCallback); - } else { - running -= 1; - if (running <= 0) { - callback(null, count); - } - } - } - for (var i = 0; i < concurrent_calls; i++) { - running += 1; - client.emptyCall({}, responseCallback); - } - } - async.waterfall([warmUp, run], function(err, count) { - testServer.server.shutdown(); - callback(err, count); - }); -} - -if (require.main === module) { - var argv = parseArgs(process.argv.slice(2), { - default: {'concurrent': 100, - 'time': 10} - }); - runTest(argv.concurrent, argv.time, function(err, count) { - if (err) { - throw err; - } - console.log('Concurrent calls:', argv.concurrent); - console.log('Time:', argv.time, 'seconds'); - console.log('QPS:', (count/argv.time)); - }); -} diff --git a/src/python/grpcio/grpc/_adapter/_c/module.c b/src/node/performance/worker_server.js index 9b93b051f6..43b86e5ecf 100644 --- a/src/python/grpcio/grpc/_adapter/_c/module.c +++ b/src/node/performance/worker_server.js @@ -31,37 +31,33 @@ * */ -#include <stdlib.h> - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> - -#include "grpc/_adapter/_c/types.h" - -static PyMethodDef c_methods[] = { - {NULL} -}; - -PyMODINIT_FUNC init_c(void) { - PyObject *module; - - module = Py_InitModule3("_c", c_methods, - "Wrappings of C structures and functions."); - - if (pygrpc_module_add_types(module) < 0) { - return; - } - - if (PyModule_AddStringConstant( - module, "PRIMARY_USER_AGENT_KEY", - GRPC_ARG_PRIMARY_USER_AGENT_STRING) < 0) { - return; - } +'use strict'; + +var worker_service_impl = require('./worker_service_impl'); + +var grpc = require('../../../'); +var serviceProto = grpc.load({ + root: __dirname + '/../../..', + file: 'test/proto/benchmarks/services.proto'}).grpc.testing; + +function runServer(port) { + var server_creds = grpc.ServerCredentials.createInsecure(); + var server = new grpc.Server(); + server.addProtoService(serviceProto.WorkerService.service, + worker_service_impl); + var address = '0.0.0.0:' + port; + server.bind(address, server_creds); + server.start(); + return server; +} - /* GRPC maintains an internal counter of how many times it has been - initialized and handles multiple pairs of grpc_init()/grpc_shutdown() - invocations accordingly. */ - grpc_init(); - atexit(&grpc_shutdown); +if (require.main === module) { + Error.stackTraceLimit = Infinity; + var parseArgs = require('minimist'); + var argv = parseArgs(process.argv, { + string: ['driver_port'] + }); + runServer(argv.driver_port); } + +exports.runServer = runServer; diff --git a/src/node/performance/worker_service_impl.js b/src/node/performance/worker_service_impl.js new file mode 100644 index 0000000000..8841ae13c3 --- /dev/null +++ b/src/node/performance/worker_service_impl.js @@ -0,0 +1,132 @@ +/* + * + * 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. + * + */ + +'use strict'; + +var BenchmarkClient = require('./benchmark_client'); +var BenchmarkServer = require('./benchmark_server'); + +exports.runClient = function runClient(call) { + var client; + call.on('data', function(request) { + var stats; + switch (request.argtype) { + case 'setup': + var setup = request.setup; + client = new BenchmarkClient(setup.server_targets, + setup.client_channels, + setup.histogram_params, + setup.security_params); + client.on('error', function(error) { + call.emit('error', error); + }); + switch (setup.load_params.load) { + case 'closed_loop': + client.startClosedLoop(setup.outstanding_rpcs_per_channel, + setup.rpc_type, + setup.payload_config.simple_params.req_size, + setup.payload_config.simple_params.resp_size); + break; + case 'poisson': + client.startPoisson(setup.outstanding_rpcs_per_channel, + setup.rpc_type, setup.payload_config.req_size, + setup.payload_config.resp_size, + setup.load_params.poisson.offered_load); + break; + default: + call.emit('error', new Error('Unsupported LoadParams type' + + setup.load_params.load)); + } + stats = client.mark(); + call.write({ + stats: stats + }); + break; + case 'mark': + if (client) { + stats = client.mark(request.mark.reset); + call.write({ + stats: stats + }); + } else { + call.emit('error', new Error('Got Mark before ClientConfig')); + } + break; + default: + throw new Error('Nonexistent client argtype option: ' + request.argtype); + } + }); + call.on('end', function() { + client.stop(function() { + call.end(); + }); + }); +}; + +exports.runServer = function runServer(call) { + var server; + call.on('data', function(request) { + var stats; + switch (request.argtype) { + case 'setup': + server = new BenchmarkServer(request.setup.host, request.setup.port, + request.setup.security_params); + server.start(); + stats = server.mark(); + call.write({ + stats: stats, + port: server.getPort() + }); + break; + case 'mark': + if (server) { + stats = server.mark(request.mark.reset); + call.write({ + stats: stats, + port: server.getPort(), + cores: 1 + }); + } else { + call.emit('error', new Error('Got Mark before ServerConfig')); + } + break; + default: + throw new Error('Nonexistent server argtype option'); + } + }); + call.on('end', function() { + server.stop(function() { + call.end(); + }); + }); +}; diff --git a/src/node/src/credentials.js b/src/node/src/credentials.js index ff10a22e6a..dcbfac18f4 100644 --- a/src/node/src/credentials.js +++ b/src/node/src/credentials.js @@ -91,7 +91,7 @@ exports.createSsl = ChannelCredentials.createSsl; */ exports.createFromMetadataGenerator = function(metadata_generator) { return CallCredentials.createFromPlugin(function(service_url, callback) { - metadata_generator(service_url, function(error, metadata) { + metadata_generator({service_url: service_url}, function(error, metadata) { var code = grpc.status.OK; var message = ''; if (error) { @@ -114,7 +114,8 @@ exports.createFromMetadataGenerator = function(metadata_generator) { * @return {CallCredentials} The resulting credentials object */ exports.createFromGoogleCredential = function(google_credential) { - return exports.createFromMetadataGenerator(function(service_url, callback) { + return exports.createFromMetadataGenerator(function(auth_context, callback) { + var service_url = auth_context.service_url; google_credential.getRequestMetadata(service_url, function(err, header) { if (err) { callback(err); diff --git a/src/node/src/server.js b/src/node/src/server.js index d1fb627e6c..ceaa9f5d1f 100644 --- a/src/node/src/server.js +++ b/src/node/src/server.js @@ -101,28 +101,6 @@ function handleError(call, error) { } /** - * Wait for the client to close, then emit a cancelled event if the client - * cancelled. - * @access private - * @param {grpc.Call} call The call object to wait on - * @param {EventEmitter} emitter The event emitter to emit the cancelled event - * on - */ -function waitForCancel(call, emitter) { - var cancel_batch = {}; - cancel_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; - call.startBatch(cancel_batch, function(err, result) { - if (err) { - emitter.emit('error', err); - } - if (result.cancelled) { - emitter.cancelled = true; - emitter.emit('cancelled'); - } - }); -} - -/** * Send a response to a unary or client streaming call. * @access private * @param {grpc.Call} call The call to respond on @@ -258,6 +236,13 @@ function setUpReadable(stream, deserialize) { }); } +util.inherits(ServerUnaryCall, EventEmitter); + +function ServerUnaryCall(call) { + EventEmitter.call(this); + this.call = call; +} + util.inherits(ServerWritableStream, Writable); /** @@ -311,33 +296,6 @@ function _write(chunk, encoding, callback) { ServerWritableStream.prototype._write = _write; -/** - * Send the initial metadata for a writable stream. - * @param {Metadata} responseMetadata Metadata to send - */ -function sendMetadata(responseMetadata) { - /* jshint validthis: true */ - var self = this; - if (!this.call.metadataSent) { - this.call.metadataSent = true; - var batch = []; - batch[grpc.opType.SEND_INITIAL_METADATA] = - responseMetadata._getCoreRepresentation(); - this.call.startBatch(batch, function(err) { - if (err) { - self.emit('error', err); - return; - } - }); - } -} - -/** - * @inheritdoc - * @alias module:src/server~ServerWritableStream#sendMetadata - */ -ServerWritableStream.prototype.sendMetadata = sendMetadata; - util.inherits(ServerReadableStream, Readable); /** @@ -427,6 +385,31 @@ function ServerDuplexStream(call, serialize, deserialize) { ServerDuplexStream.prototype._read = _read; ServerDuplexStream.prototype._write = _write; + +/** + * Send the initial metadata for a writable stream. + * @param {Metadata} responseMetadata Metadata to send + */ +function sendMetadata(responseMetadata) { + /* jshint validthis: true */ + var self = this; + if (!this.call.metadataSent) { + this.call.metadataSent = true; + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = + responseMetadata._getCoreRepresentation(); + this.call.startBatch(batch, function(err) { + if (err) { + self.emit('error', err); + return; + } + }); + } +} + +ServerUnaryCall.prototype.sendMetadata = sendMetadata; +ServerWritableStream.prototype.sendMetadata = sendMetadata; +ServerReadableStream.prototype.sendMetadata = sendMetadata; ServerDuplexStream.prototype.sendMetadata = sendMetadata; /** @@ -438,11 +421,37 @@ function getPeer() { return this.call.getPeer(); } +ServerUnaryCall.prototype.getPeer = getPeer; ServerReadableStream.prototype.getPeer = getPeer; ServerWritableStream.prototype.getPeer = getPeer; ServerDuplexStream.prototype.getPeer = getPeer; /** + * Wait for the client to close, then emit a cancelled event if the client + * cancelled. + */ +function waitForCancel() { + /* jshint validthis: true */ + var self = this; + var cancel_batch = {}; + cancel_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + self.call.startBatch(cancel_batch, function(err, result) { + if (err) { + self.emit('error', err); + } + if (result.cancelled) { + self.cancelled = true; + self.emit('cancelled'); + } + }); +} + +ServerUnaryCall.prototype.waitForCancel = waitForCancel; +ServerReadableStream.prototype.waitForCancel = waitForCancel; +ServerWritableStream.prototype.waitForCancel = waitForCancel; +ServerDuplexStream.prototype.waitForCancel = waitForCancel; + +/** * Fully handle a unary call * @access private * @param {grpc.Call} call The call to handle @@ -450,25 +459,12 @@ ServerDuplexStream.prototype.getPeer = getPeer; * @param {Metadata} metadata Metadata from the client */ function handleUnary(call, handler, metadata) { - var emitter = new EventEmitter(); - emitter.sendMetadata = function(responseMetadata) { - if (!call.metadataSent) { - call.metadataSent = true; - var batch = {}; - batch[grpc.opType.SEND_INITIAL_METADATA] = - responseMetadata._getCoreRepresentation(); - call.startBatch(batch, function() {}); - } - }; - emitter.getPeer = function() { - return call.getPeer(); - }; + var emitter = new ServerUnaryCall(call); emitter.on('error', function(error) { handleError(call, error); }); emitter.metadata = metadata; - waitForCancel(call, emitter); - emitter.call = call; + emitter.waitForCancel(); var batch = {}; batch[grpc.opType.RECV_MESSAGE] = true; call.startBatch(batch, function(err, result) { @@ -508,7 +504,7 @@ function handleUnary(call, handler, metadata) { */ function handleServerStreaming(call, handler, metadata) { var stream = new ServerWritableStream(call, handler.serialize); - waitForCancel(call, stream); + stream.waitForCancel(); stream.metadata = metadata; var batch = {}; batch[grpc.opType.RECV_MESSAGE] = true; @@ -537,19 +533,10 @@ function handleServerStreaming(call, handler, metadata) { */ function handleClientStreaming(call, handler, metadata) { var stream = new ServerReadableStream(call, handler.deserialize); - stream.sendMetadata = function(responseMetadata) { - if (!call.metadataSent) { - call.metadataSent = true; - var batch = {}; - batch[grpc.opType.SEND_INITIAL_METADATA] = - responseMetadata._getCoreRepresentation(); - call.startBatch(batch, function() {}); - } - }; stream.on('error', function(error) { handleError(call, error); }); - waitForCancel(call, stream); + stream.waitForCancel(); stream.metadata = metadata; handler.func(stream, function(err, value, trailer, flags) { stream.terminate(); @@ -574,7 +561,7 @@ function handleClientStreaming(call, handler, metadata) { function handleBidiStreaming(call, handler, metadata) { var stream = new ServerDuplexStream(call, handler.serialize, handler.deserialize); - waitForCancel(call, stream); + stream.waitForCancel(); stream.metadata = metadata; handler.func(stream); } diff --git a/src/node/test/async_test.js b/src/node/test/async_test.js index 0af63c379e..c46e745116 100644 --- a/src/node/test/async_test.js +++ b/src/node/test/async_test.js @@ -36,7 +36,7 @@ var assert = require('assert'); var grpc = require('..'); -var math = grpc.load(__dirname + '/math/math.proto').math; +var math = grpc.load(__dirname + '/../../proto/math/math.proto').math; /** diff --git a/src/node/test/credentials_test.js b/src/node/test/credentials_test.js index 647f648ca9..294600c85a 100644 --- a/src/node/test/credentials_test.js +++ b/src/node/test/credentials_test.js @@ -76,6 +76,146 @@ var fakeFailingGoogleCredentials = { } }; +var key_data, pem_data, ca_data; + +before(function() { + var key_path = path.join(__dirname, './data/server1.key'); + var pem_path = path.join(__dirname, './data/server1.pem'); + var ca_path = path.join(__dirname, '../test/data/ca.pem'); + key_data = fs.readFileSync(key_path); + pem_data = fs.readFileSync(pem_path); + ca_data = fs.readFileSync(ca_path); +}); + +describe('channel credentials', function() { + describe('#createSsl', function() { + it('works with no arguments', function() { + var creds; + assert.doesNotThrow(function() { + creds = grpc.credentials.createSsl(); + }); + assert.notEqual(creds, null); + }); + it('works with just one Buffer argument', function() { + var creds; + assert.doesNotThrow(function() { + creds = grpc.credentials.createSsl(ca_data); + }); + assert.notEqual(creds, null); + }); + it('works with 3 Buffer arguments', function() { + var creds; + assert.doesNotThrow(function() { + creds = grpc.credentials.createSsl(ca_data, key_data, pem_data); + }); + assert.notEqual(creds, null); + }); + it('works if the first argument is null', function() { + var creds; + assert.doesNotThrow(function() { + creds = grpc.credentials.createSsl(null, key_data, pem_data); + }); + assert.notEqual(creds, null); + }); + it('fails if the first argument is a non-Buffer value', function() { + assert.throws(function() { + grpc.credentials.createSsl('test'); + }, TypeError); + }); + it('fails if the second argument is a non-Buffer value', function() { + assert.throws(function() { + grpc.credentials.createSsl(null, 'test', pem_data); + }, TypeError); + }); + it('fails if the third argument is a non-Buffer value', function() { + assert.throws(function() { + grpc.credentials.createSsl(null, key_data, 'test'); + }, TypeError); + }); + it('fails if only 1 of the last 2 arguments is provided', function() { + assert.throws(function() { + grpc.credentials.createSsl(null, key_data); + }); + assert.throws(function() { + grpc.credentials.createSsl(null, null, pem_data); + }); + }); + }); +}); + +describe('server credentials', function() { + describe('#createSsl', function() { + it('accepts a buffer and array as the first 2 arguments', function() { + var creds; + assert.doesNotThrow(function() { + creds = grpc.ServerCredentials.createSsl(ca_data, []); + }); + assert.notEqual(creds, null); + }); + it('accepts a boolean as the third argument', function() { + var creds; + assert.doesNotThrow(function() { + creds = grpc.ServerCredentials.createSsl(ca_data, [], true); + }); + assert.notEqual(creds, null); + }); + it('accepts an object with two buffers in the second argument', function() { + var creds; + assert.doesNotThrow(function() { + creds = grpc.ServerCredentials.createSsl(null, + [{private_key: key_data, + cert_chain: pem_data}]); + }); + assert.notEqual(creds, null); + }); + it('accepts multiple objects in the second argument', function() { + var creds; + assert.doesNotThrow(function() { + creds = grpc.ServerCredentials.createSsl(null, + [{private_key: key_data, + cert_chain: pem_data}, + {private_key: key_data, + cert_chain: pem_data}]); + }); + assert.notEqual(creds, null); + }); + it('fails if the second argument is not an Array', function() { + assert.throws(function() { + grpc.ServerCredentials.createSsl(ca_data, 'test'); + }, TypeError); + }); + it('fails if the first argument is a non-Buffer value', function() { + assert.throws(function() { + grpc.ServerCredentials.createSsl('test', []); + }, TypeError); + }); + it('fails if the third argument is a non-boolean value', function() { + assert.throws(function() { + grpc.ServerCredentials.createSsl(ca_data, [], 'test'); + }, TypeError); + }); + it('fails if the array elements are not objects', function() { + assert.throws(function() { + grpc.ServerCredentials.createSsl(ca_data, 'test'); + }, TypeError); + }); + it('fails if the object does not have a Buffer private_key', function() { + assert.throws(function() { + grpc.ServerCredentials.createSsl(null, + [{private_key: 'test', + cert_chain: pem_data}]); + }, TypeError); + }); + it('fails if the object does not have a Buffer cert_chain', function() { + assert.throws(function() { + grpc.ServerCredentials.createSsl(null, + [{private_key: key_data, + cert_chain: 'test'}]); + }, TypeError); + }); + }); +}); + describe('client credentials', function() { var Client; var server; @@ -109,20 +249,13 @@ describe('client credentials', function() { }); } }); - var key_path = path.join(__dirname, './data/server1.key'); - var pem_path = path.join(__dirname, './data/server1.pem'); - var key_data = fs.readFileSync(key_path); - var pem_data = fs.readFileSync(pem_path); var creds = grpc.ServerCredentials.createSsl(null, [{private_key: key_data, cert_chain: pem_data}]); - //creds = grpc.ServerCredentials.createInsecure(); port = server.bind('localhost:0', creds); server.start(); Client = proto.TestService; - var ca_path = path.join(__dirname, '../test/data/ca.pem'); - var ca_data = fs.readFileSync(ca_path); client_ssl_creds = grpc.credentials.createSsl(ca_data); var host_override = 'foo.test.google.fr'; client_options['grpc.ssl_target_name_override'] = host_override; diff --git a/src/node/test/math/math.proto b/src/node/test/math/math.proto deleted file mode 100644 index 311e148c02..0000000000 --- a/src/node/test/math/math.proto +++ /dev/null @@ -1,80 +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. - -syntax = "proto3"; - -package math; - -message DivArgs { - int64 dividend = 1; - int64 divisor = 2; -} - -message DivReply { - int64 quotient = 1; - int64 remainder = 2; -} - -message FibArgs { - int64 limit = 1; -} - -message Num { - int64 num = 1; -} - -message FibReply { - int64 count = 1; -} - -service Math { - // Div divides args.dividend by args.divisor and returns the quotient and - // remainder. - rpc Div (DivArgs) returns (DivReply) { - } - - // DivMany accepts an arbitrary number of division args from the client stream - // and sends back the results in the reply stream. The stream continues until - // the client closes its end; the server does the same after sending all the - // replies. The stream ends immediately if either end aborts. - rpc DivMany (stream DivArgs) returns (stream DivReply) { - } - - // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib - // generates up to limit numbers; otherwise it continues until the call is - // canceled. Unlike Fib above, Fib has no final FibReply. - rpc Fib (FibArgs) returns (stream Num) { - } - - // Sum sums a stream of numbers, returning the final result once the stream - // is closed. - rpc Sum (stream Num) returns (Num) { - } -} diff --git a/src/node/test/math/math_server.js b/src/node/test/math/math_server.js index 9d06596f3d..9f67c52ab0 100644 --- a/src/node/test/math/math_server.js +++ b/src/node/test/math/math_server.js @@ -34,7 +34,8 @@ 'use strict'; var grpc = require('../..'); -var math = grpc.load(__dirname + '/math.proto').math; +var math = grpc.load(__dirname + '/../../../proto/math/math.proto').math; + /** * Server function for division. Provides the /Math/DivMany and /Math/Div diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js index 6361d97857..3d44610536 100644 --- a/src/node/test/math_client_test.js +++ b/src/node/test/math_client_test.js @@ -36,7 +36,7 @@ var assert = require('assert'); var grpc = require('..'); -var math = grpc.load(__dirname + '/math/math.proto').math; +var math = grpc.load(__dirname + '/../../proto/math/math.proto').math; /** * Client to use to make requests to a running server. diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js index 999a183b3c..592f47e145 100644 --- a/src/node/test/server_test.js +++ b/src/node/test/server_test.js @@ -45,9 +45,30 @@ describe('server', function() { new grpc.Server(); }); }); - it('should work with an empty list argument', function() { + it('should work with an empty object argument', function() { assert.doesNotThrow(function() { - new grpc.Server([]); + new grpc.Server({}); + }); + }); + it('should work without the new keyword', function() { + var server; + assert.doesNotThrow(function() { + server = grpc.Server(); + }); + assert(server instanceof grpc.Server); + }); + it('should only accept objects with string or int values', function() { + assert.doesNotThrow(function() { + new grpc.Server({'key' : 'value'}); + }); + assert.doesNotThrow(function() { + new grpc.Server({'key' : 5}); + }); + assert.throws(function() { + new grpc.Server({'key' : null}); + }); + assert.throws(function() { + new grpc.Server({'key' : new Date()}); }); }); }); diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js index 39673e4e05..fc765ed731 100644 --- a/src/node/test/surface_test.js +++ b/src/node/test/surface_test.js @@ -41,7 +41,8 @@ var ProtoBuf = require('protobufjs'); var grpc = require('..'); -var math_proto = ProtoBuf.loadProtoFile(__dirname + '/math/math.proto'); +var math_proto = ProtoBuf.loadProtoFile(__dirname + + '/../../proto/math/math.proto'); var mathService = math_proto.lookup('math.Math'); @@ -312,6 +313,54 @@ describe('Generic client and server', function() { }); }); }); +describe('Server-side getPeer', function() { + function toString(val) { + return val.toString(); + } + function toBuffer(str) { + return new Buffer(str); + } + var string_service_attrs = { + 'getPeer' : { + path: '/string/getPeer', + requestStream: false, + responseStream: false, + requestSerialize: toBuffer, + requestDeserialize: toString, + responseSerialize: toBuffer, + responseDeserialize: toString + } + }; + var client; + var server; + before(function() { + server = new grpc.Server(); + server.addService(string_service_attrs, { + getPeer: function(call, callback) { + try { + callback(null, call.getPeer()); + } catch (e) { + call.emit('error', e); + } + } + }); + var port = server.bind('localhost:0', server_insecure_creds); + server.start(); + var Client = grpc.makeGenericClientConstructor(string_service_attrs); + client = new Client('localhost:' + port, + grpc.credentials.createInsecure()); + }); + after(function() { + server.forceShutdown(); + }); + it('should respond with a string representing the client', function(done) { + client.getPeer('', function(err, response) { + assert.ifError(err); + // We don't expect a specific value, just that it worked without error + done(); + }); + }); +}); describe('Echo metadata', function() { var client; var server; diff --git a/src/objective-c/BoringSSL.podspec b/src/objective-c/BoringSSL.podspec new file mode 100644 index 0000000000..f5b738203d --- /dev/null +++ b/src/objective-c/BoringSSL.podspec @@ -0,0 +1,1428 @@ +# BoringSSL CocoaPods podspec + +# 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. + +Pod::Spec.new do |s| + s.name = 'BoringSSL' + s.version = '1.0' + s.summary = 'BoringSSL is a fork of OpenSSL that is designed to meet Google’s needs.' + # Adapted from the homepage: + s.description = <<-DESC + BoringSSL is a fork of OpenSSL that is designed to meet Google’s needs. + + Although BoringSSL is an open source project, it is not intended for general use, as OpenSSL is. + We don’t recommend that third parties depend upon it. Doing so is likely to be frustrating + because there are no guarantees of API stability. Only the latest version of this pod is + supported, and every new version is a new major version. + + We update Google libraries and programs that use BoringSSL as needed when deciding to make API + changes. This allows us to mostly avoid compromises in the name of compatibility. It works for + us, but it may not work for you. + + As a Cocoapods pod, it has the advantage over OpenSSL's pods that the library doesn't need to + be precompiled. This eliminates the 10 - 20 minutes of wait the first time a user does "pod + install", lets it be used as a dynamic framework (pending solution of Cocoapods' issue #4605), + and works with bitcode automatically. It's also thought to be smaller than OpenSSL (which takes + 1MB - 2MB per ARM architecture), but we don't have specific numbers yet. + + BoringSSL arose because Google used OpenSSL for many years in various ways and, over time, built + up a large number of patches that were maintained while tracking upstream OpenSSL. As Google’s + product portfolio became more complex, more copies of OpenSSL sprung up and the effort involved + in maintaining all these patches in multiple places was growing steadily. + + Currently BoringSSL is the SSL library in Chrome/Chromium, Android (but it’s not part of the + NDK) and a number of other apps/programs. + DESC + s.homepage = 'https://boringssl.googlesource.com/boringssl/' + s.documentation_url = 'https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html' + s.license = { :type => 'Mixed', :file => 'LICENSE' } + # "The name and email addresses of the library maintainers, not the Podspec maintainer." + s.authors = 'Adam Langley', 'David Benjamin', 'Matt Braithwaite' + + s.source = { :git => 'https://boringssl.googlesource.com/boringssl', + :tag => 'version_for_cocoapods_1.0' } + + s.source_files = 'ssl/*.{h,c}', + 'ssl/**/*.{h,c}', + '*.{h,c}', + 'crypto/*.{h,c}', + 'crypto/**/*.{h,c}', + 'include/openssl/*.h' + + s.public_header_files = 'include/openssl/*.h' + s.header_mappings_dir = 'include' + + s.exclude_files = "**/*_test.*" + + # We don't need to inhibit all warnings; only -Wno-shorten-64-to-32. But Cocoapods' linter doesn't + # want that for some reason. + s.compiler_flags = '-DOPENSSL_NO_ASM', '-GCC_WARN_INHIBIT_ALL_WARNINGS', '-w' + s.requires_arc = false + + s.prepare_command = <<-END_OF_COMMAND + # Replace "const BIGNUM *I" in rsa.h with a lowercase i, as the former fails when including + # OpenSSL in a Swift bridging header (complex.h defines "I", and it's as if the compiler + # included it in every bridged header). + sed -E -i '.back' 's/\\*I,/*i,/g' include/openssl/rsa.h + + # This is a bit ridiculous, but requiring people to install Go in order to build is slightly + # more ridiculous IMO. To save you from scrolling, this is the last part of the podspec. + # TODO(jcanizales): Translate err_data_generate.go into a Bash or Ruby script. + cat > err_data.c <<EOF + /* Copyright (c) 2015, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + + /* This file was generated by err_data_generate.go. */ + + #include <openssl/base.h> + #include <openssl/err.h> + #include <openssl/type_check.h> + + + OPENSSL_COMPILE_ASSERT(ERR_LIB_NONE == 1, library_values_changed_1); + OPENSSL_COMPILE_ASSERT(ERR_LIB_SYS == 2, library_values_changed_2); + OPENSSL_COMPILE_ASSERT(ERR_LIB_BN == 3, library_values_changed_3); + OPENSSL_COMPILE_ASSERT(ERR_LIB_RSA == 4, library_values_changed_4); + OPENSSL_COMPILE_ASSERT(ERR_LIB_DH == 5, library_values_changed_5); + OPENSSL_COMPILE_ASSERT(ERR_LIB_EVP == 6, library_values_changed_6); + OPENSSL_COMPILE_ASSERT(ERR_LIB_BUF == 7, library_values_changed_7); + OPENSSL_COMPILE_ASSERT(ERR_LIB_OBJ == 8, library_values_changed_8); + OPENSSL_COMPILE_ASSERT(ERR_LIB_PEM == 9, library_values_changed_9); + OPENSSL_COMPILE_ASSERT(ERR_LIB_DSA == 10, library_values_changed_10); + OPENSSL_COMPILE_ASSERT(ERR_LIB_X509 == 11, library_values_changed_11); + OPENSSL_COMPILE_ASSERT(ERR_LIB_ASN1 == 12, library_values_changed_12); + OPENSSL_COMPILE_ASSERT(ERR_LIB_CONF == 13, library_values_changed_13); + OPENSSL_COMPILE_ASSERT(ERR_LIB_CRYPTO == 14, library_values_changed_14); + OPENSSL_COMPILE_ASSERT(ERR_LIB_EC == 15, library_values_changed_15); + OPENSSL_COMPILE_ASSERT(ERR_LIB_SSL == 16, library_values_changed_16); + OPENSSL_COMPILE_ASSERT(ERR_LIB_BIO == 17, library_values_changed_17); + OPENSSL_COMPILE_ASSERT(ERR_LIB_PKCS7 == 18, library_values_changed_18); + OPENSSL_COMPILE_ASSERT(ERR_LIB_PKCS8 == 19, library_values_changed_19); + OPENSSL_COMPILE_ASSERT(ERR_LIB_X509V3 == 20, library_values_changed_20); + OPENSSL_COMPILE_ASSERT(ERR_LIB_RAND == 21, library_values_changed_21); + OPENSSL_COMPILE_ASSERT(ERR_LIB_ENGINE == 22, library_values_changed_22); + OPENSSL_COMPILE_ASSERT(ERR_LIB_OCSP == 23, library_values_changed_23); + OPENSSL_COMPILE_ASSERT(ERR_LIB_UI == 24, library_values_changed_24); + OPENSSL_COMPILE_ASSERT(ERR_LIB_COMP == 25, library_values_changed_25); + OPENSSL_COMPILE_ASSERT(ERR_LIB_ECDSA == 26, library_values_changed_26); + OPENSSL_COMPILE_ASSERT(ERR_LIB_ECDH == 27, library_values_changed_27); + OPENSSL_COMPILE_ASSERT(ERR_LIB_HMAC == 28, library_values_changed_28); + OPENSSL_COMPILE_ASSERT(ERR_LIB_DIGEST == 29, library_values_changed_29); + OPENSSL_COMPILE_ASSERT(ERR_LIB_CIPHER == 30, library_values_changed_30); + OPENSSL_COMPILE_ASSERT(ERR_LIB_HKDF == 31, library_values_changed_31); + OPENSSL_COMPILE_ASSERT(ERR_LIB_USER == 32, library_values_changed_32); + OPENSSL_COMPILE_ASSERT(ERR_NUM_LIBS == 33, library_values_changed_num); + + const uint32_t kOpenSSLReasonValues[] = { + 0xc3207ba, + 0xc3287d4, + 0xc3307e3, + 0xc3387f3, + 0xc340802, + 0xc34881b, + 0xc350827, + 0xc358844, + 0xc360856, + 0xc368864, + 0xc370874, + 0xc378881, + 0xc380891, + 0xc38889c, + 0xc3908b2, + 0xc3988c1, + 0xc3a08d5, + 0xc3a87c7, + 0xc3b00b0, + 0x10321478, + 0x10329484, + 0x1033149d, + 0x103394b0, + 0x10340de1, + 0x103494cf, + 0x103514e4, + 0x10359516, + 0x1036152f, + 0x10369544, + 0x10371562, + 0x10379571, + 0x1038158d, + 0x103895a8, + 0x103915b7, + 0x103995d3, + 0x103a15ee, + 0x103a9605, + 0x103b1616, + 0x103b962a, + 0x103c1649, + 0x103c9658, + 0x103d166f, + 0x103d9682, + 0x103e0b6c, + 0x103e96b3, + 0x103f16c6, + 0x103f96e0, + 0x104016f0, + 0x10409704, + 0x1041171a, + 0x10419732, + 0x10421747, + 0x1042975b, + 0x1043176d, + 0x104385d0, + 0x104408c1, + 0x10449782, + 0x10451799, + 0x104597ae, + 0x104617bc, + 0x10469695, + 0x104714f7, + 0x104787c7, + 0x104800b0, + 0x104894c3, + 0x14320b4f, + 0x14328b5d, + 0x14330b6c, + 0x14338b7e, + 0x18320083, + 0x18328e47, + 0x18340e75, + 0x18348e89, + 0x18358ec0, + 0x18368eed, + 0x18370f00, + 0x18378f14, + 0x18380f38, + 0x18388f46, + 0x18390f5c, + 0x18398f70, + 0x183a0f80, + 0x183b0f90, + 0x183b8fa5, + 0x183c8fd0, + 0x183d0fe4, + 0x183d8ff4, + 0x183e0b9b, + 0x183e9001, + 0x183f1013, + 0x183f901e, + 0x1840102e, + 0x1840903f, + 0x18411050, + 0x18419062, + 0x1842108b, + 0x184290bd, + 0x184310cc, + 0x18451135, + 0x1845914b, + 0x18461166, + 0x18468ed8, + 0x184709d9, + 0x18478094, + 0x18480fbc, + 0x18489101, + 0x18490e5d, + 0x18498e9e, + 0x184a119c, + 0x184a9119, + 0x184b10e0, + 0x184b8e37, + 0x184c10a4, + 0x184c866b, + 0x184d1181, + 0x203211c3, + 0x243211cf, + 0x24328907, + 0x243311e1, + 0x243391ee, + 0x243411fb, + 0x2434920d, + 0x2435121c, + 0x24359239, + 0x24361246, + 0x24369254, + 0x24371262, + 0x24379270, + 0x24381279, + 0x24389286, + 0x24391299, + 0x28320b8f, + 0x28328b9b, + 0x28330b6c, + 0x28338bae, + 0x2c322c0b, + 0x2c32ac19, + 0x2c332c2b, + 0x2c33ac3d, + 0x2c342c51, + 0x2c34ac63, + 0x2c352c7e, + 0x2c35ac90, + 0x2c362ca3, + 0x2c3682f3, + 0x2c372cb0, + 0x2c37acc2, + 0x2c382cd5, + 0x2c38ace3, + 0x2c392cf3, + 0x2c39ad05, + 0x2c3a2d19, + 0x2c3aad2a, + 0x2c3b1359, + 0x2c3bad3b, + 0x2c3c2d4f, + 0x2c3cad65, + 0x2c3d2d7e, + 0x2c3dadac, + 0x2c3e2dba, + 0x2c3eadd2, + 0x2c3f2dea, + 0x2c3fadf7, + 0x2c402e1a, + 0x2c40ae39, + 0x2c4111c3, + 0x2c41ae4a, + 0x2c422e5d, + 0x2c429135, + 0x2c432e6e, + 0x2c4386a2, + 0x2c442d9b, + 0x30320000, + 0x30328015, + 0x3033001f, + 0x30338038, + 0x3034004a, + 0x30348064, + 0x3035006b, + 0x30358083, + 0x30360094, + 0x303680a1, + 0x303700b0, + 0x303780bd, + 0x303800d0, + 0x303880eb, + 0x30390100, + 0x30398114, + 0x303a0128, + 0x303a8139, + 0x303b0152, + 0x303b816f, + 0x303c017d, + 0x303c8191, + 0x303d01a1, + 0x303d81ba, + 0x303e01ca, + 0x303e81dd, + 0x303f01ec, + 0x303f81f8, + 0x3040020d, + 0x3040821d, + 0x30410234, + 0x30418241, + 0x30420254, + 0x30428263, + 0x30430278, + 0x30438299, + 0x304402ac, + 0x304482bf, + 0x304502d8, + 0x304582f3, + 0x30460310, + 0x30468329, + 0x30470337, + 0x30478348, + 0x30480357, + 0x3048836f, + 0x30490381, + 0x30498395, + 0x304a03b4, + 0x304a83c7, + 0x304b03d2, + 0x304b83e1, + 0x304c03f2, + 0x304c83fe, + 0x304d0414, + 0x304d8422, + 0x304e0438, + 0x304e844a, + 0x304f045c, + 0x304f846f, + 0x30500482, + 0x30508493, + 0x305104a3, + 0x305184bb, + 0x305204d0, + 0x305284e8, + 0x305304fc, + 0x30538514, + 0x3054052d, + 0x30548546, + 0x30550563, + 0x3055856e, + 0x30560586, + 0x30568596, + 0x305705a7, + 0x305785ba, + 0x305805d0, + 0x305885d9, + 0x305905ee, + 0x30598601, + 0x305a0610, + 0x305a8630, + 0x305b063f, + 0x305b864b, + 0x305c066b, + 0x305c8687, + 0x305d0698, + 0x305d86a2, + 0x34320ac9, + 0x34328add, + 0x34330afa, + 0x34338b0d, + 0x34340b1c, + 0x34348b39, + 0x3c320083, + 0x3c328bd8, + 0x3c330bf1, + 0x3c338c0c, + 0x3c340c29, + 0x3c348c44, + 0x3c350c5f, + 0x3c358c74, + 0x3c360c8d, + 0x3c368ca5, + 0x3c370cb6, + 0x3c378cc4, + 0x3c380cd1, + 0x3c388ce5, + 0x3c390b9b, + 0x3c398cf9, + 0x3c3a0d0d, + 0x3c3a8881, + 0x3c3b0d1d, + 0x3c3b8d38, + 0x3c3c0d4a, + 0x3c3c8d60, + 0x3c3d0d6a, + 0x3c3d8d7e, + 0x3c3e0d8c, + 0x3c3e8db1, + 0x3c3f0bc4, + 0x3c3f8d9a, + 0x403217d3, + 0x403297e9, + 0x40331817, + 0x40339821, + 0x40341838, + 0x40349856, + 0x40351866, + 0x40359878, + 0x40361885, + 0x40369891, + 0x403718a6, + 0x403798bb, + 0x403818cd, + 0x403898d8, + 0x403918ea, + 0x40398de1, + 0x403a18fa, + 0x403a990d, + 0x403b192e, + 0x403b993f, + 0x403c194f, + 0x403c8064, + 0x403d195b, + 0x403d9977, + 0x403e198d, + 0x403e999c, + 0x403f19af, + 0x403f99c9, + 0x404019d7, + 0x404099ec, + 0x40411a00, + 0x40419a1d, + 0x40421a36, + 0x40429a51, + 0x40431a6a, + 0x40439a7d, + 0x40441a91, + 0x40449aa9, + 0x40451af4, + 0x40459b02, + 0x40461b20, + 0x40468094, + 0x40471b35, + 0x40479b47, + 0x40481b6b, + 0x40489b99, + 0x40491bad, + 0x40499bc2, + 0x404a1bdb, + 0x404a9c15, + 0x404b1c46, + 0x404b9c7c, + 0x404c1c97, + 0x404c9cb1, + 0x404d1cc8, + 0x404d9cf0, + 0x404e1d07, + 0x404e9d23, + 0x404f1d3f, + 0x404f9d60, + 0x40501d82, + 0x40509d9e, + 0x40511db2, + 0x40519dbf, + 0x40521dd6, + 0x40529de6, + 0x40531df6, + 0x40539e0a, + 0x40541e25, + 0x40549e35, + 0x40551e4c, + 0x40559e5b, + 0x40561e88, + 0x40569ea0, + 0x40571ebc, + 0x40579ed5, + 0x40581ee8, + 0x40589efd, + 0x40591f20, + 0x40599f4b, + 0x405a1f58, + 0x405a9f71, + 0x405b1f89, + 0x405b9f9c, + 0x405c1fb1, + 0x405c9fc3, + 0x405d1fd8, + 0x405d9fe8, + 0x405e2001, + 0x405ea015, + 0x405f2025, + 0x405fa03d, + 0x4060204e, + 0x4060a061, + 0x40612072, + 0x4061a090, + 0x406220a1, + 0x4062a0ae, + 0x406320c5, + 0x4063a106, + 0x4064211d, + 0x4064a12a, + 0x40652138, + 0x4065a15a, + 0x40662182, + 0x4066a197, + 0x406721ae, + 0x4067a1bf, + 0x406821d0, + 0x4068a1e1, + 0x406921f6, + 0x4069a20d, + 0x406a221e, + 0x406aa237, + 0x406b2252, + 0x406ba269, + 0x406c22d6, + 0x406ca2f7, + 0x406d230a, + 0x406da32b, + 0x406e2346, + 0x406ea38f, + 0x406f23b0, + 0x406fa3d6, + 0x407023f6, + 0x4070a412, + 0x4071259f, + 0x4071a5c2, + 0x407225d8, + 0x4072a5f7, + 0x4073260f, + 0x4073a62f, + 0x40742859, + 0x4074a87e, + 0x40752899, + 0x4075a8b8, + 0x407628e7, + 0x4076a90f, + 0x40772940, + 0x4077a95f, + 0x40782999, + 0x4078a9b0, + 0x407929c3, + 0x4079a9e0, + 0x407a0782, + 0x407aa9f2, + 0x407b2a05, + 0x407baa1e, + 0x407c2a36, + 0x407c90bd, + 0x407d2a4a, + 0x407daa64, + 0x407e2a75, + 0x407eaa89, + 0x407f2a97, + 0x407faab2, + 0x40801286, + 0x4080aad7, + 0x40812af9, + 0x4081ab14, + 0x40822b29, + 0x4082ab41, + 0x40832b59, + 0x4083ab70, + 0x40842b86, + 0x4084ab92, + 0x40852ba5, + 0x4085abba, + 0x40862bcc, + 0x4086abe1, + 0x40872bea, + 0x40879cde, + 0x40880083, + 0x4088a0e5, + 0x40890a17, + 0x4089a281, + 0x408a1bfe, + 0x408aa2ab, + 0x408b2928, + 0x408ba984, + 0x408c2361, + 0x408c9c2f, + 0x408d1c64, + 0x408d9e76, + 0x408e1ab9, + 0x408e9add, + 0x408f1f2e, + 0x408f9b8b, + 0x41f424ca, + 0x41f9255c, + 0x41fe244f, + 0x41fea680, + 0x41ff2771, + 0x420324e3, + 0x42082505, + 0x4208a541, + 0x42092433, + 0x4209a57b, + 0x420a248a, + 0x420aa46a, + 0x420b24aa, + 0x420ba523, + 0x420c278d, + 0x420ca64d, + 0x420d2667, + 0x420da69e, + 0x421226b8, + 0x42172754, + 0x4217a6fa, + 0x421c271c, + 0x421f26d7, + 0x422127a4, + 0x42262737, + 0x422b283d, + 0x422ba806, + 0x422c2825, + 0x422ca7e0, + 0x422d27bf, + 0x443206ad, + 0x443286bc, + 0x443306c8, + 0x443386d6, + 0x443406e9, + 0x443486fa, + 0x44350701, + 0x4435870b, + 0x4436071e, + 0x44368734, + 0x44370746, + 0x44378753, + 0x44380762, + 0x4438876a, + 0x44390782, + 0x44398790, + 0x443a07a3, + 0x4c3212b0, + 0x4c3292c0, + 0x4c3312d3, + 0x4c3392f3, + 0x4c340094, + 0x4c3480b0, + 0x4c3512ff, + 0x4c35930d, + 0x4c361329, + 0x4c36933c, + 0x4c37134b, + 0x4c379359, + 0x4c38136e, + 0x4c38937a, + 0x4c39139a, + 0x4c3993c4, + 0x4c3a13dd, + 0x4c3a93f6, + 0x4c3b05d0, + 0x4c3b940f, + 0x4c3c1421, + 0x4c3c9430, + 0x4c3d10bd, + 0x4c3d9449, + 0x4c3e1456, + 0x50322e80, + 0x5032ae8f, + 0x50332e9a, + 0x5033aeaa, + 0x50342ec3, + 0x5034aedd, + 0x50352eeb, + 0x5035af01, + 0x50362f13, + 0x5036af29, + 0x50372f42, + 0x5037af55, + 0x50382f6d, + 0x5038af7e, + 0x50392f93, + 0x5039afa7, + 0x503a2fc7, + 0x503aafdd, + 0x503b2ff5, + 0x503bb007, + 0x503c3023, + 0x503cb03a, + 0x503d3053, + 0x503db069, + 0x503e3076, + 0x503eb08c, + 0x503f309e, + 0x503f8348, + 0x504030b1, + 0x5040b0c1, + 0x504130db, + 0x5041b0ea, + 0x50423104, + 0x5042b121, + 0x50433131, + 0x5043b141, + 0x50443150, + 0x50448414, + 0x50453164, + 0x5045b182, + 0x50463195, + 0x5046b1ab, + 0x504731bd, + 0x5047b1d2, + 0x504831f8, + 0x5048b206, + 0x50493219, + 0x5049b22e, + 0x504a3244, + 0x504ab254, + 0x504b3274, + 0x504bb287, + 0x504c32aa, + 0x504cb2d8, + 0x504d32ea, + 0x504db307, + 0x504e3322, + 0x504eb33e, + 0x504f3350, + 0x504fb367, + 0x50503376, + 0x50508687, + 0x50513389, + 0x58320e1f, + 0x68320de1, + 0x68328b9b, + 0x68330bae, + 0x68338def, + 0x68340dff, + 0x683480b0, + 0x6c320dbd, + 0x6c328b7e, + 0x6c330dc8, + 0x7432098d, + 0x783208f2, + 0x78328907, + 0x78330913, + 0x78338083, + 0x78340922, + 0x78348937, + 0x78350956, + 0x78358978, + 0x7836098d, + 0x783689a3, + 0x783709b3, + 0x783789c6, + 0x783809d9, + 0x783889eb, + 0x783909f8, + 0x78398a17, + 0x783a0a2c, + 0x783a8a3a, + 0x783b0a44, + 0x783b8a58, + 0x783c0a6f, + 0x783c8a84, + 0x783d0a9b, + 0x783d8ab0, + 0x783e0a06, + 0x7c3211b2, + }; + + const size_t kOpenSSLReasonValuesLen = sizeof(kOpenSSLReasonValues) / sizeof(kOpenSSLReasonValues[0]); + + const char kOpenSSLReasonStringData[] = + "ASN1_LENGTH_MISMATCH\\0" + "AUX_ERROR\\0" + "BAD_GET_ASN1_OBJECT_CALL\\0" + "BAD_OBJECT_HEADER\\0" + "BMPSTRING_IS_WRONG_LENGTH\\0" + "BN_LIB\\0" + "BOOLEAN_IS_WRONG_LENGTH\\0" + "BUFFER_TOO_SMALL\\0" + "DECODE_ERROR\\0" + "DEPTH_EXCEEDED\\0" + "ENCODE_ERROR\\0" + "ERROR_GETTING_TIME\\0" + "EXPECTING_AN_ASN1_SEQUENCE\\0" + "EXPECTING_AN_INTEGER\\0" + "EXPECTING_AN_OBJECT\\0" + "EXPECTING_A_BOOLEAN\\0" + "EXPECTING_A_TIME\\0" + "EXPLICIT_LENGTH_MISMATCH\\0" + "EXPLICIT_TAG_NOT_CONSTRUCTED\\0" + "FIELD_MISSING\\0" + "FIRST_NUM_TOO_LARGE\\0" + "HEADER_TOO_LONG\\0" + "ILLEGAL_BITSTRING_FORMAT\\0" + "ILLEGAL_BOOLEAN\\0" + "ILLEGAL_CHARACTERS\\0" + "ILLEGAL_FORMAT\\0" + "ILLEGAL_HEX\\0" + "ILLEGAL_IMPLICIT_TAG\\0" + "ILLEGAL_INTEGER\\0" + "ILLEGAL_NESTED_TAGGING\\0" + "ILLEGAL_NULL\\0" + "ILLEGAL_NULL_VALUE\\0" + "ILLEGAL_OBJECT\\0" + "ILLEGAL_OPTIONAL_ANY\\0" + "ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE\\0" + "ILLEGAL_TAGGED_ANY\\0" + "ILLEGAL_TIME_VALUE\\0" + "INTEGER_NOT_ASCII_FORMAT\\0" + "INTEGER_TOO_LARGE_FOR_LONG\\0" + "INVALID_BIT_STRING_BITS_LEFT\\0" + "INVALID_BMPSTRING_LENGTH\\0" + "INVALID_DIGIT\\0" + "INVALID_MODIFIER\\0" + "INVALID_NUMBER\\0" + "INVALID_OBJECT_ENCODING\\0" + "INVALID_SEPARATOR\\0" + "INVALID_TIME_FORMAT\\0" + "INVALID_UNIVERSALSTRING_LENGTH\\0" + "INVALID_UTF8STRING\\0" + "LIST_ERROR\\0" + "MALLOC_FAILURE\\0" + "MISSING_ASN1_EOS\\0" + "MISSING_EOC\\0" + "MISSING_SECOND_NUMBER\\0" + "MISSING_VALUE\\0" + "MSTRING_NOT_UNIVERSAL\\0" + "MSTRING_WRONG_TAG\\0" + "NESTED_ASN1_ERROR\\0" + "NESTED_ASN1_STRING\\0" + "NON_HEX_CHARACTERS\\0" + "NOT_ASCII_FORMAT\\0" + "NOT_ENOUGH_DATA\\0" + "NO_MATCHING_CHOICE_TYPE\\0" + "NULL_IS_WRONG_LENGTH\\0" + "OBJECT_NOT_ASCII_FORMAT\\0" + "ODD_NUMBER_OF_CHARS\\0" + "SECOND_NUMBER_TOO_LARGE\\0" + "SEQUENCE_LENGTH_MISMATCH\\0" + "SEQUENCE_NOT_CONSTRUCTED\\0" + "SEQUENCE_OR_SET_NEEDS_CONFIG\\0" + "SHORT_LINE\\0" + "STREAMING_NOT_SUPPORTED\\0" + "STRING_TOO_LONG\\0" + "STRING_TOO_SHORT\\0" + "TAG_VALUE_TOO_HIGH\\0" + "TIME_NOT_ASCII_FORMAT\\0" + "TOO_LONG\\0" + "TYPE_NOT_CONSTRUCTED\\0" + "TYPE_NOT_PRIMITIVE\\0" + "UNEXPECTED_EOC\\0" + "UNIVERSALSTRING_IS_WRONG_LENGTH\\0" + "UNKNOWN_FORMAT\\0" + "UNKNOWN_TAG\\0" + "UNSUPPORTED_ANY_DEFINED_BY_TYPE\\0" + "UNSUPPORTED_PUBLIC_KEY_TYPE\\0" + "UNSUPPORTED_TYPE\\0" + "WRONG_TAG\\0" + "WRONG_TYPE\\0" + "BAD_FOPEN_MODE\\0" + "BROKEN_PIPE\\0" + "CONNECT_ERROR\\0" + "ERROR_SETTING_NBIO\\0" + "INVALID_ARGUMENT\\0" + "IN_USE\\0" + "KEEPALIVE\\0" + "NBIO_CONNECT_ERROR\\0" + "NO_HOSTNAME_SPECIFIED\\0" + "NO_PORT_SPECIFIED\\0" + "NO_SUCH_FILE\\0" + "NULL_PARAMETER\\0" + "SYS_LIB\\0" + "UNABLE_TO_CREATE_SOCKET\\0" + "UNINITIALIZED\\0" + "UNSUPPORTED_METHOD\\0" + "WRITE_TO_READ_ONLY_BIO\\0" + "ARG2_LT_ARG3\\0" + "BAD_ENCODING\\0" + "BAD_RECIPROCAL\\0" + "BIGNUM_TOO_LONG\\0" + "BITS_TOO_SMALL\\0" + "CALLED_WITH_EVEN_MODULUS\\0" + "DIV_BY_ZERO\\0" + "EXPAND_ON_STATIC_BIGNUM_DATA\\0" + "INPUT_NOT_REDUCED\\0" + "INVALID_RANGE\\0" + "NEGATIVE_NUMBER\\0" + "NOT_A_SQUARE\\0" + "NOT_INITIALIZED\\0" + "NO_INVERSE\\0" + "PRIVATE_KEY_TOO_LARGE\\0" + "P_IS_NOT_PRIME\\0" + "TOO_MANY_ITERATIONS\\0" + "TOO_MANY_TEMPORARY_VARIABLES\\0" + "AES_KEY_SETUP_FAILED\\0" + "BAD_DECRYPT\\0" + "BAD_KEY_LENGTH\\0" + "CTRL_NOT_IMPLEMENTED\\0" + "CTRL_OPERATION_NOT_IMPLEMENTED\\0" + "DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH\\0" + "INITIALIZATION_ERROR\\0" + "INPUT_NOT_INITIALIZED\\0" + "INVALID_AD_SIZE\\0" + "INVALID_KEY_LENGTH\\0" + "INVALID_NONCE_SIZE\\0" + "INVALID_OPERATION\\0" + "IV_TOO_LARGE\\0" + "NO_CIPHER_SET\\0" + "NO_DIRECTION_SET\\0" + "OUTPUT_ALIASES_INPUT\\0" + "TAG_TOO_LARGE\\0" + "TOO_LARGE\\0" + "UNSUPPORTED_AD_SIZE\\0" + "UNSUPPORTED_INPUT_SIZE\\0" + "UNSUPPORTED_KEY_SIZE\\0" + "UNSUPPORTED_NONCE_SIZE\\0" + "UNSUPPORTED_TAG_SIZE\\0" + "WRONG_FINAL_BLOCK_LENGTH\\0" + "LIST_CANNOT_BE_NULL\\0" + "MISSING_CLOSE_SQUARE_BRACKET\\0" + "MISSING_EQUAL_SIGN\\0" + "NO_CLOSE_BRACE\\0" + "UNABLE_TO_CREATE_NEW_SECTION\\0" + "VARIABLE_HAS_NO_VALUE\\0" + "BAD_GENERATOR\\0" + "INVALID_PUBKEY\\0" + "MODULUS_TOO_LARGE\\0" + "NO_PRIVATE_VALUE\\0" + "BAD_Q_VALUE\\0" + "MISSING_PARAMETERS\\0" + "NEED_NEW_SETUP_VALUES\\0" + "BIGNUM_OUT_OF_RANGE\\0" + "COORDINATES_OUT_OF_RANGE\\0" + "D2I_ECPKPARAMETERS_FAILURE\\0" + "EC_GROUP_NEW_BY_NAME_FAILURE\\0" + "GROUP2PKPARAMETERS_FAILURE\\0" + "I2D_ECPKPARAMETERS_FAILURE\\0" + "INCOMPATIBLE_OBJECTS\\0" + "INVALID_COMPRESSED_POINT\\0" + "INVALID_COMPRESSION_BIT\\0" + "INVALID_ENCODING\\0" + "INVALID_FIELD\\0" + "INVALID_FORM\\0" + "INVALID_GROUP_ORDER\\0" + "INVALID_PRIVATE_KEY\\0" + "MISSING_PRIVATE_KEY\\0" + "NON_NAMED_CURVE\\0" + "PKPARAMETERS2GROUP_FAILURE\\0" + "POINT_AT_INFINITY\\0" + "POINT_IS_NOT_ON_CURVE\\0" + "SLOT_FULL\\0" + "UNDEFINED_GENERATOR\\0" + "UNKNOWN_GROUP\\0" + "UNKNOWN_ORDER\\0" + "WRONG_CURVE_PARAMETERS\\0" + "WRONG_ORDER\\0" + "KDF_FAILED\\0" + "POINT_ARITHMETIC_FAILURE\\0" + "BAD_SIGNATURE\\0" + "NOT_IMPLEMENTED\\0" + "RANDOM_NUMBER_GENERATION_FAILED\\0" + "OPERATION_NOT_SUPPORTED\\0" + "BN_DECODE_ERROR\\0" + "COMMAND_NOT_SUPPORTED\\0" + "CONTEXT_NOT_INITIALISED\\0" + "DIFFERENT_KEY_TYPES\\0" + "DIFFERENT_PARAMETERS\\0" + "DIGEST_AND_KEY_TYPE_NOT_SUPPORTED\\0" + "EXPECTING_AN_EC_KEY_KEY\\0" + "EXPECTING_AN_RSA_KEY\\0" + "EXPECTING_A_DH_KEY\\0" + "EXPECTING_A_DSA_KEY\\0" + "ILLEGAL_OR_UNSUPPORTED_PADDING_MODE\\0" + "INVALID_CURVE\\0" + "INVALID_DIGEST_LENGTH\\0" + "INVALID_DIGEST_TYPE\\0" + "INVALID_KEYBITS\\0" + "INVALID_MGF1_MD\\0" + "INVALID_PADDING_MODE\\0" + "INVALID_PSS_PARAMETERS\\0" + "INVALID_PSS_SALTLEN\\0" + "INVALID_SALT_LENGTH\\0" + "INVALID_TRAILER\\0" + "KEYS_NOT_SET\\0" + "NO_DEFAULT_DIGEST\\0" + "NO_KEY_SET\\0" + "NO_MDC2_SUPPORT\\0" + "NO_NID_FOR_CURVE\\0" + "NO_OPERATION_SET\\0" + "NO_PARAMETERS_SET\\0" + "OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE\\0" + "OPERATON_NOT_INITIALIZED\\0" + "PARAMETER_ENCODING_ERROR\\0" + "UNKNOWN_DIGEST\\0" + "UNKNOWN_MASK_DIGEST\\0" + "UNKNOWN_MESSAGE_DIGEST_ALGORITHM\\0" + "UNKNOWN_PUBLIC_KEY_TYPE\\0" + "UNKNOWN_SIGNATURE_ALGORITHM\\0" + "UNSUPPORTED_ALGORITHM\\0" + "UNSUPPORTED_MASK_ALGORITHM\\0" + "UNSUPPORTED_MASK_PARAMETER\\0" + "UNSUPPORTED_SIGNATURE_TYPE\\0" + "WRONG_PUBLIC_KEY_TYPE\\0" + "OUTPUT_TOO_LARGE\\0" + "UNKNOWN_NID\\0" + "BAD_BASE64_DECODE\\0" + "BAD_END_LINE\\0" + "BAD_IV_CHARS\\0" + "BAD_PASSWORD_READ\\0" + "CIPHER_IS_NULL\\0" + "ERROR_CONVERTING_PRIVATE_KEY\\0" + "NOT_DEK_INFO\\0" + "NOT_ENCRYPTED\\0" + "NOT_PROC_TYPE\\0" + "NO_START_LINE\\0" + "READ_KEY\\0" + "SHORT_HEADER\\0" + "UNSUPPORTED_CIPHER\\0" + "UNSUPPORTED_ENCRYPTION\\0" + "BAD_PKCS12_DATA\\0" + "BAD_PKCS12_VERSION\\0" + "CIPHER_HAS_NO_OBJECT_IDENTIFIER\\0" + "CRYPT_ERROR\\0" + "ENCRYPT_ERROR\\0" + "ERROR_SETTING_CIPHER_PARAMS\\0" + "INCORRECT_PASSWORD\\0" + "KEYGEN_FAILURE\\0" + "KEY_GEN_ERROR\\0" + "METHOD_NOT_SUPPORTED\\0" + "MISSING_MAC\\0" + "MULTIPLE_PRIVATE_KEYS_IN_PKCS12\\0" + "PKCS12_PUBLIC_KEY_INTEGRITY_NOT_SUPPORTED\\0" + "PKCS12_TOO_DEEPLY_NESTED\\0" + "PRIVATE_KEY_DECODE_ERROR\\0" + "PRIVATE_KEY_ENCODE_ERROR\\0" + "UNKNOWN_ALGORITHM\\0" + "UNKNOWN_CIPHER\\0" + "UNKNOWN_CIPHER_ALGORITHM\\0" + "UNKNOWN_HASH\\0" + "UNSUPPORTED_PRIVATE_KEY_ALGORITHM\\0" + "BAD_E_VALUE\\0" + "BAD_FIXED_HEADER_DECRYPT\\0" + "BAD_PAD_BYTE_COUNT\\0" + "BAD_RSA_PARAMETERS\\0" + "BAD_VERSION\\0" + "BLOCK_TYPE_IS_NOT_01\\0" + "BN_NOT_INITIALIZED\\0" + "CANNOT_RECOVER_MULTI_PRIME_KEY\\0" + "CRT_PARAMS_ALREADY_GIVEN\\0" + "CRT_VALUES_INCORRECT\\0" + "DATA_LEN_NOT_EQUAL_TO_MOD_LEN\\0" + "DATA_TOO_LARGE\\0" + "DATA_TOO_LARGE_FOR_KEY_SIZE\\0" + "DATA_TOO_LARGE_FOR_MODULUS\\0" + "DATA_TOO_SMALL\\0" + "DATA_TOO_SMALL_FOR_KEY_SIZE\\0" + "DIGEST_TOO_BIG_FOR_RSA_KEY\\0" + "D_E_NOT_CONGRUENT_TO_1\\0" + "EMPTY_PUBLIC_KEY\\0" + "FIRST_OCTET_INVALID\\0" + "INCONSISTENT_SET_OF_CRT_VALUES\\0" + "INTERNAL_ERROR\\0" + "INVALID_MESSAGE_LENGTH\\0" + "KEY_SIZE_TOO_SMALL\\0" + "LAST_OCTET_INVALID\\0" + "MUST_HAVE_AT_LEAST_TWO_PRIMES\\0" + "NO_PUBLIC_EXPONENT\\0" + "NULL_BEFORE_BLOCK_MISSING\\0" + "N_NOT_EQUAL_P_Q\\0" + "OAEP_DECODING_ERROR\\0" + "ONLY_ONE_OF_P_Q_GIVEN\\0" + "OUTPUT_BUFFER_TOO_SMALL\\0" + "PADDING_CHECK_FAILED\\0" + "PKCS_DECODING_ERROR\\0" + "SLEN_CHECK_FAILED\\0" + "SLEN_RECOVERY_FAILED\\0" + "UNKNOWN_ALGORITHM_TYPE\\0" + "UNKNOWN_PADDING_TYPE\\0" + "VALUE_MISSING\\0" + "WRONG_SIGNATURE_LENGTH\\0" + "APP_DATA_IN_HANDSHAKE\\0" + "ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT\\0" + "BAD_ALERT\\0" + "BAD_CHANGE_CIPHER_SPEC\\0" + "BAD_DATA_RETURNED_BY_CALLBACK\\0" + "BAD_DH_P_LENGTH\\0" + "BAD_DIGEST_LENGTH\\0" + "BAD_ECC_CERT\\0" + "BAD_ECPOINT\\0" + "BAD_HANDSHAKE_LENGTH\\0" + "BAD_HANDSHAKE_RECORD\\0" + "BAD_HELLO_REQUEST\\0" + "BAD_LENGTH\\0" + "BAD_PACKET_LENGTH\\0" + "BAD_RSA_ENCRYPT\\0" + "BAD_SRTP_MKI_VALUE\\0" + "BAD_SRTP_PROTECTION_PROFILE_LIST\\0" + "BAD_SSL_FILETYPE\\0" + "BAD_WRITE_RETRY\\0" + "BIO_NOT_SET\\0" + "CANNOT_SERIALIZE_PUBLIC_KEY\\0" + "CA_DN_LENGTH_MISMATCH\\0" + "CA_DN_TOO_LONG\\0" + "CCS_RECEIVED_EARLY\\0" + "CERTIFICATE_VERIFY_FAILED\\0" + "CERT_CB_ERROR\\0" + "CERT_LENGTH_MISMATCH\\0" + "CHANNEL_ID_NOT_P256\\0" + "CHANNEL_ID_SIGNATURE_INVALID\\0" + "CIPHER_CODE_WRONG_LENGTH\\0" + "CIPHER_OR_HASH_UNAVAILABLE\\0" + "CLIENTHELLO_PARSE_FAILED\\0" + "CLIENTHELLO_TLSEXT\\0" + "CONNECTION_REJECTED\\0" + "CONNECTION_TYPE_NOT_SET\\0" + "COOKIE_MISMATCH\\0" + "CUSTOM_EXTENSION_CONTENTS_TOO_LARGE\\0" + "CUSTOM_EXTENSION_ERROR\\0" + "D2I_ECDSA_SIG\\0" + "DATA_BETWEEN_CCS_AND_FINISHED\\0" + "DATA_LENGTH_TOO_LONG\\0" + "DECRYPTION_FAILED\\0" + "DECRYPTION_FAILED_OR_BAD_RECORD_MAC\\0" + "DH_PUBLIC_VALUE_LENGTH_IS_WRONG\\0" + "DH_P_TOO_LONG\\0" + "DIGEST_CHECK_FAILED\\0" + "DTLS_MESSAGE_TOO_BIG\\0" + "ECC_CERT_NOT_FOR_SIGNING\\0" + "EMPTY_SRTP_PROTECTION_PROFILE_LIST\\0" + "EMS_STATE_INCONSISTENT\\0" + "ENCRYPTED_LENGTH_TOO_LONG\\0" + "ERROR_ADDING_EXTENSION\\0" + "ERROR_IN_RECEIVED_CIPHER_LIST\\0" + "ERROR_PARSING_EXTENSION\\0" + "EVP_DIGESTSIGNFINAL_FAILED\\0" + "EVP_DIGESTSIGNINIT_FAILED\\0" + "EXCESSIVE_MESSAGE_SIZE\\0" + "EXTRA_DATA_IN_MESSAGE\\0" + "FRAGMENT_MISMATCH\\0" + "GOT_A_FIN_BEFORE_A_CCS\\0" + "GOT_CHANNEL_ID_BEFORE_A_CCS\\0" + "GOT_NEXT_PROTO_BEFORE_A_CCS\\0" + "GOT_NEXT_PROTO_WITHOUT_EXTENSION\\0" + "HANDSHAKE_FAILURE_ON_CLIENT_HELLO\\0" + "HANDSHAKE_RECORD_BEFORE_CCS\\0" + "HTTPS_PROXY_REQUEST\\0" + "HTTP_REQUEST\\0" + "INAPPROPRIATE_FALLBACK\\0" + "INVALID_COMMAND\\0" + "INVALID_MESSAGE\\0" + "INVALID_SSL_SESSION\\0" + "INVALID_TICKET_KEYS_LENGTH\\0" + "LENGTH_MISMATCH\\0" + "LIBRARY_HAS_NO_CIPHERS\\0" + "MISSING_DH_KEY\\0" + "MISSING_ECDSA_SIGNING_CERT\\0" + "MISSING_EXTENSION\\0" + "MISSING_RSA_CERTIFICATE\\0" + "MISSING_RSA_ENCRYPTING_CERT\\0" + "MISSING_RSA_SIGNING_CERT\\0" + "MISSING_TMP_DH_KEY\\0" + "MISSING_TMP_ECDH_KEY\\0" + "MIXED_SPECIAL_OPERATOR_WITH_GROUPS\\0" + "MTU_TOO_SMALL\\0" + "NEGOTIATED_BOTH_NPN_AND_ALPN\\0" + "NESTED_GROUP\\0" + "NO_CERTIFICATES_RETURNED\\0" + "NO_CERTIFICATE_ASSIGNED\\0" + "NO_CERTIFICATE_SET\\0" + "NO_CIPHERS_AVAILABLE\\0" + "NO_CIPHERS_PASSED\\0" + "NO_CIPHERS_SPECIFIED\\0" + "NO_CIPHER_MATCH\\0" + "NO_COMPRESSION_SPECIFIED\\0" + "NO_METHOD_SPECIFIED\\0" + "NO_P256_SUPPORT\\0" + "NO_PRIVATE_KEY_ASSIGNED\\0" + "NO_RENEGOTIATION\\0" + "NO_REQUIRED_DIGEST\\0" + "NO_SHARED_CIPHER\\0" + "NO_SHARED_SIGATURE_ALGORITHMS\\0" + "NO_SRTP_PROFILES\\0" + "NULL_SSL_CTX\\0" + "NULL_SSL_METHOD_PASSED\\0" + "OLD_SESSION_CIPHER_NOT_RETURNED\\0" + "OLD_SESSION_VERSION_NOT_RETURNED\\0" + "PACKET_LENGTH_TOO_LONG\\0" + "PARSE_TLSEXT\\0" + "PATH_TOO_LONG\\0" + "PEER_DID_NOT_RETURN_A_CERTIFICATE\\0" + "PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE\\0" + "PROTOCOL_IS_SHUTDOWN\\0" + "PSK_IDENTITY_NOT_FOUND\\0" + "PSK_NO_CLIENT_CB\\0" + "PSK_NO_SERVER_CB\\0" + "READ_BIO_NOT_SET\\0" + "READ_TIMEOUT_EXPIRED\\0" + "RECORD_LENGTH_MISMATCH\\0" + "RECORD_TOO_LARGE\\0" + "RENEGOTIATE_EXT_TOO_LONG\\0" + "RENEGOTIATION_ENCODING_ERR\\0" + "RENEGOTIATION_MISMATCH\\0" + "REQUIRED_CIPHER_MISSING\\0" + "RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION\\0" + "RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION\\0" + "SCSV_RECEIVED_WHEN_RENEGOTIATING\\0" + "SERVERHELLO_TLSEXT\\0" + "SESSION_ID_CONTEXT_UNINITIALIZED\\0" + "SESSION_MAY_NOT_BE_CREATED\\0" + "SIGNATURE_ALGORITHMS_ERROR\\0" + "SIGNATURE_ALGORITHMS_EXTENSION_SENT_BY_SERVER\\0" + "SRTP_COULD_NOT_ALLOCATE_PROFILES\\0" + "SRTP_PROTECTION_PROFILE_LIST_TOO_LONG\\0" + "SRTP_UNKNOWN_PROTECTION_PROFILE\\0" + "SSL3_EXT_INVALID_SERVERNAME\\0" + "SSL3_EXT_INVALID_SERVERNAME_TYPE\\0" + "SSLV3_ALERT_BAD_CERTIFICATE\\0" + "SSLV3_ALERT_BAD_RECORD_MAC\\0" + "SSLV3_ALERT_CERTIFICATE_EXPIRED\\0" + "SSLV3_ALERT_CERTIFICATE_REVOKED\\0" + "SSLV3_ALERT_CERTIFICATE_UNKNOWN\\0" + "SSLV3_ALERT_CLOSE_NOTIFY\\0" + "SSLV3_ALERT_DECOMPRESSION_FAILURE\\0" + "SSLV3_ALERT_HANDSHAKE_FAILURE\\0" + "SSLV3_ALERT_ILLEGAL_PARAMETER\\0" + "SSLV3_ALERT_NO_CERTIFICATE\\0" + "SSLV3_ALERT_UNEXPECTED_MESSAGE\\0" + "SSLV3_ALERT_UNSUPPORTED_CERTIFICATE\\0" + "SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION\\0" + "SSL_HANDSHAKE_FAILURE\\0" + "SSL_SESSION_ID_CALLBACK_FAILED\\0" + "SSL_SESSION_ID_CONFLICT\\0" + "SSL_SESSION_ID_CONTEXT_TOO_LONG\\0" + "SSL_SESSION_ID_HAS_BAD_LENGTH\\0" + "TLSV1_ALERT_ACCESS_DENIED\\0" + "TLSV1_ALERT_DECODE_ERROR\\0" + "TLSV1_ALERT_DECRYPTION_FAILED\\0" + "TLSV1_ALERT_DECRYPT_ERROR\\0" + "TLSV1_ALERT_EXPORT_RESTRICTION\\0" + "TLSV1_ALERT_INAPPROPRIATE_FALLBACK\\0" + "TLSV1_ALERT_INSUFFICIENT_SECURITY\\0" + "TLSV1_ALERT_INTERNAL_ERROR\\0" + "TLSV1_ALERT_NO_RENEGOTIATION\\0" + "TLSV1_ALERT_PROTOCOL_VERSION\\0" + "TLSV1_ALERT_RECORD_OVERFLOW\\0" + "TLSV1_ALERT_UNKNOWN_CA\\0" + "TLSV1_ALERT_USER_CANCELLED\\0" + "TLSV1_BAD_CERTIFICATE_HASH_VALUE\\0" + "TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE\\0" + "TLSV1_CERTIFICATE_UNOBTAINABLE\\0" + "TLSV1_UNRECOGNIZED_NAME\\0" + "TLSV1_UNSUPPORTED_EXTENSION\\0" + "TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER\\0" + "TLS_ILLEGAL_EXPORTER_LABEL\\0" + "TLS_INVALID_ECPOINTFORMAT_LIST\\0" + "TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST\\0" + "TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG\\0" + "TOO_MANY_EMPTY_FRAGMENTS\\0" + "TOO_MANY_WARNING_ALERTS\\0" + "UNABLE_TO_FIND_ECDH_PARAMETERS\\0" + "UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS\\0" + "UNEXPECTED_EXTENSION\\0" + "UNEXPECTED_GROUP_CLOSE\\0" + "UNEXPECTED_MESSAGE\\0" + "UNEXPECTED_OPERATOR_IN_GROUP\\0" + "UNEXPECTED_RECORD\\0" + "UNKNOWN_ALERT_TYPE\\0" + "UNKNOWN_CERTIFICATE_TYPE\\0" + "UNKNOWN_CIPHER_RETURNED\\0" + "UNKNOWN_CIPHER_TYPE\\0" + "UNKNOWN_KEY_EXCHANGE_TYPE\\0" + "UNKNOWN_PROTOCOL\\0" + "UNKNOWN_SSL_VERSION\\0" + "UNKNOWN_STATE\\0" + "UNPROCESSED_HANDSHAKE_DATA\\0" + "UNSAFE_LEGACY_RENEGOTIATION_DISABLED\\0" + "UNSUPPORTED_COMPRESSION_ALGORITHM\\0" + "UNSUPPORTED_ELLIPTIC_CURVE\\0" + "UNSUPPORTED_PROTOCOL\\0" + "UNSUPPORTED_SSL_VERSION\\0" + "USE_SRTP_NOT_NEGOTIATED\\0" + "WRONG_CERTIFICATE_TYPE\\0" + "WRONG_CIPHER_RETURNED\\0" + "WRONG_CURVE\\0" + "WRONG_MESSAGE_TYPE\\0" + "WRONG_SIGNATURE_TYPE\\0" + "WRONG_SSL_VERSION\\0" + "WRONG_VERSION_NUMBER\\0" + "X509_LIB\\0" + "X509_VERIFICATION_SETUP_PROBLEMS\\0" + "AKID_MISMATCH\\0" + "BAD_PKCS7_VERSION\\0" + "BAD_X509_FILETYPE\\0" + "BASE64_DECODE_ERROR\\0" + "CANT_CHECK_DH_KEY\\0" + "CERT_ALREADY_IN_HASH_TABLE\\0" + "CRL_ALREADY_DELTA\\0" + "CRL_VERIFY_FAILURE\\0" + "IDP_MISMATCH\\0" + "INVALID_DIRECTORY\\0" + "INVALID_FIELD_NAME\\0" + "INVALID_TRUST\\0" + "ISSUER_MISMATCH\\0" + "KEY_TYPE_MISMATCH\\0" + "KEY_VALUES_MISMATCH\\0" + "LOADING_CERT_DIR\\0" + "LOADING_DEFAULTS\\0" + "NEWER_CRL_NOT_NEWER\\0" + "NOT_PKCS7_SIGNED_DATA\\0" + "NO_CERTIFICATES_INCLUDED\\0" + "NO_CERT_SET_FOR_US_TO_VERIFY\\0" + "NO_CRLS_INCLUDED\\0" + "NO_CRL_NUMBER\\0" + "PUBLIC_KEY_DECODE_ERROR\\0" + "PUBLIC_KEY_ENCODE_ERROR\\0" + "SHOULD_RETRY\\0" + "UNABLE_TO_FIND_PARAMETERS_IN_CHAIN\\0" + "UNABLE_TO_GET_CERTS_PUBLIC_KEY\\0" + "UNKNOWN_KEY_TYPE\\0" + "UNKNOWN_PURPOSE_ID\\0" + "UNKNOWN_TRUST_ID\\0" + "WRONG_LOOKUP_TYPE\\0" + "BAD_IP_ADDRESS\\0" + "BAD_OBJECT\\0" + "BN_DEC2BN_ERROR\\0" + "BN_TO_ASN1_INTEGER_ERROR\\0" + "CANNOT_FIND_FREE_FUNCTION\\0" + "DIRNAME_ERROR\\0" + "DISTPOINT_ALREADY_SET\\0" + "DUPLICATE_ZONE_ID\\0" + "ERROR_CONVERTING_ZONE\\0" + "ERROR_CREATING_EXTENSION\\0" + "ERROR_IN_EXTENSION\\0" + "EXPECTED_A_SECTION_NAME\\0" + "EXTENSION_EXISTS\\0" + "EXTENSION_NAME_ERROR\\0" + "EXTENSION_NOT_FOUND\\0" + "EXTENSION_SETTING_NOT_SUPPORTED\\0" + "EXTENSION_VALUE_ERROR\\0" + "ILLEGAL_EMPTY_EXTENSION\\0" + "ILLEGAL_HEX_DIGIT\\0" + "INCORRECT_POLICY_SYNTAX_TAG\\0" + "INVALID_BOOLEAN_STRING\\0" + "INVALID_EXTENSION_STRING\\0" + "INVALID_MULTIPLE_RDNS\\0" + "INVALID_NAME\\0" + "INVALID_NULL_ARGUMENT\\0" + "INVALID_NULL_NAME\\0" + "INVALID_NULL_VALUE\\0" + "INVALID_NUMBERS\\0" + "INVALID_OBJECT_IDENTIFIER\\0" + "INVALID_OPTION\\0" + "INVALID_POLICY_IDENTIFIER\\0" + "INVALID_PROXY_POLICY_SETTING\\0" + "INVALID_PURPOSE\\0" + "INVALID_SECTION\\0" + "INVALID_SYNTAX\\0" + "ISSUER_DECODE_ERROR\\0" + "NEED_ORGANIZATION_AND_NUMBERS\\0" + "NO_CONFIG_DATABASE\\0" + "NO_ISSUER_CERTIFICATE\\0" + "NO_ISSUER_DETAILS\\0" + "NO_POLICY_IDENTIFIER\\0" + "NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED\\0" + "NO_PUBLIC_KEY\\0" + "NO_SUBJECT_DETAILS\\0" + "ODD_NUMBER_OF_DIGITS\\0" + "OPERATION_NOT_DEFINED\\0" + "OTHERNAME_ERROR\\0" + "POLICY_LANGUAGE_ALREADY_DEFINED\\0" + "POLICY_PATH_LENGTH\\0" + "POLICY_PATH_LENGTH_ALREADY_DEFINED\\0" + "POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY\\0" + "SECTION_NOT_FOUND\\0" + "UNABLE_TO_GET_ISSUER_DETAILS\\0" + "UNABLE_TO_GET_ISSUER_KEYID\\0" + "UNKNOWN_BIT_STRING_ARGUMENT\\0" + "UNKNOWN_EXTENSION\\0" + "UNKNOWN_EXTENSION_NAME\\0" + "UNKNOWN_OPTION\\0" + "UNSUPPORTED_OPTION\\0" + "USER_TOO_LONG\\0" + ""; + EOF + END_OF_COMMAND +end diff --git a/src/objective-c/GRPCClient/GRPCCall.h b/src/objective-c/GRPCClient/GRPCCall.h index 5918f8857a..c9fda42855 100644 --- a/src/objective-c/GRPCClient/GRPCCall.h +++ b/src/objective-c/GRPCClient/GRPCCall.h @@ -50,6 +50,8 @@ #import <Foundation/Foundation.h> #import <RxLibrary/GRXWriter.h> +#include <AvailabilityMacros.h> + #pragma mark gRPC errors /** Domain of NSError objects produced by gRPC. */ @@ -161,6 +163,9 @@ extern id const kGRPCTrailersKey; #pragma mark GRPCCall +/** Represents a single gRPC remote call. */ +@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. @@ -170,21 +175,6 @@ extern id const kGRPCTrailersKey; * 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. */ -@protocol GRPCRequestHeaders <NSObject> - -@property(nonatomic, readonly) NSUInteger count; - -- (id)objectForKeyedSubscript:(NSString *)key; -- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; - -- (void)removeAllObjects; -- (void)removeObjectForKey:(NSString *)key; - -@end - -/** Represents a single gRPC remote call. */ -@interface GRPCCall : GRXWriter - /** * 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. @@ -200,7 +190,7 @@ extern id const kGRPCTrailersKey; * * The property is initialized to an empty NSMutableDictionary. */ -@property(atomic, readonly) id<GRPCRequestHeaders> requestHeaders; +@property(atomic, readonly) NSMutableDictionary *requestHeaders; /** * This dictionary is populated with the HTTP headers received from the server. This happens before @@ -243,3 +233,24 @@ extern id const kGRPCTrailersKey; // TODO(jcanizales): Let specify a deadline. As a category of GRXWriter? @end + +#pragma mark Backwards compatibiity + +/** This protocol is kept for backwards compatibility with existing code. */ +DEPRECATED_MSG_ATTRIBUTE("Use NSDictionary or NSMutableDictionary instead.") +@protocol GRPCRequestHeaders <NSObject> +@property(nonatomic, readonly) NSUInteger count; + +- (id)objectForKeyedSubscript:(NSString *)key; +- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; + +- (void)removeAllObjects; +- (void)removeObjectForKey:(NSString *)key; +@end + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +/** This is only needed for backwards-compatibility. */ +@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 b6986bf59c..f79b7d0bc0 100644 --- a/src/objective-c/GRPCClient/GRPCCall.m +++ b/src/objective-c/GRPCClient/GRPCCall.m @@ -221,7 +221,7 @@ NSString * const kGRPCTrailersKey = @"io.grpc.TrailersKey"; #pragma mark Send headers -- (void)sendHeaders:(id<GRPCRequestHeaders>)headers { +- (void)sendHeaders:(NSDictionary *)headers { // TODO(jcanizales): Add error handlers for async failures [_wrappedCall startBatchWithOperations:@[[[GRPCOpSendMetadata alloc] initWithMetadata:headers handler:nil]]]; diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h index cf5a1be9d6..b580f19406 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.h @@ -32,21 +32,14 @@ */ #import <Foundation/Foundation.h> -#include <grpc/grpc.h> #import "../GRPCCall.h" -@interface GRPCRequestHeaders : NSObject<GRPCRequestHeaders> - -@property(nonatomic, readonly) NSUInteger count; -@property(nonatomic, readonly) grpc_metadata *grpc_metadataArray; +@interface GRPCRequestHeaders : NSMutableDictionary - (instancetype)initWithCall:(GRPCCall *)call; -- (id)objectForKeyedSubscript:(NSString *)key; -- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key; - -- (void)removeAllObjects; -- (void)removeObjectForKey:(NSString *)key; +- (instancetype)initWithCall:(GRPCCall *)call + storage:(NSMutableDictionary *)storage NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m index d23f21c0f9..c6a03c145e 100644 --- a/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m +++ b/src/objective-c/GRPCClient/private/GRPCRequestHeaders.m @@ -68,17 +68,44 @@ 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). NSMutableDictionary *_delegate; } +- (instancetype)init { + return [self initWithCall:nil]; +} + +- (instancetype)initWithCapacity:(NSUInteger)numItems { + return [self init]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self init]; +} + - (instancetype)initWithCall:(GRPCCall *)call { + return [self initWithCall:call storage:[NSMutableDictionary dictionary]]; +} + +// Designated initializer +- (instancetype)initWithCall:(GRPCCall *)call storage:(NSMutableDictionary *)storage { + // TODO(jcanizales): Throw if call or storage are nil. if ((self = [super init])) { _call = call; - _delegate = [NSMutableDictionary dictionary]; + _delegate = storage; } return self; } +- (instancetype)initWithObjects:(const id _Nonnull __unsafe_unretained *)objects + forKeys:(const id<NSCopying> _Nonnull __unsafe_unretained *)keys + count:(NSUInteger)cnt { + return [self init]; +} + - (void)checkCallIsNotStarted { if (_call.state != GRXWriterStateNotStarted) { [NSException raise:@"Invalid modification" @@ -86,11 +113,11 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { } } -- (id)objectForKeyedSubscript:(NSString *)key { +- (id)objectForKey:(NSString *)key { return _delegate[key.lowercaseString]; } -- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key { +- (void)setObject:(id)obj forKey:(NSString *)key { [self checkCallIsNotStarted]; CheckIsNonNilASCII(@"Header name", key); key = key.lowercaseString; @@ -103,16 +130,12 @@ static void CheckKeyValuePairIsValid(NSString *key, id value) { [_delegate removeObjectForKey:key.lowercaseString]; } -- (void)removeAllObjects { - [self checkCallIsNotStarted]; - [_delegate removeAllObjects]; -} - - (NSUInteger)count { return _delegate.count; } -- (grpc_metadata *)grpc_metadataArray { - return _delegate.grpc_metadataArray; +- (NSEnumerator * _Nonnull)keyEnumerator { + return [_delegate keyEnumerator]; } + @end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h index 7747aa53ef..71e7e0e54e 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.h +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.h @@ -45,7 +45,7 @@ @interface GRPCOpSendMetadata : GRPCOperation -- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata +- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void(^)())handler NS_DESIGNATED_INITIALIZER; @end diff --git a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m index cea7c479e0..fe3d51da53 100644 --- a/src/objective-c/GRPCClient/private/GRPCWrappedCall.m +++ b/src/objective-c/GRPCClient/private/GRPCWrappedCall.m @@ -65,7 +65,7 @@ return [self initWithMetadata:nil handler:nil]; } -- (instancetype)initWithMetadata:(GRPCRequestHeaders *)metadata handler:(void (^)())handler { +- (instancetype)initWithMetadata:(NSDictionary *)metadata handler:(void (^)())handler { if (self = [super init]) { _op.op = GRPC_OP_SEND_INITIAL_METADATA; _op.data.send_initial_metadata.count = metadata.count; diff --git a/src/objective-c/examples/Sample/Podfile b/src/objective-c/examples/Sample/Podfile index 3b2f412569..93859fb734 100644 --- a/src/objective-c/examples/Sample/Podfile +++ b/src/objective-c/examples/Sample/Podfile @@ -2,6 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' pod 'Protobuf', :path => "../../../../third_party/protobuf" +pod 'BoringSSL', :podspec => "../.." pod 'gRPC', :path => "../../../.." pod 'RemoteTest', :path => "../RemoteTestClient" diff --git a/src/objective-c/examples/SwiftSample/Podfile b/src/objective-c/examples/SwiftSample/Podfile index 3611b00863..f2df4a34a3 100644 --- a/src/objective-c/examples/SwiftSample/Podfile +++ b/src/objective-c/examples/SwiftSample/Podfile @@ -2,6 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' pod 'Protobuf', :path => "../../../../third_party/protobuf" +pod 'BoringSSL', :podspec => "../.." pod 'gRPC', :path => "../../../.." pod 'RemoteTest', :path => "../RemoteTestClient" diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile index cab608d37f..7ec7a25898 100644 --- a/src/objective-c/tests/Podfile +++ b/src/objective-c/tests/Podfile @@ -2,6 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git' platform :ios, '8.0' pod 'Protobuf', :path => "../../../third_party/protobuf" +pod 'BoringSSL', :podspec => ".." pod 'gRPC', :path => "../../.." pod 'RemoteTest', :path => "RemoteTestClient" diff --git a/src/php/bin/run_tests.sh b/src/php/bin/run_tests.sh index 2a7335e20a..235d161ca7 100755 --- a/src/php/bin/run_tests.sh +++ b/src/php/bin/run_tests.sh @@ -30,7 +30,7 @@ # Loads the local shared library, and runs all of the test cases in tests/ # against it -set -e +set -ex cd $(dirname $0)/../../.. root=$(pwd) cd src/php/bin diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index 3b99de7538..7ba14a38d8 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -42,13 +42,13 @@ #include <ext/standard/info.h> #include <ext/spl/spl_exceptions.h> #include "php_grpc.h" +#include "call_credentials.h" #include <zend_exceptions.h> #include <zend_hash.h> #include <stdbool.h> -#include <grpc/support/log.h> #include <grpc/support/alloc.h> #include <grpc/grpc.h> @@ -515,11 +515,41 @@ PHP_METHOD(Call, cancel) { grpc_call_cancel(call->wrapped, NULL); } +/** + * Set the CallCredentials for this call. + * @param CallCredentials creds_obj The CallCredentials object + * @param int The error code + */ +PHP_METHOD(Call, setCredentials) { + zval *creds_obj; + + /* "O" == 1 Object */ + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &creds_obj, + grpc_ce_call_credentials) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "setCredentials expects 1 CallCredentials", + 1 TSRMLS_CC); + return; + } + + wrapped_grpc_call_credentials *creds = + (wrapped_grpc_call_credentials *)zend_object_store_get_object( + creds_obj TSRMLS_CC); + + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); + + grpc_call_error error = GRPC_CALL_ERROR; + error = grpc_call_set_credentials(call->wrapped, creds->wrapped); + RETURN_LONG(error); +} + static zend_function_entry call_methods[] = { PHP_ME(Call, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) PHP_ME(Call, startBatch, NULL, ZEND_ACC_PUBLIC) PHP_ME(Call, getPeer, NULL, ZEND_ACC_PUBLIC) PHP_ME(Call, cancel, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, setCredentials, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; void grpc_init_call(TSRMLS_D) { diff --git a/src/php/ext/grpc/call.h b/src/php/ext/grpc/call.h index f3ef89dc97..73efadae35 100644 --- a/src/php/ext/grpc/call.h +++ b/src/php/ext/grpc/call.h @@ -66,4 +66,8 @@ zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned); * call metadata */ zval *grpc_parse_metadata_array(grpc_metadata_array *metadata_array); +/* Populates a grpc_metadata_array with the data in a PHP array object. + Returns true on success and false on failure */ +bool create_metadata_array(zval *array, grpc_metadata_array *metadata); + #endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */ diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c index 17f167befb..285c4e7c85 100644 --- a/src/php/ext/grpc/call_credentials.c +++ b/src/php/ext/grpc/call_credentials.c @@ -43,6 +43,7 @@ #include <ext/standard/info.h> #include <ext/spl/spl_exceptions.h> #include "php_grpc.h" +#include "call.h" #include <zend_exceptions.h> #include <zend_hash.h> @@ -103,7 +104,7 @@ PHP_METHOD(CallCredentials, createComposite) { zval *cred1_obj; zval *cred2_obj; - /* "OO" == 3 Objects */ + /* "OO" == 2 Objects */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO", &cred1_obj, grpc_ce_call_credentials, &cred2_obj, grpc_ce_call_credentials) == FAILURE) { @@ -125,9 +126,107 @@ PHP_METHOD(CallCredentials, createComposite) { RETURN_DESTROY_ZVAL(creds_object); } +/** + * Create a call credentials object from the plugin API + * @param function callback The callback function + * @return CallCredentials The new call credentials object + */ +PHP_METHOD(CallCredentials, createFromPlugin) { + zend_fcall_info *fci; + zend_fcall_info_cache *fci_cache; + + fci = (zend_fcall_info *)emalloc(sizeof(zend_fcall_info)); + fci_cache = (zend_fcall_info_cache *)emalloc(sizeof(zend_fcall_info_cache)); + memset(fci, 0, sizeof(zend_fcall_info)); + memset(fci_cache, 0, sizeof(zend_fcall_info_cache)); + + /* "f" == 1 function */ + if (zend_parse_parameters(ZEND_NUM_ARGS(), "f", fci, + fci_cache, + fci->params, + fci->param_count) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "createFromPlugin expects 1 callback", + 1 TSRMLS_CC); + return; + } + + plugin_state *state; + state = (plugin_state *)emalloc(sizeof(plugin_state)); + memset(state, 0, sizeof(plugin_state)); + + /* save the user provided PHP callback function */ + state->fci = fci; + state->fci_cache = fci_cache; + + grpc_metadata_credentials_plugin plugin; + plugin.get_metadata = plugin_get_metadata; + plugin.destroy = plugin_destroy_state; + plugin.state = (void *)state; + plugin.type = ""; + + grpc_call_credentials *creds = grpc_metadata_credentials_create_from_plugin( + plugin, NULL); + zval *creds_object = grpc_php_wrap_call_credentials(creds); + RETURN_DESTROY_ZVAL(creds_object); +} + +/* Callback function for plugin creds API */ +void plugin_get_metadata(void *ptr, grpc_auth_metadata_context context, + grpc_credentials_plugin_metadata_cb cb, + void *user_data) { + plugin_state *state = (plugin_state *)ptr; + + /* prepare to call the user callback function with info from the + * grpc_auth_metadata_context */ + zval **params[1]; + zval *arg; + zval *retval; + MAKE_STD_ZVAL(arg); + object_init(arg); + add_property_string(arg, "service_url", context.service_url, true); + add_property_string(arg, "method_name", context.method_name, true); + params[0] = &arg; + state->fci->param_count = 1; + state->fci->params = params; + state->fci->retval_ptr_ptr = &retval; + + /* call the user callback function */ + zend_call_function(state->fci, state->fci_cache); + + if (Z_TYPE_P(retval) != IS_ARRAY) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "plugin callback must return metadata array", + 1 TSRMLS_CC); + } + + grpc_metadata_array metadata; + if (!create_metadata_array(retval, &metadata)) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "invalid metadata", 1 TSRMLS_CC); + grpc_metadata_array_destroy(&metadata); + } + + /* TODO: handle error */ + grpc_status_code code = GRPC_STATUS_OK; + + /* Pass control back to core */ + cb(user_data, metadata.metadata, metadata.count, code, NULL); +} + +/* Cleanup function for plugin creds API */ +void plugin_destroy_state(void *ptr) { + plugin_state *state = (plugin_state *)ptr; + efree(state->fci); + efree(state->fci_cache); + efree(state); +} + static zend_function_entry call_credentials_methods[] = { PHP_ME(CallCredentials, createComposite, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(CallCredentials, createFromPlugin, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; void grpc_init_call_credentials(TSRMLS_D) { diff --git a/src/php/ext/grpc/call_credentials.h b/src/php/ext/grpc/call_credentials.h index 8f35ac68bc..d2f6a92449 100755 --- a/src/php/ext/grpc/call_credentials.h +++ b/src/php/ext/grpc/call_credentials.h @@ -57,6 +57,20 @@ typedef struct wrapped_grpc_call_credentials { grpc_call_credentials *wrapped; } wrapped_grpc_call_credentials; +/* Struct to hold callback function for plugin creds API */ +typedef struct plugin_state { + zend_fcall_info *fci; + zend_fcall_info_cache *fci_cache; +} plugin_state; + +/* Callback function for plugin creds API */ +void plugin_get_metadata(void *state, grpc_auth_metadata_context context, + grpc_credentials_plugin_metadata_cb cb, + void *user_data); + +/* Cleanup function for plugin creds API */ +void plugin_destroy_state(void *ptr); + /* Initializes the CallCredentials PHP class */ void grpc_init_call_credentials(TSRMLS_D); diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c index f8c4f0423f..60c94412dc 100644 --- a/src/php/ext/grpc/channel.c +++ b/src/php/ext/grpc/channel.c @@ -154,16 +154,20 @@ PHP_METHOD(Channel, __construct) { array_hash = Z_ARRVAL_P(args_array); if (zend_hash_find(array_hash, "credentials", sizeof("credentials"), (void **)&creds_obj) == SUCCESS) { - if (zend_get_class_entry(*creds_obj TSRMLS_CC) != + if (Z_TYPE_P(*creds_obj) == IS_NULL) { + creds = NULL; + zend_hash_del(array_hash, "credentials", 12); + } else if (zend_get_class_entry(*creds_obj TSRMLS_CC) != grpc_ce_channel_credentials) { zend_throw_exception(spl_ce_InvalidArgumentException, "credentials must be a ChannelCredentials object", 1 TSRMLS_CC); return; + } else { + creds = (wrapped_grpc_channel_credentials *)zend_object_store_get_object( + *creds_obj TSRMLS_CC); + zend_hash_del(array_hash, "credentials", 12); } - creds = (wrapped_grpc_channel_credentials *)zend_object_store_get_object( - *creds_obj TSRMLS_CC); - zend_hash_del(array_hash, "credentials", 12); } php_grpc_read_args_array(args_array, &args); if (creds == NULL) { diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c index df4a5d5162..ae9a9897fc 100644 --- a/src/php/ext/grpc/channel_credentials.c +++ b/src/php/ext/grpc/channel_credentials.c @@ -169,6 +169,14 @@ PHP_METHOD(ChannelCredentials, createComposite) { RETURN_DESTROY_ZVAL(creds_object); } +/** + * Create insecure channel credentials + * @return null + */ +PHP_METHOD(ChannelCredentials, createInsecure) { + RETURN_NULL(); +} + static zend_function_entry channel_credentials_methods[] = { PHP_ME(ChannelCredentials, createDefault, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) @@ -176,6 +184,8 @@ static zend_function_entry channel_credentials_methods[] = { ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(ChannelCredentials, createComposite, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(ChannelCredentials, createInsecure, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; void grpc_init_channel_credentials(TSRMLS_D) { diff --git a/src/php/ext/grpc/package.xml b/src/php/ext/grpc/package.xml index a2e3e2826a..9c98f82540 100644 --- a/src/php/ext/grpc/package.xml +++ b/src/php/ext/grpc/package.xml @@ -10,11 +10,11 @@ <email>grpc-packages@google.com</email> <active>yes</active> </lead> - <date>2015-10-21</date> - <time>17:04:32</time> + <date>2015-12-16</date> + <time>12:56:11</time> <version> - <release>0.6.1</release> - <api>0.6.0</api> + <release>0.7.0</release> + <api>0.7.0</api> </version> <stability> <release>beta</release> @@ -22,19 +22,22 @@ </stability> <license>BSD</license> <notes> -- fixed undefined constant fatal error when run with apache/nginx #2275 +- Breaking change to Credentials class (removed) #3765 +- Replaced by ChannelCredentials and CallCredentials class #3765 +- New plugin based metadata auth API #4394 +- Explicit ChannelCredentials::createInsecure() call </notes> <contents> <dir baseinstalldir="/" name="/"> <file baseinstalldir="/" md5sum="6f19828fb869b7b8a590cbb76b4f996d" name="byte_buffer.c" role="src" /> <file baseinstalldir="/" md5sum="c8de0f819499c48adfc8d7f472c0196b" name="byte_buffer.h" role="src" /> - <file baseinstalldir="/" md5sum="d64c9005993de02abac55664b0b9e0b2" name="call.c" role="src" /> - <file baseinstalldir="/" md5sum="26acbf04c30162c2d2aca4688bb2aec8" name="call.h" role="src" /> - <file baseinstalldir="/" md5sum="6fa13d260dfde216f795225644f04e7a" name="call_credentials.c" role="src" /> - <file baseinstalldir="/" md5sum="e45269975f9a30fd349a90daf6b31aa2" name="call_credentials.h" role="src" /> - <file baseinstalldir="/" md5sum="0779db3b196c98081b2260ceec22cd4d" name="channel.c" role="src" /> + <file baseinstalldir="/" md5sum="ee7eb7757f9e6f0e36f8f616b6bd0af5" name="call.c" role="src" /> + <file baseinstalldir="/" md5sum="44c56bd9912d2538cbd6059e3e0452b6" name="call.h" role="src" /> + <file baseinstalldir="/" md5sum="ff90f6c03ed44b5f4170bf3259a6704e" name="call_credentials.c" role="src" /> + <file baseinstalldir="/" md5sum="3c3860e1d84f43cb6b2fbaa8d2ae1ab7" name="call_credentials.h" role="src" /> + <file baseinstalldir="/" md5sum="aee9b63f790522aec2c682055240cc61" name="channel.c" role="src" /> <file baseinstalldir="/" md5sum="ed4b00c0cf3702b115d0cfa87450dc09" name="channel.h" role="src" /> - <file baseinstalldir="/" md5sum="1b40f50fa6184ad7d24a961ac76151ff" name="channel_credentials.c" role="src" /> + <file baseinstalldir="/" md5sum="1a51c76d0b7b7d3ab570ed7d60c2ea46" name="channel_credentials.c" role="src" /> <file baseinstalldir="/" md5sum="a86250e03f610ce6c2c7595a84e08821" name="channel_credentials.h" role="src" /> <file baseinstalldir="/" md5sum="55ab7a42f9dd9bfc7e28a61cfc5fca63" name="completion_queue.c" role="src" /> <file baseinstalldir="/" md5sum="f10b5bb232d74a6878e829e2e76cdaa2" name="completion_queue.h" role="src" /> @@ -130,5 +133,23 @@ Update to wrap gRPC C Core version 0.10.0 - fixed undefined constant fatal error when run with apache/nginx #2275 </notes> </release> + <release> + <version> + <release>0.7.0</release> + <api>0.7.0</api> + </version> + <stability> + <release>beta</release> + <api>beta</api> + </stability> + <date>2015-12-16</date> + <license>BSD</license> + <notes> +- Breaking change to Credentials class (removed) #3765 +- Replaced by ChannelCredentials and CallCredentials class #3765 +- New plugin based metadata auth API #4394 +- Explicit ChannelCredentials::createInsecure() call + </notes> + </release> </changelog> </package> diff --git a/src/php/lib/Grpc/AbstractCall.php b/src/php/lib/Grpc/AbstractCall.php index 53849d51fc..712af91eb2 100644 --- a/src/php/lib/Grpc/AbstractCall.php +++ b/src/php/lib/Grpc/AbstractCall.php @@ -43,19 +43,20 @@ abstract class AbstractCall /** * Create a new Call wrapper object. * - * @param Channel $channel The channel to communicate on - * @param string $method The method to call on the - * remote server - * @param callback $deserialize A callback function to deserialize - * the response - * @param (optional) long $timeout Timeout in microseconds + * @param Channel $channel The channel to communicate on + * @param string $method The method to call on the + * remote server + * @param callback $deserialize A callback function to deserialize + * the response + * @param array $options Call options (optional) */ public function __construct(Channel $channel, $method, $deserialize, - $timeout = false) + $options = []) { - if ($timeout) { + if (isset($options['timeout']) && + is_numeric($timeout = $options['timeout'])) { $now = Timeval::now(); $delta = new Timeval($timeout); $deadline = $now->add($delta); @@ -65,6 +66,13 @@ abstract class AbstractCall $this->call = new Call($channel, $method, $deadline); $this->deserialize = $deserialize; $this->metadata = null; + if (isset($options['call_credentials_callback']) && + is_callable($call_credentials_callback = + $options['call_credentials_callback'])) { + $call_credentials = CallCredentials::createFromPlugin( + $call_credentials_callback); + $this->call->setCredentials($call_credentials); + } } /** @@ -106,4 +114,15 @@ abstract class AbstractCall return call_user_func($this->deserialize, $value); } + + /** + * Set the CallCredentials for the underlying Call. + * + * @param CallCredentials $call_credentials The CallCredentials + * object + */ + public function setCallCredentials($call_credentials) + { + $this->call->setCredentials($call_credentials); + } } diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php index 7b2627f516..2de1b337e5 100755 --- a/src/php/lib/Grpc/BaseStub.php +++ b/src/php/lib/Grpc/BaseStub.php @@ -72,6 +72,11 @@ class BaseStub } $opts['grpc.primary_user_agent'] .= 'grpc-php/'.$package_config['version']; + if (!array_key_exists('credentials', $opts)) { + throw new \Exception("The opts['credentials'] key is now ". + 'required. Please see one of the '. + 'ChannelCredentials::create methods'); + } $this->channel = new Channel($hostname, $opts); } @@ -159,25 +164,6 @@ class BaseStub } /** - * extract $timeout from $metadata. - * - * @param $metadata The metadata map - * - * @return list($metadata_copy, $timeout) - */ - private function _extract_timeout_from_metadata($metadata) - { - $timeout = false; - $metadata_copy = $metadata; - if (isset($metadata['timeout'])) { - $timeout = $metadata['timeout']; - unset($metadata_copy['timeout']); - } - - return [$metadata_copy, $timeout]; - } - - /** * validate and normalize the metadata array. * * @param $metadata The metadata map @@ -220,21 +206,19 @@ class BaseStub $metadata = [], $options = []) { - list($actual_metadata, $timeout) = - $this->_extract_timeout_from_metadata($metadata); $call = new UnaryCall($this->channel, $method, $deserialize, - $timeout); + $options); $jwt_aud_uri = $this->_get_jwt_aud_uri($method); if (is_callable($this->update_metadata)) { - $actual_metadata = call_user_func($this->update_metadata, - $actual_metadata, + $metadata = call_user_func($this->update_metadata, + $metadata, $jwt_aud_uri); } - $actual_metadata = $this->_validate_and_normalize_metadata( - $actual_metadata); - $call->start($argument, $actual_metadata, $options); + $metadata = $this->_validate_and_normalize_metadata( + $metadata); + $call->start($argument, $metadata, $options); return $call; } @@ -253,23 +237,22 @@ class BaseStub */ public function _clientStreamRequest($method, callable $deserialize, - $metadata = []) + $metadata = [], + $options = []) { - list($actual_metadata, $timeout) = - $this->_extract_timeout_from_metadata($metadata); $call = new ClientStreamingCall($this->channel, $method, $deserialize, - $timeout); + $options); $jwt_aud_uri = $this->_get_jwt_aud_uri($method); if (is_callable($this->update_metadata)) { - $actual_metadata = call_user_func($this->update_metadata, - $actual_metadata, + $metadata = call_user_func($this->update_metadata, + $metadata, $jwt_aud_uri); } - $actual_metadata = $this->_validate_and_normalize_metadata( - $actual_metadata); - $call->start($actual_metadata); + $metadata = $this->_validate_and_normalize_metadata( + $metadata); + $call->start($metadata); return $call; } @@ -291,21 +274,19 @@ class BaseStub $metadata = [], $options = []) { - list($actual_metadata, $timeout) = - $this->_extract_timeout_from_metadata($metadata); $call = new ServerStreamingCall($this->channel, $method, $deserialize, - $timeout); + $options); $jwt_aud_uri = $this->_get_jwt_aud_uri($method); if (is_callable($this->update_metadata)) { - $actual_metadata = call_user_func($this->update_metadata, - $actual_metadata, + $metadata = call_user_func($this->update_metadata, + $metadata, $jwt_aud_uri); } - $actual_metadata = $this->_validate_and_normalize_metadata( - $actual_metadata); - $call->start($argument, $actual_metadata, $options); + $metadata = $this->_validate_and_normalize_metadata( + $metadata); + $call->start($argument, $metadata, $options); return $call; } @@ -321,23 +302,22 @@ class BaseStub */ public function _bidiRequest($method, callable $deserialize, - $metadata = []) + $metadata = [], + $options = []) { - list($actual_metadata, $timeout) = - $this->_extract_timeout_from_metadata($metadata); $call = new BidiStreamingCall($this->channel, $method, $deserialize, - $timeout); + $options); $jwt_aud_uri = $this->_get_jwt_aud_uri($method); if (is_callable($this->update_metadata)) { - $actual_metadata = call_user_func($this->update_metadata, - $actual_metadata, + $metadata = call_user_func($this->update_metadata, + $metadata, $jwt_aud_uri); } - $actual_metadata = $this->_validate_and_normalize_metadata( - $actual_metadata); - $call->start($actual_metadata); + $metadata = $this->_validate_and_normalize_metadata( + $metadata); + $call->start($metadata); return $call; } diff --git a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php index 4a0bd6a1f1..1fe81b9d54 100644 --- a/src/php/tests/generated_code/AbstractGeneratedCodeTest.php +++ b/src/php/tests/generated_code/AbstractGeneratedCodeTest.php @@ -41,7 +41,6 @@ abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase * running on $GRPC_TEST_HOST. */ protected static $client; - protected static $timeout; public function testWaitForNotReady() { @@ -93,7 +92,7 @@ abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase public function testTimeout() { $div_arg = new math\DivArgs(); - $call = self::$client->Div($div_arg, ['timeout' => 100]); + $call = self::$client->Div($div_arg, [], ['timeout' => 100]); list($response, $status) = $call->wait(); $this->assertSame(\Grpc\STATUS_DEADLINE_EXCEEDED, $status->code); } @@ -112,7 +111,9 @@ abstract class AbstractGeneratedCodeTest extends PHPUnit_Framework_TestCase */ public function testInvalidMethodName() { - $invalid_client = new DummyInvalidClient('host', []); + $invalid_client = new DummyInvalidClient('host', [ + 'credentials' => Grpc\ChannelCredentials::createInsecure(), + ]); $div_arg = new math\DivArgs(); $invalid_client->InvalidUnaryCall($div_arg); } diff --git a/src/php/tests/generated_code/GeneratedCodeTest.php b/src/php/tests/generated_code/GeneratedCodeTest.php index 7043e8e1d1..0cdce6cf92 100755 --- a/src/php/tests/generated_code/GeneratedCodeTest.php +++ b/src/php/tests/generated_code/GeneratedCodeTest.php @@ -38,10 +38,12 @@ class GeneratedCodeTest extends AbstractGeneratedCodeTest public function setUp() { self::$client = new math\MathClient( - getenv('GRPC_TEST_HOST'), []); + getenv('GRPC_TEST_HOST'), [ + 'credentials' => Grpc\ChannelCredentials::createInsecure(), + ]); } - public static function tearDownAfterClass() + public function tearDown() { self::$client->close(); } diff --git a/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php b/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php index 5a20e684c7..6bb1955ccb 100644 --- a/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php +++ b/src/php/tests/generated_code/GeneratedCodeWithCallbackTest.php @@ -39,7 +39,8 @@ class GeneratedCodeWithCallbackTest extends AbstractGeneratedCodeTest { self::$client = new math\MathClient( getenv('GRPC_TEST_HOST'), - ['update_metadata' => function ($a_hash, + ['credentials' => Grpc\ChannelCredentials::createInsecure(), + 'update_metadata' => function ($a_hash, $client = []) { $a_copy = $a_hash; $a_copy['foo'] = ['bar']; @@ -48,7 +49,7 @@ class GeneratedCodeWithCallbackTest extends AbstractGeneratedCodeTest }]); } - public static function tearDownAfterClass() + public function tearDown() { self::$client->close(); } diff --git a/src/php/tests/generated_code/math.proto b/src/php/tests/generated_code/math.proto index 1de7d0b8de..c872ee6e0b 100644 --- a/src/php/tests/generated_code/math.proto +++ b/src/php/tests/generated_code/math.proto @@ -28,6 +28,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// TODO: start using src/proto/math/math.proto and remove this file once +// PHP supports proto3. + syntax = "proto2"; package math; diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php index 9aab5c966c..aebf80f6bf 100755 --- a/src/php/tests/interop/interop_client.php +++ b/src/php/tests/interop/interop_client.php @@ -84,7 +84,7 @@ function largeUnary($stub) * @param $fillOauthScope boolean whether to fill result with oauth scope */ function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false, - $metadata = []) + $callback = false) { $request_len = 271828; $response_len = 314159; @@ -99,7 +99,12 @@ function performLargeUnary($stub, $fillUsername = false, $fillOauthScope = false $request->setFillUsername($fillUsername); $request->setFillOauthScope($fillOauthScope); - list($result, $status) = $stub->UnaryCall($request, $metadata)->wait(); + $options = false; + if ($callback) { + $options['call_credentials_callback'] = $callback; + } + + list($result, $status) = $stub->UnaryCall($request, [], $options)->wait(); hardAssert($status->code === Grpc\STATUS_OK, 'Call did not complete successfully'); hardAssert($result !== null, 'Call returned a null response'); $payload = $result->getPayload(); @@ -186,6 +191,15 @@ function oauth2AuthToken($stub, $args) 'invalid email returned'); } +function updateAuthMetadataCallback($context) +{ + $authUri = $context->service_url; + $methodName = $context->method_name; + $auth_credentials = ApplicationDefaultCredentials::getCredentials(); + + return $auth_credentials->updateMetadata($metadata = [], $authUri); +} + /** * Run the per_rpc_creds auth test. * @@ -197,15 +211,9 @@ function perRpcCreds($stub, $args) $jsonKey = json_decode( file_get_contents(getenv(CredentialsLoader::ENV_VAR)), true); - $auth_credentials = ApplicationDefaultCredentials::getCredentials( - $args['oauth_scope'] - ); - $token = $auth_credentials->fetchAuthToken(); - $metadata = [CredentialsLoader::AUTH_METADATA_KEY => [sprintf('%s %s', - $token['token_type'], - $token['access_token'])]]; + $result = performLargeUnary($stub, $fillUsername = true, $fillOauthScope = true, - $metadata); + 'updateAuthMetadataCallback'); hardAssert($result->getUsername() == $jsonKey['client_email'], 'invalid email returned'); } @@ -363,7 +371,7 @@ function cancelAfterFirstResponse($stub) function timeoutOnSleepingServer($stub) { - $call = $stub->FullDuplexCall(['timeout' => 1000]); + $call = $stub->FullDuplexCall([], ['timeout' => 1000]); $request = new grpc\testing\StreamingOutputCallRequest(); $request->setResponseType(grpc\testing\PayloadType::COMPRESSABLE); $response_parameters = new grpc\testing\ResponseParameters(); @@ -430,6 +438,8 @@ if ($use_tls) { } $opts['credentials'] = $ssl_credentials; $opts['grpc.ssl_target_name_override'] = $host_override; +} else { + $opts['credentials'] = Grpc\ChannelCredentials::createInsecure(); } if (in_array($test_case, ['service_account_creds', diff --git a/src/php/tests/unit_tests/CallCredentialsTest.php b/src/php/tests/unit_tests/CallCredentialsTest.php new file mode 100644 index 0000000000..0918412781 --- /dev/null +++ b/src/php/tests/unit_tests/CallCredentialsTest.php @@ -0,0 +1,137 @@ +<?php +/* + * + * 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. + * + */ + +class CallCredentialsTest extends PHPUnit_Framework_TestCase +{ + public function setUp() + { + $credentials = Grpc\ChannelCredentials::createSsl( + file_get_contents(dirname(__FILE__).'/../data/ca.pem')); + $call_credentials = Grpc\CallCredentials::createFromPlugin( + array($this, 'callbackFunc')); + $credentials = Grpc\ChannelCredentials::createComposite( + $credentials, + $call_credentials + ); + $server_credentials = Grpc\ServerCredentials::createSsl( + null, + file_get_contents(dirname(__FILE__).'/../data/server1.key'), + file_get_contents(dirname(__FILE__).'/../data/server1.pem')); + $this->server = new Grpc\Server(); + $this->port = $this->server->addSecureHttp2Port('0.0.0.0:0', + $server_credentials); + $this->server->start(); + $this->host_override = 'foo.test.google.fr'; + $this->channel = new Grpc\Channel( + 'localhost:'.$this->port, + [ + 'grpc.ssl_target_name_override' => $this->host_override, + 'grpc.default_authority' => $this->host_override, + 'credentials' => $credentials, + ] + ); + } + + public function tearDown() + { + unset($this->channel); + unset($this->server); + } + + public function callbackFunc($context) + { + $this->assertTrue(is_string($context->service_url)); + $this->assertTrue(is_string($context->method_name)); + + return ['k1' => ['v1'], 'k2' => ['v2']]; + } + + public function testCreateFromPlugin() + { + $deadline = Grpc\Timeval::infFuture(); + $status_text = 'xyz'; + $call = new Grpc\Call($this->channel, + '/abc/dummy_method', + $deadline, + $this->host_override); + + $event = $call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_close); + + $event = $this->server->requestCall(); + + $this->assertTrue(is_array($event->metadata)); + $metadata = $event->metadata; + $this->assertTrue(array_key_exists('k1', $metadata)); + $this->assertTrue(array_key_exists('k2', $metadata)); + $this->assertSame($metadata['k1'], ['v1']); + $this->assertSame($metadata['k2'], ['v2']); + + $this->assertSame('/abc/dummy_method', $event->method); + $server_call = $event->call; + + $event = $server_call->startBatch([ + Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_STATUS_FROM_SERVER => [ + 'metadata' => [], + 'code' => Grpc\STATUS_OK, + 'details' => $status_text, + ], + Grpc\OP_RECV_CLOSE_ON_SERVER => true, + ]); + + $this->assertTrue($event->send_metadata); + $this->assertTrue($event->send_status); + $this->assertFalse($event->cancelled); + + $event = $call->startBatch([ + Grpc\OP_RECV_INITIAL_METADATA => true, + Grpc\OP_RECV_STATUS_ON_CLIENT => true, + ]); + + $this->assertSame([], $event->metadata); + $status = $event->status; + $this->assertSame([], $status->metadata); + $this->assertSame(Grpc\STATUS_OK, $status->code); + $this->assertSame($status_text, $status->details); + + unset($call); + unset($server_call); + } +} diff --git a/src/ruby/pb/grpc/health/v1alpha/health.proto b/src/proto/grpc/health/v1alpha/health.proto index d31df1e0a7..28786c4427 100644 --- a/src/ruby/pb/grpc/health/v1alpha/health.proto +++ b/src/proto/grpc/health/v1alpha/health.proto @@ -30,6 +30,7 @@ syntax = "proto3"; package grpc.health.v1alpha; +option csharp_namespace = "Grpc.Health.V1Alpha"; message HealthCheckRequest { string host = 1; diff --git a/src/csharp/Grpc.Examples/proto/math.proto b/src/proto/math/math.proto index 311e148c02..311e148c02 100644 --- a/src/csharp/Grpc.Examples/proto/math.proto +++ b/src/proto/math/math.proto diff --git a/src/python/grpcio/.gitignore b/src/python/grpcio/.gitignore index 4c02b8d14d..95b96f7c1e 100644 --- a/src/python/grpcio/.gitignore +++ b/src/python/grpcio/.gitignore @@ -5,5 +5,12 @@ dist/ *.egg *.egg/ *.eggs/ +*_pb2.py +.coverage +.coverage.* +.cache/ +.tox/ +nosetests.xml doc/ _grpcio_metadata.py +htmlcov/ diff --git a/src/python/grpcio/MANIFEST.in b/src/python/grpcio/MANIFEST.in index 9583dc7768..407eeabc17 100644 --- a/src/python/grpcio/MANIFEST.in +++ b/src/python/grpcio/MANIFEST.in @@ -1,3 +1,4 @@ graft grpc +graft tests include commands.py include requirements.txt diff --git a/src/python/grpcio/commands.py b/src/python/grpcio/commands.py index 8a2f2d6283..d9fd023b21 100644 --- a/src/python/grpcio/commands.py +++ b/src/python/grpcio/commands.py @@ -29,14 +29,18 @@ """Provides distutils command classes for the GRPC Python setup process.""" +import distutils import os import os.path +import re +import subprocess import sys import setuptools from setuptools.command import build_py +from setuptools.command import test -_CONF_PY_ADDENDUM = """ +CONF_PY_ADDENDUM = """ extensions.append('sphinx.ext.napoleon') napoleon_google_docstring = True napoleon_numpy_docstring = True @@ -48,7 +52,7 @@ html_theme = 'sphinx_rtd_theme' class SphinxDocumentation(setuptools.Command): """Command to generate documentation via sphinx.""" - description = '' + description = 'generate sphinx documentation' user_options = [] def initialize_options(self): @@ -72,14 +76,61 @@ class SphinxDocumentation(setuptools.Command): '-o', os.path.join('doc', 'src'), src_dir]) conf_filepath = os.path.join('doc', 'src', 'conf.py') with open(conf_filepath, 'a') as conf_file: - conf_file.write(_CONF_PY_ADDENDUM) + conf_file.write(CONF_PY_ADDENDUM) sphinx.main(['', os.path.join('doc', 'src'), os.path.join('doc', 'build')]) +class BuildProtoModules(setuptools.Command): + """Command to generate project *_pb2.py modules from proto files.""" + + description = 'build protobuf modules' + user_options = [ + ('include=', None, 'path patterns to include in protobuf generation'), + ('exclude=', None, 'path patterns to exclude from protobuf generation') + ] + + def initialize_options(self): + self.exclude = None + self.include = r'.*\.proto$' + self.protoc_command = None + self.grpc_python_plugin_command = None + + def finalize_options(self): + self.protoc_command = distutils.spawn.find_executable('protoc') + self.grpc_python_plugin_command = distutils.spawn.find_executable( + 'grpc_python_plugin') + + def run(self): + include_regex = re.compile(self.include) + exclude_regex = re.compile(self.exclude) if self.exclude else None + paths = [] + root_directory = os.getcwd() + for walk_root, directories, filenames in os.walk(root_directory): + for filename in filenames: + path = os.path.join(walk_root, filename) + if include_regex.match(path) and not ( + exclude_regex and exclude_regex.match(path)): + paths.append(path) + command = [ + self.protoc_command, + '--plugin=protoc-gen-python-grpc={}'.format( + self.grpc_python_plugin_command), + '-I {}'.format(root_directory), + '--python_out={}'.format(root_directory), + '--python-grpc_out={}'.format(root_directory), + ] + paths + try: + subprocess.check_output(' '.join(command), cwd=root_directory, shell=True, + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + raise Exception('Command:\n{}\nMessage:\n{}\nOutput:\n{}'.format( + command, e.message, e.output)) + + class BuildProjectMetadata(setuptools.Command): """Command to generate project metadata in a module.""" - description = '' + description = 'build grpcio project metadata files' user_options = [] def initialize_options(self): @@ -98,5 +149,73 @@ class BuildPy(build_py.build_py): """Custom project build command.""" def run(self): + self.run_command('build_proto_modules') self.run_command('build_project_metadata') build_py.build_py.run(self) + + +class Gather(setuptools.Command): + """Command to gather project dependencies.""" + + description = 'gather dependencies for grpcio' + user_options = [ + ('test', 't', 'flag indicating to gather test dependencies'), + ('install', 'i', 'flag indicating to gather install dependencies') + ] + + def initialize_options(self): + self.test = False + self.install = False + + def finalize_options(self): + # distutils requires this override. + pass + + def run(self): + if self.install and self.distribution.install_requires: + self.distribution.fetch_build_eggs(self.distribution.install_requires) + if self.test and self.distribution.tests_require: + self.distribution.fetch_build_eggs(self.distribution.tests_require) + + +class RunInterop(test.test): + + description = 'run interop test client/server' + user_options = [ + ('args=', 'a', 'pass-thru arguments for the client/server'), + ('client', 'c', 'flag indicating to run the client'), + ('server', 's', 'flag indicating to run the server') + ] + + def initialize_options(self): + self.args = '' + self.client = False + self.server = False + + def finalize_options(self): + if self.client and self.server: + raise DistutilsOptionError('you may only specify one of client or server') + + def run(self): + if self.distribution.install_requires: + self.distribution.fetch_build_eggs(self.distribution.install_requires) + if self.distribution.tests_require: + self.distribution.fetch_build_eggs(self.distribution.tests_require) + if self.client: + self.run_client() + elif self.server: + self.run_server() + + def run_server(self): + # We import here to ensure that our setuptools parent has had a chance to + # edit the Python system path. + from tests.interop import server + sys.argv[1:] = self.args.split() + server.serve() + + def run_client(self): + # We import here to ensure that our setuptools parent has had a chance to + # edit the Python system path. + from tests.interop import client + sys.argv[1:] = self.args.split() + client.test_interoperability() diff --git a/src/python/grpcio/grpc/_adapter/_c/types.h b/src/python/grpcio/grpc/_adapter/_c/types.h deleted file mode 100644 index 9ab415d216..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c/types.h +++ /dev/null @@ -1,286 +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__ADAPTER__C_TYPES_H_ -#define GRPC__ADAPTER__C_TYPES_H_ - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> -#include <grpc/grpc_security.h> - - -/*=========================*/ -/* Client-side credentials */ -/*=========================*/ - -typedef struct ChannelCredentials { - PyObject_HEAD - grpc_channel_credentials *c_creds; -} ChannelCredentials; -void pygrpc_ChannelCredentials_dealloc(ChannelCredentials *self); -ChannelCredentials *pygrpc_ChannelCredentials_google_default( - PyTypeObject *type, PyObject *ignored); -ChannelCredentials *pygrpc_ChannelCredentials_ssl( - PyTypeObject *type, PyObject *args, PyObject *kwargs); -ChannelCredentials *pygrpc_ChannelCredentials_composite( - PyTypeObject *type, PyObject *args, PyObject *kwargs); -extern PyTypeObject pygrpc_ChannelCredentials_type; - -typedef struct CallCredentials { - PyObject_HEAD - grpc_call_credentials *c_creds; -} CallCredentials; -void pygrpc_CallCredentials_dealloc(CallCredentials *self); -CallCredentials *pygrpc_CallCredentials_composite( - PyTypeObject *type, PyObject *args, PyObject *kwargs); -CallCredentials *pygrpc_CallCredentials_compute_engine( - PyTypeObject *type, PyObject *ignored); -CallCredentials *pygrpc_CallCredentials_jwt( - PyTypeObject *type, PyObject *args, PyObject *kwargs); -CallCredentials *pygrpc_CallCredentials_refresh_token( - PyTypeObject *type, PyObject *args, PyObject *kwargs); -CallCredentials *pygrpc_CallCredentials_iam( - PyTypeObject *type, PyObject *args, PyObject *kwargs); -extern PyTypeObject pygrpc_CallCredentials_type; - -/*=========================*/ -/* Server-side credentials */ -/*=========================*/ - -typedef struct ServerCredentials { - PyObject_HEAD - grpc_server_credentials *c_creds; -} ServerCredentials; -void pygrpc_ServerCredentials_dealloc(ServerCredentials *self); -ServerCredentials *pygrpc_ServerCredentials_ssl( - PyTypeObject *type, PyObject *args, PyObject *kwargs); -extern PyTypeObject pygrpc_ServerCredentials_type; - - -/*==================*/ -/* Completion queue */ -/*==================*/ - -typedef struct CompletionQueue { - PyObject_HEAD - grpc_completion_queue *c_cq; -} CompletionQueue; -CompletionQueue *pygrpc_CompletionQueue_new( - PyTypeObject *type, PyObject *args, PyObject *kwargs); -void pygrpc_CompletionQueue_dealloc(CompletionQueue *self); -PyObject *pygrpc_CompletionQueue_next( - CompletionQueue *self, PyObject *args, PyObject *kwargs); -PyObject *pygrpc_CompletionQueue_shutdown( - CompletionQueue *self, PyObject *ignored); -extern PyTypeObject pygrpc_CompletionQueue_type; - - -/*======*/ -/* Call */ -/*======*/ - -typedef struct Call { - PyObject_HEAD - grpc_call *c_call; - CompletionQueue *cq; -} Call; -Call *pygrpc_Call_new_empty(CompletionQueue *cq); -void pygrpc_Call_dealloc(Call *self); -PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs); -PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs); -PyObject *pygrpc_Call_peer(Call *self); -PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args, - PyObject *kwargs); -extern PyTypeObject pygrpc_Call_type; - - -/*=========*/ -/* Channel */ -/*=========*/ - -typedef struct Channel { - PyObject_HEAD - grpc_channel *c_chan; -} Channel; -Channel *pygrpc_Channel_new( - PyTypeObject *type, PyObject *args, PyObject *kwargs); -void pygrpc_Channel_dealloc(Channel *self); -Call *pygrpc_Channel_create_call( - Channel *self, PyObject *args, PyObject *kwargs); -PyObject *pygrpc_Channel_check_connectivity_state(Channel *self, PyObject *args, - PyObject *kwargs); -PyObject *pygrpc_Channel_watch_connectivity_state(Channel *self, PyObject *args, - PyObject *kwargs); -PyObject *pygrpc_Channel_target(Channel *self); -extern PyTypeObject pygrpc_Channel_type; - - -/*========*/ -/* Server */ -/*========*/ - -typedef struct Server { - PyObject_HEAD - grpc_server *c_serv; - CompletionQueue *cq; - int shutdown_called; -} Server; -Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs); -void pygrpc_Server_dealloc(Server *self); -PyObject *pygrpc_Server_request_call( - Server *self, PyObject *args, PyObject *kwargs); -PyObject *pygrpc_Server_add_http2_port( - Server *self, PyObject *args, PyObject *kwargs); -PyObject *pygrpc_Server_start(Server *self, PyObject *ignored); -PyObject *pygrpc_Server_shutdown( - Server *self, PyObject *args, PyObject *kwargs); -PyObject *pygrpc_Server_cancel_all_calls(Server *self, PyObject *unused); -extern PyTypeObject pygrpc_Server_type; - -/*=========*/ -/* Utility */ -/*=========*/ - -/* Every tag that passes from Python GRPC to GRPC core is of this type. */ -typedef struct pygrpc_tag { - PyObject *user_tag; - Call *call; - grpc_call_details request_call_details; - grpc_metadata_array request_metadata; - grpc_op *ops; - size_t nops; - int is_new_call; -} pygrpc_tag; - -/* Construct a tag associated with a batch call. Does not take ownership of the - resources in the elements of ops. */ -pygrpc_tag *pygrpc_produce_batch_tag(PyObject *user_tag, Call *call, - grpc_op *ops, size_t nops); - - -/* Construct a tag associated with a server request. The calling code should - use the appropriate fields of the produced tag in the invocation of - grpc_server_request_call. */ -pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call); - -/* Construct a tag associated with a server shutdown. */ -pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag); - -/* Construct a tag associated with a channel state change. */ -pygrpc_tag *pygrpc_produce_channel_state_change_tag(PyObject *user_tag); - -/* Frees all resources owned by the tag and the tag itself. */ -void pygrpc_discard_tag(pygrpc_tag *tag); - -/* Consumes an event and its associated tag, providing a Python tuple of the - form `(type, tag, call, call_details, results)` (where type is an integer - corresponding to a grpc_completion_type, tag is an arbitrary PyObject, call - is the call object associated with the event [if any], call_details is a - tuple of form `(method, host, deadline)` [if such details are available], - and resultd is a list of tuples of form `(type, metadata, message, status, - cancelled)` [where type corresponds to a grpc_op_type, metadata is a - sequence of 2-sequences of strings, message is a byte string, and status is - a 2-tuple of an integer corresponding to grpc_status_code and a string of - status details]). - - Frees all resources associated with the event tag. */ -PyObject *pygrpc_consume_event(grpc_event event); - -/* Transliterate the Python tuple of form `(type, metadata, message, - status)` (where type is an integer corresponding to a grpc_op_type, metadata - is a sequence of 2-sequences of strings, message is a byte string, and - status is 2-tuple of an integer corresponding to grpc_status_code and a - string of status details) to a grpc_op suitable for use in a - grpc_call_start_batch invocation. The grpc_op is a 'directory' of resources - that must be freed after GRPC core is done with them. - - Calls gpr_malloc (or the appropriate type-specific grpc_*_create function) - to populate the appropriate union-discriminated members of the op. - - Returns true on success, false on failure. */ -int pygrpc_produce_op(PyObject *op, grpc_op *result); - -/* Discards all resources associated with the passed in op that was produced by - pygrpc_produce_op. */ -void pygrpc_discard_op(grpc_op op); - -/* Transliterate the grpc_ops (which have been sent through a - grpc_call_start_batch invocation and whose corresponding event has appeared - on a completion queue) to a Python tuple of form `(type, metadata, message, - status, cancelled)` (where type is an integer corresponding to a - grpc_op_type, metadata is a sequence of 2-sequences of strings, message is a - byte string, and status is 2-tuple of an integer corresponding to - grpc_status_code and a string of status details). - - Calls gpr_free (or the appropriate type-specific grpc_*_destroy function) on - the appropriate union-discriminated populated members of the ops. */ -PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops); - -/* Transliterate from a gpr_timespec to a double (in units of seconds, either - from the epoch if interpreted absolutely or as a delta otherwise). */ -double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec); - -/* Transliterate from a double (in units of seconds from the epoch if - interpreted absolutely or as a delta otherwise) to a gpr_timespec. */ -gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds); - -/* Returns true on success, false on failure. */ -int pygrpc_cast_pyseq_to_send_metadata( - PyObject *pyseq, grpc_metadata **metadata, size_t *count); -/* Returns a metadata array as a Python object on success, else NULL. */ -PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata); - -/* Transliterate from a list of python channel arguments (2-tuples of string - and string|integer|None) to a grpc_channel_args object. The strings placed - in the grpc_channel_args object's grpc_arg elements are views of the Python - object. The Python object must live long enough for the grpc_channel_args - to be used. Arguments set to None are silently ignored. Returns true on - success, false on failure. */ -int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args); -void pygrpc_discard_channel_args(grpc_channel_args args); - -/* Read the bytes from grpc_byte_buffer to a gpr_malloc'd array of bytes; - output to result and result_size. */ -void pygrpc_byte_buffer_to_bytes( - grpc_byte_buffer *buffer, char **result, size_t *result_size); - - -/*========*/ -/* Module */ -/*========*/ - -/* Returns 0 on success, -1 on failure. */ -int pygrpc_module_add_types(PyObject *module); - -#endif /* GRPC__ADAPTER__C_TYPES_H_ */ diff --git a/src/python/grpcio/grpc/_adapter/_c/types/call.c b/src/python/grpcio/grpc/_adapter/_c/types/call.c deleted file mode 100644 index 04ec871880..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c/types/call.c +++ /dev/null @@ -1,186 +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 "grpc/_adapter/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> -#include <grpc/support/alloc.h> - - -PyMethodDef pygrpc_Call_methods[] = { - {"start_batch", (PyCFunction)pygrpc_Call_start_batch, METH_KEYWORDS, ""}, - {"cancel", (PyCFunction)pygrpc_Call_cancel, METH_KEYWORDS, ""}, - {"peer", (PyCFunction)pygrpc_Call_peer, METH_NOARGS, ""}, - {"set_credentials", (PyCFunction)pygrpc_Call_set_credentials, METH_KEYWORDS, - ""}, - {NULL} -}; -const char pygrpc_Call_doc[] = "See grpc._adapter._types.Call."; -PyTypeObject pygrpc_Call_type = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "Call", /* tp_name */ - sizeof(Call), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pygrpc_Call_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - pygrpc_Call_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - pygrpc_Call_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0 /* tp_new */ -}; - -Call *pygrpc_Call_new_empty(CompletionQueue *cq) { - Call *call = (Call *)pygrpc_Call_type.tp_alloc(&pygrpc_Call_type, 0); - call->c_call = NULL; - call->cq = cq; - Py_XINCREF(call->cq); - return call; -} -void pygrpc_Call_dealloc(Call *self) { - if (self->c_call) { - grpc_call_destroy(self->c_call); - } - Py_XDECREF(self->cq); - self->ob_type->tp_free((PyObject *)self); -} -PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs) { - PyObject *op_list; - PyObject *user_tag; - grpc_op *ops; - size_t nops; - size_t i; - size_t j; - pygrpc_tag *tag; - grpc_call_error errcode; - static char *keywords[] = {"ops", "tag", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO:start_batch", keywords, - &op_list, &user_tag)) { - return NULL; - } - if (!PyList_Check(op_list)) { - PyErr_SetString(PyExc_TypeError, "expected a list of OpArgs"); - return NULL; - } - nops = PyList_Size(op_list); - ops = gpr_malloc(sizeof(grpc_op) * nops); - for (i = 0; i < nops; ++i) { - PyObject *item = PyList_GET_ITEM(op_list, i); - if (!pygrpc_produce_op(item, &ops[i])) { - for (j = 0; j < i; ++j) { - pygrpc_discard_op(ops[j]); - } - return NULL; - } - } - tag = pygrpc_produce_batch_tag(user_tag, self, ops, nops); - errcode = grpc_call_start_batch(self->c_call, tag->ops, tag->nops, tag, NULL); - gpr_free(ops); - return PyInt_FromLong(errcode); -} -PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs) { - PyObject *py_code = NULL; - grpc_call_error errcode; - int code; - char *details = NULL; - static char *keywords[] = {"code", "details", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Os:start_batch", keywords, - &py_code, &details)) { - return NULL; - } - if (py_code != NULL && details != NULL) { - if (!PyInt_Check(py_code)) { - PyErr_SetString(PyExc_TypeError, "expected integer code"); - return NULL; - } - code = PyInt_AsLong(py_code); - errcode = grpc_call_cancel_with_status(self->c_call, code, details, NULL); - } else if (py_code != NULL || details != NULL) { - PyErr_SetString(PyExc_ValueError, - "if `code` is specified, so must `details`"); - return NULL; - } else { - errcode = grpc_call_cancel(self->c_call, NULL); - } - return PyInt_FromLong(errcode); -} - -PyObject *pygrpc_Call_peer(Call *self) { - char *peer = grpc_call_get_peer(self->c_call); - PyObject *py_peer = PyString_FromString(peer); - gpr_free(peer); - return py_peer; -} -PyObject *pygrpc_Call_set_credentials(Call *self, PyObject *args, - PyObject *kwargs) { - CallCredentials *creds; - grpc_call_error errcode; - static char *keywords[] = {"creds", NULL}; - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "O!:set_credentials", keywords, - &pygrpc_CallCredentials_type, &creds)) { - return NULL; - } - errcode = grpc_call_set_credentials(self->c_call, creds->c_creds); - return PyInt_FromLong(errcode); -} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/call_credentials.c b/src/python/grpcio/grpc/_adapter/_c/types/call_credentials.c deleted file mode 100644 index 5a15a6e17d..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c/types/call_credentials.c +++ /dev/null @@ -1,203 +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 "grpc/_adapter/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> -#include <grpc/grpc_security.h> - - -PyMethodDef pygrpc_CallCredentials_methods[] = { - {"composite", (PyCFunction)pygrpc_CallCredentials_composite, - METH_CLASS|METH_KEYWORDS, ""}, - {"compute_engine", (PyCFunction)pygrpc_CallCredentials_compute_engine, - METH_CLASS|METH_NOARGS, ""}, - {"jwt", (PyCFunction)pygrpc_CallCredentials_jwt, - METH_CLASS|METH_KEYWORDS, ""}, - {"refresh_token", (PyCFunction)pygrpc_CallCredentials_refresh_token, - METH_CLASS|METH_KEYWORDS, ""}, - {"iam", (PyCFunction)pygrpc_CallCredentials_iam, - METH_CLASS|METH_KEYWORDS, ""}, - {NULL} -}; - -const char pygrpc_CallCredentials_doc[] = ""; -PyTypeObject pygrpc_CallCredentials_type = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "CallCredentials", /* tp_name */ - sizeof(CallCredentials), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pygrpc_CallCredentials_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - pygrpc_CallCredentials_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - pygrpc_CallCredentials_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0 /* tp_new */ -}; - -void pygrpc_CallCredentials_dealloc(CallCredentials *self) { - grpc_call_credentials_release(self->c_creds); - self->ob_type->tp_free((PyObject *)self); -} - -CallCredentials *pygrpc_CallCredentials_composite( - PyTypeObject *type, PyObject *args, PyObject *kwargs) { - CallCredentials *self; - CallCredentials *creds1; - CallCredentials *creds2; - static char *keywords[] = {"creds1", "creds2", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!:composite", keywords, - &pygrpc_CallCredentials_type, &creds1, - &pygrpc_CallCredentials_type, &creds2)) { - return NULL; - } - self = (CallCredentials *)type->tp_alloc(type, 0); - self->c_creds = - grpc_composite_call_credentials_create( - creds1->c_creds, creds2->c_creds, NULL); - if (!self->c_creds) { - Py_DECREF(self); - PyErr_SetString(PyExc_RuntimeError, "couldn't create composite credentials"); - return NULL; - } - return self; -} - -CallCredentials *pygrpc_CallCredentials_compute_engine( - PyTypeObject *type, PyObject *ignored) { - CallCredentials *self = (CallCredentials *)type->tp_alloc(type, 0); - self->c_creds = grpc_google_compute_engine_credentials_create(NULL); - if (!self->c_creds) { - Py_DECREF(self); - PyErr_SetString(PyExc_RuntimeError, - "couldn't create compute engine credentials"); - return NULL; - } - return self; -} - -/* TODO: Rename this credentials to something like service_account_jwt_access */ -CallCredentials *pygrpc_CallCredentials_jwt( - PyTypeObject *type, PyObject *args, PyObject *kwargs) { - CallCredentials *self; - const char *json_key; - double lifetime; - static char *keywords[] = {"json_key", "token_lifetime", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sd:jwt", keywords, - &json_key, &lifetime)) { - return NULL; - } - self = (CallCredentials *)type->tp_alloc(type, 0); - self->c_creds = grpc_service_account_jwt_access_credentials_create( - json_key, pygrpc_cast_double_to_gpr_timespec(lifetime), NULL); - if (!self->c_creds) { - Py_DECREF(self); - PyErr_SetString(PyExc_RuntimeError, "couldn't create JWT credentials"); - return NULL; - } - return self; -} - -CallCredentials *pygrpc_CallCredentials_refresh_token( - PyTypeObject *type, PyObject *args, PyObject *kwargs) { - CallCredentials *self; - const char *json_refresh_token; - static char *keywords[] = {"json_refresh_token", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s:refresh_token", keywords, - &json_refresh_token)) { - return NULL; - } - self = (CallCredentials *)type->tp_alloc(type, 0); - self->c_creds = - grpc_google_refresh_token_credentials_create(json_refresh_token, NULL); - if (!self->c_creds) { - Py_DECREF(self); - PyErr_SetString(PyExc_RuntimeError, - "couldn't create credentials from refresh token"); - return NULL; - } - return self; -} - -CallCredentials *pygrpc_CallCredentials_iam( - PyTypeObject *type, PyObject *args, PyObject *kwargs) { - CallCredentials *self; - const char *authorization_token; - const char *authority_selector; - static char *keywords[] = {"authorization_token", "authority_selector", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ss:iam", keywords, - &authorization_token, &authority_selector)) { - return NULL; - } - self = (CallCredentials *)type->tp_alloc(type, 0); - self->c_creds = grpc_google_iam_credentials_create(authorization_token, - authority_selector, NULL); - if (!self->c_creds) { - Py_DECREF(self); - PyErr_SetString(PyExc_RuntimeError, "couldn't create IAM credentials"); - return NULL; - } - return self; -} - diff --git a/src/python/grpcio/grpc/_adapter/_c/types/channel.c b/src/python/grpcio/grpc/_adapter/_c/types/channel.c deleted file mode 100644 index c4db2a0dfd..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c/types/channel.c +++ /dev/null @@ -1,187 +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 "grpc/_adapter/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> -#include <grpc/support/alloc.h> - - -PyMethodDef pygrpc_Channel_methods[] = { - {"create_call", (PyCFunction)pygrpc_Channel_create_call, METH_KEYWORDS, ""}, - {"check_connectivity_state", (PyCFunction)pygrpc_Channel_check_connectivity_state, METH_KEYWORDS, ""}, - {"watch_connectivity_state", (PyCFunction)pygrpc_Channel_watch_connectivity_state, METH_KEYWORDS, ""}, - {"target", (PyCFunction)pygrpc_Channel_target, METH_NOARGS, ""}, - {NULL} -}; -const char pygrpc_Channel_doc[] = "See grpc._adapter._types.Channel."; -PyTypeObject pygrpc_Channel_type = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "Channel", /* tp_name */ - sizeof(Channel), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pygrpc_Channel_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - pygrpc_Channel_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - pygrpc_Channel_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - (newfunc)pygrpc_Channel_new /* tp_new */ -}; - -Channel *pygrpc_Channel_new( - PyTypeObject *type, PyObject *args, PyObject *kwargs) { - Channel *self; - const char *target; - PyObject *py_args; - ChannelCredentials *creds = NULL; - grpc_channel_args c_args; - char *keywords[] = {"target", "args", "creds", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O!:Channel", keywords, - &target, &py_args, &pygrpc_ChannelCredentials_type, &creds)) { - return NULL; - } - if (!pygrpc_produce_channel_args(py_args, &c_args)) { - return NULL; - } - self = (Channel *)type->tp_alloc(type, 0); - if (creds) { - self->c_chan = - grpc_secure_channel_create(creds->c_creds, target, &c_args, NULL); - } else { - self->c_chan = grpc_insecure_channel_create(target, &c_args, NULL); - } - pygrpc_discard_channel_args(c_args); - return self; -} -void pygrpc_Channel_dealloc(Channel *self) { - grpc_channel_destroy(self->c_chan); - self->ob_type->tp_free((PyObject *)self); -} - -Call *pygrpc_Channel_create_call( - Channel *self, PyObject *args, PyObject *kwargs) { - Call *call; - CompletionQueue *cq; - const char *method; - const char *host; - double deadline; - char *keywords[] = {"cq", "method", "host", "deadline", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!szd:create_call", keywords, - &pygrpc_CompletionQueue_type, &cq, &method, &host, &deadline)) { - return NULL; - } - call = pygrpc_Call_new_empty(cq); - call->c_call = grpc_channel_create_call( - self->c_chan, NULL, GRPC_PROPAGATE_DEFAULTS, cq->c_cq, method, host, - pygrpc_cast_double_to_gpr_timespec(deadline), NULL); - return call; -} - -PyObject *pygrpc_Channel_check_connectivity_state( - Channel *self, PyObject *args, PyObject *kwargs) { - PyObject *py_try_to_connect; - int try_to_connect; - char *keywords[] = {"try_to_connect", NULL}; - grpc_connectivity_state state; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:connectivity_state", keywords, - &py_try_to_connect)) { - return NULL; - } - if (!PyBool_Check(py_try_to_connect)) { - Py_XDECREF(py_try_to_connect); - return NULL; - } - try_to_connect = Py_True == py_try_to_connect; - Py_DECREF(py_try_to_connect); - state = grpc_channel_check_connectivity_state(self->c_chan, try_to_connect); - return PyInt_FromLong(state); -} - -PyObject *pygrpc_Channel_watch_connectivity_state( - Channel *self, PyObject *args, PyObject *kwargs) { - PyObject *tag; - double deadline; - int last_observed_state; - CompletionQueue *completion_queue; - char *keywords[] = {"last_observed_state", "deadline", - "completion_queue", "tag", NULL}; - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "idO!O:watch_connectivity_state", keywords, - &last_observed_state, &deadline, &pygrpc_CompletionQueue_type, - &completion_queue, &tag)) { - return NULL; - } - grpc_channel_watch_connectivity_state( - self->c_chan, (grpc_connectivity_state)last_observed_state, - pygrpc_cast_double_to_gpr_timespec(deadline), completion_queue->c_cq, - pygrpc_produce_channel_state_change_tag(tag)); - Py_RETURN_NONE; -} - -PyObject *pygrpc_Channel_target(Channel *self) { - char *target = grpc_channel_get_target(self->c_chan); - PyObject *py_target = PyString_FromString(target); - gpr_free(target); - return py_target; -} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/channel_credentials.c b/src/python/grpcio/grpc/_adapter/_c/types/channel_credentials.c deleted file mode 100644 index 83b1fc0406..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c/types/channel_credentials.c +++ /dev/null @@ -1,165 +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 "grpc/_adapter/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> -#include <grpc/grpc_security.h> - - -PyMethodDef pygrpc_ChannelCredentials_methods[] = { - {"google_default", (PyCFunction)pygrpc_ChannelCredentials_google_default, - METH_CLASS|METH_NOARGS, ""}, - {"ssl", (PyCFunction)pygrpc_ChannelCredentials_ssl, - METH_CLASS|METH_KEYWORDS, ""}, - {"composite", (PyCFunction)pygrpc_ChannelCredentials_composite, - METH_CLASS|METH_KEYWORDS, ""}, - {NULL} -}; - -const char pygrpc_ChannelCredentials_doc[] = ""; -PyTypeObject pygrpc_ChannelCredentials_type = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "ChannelCredentials", /* tp_name */ - sizeof(ChannelCredentials), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pygrpc_ChannelCredentials_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - pygrpc_ChannelCredentials_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - pygrpc_ChannelCredentials_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0 /* tp_new */ -}; - -void pygrpc_ChannelCredentials_dealloc(ChannelCredentials *self) { - grpc_channel_credentials_release(self->c_creds); - self->ob_type->tp_free((PyObject *)self); -} - -ChannelCredentials *pygrpc_ChannelCredentials_google_default( - PyTypeObject *type, PyObject *ignored) { - ChannelCredentials *self = (ChannelCredentials *)type->tp_alloc(type, 0); - self->c_creds = grpc_google_default_credentials_create(); - if (!self->c_creds) { - Py_DECREF(self); - PyErr_SetString(PyExc_RuntimeError, - "couldn't create Google default credentials"); - return NULL; - } - return self; -} - -ChannelCredentials *pygrpc_ChannelCredentials_ssl( - PyTypeObject *type, PyObject *args, PyObject *kwargs) { - ChannelCredentials *self; - const char *root_certs; - const char *private_key = NULL; - const char *cert_chain = NULL; - grpc_ssl_pem_key_cert_pair key_cert_pair; - static char *keywords[] = {"root_certs", "private_key", "cert_chain", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "z|zz:ssl", keywords, - &root_certs, &private_key, &cert_chain)) { - return NULL; - } - self = (ChannelCredentials *)type->tp_alloc(type, 0); - if (private_key && cert_chain) { - key_cert_pair.private_key = private_key; - key_cert_pair.cert_chain = cert_chain; - self->c_creds = - grpc_ssl_credentials_create(root_certs, &key_cert_pair, NULL); - } else { - self->c_creds = grpc_ssl_credentials_create(root_certs, NULL, NULL); - } - if (!self->c_creds) { - Py_DECREF(self); - PyErr_SetString(PyExc_RuntimeError, "couldn't create ssl credentials"); - return NULL; - } - return self; -} - -ChannelCredentials *pygrpc_ChannelCredentials_composite( - PyTypeObject *type, PyObject *args, PyObject *kwargs) { - ChannelCredentials *self; - ChannelCredentials *creds1; - CallCredentials *creds2; - static char *keywords[] = {"creds1", "creds2", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O!:composite", keywords, - &pygrpc_ChannelCredentials_type, &creds1, - &pygrpc_CallCredentials_type, &creds2)) { - return NULL; - } - self = (ChannelCredentials *)type->tp_alloc(type, 0); - self->c_creds = - grpc_composite_channel_credentials_create( - creds1->c_creds, creds2->c_creds, NULL); - if (!self->c_creds) { - Py_DECREF(self); - PyErr_SetString( - PyExc_RuntimeError, "couldn't create composite credentials"); - return NULL; - } - return self; -} - diff --git a/src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c b/src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c deleted file mode 100644 index d8bb89ca4b..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c/types/completion_queue.c +++ /dev/null @@ -1,124 +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 "grpc/_adapter/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> - - -PyMethodDef pygrpc_CompletionQueue_methods[] = { - {"next", (PyCFunction)pygrpc_CompletionQueue_next, METH_KEYWORDS, ""}, - {"shutdown", (PyCFunction)pygrpc_CompletionQueue_shutdown, METH_NOARGS, ""}, - {NULL} -}; -const char pygrpc_CompletionQueue_doc[] = - "See grpc._adapter._types.CompletionQueue."; -PyTypeObject pygrpc_CompletionQueue_type = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "CompletionQueue", /* tp_name */ - sizeof(CompletionQueue), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pygrpc_CompletionQueue_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - pygrpc_CompletionQueue_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - pygrpc_CompletionQueue_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - (newfunc)pygrpc_CompletionQueue_new /* tp_new */ -}; - -CompletionQueue *pygrpc_CompletionQueue_new( - PyTypeObject *type, PyObject *args, PyObject *kwargs) { - CompletionQueue *self = (CompletionQueue *)type->tp_alloc(type, 0); - self->c_cq = grpc_completion_queue_create(NULL); - return self; -} - -void pygrpc_CompletionQueue_dealloc(CompletionQueue *self) { - grpc_completion_queue_destroy(self->c_cq); - self->ob_type->tp_free((PyObject *)self); -} - -PyObject *pygrpc_CompletionQueue_next( - CompletionQueue *self, PyObject *args, PyObject *kwargs) { - double deadline; - grpc_event event; - PyObject *transliterated_event; - static char *keywords[] = {"deadline", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:next", keywords, - &deadline)) { - return NULL; - } - Py_BEGIN_ALLOW_THREADS; - event = grpc_completion_queue_next( - self->c_cq, pygrpc_cast_double_to_gpr_timespec(deadline), NULL); - Py_END_ALLOW_THREADS; - transliterated_event = pygrpc_consume_event(event); - return transliterated_event; -} - -PyObject *pygrpc_CompletionQueue_shutdown( - CompletionQueue *self, PyObject *ignored) { - grpc_completion_queue_shutdown(self->c_cq); - Py_RETURN_NONE; -} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/server.c b/src/python/grpcio/grpc/_adapter/_c/types/server.c deleted file mode 100644 index 8feab8aab1..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c/types/server.c +++ /dev/null @@ -1,196 +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 "grpc/_adapter/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> - - -PyMethodDef pygrpc_Server_methods[] = { - {"request_call", (PyCFunction)pygrpc_Server_request_call, - METH_KEYWORDS, ""}, - {"add_http2_port", (PyCFunction)pygrpc_Server_add_http2_port, - METH_KEYWORDS, ""}, - {"start", (PyCFunction)pygrpc_Server_start, METH_NOARGS, ""}, - {"shutdown", (PyCFunction)pygrpc_Server_shutdown, METH_KEYWORDS, ""}, - {"cancel_all_calls", (PyCFunction)pygrpc_Server_cancel_all_calls, - METH_NOARGS, ""}, - {NULL} -}; -const char pygrpc_Server_doc[] = "See grpc._adapter._types.Server."; -PyTypeObject pygrpc_Server_type = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "Server", /* tp_name */ - sizeof(Server), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pygrpc_Server_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - pygrpc_Server_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - pygrpc_Server_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - (newfunc)pygrpc_Server_new /* tp_new */ -}; - -Server *pygrpc_Server_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { - Server *self; - CompletionQueue *cq; - PyObject *py_args; - grpc_channel_args c_args; - char *keywords[] = {"cq", "args", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O:Server", keywords, - &pygrpc_CompletionQueue_type, &cq, &py_args)) { - return NULL; - } - if (!pygrpc_produce_channel_args(py_args, &c_args)) { - return NULL; - } - self = (Server *)type->tp_alloc(type, 0); - self->c_serv = grpc_server_create(&c_args, NULL); - grpc_server_register_completion_queue(self->c_serv, cq->c_cq, NULL); - pygrpc_discard_channel_args(c_args); - self->cq = cq; - Py_INCREF(self->cq); - self->shutdown_called = 0; - return self; -} - -void pygrpc_Server_dealloc(Server *self) { - grpc_server_destroy(self->c_serv); - Py_XDECREF(self->cq); - self->ob_type->tp_free((PyObject *)self); -} - -PyObject *pygrpc_Server_request_call( - Server *self, PyObject *args, PyObject *kwargs) { - CompletionQueue *cq; - PyObject *user_tag; - pygrpc_tag *tag; - Call *empty_call; - grpc_call_error errcode; - static char *keywords[] = {"cq", "tag", NULL}; - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "O!O", keywords, - &pygrpc_CompletionQueue_type, &cq, &user_tag)) { - return NULL; - } - empty_call = pygrpc_Call_new_empty(cq); - tag = pygrpc_produce_request_tag(user_tag, empty_call); - errcode = grpc_server_request_call( - self->c_serv, &tag->call->c_call, &tag->request_call_details, - &tag->request_metadata, tag->call->cq->c_cq, self->cq->c_cq, tag); - Py_DECREF(empty_call); - return PyInt_FromLong(errcode); -} - -PyObject *pygrpc_Server_add_http2_port( - Server *self, PyObject *args, PyObject *kwargs) { - const char *addr; - ServerCredentials *creds = NULL; - int port; - static char *keywords[] = {"addr", "creds", NULL}; - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "s|O!:add_http2_port", keywords, - &addr, &pygrpc_ServerCredentials_type, &creds)) { - return NULL; - } - if (creds) { - port = grpc_server_add_secure_http2_port( - self->c_serv, addr, creds->c_creds); - } else { - port = grpc_server_add_insecure_http2_port(self->c_serv, addr); - } - return PyInt_FromLong(port); - -} - -PyObject *pygrpc_Server_start(Server *self, PyObject *ignored) { - grpc_server_start(self->c_serv); - self->shutdown_called = 0; - Py_RETURN_NONE; -} - -PyObject *pygrpc_Server_shutdown( - Server *self, PyObject *args, PyObject *kwargs) { - PyObject *user_tag; - pygrpc_tag *tag; - static char *keywords[] = {"tag", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", keywords, &user_tag)) { - return NULL; - } - tag = pygrpc_produce_server_shutdown_tag(user_tag); - grpc_server_shutdown_and_notify(self->c_serv, self->cq->c_cq, tag); - self->shutdown_called = 1; - Py_RETURN_NONE; -} - -PyObject *pygrpc_Server_cancel_all_calls(Server *self, PyObject *unused) { - if (!self->shutdown_called) { - PyErr_SetString( - PyExc_RuntimeError, - "shutdown must have been called prior to calling cancel_all_calls!"); - return NULL; - } - grpc_server_cancel_all_calls(self->c_serv); - Py_RETURN_NONE; -} diff --git a/src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c b/src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c deleted file mode 100644 index df51a99b6a..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c/types/server_credentials.c +++ /dev/null @@ -1,137 +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 "grpc/_adapter/_c/types.h" - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> -#include <grpc/grpc_security.h> -#include <grpc/support/alloc.h> - - -PyMethodDef pygrpc_ServerCredentials_methods[] = { - {"ssl", (PyCFunction)pygrpc_ServerCredentials_ssl, - METH_CLASS|METH_KEYWORDS, ""}, - {NULL} -}; -const char pygrpc_ServerCredentials_doc[] = ""; -PyTypeObject pygrpc_ServerCredentials_type = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "ServerCredentials", /* tp_name */ - sizeof(ServerCredentials), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)pygrpc_ServerCredentials_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - pygrpc_ServerCredentials_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - pygrpc_ServerCredentials_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0 /* tp_new */ -}; - -void pygrpc_ServerCredentials_dealloc(ServerCredentials *self) { - grpc_server_credentials_release(self->c_creds); - self->ob_type->tp_free((PyObject *)self); -} - -ServerCredentials *pygrpc_ServerCredentials_ssl( - PyTypeObject *type, PyObject *args, PyObject *kwargs) { - ServerCredentials *self; - const char *root_certs; - PyObject *py_key_cert_pairs; - grpc_ssl_pem_key_cert_pair *key_cert_pairs; - int force_client_auth; - size_t num_key_cert_pairs; - size_t i; - static char *keywords[] = { - "root_certs", "key_cert_pairs", "force_client_auth", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zOi:ssl", keywords, - &root_certs, &py_key_cert_pairs, &force_client_auth)) { - return NULL; - } - if (!PyList_Check(py_key_cert_pairs)) { - PyErr_SetString(PyExc_TypeError, "expected a list of 2-tuples of strings"); - return NULL; - } - num_key_cert_pairs = PyList_Size(py_key_cert_pairs); - key_cert_pairs = - gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs); - for (i = 0; i < num_key_cert_pairs; ++i) { - PyObject *item = PyList_GET_ITEM(py_key_cert_pairs, i); - const char *key; - const char *cert; - if (!PyArg_ParseTuple(item, "zz", &key, &cert)) { - gpr_free(key_cert_pairs); - PyErr_SetString(PyExc_TypeError, - "expected a list of 2-tuples of strings"); - return NULL; - } - key_cert_pairs[i].private_key = key; - key_cert_pairs[i].cert_chain = cert; - } - - self = (ServerCredentials *)type->tp_alloc(type, 0); - self->c_creds = grpc_ssl_server_credentials_create( - root_certs, key_cert_pairs, num_key_cert_pairs, force_client_auth, NULL); - gpr_free(key_cert_pairs); - return self; -} diff --git a/src/python/grpcio/grpc/_adapter/_c/utility.c b/src/python/grpcio/grpc/_adapter/_c/utility.c deleted file mode 100644 index 590f7e013a..0000000000 --- a/src/python/grpcio/grpc/_adapter/_c/utility.c +++ /dev/null @@ -1,524 +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 <math.h> -#include <string.h> - -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#include <grpc/grpc.h> -#include <grpc/byte_buffer_reader.h> -#include <grpc/support/alloc.h> -#include <grpc/support/slice.h> -#include <grpc/support/time.h> -#include <grpc/support/string_util.h> - -#include "grpc/_adapter/_c/types.h" - -pygrpc_tag *pygrpc_produce_batch_tag( - PyObject *user_tag, Call *call, grpc_op *ops, size_t nops) { - pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); - tag->user_tag = user_tag; - Py_XINCREF(tag->user_tag); - tag->call = call; - Py_XINCREF(tag->call); - tag->ops = gpr_malloc(sizeof(grpc_op)*nops); - memcpy(tag->ops, ops, sizeof(grpc_op)*nops); - tag->nops = nops; - grpc_call_details_init(&tag->request_call_details); - grpc_metadata_array_init(&tag->request_metadata); - tag->is_new_call = 0; - return tag; -} - -pygrpc_tag *pygrpc_produce_request_tag(PyObject *user_tag, Call *empty_call) { - pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); - tag->user_tag = user_tag; - Py_XINCREF(tag->user_tag); - tag->call = empty_call; - Py_XINCREF(tag->call); - tag->ops = NULL; - tag->nops = 0; - grpc_call_details_init(&tag->request_call_details); - grpc_metadata_array_init(&tag->request_metadata); - tag->is_new_call = 1; - return tag; -} - -pygrpc_tag *pygrpc_produce_server_shutdown_tag(PyObject *user_tag) { - pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); - tag->user_tag = user_tag; - Py_XINCREF(tag->user_tag); - tag->call = NULL; - tag->ops = NULL; - tag->nops = 0; - grpc_call_details_init(&tag->request_call_details); - grpc_metadata_array_init(&tag->request_metadata); - tag->is_new_call = 0; - return tag; -} - -pygrpc_tag *pygrpc_produce_channel_state_change_tag(PyObject *user_tag) { - pygrpc_tag *tag = gpr_malloc(sizeof(pygrpc_tag)); - tag->user_tag = user_tag; - Py_XINCREF(tag->user_tag); - tag->call = NULL; - tag->ops = NULL; - tag->nops = 0; - grpc_call_details_init(&tag->request_call_details); - grpc_metadata_array_init(&tag->request_metadata); - tag->is_new_call = 0; - return tag; -} - -void pygrpc_discard_tag(pygrpc_tag *tag) { - if (!tag) { - return; - } - Py_XDECREF(tag->user_tag); - Py_XDECREF(tag->call); - gpr_free(tag->ops); - grpc_call_details_destroy(&tag->request_call_details); - grpc_metadata_array_destroy(&tag->request_metadata); - gpr_free(tag); -} - -PyObject *pygrpc_consume_event(grpc_event event) { - pygrpc_tag *tag; - PyObject *result; - if (event.type == GRPC_QUEUE_TIMEOUT) { - Py_RETURN_NONE; - } - tag = event.tag; - switch (event.type) { - case GRPC_QUEUE_SHUTDOWN: - result = Py_BuildValue("iOOOOO", GRPC_QUEUE_SHUTDOWN, - Py_None, Py_None, Py_None, Py_None, Py_True); - break; - case GRPC_OP_COMPLETE: - if (tag->is_new_call) { - result = Py_BuildValue( - "iOO(ssd)[(iNOOOO)]O", GRPC_OP_COMPLETE, tag->user_tag, tag->call, - tag->request_call_details.method, tag->request_call_details.host, - pygrpc_cast_gpr_timespec_to_double(tag->request_call_details.deadline), - GRPC_OP_RECV_INITIAL_METADATA, - pygrpc_cast_metadata_array_to_pyseq(tag->request_metadata), Py_None, - Py_None, Py_None, Py_None, - event.success ? Py_True : Py_False); - } else { - result = Py_BuildValue("iOOONO", GRPC_OP_COMPLETE, tag->user_tag, - tag->call ? (PyObject*)tag->call : Py_None, Py_None, - pygrpc_consume_ops(tag->ops, tag->nops), - event.success ? Py_True : Py_False); - } - break; - default: - PyErr_SetString(PyExc_ValueError, - "unknown completion type; could not translate event"); - return NULL; - } - pygrpc_discard_tag(tag); - return result; -} - -int pygrpc_produce_op(PyObject *op, grpc_op *result) { - static const int OP_TUPLE_SIZE = 6; - static const int STATUS_TUPLE_SIZE = 2; - static const int TYPE_INDEX = 0; - static const int INITIAL_METADATA_INDEX = 1; - static const int TRAILING_METADATA_INDEX = 2; - static const int MESSAGE_INDEX = 3; - static const int STATUS_INDEX = 4; - static const int STATUS_CODE_INDEX = 0; - static const int STATUS_DETAILS_INDEX = 1; - static const int WRITE_FLAGS_INDEX = 5; - int type; - Py_ssize_t message_size; - char *message; - char *status_details; - gpr_slice message_slice; - grpc_op c_op; - if (!PyTuple_Check(op)) { - PyErr_SetString(PyExc_TypeError, "expected tuple op"); - return 0; - } - if (PyTuple_Size(op) != OP_TUPLE_SIZE) { - char *buf; - gpr_asprintf(&buf, "expected tuple op of length %d", OP_TUPLE_SIZE); - PyErr_SetString(PyExc_ValueError, buf); - gpr_free(buf); - return 0; - } - type = PyInt_AsLong(PyTuple_GET_ITEM(op, TYPE_INDEX)); - if (PyErr_Occurred()) { - return 0; - } - c_op.op = type; - c_op.reserved = NULL; - c_op.flags = PyInt_AsLong(PyTuple_GET_ITEM(op, WRITE_FLAGS_INDEX)); - if (PyErr_Occurred()) { - return 0; - } - switch (type) { - case GRPC_OP_SEND_INITIAL_METADATA: - if (!pygrpc_cast_pyseq_to_send_metadata( - PyTuple_GetItem(op, INITIAL_METADATA_INDEX), - &c_op.data.send_initial_metadata.metadata, - &c_op.data.send_initial_metadata.count)) { - return 0; - } - break; - case GRPC_OP_SEND_MESSAGE: - PyString_AsStringAndSize( - PyTuple_GET_ITEM(op, MESSAGE_INDEX), &message, &message_size); - message_slice = gpr_slice_from_copied_buffer(message, message_size); - c_op.data.send_message = grpc_raw_byte_buffer_create(&message_slice, 1); - gpr_slice_unref(message_slice); - break; - case GRPC_OP_SEND_CLOSE_FROM_CLIENT: - /* Don't need to fill in any other fields. */ - break; - case GRPC_OP_SEND_STATUS_FROM_SERVER: - if (!pygrpc_cast_pyseq_to_send_metadata( - PyTuple_GetItem(op, TRAILING_METADATA_INDEX), - &c_op.data.send_status_from_server.trailing_metadata, - &c_op.data.send_status_from_server.trailing_metadata_count)) { - return 0; - } - if (!PyTuple_Check(PyTuple_GET_ITEM(op, STATUS_INDEX))) { - char *buf; - gpr_asprintf(&buf, "expected tuple status in op of length %d", - STATUS_TUPLE_SIZE); - PyErr_SetString(PyExc_ValueError, buf); - gpr_free(buf); - return 0; - } - c_op.data.send_status_from_server.status = PyInt_AsLong( - PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_CODE_INDEX)); - status_details = PyString_AsString( - PyTuple_GET_ITEM(PyTuple_GET_ITEM(op, STATUS_INDEX), STATUS_DETAILS_INDEX)); - if (PyErr_Occurred()) { - return 0; - } - c_op.data.send_status_from_server.status_details = - gpr_malloc(strlen(status_details) + 1); - strcpy((char *)c_op.data.send_status_from_server.status_details, - status_details); - break; - case GRPC_OP_RECV_INITIAL_METADATA: - c_op.data.recv_initial_metadata = gpr_malloc(sizeof(grpc_metadata_array)); - grpc_metadata_array_init(c_op.data.recv_initial_metadata); - break; - case GRPC_OP_RECV_MESSAGE: - c_op.data.recv_message = gpr_malloc(sizeof(grpc_byte_buffer *)); - break; - case GRPC_OP_RECV_STATUS_ON_CLIENT: - c_op.data.recv_status_on_client.trailing_metadata = - gpr_malloc(sizeof(grpc_metadata_array)); - grpc_metadata_array_init(c_op.data.recv_status_on_client.trailing_metadata); - c_op.data.recv_status_on_client.status = - gpr_malloc(sizeof(grpc_status_code *)); - c_op.data.recv_status_on_client.status_details = - gpr_malloc(sizeof(char *)); - *c_op.data.recv_status_on_client.status_details = NULL; - c_op.data.recv_status_on_client.status_details_capacity = - gpr_malloc(sizeof(size_t)); - *c_op.data.recv_status_on_client.status_details_capacity = 0; - break; - case GRPC_OP_RECV_CLOSE_ON_SERVER: - c_op.data.recv_close_on_server.cancelled = gpr_malloc(sizeof(int)); - break; - default: - return 0; - } - *result = c_op; - return 1; -} - -void pygrpc_discard_op(grpc_op op) { - size_t i; - switch(op.op) { - case GRPC_OP_SEND_INITIAL_METADATA: - /* Whenever we produce send-metadata, we allocate new strings (to handle - arbitrary sequence input as opposed to just lists or just tuples). We - thus must free those elements. */ - for (i = 0; i < op.data.send_initial_metadata.count; ++i) { - gpr_free((void *)op.data.send_initial_metadata.metadata[i].key); - gpr_free((void *)op.data.send_initial_metadata.metadata[i].value); - } - gpr_free(op.data.send_initial_metadata.metadata); - break; - case GRPC_OP_SEND_MESSAGE: - grpc_byte_buffer_destroy(op.data.send_message); - break; - case GRPC_OP_SEND_CLOSE_FROM_CLIENT: - /* Don't need to free any fields. */ - break; - case GRPC_OP_SEND_STATUS_FROM_SERVER: - /* Whenever we produce send-metadata, we allocate new strings (to handle - arbitrary sequence input as opposed to just lists or just tuples). We - thus must free those elements. */ - for (i = 0; i < op.data.send_status_from_server.trailing_metadata_count; - ++i) { - gpr_free( - (void *)op.data.send_status_from_server.trailing_metadata[i].key); - gpr_free( - (void *)op.data.send_status_from_server.trailing_metadata[i].value); - } - gpr_free(op.data.send_status_from_server.trailing_metadata); - gpr_free((char *)op.data.send_status_from_server.status_details); - break; - case GRPC_OP_RECV_INITIAL_METADATA: - grpc_metadata_array_destroy(op.data.recv_initial_metadata); - gpr_free(op.data.recv_initial_metadata); - break; - case GRPC_OP_RECV_MESSAGE: - grpc_byte_buffer_destroy(*op.data.recv_message); - gpr_free(op.data.recv_message); - break; - case GRPC_OP_RECV_STATUS_ON_CLIENT: - grpc_metadata_array_destroy(op.data.recv_status_on_client.trailing_metadata); - gpr_free(op.data.recv_status_on_client.trailing_metadata); - gpr_free(op.data.recv_status_on_client.status); - gpr_free(*op.data.recv_status_on_client.status_details); - gpr_free(op.data.recv_status_on_client.status_details); - gpr_free(op.data.recv_status_on_client.status_details_capacity); - break; - case GRPC_OP_RECV_CLOSE_ON_SERVER: - gpr_free(op.data.recv_close_on_server.cancelled); - break; - } -} - -PyObject *pygrpc_consume_ops(grpc_op *op, size_t nops) { - static const int TYPE_INDEX = 0; - static const int INITIAL_METADATA_INDEX = 1; - static const int TRAILING_METADATA_INDEX = 2; - static const int MESSAGE_INDEX = 3; - static const int STATUS_INDEX = 4; - static const int CANCELLED_INDEX = 5; - static const int OPRESULT_LENGTH = 6; - PyObject *list; - size_t i; - size_t j; - char *bytes; - size_t bytes_size; - PyObject *results = PyList_New(nops); - if (!results) { - return NULL; - } - for (i = 0; i < nops; ++i) { - PyObject *result = PyTuple_Pack(OPRESULT_LENGTH, Py_None, Py_None, Py_None, - Py_None, Py_None, Py_None); - PyTuple_SetItem(result, TYPE_INDEX, PyInt_FromLong(op[i].op)); - switch(op[i].op) { - case GRPC_OP_RECV_INITIAL_METADATA: - PyTuple_SetItem(result, INITIAL_METADATA_INDEX, - list=PyList_New(op[i].data.recv_initial_metadata->count)); - for (j = 0; j < op[i].data.recv_initial_metadata->count; ++j) { - grpc_metadata md = op[i].data.recv_initial_metadata->metadata[j]; - PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value, - (Py_ssize_t)md.value_length)); - } - break; - case GRPC_OP_RECV_MESSAGE: - if (*op[i].data.recv_message) { - pygrpc_byte_buffer_to_bytes( - *op[i].data.recv_message, &bytes, &bytes_size); - PyTuple_SetItem(result, MESSAGE_INDEX, - PyString_FromStringAndSize(bytes, bytes_size)); - gpr_free(bytes); - } else { - PyTuple_SetItem(result, MESSAGE_INDEX, Py_BuildValue("")); - } - break; - case GRPC_OP_RECV_STATUS_ON_CLIENT: - PyTuple_SetItem( - result, TRAILING_METADATA_INDEX, - list = PyList_New(op[i].data.recv_status_on_client.trailing_metadata->count)); - for (j = 0; j < op[i].data.recv_status_on_client.trailing_metadata->count; ++j) { - grpc_metadata md = - op[i].data.recv_status_on_client.trailing_metadata->metadata[j]; - PyList_SetItem(list, j, Py_BuildValue("ss#", md.key, md.value, - (Py_ssize_t)md.value_length)); - } - PyTuple_SetItem( - result, STATUS_INDEX, Py_BuildValue( - "is", *op[i].data.recv_status_on_client.status, - *op[i].data.recv_status_on_client.status_details)); - break; - case GRPC_OP_RECV_CLOSE_ON_SERVER: - PyTuple_SetItem( - result, CANCELLED_INDEX, - PyBool_FromLong(*op[i].data.recv_close_on_server.cancelled)); - break; - default: - break; - } - pygrpc_discard_op(op[i]); - PyList_SetItem(results, i, result); - } - return results; -} - -double pygrpc_cast_gpr_timespec_to_double(gpr_timespec timespec) { - timespec = gpr_convert_clock_type(timespec, GPR_CLOCK_REALTIME); - return timespec.tv_sec + 1e-9*timespec.tv_nsec; -} - -/* Because C89 doesn't have a way to check for infinity... */ -static int pygrpc_isinf(double x) { - return x * 0 != 0; -} - -gpr_timespec pygrpc_cast_double_to_gpr_timespec(double seconds) { - gpr_timespec result; - if (pygrpc_isinf(seconds)) { - result = seconds > 0.0 ? gpr_inf_future(GPR_CLOCK_REALTIME) - : gpr_inf_past(GPR_CLOCK_REALTIME); - } else { - result.tv_sec = (time_t)seconds; - result.tv_nsec = ((seconds - result.tv_sec) * 1e9); - result.clock_type = GPR_CLOCK_REALTIME; - } - return result; -} - -int pygrpc_produce_channel_args(PyObject *py_args, grpc_channel_args *c_args) { - size_t num_args = PyList_Size(py_args); - size_t i; - grpc_channel_args args; - args.num_args = num_args; - args.args = gpr_malloc(sizeof(grpc_arg) * num_args); - for (i = 0; i < args.num_args; ++i) { - char *key; - PyObject *value; - if (!PyArg_ParseTuple(PyList_GetItem(py_args, i), "zO", &key, &value)) { - gpr_free(args.args); - args.num_args = 0; - args.args = NULL; - PyErr_SetString(PyExc_TypeError, - "expected a list of 2-tuple of str and str|int|None"); - return 0; - } - args.args[i].key = key; - if (PyInt_Check(value)) { - args.args[i].type = GRPC_ARG_INTEGER; - args.args[i].value.integer = PyInt_AsLong(value); - } else if (PyString_Check(value)) { - args.args[i].type = GRPC_ARG_STRING; - args.args[i].value.string = PyString_AsString(value); - } else if (value == Py_None) { - --args.num_args; - --i; - continue; - } else { - gpr_free(args.args); - args.num_args = 0; - args.args = NULL; - PyErr_SetString(PyExc_TypeError, - "expected a list of 2-tuple of str and str|int|None"); - return 0; - } - } - *c_args = args; - return 1; -} - -void pygrpc_discard_channel_args(grpc_channel_args args) { - gpr_free(args.args); -} - -int pygrpc_cast_pyseq_to_send_metadata( - PyObject *pyseq, grpc_metadata **metadata, size_t *count) { - size_t i; - Py_ssize_t value_length; - char *key; - char *value; - if (!PySequence_Check(pyseq)) { - return 0; - } - *count = PySequence_Size(pyseq); - *metadata = gpr_malloc(sizeof(grpc_metadata) * *count); - for (i = 0; i < *count; ++i) { - PyObject *item = PySequence_GetItem(pyseq, i); - if (!PyArg_ParseTuple(item, "ss#", &key, &value, &value_length)) { - Py_DECREF(item); - gpr_free(*metadata); - *count = 0; - *metadata = NULL; - return 0; - } else { - (*metadata)[i].key = gpr_strdup(key); - (*metadata)[i].value = gpr_malloc(value_length); - memcpy((void *)(*metadata)[i].value, value, value_length); - Py_DECREF(item); - } - (*metadata)[i].value_length = value_length; - } - return 1; -} - -PyObject *pygrpc_cast_metadata_array_to_pyseq(grpc_metadata_array metadata) { - PyObject *result = PyTuple_New(metadata.count); - size_t i; - for (i = 0; i < metadata.count; ++i) { - PyTuple_SetItem( - result, i, Py_BuildValue( - "ss#", metadata.metadata[i].key, metadata.metadata[i].value, - (Py_ssize_t)metadata.metadata[i].value_length)); - if (PyErr_Occurred()) { - Py_DECREF(result); - return NULL; - } - } - return result; -} - -void pygrpc_byte_buffer_to_bytes( - grpc_byte_buffer *buffer, char **result, size_t *result_size) { - grpc_byte_buffer_reader reader; - gpr_slice slice; - char *read_result = NULL; - size_t size = 0; - grpc_byte_buffer_reader_init(&reader, buffer); - while (grpc_byte_buffer_reader_next(&reader, &slice)) { - read_result = gpr_realloc(read_result, size + GPR_SLICE_LENGTH(slice)); - memcpy(read_result + size, GPR_SLICE_START_PTR(slice), - GPR_SLICE_LENGTH(slice)); - size = size + GPR_SLICE_LENGTH(slice); - gpr_slice_unref(slice); - } - *result_size = size; - *result = read_result; -} diff --git a/src/python/grpcio_test/grpc_test/_adapter/_c_test.py b/src/python/grpcio/grpc/_adapter/_implementations.py index fe020e2a9c..b85f228bf6 100644 --- a/src/python/grpcio_test/grpc_test/_adapter/_c_test.py +++ b/src/python/grpcio/grpc/_adapter/_implementations.py @@ -27,29 +27,22 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import time -import unittest +import collections -from grpc._adapter import _c -from grpc._adapter import _types +from grpc.beta import interfaces +class AuthMetadataContext(collections.namedtuple( + 'AuthMetadataContext', [ + 'service_url', + 'method_name' + ]), interfaces.GRPCAuthMetadataContext): + pass -class CTypeSmokeTest(unittest.TestCase): - def testCompletionQueueUpDown(self): - completion_queue = _c.CompletionQueue() - del completion_queue +class AuthMetadataPluginCallback(interfaces.GRPCAuthMetadataContext): - def testServerUpDown(self): - completion_queue = _c.CompletionQueue() - serv = _c.Server(completion_queue, []) - del serv - del completion_queue + def __init__(self, callback): + self._callback = callback - def testChannelUpDown(self): - channel = _c.Channel('[::]:0', []) - del channel - - -if __name__ == '__main__': - unittest.main(verbosity=2) + def __call__(self, metadata, error): + self._callback(metadata, error) diff --git a/src/python/grpcio/grpc/_adapter/_intermediary_low.py b/src/python/grpcio/grpc/_adapter/_intermediary_low.py index 5634c2024d..9698ffeabf 100644 --- a/src/python/grpcio/grpc/_adapter/_intermediary_low.py +++ b/src/python/grpcio/grpc/_adapter/_intermediary_low.py @@ -115,16 +115,20 @@ class Call(object): return call def invoke(self, completion_queue, metadata_tag, finish_tag): - err0 = self._internal.start_batch([ + err = self._internal.start_batch([ _types.OpArgs.send_initial_metadata(self._metadata) ], _IGNORE_ME_TAG) - err1 = self._internal.start_batch([ + if err != _types.CallError.OK: + return err + err = self._internal.start_batch([ _types.OpArgs.recv_initial_metadata() ], _TagAdapter(metadata_tag, Event.Kind.METADATA_ACCEPTED)) - err2 = self._internal.start_batch([ + if err != _types.CallError.OK: + return err + err = self._internal.start_batch([ _types.OpArgs.recv_status_on_client() ], _TagAdapter(finish_tag, Event.Kind.FINISH)) - return err0 if err0 != _types.CallError.OK else err1 if err1 != _types.CallError.OK else err2 if err2 != _types.CallError.OK else _types.CallError.OK + return err def write(self, message, tag, flags): return self._internal.start_batch([ @@ -158,7 +162,8 @@ class Call(object): def status(self, status, tag): return self._internal.start_batch([ - _types.OpArgs.send_status_from_server(self._metadata, status.code, status.details) + _types.OpArgs.send_status_from_server( + self._metadata, status.code, status.details) ], _TagAdapter(tag, Event.Kind.COMPLETE_ACCEPTED)) def cancel(self): @@ -168,20 +173,17 @@ class Call(object): return self._internal.peer() def set_credentials(self, creds): - return self._internal.set_credentials(creds._internal) + return self._internal.set_credentials(creds) class Channel(object): """Adapter from old _low.Channel interface to new _low.Channel.""" - def __init__(self, hostport, client_credentials, server_host_override=None): + def __init__(self, hostport, channel_credentials, server_host_override=None): args = [] if server_host_override: args.append((_types.GrpcChannelArgumentKeys.SSL_TARGET_NAME_OVERRIDE.value, server_host_override)) - creds = None - if client_credentials: - creds = client_credentials._internal - self._internal = _low.Channel(hostport, args, creds) + self._internal = _low.Channel(hostport, args, channel_credentials) class CompletionQueue(object): @@ -192,7 +194,7 @@ class CompletionQueue(object): def get(self, deadline=None): if deadline is None: - ev = self._internal.next() + ev = self._internal.next(float('+inf')) else: ev = self._internal.next(deadline) if ev is None: @@ -240,7 +242,7 @@ class Server(object): if server_credentials is None: return self._internal.add_http2_port(addr, None) else: - return self._internal.add_http2_port(addr, server_credentials._internal) + return self._internal.add_http2_port(addr, server_credentials) def start(self): return self._internal.start() @@ -248,20 +250,9 @@ class Server(object): def service(self, tag): return self._internal.request_call(self._internal_cq, _TagAdapter(tag, Event.Kind.SERVICE_ACCEPTED)) + def cancel_all_calls(self): + self._internal.cancel_all_calls() + def stop(self): return self._internal.shutdown(_TagAdapter(None, Event.Kind.STOP)) - -class ClientCredentials(object): - """Adapter from old _low.ClientCredentials interface to new _low.ChannelCredentials.""" - - def __init__(self, root_certificates, private_key, certificate_chain): - self._internal = _low.ChannelCredentials.ssl(root_certificates, private_key, certificate_chain) - - -class ServerCredentials(object): - """Adapter from old _low.ServerCredentials interface to new _low.ServerCredentials.""" - - def __init__(self, root_credentials, pair_sequence, force_client_auth): - self._internal = _low.ServerCredentials.ssl( - root_credentials, list(pair_sequence), force_client_auth) diff --git a/src/python/grpcio/grpc/_adapter/_low.py b/src/python/grpcio/grpc/_adapter/_low.py index 57146aaefe..b13d8dd9dd 100644 --- a/src/python/grpcio/grpc/_adapter/_low.py +++ b/src/python/grpcio/grpc/_adapter/_low.py @@ -27,36 +27,157 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +import threading + from grpc import _grpcio_metadata -from grpc._adapter import _c +from grpc._cython import cygrpc +from grpc._adapter import _implementations from grpc._adapter import _types _USER_AGENT = 'Python-gRPC-{}'.format(_grpcio_metadata.__version__) -ChannelCredentials = _c.ChannelCredentials -CallCredentials = _c.CallCredentials -ServerCredentials = _c.ServerCredentials +ChannelCredentials = cygrpc.ChannelCredentials +CallCredentials = cygrpc.CallCredentials +ServerCredentials = cygrpc.ServerCredentials + +channel_credentials_composite = cygrpc.channel_credentials_composite +call_credentials_composite = cygrpc.call_credentials_composite + +def server_credentials_ssl(root_credentials, pair_sequence, force_client_auth): + return cygrpc.server_credentials_ssl( + root_credentials, + [cygrpc.SslPemKeyCertPair(key, pem) for key, pem in pair_sequence], + force_client_auth) + +def channel_credentials_ssl( + root_certificates, private_key, certificate_chain): + pair = None + if private_key is not None or certificate_chain is not None: + pair = cygrpc.SslPemKeyCertPair(private_key, certificate_chain) + return cygrpc.channel_credentials_ssl(root_certificates, pair) + + +class _WrappedCygrpcCallback(object): + + def __init__(self, cygrpc_callback): + self.is_called = False + self.error = None + self.is_called_lock = threading.Lock() + self.cygrpc_callback = cygrpc_callback + + def _invoke_failure(self, error): + # TODO(atash) translate different Exception superclasses into different + # status codes. + self.cygrpc_callback( + cygrpc.Metadata([]), cygrpc.StatusCode.internal, error.message) + + def _invoke_success(self, metadata): + try: + cygrpc_metadata = cygrpc.Metadata( + cygrpc.Metadatum(key, value) + for key, value in metadata) + except Exception as error: + self._invoke_failure(error) + return + self.cygrpc_callback(cygrpc_metadata, cygrpc.StatusCode.ok, '') + + def __call__(self, metadata, error): + with self.is_called_lock: + if self.is_called: + raise RuntimeError('callback should only ever be invoked once') + if self.error: + self._invoke_failure(self.error) + return + self.is_called = True + if error is None: + self._invoke_success(metadata) + else: + self._invoke_failure(error) + + def notify_failure(self, error): + with self.is_called_lock: + if not self.is_called: + self.error = error + + +class _WrappedPlugin(object): + + def __init__(self, plugin): + self.plugin = plugin + + def __call__(self, context, cygrpc_callback): + wrapped_cygrpc_callback = _WrappedCygrpcCallback(cygrpc_callback) + wrapped_context = _implementations.AuthMetadataContext(context.service_url, + context.method_name) + try: + self.plugin( + wrapped_context, + _implementations.AuthMetadataPluginCallback(wrapped_cygrpc_callback)) + except Exception as error: + wrapped_cygrpc_callback.notify_failure(error) + raise + + +def call_credentials_metadata_plugin(plugin, name): + """ + Args: + plugin: A callable accepting a _types.AuthMetadataContext + object and a callback (itself accepting a list of metadata key/value + 2-tuples and a None-able exception value). The callback must be eventually + called, but need not be called in plugin's invocation. + plugin's invocation must be non-blocking. + """ + return cygrpc.call_credentials_metadata_plugin( + cygrpc.CredentialsMetadataPlugin(_WrappedPlugin(plugin), name)) class CompletionQueue(_types.CompletionQueue): def __init__(self): - self.completion_queue = _c.CompletionQueue() + self.completion_queue = cygrpc.CompletionQueue() def next(self, deadline=float('+inf')): - raw_event = self.completion_queue.next(deadline) - if raw_event is None: + raw_event = self.completion_queue.poll(cygrpc.Timespec(deadline)) + if raw_event.type == cygrpc.CompletionType.queue_timeout: return None - event = _types.Event(*raw_event) - if event.call is not None: - event = event._replace(call=Call(event.call)) - if event.call_details is not None: - event = event._replace(call_details=_types.CallDetails(*event.call_details)) - if event.results is not None: - new_results = [_types.OpResult(*r) for r in event.results] - new_results = [r if r.status is None else r._replace(status=_types.Status(_types.StatusCode(r.status[0]), r.status[1])) for r in new_results] - event = event._replace(results=new_results) - return event + event_type = raw_event.type + event_tag = raw_event.tag + event_call = Call(raw_event.operation_call) + if raw_event.request_call_details: + event_call_details = _types.CallDetails( + raw_event.request_call_details.method, + raw_event.request_call_details.host, + float(raw_event.request_call_details.deadline)) + else: + event_call_details = None + event_success = raw_event.success + event_results = [] + if raw_event.is_new_request: + event_results.append(_types.OpResult( + _types.OpType.RECV_INITIAL_METADATA, raw_event.request_metadata, + None, None, None, None)) + else: + if raw_event.batch_operations: + for operation in raw_event.batch_operations: + result_type = operation.type + result_initial_metadata = operation.received_metadata_or_none + result_trailing_metadata = operation.received_metadata_or_none + result_message = operation.received_message_or_none + if result_message is not None: + result_message = result_message.bytes() + result_cancelled = operation.received_cancelled_or_none + if operation.has_status: + result_status = _types.Status( + operation.received_status_code_or_none, + operation.received_status_details_or_none) + else: + result_status = None + event_results.append( + _types.OpResult(result_type, result_initial_metadata, + result_trailing_metadata, result_message, + result_status, result_cancelled)) + return _types.Event(event_type, event_tag, event_call, event_call_details, + event_results, event_success) def shutdown(self): self.completion_queue.shutdown() @@ -68,7 +189,36 @@ class Call(_types.Call): self.call = call def start_batch(self, ops, tag): - return self.call.start_batch(ops, tag) + translated_ops = [] + for op in ops: + if op.type == _types.OpType.SEND_INITIAL_METADATA: + translated_op = cygrpc.operation_send_initial_metadata( + cygrpc.Metadata( + cygrpc.Metadatum(key, value) + for key, value in op.initial_metadata)) + elif op.type == _types.OpType.SEND_MESSAGE: + translated_op = cygrpc.operation_send_message(op.message) + elif op.type == _types.OpType.SEND_CLOSE_FROM_CLIENT: + translated_op = cygrpc.operation_send_close_from_client() + elif op.type == _types.OpType.SEND_STATUS_FROM_SERVER: + translated_op = cygrpc.operation_send_status_from_server( + cygrpc.Metadata( + cygrpc.Metadatum(key, value) + for key, value in op.trailing_metadata), + op.status.code, + op.status.details) + elif op.type == _types.OpType.RECV_INITIAL_METADATA: + translated_op = cygrpc.operation_receive_initial_metadata() + elif op.type == _types.OpType.RECV_MESSAGE: + translated_op = cygrpc.operation_receive_message() + elif op.type == _types.OpType.RECV_STATUS_ON_CLIENT: + translated_op = cygrpc.operation_receive_status_on_client() + elif op.type == _types.OpType.RECV_CLOSE_ON_SERVER: + translated_op = cygrpc.operation_receive_close_on_server() + else: + raise ValueError('unexpected operation type {}'.format(op.type)) + translated_ops.append(translated_op) + return self.call.start_batch(cygrpc.Operations(translated_ops), tag) def cancel(self, code=None, details=None): if code is None and details is None: @@ -86,14 +236,20 @@ class Call(_types.Call): class Channel(_types.Channel): def __init__(self, target, args, creds=None): - args = list(args) + [(_c.PRIMARY_USER_AGENT_KEY, _USER_AGENT)] + args = list(args) + [ + (cygrpc.ChannelArgKey.primary_user_agent_string, _USER_AGENT)] + args = cygrpc.ChannelArgs( + cygrpc.ChannelArg(key, value) for key, value in args) if creds is None: - self.channel = _c.Channel(target, args) + self.channel = cygrpc.Channel(target, args) else: - self.channel = _c.Channel(target, args, creds) + self.channel = cygrpc.Channel(target, args, creds) def create_call(self, completion_queue, method, host, deadline=None): - return Call(self.channel.create_call(completion_queue.completion_queue, method, host, deadline)) + internal_call = self.channel.create_call( + None, 0, completion_queue.completion_queue, method, host, + cygrpc.Timespec(deadline)) + return Call(internal_call) def check_connectivity_state(self, try_to_connect): return self.channel.check_connectivity_state(try_to_connect) @@ -101,7 +257,8 @@ class Channel(_types.Channel): def watch_connectivity_state(self, last_observed_state, deadline, completion_queue, tag): self.channel.watch_connectivity_state( - last_observed_state, deadline, completion_queue.completion_queue, tag) + last_observed_state, cygrpc.Timespec(deadline), + completion_queue.completion_queue, tag) def target(self): return self.channel.target() @@ -112,7 +269,11 @@ _NO_TAG = object() class Server(_types.Server): def __init__(self, completion_queue, args): - self.server = _c.Server(completion_queue.completion_queue, args) + args = cygrpc.ChannelArgs( + cygrpc.ChannelArg(key, value) for key, value in args) + self.server = cygrpc.Server(args) + self.server.register_completion_queue(completion_queue.completion_queue) + self.server_queue = completion_queue def add_http2_port(self, addr, creds=None): if creds is None: @@ -124,10 +285,11 @@ class Server(_types.Server): return self.server.start() def shutdown(self, tag=None): - return self.server.shutdown(tag) + return self.server.shutdown(self.server_queue.completion_queue, tag) def request_call(self, completion_queue, tag): - return self.server.request_call(completion_queue.completion_queue, tag) + return self.server.request_call(completion_queue.completion_queue, + self.server_queue.completion_queue, tag) def cancel_all_calls(self): return self.server.cancel_all_calls() diff --git a/src/python/grpcio/grpc/_adapter/_types.py b/src/python/grpcio/grpc/_adapter/_types.py index ca0fa066bc..3d5ab33d00 100644 --- a/src/python/grpcio/grpc/_adapter/_types.py +++ b/src/python/grpcio/grpc/_adapter/_types.py @@ -31,6 +31,8 @@ import abc import collections import enum +from grpc._cython import cygrpc + class GrpcChannelArgumentKeys(enum.Enum): """Mirrors keys used in grpc_channel_args for GRPC-specific arguments.""" @@ -40,77 +42,77 @@ class GrpcChannelArgumentKeys(enum.Enum): @enum.unique class CallError(enum.IntEnum): """Mirrors grpc_call_error in the C core.""" - OK = 0 - ERROR = 1 - ERROR_NOT_ON_SERVER = 2 - ERROR_NOT_ON_CLIENT = 3 - ERROR_ALREADY_ACCEPTED = 4 - ERROR_ALREADY_INVOKED = 5 - ERROR_NOT_INVOKED = 6 - ERROR_ALREADY_FINISHED = 7 - ERROR_TOO_MANY_OPERATIONS = 8 - ERROR_INVALID_FLAGS = 9 - ERROR_INVALID_METADATA = 10 + OK = cygrpc.CallError.ok + ERROR = cygrpc.CallError.error + ERROR_NOT_ON_SERVER = cygrpc.CallError.not_on_server + ERROR_NOT_ON_CLIENT = cygrpc.CallError.not_on_client + ERROR_ALREADY_ACCEPTED = cygrpc.CallError.already_accepted + ERROR_ALREADY_INVOKED = cygrpc.CallError.already_invoked + ERROR_NOT_INVOKED = cygrpc.CallError.not_invoked + ERROR_ALREADY_FINISHED = cygrpc.CallError.already_finished + ERROR_TOO_MANY_OPERATIONS = cygrpc.CallError.too_many_operations + ERROR_INVALID_FLAGS = cygrpc.CallError.invalid_flags + ERROR_INVALID_METADATA = cygrpc.CallError.invalid_metadata @enum.unique class StatusCode(enum.IntEnum): """Mirrors grpc_status_code in the C core.""" - OK = 0 - CANCELLED = 1 - UNKNOWN = 2 - INVALID_ARGUMENT = 3 - DEADLINE_EXCEEDED = 4 - NOT_FOUND = 5 - ALREADY_EXISTS = 6 - PERMISSION_DENIED = 7 - RESOURCE_EXHAUSTED = 8 - FAILED_PRECONDITION = 9 - ABORTED = 10 - OUT_OF_RANGE = 11 - UNIMPLEMENTED = 12 - INTERNAL = 13 - UNAVAILABLE = 14 - DATA_LOSS = 15 - UNAUTHENTICATED = 16 + OK = cygrpc.StatusCode.ok + CANCELLED = cygrpc.StatusCode.cancelled + UNKNOWN = cygrpc.StatusCode.unknown + INVALID_ARGUMENT = cygrpc.StatusCode.invalid_argument + DEADLINE_EXCEEDED = cygrpc.StatusCode.deadline_exceeded + NOT_FOUND = cygrpc.StatusCode.not_found + ALREADY_EXISTS = cygrpc.StatusCode.already_exists + PERMISSION_DENIED = cygrpc.StatusCode.permission_denied + RESOURCE_EXHAUSTED = cygrpc.StatusCode.resource_exhausted + FAILED_PRECONDITION = cygrpc.StatusCode.failed_precondition + ABORTED = cygrpc.StatusCode.aborted + OUT_OF_RANGE = cygrpc.StatusCode.out_of_range + UNIMPLEMENTED = cygrpc.StatusCode.unimplemented + INTERNAL = cygrpc.StatusCode.internal + UNAVAILABLE = cygrpc.StatusCode.unavailable + DATA_LOSS = cygrpc.StatusCode.data_loss + UNAUTHENTICATED = cygrpc.StatusCode.unauthenticated @enum.unique class OpWriteFlags(enum.IntEnum): """Mirrors defined write-flag constants in the C core.""" - WRITE_BUFFER_HINT = 1 - WRITE_NO_COMPRESS = 2 + WRITE_BUFFER_HINT = cygrpc.WriteFlag.buffer_hint + WRITE_NO_COMPRESS = cygrpc.WriteFlag.no_compress @enum.unique class OpType(enum.IntEnum): """Mirrors grpc_op_type in the C core.""" - SEND_INITIAL_METADATA = 0 - SEND_MESSAGE = 1 - SEND_CLOSE_FROM_CLIENT = 2 - SEND_STATUS_FROM_SERVER = 3 - RECV_INITIAL_METADATA = 4 - RECV_MESSAGE = 5 - RECV_STATUS_ON_CLIENT = 6 - RECV_CLOSE_ON_SERVER = 7 + SEND_INITIAL_METADATA = cygrpc.OperationType.send_initial_metadata + SEND_MESSAGE = cygrpc.OperationType.send_message + SEND_CLOSE_FROM_CLIENT = cygrpc.OperationType.send_close_from_client + SEND_STATUS_FROM_SERVER = cygrpc.OperationType.send_status_from_server + RECV_INITIAL_METADATA = cygrpc.OperationType.receive_initial_metadata + RECV_MESSAGE = cygrpc.OperationType.receive_message + RECV_STATUS_ON_CLIENT = cygrpc.OperationType.receive_status_on_client + RECV_CLOSE_ON_SERVER = cygrpc.OperationType.receive_close_on_server @enum.unique class EventType(enum.IntEnum): """Mirrors grpc_completion_type in the C core.""" - QUEUE_SHUTDOWN = 0 - QUEUE_TIMEOUT = 1 # if seen on the Python side, something went horridly wrong - OP_COMPLETE = 2 + QUEUE_SHUTDOWN = cygrpc.CompletionType.queue_shutdown + QUEUE_TIMEOUT = cygrpc.CompletionType.queue_timeout + OP_COMPLETE = cygrpc.CompletionType.operation_complete @enum.unique class ConnectivityState(enum.IntEnum): """Mirrors grpc_connectivity_state in the C core.""" - IDLE = 0 - CONNECTING = 1 - READY = 2 - TRANSIENT_FAILURE = 3 - FATAL_FAILURE = 4 + IDLE = cygrpc.ConnectivityState.idle + CONNECTING = cygrpc.ConnectivityState.connecting + READY = cygrpc.ConnectivityState.ready + TRANSIENT_FAILURE = cygrpc.ConnectivityState.transient_failure + FATAL_FAILURE = cygrpc.ConnectivityState.fatal_failure class Status(collections.namedtuple( diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx index 51c4668138..1c07f9f4f4 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/call.pyx @@ -53,24 +53,24 @@ cdef class Call: self.c_call, cy_operations.c_ops, cy_operations.c_nops, <cpython.PyObject *>operation_tag, NULL) - def cancel(self, - grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE, - details=None): + def cancel( + self, grpc.grpc_status_code error_code=grpc.GRPC_STATUS__DO_NOT_USE, + details=None): if not self.is_valid: raise ValueError("invalid call object cannot be used from Python") if (details is None) != (error_code == grpc.GRPC_STATUS__DO_NOT_USE): raise ValueError("if error_code is specified, so must details " "(and vice-versa)") - if isinstance(details, bytes): - pass - elif isinstance(details, basestring): - details = details.encode() - else: - raise TypeError("expected details to be str or bytes") if error_code != grpc.GRPC_STATUS__DO_NOT_USE: + if isinstance(details, bytes): + pass + elif isinstance(details, basestring): + details = details.encode() + else: + raise TypeError("expected details to be str or bytes") self.references.append(details) - return grpc.grpc_call_cancel_with_status(self.c_call, error_code, details, - NULL) + return grpc.grpc_call_cancel_with_status( + self.c_call, error_code, details, NULL) else: return grpc.grpc_call_cancel(self.c_call, NULL) @@ -79,6 +79,12 @@ cdef class Call: return grpc.grpc_call_set_credentials( self.c_call, call_credentials.c_credentials) + def peer(self): + cdef char *peer = grpc.grpc_call_get_peer(self.c_call) + result = <bytes>peer + grpc.gpr_free(peer) + return result + def __dealloc__(self): if self.c_call != NULL: grpc.grpc_call_destroy(self.c_call) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx index e25db3e2a4..a944a83576 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/channel.pyx @@ -27,6 +27,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +cimport cpython + from grpc._cython._cygrpc cimport call from grpc._cython._cygrpc cimport completion_queue from grpc._cython._cygrpc cimport credentials @@ -70,12 +72,16 @@ cdef class Channel: method = method.encode() else: raise TypeError("expected method to be str or bytes") - if isinstance(host, bytes): + cdef char *host_c_string = NULL + if host is None: pass + elif isinstance(host, bytes): + host_c_string = host elif isinstance(host, basestring): host = host.encode() + host_c_string = host else: - raise TypeError("expected host to be str or bytes") + raise TypeError("expected host to be str, bytes, or None") cdef call.Call operation_call = call.Call() operation_call.references = [self, method, host, queue] cdef grpc.grpc_call *parent_call = NULL @@ -83,10 +89,29 @@ cdef class Channel: parent_call = parent.c_call operation_call.c_call = grpc.grpc_channel_create_call( self.c_channel, parent_call, flags, - queue.c_completion_queue, method, host, deadline.c_time, + queue.c_completion_queue, method, host_c_string, deadline.c_time, NULL) return operation_call + def check_connectivity_state(self, bint try_to_connect): + return grpc.grpc_channel_check_connectivity_state(self.c_channel, + try_to_connect) + + def watch_connectivity_state( + self, last_observed_state, records.Timespec deadline not None, + completion_queue.CompletionQueue queue not None, tag): + cdef records.OperationTag operation_tag = records.OperationTag(tag) + cpython.Py_INCREF(operation_tag) + grpc.grpc_channel_watch_connectivity_state( + self.c_channel, last_observed_state, deadline.c_time, + queue.c_completion_queue, <cpython.PyObject *>operation_tag) + + def target(self): + cdef char * target = grpc.grpc_channel_get_target(self.c_channel) + result = <bytes>target + grpc.gpr_free(target) + return result + def __dealloc__(self): if self.c_channel != NULL: grpc.grpc_channel_destroy(self.c_channel) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx index a7a265eab7..2cf49707b4 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/completion_queue.pyx @@ -62,6 +62,8 @@ cdef class CompletionQueue: cdef grpc.grpc_event event # Poll within a critical section + # TODO consider making queue polling contention a hard error to enable + # easier bug discovery with self.poll_condition: while self.is_polling: self.poll_condition.wait(float(deadline) - time.time()) @@ -74,10 +76,12 @@ cdef class CompletionQueue: self.poll_condition.notify() if event.type == grpc.GRPC_QUEUE_TIMEOUT: - return records.Event(event.type, False, None, None, None, None, None) + return records.Event( + event.type, False, None, None, None, None, False, None) elif event.type == grpc.GRPC_QUEUE_SHUTDOWN: self.is_shutdown = True - return records.Event(event.type, True, None, None, None, None, None) + return records.Event( + event.type, True, None, None, None, None, False, None) else: if event.tag != NULL: tag = <records.OperationTag>event.tag @@ -97,7 +101,8 @@ cdef class CompletionQueue: operation_call.references.extend(tag.references) return records.Event( event.type, event.success, user_tag, operation_call, - request_call_details, request_metadata, batch_operations) + request_call_details, request_metadata, tag.is_new_request, + batch_operations) def shutdown(self): grpc.grpc_completion_queue_shutdown(self.c_completion_queue) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd index 7a9fa7b76d..db9f8ddec9 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pxd @@ -27,7 +27,10 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +cimport cpython + from grpc._cython._cygrpc cimport grpc +from grpc._cython._cygrpc cimport records cdef class ChannelCredentials: @@ -49,3 +52,23 @@ cdef class ServerCredentials: cdef grpc.grpc_ssl_pem_key_cert_pair *c_ssl_pem_key_cert_pairs cdef size_t c_ssl_pem_key_cert_pairs_count cdef list references + + +cdef class CredentialsMetadataPlugin: + + cdef object plugin_callback + cdef str plugin_name + + cdef grpc.grpc_metadata_credentials_plugin make_c_plugin(self) + + +cdef class AuthMetadataContext: + + cdef grpc.grpc_auth_metadata_context context + + +cdef void plugin_get_metadata( + void *state, grpc.grpc_auth_metadata_context context, + grpc.grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil + +cdef void plugin_destroy_c_plugin_state(void *state) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx index e9836fec2c..a968894967 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/credentials.pyx @@ -27,6 +27,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +cimport cpython + from grpc._cython._cygrpc cimport grpc from grpc._cython._cygrpc cimport records @@ -71,19 +73,80 @@ cdef class ServerCredentials: def __cinit__(self): self.c_credentials = NULL + self.references = [] def __dealloc__(self): if self.c_credentials != NULL: grpc.grpc_server_credentials_release(self.c_credentials) +cdef class CredentialsMetadataPlugin: + + def __cinit__(self, object plugin_callback, str name): + """ + Args: + plugin_callback (callable): Callback accepting a service URL (str/bytes) + and callback object (accepting a records.Metadata, + grpc.grpc_status_code, and a str/bytes error message). This argument + when called should be non-blocking and eventually call the callback + object with the appropriate status code/details and metadata (if + successful). + name (str): Plugin name. + """ + if not callable(plugin_callback): + raise ValueError('expected callable plugin_callback') + self.plugin_callback = plugin_callback + self.plugin_name = name + + @staticmethod + cdef grpc.grpc_metadata_credentials_plugin make_c_plugin(self): + cdef grpc.grpc_metadata_credentials_plugin result + result.get_metadata = plugin_get_metadata + result.destroy = plugin_destroy_c_plugin_state + result.state = <void *>self + result.type = self.plugin_name + cpython.Py_INCREF(self) + return result + + +cdef class AuthMetadataContext: + + def __cinit__(self): + self.context.service_url = NULL + self.context.method_name = NULL + + @property + def service_url(self): + return self.context.service_url + + @property + def method_name(self): + return self.context.method_name + + +cdef void plugin_get_metadata( + void *state, grpc.grpc_auth_metadata_context context, + grpc.grpc_credentials_plugin_metadata_cb cb, void *user_data) with gil: + def python_callback( + records.Metadata metadata, grpc.grpc_status_code status, + const char *error_details): + cb(user_data, metadata.c_metadata_array.metadata, + metadata.c_metadata_array.count, status, error_details) + cdef CredentialsMetadataPlugin self = <CredentialsMetadataPlugin>state + cdef AuthMetadataContext cy_context = AuthMetadataContext() + cy_context.context = context + self.plugin_callback(cy_context, python_callback) + +cdef void plugin_destroy_c_plugin_state(void *state): + cpython.Py_DECREF(<CredentialsMetadataPlugin>state) + def channel_credentials_google_default(): cdef ChannelCredentials credentials = ChannelCredentials(); credentials.c_credentials = grpc.grpc_google_default_credentials_create() return credentials def channel_credentials_ssl(pem_root_certificates, - records.SslPemKeyCertPair ssl_pem_key_cert_pair): + records.SslPemKeyCertPair ssl_pem_key_cert_pair): if pem_root_certificates is None: pass elif isinstance(pem_root_certificates, bytes): @@ -104,6 +167,7 @@ def channel_credentials_ssl(pem_root_certificates, else: credentials.c_credentials = grpc.grpc_ssl_credentials_create( c_pem_root_certificates, NULL, NULL) + return credentials def channel_credentials_composite( ChannelCredentials credentials_1 not None, @@ -135,7 +199,6 @@ def call_credentials_google_compute_engine(): grpc.grpc_google_compute_engine_credentials_create(NULL)) return credentials -#TODO rename to something like client_credentials_service_account_jwt_access. def call_credentials_service_account_jwt_access( json_key, records.Timespec token_lifetime not None): if isinstance(json_key, bytes): @@ -184,14 +247,25 @@ def call_credentials_google_iam(authorization_token, authority_selector): credentials.references.append(authority_selector) return credentials +def call_credentials_metadata_plugin(CredentialsMetadataPlugin plugin): + cdef CallCredentials credentials = CallCredentials() + credentials.c_credentials = ( + grpc.grpc_metadata_credentials_create_from_plugin(plugin.make_c_plugin(), + NULL)) + # TODO(atash): the following held reference is *probably* never necessary + credentials.references.append(plugin) + return credentials + def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs, bint force_client_auth): + cdef char *c_pem_root_certs = NULL if pem_root_certs is None: pass elif isinstance(pem_root_certs, bytes): - pass + c_pem_root_certs = pem_root_certs elif isinstance(pem_root_certs, basestring): pem_root_certs = pem_root_certs.encode() + c_pem_root_certs = pem_root_certs else: raise TypeError("expected pem_root_certs to be str or bytes") pem_key_cert_pairs = list(pem_key_cert_pairs) @@ -212,7 +286,7 @@ def server_credentials_ssl(pem_root_certs, pem_key_cert_pairs, credentials.c_ssl_pem_key_cert_pairs[i] = ( (<records.SslPemKeyCertPair>pem_key_cert_pairs[i]).c_pair) credentials.c_credentials = grpc.grpc_ssl_server_credentials_create( - pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, + c_pem_root_certs, credentials.c_ssl_pem_key_cert_pairs, credentials.c_ssl_pem_key_cert_pairs_count, force_client_auth, NULL) return credentials diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd index 36aea81a6c..643cdc9e3d 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd +++ b/src/python/grpcio/grpc/_cython/_cygrpc/grpc.pxd @@ -60,6 +60,8 @@ cdef extern from "grpc/support/port_platform.h": # type exactly; just close enough that the operations will be supported in the # underlying C layers. ctypedef unsigned int gpr_uint32 + ctypedef int gpr_int32 + ctypedef long int gpr_int64 cdef extern from "grpc/support/time.h": @@ -71,8 +73,8 @@ cdef extern from "grpc/support/time.h": GPR_TIMESPAN ctypedef struct gpr_timespec: - libc.time.time_t seconds "tv_sec" - int nanoseconds "tv_nsec" + gpr_int64 seconds "tv_sec" + gpr_int32 nanoseconds "tv_nsec" gpr_clock_type clock_type gpr_timespec gpr_time_0(gpr_clock_type type) @@ -132,6 +134,20 @@ cdef extern from "grpc/byte_buffer.h": cdef extern from "grpc/grpc.h": + const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING + const char *GRPC_ARG_ENABLE_CENSUS + const char *GRPC_ARG_MAX_CONCURRENT_STREAMS + const char *GRPC_ARG_MAX_MESSAGE_LENGTH + const char *GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER + const char *GRPC_ARG_DEFAULT_AUTHORITY + const char *GRPC_ARG_PRIMARY_USER_AGENT_STRING + const char *GRPC_ARG_SECONDARY_USER_AGENT_STRING + const char *GRPC_SSL_TARGET_NAME_OVERRIDE_ARG + + const int GRPC_WRITE_BUFFER_HINT + const int GRPC_WRITE_NO_COMPRESS + const int GRPC_WRITE_USED_MASK + ctypedef struct grpc_completion_queue: # We don't care about the internals (and in fact don't know them) pass @@ -149,9 +165,9 @@ cdef extern from "grpc/grpc.h": pass ctypedef enum grpc_arg_type: - grpc_arg_string "GRPC_ARG_STRING" - grpc_arg_integer "GRPC_ARG_INTEGER" - grpc_arg_pointer "GRPC_ARG_POINTER" + GRPC_ARG_STRING + GRPC_ARG_INTEGER + GRPC_ARG_POINTER ctypedef struct grpc_arg_value_pointer: void *address "p" @@ -185,6 +201,13 @@ cdef extern from "grpc/grpc.h": GRPC_CALL_ERROR_INVALID_FLAGS GRPC_CALL_ERROR_INVALID_METADATA + ctypedef enum grpc_connectivity_state: + GRPC_CHANNEL_IDLE + GRPC_CHANNEL_CONNECTING + GRPC_CHANNEL_READY + GRPC_CHANNEL_TRANSIENT_FAILURE + GRPC_CHANNEL_FATAL_FAILURE + ctypedef struct grpc_metadata: const char *key const char *value @@ -279,9 +302,9 @@ cdef extern from "grpc/grpc.h": grpc_status_code status, const char *description, void *reserved) + char *grpc_call_get_peer(grpc_call *call) void grpc_call_destroy(grpc_call *call) - grpc_channel *grpc_insecure_channel_create(const char *target, const grpc_channel_args *args, void *reserved) @@ -291,6 +314,12 @@ cdef extern from "grpc/grpc.h": grpc_completion_queue *completion_queue, const char *method, const char *host, gpr_timespec deadline, void *reserved) + grpc_connectivity_state grpc_channel_check_connectivity_state( + grpc_channel *channel, int try_to_connect) + void grpc_channel_watch_connectivity_state( + grpc_channel *channel, grpc_connectivity_state last_observed_state, + gpr_timespec deadline, grpc_completion_queue *cq, void *tag) + char *grpc_channel_get_target(grpc_channel *channel) void grpc_channel_destroy(grpc_channel *channel) grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) @@ -367,3 +396,27 @@ cdef extern from "grpc/grpc_security.h": grpc_call_error grpc_call_set_credentials(grpc_call *call, grpc_call_credentials *creds) + + ctypedef struct grpc_auth_context: + # We don't care about the internals (and in fact don't know them) + pass + + ctypedef struct grpc_auth_metadata_context: + const char *service_url + const char *method_name + const grpc_auth_context *channel_auth_context + + ctypedef void (*grpc_credentials_plugin_metadata_cb)( + void *user_data, const grpc_metadata *creds_md, size_t num_creds_md, + grpc_status_code status, const char *error_details) + + ctypedef struct grpc_metadata_credentials_plugin: + void (*get_metadata)( + void *state, grpc_auth_metadata_context context, + grpc_credentials_plugin_metadata_cb cb, void *user_data) + void (*destroy)(void *state) + void *state + const char *type + + grpc_call_credentials *grpc_metadata_credentials_create_from_plugin( + grpc_metadata_credentials_plugin plugin, void *reserved) diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd index 9ee487882a..4c844e4cb6 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pxd @@ -66,6 +66,7 @@ cdef class Event: cdef readonly call.Call operation_call # For Server.request_call + cdef readonly bint is_new_request cdef readonly CallDetails request_call_details cdef readonly Metadata request_metadata diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx index 8edee09c2d..79a7f8f563 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/records.pyx @@ -32,6 +32,30 @@ from grpc._cython._cygrpc cimport call from grpc._cython._cygrpc cimport server +class ConnectivityState: + idle = grpc.GRPC_CHANNEL_IDLE + connecting = grpc.GRPC_CHANNEL_CONNECTING + ready = grpc.GRPC_CHANNEL_READY + transient_failure = grpc.GRPC_CHANNEL_TRANSIENT_FAILURE + fatal_failure = grpc.GRPC_CHANNEL_FATAL_FAILURE + + +class ChannelArgKey: + enable_census = grpc.GRPC_ARG_ENABLE_CENSUS + max_concurrent_streams = grpc.GRPC_ARG_MAX_CONCURRENT_STREAMS + max_message_length = grpc.GRPC_ARG_MAX_MESSAGE_LENGTH + http2_initial_sequence_number = grpc.GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER + default_authority = grpc.GRPC_ARG_DEFAULT_AUTHORITY + primary_user_agent_string = grpc.GRPC_ARG_PRIMARY_USER_AGENT_STRING + secondary_user_agent_string = grpc.GRPC_ARG_SECONDARY_USER_AGENT_STRING + ssl_target_name_override = grpc.GRPC_SSL_TARGET_NAME_OVERRIDE_ARG + + +class WriteFlag: + buffer_hint = grpc.GRPC_WRITE_BUFFER_HINT + no_compress = grpc.GRPC_WRITE_NO_COMPRESS + + class StatusCode: ok = grpc.GRPC_STATUS_OK cancelled = grpc.GRPC_STATUS_CANCELLED @@ -88,7 +112,10 @@ cdef class Timespec: def __cinit__(self, time): if time is None: self.c_time = grpc.gpr_now(grpc.GPR_CLOCK_REALTIME) - elif isinstance(time, float): + return + if isinstance(time, int): + time = float(time) + if isinstance(time, float): if time == float("+inf"): self.c_time = grpc.gpr_inf_future(grpc.GPR_CLOCK_REALTIME) elif time == float("-inf"): @@ -97,8 +124,11 @@ cdef class Timespec: self.c_time.seconds = time self.c_time.nanoseconds = (time - float(self.c_time.seconds)) * 1e9 self.c_time.clock_type = grpc.GPR_CLOCK_REALTIME + elif isinstance(time, Timespec): + self.c_time = (<Timespec>time).c_time else: - raise TypeError("expected time to be float") + raise TypeError("expected time to be float, int, or Timespec, not {}" + .format(type(time))) @property def seconds(self): @@ -166,6 +196,7 @@ cdef class Event: object tag, call.Call operation_call, CallDetails request_call_details, Metadata request_metadata, + bint is_new_request, Operations batch_operations): self.type = type self.success = success @@ -174,6 +205,7 @@ cdef class Event: self.request_call_details = request_call_details self.request_metadata = request_metadata self.batch_operations = batch_operations + self.is_new_request = is_new_request cdef class ByteBuffer: @@ -186,8 +218,14 @@ cdef class ByteBuffer: pass elif isinstance(data, basestring): data = data.encode() + elif isinstance(data, ByteBuffer): + data = (<ByteBuffer>data).bytes() + if data is None: + self.c_byte_buffer = NULL + return else: - raise TypeError("expected value to be of type str or bytes") + raise TypeError("expected value to be of type str, bytes, or " + "ByteBuffer, not {}".format(type(data))) cdef char *c_data = data data_slice = grpc.gpr_slice_from_copied_buffer(c_data, len(data)) @@ -410,12 +448,22 @@ cdef class Operation: return self.c_op.type @property + def has_status(self): + return self.c_op.type == grpc.GRPC_OP_RECV_STATUS_ON_CLIENT + + @property def received_message(self): if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE: raise TypeError("self must be an operation receiving a message") return self._received_message @property + def received_message_or_none(self): + if self.c_op.type != grpc.GRPC_OP_RECV_MESSAGE: + return None + return self._received_message + + @property def received_metadata(self): if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT): @@ -423,12 +471,25 @@ cdef class Operation: return self._received_metadata @property + def received_metadata_or_none(self): + if (self.c_op.type != grpc.GRPC_OP_RECV_INITIAL_METADATA and + self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT): + return None + return self._received_metadata + + @property def received_status_code(self): if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: raise TypeError("self must be an operation receiving a status code") return self._received_status_code @property + def received_status_code_or_none(self): + if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: + return None + return self._received_status_code + + @property def received_status_details(self): if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: raise TypeError("self must be an operation receiving status details") @@ -438,12 +499,27 @@ cdef class Operation: return None @property + def received_status_details_or_none(self): + if self.c_op.type != grpc.GRPC_OP_RECV_STATUS_ON_CLIENT: + return None + if self._received_status_details: + return self._received_status_details + else: + return None + + @property def received_cancelled(self): if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER: raise TypeError("self must be an operation receiving cancellation " "information") return False if self._received_cancelled == 0 else True + @property + def received_cancelled_or_none(self): + if self.c_op.type != grpc.GRPC_OP_RECV_CLOSE_ON_SERVER: + return None + return False if self._received_cancelled == 0 else True + def __dealloc__(self): # We *almost* don't need to do anything; most of the objects are handled by # Python. The remaining one(s) are primitive fields filled in by GRPC core. diff --git a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx index 6d20d2910c..46df8bf77f 100644 --- a/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx +++ b/src/python/grpcio/grpc/_cython/_cygrpc/server.pyx @@ -132,7 +132,7 @@ cdef class Server: def cancel_all_calls(self): if not self.is_shutting_down: - raise ValueError("the server must be shutting down to cancel all calls") + raise RuntimeError("the server must be shutting down to cancel all calls") elif self.is_shutdown: return else: diff --git a/src/python/grpcio/grpc/_cython/adapter_low.py b/src/python/grpcio/grpc/_cython/adapter_low.py deleted file mode 100644 index 4f24da330f..0000000000 --- a/src/python/grpcio/grpc/_cython/adapter_low.py +++ /dev/null @@ -1,102 +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. - - -# Adapter from grpc._cython.types to the surface expected by -# grpc._adapter._intermediary_low. -# -# TODO(atash): Once this is plugged into grpc._adapter._intermediary_low, remove -# both grpc._adapter._intermediary_low and this file. The fore and rear links in -# grpc._adapter should be able to use grpc._cython.types directly. - -from grpc._adapter import _types as type_interfaces -from grpc._cython import cygrpc - - -class ClientCredentials(object): - def __init__(self): - raise NotImplementedError() - - @staticmethod - def google_default(): - raise NotImplementedError() - - @staticmethod - def ssl(): - raise NotImplementedError() - - @staticmethod - def composite(): - raise NotImplementedError() - - @staticmethod - def compute_engine(): - raise NotImplementedError() - - @staticmethod - def jwt(): - raise NotImplementedError() - - @staticmethod - def refresh_token(): - raise NotImplementedError() - - @staticmethod - def iam(): - raise NotImplementedError() - - -class ServerCredentials(object): - def __init__(self): - raise NotImplementedError() - - @staticmethod - def ssl(): - raise NotImplementedError() - - -class CompletionQueue(type_interfaces.CompletionQueue): - def __init__(self): - raise NotImplementedError() - - -class Call(type_interfaces.Call): - def __init__(self): - raise NotImplementedError() - - -class Channel(type_interfaces.Channel): - def __init__(self): - raise NotImplementedError() - - -class Server(type_interfaces.Server): - def __init__(self): - raise NotImplementedError() - diff --git a/src/python/grpcio/grpc/_cython/cygrpc.pyx b/src/python/grpcio/grpc/_cython/cygrpc.pyx index b20dda8a95..16ec12dac0 100644 --- a/src/python/grpcio/grpc/_cython/cygrpc.pyx +++ b/src/python/grpcio/grpc/_cython/cygrpc.pyx @@ -44,6 +44,9 @@ from grpc._cython._cygrpc import completion_queue from grpc._cython._cygrpc import records from grpc._cython._cygrpc import server +ConnectivityState = records.ConnectivityState +ChannelArgKey = records.ChannelArgKey +WriteFlag = records.WriteFlag StatusCode = records.StatusCode CallError = records.CallError CompletionType = records.CompletionType @@ -73,6 +76,8 @@ Operations = records.Operations CallCredentials = credentials.CallCredentials ChannelCredentials = credentials.ChannelCredentials ServerCredentials = credentials.ServerCredentials +CredentialsMetadataPlugin = credentials.CredentialsMetadataPlugin +AuthMetadataContext = credentials.AuthMetadataContext channel_credentials_google_default = ( credentials.channel_credentials_google_default) @@ -88,6 +93,7 @@ call_credentials_jwt_access = ( call_credentials_refresh_token = ( credentials.call_credentials_google_refresh_token) call_credentials_google_iam = credentials.call_credentials_google_iam +call_credentials_metadata_plugin = credentials.call_credentials_metadata_plugin server_credentials_ssl = credentials.server_credentials_ssl CompletionQueue = completion_queue.CompletionQueue diff --git a/src/python/grpcio/grpc/_links/invocation.py b/src/python/grpcio/grpc/_links/invocation.py index 67ef86a176..5ca0a0ee60 100644 --- a/src/python/grpcio/grpc/_links/invocation.py +++ b/src/python/grpcio/grpc/_links/invocation.py @@ -182,15 +182,15 @@ class _Kernel(object): def _on_finish_event(self, operation_id, event, rpc_state): _no_longer_due(_FINISH, rpc_state, operation_id, self._rpc_states) - if event.status.code is _intermediary_low.Code.OK: + if event.status.code == _intermediary_low.Code.OK: termination = links.Ticket.Termination.COMPLETION - elif event.status.code is _intermediary_low.Code.CANCELLED: + elif event.status.code == _intermediary_low.Code.CANCELLED: termination = links.Ticket.Termination.CANCELLATION - elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED: + elif event.status.code == _intermediary_low.Code.DEADLINE_EXCEEDED: termination = links.Ticket.Termination.EXPIRATION - elif event.status.code is _intermediary_low.Code.UNIMPLEMENTED: + elif event.status.code == _intermediary_low.Code.UNIMPLEMENTED: termination = links.Ticket.Termination.REMOTE_FAILURE - elif event.status.code is _intermediary_low.Code.UNKNOWN: + elif event.status.code == _intermediary_low.Code.UNKNOWN: termination = links.Ticket.Termination.LOCAL_FAILURE else: termination = links.Ticket.Termination.TRANSMISSION_FAILURE @@ -262,7 +262,7 @@ class _Kernel(object): self._channel, self._completion_queue, '/%s/%s' % (group, method), self._host, time.time() + timeout) if options is not None and options.credentials is not None: - call.set_credentials(options.credentials._intermediary_low_credentials) + call.set_credentials(options.credentials._low_credentials) if transformed_initial_metadata is not None: for metadata_key, metadata_value in transformed_initial_metadata: call.add_metadata(metadata_key, metadata_value) diff --git a/src/python/grpcio/grpc/_links/service.py b/src/python/grpcio/grpc/_links/service.py index f56df84007..01edee6896 100644 --- a/src/python/grpcio/grpc/_links/service.py +++ b/src/python/grpcio/grpc/_links/service.py @@ -254,12 +254,12 @@ class _Kernel(object): rpc_state = self._rpc_states[call] _no_longer_due(_FINISH, rpc_state, call, self._rpc_states) code = event.status.code - if code is _intermediary_low.Code.OK: + if code == _intermediary_low.Code.OK: return - if code is _intermediary_low.Code.CANCELLED: + if code == _intermediary_low.Code.CANCELLED: termination = links.Ticket.Termination.CANCELLATION - elif code is _intermediary_low.Code.DEADLINE_EXCEEDED: + elif code == _intermediary_low.Code.DEADLINE_EXCEEDED: termination = links.Ticket.Termination.EXPIRATION else: termination = links.Ticket.Termination.TRANSMISSION_FAILURE diff --git a/src/python/grpcio/grpc/beta/_server.py b/src/python/grpcio/grpc/beta/_server.py index 05b954d186..2b520cc7e5 100644 --- a/src/python/grpcio/grpc/beta/_server.py +++ b/src/python/grpcio/grpc/beta/_server.py @@ -44,6 +44,12 @@ _DEFAULT_TIMEOUT = 300 _MAXIMUM_TIMEOUT = 24 * 60 * 60 +def _set_event(): + event = threading.Event() + event.set() + return event + + class _GRPCServicer(base.Servicer): def __init__(self, delegate): @@ -61,86 +67,143 @@ class _GRPCServicer(base.Servicer): raise -def _disassemble(grpc_link, end_link, pool, event, grace): - grpc_link.begin_stop() - end_link.stop(grace).wait() - grpc_link.end_stop() - grpc_link.join_link(utilities.NULL_LINK) - end_link.join_link(utilities.NULL_LINK) - if pool is not None: - pool.shutdown(wait=True) - event.set() +class _Server(interfaces.Server): + def __init__( + self, implementations, multi_implementation, pool, pool_size, + default_timeout, maximum_timeout, grpc_link): + self._lock = threading.Lock() + self._implementations = implementations + self._multi_implementation = multi_implementation + self._customer_pool = pool + self._pool_size = pool_size + self._default_timeout = default_timeout + self._maximum_timeout = maximum_timeout + self._grpc_link = grpc_link -class Server(interfaces.Server): + self._end_link = None + self._stop_events = None + self._pool = None - def __init__(self, grpc_link, end_link, pool): - self._grpc_link = grpc_link - self._end_link = end_link - self._pool = pool + def _start(self): + with self._lock: + if self._end_link is not None: + raise ValueError('Cannot start already-started server!') + + if self._customer_pool is None: + self._pool = logging_pool.pool(self._pool_size) + assembly_pool = self._pool + else: + assembly_pool = self._customer_pool + + servicer = _GRPCServicer( + _crust_implementations.servicer( + self._implementations, self._multi_implementation, assembly_pool)) + + self._end_link = _core_implementations.service_end_link( + servicer, self._default_timeout, self._maximum_timeout) + + self._grpc_link.join_link(self._end_link) + self._end_link.join_link(self._grpc_link) + self._grpc_link.start() + self._end_link.start() + + def _dissociate_links_and_shut_down_pool(self): + self._grpc_link.end_stop() + self._grpc_link.join_link(utilities.NULL_LINK) + self._end_link.join_link(utilities.NULL_LINK) + self._end_link = None + if self._pool is not None: + self._pool.shutdown(wait=True) + self._pool = None + + def _stop_stopping(self): + self._dissociate_links_and_shut_down_pool() + for stop_event in self._stop_events: + stop_event.set() + self._stop_events = None + + def _stop_started(self): + self._grpc_link.begin_stop() + self._end_link.stop(0).wait() + self._dissociate_links_and_shut_down_pool() + + def _foreign_thread_stop(self, end_stop_event, stop_events): + end_stop_event.wait() + with self._lock: + if self._stop_events is stop_events: + self._stop_stopping() + + def _schedule_stop(self, grace): + with self._lock: + if self._end_link is None: + return _set_event() + server_stop_event = threading.Event() + if self._stop_events is None: + self._stop_events = [server_stop_event] + self._grpc_link.begin_stop() + else: + self._stop_events.append(server_stop_event) + end_stop_event = self._end_link.stop(grace) + end_stop_thread = threading.Thread( + target=self._foreign_thread_stop, + args=(end_stop_event, self._stop_events)) + end_stop_thread.start() + return server_stop_event + + def _stop_now(self): + with self._lock: + if self._end_link is not None: + if self._stop_events is None: + self._stop_started() + else: + self._stop_stopping() def add_insecure_port(self, address): - return self._grpc_link.add_port(address, None) + with self._lock: + if self._end_link is None: + return self._grpc_link.add_port(address, None) + else: + raise ValueError('Can\'t add port to serving server!') def add_secure_port(self, address, server_credentials): - return self._grpc_link.add_port( - address, server_credentials._intermediary_low_credentials) # pylint: disable=protected-access - - def _start(self): - self._grpc_link.join_link(self._end_link) - self._end_link.join_link(self._grpc_link) - self._grpc_link.start() - self._end_link.start() - - def _stop(self, grace): - stop_event = threading.Event() - if 0 < grace: - disassembly_thread = threading.Thread( - target=_disassemble, - args=( - self._grpc_link, self._end_link, self._pool, stop_event, grace,)) - disassembly_thread.start() - return stop_event - else: - _disassemble(self._grpc_link, self._end_link, self._pool, stop_event, 0) - return stop_event + with self._lock: + if self._end_link is None: + return self._grpc_link.add_port( + address, server_credentials._low_credentials) # pylint: disable=protected-access + else: + raise ValueError('Can\'t add port to serving server!') def start(self): self._start() def stop(self, grace): - return self._stop(grace) + if 0 < grace: + return self._schedule_stop(grace) + else: + self._stop_now() + return _set_event() def __enter__(self): self._start() return self def __exit__(self, exc_type, exc_val, exc_tb): - self._stop(0).wait() + self._stop_now() return False + def __del__(self): + self._stop_now() + def server( implementations, multi_implementation, request_deserializers, response_serializers, thread_pool, thread_pool_size, default_timeout, maximum_timeout): - if thread_pool is None: - service_thread_pool = logging_pool.pool( - _DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size) - assembly_thread_pool = service_thread_pool - else: - service_thread_pool = thread_pool - assembly_thread_pool = None - - servicer = _GRPCServicer( - _crust_implementations.servicer( - implementations, multi_implementation, service_thread_pool)) - grpc_link = service.service_link(request_deserializers, response_serializers) - - end_link = _core_implementations.service_end_link( - servicer, + return _Server( + implementations, multi_implementation, thread_pool, + _DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size, _DEFAULT_TIMEOUT if default_timeout is None else default_timeout, - _MAXIMUM_TIMEOUT if maximum_timeout is None else maximum_timeout) - - return Server(grpc_link, end_link, assembly_thread_pool) + _MAXIMUM_TIMEOUT if maximum_timeout is None else maximum_timeout, + grpc_link) diff --git a/src/python/grpcio/grpc/beta/_stub.py b/src/python/grpcio/grpc/beta/_stub.py index 11dab889cd..2af019309a 100644 --- a/src/python/grpcio/grpc/beta/_stub.py +++ b/src/python/grpcio/grpc/beta/_stub.py @@ -42,76 +42,114 @@ _DEFAULT_POOL_SIZE = 6 class _AutoIntermediary(object): - def __init__(self, delegate, on_deletion): + def __init__(self, up, down, delegate): + self._lock = threading.Lock() + self._up = up + self._down = down + self._in_context = False self._delegate = delegate - self._on_deletion = on_deletion def __getattr__(self, attr): - return getattr(self._delegate, attr) + with self._lock: + if self._delegate is None: + raise AttributeError('No useful attributes out of context!') + else: + return getattr(self._delegate, attr) def __enter__(self): - return self + with self._lock: + if self._in_context: + raise ValueError('Already in context!') + elif self._delegate is None: + self._delegate = self._up() + self._in_context = True + return self def __exit__(self, exc_type, exc_val, exc_tb): - return False + with self._lock: + if not self._in_context: + raise ValueError('Not in context!') + self._down() + self._in_context = False + self._delegate = None + return False def __del__(self): - self._on_deletion() + with self._lock: + if self._delegate is not None: + self._down() + self._delegate = None + + +class _StubAssemblyManager(object): + + def __init__( + self, thread_pool, thread_pool_size, end_link, grpc_link, stub_creator): + self._thread_pool = thread_pool + self._pool_size = thread_pool_size + self._end_link = end_link + self._grpc_link = grpc_link + self._stub_creator = stub_creator + self._own_pool = None + + def up(self): + if self._thread_pool is None: + self._own_pool = logging_pool.pool( + _DEFAULT_POOL_SIZE if self._pool_size is None else self._pool_size) + assembly_pool = self._own_pool + else: + assembly_pool = self._thread_pool + self._end_link.join_link(self._grpc_link) + self._grpc_link.join_link(self._end_link) + self._end_link.start() + self._grpc_link.start() + return self._stub_creator(self._end_link, assembly_pool) + + def down(self): + self._end_link.stop(0).wait() + self._grpc_link.stop() + self._end_link.join_link(utilities.NULL_LINK) + self._grpc_link.join_link(utilities.NULL_LINK) + if self._own_pool is not None: + self._own_pool.shutdown(wait=True) + self._own_pool = None def _assemble( channel, host, metadata_transformer, request_serializers, - response_deserializers, thread_pool, thread_pool_size): + response_deserializers, thread_pool, thread_pool_size, stub_creator): end_link = _core_implementations.invocation_end_link() grpc_link = invocation.invocation_link( channel, host, metadata_transformer, request_serializers, response_deserializers) - if thread_pool is None: - invocation_pool = logging_pool.pool( - _DEFAULT_POOL_SIZE if thread_pool_size is None else thread_pool_size) - assembly_pool = invocation_pool - else: - invocation_pool = thread_pool - assembly_pool = None - end_link.join_link(grpc_link) - grpc_link.join_link(end_link) - end_link.start() - grpc_link.start() - return end_link, grpc_link, invocation_pool, assembly_pool - - -def _disassemble(end_link, grpc_link, pool): - end_link.stop(24 * 60 * 60).wait() - grpc_link.stop() - end_link.join_link(utilities.NULL_LINK) - grpc_link.join_link(utilities.NULL_LINK) - if pool is not None: - pool.shutdown(wait=True) - - -def _wrap_assembly(stub, end_link, grpc_link, assembly_pool): - disassembly_thread = threading.Thread( - target=_disassemble, args=(end_link, grpc_link, assembly_pool)) - return _AutoIntermediary(stub, disassembly_thread.start) + stub_assembly_manager = _StubAssemblyManager( + thread_pool, thread_pool_size, end_link, grpc_link, stub_creator) + stub = stub_assembly_manager.up() + return _AutoIntermediary( + stub_assembly_manager.up, stub_assembly_manager.down, stub) + + +def _dynamic_stub_creator(service, cardinalities): + def create_dynamic_stub(end_link, invocation_pool): + return _crust_implementations.dynamic_stub( + end_link, service, cardinalities, invocation_pool) + return create_dynamic_stub def generic_stub( channel, host, metadata_transformer, request_serializers, response_deserializers, thread_pool, thread_pool_size): - end_link, grpc_link, invocation_pool, assembly_pool = _assemble( + return _assemble( channel, host, metadata_transformer, request_serializers, - response_deserializers, thread_pool, thread_pool_size) - stub = _crust_implementations.generic_stub(end_link, invocation_pool) - return _wrap_assembly(stub, end_link, grpc_link, assembly_pool) + response_deserializers, thread_pool, thread_pool_size, + _crust_implementations.generic_stub) def dynamic_stub( channel, host, service, cardinalities, metadata_transformer, request_serializers, response_deserializers, thread_pool, thread_pool_size): - end_link, grpc_link, invocation_pool, assembly_pool = _assemble( + return _assemble( channel, host, metadata_transformer, request_serializers, - response_deserializers, thread_pool, thread_pool_size) - stub = _crust_implementations.dynamic_stub( - end_link, service, cardinalities, invocation_pool) - return _wrap_assembly(stub, end_link, grpc_link, assembly_pool) + response_deserializers, thread_pool, thread_pool_size, + _dynamic_stub_creator(service, cardinalities)) diff --git a/src/python/grpcio/grpc/beta/implementations.py b/src/python/grpcio/grpc/beta/implementations.py index c9d64ad35a..a0ca330d2c 100644 --- a/src/python/grpcio/grpc/beta/implementations.py +++ b/src/python/grpcio/grpc/beta/implementations.py @@ -36,6 +36,7 @@ import threading # pylint: disable=unused-import # cardinality and face are referenced from specification in this module. from grpc._adapter import _intermediary_low +from grpc._adapter import _low from grpc._adapter import _types from grpc.beta import _connectivity_channel from grpc.beta import _server @@ -48,7 +49,7 @@ _CHANNEL_SUBSCRIPTION_CALLBACK_ERROR_LOG_MESSAGE = ( 'Exception calling channel subscription callback!') -class ClientCredentials(object): +class ChannelCredentials(object): """A value encapsulating the data required to create a secure Channel. This class and its instances have no supported interface - it exists to define @@ -56,13 +57,12 @@ class ClientCredentials(object): functions. """ - def __init__(self, low_credentials, intermediary_low_credentials): + def __init__(self, low_credentials): self._low_credentials = low_credentials - self._intermediary_low_credentials = intermediary_low_credentials -def ssl_client_credentials(root_certificates, private_key, certificate_chain): - """Creates a ClientCredentials for use with an SSL-enabled Channel. +def ssl_channel_credentials(root_certificates, private_key, certificate_chain): + """Creates a ChannelCredentials for use with an SSL-enabled Channel. Args: root_certificates: The PEM-encoded root certificates or None to ask for @@ -73,12 +73,73 @@ def ssl_client_credentials(root_certificates, private_key, certificate_chain): certificate chain should be used. Returns: - A ClientCredentials for use with an SSL-enabled Channel. + A ChannelCredentials for use with an SSL-enabled Channel. """ - intermediary_low_credentials = _intermediary_low.ClientCredentials( - root_certificates, private_key, certificate_chain) - return ClientCredentials( - intermediary_low_credentials._internal, intermediary_low_credentials) # pylint: disable=protected-access + return ChannelCredentials(_low.channel_credentials_ssl( + root_certificates, private_key, certificate_chain)) + + +class CallCredentials(object): + """A value encapsulating data asserting an identity over an *established* + channel. May be composed with ChannelCredentials to always assert identity for + every call over that channel. + + This class and its instances have no supported interface - it exists to define + the type of its instances and its instances exist to be passed to other + functions. + """ + + def __init__(self, low_credentials): + self._low_credentials = low_credentials + + +def metadata_call_credentials(metadata_plugin, name=None): + """Construct CallCredentials from an interfaces.GRPCAuthMetadataPlugin. + + Args: + metadata_plugin: An interfaces.GRPCAuthMetadataPlugin to use in constructing + the CallCredentials object. + + Returns: + A CallCredentials object for use in a GRPCCallOptions object. + """ + if name is None: + name = metadata_plugin.__name__ + return CallCredentials( + _low.call_credentials_metadata_plugin(metadata_plugin, name)) + +def composite_call_credentials(call_credentials, additional_call_credentials): + """Compose two CallCredentials to make a new one. + + Args: + call_credentials: A CallCredentials object. + additional_call_credentials: Another CallCredentials object to compose on + top of call_credentials. + + Returns: + A CallCredentials object for use in a GRPCCallOptions object. + """ + return CallCredentials( + _low.call_credentials_composite( + call_credentials._low_credentials, + additional_call_credentials._low_credentials)) + +def composite_channel_credentials(channel_credentials, + additional_call_credentials): + """Compose ChannelCredentials on top of client credentials to make a new one. + + Args: + channel_credentials: A ChannelCredentials object. + additional_call_credentials: A CallCredentials object to compose on + top of channel_credentials. + + Returns: + A ChannelCredentials object for use in a GRPCCallOptions object. + """ + return ChannelCredentials( + _low.channel_credentials_composite( + channel_credentials._low_credentials, + additional_call_credentials._low_credentials)) class Channel(object): @@ -135,19 +196,19 @@ def insecure_channel(host, port): return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access -def secure_channel(host, port, client_credentials): +def secure_channel(host, port, channel_credentials): """Creates a secure Channel to a remote host. Args: host: The name of the remote host to which to connect. port: The port of the remote host to which to connect. - client_credentials: A ClientCredentials. + channel_credentials: A ChannelCredentials. Returns: A secure Channel to the remote host through which RPCs may be conducted. """ intermediary_low_channel = _intermediary_low.Channel( - '%s:%d' % (host, port), client_credentials._intermediary_low_credentials) + '%s:%d' % (host, port), channel_credentials._low_credentials) return Channel(intermediary_low_channel._internal, intermediary_low_channel) # pylint: disable=protected-access @@ -251,9 +312,8 @@ class ServerCredentials(object): functions. """ - def __init__(self, low_credentials, intermediary_low_credentials): + def __init__(self, low_credentials): self._low_credentials = low_credentials - self._intermediary_low_credentials = intermediary_low_credentials def ssl_server_credentials( @@ -282,11 +342,9 @@ def ssl_server_credentials( raise ValueError( 'Illegal to require client auth without providing root certificates!') else: - intermediary_low_credentials = _intermediary_low.ServerCredentials( + return ServerCredentials(_low.server_credentials_ssl( root_certificates, private_key_certificate_chain_pairs, - require_client_auth) - return ServerCredentials( - intermediary_low_credentials._internal, intermediary_low_credentials) # pylint: disable=protected-access + require_client_auth)) class ServerOptions(object): diff --git a/src/python/grpcio/grpc/beta/interfaces.py b/src/python/grpcio/grpc/beta/interfaces.py index d4ca56500f..0663119163 100644 --- a/src/python/grpcio/grpc/beta/interfaces.py +++ b/src/python/grpcio/grpc/beta/interfaces.py @@ -100,14 +100,55 @@ def grpc_call_options(disable_compression=False, credentials=None): disable_compression: A boolean indicating whether or not compression should be disabled for the request object of the RPC. Only valid for request-unary RPCs. - credentials: Reserved for gRPC per-call credentials. The type for this does - not exist yet at the Python level. + credentials: A CallCredentials object to use for the invoked RPC. """ - if credentials is not None: - raise ValueError('`credentials` is a reserved argument') return GRPCCallOptions(disable_compression, None, credentials) +class GRPCAuthMetadataContext(object): + """Provides information to call credentials metadata plugins. + + Attributes: + service_url: A string URL of the service being called into. + method_name: A string of the fully qualified method name being called. + """ + __metaclass__ = abc.ABCMeta + + +class GRPCAuthMetadataPluginCallback(object): + """Callback object received by a metadata plugin.""" + __metaclass__ = abc.ABCMeta + + def __call__(self, metadata, error): + """Inform the gRPC runtime of the metadata to construct a CallCredentials. + + Args: + metadata: An iterable of 2-sequences (e.g. tuples) of metadata key/value + pairs. + error: An Exception to indicate error or None to indicate success. + """ + raise NotImplementedError() + + +class GRPCAuthMetadataPlugin(object): + """ + """ + __metaclass__ = abc.ABCMeta + + def __call__(self, context, callback): + """Invoke the plugin. + + Must not block. Need only be called by the gRPC runtime. + + Args: + context: A GRPCAuthMetadataContext providing information on what the + plugin is being used for. + callback: A GRPCAuthMetadataPluginCallback to be invoked either + synchronously or asynchronously. + """ + raise NotImplementedError() + + class GRPCServicerContext(object): """Exposes gRPC-specific options and behaviors to code servicing RPCs.""" __metaclass__ = abc.ABCMeta diff --git a/src/python/grpcio/grpc/framework/core/_end.py b/src/python/grpcio/grpc/framework/core/_end.py index 8e07d9061e..9c615672aa 100644 --- a/src/python/grpcio/grpc/framework/core/_end.py +++ b/src/python/grpcio/grpc/framework/core/_end.py @@ -85,35 +85,6 @@ def _future_shutdown(lock, cycle, event): return in_future -def _termination_action(lock, stats, operation_id, cycle): - """Constructs the termination action for a single operation. - - Args: - lock: A lock to hold during the termination action. - stats: A mapping from base.Outcome.Kind values to integers to increment - with the outcome kind given to the termination action. - operation_id: The operation ID for the termination action. - cycle: A _Cycle value to be updated during the termination action. - - Returns: - A callable that takes an operation outcome kind as its sole parameter and - that should be used as the termination action for the operation - associated with the given operation ID. - """ - def termination_action(outcome_kind): - with lock: - stats[outcome_kind] += 1 - cycle.operations.pop(operation_id, None) - if not cycle.operations: - for action in cycle.idle_actions: - cycle.pool.submit(action) - cycle.idle_actions = [] - if cycle.grace: - _cancel_futures(cycle.futures) - cycle.pool.shutdown(wait=False) - return termination_action - - class _End(End): """An End implementation.""" @@ -133,6 +104,31 @@ class _End(End): self._cycle = None + def _termination_action(self, operation_id): + """Constructs the termination action for a single operation. + + Args: + operation_id: The operation ID for the termination action. + + Returns: + A callable that takes an operation outcome kind as its sole parameter and + that should be used as the termination action for the operation + associated with the given operation ID. + """ + def termination_action(outcome_kind): + with self._lock: + self._stats[outcome_kind] += 1 + self._cycle.operations.pop(operation_id, None) + if not self._cycle.operations: + for action in self._cycle.idle_actions: + self._cycle.pool.submit(action) + self._cycle.idle_actions = [] + if self._cycle.grace: + _cancel_futures(self._cycle.futures) + self._cycle.pool.shutdown(wait=False) + self._cycle = None + return termination_action + def start(self): """See base.End.start for specification.""" with self._lock: @@ -174,8 +170,7 @@ class _End(End): with self._lock: if self._cycle is None or self._cycle.grace: raise ValueError('Can\'t operate on stopped or stopping End!') - termination_action = _termination_action( - self._lock, self._stats, operation_id, self._cycle) + termination_action = self._termination_action(operation_id) operation = _operation.invocation_operate( operation_id, group, method, subscription, timeout, protocol_options, initial_metadata, payload, completion, self._mate.accept_ticket, @@ -208,8 +203,7 @@ class _End(End): if operation is not None: operation.handle_ticket(ticket) elif self._servicer_package is not None and not self._cycle.grace: - termination_action = _termination_action( - self._lock, self._stats, ticket.operation_id, self._cycle) + termination_action = self._termination_action(ticket.operation_id) operation = _operation.service_operate( self._servicer_package, ticket, self._mate.accept_ticket, termination_action, self._cycle.pool) diff --git a/src/python/grpcio/requirements.txt b/src/python/grpcio/requirements.txt index ee8568120b..06516ee0d7 100644 --- a/src/python/grpcio/requirements.txt +++ b/src/python/grpcio/requirements.txt @@ -1,3 +1,4 @@ enum34>=1.0.4 futures>=2.2.0 cython>=0.23 +coverage>=4.0 diff --git a/src/python/grpcio/setup.cfg b/src/python/grpcio/setup.cfg index 8f69613632..52b6b50900 100644 --- a/src/python/grpcio/setup.cfg +++ b/src/python/grpcio/setup.cfg @@ -1,2 +1,8 @@ +[coverage:run] +plugins = Cython.Coverage + [build_ext] inplace=1 + +[build_proto_modules] +exclude=.*protoc_plugin/protoc_plugin_test\.proto$ diff --git a/src/python/grpcio/setup.py b/src/python/grpcio/setup.py index ec68eb6755..b8a98c3d85 100644 --- a/src/python/grpcio/setup.py +++ b/src/python/grpcio/setup.py @@ -43,27 +43,23 @@ os.chdir(os.path.dirname(os.path.abspath(__file__))) # Break import-style to ensure we can actually find our commands module. import commands -# Use environment variables to determine whether or not the Cython extension -# should *use* Cython or use the generated C files. Note that this requires the -# C files to have been generated by building first *with* Cython support. -_BUILD_WITH_CYTHON = os.environ.get('GRPC_PYTHON_BUILD_WITH_CYTHON', False) - -_C_EXTENSION_SOURCES = ( - 'grpc/_adapter/_c/module.c', - 'grpc/_adapter/_c/types.c', - 'grpc/_adapter/_c/utility.c', - 'grpc/_adapter/_c/types/call_credentials.c', - 'grpc/_adapter/_c/types/channel_credentials.c', - 'grpc/_adapter/_c/types/server_credentials.c', - 'grpc/_adapter/_c/types/completion_queue.c', - 'grpc/_adapter/_c/types/call.c', - 'grpc/_adapter/_c/types/channel.c', - 'grpc/_adapter/_c/types/server.c', -) +# Environment variable to determine whether or not the Cython extension should +# *use* Cython or use the generated C files. Note that this requires the C files +# to have been generated by building first *with* Cython support. +BUILD_WITH_CYTHON = os.environ.get('GRPC_PYTHON_BUILD_WITH_CYTHON', False) + +# Environment variable to determine whether or not to enable coverage analysis +# in Cython modules. +ENABLE_CYTHON_TRACING = os.environ.get( + 'GRPC_PYTHON_ENABLE_CYTHON_TRACING', False) + +# Environment variable to determine whether or not to include the test files in +# the installation. +INSTALL_TESTS = os.environ.get('GRPC_PYTHON_INSTALL_TESTS', False) -_CYTHON_EXTENSION_PACKAGE_NAMES = () +CYTHON_EXTENSION_PACKAGE_NAMES = () -_CYTHON_EXTENSION_MODULE_NAMES = ( +CYTHON_EXTENSION_MODULE_NAMES = ( 'grpc._cython.cygrpc', 'grpc._cython._cygrpc.call', 'grpc._cython._cygrpc.channel', @@ -73,24 +69,16 @@ _CYTHON_EXTENSION_MODULE_NAMES = ( 'grpc._cython._cygrpc.server', ) -_EXTENSION_INCLUDE_DIRECTORIES = ( +EXTENSION_INCLUDE_DIRECTORIES = ( '.', ) -_EXTENSION_LIBRARIES = ( +EXTENSION_LIBRARIES = ( 'grpc', 'gpr', ) if not "darwin" in sys.platform: - _EXTENSION_LIBRARIES += ('rt',) - - -_C_EXTENSION_MODULE = _core.Extension( - 'grpc._adapter._c', sources=list(_C_EXTENSION_SOURCES), - include_dirs=list(_EXTENSION_INCLUDE_DIRECTORIES), - libraries=list(_EXTENSION_LIBRARIES), -) -_EXTENSION_MODULES = [_C_EXTENSION_MODULE] + EXTENSION_LIBRARIES += ('rt',) def cython_extensions(package_names, module_names, include_dirs, libraries, @@ -101,48 +89,89 @@ def cython_extensions(package_names, module_names, include_dirs, libraries, extensions = [ _extension.Extension( name=module_name, sources=[module_file], - include_dirs=include_dirs, libraries=libraries + include_dirs=include_dirs, libraries=libraries, + define_macros=[('CYTHON_TRACE_NOGIL', 1)] if ENABLE_CYTHON_TRACING else [] ) for (module_name, module_file) in zip(module_names, module_files) ] if build_with_cython: import Cython.Build - return Cython.Build.cythonize(extensions) + return Cython.Build.cythonize( + extensions, + compiler_directives={'linetrace': bool(ENABLE_CYTHON_TRACING)}) else: return extensions -_CYTHON_EXTENSION_MODULES = cython_extensions( - list(_CYTHON_EXTENSION_PACKAGE_NAMES), list(_CYTHON_EXTENSION_MODULE_NAMES), - list(_EXTENSION_INCLUDE_DIRECTORIES), list(_EXTENSION_LIBRARIES), - bool(_BUILD_WITH_CYTHON)) +CYTHON_EXTENSION_MODULES = cython_extensions( + list(CYTHON_EXTENSION_PACKAGE_NAMES), list(CYTHON_EXTENSION_MODULE_NAMES), + list(EXTENSION_INCLUDE_DIRECTORIES), list(EXTENSION_LIBRARIES), + bool(BUILD_WITH_CYTHON)) -_PACKAGES = setuptools.find_packages('.') - -_PACKAGE_DIRECTORIES = { +PACKAGE_DIRECTORIES = { '': '.', } -_INSTALL_REQUIRES = ( +INSTALL_REQUIRES = ( 'enum34>=1.0.4', 'futures>=2.2.0', ) -_SETUP_REQUIRES = ( +SETUP_REQUIRES = ( 'sphinx>=1.3', -) + _INSTALL_REQUIRES +) + INSTALL_REQUIRES -_COMMAND_CLASS = { +COMMAND_CLASS = { 'doc': commands.SphinxDocumentation, + 'build_proto_modules': commands.BuildProtoModules, 'build_project_metadata': commands.BuildProjectMetadata, 'build_py': commands.BuildPy, + 'gather': commands.Gather, + 'run_interop': commands.RunInterop, +} + +TEST_PACKAGE_DATA = { + 'tests.interop': [ + 'credentials/ca.pem', + 'credentials/server1.key', + 'credentials/server1.pem', + ], + 'tests.protoc_plugin': [ + 'protoc_plugin_test.proto', + ], + 'tests.unit': [ + 'credentials/ca.pem', + 'credentials/server1.key', + 'credentials/server1.pem', + ], } +TESTS_REQUIRE = ( + 'oauth2client>=1.4.7', + 'protobuf==3.0.0a3', + 'coverage>=4.0', +) + INSTALL_REQUIRES + +TEST_SUITE = 'tests' +TEST_LOADER = 'tests:Loader' +TEST_RUNNER = 'tests:Runner' + +PACKAGE_DATA = {} +if INSTALL_TESTS: + PACKAGE_DATA = dict(PACKAGE_DATA, **TEST_PACKAGE_DATA) + PACKAGES = setuptools.find_packages('.') +else: + PACKAGES = setuptools.find_packages('.', exclude=['tests', 'tests.*']) + setuptools.setup( name='grpcio', - version='0.11.0b1', - ext_modules=_EXTENSION_MODULES + _CYTHON_EXTENSION_MODULES, - packages=list(_PACKAGES), - package_dir=_PACKAGE_DIRECTORIES, - install_requires=_INSTALL_REQUIRES, - setup_requires=_SETUP_REQUIRES, - cmdclass=_COMMAND_CLASS + version='0.12.0b0', + ext_modules=CYTHON_EXTENSION_MODULES, + packages=list(PACKAGES), + package_dir=PACKAGE_DIRECTORIES, + install_requires=INSTALL_REQUIRES, + setup_requires=SETUP_REQUIRES, + cmdclass=COMMAND_CLASS, + tests_require=TESTS_REQUIRE, + test_suite=TEST_SUITE, + test_loader=TEST_LOADER, + test_runner=TEST_RUNNER, ) diff --git a/src/python/grpcio/tests/__init__.py b/src/python/grpcio/tests/__init__.py new file mode 100644 index 0000000000..b76b3985a1 --- /dev/null +++ b/src/python/grpcio/tests/__init__.py @@ -0,0 +1,34 @@ +# 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. + +from tests import _loader +from tests import _runner + +Loader = _loader.Loader +Runner = _runner.Runner diff --git a/src/python/grpcio/tests/_loader.py b/src/python/grpcio/tests/_loader.py new file mode 100644 index 0000000000..6992029b5e --- /dev/null +++ b/src/python/grpcio/tests/_loader.py @@ -0,0 +1,119 @@ +# 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. + +import importlib +import pkgutil +import re +import unittest + +import coverage + +TEST_MODULE_REGEX = r'^.*_test$' + + +class Loader(object): + """Test loader for setuptools test suite support. + + Attributes: + suite (unittest.TestSuite): All tests collected by the loader. + loader (unittest.TestLoader): Standard Python unittest loader to be ran per + module discovered. + module_matcher (re.RegexObject): A regular expression object to match + against module names and determine whether or not the discovered module + contributes to the test suite. + """ + + def __init__(self): + self.suite = unittest.TestSuite() + self.loader = unittest.TestLoader() + self.module_matcher = re.compile(TEST_MODULE_REGEX) + + def loadTestsFromNames(self, names, module=None): + """Function mirroring TestLoader::loadTestsFromNames, as expected by + setuptools.setup argument `test_loader`.""" + # ensure that we capture decorators and definitions (else our coverage + # measure unnecessarily suffers) + coverage_context = coverage.Coverage(data_suffix=True) + coverage_context.start() + modules = [importlib.import_module(name) for name in names] + for module in modules: + self.visit_module(module) + for module in modules: + try: + package_paths = module.__path__ + except: + continue + self.walk_packages(package_paths) + coverage_context.stop() + coverage_context.save() + return self.suite + + def walk_packages(self, package_paths): + """Walks over the packages, dispatching `visit_module` calls. + + Args: + package_paths (list): A list of paths over which to walk through modules + along. + """ + for importer, module_name, is_package in ( + pkgutil.iter_modules(package_paths)): + module = importer.find_module(module_name).load_module(module_name) + self.visit_module(module) + if is_package: + self.walk_packages(module.__path__) + + def visit_module(self, module): + """Visits the module, adding discovered tests to the test suite. + + Args: + module (module): Module to match against self.module_matcher; if matched + it has its tests loaded via self.loader into self.suite. + """ + if self.module_matcher.match(module.__name__): + module_suite = self.loader.loadTestsFromModule(module) + self.suite.addTest(module_suite) + + +def iterate_suite_cases(suite): + """Generator over all unittest.TestCases in a unittest.TestSuite. + + Args: + suite (unittest.TestSuite): Suite to iterate over in the generator. + + Returns: + generator: A generator over all unittest.TestCases in `suite`. + """ + for item in suite: + if isinstance(item, unittest.TestSuite): + for child_item in iterate_suite_cases(item): + yield child_item + elif isinstance(item, unittest.TestCase): + yield item + else: + raise ValueError('unexpected suite item of type {}'.format(type(item))) diff --git a/src/python/grpcio/tests/_result.py b/src/python/grpcio/tests/_result.py new file mode 100644 index 0000000000..5a570f4279 --- /dev/null +++ b/src/python/grpcio/tests/_result.py @@ -0,0 +1,451 @@ +# 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. + +import cStringIO as StringIO +import collections +import itertools +import traceback +import unittest +from xml.etree import ElementTree + +import coverage + +from tests import _loader + + +class CaseResult(collections.namedtuple('CaseResult', [ + 'id', 'name', 'kind', 'stdout', 'stderr', 'skip_reason', 'traceback'])): + """A serializable result of a single test case. + + Attributes: + id (object): Any serializable object used to denote the identity of this + test case. + name (str or None): A human-readable name of the test case. + kind (CaseResult.Kind): The kind of test result. + stdout (object or None): Output on stdout, or None if nothing was captured. + stderr (object or None): Output on stderr, or None if nothing was captured. + skip_reason (object or None): The reason the test was skipped. Must be + something if self.kind is CaseResult.Kind.SKIP, else None. + traceback (object or None): The traceback of the test. Must be something if + self.kind is CaseResult.Kind.{ERROR, FAILURE, EXPECTED_FAILURE}, else + None. + """ + + class Kind: + UNTESTED = 'untested' + RUNNING = 'running' + ERROR = 'error' + FAILURE = 'failure' + SUCCESS = 'success' + SKIP = 'skip' + EXPECTED_FAILURE = 'expected failure' + UNEXPECTED_SUCCESS = 'unexpected success' + + def __new__(cls, id=None, name=None, kind=None, stdout=None, stderr=None, + skip_reason=None, traceback=None): + """Helper keyword constructor for the namedtuple. + + See this class' attributes for information on the arguments.""" + assert id is not None + assert name is None or isinstance(name, str) + if kind is CaseResult.Kind.UNTESTED: + pass + elif kind is CaseResult.Kind.RUNNING: + pass + elif kind is CaseResult.Kind.ERROR: + assert traceback is not None + elif kind is CaseResult.Kind.FAILURE: + assert traceback is not None + elif kind is CaseResult.Kind.SUCCESS: + pass + elif kind is CaseResult.Kind.SKIP: + assert skip_reason is not None + elif kind is CaseResult.Kind.EXPECTED_FAILURE: + assert traceback is not None + elif kind is CaseResult.Kind.UNEXPECTED_SUCCESS: + pass + else: + assert False + return super(cls, CaseResult).__new__( + cls, id, name, kind, stdout, stderr, skip_reason, traceback) + + def updated(self, name=None, kind=None, stdout=None, stderr=None, + skip_reason=None, traceback=None): + """Get a new validated CaseResult with the fields updated. + + See this class' attributes for information on the arguments.""" + name = self.name if name is None else name + kind = self.kind if kind is None else kind + stdout = self.stdout if stdout is None else stdout + stderr = self.stderr if stderr is None else stderr + skip_reason = self.skip_reason if skip_reason is None else skip_reason + traceback = self.traceback if traceback is None else traceback + return CaseResult(id=self.id, name=name, kind=kind, stdout=stdout, + stderr=stderr, skip_reason=skip_reason, + traceback=traceback) + + +class AugmentedResult(unittest.TestResult): + """unittest.Result that keeps track of additional information. + + Uses CaseResult objects to store test-case results, providing additional + information beyond that of the standard Python unittest library, such as + standard output. + + Attributes: + id_map (callable): A unary callable mapping unittest.TestCase objects to + unique identifiers. + cases (dict): A dictionary mapping from the identifiers returned by id_map + to CaseResult objects corresponding to those IDs. + """ + + def __init__(self, id_map): + """Initialize the object with an identifier mapping. + + Arguments: + id_map (callable): Corresponds to the attribute `id_map`.""" + super(AugmentedResult, self).__init__() + self.id_map = id_map + self.cases = None + + def startTestRun(self): + """See unittest.TestResult.startTestRun.""" + super(AugmentedResult, self).startTestRun() + self.cases = dict() + + def stopTestRun(self): + """See unittest.TestResult.stopTestRun.""" + super(AugmentedResult, self).stopTestRun() + + def startTest(self, test): + """See unittest.TestResult.startTest.""" + super(AugmentedResult, self).startTest(test) + case_id = self.id_map(test) + self.cases[case_id] = CaseResult( + id=case_id, name=test.id(), kind=CaseResult.Kind.RUNNING) + + def addError(self, test, error): + """See unittest.TestResult.addError.""" + super(AugmentedResult, self).addError(test, error) + case_id = self.id_map(test) + self.cases[case_id] = self.cases[case_id].updated( + kind=CaseResult.Kind.ERROR, traceback=error) + + def addFailure(self, test, error): + """See unittest.TestResult.addFailure.""" + super(AugmentedResult, self).addFailure(test, error) + case_id = self.id_map(test) + self.cases[case_id] = self.cases[case_id].updated( + kind=CaseResult.Kind.FAILURE, traceback=error) + + def addSuccess(self, test): + """See unittest.TestResult.addSuccess.""" + super(AugmentedResult, self).addSuccess(test) + case_id = self.id_map(test) + self.cases[case_id] = self.cases[case_id].updated( + kind=CaseResult.Kind.SUCCESS) + + def addSkip(self, test, reason): + """See unittest.TestResult.addSkip.""" + super(AugmentedResult, self).addSkip(test, reason) + case_id = self.id_map(test) + self.cases[case_id] = self.cases[case_id].updated( + kind=CaseResult.Kind.SKIP, skip_reason=reason) + + def addExpectedFailure(self, test, error): + """See unittest.TestResult.addExpectedFailure.""" + super(AugmentedResult, self).addExpectedFailure(test, error) + case_id = self.id_map(test) + self.cases[case_id] = self.cases[case_id].updated( + kind=CaseResult.Kind.EXPECTED_FAILURE, traceback=error) + + def addUnexpectedSuccess(self, test): + """See unittest.TestResult.addUnexpectedSuccess.""" + super(AugmentedResult, self).addUnexpectedSuccess(test) + case_id = self.id_map(test) + self.cases[case_id] = self.cases[case_id].updated( + kind=CaseResult.Kind.UNEXPECTED_SUCCESS) + + def set_output(self, test, stdout, stderr): + """Set the output attributes for the CaseResult corresponding to a test. + + Args: + test (unittest.TestCase): The TestCase to set the outputs of. + stdout (str): Output from stdout to assign to self.id_map(test). + stderr (str): Output from stderr to assign to self.id_map(test). + """ + case_id = self.id_map(test) + self.cases[case_id] = self.cases[case_id].updated( + stdout=stdout, stderr=stderr) + + def augmented_results(self, filter): + """Convenience method to retrieve filtered case results. + + Args: + filter (callable): A unary predicate to filter over CaseResult objects. + """ + return (self.cases[case_id] for case_id in self.cases + if filter(self.cases[case_id])) + + +class CoverageResult(AugmentedResult): + """Extension to AugmentedResult adding coverage.py support per test.\ + + Attributes: + coverage_context (coverage.Coverage): coverage.py management object. + """ + + def __init__(self, id_map): + """See AugmentedResult.__init__.""" + super(CoverageResult, self).__init__(id_map=id_map) + self.coverage_context = None + + def startTest(self, test): + """See unittest.TestResult.startTest. + + Additionally initializes and begins code coverage tracking.""" + super(CoverageResult, self).startTest(test) + self.coverage_context = coverage.Coverage(data_suffix=True) + self.coverage_context.start() + + def stopTest(self, test): + """See unittest.TestResult.stopTest. + + Additionally stops and deinitializes code coverage tracking.""" + super(CoverageResult, self).stopTest(test) + self.coverage_context.stop() + self.coverage_context.save() + self.coverage_context = None + + def stopTestRun(self): + """See unittest.TestResult.stopTestRun.""" + super(CoverageResult, self).stopTestRun() + # TODO(atash): Dig deeper into why the following line fails to properly + # combine coverage data from the Cython plugin. + #coverage.Coverage().combine() + + +class _Colors: + """Namespaced constants for terminal color magic numbers.""" + HEADER = '\033[95m' + INFO = '\033[94m' + OK = '\033[92m' + WARN = '\033[93m' + FAIL = '\033[91m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + END = '\033[0m' + + +class TerminalResult(CoverageResult): + """Extension to CoverageResult adding basic terminal reporting.""" + + def __init__(self, out, id_map): + """Initialize the result object. + + Args: + out (file-like): Output file to which terminal-colored live results will + be written. + id_map (callable): See AugmentedResult.__init__. + """ + super(TerminalResult, self).__init__(id_map=id_map) + self.out = out + + def startTestRun(self): + """See unittest.TestResult.startTestRun.""" + super(TerminalResult, self).startTestRun() + self.out.write( + _Colors.HEADER + + 'Testing gRPC Python...\n' + + _Colors.END) + + def stopTestRun(self): + """See unittest.TestResult.stopTestRun.""" + super(TerminalResult, self).stopTestRun() + self.out.write(summary(self)) + self.out.flush() + + def addError(self, test, error): + """See unittest.TestResult.addError.""" + super(TerminalResult, self).addError(test, error) + self.out.write( + _Colors.FAIL + + 'ERROR {}\n'.format(test.id()) + + _Colors.END) + self.out.flush() + + def addFailure(self, test, error): + """See unittest.TestResult.addFailure.""" + super(TerminalResult, self).addFailure(test, error) + self.out.write( + _Colors.FAIL + + 'FAILURE {}\n'.format(test.id()) + + _Colors.END) + self.out.flush() + + def addSuccess(self, test): + """See unittest.TestResult.addSuccess.""" + super(TerminalResult, self).addSuccess(test) + self.out.write( + _Colors.OK + + 'SUCCESS {}\n'.format(test.id()) + + _Colors.END) + self.out.flush() + + def addSkip(self, test, reason): + """See unittest.TestResult.addSkip.""" + super(TerminalResult, self).addSkip(test, reason) + self.out.write( + _Colors.INFO + + 'SKIP {}\n'.format(test.id()) + + _Colors.END) + self.out.flush() + + def addExpectedFailure(self, test, error): + """See unittest.TestResult.addExpectedFailure.""" + super(TerminalResult, self).addExpectedFailure(test, error) + self.out.write( + _Colors.INFO + + 'FAILURE_OK {}\n'.format(test.id()) + + _Colors.END) + self.out.flush() + + def addUnexpectedSuccess(self, test): + """See unittest.TestResult.addUnexpectedSuccess.""" + super(TerminalResult, self).addUnexpectedSuccess(test) + self.out.write( + _Colors.INFO + + 'UNEXPECTED_OK {}\n'.format(test.id()) + + _Colors.END) + self.out.flush() + +def _traceback_string(type, value, trace): + """Generate a descriptive string of a Python exception traceback. + + Args: + type (class): The type of the exception. + value (Exception): The value of the exception. + trace (traceback): Traceback of the exception. + + Returns: + str: Formatted exception descriptive string. + """ + buffer = StringIO.StringIO() + traceback.print_exception(type, value, trace, file=buffer) + return buffer.getvalue() + +def summary(result): + """A summary string of a result object. + + Args: + result (AugmentedResult): The result object to get the summary of. + + Returns: + str: The summary string. + """ + assert isinstance(result, AugmentedResult) + untested = list(result.augmented_results( + lambda case_result: case_result.kind is CaseResult.Kind.UNTESTED)) + running = list(result.augmented_results( + lambda case_result: case_result.kind is CaseResult.Kind.RUNNING)) + failures = list(result.augmented_results( + lambda case_result: case_result.kind is CaseResult.Kind.FAILURE)) + errors = list(result.augmented_results( + lambda case_result: case_result.kind is CaseResult.Kind.ERROR)) + successes = list(result.augmented_results( + lambda case_result: case_result.kind is CaseResult.Kind.SUCCESS)) + skips = list(result.augmented_results( + lambda case_result: case_result.kind is CaseResult.Kind.SKIP)) + expected_failures = list(result.augmented_results( + lambda case_result: case_result.kind is CaseResult.Kind.EXPECTED_FAILURE)) + unexpected_successes = list(result.augmented_results( + lambda case_result: case_result.kind is CaseResult.Kind.UNEXPECTED_SUCCESS)) + running_names = [case.name for case in running] + finished_count = (len(failures) + len(errors) + len(successes) + + len(expected_failures) + len(unexpected_successes)) + statistics = ( + '{finished} tests finished:\n' + '\t{successful} successful\n' + '\t{unsuccessful} unsuccessful\n' + '\t{skipped} skipped\n' + '\t{expected_fail} expected failures\n' + '\t{unexpected_successful} unexpected successes\n' + 'Interrupted Tests:\n' + '\t{interrupted}\n' + .format(finished=finished_count, + successful=len(successes), + unsuccessful=(len(failures)+len(errors)), + skipped=len(skips), + expected_fail=len(expected_failures), + unexpected_successful=len(unexpected_successes), + interrupted=str(running_names))) + tracebacks = '\n\n'.join([ + (_Colors.FAIL + '{test_name}' + _Colors.END + + '\n' + + _Colors.BOLD + 'traceback:' + _Colors.END + '\n' + + '{traceback}\n' + + _Colors.BOLD + 'stdout:' + _Colors.END + '\n' + + '{stdout}\n' + + _Colors.BOLD + 'stderr:' + _Colors.END + '\n' + + '{stderr}\n').format( + test_name=result.name, + traceback=_traceback_string(*result.traceback), + stdout=result.stdout, stderr=result.stderr) + for result in itertools.chain(failures, errors) + ]) + notes = 'Unexpected successes: {}\n'.format([ + result.name for result in unexpected_successes]) + return statistics + '\nErrors/Failures: \n' + tracebacks + '\n' + notes + + +def jenkins_junit_xml(result): + """An XML tree object that when written is recognizable by Jenkins. + + Args: + result (AugmentedResult): The result object to get the junit xml output of. + + Returns: + ElementTree.ElementTree: The XML tree. + """ + assert isinstance(result, AugmentedResult) + root = ElementTree.Element('testsuites') + suite = ElementTree.SubElement(root, 'testsuite', { + 'name': 'Python gRPC tests', + }) + for case in result.cases.values(): + if case.kind is CaseResult.Kind.SUCCESS: + ElementTree.SubElement(suite, 'testcase', { + 'name': case.name, + }) + elif case.kind in (CaseResult.Kind.ERROR, CaseResult.Kind.FAILURE): + case_xml = ElementTree.SubElement(suite, 'testcase', { + 'name': case.name, + }) + error_xml = ElementTree.SubElement(case_xml, 'error', {}) + error_xml.text = ''.format(case.stderr, case.traceback) + return ElementTree.ElementTree(element=root) diff --git a/src/python/grpcio/tests/_runner.py b/src/python/grpcio/tests/_runner.py new file mode 100644 index 0000000000..4f1ddb57fc --- /dev/null +++ b/src/python/grpcio/tests/_runner.py @@ -0,0 +1,224 @@ +# 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. + +import cStringIO as StringIO +import collections +import fcntl +import multiprocessing +import os +import select +import signal +import sys +import threading +import time +import unittest +import uuid + +from tests import _loader +from tests import _result + + +class CapturePipe(object): + """A context-manager pipe to redirect output to a byte array. + + Attributes: + _redirect_fd (int): File descriptor of file to redirect writes from. + _saved_fd (int): A copy of the original value of the redirected file + descriptor. + _read_thread (threading.Thread or None): Thread upon which reads through the + pipe are performed. Only non-None when self is started. + _read_fd (int or None): File descriptor of the read end of the redirect + pipe. Only non-None when self is started. + _write_fd (int or None): File descriptor of the write end of the redirect + pipe. Only non-None when self is started. + output (bytearray or None): Redirected output from writes to the redirected + file descriptor. Only valid during and after self has started. + """ + + def __init__(self, fd): + self._redirect_fd = fd + self._saved_fd = os.dup(self._redirect_fd) + self._read_thread = None + self._read_fd = None + self._write_fd = None + self.output = None + + def start(self): + """Start redirection of writes to the file descriptor.""" + self._read_fd, self._write_fd = os.pipe() + os.dup2(self._write_fd, self._redirect_fd) + flags = fcntl.fcntl(self._read_fd, fcntl.F_GETFL) + fcntl.fcntl(self._read_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK) + self._read_thread = threading.Thread(target=self._read) + self._read_thread.start() + + def stop(self): + """Stop redirection of writes to the file descriptor.""" + os.close(self._write_fd) + os.dup2(self._saved_fd, self._redirect_fd) # auto-close self._redirect_fd + self._read_thread.join() + self._read_thread = None + # we waited for the read thread to finish, so _read_fd has been read and we + # can close it. + os.close(self._read_fd) + + def _read(self): + """Read-thread target for self.""" + self.output = bytearray() + while True: + select.select([self._read_fd], [], []) + read_bytes = os.read(self._read_fd, 1024) + if read_bytes: + self.output.extend(read_bytes) + else: + break + + def write_bypass(self, value): + """Bypass the redirection and write directly to the original file. + + Arguments: + value (str): What to write to the original file. + """ + if self._saved_fd is None: + os.write(self._redirect_fd, value) + else: + os.write(self._saved_fd, value) + + def __enter__(self): + self.start() + return self + + def __exit__(self, type, value, traceback): + self.stop() + + def close(self): + """Close any resources used by self not closed by stop().""" + os.close(self._saved_fd) + + +class AugmentedCase(collections.namedtuple('AugmentedCase', [ + 'case', 'id'])): + """A test case with a guaranteed unique externally specified identifier. + + Attributes: + case (unittest.TestCase): TestCase we're decorating with an additional + identifier. + id (object): Any identifier that may be considered 'unique' for testing + purposes. + """ + + def __new__(cls, case, id=None): + if id is None: + id = uuid.uuid4() + return super(cls, AugmentedCase).__new__(cls, case, id) + + +class Runner(object): + + def run(self, suite): + """See setuptools' test_runner setup argument for information.""" + # Ensure that every test case has no collision with any other test case in + # the augmented results. + augmented_cases = [AugmentedCase(case, uuid.uuid4()) + for case in _loader.iterate_suite_cases(suite)] + case_id_by_case = dict((augmented_case.case, augmented_case.id) + for augmented_case in augmented_cases) + result_out = StringIO.StringIO() + result = _result.TerminalResult( + result_out, id_map=lambda case: case_id_by_case[case]) + stdout_pipe = CapturePipe(sys.stdout.fileno()) + stderr_pipe = CapturePipe(sys.stderr.fileno()) + kill_flag = [False] + + def sigint_handler(signal_number, frame): + if signal_number == signal.SIGINT: + kill_flag[0] = True # Python 2.7 not having 'local'... :-( + signal.signal(signal_number, signal.SIG_DFL) + + def fault_handler(signal_number, frame): + stdout_pipe.write_bypass( + 'Received fault signal {}\nstdout:\n{}\n\nstderr:{}\n' + .format(signal_number, stdout_pipe.output, stderr_pipe.output)) + os._exit(1) + + def check_kill_self(): + if kill_flag[0]: + stdout_pipe.write_bypass('Stopping tests short...') + result.stopTestRun() + stdout_pipe.write_bypass(result_out.getvalue()) + stdout_pipe.write_bypass( + '\ninterrupted stdout:\n{}\n'.format(stdout_pipe.output)) + stderr_pipe.write_bypass( + '\ninterrupted stderr:\n{}\n'.format(stderr_pipe.output)) + os._exit(1) + signal.signal(signal.SIGINT, sigint_handler) + signal.signal(signal.SIGSEGV, fault_handler) + signal.signal(signal.SIGBUS, fault_handler) + signal.signal(signal.SIGABRT, fault_handler) + signal.signal(signal.SIGFPE, fault_handler) + signal.signal(signal.SIGILL, fault_handler) + # Sometimes output will lag after a test has successfully finished; we + # ignore such writes to our pipes. + signal.signal(signal.SIGPIPE, signal.SIG_IGN) + + # Run the tests + result.startTestRun() + for augmented_case in augmented_cases: + sys.stdout.write('Running {}\n'.format(augmented_case.case.id())) + sys.stdout.flush() + case_thread = threading.Thread( + target=augmented_case.case.run, args=(result,)) + try: + with stdout_pipe, stderr_pipe: + case_thread.start() + while case_thread.is_alive(): + check_kill_self() + time.sleep(0) + case_thread.join() + except: + # re-raise the exception after forcing the with-block to end + raise + result.set_output( + augmented_case.case, stdout_pipe.output, stderr_pipe.output) + sys.stdout.write(result_out.getvalue()) + sys.stdout.flush() + result_out.truncate(0) + check_kill_self() + result.stopTestRun() + stdout_pipe.close() + stderr_pipe.close() + + # Report results + sys.stdout.write(result_out.getvalue()) + sys.stdout.flush() + signal.signal(signal.SIGINT, signal.SIG_DFL) + with open('report.xml', 'w') as report_xml_file: + _result.jenkins_junit_xml(result).write(report_xml_file) + return result + diff --git a/src/python/grpcio_test/grpc_interop/__init__.py b/src/python/grpcio/tests/interop/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_interop/__init__.py +++ b/src/python/grpcio/tests/interop/__init__.py diff --git a/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py b/src/python/grpcio/tests/interop/_insecure_interop_test.py index 5007be28ff..00b49aba37 100644 --- a/src/python/grpcio_test/grpc_interop/_insecure_interop_test.py +++ b/src/python/grpcio/tests/interop/_insecure_interop_test.py @@ -33,10 +33,10 @@ import unittest from grpc.beta import implementations -from grpc_interop import _interop_test_case -from grpc_interop import methods -from grpc_interop import server -from grpc_interop import test_pb2 +from tests.interop import _interop_test_case +from tests.interop import methods +from tests.interop import server +from tests.interop import test_pb2 class InsecureInteropTest( diff --git a/src/python/grpcio_test/grpc_interop/_interop_test_case.py b/src/python/grpcio/tests/interop/_interop_test_case.py index b6d06b300d..ccea17a66d 100644 --- a/src/python/grpcio_test/grpc_interop/_interop_test_case.py +++ b/src/python/grpcio/tests/interop/_interop_test_case.py @@ -29,7 +29,7 @@ """Common code for unit tests of the interoperability test code.""" -from grpc_interop import methods +from tests.interop import methods class InteropTestCase(object): diff --git a/src/python/grpcio_test/grpc_interop/_secure_interop_test.py b/src/python/grpcio/tests/interop/_secure_interop_test.py index 108e15b0f9..7e3061133f 100644 --- a/src/python/grpcio_test/grpc_interop/_secure_interop_test.py +++ b/src/python/grpcio/tests/interop/_secure_interop_test.py @@ -33,12 +33,12 @@ import unittest from grpc.beta import implementations -from grpc_test.beta import test_utilities +from tests.interop import _interop_test_case +from tests.interop import methods +from tests.interop import resources +from tests.interop import test_pb2 -from grpc_interop import _interop_test_case -from grpc_interop import methods -from grpc_interop import resources -from grpc_interop import test_pb2 +from tests.unit.beta import test_utilities _SERVER_HOST_OVERRIDE = 'foo.test.google.fr' @@ -55,7 +55,7 @@ class SecureInteropTest( self.server.start() self.stub = test_pb2.beta_create_TestService_stub( test_utilities.not_really_secure_channel( - '[::]', port, implementations.ssl_client_credentials( + '[::]', port, implementations.ssl_channel_credentials( resources.test_root_certificates(), None, None), _SERVER_HOST_OVERRIDE)) diff --git a/src/python/grpcio_test/grpc_interop/client.py b/src/python/grpcio/tests/interop/client.py index b8d5047ca5..573ec2bd71 100644 --- a/src/python/grpcio_test/grpc_interop/client.py +++ b/src/python/grpcio/tests/interop/client.py @@ -34,11 +34,10 @@ from oauth2client import client as oauth2client_client from grpc.beta import implementations -from grpc_test.beta import test_utilities - -from grpc_interop import methods -from grpc_interop import resources -from grpc_interop import test_pb2 +from tests.interop import methods +from tests.interop import resources +from tests.interop import test_pb2 +from tests.unit.beta import test_utilities _ONE_DAY_IN_SECONDS = 60 * 60 * 24 @@ -91,11 +90,11 @@ def _stub(args): if args.use_test_ca: root_certificates = resources.test_root_certificates() else: - root_certificates = resources.prod_root_certificates() + root_certificates = None # will load default roots. channel = test_utilities.not_really_secure_channel( args.server_host, args.server_port, - implementations.ssl_client_credentials(root_certificates, None, None), + implementations.ssl_channel_credentials(root_certificates, None, None), args.server_host_override) stub = test_pb2.beta_create_TestService_stub( channel, metadata_transformer=metadata_transformer) @@ -114,7 +113,7 @@ def _test_case_from_arg(test_case_arg): raise ValueError('No test case "%s"!' % test_case_arg) -def _test_interoperability(): +def test_interoperability(): args = _args() stub = _stub(args) test_case = _test_case_from_arg(args.test_case) @@ -122,4 +121,4 @@ def _test_interoperability(): if __name__ == '__main__': - _test_interoperability() + test_interoperability() diff --git a/src/python/grpcio_test/grpc_interop/credentials/README b/src/python/grpcio/tests/interop/credentials/README index cb20dcb49f..cb20dcb49f 100644 --- a/src/python/grpcio_test/grpc_interop/credentials/README +++ b/src/python/grpcio/tests/interop/credentials/README diff --git a/src/python/grpcio_test/grpc_interop/credentials/ca.pem b/src/python/grpcio/tests/interop/credentials/ca.pem index 6c8511a73c..6c8511a73c 100755 --- a/src/python/grpcio_test/grpc_interop/credentials/ca.pem +++ b/src/python/grpcio/tests/interop/credentials/ca.pem diff --git a/src/python/grpcio_test/grpc_interop/credentials/server1.key b/src/python/grpcio/tests/interop/credentials/server1.key index 143a5b8765..143a5b8765 100755 --- a/src/python/grpcio_test/grpc_interop/credentials/server1.key +++ b/src/python/grpcio/tests/interop/credentials/server1.key diff --git a/src/python/grpcio_test/grpc_interop/credentials/server1.pem b/src/python/grpcio/tests/interop/credentials/server1.pem index f3d43fcc5b..f3d43fcc5b 100755 --- a/src/python/grpcio_test/grpc_interop/credentials/server1.pem +++ b/src/python/grpcio/tests/interop/credentials/server1.pem diff --git a/src/python/grpcio_test/grpc_interop/empty.proto b/src/python/grpcio/tests/interop/empty.proto index 6d0eb937d6..6d0eb937d6 100644 --- a/src/python/grpcio_test/grpc_interop/empty.proto +++ b/src/python/grpcio/tests/interop/empty.proto diff --git a/src/python/grpcio_test/grpc_interop/messages.proto b/src/python/grpcio/tests/interop/messages.proto index 193b6c4171..193b6c4171 100644 --- a/src/python/grpcio_test/grpc_interop/messages.proto +++ b/src/python/grpcio/tests/interop/messages.proto diff --git a/src/python/grpcio_test/grpc_interop/methods.py b/src/python/grpcio/tests/interop/methods.py index 3ef8545355..b3591aef7b 100644 --- a/src/python/grpcio_test/grpc_interop/methods.py +++ b/src/python/grpcio/tests/interop/methods.py @@ -40,9 +40,9 @@ from oauth2client import client as oauth2client_client from grpc.framework.common import cardinality from grpc.framework.interfaces.face import face -from grpc_interop import empty_pb2 -from grpc_interop import messages_pb2 -from grpc_interop import test_pb2 +from tests.interop import empty_pb2 +from tests.interop import messages_pb2 +from tests.interop import test_pb2 _TIMEOUT = 7 diff --git a/src/python/grpcio_test/grpc_interop/resources.py b/src/python/grpcio/tests/interop/resources.py index 1122499418..c424385cf6 100644 --- a/src/python/grpcio_test/grpc_interop/resources.py +++ b/src/python/grpcio/tests/interop/resources.py @@ -44,10 +44,6 @@ def test_root_certificates(): __name__, _ROOT_CERTIFICATES_RESOURCE_PATH) -def prod_root_certificates(): - return open(os.environ['SSL_CERT_FILE'], mode='rb').read() - - def private_key(): return pkg_resources.resource_string(__name__, _PRIVATE_KEY_RESOURCE_PATH) diff --git a/src/python/grpcio_test/grpc_interop/server.py b/src/python/grpcio/tests/interop/server.py index b541087663..6dd55f008c 100644 --- a/src/python/grpcio_test/grpc_interop/server.py +++ b/src/python/grpcio/tests/interop/server.py @@ -35,9 +35,9 @@ import time from grpc.beta import implementations -from grpc_interop import methods -from grpc_interop import resources -from grpc_interop import test_pb2 +from tests.interop import methods +from tests.interop import resources +from tests.interop import test_pb2 _ONE_DAY_IN_SECONDS = 60 * 60 * 24 @@ -68,7 +68,7 @@ def serve(): time.sleep(_ONE_DAY_IN_SECONDS) except BaseException as e: logging.info('Caught exception "%s"; stopping server...', e) - server.stop() + server.stop(0) logging.info('Server stopped; exiting.') if __name__ == '__main__': diff --git a/src/python/grpcio_test/grpc_interop/test.proto b/src/python/grpcio/tests/interop/test.proto index b499813e56..9feecc0278 100644 --- a/src/python/grpcio_test/grpc_interop/test.proto +++ b/src/python/grpcio/tests/interop/test.proto @@ -33,8 +33,8 @@ syntax = "proto3"; -import "grpc_interop/empty.proto"; -import "grpc_interop/messages.proto"; +import "tests/interop/empty.proto"; +import "tests/interop/messages.proto"; package grpc.testing; diff --git a/src/python/grpcio_test/grpc_protoc_plugin/__init__.py b/src/python/grpcio/tests/protoc_plugin/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_protoc_plugin/__init__.py +++ b/src/python/grpcio/tests/protoc_plugin/__init__.py diff --git a/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py b/src/python/grpcio/tests/protoc_plugin/beta_python_plugin_test.py index 259b978de2..ba5b219a88 100644 --- a/src/python/grpcio_test/grpc_protoc_plugin/beta_python_plugin_test.py +++ b/src/python/grpcio/tests/protoc_plugin/beta_python_plugin_test.py @@ -45,7 +45,7 @@ import unittest from grpc.beta import implementations from grpc.framework.foundation import future from grpc.framework.interfaces.face import face -from grpc_test.framework.common import test_constants +from tests.unit.framework.common import test_constants # Identifiers of entities we expect to find in the generated module. SERVICER_IDENTIFIER = 'BetaTestServiceServicer' @@ -218,7 +218,7 @@ class PythonPluginTest(unittest.TestCase): protoc_plugin_filename = distutils.spawn.find_executable( 'grpc_python_plugin') test_proto_filename = pkg_resources.resource_filename( - 'grpc_protoc_plugin', 'test.proto') + 'tests.protoc_plugin', 'protoc_plugin_test.proto') if not os.path.isfile(protoc_command): # Assume that if we haven't built protoc that it's on the system. protoc_command = 'protoc' @@ -237,7 +237,7 @@ class PythonPluginTest(unittest.TestCase): ] subprocess.check_call(' '.join(cmd), shell=True, env=os.environ, cwd=os.path.dirname(test_proto_filename)) - sys.path.append(self.outdir) + sys.path.insert(0, self.outdir) def tearDown(self): try: @@ -245,22 +245,26 @@ class PythonPluginTest(unittest.TestCase): except OSError as exc: if exc.errno != errno.ENOENT: raise + sys.path.remove(self.outdir) def testImportAttributes(self): # check that we can access the generated module and its members. - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) self.assertIsNotNone(getattr(test_pb2, SERVICER_IDENTIFIER, None)) self.assertIsNotNone(getattr(test_pb2, STUB_IDENTIFIER, None)) self.assertIsNotNone(getattr(test_pb2, SERVER_FACTORY_IDENTIFIER, None)) self.assertIsNotNone(getattr(test_pb2, STUB_FACTORY_IDENTIFIER, None)) def testUpDown(self): - import test_pb2 + import protoc_plugin_test_pb2 as test_pb2 + reload(test_pb2) with _CreateService(test_pb2) as (servicer, stub): request = test_pb2.SimpleRequest(response_size=13) def testUnaryCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): request = test_pb2.SimpleRequest(response_size=13) response = stub.UnaryCall(request, test_constants.LONG_TIMEOUT) @@ -268,7 +272,8 @@ class PythonPluginTest(unittest.TestCase): self.assertEqual(expected_response, response) def testUnaryCallFuture(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) request = test_pb2.SimpleRequest(response_size=13) with _CreateService(test_pb2) as (methods, stub): # Check that the call does not block waiting for the server to respond. @@ -280,7 +285,8 @@ class PythonPluginTest(unittest.TestCase): self.assertEqual(expected_response, response) def testUnaryCallFutureExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): request = test_pb2.SimpleRequest(response_size=13) with methods.pause(): @@ -290,7 +296,8 @@ class PythonPluginTest(unittest.TestCase): response_future.result() def testUnaryCallFutureCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) request = test_pb2.SimpleRequest(response_size=13) with _CreateService(test_pb2) as (methods, stub): with methods.pause(): @@ -299,7 +306,8 @@ class PythonPluginTest(unittest.TestCase): self.assertTrue(response_future.cancelled()) def testUnaryCallFutureFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) request = test_pb2.SimpleRequest(response_size=13) with _CreateService(test_pb2) as (methods, stub): with methods.fail(): @@ -308,7 +316,8 @@ class PythonPluginTest(unittest.TestCase): self.assertIsNotNone(response_future.exception()) def testStreamingOutputCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) request = _streaming_output_request(test_pb2) with _CreateService(test_pb2) as (methods, stub): responses = stub.StreamingOutputCall( @@ -320,7 +329,8 @@ class PythonPluginTest(unittest.TestCase): self.assertEqual(expected_response, response) def testStreamingOutputCallExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) request = _streaming_output_request(test_pb2) with _CreateService(test_pb2) as (methods, stub): with methods.pause(): @@ -330,7 +340,8 @@ class PythonPluginTest(unittest.TestCase): list(responses) def testStreamingOutputCallCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) request = _streaming_output_request(test_pb2) with _CreateService(test_pb2) as (unused_methods, stub): responses = stub.StreamingOutputCall( @@ -341,7 +352,8 @@ class PythonPluginTest(unittest.TestCase): next(responses) def testStreamingOutputCallFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) request = _streaming_output_request(test_pb2) with _CreateService(test_pb2) as (methods, stub): with methods.fail(): @@ -351,7 +363,8 @@ class PythonPluginTest(unittest.TestCase): next(responses) def testStreamingInputCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): response = stub.StreamingInputCall( _streaming_input_request_iterator(test_pb2), @@ -361,7 +374,8 @@ class PythonPluginTest(unittest.TestCase): self.assertEqual(expected_response, response) def testStreamingInputCallFuture(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): with methods.pause(): response_future = stub.StreamingInputCall.future( @@ -373,7 +387,8 @@ class PythonPluginTest(unittest.TestCase): self.assertEqual(expected_response, response) def testStreamingInputCallFutureExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): with methods.pause(): response_future = stub.StreamingInputCall.future( @@ -385,7 +400,8 @@ class PythonPluginTest(unittest.TestCase): response_future.exception(), face.ExpirationError) def testStreamingInputCallFutureCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): with methods.pause(): response_future = stub.StreamingInputCall.future( @@ -397,7 +413,8 @@ class PythonPluginTest(unittest.TestCase): response_future.result() def testStreamingInputCallFutureFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): with methods.fail(): response_future = stub.StreamingInputCall.future( @@ -406,7 +423,8 @@ class PythonPluginTest(unittest.TestCase): self.assertIsNotNone(response_future.exception()) def testFullDuplexCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): responses = stub.FullDuplexCall( _full_duplex_request_iterator(test_pb2), test_constants.LONG_TIMEOUT) @@ -417,7 +435,8 @@ class PythonPluginTest(unittest.TestCase): self.assertEqual(expected_response, response) def testFullDuplexCallExpired(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) request_iterator = _full_duplex_request_iterator(test_pb2) with _CreateService(test_pb2) as (methods, stub): with methods.pause(): @@ -427,7 +446,8 @@ class PythonPluginTest(unittest.TestCase): list(responses) def testFullDuplexCallCancelled(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): request_iterator = _full_duplex_request_iterator(test_pb2) responses = stub.FullDuplexCall( @@ -438,7 +458,8 @@ class PythonPluginTest(unittest.TestCase): next(responses) def testFullDuplexCallFailed(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) request_iterator = _full_duplex_request_iterator(test_pb2) with _CreateService(test_pb2) as (methods, stub): with methods.fail(): @@ -449,7 +470,8 @@ class PythonPluginTest(unittest.TestCase): next(responses) def testHalfDuplexCall(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) with _CreateService(test_pb2) as (methods, stub): def half_duplex_request_iterator(): request = test_pb2.StreamingOutputCallRequest() @@ -468,7 +490,8 @@ class PythonPluginTest(unittest.TestCase): self.assertEqual(expected_response, response) def testHalfDuplexCallWedged(self): - import test_pb2 # pylint: disable=g-import-not-at-top + import protoc_plugin_test_pb2 as test_pb2 # pylint: disable=g-import-not-at-top + reload(test_pb2) condition = threading.Condition() wait_cell = [False] @contextlib.contextmanager diff --git a/src/python/grpcio_test/grpc_protoc_plugin/test.proto b/src/python/grpcio/tests/protoc_plugin/protoc_plugin_test.proto index 6762a8e7f3..6762a8e7f3 100644 --- a/src/python/grpcio_test/grpc_protoc_plugin/test.proto +++ b/src/python/grpcio/tests/protoc_plugin/protoc_plugin_test.proto diff --git a/src/python/grpcio_test/grpc_test/__init__.py b/src/python/grpcio/tests/unit/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/__init__.py +++ b/src/python/grpcio/tests/unit/__init__.py diff --git a/src/python/grpcio_test/grpc_test/_adapter/.gitignore b/src/python/grpcio/tests/unit/_adapter/.gitignore index a6f96cd6db..a6f96cd6db 100644 --- a/src/python/grpcio_test/grpc_test/_adapter/.gitignore +++ b/src/python/grpcio/tests/unit/_adapter/.gitignore diff --git a/src/python/grpcio_test/grpc_test/_adapter/__init__.py b/src/python/grpcio/tests/unit/_adapter/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/_adapter/__init__.py +++ b/src/python/grpcio/tests/unit/_adapter/__init__.py diff --git a/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py b/src/python/grpcio/tests/unit/_adapter/_intermediary_low_test.py index 90ad0b9bcb..a6fd82388c 100644 --- a/src/python/grpcio_test/grpc_test/_adapter/_intermediary_low_test.py +++ b/src/python/grpcio/tests/unit/_adapter/_intermediary_low_test.py @@ -115,6 +115,7 @@ class EchoTest(unittest.TestCase): def tearDown(self): self.server.stop() + self.server.cancel_all_calls() self.server_completion_queue.stop() self.client_completion_queue.stop() self.server_completion_queue_thread.join() @@ -286,11 +287,6 @@ class EchoTest(unittest.TestCase): set((server_trailing_metadata_key, server_trailing_binary_metadata_key,))) - server_timeout_none_event = self.server_completion_queue.get(0) - self.assertIsNone(server_timeout_none_event) - client_timeout_none_event = self.client_completion_queue.get(0) - self.assertIsNone(client_timeout_none_event) - self.assertSequenceEqual(test_data, server_data) self.assertSequenceEqual(test_data, client_data) @@ -335,6 +331,7 @@ class CancellationTest(unittest.TestCase): def tearDown(self): self.server.stop() + self.server.cancel_all_calls() self.server_completion_queue.stop() self.client_completion_queue.stop() self.server_completion_queue_thread.join() @@ -410,14 +407,9 @@ class CancellationTest(unittest.TestCase): finish_event = self.client_events.get() self.assertEqual(_low.Event.Kind.FINISH, finish_event.kind) - self.assertEqual(_low.Status(_low.Code.CANCELLED, 'Cancelled'), + self.assertEqual(_low.Status(_low.Code.CANCELLED, 'Cancelled'), finish_event.status) - server_timeout_none_event = self.server_completion_queue.get(0) - self.assertIsNone(server_timeout_none_event) - client_timeout_none_event = self.client_completion_queue.get(0) - self.assertIsNone(client_timeout_none_event) - self.assertSequenceEqual(test_data, server_data) self.assertSequenceEqual(test_data, client_data) diff --git a/src/python/grpcio_test/grpc_test/_adapter/_low_test.py b/src/python/grpcio/tests/unit/_adapter/_low_test.py index 8115cd0e83..ec46617996 100644 --- a/src/python/grpcio_test/grpc_test/_adapter/_low_test.py +++ b/src/python/grpcio/tests/unit/_adapter/_low_test.py @@ -34,7 +34,7 @@ import unittest from grpc import _grpcio_metadata from grpc._adapter import _types from grpc._adapter import _low -from grpc_test import test_common +from tests.unit import test_common def wait_for_events(completion_queues, deadline): @@ -80,11 +80,11 @@ class InsecureServerInsecureClient(unittest.TestCase): del self.client_channel self.client_completion_queue.shutdown() - while (self.client_completion_queue.next().type != + while (self.client_completion_queue.next(float('+inf')).type != _types.EventType.QUEUE_SHUTDOWN): pass self.server_completion_queue.shutdown() - while (self.server_completion_queue.next().type != + while (self.server_completion_queue.next(float('+inf')).type != _types.EventType.QUEUE_SHUTDOWN): pass @@ -294,8 +294,12 @@ class HangingServerShutdown(unittest.TestCase): # Now try to shutdown the server and expect that we see server shutdown # almost immediately after calling cancel_all_calls. + + # First attempt to cancel all calls before shutting down, and expect + # our state machine to catch the erroneous API use. with self.assertRaises(RuntimeError): self.server.cancel_all_calls() + shutdown_tag = object() self.server.shutdown(shutdown_tag) pre_cancel_timestamp = time.time() diff --git a/src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py b/src/python/grpcio/tests/unit/_adapter/_proto_scenarios.py index b3d6ec8607..f55a7a23ea 100644 --- a/src/python/grpcio_test/grpc_test/_adapter/_proto_scenarios.py +++ b/src/python/grpcio/tests/unit/_adapter/_proto_scenarios.py @@ -32,7 +32,7 @@ import abc import threading -from grpc_test._junkdrawer import math_pb2 +from tests.unit._junkdrawer import math_pb2 class ProtoScenario(object): diff --git a/src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py b/src/python/grpcio/tests/unit/_core_over_links_base_interface_test.py index cafb6b6eae..efc990421a 100644 --- a/src/python/grpcio_test/grpc_test/_core_over_links_base_interface_test.py +++ b/src/python/grpcio/tests/unit/_core_over_links_base_interface_test.py @@ -41,10 +41,10 @@ from grpc._links import service from grpc.beta import interfaces as beta_interfaces from grpc.framework.core import implementations from grpc.framework.interfaces.base import utilities -from grpc_test import test_common as grpc_test_common -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.base import test_cases -from grpc_test.framework.interfaces.base import test_interfaces +from tests.unit import test_common as grpc_test_common +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.base import test_cases +from tests.unit.framework.interfaces.base import test_interfaces class _SerializationBehaviors( diff --git a/src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py b/src/python/grpcio/tests/unit/_crust_over_core_over_links_face_interface_test.py index a4d4dee38c..4faaaadc2b 100644 --- a/src/python/grpcio_test/grpc_test/_crust_over_core_over_links_face_interface_test.py +++ b/src/python/grpcio/tests/unit/_crust_over_core_over_links_face_interface_test.py @@ -40,10 +40,10 @@ from grpc.framework.core import implementations as core_implementations from grpc.framework.crust import implementations as crust_implementations from grpc.framework.foundation import logging_pool from grpc.framework.interfaces.links import utilities -from grpc_test import test_common as grpc_test_common -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.face import test_cases -from grpc_test.framework.interfaces.face import test_interfaces +from tests.unit import test_common as grpc_test_common +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.face import test_cases +from tests.unit.framework.interfaces.face import test_interfaces class _SerializationBehaviors( diff --git a/src/python/grpcio_test/grpc_test/_cython/.gitignore b/src/python/grpcio/tests/unit/_cython/.gitignore index c315029288..c315029288 100644 --- a/src/python/grpcio_test/grpc_test/_cython/.gitignore +++ b/src/python/grpcio/tests/unit/_cython/.gitignore diff --git a/src/python/grpcio_test/grpc_test/_cython/__init__.py b/src/python/grpcio/tests/unit/_cython/__init__.py index b89398809f..b89398809f 100644 --- a/src/python/grpcio_test/grpc_test/_cython/__init__.py +++ b/src/python/grpcio/tests/unit/_cython/__init__.py diff --git a/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py b/src/python/grpcio/tests/unit/_cython/cygrpc_test.py index 1307a30ca0..876da88de9 100644 --- a/src/python/grpcio_test/grpc_test/_cython/cygrpc_test.py +++ b/src/python/grpcio/tests/unit/_cython/cygrpc_test.py @@ -28,11 +28,24 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import time +import threading import unittest from grpc._cython import cygrpc -from grpc_test._cython import test_utilities -from grpc_test import test_common +from tests.unit._cython import test_utilities +from tests.unit import test_common +from tests.unit import resources + + +_SSL_HOST_OVERRIDE = 'foo.test.google.fr' +_CALL_CREDENTIALS_METADATA_KEY = 'call-creds-key' +_CALL_CREDENTIALS_METADATA_VALUE = 'call-creds-value' + +def _metadata_plugin_callback(context, callback): + callback(cygrpc.Metadata( + [cygrpc.Metadatum(_CALL_CREDENTIALS_METADATA_KEY, + _CALL_CREDENTIALS_METADATA_VALUE)]), + cygrpc.StatusCode.ok, '') class TypeSmokeTest(unittest.TestCase): @@ -89,7 +102,17 @@ class TypeSmokeTest(unittest.TestCase): channel = cygrpc.Channel('[::]:0', cygrpc.ChannelArgs([])) del channel - @unittest.skip('TODO(atash): undo skip after #2229 is merged') + def testCredentialsMetadataPluginUpDown(self): + plugin = cygrpc.CredentialsMetadataPlugin( + lambda ignored_a, ignored_b: None, '') + del plugin + + def testCallCredentialsFromPluginUpDown(self): + plugin = cygrpc.CredentialsMetadataPlugin(_metadata_plugin_callback, '') + call_credentials = cygrpc.call_credentials_metadata_plugin(plugin) + del plugin + del call_credentials + def testServerStartNoExplicitShutdown(self): server = cygrpc.Server() completion_queue = cygrpc.CompletionQueue() @@ -99,7 +122,6 @@ class TypeSmokeTest(unittest.TestCase): server.start() del server - @unittest.skip('TODO(atash): undo skip after #2229 is merged') def testServerStartShutdown(self): completion_queue = cygrpc.CompletionQueue() server = cygrpc.Server() @@ -262,5 +284,169 @@ class InsecureServerInsecureClient(unittest.TestCase): del server_call +class SecureServerSecureClient(unittest.TestCase): + + def setUp(self): + server_credentials = cygrpc.server_credentials_ssl( + None, [cygrpc.SslPemKeyCertPair(resources.private_key(), + resources.certificate_chain())], False) + channel_credentials = cygrpc.channel_credentials_ssl( + resources.test_root_certificates(), None) + self.server_completion_queue = cygrpc.CompletionQueue() + self.server = cygrpc.Server() + self.server.register_completion_queue(self.server_completion_queue) + self.port = self.server.add_http2_port('[::]:0', server_credentials) + self.server.start() + self.client_completion_queue = cygrpc.CompletionQueue() + client_channel_arguments = cygrpc.ChannelArgs([ + cygrpc.ChannelArg(cygrpc.ChannelArgKey.ssl_target_name_override, + _SSL_HOST_OVERRIDE)]) + self.client_channel = cygrpc.Channel( + 'localhost:{}'.format(self.port), client_channel_arguments, + channel_credentials) + + def tearDown(self): + del self.server + del self.client_completion_queue + del self.server_completion_queue + + def testEcho(self): + DEADLINE = time.time()+5 + DEADLINE_TOLERANCE = 0.25 + CLIENT_METADATA_ASCII_KEY = b'key' + CLIENT_METADATA_ASCII_VALUE = b'val' + CLIENT_METADATA_BIN_KEY = b'key-bin' + CLIENT_METADATA_BIN_VALUE = b'\0'*1000 + SERVER_INITIAL_METADATA_KEY = b'init_me_me_me' + SERVER_INITIAL_METADATA_VALUE = b'whodawha?' + SERVER_TRAILING_METADATA_KEY = b'california_is_in_a_drought' + SERVER_TRAILING_METADATA_VALUE = b'zomg it is' + SERVER_STATUS_CODE = cygrpc.StatusCode.ok + SERVER_STATUS_DETAILS = b'our work is never over' + REQUEST = b'in death a member of project mayhem has a name' + RESPONSE = b'his name is robert paulson' + METHOD = b'/twinkies' + HOST = None # Default host + + cygrpc_deadline = cygrpc.Timespec(DEADLINE) + + server_request_tag = object() + request_call_result = self.server.request_call( + self.server_completion_queue, self.server_completion_queue, + server_request_tag) + + self.assertEqual(cygrpc.CallError.ok, request_call_result) + + plugin = cygrpc.CredentialsMetadataPlugin(_metadata_plugin_callback, '') + call_credentials = cygrpc.call_credentials_metadata_plugin(plugin) + + client_call_tag = object() + client_call = self.client_channel.create_call( + None, 0, self.client_completion_queue, METHOD, HOST, cygrpc_deadline) + client_call.set_credentials(call_credentials) + client_initial_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(CLIENT_METADATA_ASCII_KEY, + CLIENT_METADATA_ASCII_VALUE), + cygrpc.Metadatum(CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)]) + client_start_batch_result = client_call.start_batch(cygrpc.Operations([ + cygrpc.operation_send_initial_metadata(client_initial_metadata), + cygrpc.operation_send_message(REQUEST), + cygrpc.operation_send_close_from_client(), + cygrpc.operation_receive_initial_metadata(), + cygrpc.operation_receive_message(), + cygrpc.operation_receive_status_on_client() + ]), client_call_tag) + self.assertEqual(cygrpc.CallError.ok, client_start_batch_result) + client_event_future = test_utilities.CompletionQueuePollFuture( + self.client_completion_queue, cygrpc_deadline) + + request_event = self.server_completion_queue.poll(cygrpc_deadline) + self.assertEqual(cygrpc.CompletionType.operation_complete, + request_event.type) + self.assertIsInstance(request_event.operation_call, cygrpc.Call) + self.assertIs(server_request_tag, request_event.tag) + self.assertEqual(0, len(request_event.batch_operations)) + client_metadata_with_credentials = list(client_initial_metadata) + [ + (_CALL_CREDENTIALS_METADATA_KEY, _CALL_CREDENTIALS_METADATA_VALUE)] + self.assertTrue( + test_common.metadata_transmitted(client_metadata_with_credentials, + request_event.request_metadata)) + self.assertEqual(METHOD, request_event.request_call_details.method) + self.assertEqual(_SSL_HOST_OVERRIDE, + request_event.request_call_details.host) + self.assertLess( + abs(DEADLINE - float(request_event.request_call_details.deadline)), + DEADLINE_TOLERANCE) + + server_call_tag = object() + server_call = request_event.operation_call + server_initial_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(SERVER_INITIAL_METADATA_KEY, + SERVER_INITIAL_METADATA_VALUE)]) + server_trailing_metadata = cygrpc.Metadata([ + cygrpc.Metadatum(SERVER_TRAILING_METADATA_KEY, + SERVER_TRAILING_METADATA_VALUE)]) + server_start_batch_result = server_call.start_batch([ + cygrpc.operation_send_initial_metadata(server_initial_metadata), + cygrpc.operation_receive_message(), + cygrpc.operation_send_message(RESPONSE), + cygrpc.operation_receive_close_on_server(), + cygrpc.operation_send_status_from_server( + server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) + ], server_call_tag) + self.assertEqual(cygrpc.CallError.ok, server_start_batch_result) + + client_event = client_event_future.result() + server_event = self.server_completion_queue.poll(cygrpc_deadline) + + self.assertEqual(6, len(client_event.batch_operations)) + found_client_op_types = set() + for client_result in client_event.batch_operations: + # we expect each op type to be unique + self.assertNotIn(client_result.type, found_client_op_types) + found_client_op_types.add(client_result.type) + if client_result.type == cygrpc.OperationType.receive_initial_metadata: + self.assertTrue( + test_common.metadata_transmitted(server_initial_metadata, + client_result.received_metadata)) + elif client_result.type == cygrpc.OperationType.receive_message: + self.assertEqual(RESPONSE, client_result.received_message.bytes()) + elif client_result.type == cygrpc.OperationType.receive_status_on_client: + self.assertTrue( + test_common.metadata_transmitted(server_trailing_metadata, + client_result.received_metadata)) + self.assertEqual(SERVER_STATUS_DETAILS, + client_result.received_status_details) + self.assertEqual(SERVER_STATUS_CODE, client_result.received_status_code) + self.assertEqual(set([ + cygrpc.OperationType.send_initial_metadata, + cygrpc.OperationType.send_message, + cygrpc.OperationType.send_close_from_client, + cygrpc.OperationType.receive_initial_metadata, + cygrpc.OperationType.receive_message, + cygrpc.OperationType.receive_status_on_client + ]), found_client_op_types) + + self.assertEqual(5, len(server_event.batch_operations)) + found_server_op_types = set() + for server_result in server_event.batch_operations: + self.assertNotIn(client_result.type, found_server_op_types) + found_server_op_types.add(server_result.type) + if server_result.type == cygrpc.OperationType.receive_message: + self.assertEqual(REQUEST, server_result.received_message.bytes()) + elif server_result.type == cygrpc.OperationType.receive_close_on_server: + self.assertFalse(server_result.received_cancelled) + self.assertEqual(set([ + cygrpc.OperationType.send_initial_metadata, + cygrpc.OperationType.receive_message, + cygrpc.OperationType.send_message, + cygrpc.OperationType.receive_close_on_server, + cygrpc.OperationType.send_status_from_server + ]), found_server_op_types) + + del client_call + del server_call + + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/_cython/test_utilities.py b/src/python/grpcio/tests/unit/_cython/test_utilities.py index 21ea3075b4..21ea3075b4 100644 --- a/src/python/grpcio_test/grpc_test/_cython/test_utilities.py +++ b/src/python/grpcio/tests/unit/_cython/test_utilities.py diff --git a/src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py b/src/python/grpcio/tests/unit/_junkdrawer/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/_junkdrawer/__init__.py +++ b/src/python/grpcio/tests/unit/_junkdrawer/__init__.py diff --git a/src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py b/src/python/grpcio/tests/unit/_junkdrawer/math_pb2.py index 20165955b4..20165955b4 100644 --- a/src/python/grpcio_test/grpc_test/_junkdrawer/math_pb2.py +++ b/src/python/grpcio/tests/unit/_junkdrawer/math_pb2.py diff --git a/src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py b/src/python/grpcio/tests/unit/_junkdrawer/stock_pb2.py index eef18f82d6..eef18f82d6 100644 --- a/src/python/grpcio_test/grpc_test/_junkdrawer/stock_pb2.py +++ b/src/python/grpcio/tests/unit/_junkdrawer/stock_pb2.py diff --git a/src/python/grpcio_test/grpc_test/_links/__init__.py b/src/python/grpcio/tests/unit/_links/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/_links/__init__.py +++ b/src/python/grpcio/tests/unit/_links/__init__.py diff --git a/src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py b/src/python/grpcio/tests/unit/_links/_lonely_invocation_link_test.py index 8e12e8cc22..890755f81c 100644 --- a/src/python/grpcio_test/grpc_test/_links/_lonely_invocation_link_test.py +++ b/src/python/grpcio/tests/unit/_links/_lonely_invocation_link_test.py @@ -34,9 +34,9 @@ import unittest from grpc._adapter import _intermediary_low from grpc._links import invocation from grpc.framework.interfaces.links import links -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.links import test_cases -from grpc_test.framework.interfaces.links import test_utilities +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.links import test_cases +from tests.unit.framework.interfaces.links import test_utilities _NULL_BEHAVIOR = lambda unused_argument: None diff --git a/src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py b/src/python/grpcio/tests/unit/_links/_proto_scenarios.py index 0d74d66297..f69ff51b16 100644 --- a/src/python/grpcio_test/grpc_test/_links/_proto_scenarios.py +++ b/src/python/grpcio/tests/unit/_links/_proto_scenarios.py @@ -32,8 +32,8 @@ import abc import threading -from grpc_test._junkdrawer import math_pb2 -from grpc_test.framework.common import test_constants +from tests.unit._junkdrawer import math_pb2 +from tests.unit.framework.common import test_constants class ProtoScenario(object): diff --git a/src/python/grpcio_test/grpc_test/_links/_transmission_test.py b/src/python/grpcio/tests/unit/_links/_transmission_test.py index 77e83d5561..888684d197 100644 --- a/src/python/grpcio_test/grpc_test/_links/_transmission_test.py +++ b/src/python/grpcio/tests/unit/_links/_transmission_test.py @@ -36,11 +36,11 @@ from grpc._links import invocation from grpc._links import service from grpc.beta import interfaces as beta_interfaces from grpc.framework.interfaces.links import links -from grpc_test import test_common -from grpc_test._links import _proto_scenarios -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.links import test_cases -from grpc_test.framework.interfaces.links import test_utilities +from tests.unit import test_common +from tests.unit._links import _proto_scenarios +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.links import test_cases +from tests.unit.framework.interfaces.links import test_utilities _IDENTITY = lambda x: x diff --git a/src/python/grpcio_test/grpc_test/beta/__init__.py b/src/python/grpcio/tests/unit/beta/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/beta/__init__.py +++ b/src/python/grpcio/tests/unit/beta/__init__.py diff --git a/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py b/src/python/grpcio/tests/unit/beta/_beta_features_test.py index 5916a9e3ea..ea44177b49 100644 --- a/src/python/grpcio_test/grpc_test/beta/_beta_features_test.py +++ b/src/python/grpcio/tests/unit/beta/_beta_features_test.py @@ -36,12 +36,15 @@ from grpc.beta import implementations from grpc.beta import interfaces from grpc.framework.common import cardinality from grpc.framework.interfaces.face import utilities -from grpc_test import resources -from grpc_test.beta import test_utilities -from grpc_test.framework.common import test_constants +from tests.unit import resources +from tests.unit.beta import test_utilities +from tests.unit.framework.common import test_constants _SERVER_HOST_OVERRIDE = 'foo.test.google.fr' +_PER_RPC_CREDENTIALS_METADATA_KEY = 'my-call-credentials-metadata-key' +_PER_RPC_CREDENTIALS_METADATA_VALUE = 'my-call-credentials-metadata-value' + _GROUP = 'group' _UNARY_UNARY = 'unary-unary' _UNARY_STREAM = 'unary-stream' @@ -63,6 +66,7 @@ class _Servicer(object): with self._condition: self._request = request self._peer = context.protocol_context().peer() + self._invocation_metadata = context.invocation_metadata() context.protocol_context().disable_next_response_compression() self._serviced = True self._condition.notify_all() @@ -72,6 +76,7 @@ class _Servicer(object): with self._condition: self._request = request self._peer = context.protocol_context().peer() + self._invocation_metadata = context.invocation_metadata() context.protocol_context().disable_next_response_compression() self._serviced = True self._condition.notify_all() @@ -83,6 +88,7 @@ class _Servicer(object): self._request = request with self._condition: self._peer = context.protocol_context().peer() + self._invocation_metadata = context.invocation_metadata() context.protocol_context().disable_next_response_compression() self._serviced = True self._condition.notify_all() @@ -95,6 +101,7 @@ class _Servicer(object): context.protocol_context().disable_next_response_compression() yield _RESPONSE with self._condition: + self._invocation_metadata = context.invocation_metadata() self._serviced = True self._condition.notify_all() @@ -137,6 +144,11 @@ class _BlockingIterator(object): self._condition.notify_all() +def _metadata_plugin(context, callback): + callback([(_PER_RPC_CREDENTIALS_METADATA_KEY, + _PER_RPC_CREDENTIALS_METADATA_VALUE)], None) + + class BetaFeaturesTest(unittest.TestCase): def setUp(self): @@ -167,10 +179,12 @@ class BetaFeaturesTest(unittest.TestCase): [(resources.private_key(), resources.certificate_chain(),),]) port = self._server.add_secure_port('[::]:0', server_credentials) self._server.start() - self._client_credentials = implementations.ssl_client_credentials( + self._channel_credentials = implementations.ssl_channel_credentials( resources.test_root_certificates(), None, None) + self._call_credentials = implementations.metadata_call_credentials( + _metadata_plugin) channel = test_utilities.not_really_secure_channel( - 'localhost', port, self._client_credentials, _SERVER_HOST_OVERRIDE) + 'localhost', port, self._channel_credentials, _SERVER_HOST_OVERRIDE) stub_options = implementations.stub_options( thread_pool_size=test_constants.POOL_SIZE) self._dynamic_stub = implementations.dynamic_stub( @@ -181,21 +195,36 @@ class BetaFeaturesTest(unittest.TestCase): self._server.stop(test_constants.SHORT_TIMEOUT).wait() def test_unary_unary(self): - call_options = interfaces.grpc_call_options(disable_compression=True) + call_options = interfaces.grpc_call_options( + disable_compression=True, credentials=self._call_credentials) response = getattr(self._dynamic_stub, _UNARY_UNARY)( _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options) self.assertEqual(_RESPONSE, response) self.assertIsNotNone(self._servicer.peer()) + invocation_metadata = [(metadatum.key, metadatum.value) for metadatum in + self._servicer._invocation_metadata] + self.assertIn( + (_PER_RPC_CREDENTIALS_METADATA_KEY, + _PER_RPC_CREDENTIALS_METADATA_VALUE), + invocation_metadata) def test_unary_stream(self): - call_options = interfaces.grpc_call_options(disable_compression=True) + call_options = interfaces.grpc_call_options( + disable_compression=True, credentials=self._call_credentials) response_iterator = getattr(self._dynamic_stub, _UNARY_STREAM)( _REQUEST, test_constants.LONG_TIMEOUT, protocol_options=call_options) self._servicer.block_until_serviced() self.assertIsNotNone(self._servicer.peer()) + invocation_metadata = [(metadatum.key, metadatum.value) for metadatum in + self._servicer._invocation_metadata] + self.assertIn( + (_PER_RPC_CREDENTIALS_METADATA_KEY, + _PER_RPC_CREDENTIALS_METADATA_VALUE), + invocation_metadata) def test_stream_unary(self): - call_options = interfaces.grpc_call_options() + call_options = interfaces.grpc_call_options( + credentials=self._call_credentials) request_iterator = _BlockingIterator(iter((_REQUEST,))) response_future = getattr(self._dynamic_stub, _STREAM_UNARY).future( request_iterator, test_constants.LONG_TIMEOUT, @@ -207,9 +236,16 @@ class BetaFeaturesTest(unittest.TestCase): self._servicer.block_until_serviced() self.assertIsNotNone(self._servicer.peer()) self.assertEqual(_RESPONSE, response_future.result()) + invocation_metadata = [(metadatum.key, metadatum.value) for metadatum in + self._servicer._invocation_metadata] + self.assertIn( + (_PER_RPC_CREDENTIALS_METADATA_KEY, + _PER_RPC_CREDENTIALS_METADATA_VALUE), + invocation_metadata) def test_stream_stream(self): - call_options = interfaces.grpc_call_options() + call_options = interfaces.grpc_call_options( + credentials=self._call_credentials) request_iterator = _BlockingIterator(iter((_REQUEST,))) response_iterator = getattr(self._dynamic_stub, _STREAM_STREAM)( request_iterator, test_constants.SHORT_TIMEOUT, @@ -222,6 +258,85 @@ class BetaFeaturesTest(unittest.TestCase): self._servicer.block_until_serviced() self.assertIsNotNone(self._servicer.peer()) self.assertEqual(_RESPONSE, response) + invocation_metadata = [(metadatum.key, metadatum.value) for metadatum in + self._servicer._invocation_metadata] + self.assertIn( + (_PER_RPC_CREDENTIALS_METADATA_KEY, + _PER_RPC_CREDENTIALS_METADATA_VALUE), + invocation_metadata) + + +class ContextManagementAndLifecycleTest(unittest.TestCase): + + def setUp(self): + self._servicer = _Servicer() + self._method_implementations = { + (_GROUP, _UNARY_UNARY): + utilities.unary_unary_inline(self._servicer.unary_unary), + (_GROUP, _UNARY_STREAM): + utilities.unary_stream_inline(self._servicer.unary_stream), + (_GROUP, _STREAM_UNARY): + utilities.stream_unary_inline(self._servicer.stream_unary), + (_GROUP, _STREAM_STREAM): + utilities.stream_stream_inline(self._servicer.stream_stream), + } + + self._cardinalities = { + _UNARY_UNARY: cardinality.Cardinality.UNARY_UNARY, + _UNARY_STREAM: cardinality.Cardinality.UNARY_STREAM, + _STREAM_UNARY: cardinality.Cardinality.STREAM_UNARY, + _STREAM_STREAM: cardinality.Cardinality.STREAM_STREAM, + } + + self._server_options = implementations.server_options( + thread_pool_size=test_constants.POOL_SIZE) + self._server_credentials = implementations.ssl_server_credentials( + [(resources.private_key(), resources.certificate_chain(),),]) + self._channel_credentials = implementations.ssl_channel_credentials( + resources.test_root_certificates(), None, None) + self._stub_options = implementations.stub_options( + thread_pool_size=test_constants.POOL_SIZE) + + def test_stub_context(self): + server = implementations.server( + self._method_implementations, options=self._server_options) + port = server.add_secure_port('[::]:0', self._server_credentials) + server.start() + + channel = test_utilities.not_really_secure_channel( + 'localhost', port, self._channel_credentials, _SERVER_HOST_OVERRIDE) + dynamic_stub = implementations.dynamic_stub( + channel, _GROUP, self._cardinalities, options=self._stub_options) + for _ in range(100): + with dynamic_stub: + pass + for _ in range(10): + with dynamic_stub: + call_options = interfaces.grpc_call_options( + disable_compression=True) + response = getattr(dynamic_stub, _UNARY_UNARY)( + _REQUEST, test_constants.LONG_TIMEOUT, + protocol_options=call_options) + self.assertEqual(_RESPONSE, response) + self.assertIsNotNone(self._servicer.peer()) + + server.stop(test_constants.SHORT_TIMEOUT).wait() + + def test_server_lifecycle(self): + for _ in range(100): + server = implementations.server( + self._method_implementations, options=self._server_options) + port = server.add_secure_port('[::]:0', self._server_credentials) + server.start() + server.stop(test_constants.SHORT_TIMEOUT).wait() + for _ in range(100): + server = implementations.server( + self._method_implementations, options=self._server_options) + server.add_secure_port('[::]:0', self._server_credentials) + server.add_insecure_port('[::]:0') + with server: + server.stop(test_constants.SHORT_TIMEOUT) + server.stop(test_constants.SHORT_TIMEOUT) if __name__ == '__main__': diff --git a/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py b/src/python/grpcio/tests/unit/beta/_connectivity_channel_test.py index b3c05bdb0c..5dc8720639 100644 --- a/src/python/grpcio_test/grpc_test/beta/_connectivity_channel_test.py +++ b/src/python/grpcio/tests/unit/beta/_connectivity_channel_test.py @@ -37,7 +37,7 @@ from grpc._adapter import _low from grpc._adapter import _types from grpc.beta import _connectivity_channel from grpc.beta import interfaces -from grpc_test.framework.common import test_constants +from tests.unit.framework.common import test_constants def _drive_completion_queue(completion_queue): diff --git a/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py b/src/python/grpcio/tests/unit/beta/_face_interface_test.py index aa33e1e6f8..1c21dfd03d 100644 --- a/src/python/grpcio_test/grpc_test/beta/_face_interface_test.py +++ b/src/python/grpcio/tests/unit/beta/_face_interface_test.py @@ -34,12 +34,12 @@ import unittest from grpc.beta import implementations from grpc.beta import interfaces -from grpc_test import resources -from grpc_test import test_common as grpc_test_common -from grpc_test.beta import test_utilities -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.face import test_cases -from grpc_test.framework.interfaces.face import test_interfaces +from tests.unit import resources +from tests.unit import test_common as grpc_test_common +from tests.unit.beta import test_utilities +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.face import test_cases +from tests.unit.framework.interfaces.face import test_interfaces _SERVER_HOST_OVERRIDE = 'foo.test.google.fr' @@ -91,10 +91,10 @@ class _Implementation(test_interfaces.Implementation): [(resources.private_key(), resources.certificate_chain(),),]) port = server.add_secure_port('[::]:0', server_credentials) server.start() - client_credentials = implementations.ssl_client_credentials( + channel_credentials = implementations.ssl_channel_credentials( resources.test_root_certificates(), None, None) channel = test_utilities.not_really_secure_channel( - 'localhost', port, client_credentials, _SERVER_HOST_OVERRIDE) + 'localhost', port, channel_credentials, _SERVER_HOST_OVERRIDE) stub_options = implementations.stub_options( request_serializers=serialization_behaviors.request_serializers, response_deserializers=serialization_behaviors.response_deserializers, diff --git a/src/python/grpcio_test/grpc_test/beta/_not_found_test.py b/src/python/grpcio/tests/unit/beta/_not_found_test.py index 5feb997fef..44fcd1e13c 100644 --- a/src/python/grpcio_test/grpc_test/beta/_not_found_test.py +++ b/src/python/grpcio/tests/unit/beta/_not_found_test.py @@ -34,7 +34,7 @@ import unittest from grpc.beta import implementations from grpc.beta import interfaces from grpc.framework.interfaces.face import face -from grpc_test.framework.common import test_constants +from tests.unit.framework.common import test_constants class NotFoundTest(unittest.TestCase): diff --git a/src/python/grpcio_test/grpc_test/beta/_utilities_test.py b/src/python/grpcio/tests/unit/beta/_utilities_test.py index 996cea9118..08ce98e751 100644 --- a/src/python/grpcio_test/grpc_test/beta/_utilities_test.py +++ b/src/python/grpcio/tests/unit/beta/_utilities_test.py @@ -38,7 +38,7 @@ from grpc._adapter import _types from grpc.beta import implementations from grpc.beta import utilities from grpc.framework.foundation import future -from grpc_test.framework.common import test_constants +from tests.unit.framework.common import test_constants def _drive_completion_queue(completion_queue): diff --git a/src/python/grpcio_test/grpc_test/beta/test_utilities.py b/src/python/grpcio/tests/unit/beta/test_utilities.py index 24a8600e12..0313e06a93 100644 --- a/src/python/grpcio_test/grpc_test/beta/test_utilities.py +++ b/src/python/grpcio/tests/unit/beta/test_utilities.py @@ -34,13 +34,13 @@ from grpc.beta import implementations def not_really_secure_channel( - host, port, client_credentials, server_host_override): + host, port, channel_credentials, server_host_override): """Creates an insecure Channel to a remote host. Args: host: The name of the remote host to which to connect. port: The port of the remote host to which to connect. - client_credentials: The implementations.ClientCredentials with which to + channel_credentials: The implementations.ChannelCredentials with which to connect. server_host_override: The target name used for SSL host name checking. @@ -50,7 +50,7 @@ def not_really_secure_channel( """ hostport = '%s:%d' % (host, port) intermediary_low_channel = _intermediary_low.Channel( - hostport, client_credentials._intermediary_low_credentials, + hostport, channel_credentials._low_credentials, server_host_override=server_host_override) return implementations.Channel( intermediary_low_channel._internal, intermediary_low_channel) diff --git a/src/python/grpcio_test/grpc_test/credentials/README b/src/python/grpcio/tests/unit/credentials/README index cb20dcb49f..cb20dcb49f 100644 --- a/src/python/grpcio_test/grpc_test/credentials/README +++ b/src/python/grpcio/tests/unit/credentials/README diff --git a/src/python/grpcio_test/grpc_test/credentials/ca.pem b/src/python/grpcio/tests/unit/credentials/ca.pem index 6c8511a73c..6c8511a73c 100755 --- a/src/python/grpcio_test/grpc_test/credentials/ca.pem +++ b/src/python/grpcio/tests/unit/credentials/ca.pem diff --git a/src/python/grpcio_test/grpc_test/credentials/server1.key b/src/python/grpcio/tests/unit/credentials/server1.key index 143a5b8765..143a5b8765 100755 --- a/src/python/grpcio_test/grpc_test/credentials/server1.key +++ b/src/python/grpcio/tests/unit/credentials/server1.key diff --git a/src/python/grpcio_test/grpc_test/credentials/server1.pem b/src/python/grpcio/tests/unit/credentials/server1.pem index f3d43fcc5b..f3d43fcc5b 100755 --- a/src/python/grpcio_test/grpc_test/credentials/server1.pem +++ b/src/python/grpcio/tests/unit/credentials/server1.pem diff --git a/src/python/grpcio_test/grpc_test/framework/__init__.py b/src/python/grpcio/tests/unit/framework/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/__init__.py +++ b/src/python/grpcio/tests/unit/framework/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/_crust_over_core_face_interface_test.py b/src/python/grpcio/tests/unit/framework/_crust_over_core_face_interface_test.py index 30bb85f6c3..360ecc95d5 100644 --- a/src/python/grpcio_test/grpc_test/framework/_crust_over_core_face_interface_test.py +++ b/src/python/grpcio/tests/unit/framework/_crust_over_core_face_interface_test.py @@ -36,10 +36,10 @@ from grpc.framework.core import implementations as core_implementations from grpc.framework.crust import implementations as crust_implementations from grpc.framework.foundation import logging_pool from grpc.framework.interfaces.links import utilities -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.face import test_cases -from grpc_test.framework.interfaces.face import test_interfaces -from grpc_test.framework.interfaces.links import test_utilities +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.face import test_cases +from tests.unit.framework.interfaces.face import test_interfaces +from tests.unit.framework.interfaces.links import test_utilities class _Implementation(test_interfaces.Implementation): diff --git a/src/python/grpcio_test/grpc_test/framework/common/__init__.py b/src/python/grpcio/tests/unit/framework/common/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/common/__init__.py +++ b/src/python/grpcio/tests/unit/framework/common/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/common/test_constants.py b/src/python/grpcio/tests/unit/framework/common/test_constants.py index e1d3c2709d..e1d3c2709d 100644 --- a/src/python/grpcio_test/grpc_test/framework/common/test_constants.py +++ b/src/python/grpcio/tests/unit/framework/common/test_constants.py diff --git a/src/python/grpcio_test/grpc_test/framework/common/test_control.py b/src/python/grpcio/tests/unit/framework/common/test_control.py index 8d6eba5c2c..8d6eba5c2c 100644 --- a/src/python/grpcio_test/grpc_test/framework/common/test_control.py +++ b/src/python/grpcio/tests/unit/framework/common/test_control.py diff --git a/src/python/grpcio_test/grpc_test/framework/common/test_coverage.py b/src/python/grpcio/tests/unit/framework/common/test_coverage.py index a7ed3582c4..a7ed3582c4 100644 --- a/src/python/grpcio_test/grpc_test/framework/common/test_coverage.py +++ b/src/python/grpcio/tests/unit/framework/common/test_coverage.py diff --git a/src/python/grpcio_test/grpc_test/framework/core/__init__.py b/src/python/grpcio/tests/unit/framework/core/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/core/__init__.py +++ b/src/python/grpcio/tests/unit/framework/core/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/core/_base_interface_test.py b/src/python/grpcio/tests/unit/framework/core/_base_interface_test.py index 8d72f131d5..1310292306 100644 --- a/src/python/grpcio_test/grpc_test/framework/core/_base_interface_test.py +++ b/src/python/grpcio/tests/unit/framework/core/_base_interface_test.py @@ -36,9 +36,9 @@ import unittest from grpc.framework.core import implementations from grpc.framework.interfaces.base import utilities -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.base import test_cases -from grpc_test.framework.interfaces.base import test_interfaces +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.base import test_cases +from tests.unit.framework.interfaces.base import test_interfaces class _Implementation(test_interfaces.Implementation): diff --git a/src/python/grpcio_test/grpc_test/framework/face/__init__.py b/src/python/grpcio/tests/unit/framework/face/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/__init__.py +++ b/src/python/grpcio/tests/unit/framework/face/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py b/src/python/grpcio/tests/unit/framework/face/testing/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/__init__.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py b/src/python/grpcio/tests/unit/framework/face/testing/base_util.py index 1df1529b27..1df1529b27 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/base_util.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/base_util.py diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py b/src/python/grpcio/tests/unit/framework/face/testing/blocking_invocation_inline_service_test_case.py index 251e1eb68e..0613516421 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/blocking_invocation_inline_service_test_case.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/blocking_invocation_inline_service_test_case.py @@ -34,12 +34,12 @@ import abc import unittest # pylint: disable=unused-import from grpc.framework.face import exceptions -from grpc_test.framework.common import test_constants -from grpc_test.framework.face.testing import control -from grpc_test.framework.face.testing import coverage -from grpc_test.framework.face.testing import digest -from grpc_test.framework.face.testing import stock_service -from grpc_test.framework.face.testing import test_case +from tests.unit.framework.common import test_constants +from tests.unit.framework.face.testing import control +from tests.unit.framework.face.testing import coverage +from tests.unit.framework.face.testing import digest +from tests.unit.framework.face.testing import stock_service +from tests.unit.framework.face.testing import test_case class BlockingInvocationInlineServiceTestCase( diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/callback.py b/src/python/grpcio/tests/unit/framework/face/testing/callback.py index d0e63c8c56..d0e63c8c56 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/callback.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/callback.py diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/control.py b/src/python/grpcio/tests/unit/framework/face/testing/control.py index 3960c4e649..3960c4e649 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/control.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/control.py diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py b/src/python/grpcio/tests/unit/framework/face/testing/coverage.py index f3aca113fe..f3aca113fe 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/coverage.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/coverage.py diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/digest.py b/src/python/grpcio/tests/unit/framework/face/testing/digest.py index 54ff21779a..39f28b9657 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/digest.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/digest.py @@ -40,9 +40,9 @@ from grpc.framework.face import exceptions from grpc.framework.face import interfaces as face_interfaces from grpc.framework.foundation import stream from grpc.framework.foundation import stream_util -from grpc_test.framework.face.testing import control as testing_control # pylint: disable=unused-import -from grpc_test.framework.face.testing import interfaces # pylint: disable=unused-import -from grpc_test.framework.face.testing import service as testing_service # pylint: disable=unused-import +from tests.unit.framework.face.testing import control as testing_control # pylint: disable=unused-import +from tests.unit.framework.face.testing import interfaces # pylint: disable=unused-import +from tests.unit.framework.face.testing import service as testing_service # pylint: disable=unused-import _IDENTITY = lambda x: x diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py b/src/python/grpcio/tests/unit/framework/face/testing/event_invocation_synchronous_event_service_test_case.py index 9df77678eb..179f3a2f67 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/event_invocation_synchronous_event_service_test_case.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/event_invocation_synchronous_event_service_test_case.py @@ -33,13 +33,13 @@ import abc import unittest from grpc.framework.face import interfaces -from grpc_test.framework.common import test_constants -from grpc_test.framework.face.testing import callback as testing_callback -from grpc_test.framework.face.testing import control -from grpc_test.framework.face.testing import coverage -from grpc_test.framework.face.testing import digest -from grpc_test.framework.face.testing import stock_service -from grpc_test.framework.face.testing import test_case +from tests.unit.framework.common import test_constants +from tests.unit.framework.face.testing import callback as testing_callback +from tests.unit.framework.face.testing import control +from tests.unit.framework.face.testing import coverage +from tests.unit.framework.face.testing import digest +from tests.unit.framework.face.testing import stock_service +from tests.unit.framework.face.testing import test_case class EventInvocationSynchronousEventServiceTestCase( diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py b/src/python/grpcio/tests/unit/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py index 70d86a0422..485524a356 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/future_invocation_asynchronous_event_service_test_case.py @@ -37,12 +37,12 @@ import unittest from grpc.framework.face import exceptions from grpc.framework.foundation import future from grpc.framework.foundation import logging_pool -from grpc_test.framework.common import test_constants -from grpc_test.framework.face.testing import control -from grpc_test.framework.face.testing import coverage -from grpc_test.framework.face.testing import digest -from grpc_test.framework.face.testing import stock_service -from grpc_test.framework.face.testing import test_case +from tests.unit.framework.common import test_constants +from tests.unit.framework.face.testing import control +from tests.unit.framework.face.testing import coverage +from tests.unit.framework.face.testing import digest +from tests.unit.framework.face.testing import stock_service +from tests.unit.framework.face.testing import test_case _MAXIMUM_POOL_SIZE = 10 diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py b/src/python/grpcio/tests/unit/framework/face/testing/interfaces.py index 5932dabf1e..5932dabf1e 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/interfaces.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/interfaces.py diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/service.py b/src/python/grpcio/tests/unit/framework/face/testing/service.py index ee9d6a3da3..ac0b89b6ee 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/service.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/service.py @@ -33,7 +33,7 @@ import abc # interfaces is referenced from specification in this module. from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import -from grpc_test.framework.face.testing import interfaces +from tests.unit.framework.face.testing import interfaces class UnaryUnaryTestMethodImplementation(interfaces.Method): diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py b/src/python/grpcio/tests/unit/framework/face/testing/stock_service.py index 0f83ca4db1..117c723f79 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/stock_service.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/stock_service.py @@ -33,8 +33,8 @@ from grpc.framework.common import cardinality from grpc.framework.foundation import abandonment from grpc.framework.foundation import stream from grpc.framework.foundation import stream_util -from grpc_test.framework.face.testing import service -from grpc_test._junkdrawer import stock_pb2 +from tests.unit.framework.face.testing import service +from tests.unit._junkdrawer import stock_pb2 SYMBOL_FORMAT = 'test symbol:%03d' STREAM_LENGTH = 400 diff --git a/src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py b/src/python/grpcio/tests/unit/framework/face/testing/test_case.py index 858d5cf7fd..23d4d919c2 100644 --- a/src/python/grpcio_test/grpc_test/framework/face/testing/test_case.py +++ b/src/python/grpcio/tests/unit/framework/face/testing/test_case.py @@ -33,7 +33,7 @@ import abc # face_interfaces and interfaces are referenced in specification in this module. from grpc.framework.face import interfaces as face_interfaces # pylint: disable=unused-import -from grpc_test.framework.face.testing import interfaces # pylint: disable=unused-import +from tests.unit.framework.face.testing import interfaces # pylint: disable=unused-import class FaceTestCase(object): diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/__init__.py b/src/python/grpcio/tests/unit/framework/foundation/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/foundation/__init__.py +++ b/src/python/grpcio/tests/unit/framework/foundation/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py b/src/python/grpcio/tests/unit/framework/foundation/_later_test.py index 6c2459e185..6c2459e185 100644 --- a/src/python/grpcio_test/grpc_test/framework/foundation/_later_test.py +++ b/src/python/grpcio/tests/unit/framework/foundation/_later_test.py diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py b/src/python/grpcio/tests/unit/framework/foundation/_logging_pool_test.py index 452802da6a..452802da6a 100644 --- a/src/python/grpcio_test/grpc_test/framework/foundation/_logging_pool_test.py +++ b/src/python/grpcio/tests/unit/framework/foundation/_logging_pool_test.py diff --git a/src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py b/src/python/grpcio/tests/unit/framework/foundation/stream_testing.py index 098a53d5e7..098a53d5e7 100644 --- a/src/python/grpcio_test/grpc_test/framework/foundation/stream_testing.py +++ b/src/python/grpcio/tests/unit/framework/foundation/stream_testing.py diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py b/src/python/grpcio/tests/unit/framework/interfaces/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/__init__.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/__init__.py b/src/python/grpcio/tests/unit/framework/interfaces/base/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/__init__.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/base/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py b/src/python/grpcio/tests/unit/framework/interfaces/base/_control.py index 46a01876d8..38102b198a 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_control.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/base/_control.py @@ -37,10 +37,10 @@ import threading import time from grpc.framework.interfaces.base import base -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.base import _sequence -from grpc_test.framework.interfaces.base import _state -from grpc_test.framework.interfaces.base import test_interfaces # pylint: disable=unused-import +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.base import _sequence +from tests.unit.framework.interfaces.base import _state +from tests.unit.framework.interfaces.base import test_interfaces # pylint: disable=unused-import _GROUP = 'base test cases test group' _METHOD = 'base test cases test method' diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py b/src/python/grpcio/tests/unit/framework/interfaces/base/_sequence.py index f547d91681..571d0e1e63 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_sequence.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/base/_sequence.py @@ -33,7 +33,7 @@ import collections import enum from grpc.framework.interfaces.base import base -from grpc_test.framework.common import test_constants +from tests.unit.framework.common import test_constants class Invocation( diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_state.py b/src/python/grpcio/tests/unit/framework/interfaces/base/_state.py index 21cf33aeb6..21cf33aeb6 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/_state.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/base/_state.py diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py b/src/python/grpcio/tests/unit/framework/interfaces/base/test_cases.py index ddda1018c3..4f8e26c9a2 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_cases.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/base/test_cases.py @@ -38,9 +38,9 @@ import unittest from grpc.framework.foundation import logging_pool from grpc.framework.interfaces.base import base from grpc.framework.interfaces.base import utilities -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.base import _control -from grpc_test.framework.interfaces.base import test_interfaces +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.base import _control +from tests.unit.framework.interfaces.base import test_interfaces _SYNCHRONICITY_VARIATION = (('Sync', False), ('Async', True)) @@ -271,6 +271,7 @@ def test_cases(implementation): '_randomness': randomness, '_synchronicity_variation': synchronicity_variation[1], '_controller_creator': controller_creator, + '__module__': implementation.__module__, })) return test_case_classes diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_interfaces.py b/src/python/grpcio/tests/unit/framework/interfaces/base/test_interfaces.py index 02426ab846..84afd24d47 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/base/test_interfaces.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/base/test_interfaces.py @@ -131,7 +131,7 @@ class Implementation(object): @abc.abstractmethod def service_initial_metadata(self): - """Provices an operation's service-side initial metadata. + """Provides an operation's service-side initial metadata. Returns: A value to use for an operation's service-side initial metadata, or diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_3069_test_constant.py b/src/python/grpcio/tests/unit/framework/interfaces/face/_3069_test_constant.py index 363d9ce8f1..1ea356c0bf 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_3069_test_constant.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/_3069_test_constant.py @@ -30,7 +30,7 @@ """A test constant working around issue 3069.""" # test_constants is referenced from specification in this module. -from grpc_test.framework.common import test_constants # pylint: disable=unused-import +from tests.unit.framework.common import test_constants # pylint: disable=unused-import # TODO(issue 3069): Replace uses of this constant with # test_constants.SHORT_TIMEOUT. diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/__init__.py b/src/python/grpcio/tests/unit/framework/interfaces/face/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/__init__.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_blocking_invocation_inline_service.py b/src/python/grpcio/tests/unit/framework/interfaces/face/_blocking_invocation_inline_service.py index 2d2a081955..3bcefa601d 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_blocking_invocation_inline_service.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/_blocking_invocation_inline_service.py @@ -34,13 +34,13 @@ import unittest # test_interfaces is referenced from specification in this module. from grpc.framework.interfaces.face import face -from grpc_test.framework.common import test_constants -from grpc_test.framework.common import test_control -from grpc_test.framework.common import test_coverage -from grpc_test.framework.interfaces.face import _3069_test_constant -from grpc_test.framework.interfaces.face import _digest -from grpc_test.framework.interfaces.face import _stock_service -from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import +from tests.unit.framework.common import test_constants +from tests.unit.framework.common import test_control +from tests.unit.framework.common import test_coverage +from tests.unit.framework.interfaces.face import _3069_test_constant +from tests.unit.framework.interfaces.face import _digest +from tests.unit.framework.interfaces.face import _stock_service +from tests.unit.framework.interfaces.face import test_interfaces # pylint: disable=unused-import class TestCase(test_coverage.Coverage, unittest.TestCase): diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_digest.py b/src/python/grpcio/tests/unit/framework/interfaces/face/_digest.py index da56ed7b27..9304b6b1db 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_digest.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/_digest.py @@ -39,9 +39,9 @@ from grpc.framework.common import style from grpc.framework.foundation import stream from grpc.framework.foundation import stream_util from grpc.framework.interfaces.face import face -from grpc_test.framework.common import test_control # pylint: disable=unused-import -from grpc_test.framework.interfaces.face import _service # pylint: disable=unused-import -from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import +from tests.unit.framework.common import test_control # pylint: disable=unused-import +from tests.unit.framework.interfaces.face import _service # pylint: disable=unused-import +from tests.unit.framework.interfaces.face import test_interfaces # pylint: disable=unused-import _IDENTITY = lambda x: x diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_event_invocation_synchronous_event_service.py b/src/python/grpcio/tests/unit/framework/interfaces/face/_event_invocation_synchronous_event_service.py index 7cb273bf78..34db6c3e55 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_event_invocation_synchronous_event_service.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/_event_invocation_synchronous_event_service.py @@ -34,14 +34,14 @@ import unittest # test_interfaces is referenced from specification in this module. from grpc.framework.interfaces.face import face -from grpc_test.framework.common import test_constants -from grpc_test.framework.common import test_control -from grpc_test.framework.common import test_coverage -from grpc_test.framework.interfaces.face import _3069_test_constant -from grpc_test.framework.interfaces.face import _digest -from grpc_test.framework.interfaces.face import _receiver -from grpc_test.framework.interfaces.face import _stock_service -from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import +from tests.unit.framework.common import test_constants +from tests.unit.framework.common import test_control +from tests.unit.framework.common import test_coverage +from tests.unit.framework.interfaces.face import _3069_test_constant +from tests.unit.framework.interfaces.face import _digest +from tests.unit.framework.interfaces.face import _receiver +from tests.unit.framework.interfaces.face import _stock_service +from tests.unit.framework.interfaces.face import test_interfaces # pylint: disable=unused-import class TestCase(test_coverage.Coverage, unittest.TestCase): diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py b/src/python/grpcio/tests/unit/framework/interfaces/face/_future_invocation_asynchronous_event_service.py index 3032736975..c178f2f108 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_future_invocation_asynchronous_event_service.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/_future_invocation_asynchronous_event_service.py @@ -37,13 +37,13 @@ import unittest # test_interfaces is referenced from specification in this module. from grpc.framework.foundation import logging_pool from grpc.framework.interfaces.face import face -from grpc_test.framework.common import test_constants -from grpc_test.framework.common import test_control -from grpc_test.framework.common import test_coverage -from grpc_test.framework.interfaces.face import _3069_test_constant -from grpc_test.framework.interfaces.face import _digest -from grpc_test.framework.interfaces.face import _stock_service -from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import +from tests.unit.framework.common import test_constants +from tests.unit.framework.common import test_control +from tests.unit.framework.common import test_coverage +from tests.unit.framework.interfaces.face import _3069_test_constant +from tests.unit.framework.interfaces.face import _digest +from tests.unit.framework.interfaces.face import _stock_service +from tests.unit.framework.interfaces.face import test_interfaces # pylint: disable=unused-import class _PauseableIterator(object): diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_invocation.py b/src/python/grpcio/tests/unit/framework/interfaces/face/_invocation.py index 448e845a08..448e845a08 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_invocation.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/_invocation.py diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_receiver.py b/src/python/grpcio/tests/unit/framework/interfaces/face/_receiver.py index 2e444ff09d..2e444ff09d 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_receiver.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/_receiver.py diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_service.py b/src/python/grpcio/tests/unit/framework/interfaces/face/_service.py index e25b8a038c..28941e2ad0 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_service.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/_service.py @@ -33,7 +33,7 @@ import abc # face is referenced from specification in this module. from grpc.framework.interfaces.face import face # pylint: disable=unused-import -from grpc_test.framework.interfaces.face import test_interfaces +from tests.unit.framework.interfaces.face import test_interfaces class UnaryUnaryTestMethodImplementation(test_interfaces.Method): diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_stock_service.py b/src/python/grpcio/tests/unit/framework/interfaces/face/_stock_service.py index 808e2c4e36..5299655bb3 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/_stock_service.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/_stock_service.py @@ -32,9 +32,9 @@ from grpc.framework.common import cardinality from grpc.framework.foundation import abandonment from grpc.framework.foundation import stream -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.face import _service -from grpc_test._junkdrawer import stock_pb2 +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.face import _service +from tests.unit._junkdrawer import stock_pb2 _STOCK_GROUP_NAME = 'Stock' _SYMBOL_FORMAT = 'test symbol:%03d' diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_cases.py b/src/python/grpcio/tests/unit/framework/interfaces/face/test_cases.py index ca623662f7..462829b660 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_cases.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/test_cases.py @@ -33,11 +33,11 @@ import unittest # pylint: disable=unused-import # test_interfaces is referenced from specification in this module. -from grpc_test.framework.interfaces.face import _blocking_invocation_inline_service -from grpc_test.framework.interfaces.face import _event_invocation_synchronous_event_service -from grpc_test.framework.interfaces.face import _future_invocation_asynchronous_event_service -from grpc_test.framework.interfaces.face import _invocation -from grpc_test.framework.interfaces.face import test_interfaces # pylint: disable=unused-import +from tests.unit.framework.interfaces.face import _blocking_invocation_inline_service +from tests.unit.framework.interfaces.face import _event_invocation_synchronous_event_service +from tests.unit.framework.interfaces.face import _future_invocation_asynchronous_event_service +from tests.unit.framework.interfaces.face import _invocation +from tests.unit.framework.interfaces.face import test_interfaces # pylint: disable=unused-import _TEST_CASE_SUPERCLASSES = ( _blocking_invocation_inline_service.TestCase, @@ -63,5 +63,7 @@ def test_cases(implementation): test_case_classes.append( type(invoker_constructor.name() + super_class.NAME, (super_class,), {'implementation': implementation, - 'invoker_constructor': invoker_constructor})) + 'invoker_constructor': invoker_constructor, + '__module__': implementation.__module__, + })) return test_case_classes diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_interfaces.py b/src/python/grpcio/tests/unit/framework/interfaces/face/test_interfaces.py index b2b5c10fa6..b2b5c10fa6 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/face/test_interfaces.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/face/test_interfaces.py diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py b/src/python/grpcio/tests/unit/framework/interfaces/links/__init__.py index 7086519106..7086519106 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/links/__init__.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/links/__init__.py diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py b/src/python/grpcio/tests/unit/framework/interfaces/links/test_cases.py index ecf49d9cdb..dace6c23f3 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_cases.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/links/test_cases.py @@ -34,8 +34,8 @@ import abc import unittest # pylint: disable=unused-import from grpc.framework.interfaces.links import links -from grpc_test.framework.common import test_constants -from grpc_test.framework.interfaces.links import test_utilities +from tests.unit.framework.common import test_constants +from tests.unit.framework.interfaces.links import test_utilities def at_least_n_payloads_received_predicate(n): diff --git a/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py b/src/python/grpcio/tests/unit/framework/interfaces/links/test_utilities.py index 39c7f2fc63..39c7f2fc63 100644 --- a/src/python/grpcio_test/grpc_test/framework/interfaces/links/test_utilities.py +++ b/src/python/grpcio/tests/unit/framework/interfaces/links/test_utilities.py diff --git a/src/python/grpcio_test/grpc_test/resources.py b/src/python/grpcio/tests/unit/resources.py index 2c3045313d..023cdb155f 100644 --- a/src/python/grpcio_test/grpc_test/resources.py +++ b/src/python/grpcio/tests/unit/resources.py @@ -43,10 +43,6 @@ def test_root_certificates(): __name__, _ROOT_CERTIFICATES_RESOURCE_PATH) -def prod_root_certificates(): - return open(os.environ['SSL_CERT_FILE'], mode='rb').read() - - def private_key(): return pkg_resources.resource_string(__name__, _PRIVATE_KEY_RESOURCE_PATH) diff --git a/src/python/grpcio_test/grpc_test/test_common.py b/src/python/grpcio/tests/unit/test_common.py index 29431bfb9d..29431bfb9d 100644 --- a/src/python/grpcio_test/grpc_test/test_common.py +++ b/src/python/grpcio/tests/unit/test_common.py diff --git a/src/python/grpcio/tox.ini b/src/python/grpcio/tox.ini new file mode 100644 index 0000000000..bfb1ca0cfa --- /dev/null +++ b/src/python/grpcio/tox.ini @@ -0,0 +1,19 @@ +# Tox (http://tox.testrun.org/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +skipsdist = true +envlist = py27 + +[testenv] +commands = + {envpython} setup.py build_py + {envpython} setup.py test + coverage combine + coverage html --include='grpc/*' --omit='grpc/framework/alpha/*','grpc/early_adopter/*','grpc/framework/base/*','grpc/framework/face/*','grpc/_adapter/fore.py','grpc/_adapter/rear.py' + coverage report --include='grpc/*' --omit='grpc/framework/alpha/*','grpc/early_adopter/*','grpc/framework/base/*','grpc/framework/face/*','grpc/_adapter/fore.py','grpc/_adapter/rear.py' +deps = + -rrequirements.txt +passenv = * diff --git a/src/python/grpcio_test/.gitignore b/src/python/grpcio_test/.gitignore deleted file mode 100644 index 6158313bde..0000000000 --- a/src/python/grpcio_test/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -MANIFEST -*.egg-info/ -build/ -dist/ -*.egg -*.egg/ -*.eggs/ -*_pb2.py -.coverage -.coverage.* -.cache/ -nosetests.xml diff --git a/src/python/grpcio_test/MANIFEST.in b/src/python/grpcio_test/MANIFEST.in deleted file mode 100644 index c9327307dc..0000000000 --- a/src/python/grpcio_test/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -graft grpc_interop -graft grpc_test -include commands.py -include requirements.txt diff --git a/src/python/grpcio_test/commands.py b/src/python/grpcio_test/commands.py deleted file mode 100644 index edaa2aa72d..0000000000 --- a/src/python/grpcio_test/commands.py +++ /dev/null @@ -1,106 +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. - -"""Provides distutils command classes for the GRPC Python test setup process.""" - -import distutils -import os -import os.path -import subprocess -import sys - -import setuptools -from setuptools.command import build_py - - -class RunTests(setuptools.Command): - """Command to run all tests via py.test.""" - - description = '' - user_options = [('pytest-args=', 'a', 'arguments to pass to py.test')] - - def initialize_options(self): - self.pytest_args = [] - - def finalize_options(self): - pass - - def run(self): - # We import here to ensure that setup.py has had a chance to install the - # relevant package eggs first. - import pytest - - self.run_command('build_proto_modules') - result = pytest.main(self.pytest_args) - if result != 0: - raise SystemExit(result) - - -class BuildProtoModules(setuptools.Command): - """Command to generate project *_pb2.py modules from proto files.""" - - description = '' - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - self.protoc_command = distutils.spawn.find_executable('protoc') - self.grpc_python_plugin_command = distutils.spawn.find_executable( - 'grpc_python_plugin') - - def run(self): - paths = [] - root_directory = os.getcwd() - for walk_root, directories, filenames in os.walk(root_directory): - for filename in filenames: - if filename.endswith('.proto'): - paths.append(os.path.join(walk_root, filename)) - command = [ - self.protoc_command, - '--plugin=protoc-gen-python-grpc={}'.format( - self.grpc_python_plugin_command), - '-I {}'.format(root_directory), - '--python_out={}'.format(root_directory), - '--python-grpc_out={}'.format(root_directory), - ] + paths - try: - subprocess.check_output(' '.join(command), cwd=root_directory, shell=True, - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError as e: - raise Exception('{}\nOutput:\n{}'.format(e.message, e.output)) - - -class BuildPy(build_py.build_py): - """Custom project build command.""" - - def run(self): - self.run_command('build_proto_modules') - build_py.build_py.run(self) diff --git a/src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py b/src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py deleted file mode 100644 index f1bec238cf..0000000000 --- a/src/python/grpcio_test/grpc_test/_cython/adapter_low_test.py +++ /dev/null @@ -1,187 +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. - -# Fork of grpc._adapter._low_test; the grpc._cython.types adapter in -# grpc._cython.low should transparently support the semantics expected of -# grpc._adapter._low. - -import time -import unittest - -from grpc._adapter import _types -from grpc._cython import adapter_low as _low - - -class InsecureServerInsecureClient(unittest.TestCase): - - def setUp(self): - self.server_completion_queue = _low.CompletionQueue() - self.server = _low.Server(self.server_completion_queue, []) - self.port = self.server.add_http2_port('[::]:0') - self.client_completion_queue = _low.CompletionQueue() - self.client_channel = _low.Channel('localhost:%d'%self.port, []) - - self.server.start() - - def tearDown(self): - self.server.shutdown() - del self.client_channel - - self.client_completion_queue.shutdown() - while (self.client_completion_queue.next().type != - _types.EventType.QUEUE_SHUTDOWN): - pass - self.server_completion_queue.shutdown() - while (self.server_completion_queue.next().type != - _types.EventType.QUEUE_SHUTDOWN): - pass - - del self.client_completion_queue - del self.server_completion_queue - del self.server - - @unittest.skip('TODO(atash): implement grpc._cython.adapter_low') - def testEcho(self): - DEADLINE = time.time()+5 - DEADLINE_TOLERANCE = 0.25 - CLIENT_METADATA_ASCII_KEY = 'key' - CLIENT_METADATA_ASCII_VALUE = 'val' - CLIENT_METADATA_BIN_KEY = 'key-bin' - CLIENT_METADATA_BIN_VALUE = b'\0'*1000 - SERVER_INITIAL_METADATA_KEY = 'init_me_me_me' - SERVER_INITIAL_METADATA_VALUE = 'whodawha?' - SERVER_TRAILING_METADATA_KEY = 'california_is_in_a_drought' - SERVER_TRAILING_METADATA_VALUE = 'zomg it is' - SERVER_STATUS_CODE = _types.StatusCode.OK - SERVER_STATUS_DETAILS = 'our work is never over' - REQUEST = 'in death a member of project mayhem has a name' - RESPONSE = 'his name is robert paulson' - METHOD = 'twinkies' - HOST = 'hostess' - server_request_tag = object() - request_call_result = self.server.request_call(self.server_completion_queue, - server_request_tag) - - self.assertEqual(_types.CallError.OK, request_call_result) - - client_call_tag = object() - client_call = self.client_channel.create_call(self.client_completion_queue, - METHOD, HOST, DEADLINE) - client_initial_metadata = [ - (CLIENT_METADATA_ASCII_KEY, CLIENT_METADATA_ASCII_VALUE), - (CLIENT_METADATA_BIN_KEY, CLIENT_METADATA_BIN_VALUE)] - client_start_batch_result = client_call.start_batch([ - _types.OpArgs.send_initial_metadata(client_initial_metadata), - _types.OpArgs.send_message(REQUEST), - _types.OpArgs.send_close_from_client(), - _types.OpArgs.recv_initial_metadata(), - _types.OpArgs.recv_message(), - _types.OpArgs.recv_status_on_client() - ], client_call_tag) - self.assertEqual(_types.CallError.OK, client_start_batch_result) - - request_event = self.server_completion_queue.next(DEADLINE) - self.assertEqual(_types.EventType.OP_COMPLETE, request_event.type) - self.assertIsInstance(request_event.call, _low.Call) - self.assertIs(server_request_tag, request_event.tag) - self.assertEqual(1, len(request_event.results)) - self.assertEqual(dict(client_initial_metadata), - dict(request_event.results[0].initial_metadata)) - self.assertEqual(METHOD, request_event.call_details.method) - self.assertEqual(HOST, request_event.call_details.host) - self.assertLess(abs(DEADLINE - request_event.call_details.deadline), - DEADLINE_TOLERANCE) - - server_call_tag = object() - server_call = request_event.call - server_initial_metadata = [ - (SERVER_INITIAL_METADATA_KEY, SERVER_INITIAL_METADATA_VALUE)] - server_trailing_metadata = [ - (SERVER_TRAILING_METADATA_KEY, SERVER_TRAILING_METADATA_VALUE)] - server_start_batch_result = server_call.start_batch([ - _types.OpArgs.send_initial_metadata(server_initial_metadata), - _types.OpArgs.recv_message(), - _types.OpArgs.send_message(RESPONSE), - _types.OpArgs.recv_close_on_server(), - _types.OpArgs.send_status_from_server( - server_trailing_metadata, SERVER_STATUS_CODE, SERVER_STATUS_DETAILS) - ], server_call_tag) - self.assertEqual(_types.CallError.OK, server_start_batch_result) - - client_event = self.client_completion_queue.next(DEADLINE) - server_event = self.server_completion_queue.next(DEADLINE) - - self.assertEqual(6, len(client_event.results)) - found_client_op_types = set() - for client_result in client_event.results: - # we expect each op type to be unique - self.assertNotIn(client_result.type, found_client_op_types) - found_client_op_types.add(client_result.type) - if client_result.type == _types.OpType.RECV_INITIAL_METADATA: - self.assertEqual(dict(server_initial_metadata), - dict(client_result.initial_metadata)) - elif client_result.type == _types.OpType.RECV_MESSAGE: - self.assertEqual(RESPONSE, client_result.message) - elif client_result.type == _types.OpType.RECV_STATUS_ON_CLIENT: - self.assertEqual(dict(server_trailing_metadata), - dict(client_result.trailing_metadata)) - self.assertEqual(SERVER_STATUS_DETAILS, client_result.status.details) - self.assertEqual(SERVER_STATUS_CODE, client_result.status.code) - self.assertEqual(set([ - _types.OpType.SEND_INITIAL_METADATA, - _types.OpType.SEND_MESSAGE, - _types.OpType.SEND_CLOSE_FROM_CLIENT, - _types.OpType.RECV_INITIAL_METADATA, - _types.OpType.RECV_MESSAGE, - _types.OpType.RECV_STATUS_ON_CLIENT - ]), found_client_op_types) - - self.assertEqual(5, len(server_event.results)) - found_server_op_types = set() - for server_result in server_event.results: - self.assertNotIn(client_result.type, found_server_op_types) - found_server_op_types.add(server_result.type) - if server_result.type == _types.OpType.RECV_MESSAGE: - self.assertEqual(REQUEST, server_result.message) - elif server_result.type == _types.OpType.RECV_CLOSE_ON_SERVER: - self.assertFalse(server_result.cancelled) - self.assertEqual(set([ - _types.OpType.SEND_INITIAL_METADATA, - _types.OpType.RECV_MESSAGE, - _types.OpType.SEND_MESSAGE, - _types.OpType.RECV_CLOSE_ON_SERVER, - _types.OpType.SEND_STATUS_FROM_SERVER - ]), found_server_op_types) - - del client_call - del server_call - - -if __name__ == '__main__': - unittest.main(verbosity=2) diff --git a/src/python/grpcio_test/grpc_test/conftest.py b/src/python/grpcio_test/grpc_test/conftest.py deleted file mode 100644 index 357320ec64..0000000000 --- a/src/python/grpcio_test/grpc_test/conftest.py +++ /dev/null @@ -1,60 +0,0 @@ -import types -import unittest - -import pytest - - -class LoadTestsSuiteCollector(pytest.Collector): - - def __init__(self, name, parent, suite): - super(LoadTestsSuiteCollector, self).__init__(name, parent=parent) - self.suite = suite - self.obj = suite - - def collect(self): - collected = [] - for case in self.suite: - if isinstance(case, unittest.TestCase): - collected.append(LoadTestsCase(case.id(), self, case)) - elif isinstance(case, unittest.TestSuite): - collected.append( - LoadTestsSuiteCollector('suite_child_of_mine', self, case)) - return collected - - def reportinfo(self): - return str(self.suite) - - -class LoadTestsCase(pytest.Function): - - def __init__(self, name, parent, item): - super(LoadTestsCase, self).__init__(name, parent, callobj=self._item_run) - self.item = item - - def _item_run(self): - result = unittest.TestResult() - self.item(result) - if result.failures: - test_method, trace = result.failures[0] - pytest.fail(trace, False) - elif result.errors: - test_method, trace = result.errors[0] - pytest.fail(trace, False) - elif result.skipped: - test_method, reason = result.skipped[0] - pytest.skip(reason) - - -def pytest_pycollect_makeitem(collector, name, obj): - if name == 'load_tests' and isinstance(obj, types.FunctionType): - suite = unittest.TestSuite() - loader = unittest.TestLoader() - pattern = '*' - try: - # Check that the 'load_tests' object is actually a callable that actually - # accepts the arguments expected for the load_tests protocol. - suite = obj(loader, suite, pattern) - except Exception as e: - return None - else: - return LoadTestsSuiteCollector(name, collector, suite) diff --git a/src/python/grpcio_test/requirements.txt b/src/python/grpcio_test/requirements.txt deleted file mode 100644 index fea80ca07f..0000000000 --- a/src/python/grpcio_test/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -grpcio>=0.11.0b0 -oauth2client>=1.4.7 -protobuf>=3.0.0a3 -pytest>=2.6 -pytest-cov>=2.0 -pytest-xdist>=1.11 diff --git a/src/python/grpcio_test/setup.cfg b/src/python/grpcio_test/setup.cfg deleted file mode 100644 index 3be93cb918..0000000000 --- a/src/python/grpcio_test/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -python_files = *_test.py diff --git a/src/ruby/bin/apis/pubsub_demo.rb b/src/ruby/bin/apis/pubsub_demo.rb index 003e91a6b3..143ecc7a8f 100755 --- a/src/ruby/bin/apis/pubsub_demo.rb +++ b/src/ruby/bin/apis/pubsub_demo.rb @@ -32,7 +32,6 @@ # pubsub_demo demos accesses the Google PubSub API via its gRPC interface # # $ GOOGLE_APPLICATION_CREDENTIALS=<path_to_service_account_key_file> \ -# SSL_CERT_FILE=<path/to/ssl/certs> \ # path/to/pubsub_demo.rb \ # [--action=<chosen_demo_action> ] # @@ -55,18 +54,9 @@ require 'google/protobuf/empty' require 'tech/pubsub/proto/pubsub' require 'tech/pubsub/proto/pubsub_services' -# loads the certificates used to access the test server securely. -def load_prod_cert - fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil? - p "loading prod certs from #{ENV['SSL_CERT_FILE']}" - File.open(ENV['SSL_CERT_FILE']) do |f| - return f.read - end -end - # creates a SSL Credentials from the production certificates. def ssl_creds - GRPC::Core::ChannelCredentials.new(load_prod_cert) + GRPC::Core::ChannelCredentials.new() end # Builds the metadata authentication update proc. @@ -80,8 +70,9 @@ def publisher_stub(opts) address = "#{opts.host}:#{opts.port}" stub_clz = Tech::Pubsub::PublisherService::Stub # shorter GRPC.logger.info("... access PublisherService at #{address}") - stub_clz.new(address, - creds: ssl_creds, update_metadata: auth_proc(opts), + call_creds = GRPC::Core::CallCredentials.new(auth_proc(opts)) + combined_creds = ssl_creds.compose(call_creds) + stub_clz.new(address, creds: combined_creds, GRPC::Core::Channel::SSL_TARGET => opts.host) end @@ -90,8 +81,9 @@ def subscriber_stub(opts) address = "#{opts.host}:#{opts.port}" stub_clz = Tech::Pubsub::SubscriberService::Stub # shorter GRPC.logger.info("... access SubscriberService at #{address}") - stub_clz.new(address, - creds: ssl_creds, update_metadata: auth_proc(opts), + call_creds = GRPC::Core::CallCredentials.new(auth_proc(opts)) + combined_creds = ssl_creds.compose(call_creds) + stub_clz.new(address, creds: combined_creds, GRPC::Core::Channel::SSL_TARGET => opts.host) end diff --git a/src/ruby/bin/math.proto b/src/ruby/bin/math.proto deleted file mode 100755 index 311e148c02..0000000000 --- a/src/ruby/bin/math.proto +++ /dev/null @@ -1,80 +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. - -syntax = "proto3"; - -package math; - -message DivArgs { - int64 dividend = 1; - int64 divisor = 2; -} - -message DivReply { - int64 quotient = 1; - int64 remainder = 2; -} - -message FibArgs { - int64 limit = 1; -} - -message Num { - int64 num = 1; -} - -message FibReply { - int64 count = 1; -} - -service Math { - // Div divides args.dividend by args.divisor and returns the quotient and - // remainder. - rpc Div (DivArgs) returns (DivReply) { - } - - // DivMany accepts an arbitrary number of division args from the client stream - // and sends back the results in the reply stream. The stream continues until - // the client closes its end; the server does the same after sending all the - // replies. The stream ends immediately if either end aborts. - rpc DivMany (stream DivArgs) returns (stream DivReply) { - } - - // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib - // generates up to limit numbers; otherwise it continues until the call is - // canceled. Unlike Fib above, Fib has no final FibReply. - rpc Fib (FibArgs) returns (stream Num) { - } - - // Sum sums a stream of numbers, returning the final result once the stream - // is closed. - rpc Sum (stream Num) returns (Num) { - } -} diff --git a/src/ruby/bin/math.rb b/src/ruby/bin/math.rb index 323993ed43..60429a1505 100755 --- a/src/ruby/bin/math.rb +++ b/src/ruby/bin/math.rb @@ -1,32 +1,3 @@ -# 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. - # Generated by the protocol buffer compiler. DO NOT EDIT! # source: math.proto diff --git a/src/ruby/bin/math_services.rb b/src/ruby/bin/math_services.rb index cf58a53913..2d482129c2 100755 --- a/src/ruby/bin/math_services.rb +++ b/src/ruby/bin/math_services.rb @@ -1,32 +1,3 @@ -# 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. - # Generated by the protocol buffer compiler. DO NOT EDIT! # Source: math.proto for package 'math' diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c index 40364328ee..1647d9b484 100644 --- a/src/ruby/ext/grpc/rb_call.c +++ b/src/ruby/ext/grpc/rb_call.c @@ -39,6 +39,7 @@ #include <grpc/support/alloc.h> #include "rb_byte_buffer.h" +#include "rb_call_credentials.h" #include "rb_completion_queue.h" #include "rb_grpc.h" @@ -279,6 +280,26 @@ static VALUE grpc_rb_call_set_write_flag(VALUE self, VALUE write_flag) { return rb_ivar_set(self, id_write_flag, write_flag); } +/* + call-seq: + call.set_credentials call_credentials + + Sets credentials on a call */ +static VALUE grpc_rb_call_set_credentials(VALUE self, VALUE credentials) { + grpc_call *call = NULL; + grpc_call_credentials *creds; + grpc_call_error err; + TypedData_Get_Struct(self, grpc_call, &grpc_call_data_type, call); + creds = grpc_rb_get_wrapped_call_credentials(credentials); + err = grpc_call_set_credentials(call, creds); + if (err != GRPC_CALL_OK) { + rb_raise(grpc_rb_eCallError, + "grpc_call_set_credentials failed with %s (code=%d)", + grpc_call_error_detail_of(err), err); + } + return Qnil; +} + /* grpc_rb_md_ary_fill_hash_cb is the hash iteration callback used to fill grpc_metadata_array. @@ -347,7 +368,7 @@ static int grpc_rb_md_ary_capacity_hash_cb(VALUE key, VALUE val, /* grpc_rb_md_ary_convert converts a ruby metadata hash into a grpc_metadata_array. */ -static void grpc_rb_md_ary_convert(VALUE md_ary_hash, +void grpc_rb_md_ary_convert(VALUE md_ary_hash, grpc_metadata_array *md_ary) { VALUE md_ary_obj = Qnil; if (md_ary_hash == Qnil) { @@ -795,6 +816,8 @@ void Init_grpc_call() { rb_define_method(grpc_rb_cCall, "write_flag", grpc_rb_call_get_write_flag, 0); rb_define_method(grpc_rb_cCall, "write_flag=", grpc_rb_call_set_write_flag, 1); + rb_define_method(grpc_rb_cCall, "set_credentials!", + grpc_rb_call_set_credentials, 1); /* Ids used to support call attributes */ id_metadata = rb_intern("metadata"); diff --git a/src/ruby/ext/grpc/rb_call.h b/src/ruby/ext/grpc/rb_call.h index 1d2fbc3580..24adb3477b 100644 --- a/src/ruby/ext/grpc/rb_call.h +++ b/src/ruby/ext/grpc/rb_call.h @@ -50,6 +50,12 @@ const char* grpc_call_error_detail_of(grpc_call_error err); /* Converts a metadata array to a hash. */ VALUE grpc_rb_md_ary_to_h(grpc_metadata_array *md_ary); +/* grpc_rb_md_ary_convert converts a ruby metadata hash into + a grpc_metadata_array. +*/ +void grpc_rb_md_ary_convert(VALUE md_ary_hash, + grpc_metadata_array *md_ary); + /* grpc_rb_eCallError is the ruby class of the exception thrown during call operations. */ extern VALUE grpc_rb_eCallError; diff --git a/src/ruby/ext/grpc/rb_call_credentials.c b/src/ruby/ext/grpc/rb_call_credentials.c new file mode 100644 index 0000000000..acc5472799 --- /dev/null +++ b/src/ruby/ext/grpc/rb_call_credentials.c @@ -0,0 +1,312 @@ +/* + * + * 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 "rb_call_credentials.h" + +#include <ruby/ruby.h> +#include <ruby/thread.h> + +#include <grpc/grpc.h> +#include <grpc/grpc_security.h> + +#include "rb_call.h" +#include "rb_grpc.h" + +/* grpc_rb_cCallCredentials is the ruby class that proxies + * grpc_call_credentials */ +static VALUE grpc_rb_cCallCredentials = Qnil; + +/* grpc_rb_call_credentials wraps a grpc_call_credentials. It provides a peer + * ruby object, 'mark' to minimize copying when a credential is created from + * ruby. */ +typedef struct grpc_rb_call_credentials { + /* Holder of ruby objects involved in contructing the credentials */ + VALUE mark; + + /* The actual credentials */ + grpc_call_credentials *wrapped; +} grpc_rb_call_credentials; + +typedef struct callback_params { + VALUE get_metadata; + grpc_auth_metadata_context context; + void *user_data; + grpc_credentials_plugin_metadata_cb callback; +} callback_params; + +static VALUE grpc_rb_call_credentials_callback(VALUE callback_args) { + VALUE result = rb_hash_new(); + VALUE metadata = rb_funcall(rb_ary_entry(callback_args, 0), rb_intern("call"), + 1, rb_ary_entry(callback_args, 1)); + rb_hash_aset(result, rb_str_new2("metadata"), metadata); + rb_hash_aset(result, rb_str_new2("status"), INT2NUM(GRPC_STATUS_OK)); + rb_hash_aset(result, rb_str_new2("details"), rb_str_new2("")); + return result; +} + +static VALUE grpc_rb_call_credentials_callback_rescue(VALUE args, + VALUE exception_object) { + VALUE result = rb_hash_new(); + rb_hash_aset(result, rb_str_new2("metadata"), Qnil); + /* Currently only gives the exception class name. It should be possible get + more details */ + rb_hash_aset(result, rb_str_new2("status"), + INT2NUM(GRPC_STATUS_PERMISSION_DENIED)); + rb_hash_aset(result, rb_str_new2("details"), + rb_str_new2(rb_obj_classname(exception_object))); + return result; +} + +static void *grpc_rb_call_credentials_callback_with_gil(void *param) { + callback_params *const params = (callback_params *)param; + VALUE auth_uri = rb_str_new_cstr(params->context.service_url); + /* Pass the arguments to the proc in a hash, which currently only has they key + 'auth_uri' */ + VALUE callback_args = rb_ary_new(); + VALUE args = rb_hash_new(); + VALUE result; + grpc_metadata_array md_ary; + grpc_status_code status; + VALUE details; + char *error_details; + grpc_metadata_array_init(&md_ary); + rb_hash_aset(args, ID2SYM(rb_intern("jwt_aud_uri")), auth_uri); + rb_ary_push(callback_args, params->get_metadata); + rb_ary_push(callback_args, args); + result = rb_rescue(grpc_rb_call_credentials_callback, callback_args, + grpc_rb_call_credentials_callback_rescue, Qnil); + // Both callbacks return a hash, so result should be a hash + grpc_rb_md_ary_convert(rb_hash_aref(result, rb_str_new2("metadata")), &md_ary); + status = NUM2INT(rb_hash_aref(result, rb_str_new2("status"))); + details = rb_hash_aref(result, rb_str_new2("details")); + error_details = StringValueCStr(details); + params->callback(params->user_data, md_ary.metadata, md_ary.count, status, + error_details); + grpc_metadata_array_destroy(&md_ary); + + return NULL; +} + +static void grpc_rb_call_credentials_plugin_get_metadata( + void *state, grpc_auth_metadata_context context, + grpc_credentials_plugin_metadata_cb cb, void *user_data) { + callback_params params; + params.get_metadata = (VALUE)state; + params.context = context; + params.user_data = user_data; + params.callback = cb; + + rb_thread_call_with_gvl(grpc_rb_call_credentials_callback_with_gil, + (void*)(¶ms)); +} + +static void grpc_rb_call_credentials_plugin_destroy(void *state) { + // Not sure what needs to be done here +} + +/* Destroys the credentials instances. */ +static void grpc_rb_call_credentials_free(void *p) { + grpc_rb_call_credentials *wrapper; + if (p == NULL) { + return; + } + wrapper = (grpc_rb_call_credentials *)p; + + /* Delete the wrapped object if the mark object is Qnil, which indicates that + * no other object is the actual owner. */ + if (wrapper->wrapped != NULL && wrapper->mark == Qnil) { + grpc_call_credentials_release(wrapper->wrapped); + wrapper->wrapped = NULL; + } + + xfree(p); +} + +/* Protects the mark object from GC */ +static void grpc_rb_call_credentials_mark(void *p) { + grpc_rb_call_credentials *wrapper = NULL; + if (p == NULL) { + return; + } + wrapper = (grpc_rb_call_credentials *)p; + + /* If it's not already cleaned up, mark the mark object */ + if (wrapper->mark != Qnil) { + rb_gc_mark(wrapper->mark); + } +} + +static rb_data_type_t grpc_rb_call_credentials_data_type = { + "grpc_call_credentials", + {grpc_rb_call_credentials_mark, grpc_rb_call_credentials_free, + GRPC_RB_MEMSIZE_UNAVAILABLE, {NULL, NULL}}, + NULL, + NULL, +#ifdef RUBY_TYPED_FREE_IMMEDIATELY + RUBY_TYPED_FREE_IMMEDIATELY +#endif +}; + +/* Allocates CallCredentials instances. + Provides safe initial defaults for the instance fields. */ +static VALUE grpc_rb_call_credentials_alloc(VALUE cls) { + grpc_rb_call_credentials *wrapper = ALLOC(grpc_rb_call_credentials); + wrapper->wrapped = NULL; + wrapper->mark = Qnil; + return TypedData_Wrap_Struct(cls, &grpc_rb_call_credentials_data_type, wrapper); +} + +/* Creates a wrapping object for a given call credentials. This should only be + * called with grpc_call_credentials objects that are not already associated + * with any Ruby object */ +VALUE grpc_rb_wrap_call_credentials(grpc_call_credentials *c) { + VALUE rb_wrapper; + grpc_rb_call_credentials *wrapper; + if (c == NULL) { + return Qnil; + } + rb_wrapper = grpc_rb_call_credentials_alloc(grpc_rb_cCallCredentials); + TypedData_Get_Struct(rb_wrapper, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, wrapper); + wrapper->wrapped = c; + return rb_wrapper; +} + +/* Clones CallCredentials instances. + Gives CallCredentials a consistent implementation of Ruby's object copy/dup + protocol. */ +static VALUE grpc_rb_call_credentials_init_copy(VALUE copy, VALUE orig) { + grpc_rb_call_credentials *orig_cred = NULL; + grpc_rb_call_credentials *copy_cred = NULL; + + if (copy == orig) { + return copy; + } + + /* Raise an error if orig is not a credentials object or a subclass. */ + if (TYPE(orig) != T_DATA || + RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_call_credentials_free) { + rb_raise(rb_eTypeError, "not a %s", + rb_obj_classname(grpc_rb_cCallCredentials)); + } + + TypedData_Get_Struct(orig, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, orig_cred); + TypedData_Get_Struct(copy, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, copy_cred); + + /* use ruby's MEMCPY to make a byte-for-byte copy of the credentials + * wrapper object. */ + MEMCPY(copy_cred, orig_cred, grpc_rb_call_credentials, 1); + return copy; +} + +/* The attribute used on the mark object to hold the callback */ +static ID id_callback; + +/* + call-seq: + creds = Credentials.new auth_proc + proc: (required) Proc that generates auth metadata + Initializes CallCredential instances. */ +static VALUE grpc_rb_call_credentials_init(VALUE self, VALUE proc) { + grpc_rb_call_credentials *wrapper = NULL; + grpc_call_credentials *creds = NULL; + grpc_metadata_credentials_plugin plugin; + + TypedData_Get_Struct(self, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, wrapper); + + plugin.get_metadata = grpc_rb_call_credentials_plugin_get_metadata; + plugin.destroy = grpc_rb_call_credentials_plugin_destroy; + if (!rb_obj_is_proc(proc)) { + rb_raise(rb_eTypeError, "Argument to CallCredentials#new must be a proc"); + return Qnil; + } + plugin.state = (void*)proc; + plugin.type = ""; + + creds = grpc_metadata_credentials_create_from_plugin(plugin, NULL); + if (creds == NULL) { + rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why"); + return Qnil; + } + + wrapper->wrapped = creds; + rb_ivar_set(self, id_callback, proc); + + return self; +} + +static VALUE grpc_rb_call_credentials_compose(int argc, VALUE *argv, + VALUE self) { + grpc_call_credentials *creds; + grpc_call_credentials *other; + if (argc == 0) { + return self; + } + creds = grpc_rb_get_wrapped_call_credentials(self); + for (int i = 0; i < argc; i++) { + other = grpc_rb_get_wrapped_call_credentials(argv[i]); + creds = grpc_composite_call_credentials_create(creds, other, NULL); + } + return grpc_rb_wrap_call_credentials(creds); +} + +void Init_grpc_call_credentials() { + grpc_rb_cCallCredentials = + rb_define_class_under(grpc_rb_mGrpcCore, "CallCredentials", rb_cObject); + + /* Allocates an object managed by the ruby runtime */ + rb_define_alloc_func(grpc_rb_cCallCredentials, + grpc_rb_call_credentials_alloc); + + /* Provides a ruby constructor and support for dup/clone. */ + rb_define_method(grpc_rb_cCallCredentials, "initialize", + grpc_rb_call_credentials_init, 1); + rb_define_method(grpc_rb_cCallCredentials, "initialize_copy", + grpc_rb_call_credentials_init_copy, 1); + rb_define_method(grpc_rb_cCallCredentials, "compose", + grpc_rb_call_credentials_compose, -1); + + id_callback = rb_intern("__callback"); +} + +/* Gets the wrapped grpc_call_credentials from the ruby wrapper */ +grpc_call_credentials *grpc_rb_get_wrapped_call_credentials(VALUE v) { + grpc_rb_call_credentials *wrapper = NULL; + TypedData_Get_Struct(v, grpc_rb_call_credentials, + &grpc_rb_call_credentials_data_type, + wrapper); + return wrapper->wrapped; +} diff --git a/src/core/channel/noop_filter.h b/src/ruby/ext/grpc/rb_call_credentials.h index ded9b33117..5350a8f7ff 100644 --- a/src/core/channel/noop_filter.h +++ b/src/ruby/ext/grpc/rb_call_credentials.h @@ -31,14 +31,16 @@ * */ -#ifndef GRPC_INTERNAL_CORE_CHANNEL_NOOP_FILTER_H -#define GRPC_INTERNAL_CORE_CHANNEL_NOOP_FILTER_H +#ifndef GRPC_RB_CALL_CREDENTIALS_H_ +#define GRPC_RB_CALL_CREDENTIALS_H_ -#include "src/core/channel/channel_stack.h" +#include <ruby/ruby.h> -/* No-op filter: simply takes everything it's given, and passes it on to the - next filter. Exists simply as a starting point that others can take and - customize for their own filters */ -extern const grpc_channel_filter grpc_no_op_filter; +#include <grpc/grpc_security.h> -#endif /* GRPC_INTERNAL_CORE_CHANNEL_NOOP_FILTER_H */ +/* Initializes the ruby CallCredentials class. */ +void Init_grpc_call_credentials(); + +grpc_call_credentials* grpc_rb_get_wrapped_call_credentials(VALUE v); + +#endif /* GRPC_RB_CALL_CREDENTIALS_H_ */ diff --git a/src/ruby/ext/grpc/rb_channel_credentials.c b/src/ruby/ext/grpc/rb_channel_credentials.c index 072a6f54ab..3530081c6b 100644 --- a/src/ruby/ext/grpc/rb_channel_credentials.c +++ b/src/ruby/ext/grpc/rb_channel_credentials.c @@ -37,7 +37,9 @@ #include <grpc/grpc.h> #include <grpc/grpc_security.h> +#include <grpc/support/log.h> +#include "rb_call_credentials.h" #include "rb_grpc.h" /* grpc_rb_cChannelCredentials is the ruby class that proxies @@ -107,6 +109,22 @@ static VALUE grpc_rb_channel_credentials_alloc(VALUE cls) { return TypedData_Wrap_Struct(cls, &grpc_rb_channel_credentials_data_type, wrapper); } +/* Creates a wrapping object for a given channel credentials. This should only + * be called with grpc_channel_credentials objects that are not already + * associated with any Ruby object. */ +VALUE grpc_rb_wrap_channel_credentials(grpc_channel_credentials *c) { + VALUE rb_wrapper; + grpc_rb_channel_credentials *wrapper; + if (c == NULL) { + return Qnil; + } + rb_wrapper = grpc_rb_channel_credentials_alloc(grpc_rb_cChannelCredentials); + TypedData_Get_Struct(rb_wrapper, grpc_rb_channel_credentials, + &grpc_rb_channel_credentials_data_type, wrapper); + wrapper->wrapped = c; + return rb_wrapper; +} + /* Clones ChannelCredentials instances. Gives ChannelCredentials a consistent implementation of Ruby's object copy/dup protocol. */ @@ -148,11 +166,13 @@ static ID id_pem_cert_chain; /* call-seq: - creds1 = Credentials.new(pem_root_certs) + creds1 = Credentials.new() + ... + creds2 = Credentials.new(pem_root_certs) ... - creds2 = Credentials.new(pem_root_certs, pem_private_key, + creds3 = Credentials.new(pem_root_certs, pem_private_key, pem_cert_chain) - pem_root_certs: (required) PEM encoding of the server root certificate + pem_root_certs: (optional) PEM encoding of the server root certificate pem_private_key: (optional) PEM encoding of the client's private key pem_cert_chain: (optional) PEM encoding of the client's cert chain Initializes Credential instances. */ @@ -163,26 +183,23 @@ static VALUE grpc_rb_channel_credentials_init(int argc, VALUE *argv, VALUE self) grpc_rb_channel_credentials *wrapper = NULL; grpc_channel_credentials *creds = NULL; grpc_ssl_pem_key_cert_pair key_cert_pair; + const char *pem_root_certs_cstr = NULL; MEMZERO(&key_cert_pair, grpc_ssl_pem_key_cert_pair, 1); - /* TODO: Remove mandatory arg when we support default roots. */ - /* "12" == 1 mandatory arg, 2 (credentials) is optional */ - rb_scan_args(argc, argv, "12", &pem_root_certs, &pem_private_key, + /* "03" == no mandatory arg, 3 optional */ + rb_scan_args(argc, argv, "03", &pem_root_certs, &pem_private_key, &pem_cert_chain); TypedData_Get_Struct(self, grpc_rb_channel_credentials, &grpc_rb_channel_credentials_data_type, wrapper); - if (pem_root_certs == Qnil) { - rb_raise(rb_eRuntimeError, - "could not create a credential: nil pem_root_certs"); - return Qnil; + if (pem_root_certs != Qnil) { + pem_root_certs_cstr = RSTRING_PTR(pem_root_certs); } if (pem_private_key == Qnil && pem_cert_chain == Qnil) { - creds = - grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), NULL, NULL); + creds = grpc_ssl_credentials_create(pem_root_certs_cstr, NULL, NULL); } else { key_cert_pair.private_key = RSTRING_PTR(pem_private_key); key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain); - creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs), + creds = grpc_ssl_credentials_create(pem_root_certs_cstr, &key_cert_pair, NULL); } if (creds == NULL) { @@ -199,6 +216,25 @@ static VALUE grpc_rb_channel_credentials_init(int argc, VALUE *argv, VALUE self) return self; } +static VALUE grpc_rb_channel_credentials_compose(int argc, VALUE *argv, + VALUE self) { + grpc_channel_credentials *creds; + grpc_call_credentials *other; + if (argc == 0) { + return self; + } + creds = grpc_rb_get_wrapped_channel_credentials(self); + for (int i = 0; i < argc; i++) { + other = grpc_rb_get_wrapped_call_credentials(argv[i]); + creds = grpc_composite_channel_credentials_create(creds, other, NULL); + if (creds == NULL) { + rb_raise(rb_eRuntimeError, + "Failed to compose channel and call credentials"); + } + } + return grpc_rb_wrap_channel_credentials(creds); +} + void Init_grpc_channel_credentials() { grpc_rb_cChannelCredentials = rb_define_class_under(grpc_rb_mGrpcCore, "ChannelCredentials", rb_cObject); @@ -212,6 +248,8 @@ void Init_grpc_channel_credentials() { grpc_rb_channel_credentials_init, -1); rb_define_method(grpc_rb_cChannelCredentials, "initialize_copy", grpc_rb_channel_credentials_init_copy, 1); + rb_define_method(grpc_rb_cChannelCredentials, "compose", + grpc_rb_channel_credentials_compose, -1); id_pem_cert_chain = rb_intern("__pem_cert_chain"); id_pem_private_key = rb_intern("__pem_private_key"); diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c index 7c7c2d3440..a752a5f879 100644 --- a/src/ruby/ext/grpc/rb_grpc.c +++ b/src/ruby/ext/grpc/rb_grpc.c @@ -41,6 +41,7 @@ #include <grpc/grpc.h> #include <grpc/support/time.h> #include "rb_call.h" +#include "rb_call_credentials.h" #include "rb_channel.h" #include "rb_channel_credentials.h" #include "rb_completion_queue.h" @@ -91,7 +92,7 @@ static ID id_tv_sec; static ID id_tv_nsec; /** - * grpc_rb_time_timeval creates a time_eval from a ruby time object. + * grpc_rb_time_timeval creates a timeval from a ruby time object. * * This func is copied from ruby source, MRI/source/time.c, which is published * under the same license as the ruby.h, on which the entire extensions is @@ -137,7 +138,7 @@ gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) { d += 1; f -= 1; } - t.tv_sec = (time_t)f; + t.tv_sec = (gpr_int64)f; if (f != t.tv_sec) { rb_raise(rb_eRangeError, "%f out of Time range", RFLOAT_VALUE(time)); @@ -318,6 +319,7 @@ void Init_grpc() { Init_grpc_channel(); Init_grpc_completion_queue(); Init_grpc_call(); + Init_grpc_call_credentials(); Init_grpc_channel_credentials(); Init_grpc_server(); Init_grpc_server_credentials(); diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec index 61cf18cf54..363abe9a46 100755 --- a/src/ruby/grpc.gemspec +++ b/src/ruby/grpc.gemspec @@ -39,6 +39,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rake-compiler', '~> 0.9' s.add_development_dependency 'rspec', '~> 3.2' s.add_development_dependency 'rubocop', '~> 0.30.0' + s.add_development_dependency 'signet', '~>0.6.0' s.extensions = %w(ext/grpc/extconf.rb) end diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb index 90aaa026ec..13100a614c 100644 --- a/src/ruby/lib/grpc/generic/client_stub.rb +++ b/src/ruby/lib/grpc/generic/client_stub.rb @@ -57,21 +57,6 @@ module GRPC Core::Channel.new(host, kw, creds) end - def self.update_with_jwt_aud_uri(a_hash, host, method) - last_slash_idx, res = method.rindex('/'), a_hash.clone - return res if last_slash_idx.nil? - service_name = method[0..(last_slash_idx - 1)] - res[:jwt_aud_uri] = "https://#{host}#{service_name}" - res - end - - # check_update_metadata is used by #initialize verify that it's a Proc. - def self.check_update_metadata(update_metadata) - return update_metadata if update_metadata.nil? - fail(TypeError, '!is_a?Proc') unless update_metadata.is_a?(Proc) - update_metadata - end - # Allows users of the stub to modify the propagate mask. # # This is an advanced feature for use when making calls to another gRPC @@ -99,29 +84,21 @@ module GRPC # - :timeout # when present, this is the default timeout used for calls # - # - :update_metadata - # when present, this a func that takes a hash and returns a hash - # it can be used to update metadata, i.e, remove, or amend - # metadata values. - # # @param host [String] the host the stub connects to # @param q [Core::CompletionQueue] used to wait for events # @param channel_override [Core::Channel] a pre-created channel # @param timeout [Number] the default timeout to use in requests # @param creds [Core::ChannelCredentials] the channel credentials - # @param update_metadata a func that updates metadata as described above # @param kw [KeywordArgs]the channel arguments def initialize(host, q, channel_override: nil, timeout: nil, creds: nil, propagate_mask: nil, - update_metadata: nil, **kw) fail(TypeError, '!CompletionQueue') unless q.is_a?(Core::CompletionQueue) @queue = q @ch = ClientStub.setup_channel(channel_override, host, creds, **kw) - @update_metadata = ClientStub.check_update_metadata(update_metadata) alt_host = kw[Core::Channel::SSL_TARGET] @host = alt_host.nil? ? host : alt_host @propagate_mask = propagate_mask @@ -166,6 +143,8 @@ module GRPC # @param deadline [Time] (optional) the time the request should complete # @param parent [Core::Call] a prior call whose reserved metadata # will be propagated by this one. + # @param credentials [Core::CallCredentials] credentials to use when making + # the call # @param return_op [true|false] return an Operation if true # @return [Object] the response received from the server def request_response(method, req, marshal, unmarshal, @@ -173,19 +152,20 @@ module GRPC timeout: nil, return_op: false, parent: nil, + credentials: nil, **kw) c = new_active_call(method, marshal, unmarshal, deadline: deadline, timeout: timeout, - parent: parent) - md = update_metadata(kw, method) - return c.request_response(req, **md) unless return_op + parent: parent, + credentials: credentials) + return c.request_response(req, **kw) unless return_op # return the operation view of the active_call; define #execute as a # new method for this instance that invokes #request_response. op = c.operation op.define_singleton_method(:execute) do - c.request_response(req, **md) + c.request_response(req, **kw) end op end @@ -234,25 +214,28 @@ module GRPC # @param return_op [true|false] return an Operation if true # @param parent [Core::Call] a prior call whose reserved metadata # will be propagated by this one. + # @param credentials [Core::CallCredentials] credentials to use when making + # the call # @return [Object|Operation] the response received from the server def client_streamer(method, requests, marshal, unmarshal, deadline: nil, timeout: nil, return_op: false, parent: nil, + credentials: nil, **kw) c = new_active_call(method, marshal, unmarshal, deadline: deadline, timeout: timeout, - parent: parent) - md = update_metadata(kw, method) - return c.client_streamer(requests, **md) unless return_op + parent: parent, + credentials: credentials) + return c.client_streamer(requests, **kw) unless return_op # return the operation view of the active_call; define #execute as a # new method for this instance that invokes #client_streamer. op = c.operation op.define_singleton_method(:execute) do - c.client_streamer(requests, **md) + c.client_streamer(requests, **kw) end op end @@ -309,6 +292,8 @@ module GRPC # @param return_op [true|false]return an Operation if true # @param parent [Core::Call] a prior call whose reserved metadata # will be propagated by this one. + # @param credentials [Core::CallCredentials] credentials to use when making + # the call # @param blk [Block] when provided, is executed for each response # @return [Enumerator|Operation|nil] as discussed above def server_streamer(method, req, marshal, unmarshal, @@ -316,20 +301,21 @@ module GRPC timeout: nil, return_op: false, parent: nil, + credentials: nil, **kw, &blk) c = new_active_call(method, marshal, unmarshal, deadline: deadline, timeout: timeout, - parent: parent) - md = update_metadata(kw, method) - return c.server_streamer(req, **md, &blk) unless return_op + parent: parent, + credentials: credentials) + return c.server_streamer(req, **kw, &blk) unless return_op # return the operation view of the active_call; define #execute # as a new method for this instance that invokes #server_streamer op = c.operation op.define_singleton_method(:execute) do - c.server_streamer(req, **md, &blk) + c.server_streamer(req, **kw, &blk) end op end @@ -424,6 +410,8 @@ module GRPC # @param deadline [Time] (optional) the time the request should complete # @param parent [Core::Call] a prior call whose reserved metadata # will be propagated by this one. + # @param credentials [Core::CallCredentials] credentials to use when making + # the call # @param return_op [true|false] return an Operation if true # @param blk [Block] when provided, is executed for each response # @return [Enumerator|nil|Operation] as discussed above @@ -432,36 +420,28 @@ module GRPC timeout: nil, return_op: false, parent: nil, + credentials: nil, **kw, &blk) c = new_active_call(method, marshal, unmarshal, deadline: deadline, timeout: timeout, - parent: parent) - md = update_metadata(kw, method) - return c.bidi_streamer(requests, **md, &blk) unless return_op + parent: parent, + credentials: credentials) + + return c.bidi_streamer(requests, **kw, &blk) unless return_op # return the operation view of the active_call; define #execute # as a new method for this instance that invokes #bidi_streamer op = c.operation op.define_singleton_method(:execute) do - c.bidi_streamer(requests, **md, &blk) + c.bidi_streamer(requests, **kw, &blk) end op end private - def update_metadata(kw, method) - return kw if @update_metadata.nil? - just_jwt_uri = self.class.update_with_jwt_aud_uri({}, @host, method) - updated = @update_metadata.call(just_jwt_uri) - - # keys should be lowercase - updated = Hash[updated.each_pair.map { |k, v| [k.downcase, v] }] - kw.merge(updated) - end - # Creates a new active stub # # @param method [string] the method being called. @@ -473,7 +453,8 @@ module GRPC def new_active_call(method, marshal, unmarshal, deadline: nil, timeout: nil, - parent: nil) + parent: nil, + credentials: nil) if deadline.nil? deadline = from_relative_time(timeout.nil? ? @timeout : timeout) end @@ -483,6 +464,7 @@ module GRPC method, nil, # host use nil, deadline) + call.set_credentials credentials unless credentials.nil? ActiveCall.new(call, @queue, marshal, unmarshal, deadline, started: false) end end diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb index 0e318bd53b..410156ff03 100644 --- a/src/ruby/lib/grpc/generic/rpc_server.rb +++ b/src/ruby/lib/grpc/generic/rpc_server.rb @@ -48,6 +48,8 @@ module GRPC return false when 'TERM' return false + when nil + return true end end true diff --git a/src/ruby/pb/README.md b/src/ruby/pb/README.md index 84644e1098..e04aef185c 100644 --- a/src/ruby/pb/README.md +++ b/src/ruby/pb/README.md @@ -20,7 +20,7 @@ re-generate the surface. ```bash $ # (from this directory) -$ protoc -I . grpc/health/v1alpha/health.proto \ +$ protoc -I ../../proto ../../proto/grpc/health/v1alpha/health.proto \ --grpc_out=. \ --ruby_out=. \ --plugin=protoc-gen-grpc=`which grpc_ruby_plugin` diff --git a/src/ruby/pb/generate_proto_ruby.sh b/src/ruby/pb/generate_proto_ruby.sh new file mode 100755 index 0000000000..576b1c08d3 --- /dev/null +++ b/src/ruby/pb/generate_proto_ruby.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# 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. + +# Regenerates gRPC service stubs from proto files. +set +e +cd $(dirname $0)/../../.. + +PROTOC=bins/opt/protobuf/protoc +PLUGIN=protoc-gen-grpc=bins/opt/grpc_ruby_plugin + +$PROTOC -I src/proto src/proto/grpc/health/v1alpha/health.proto \ + --grpc_out=src/ruby/pb \ + --ruby_out=src/ruby/pb \ + --plugin=$PLUGIN + +$PROTOC -I . test/proto/{messages,test,empty}.proto \ + --grpc_out=src/ruby/pb \ + --ruby_out=src/ruby/pb \ + --plugin=$PLUGIN + +$PROTOC -I src/proto/math src/proto/math/math.proto \ + --grpc_out=src/ruby/bin \ + --ruby_out=src/ruby/bin \ + --plugin=$PLUGIN diff --git a/src/ruby/pb/test/client.rb b/src/ruby/pb/test/client.rb index 30550d6cc0..6cc616e5cb 100755 --- a/src/ruby/pb/test/client.rb +++ b/src/ruby/pb/test/client.rb @@ -93,13 +93,6 @@ def load_test_certs files.map { |f| File.open(File.join(data_dir, f)).read } end -# loads the certificates used to access the test server securely. -def load_prod_cert - fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil? - GRPC.logger.info("loading prod certs from #{ENV['SSL_CERT_FILE']}") - File.open(ENV['SSL_CERT_FILE']).read -end - # creates SSL Credentials from the test certificates. def test_creds certs = load_test_certs @@ -108,8 +101,7 @@ end # creates SSL Credentials from the production certificates. def prod_creds - cert_text = load_prod_cert - GRPC::Core::ChannelCredentials.new(cert_text) + GRPC::Core::ChannelCredentials.new() end # creates the SSL Credentials. @@ -132,7 +124,8 @@ def create_stub(opts) if wants_creds.include?(opts.test_case) unless opts.oauth_scope.nil? auth_creds = Google::Auth.get_application_default(opts.oauth_scope) - stub_opts[:update_metadata] = auth_creds.updater_proc + call_creds = GRPC::Core::CallCredentials.new(auth_creds.updater_proc) + stub_opts[:creds] = stub_opts[:creds].compose call_creds end end @@ -141,12 +134,14 @@ def create_stub(opts) kw = auth_creds.updater_proc.call({}) # gives as an auth token # use a metadata update proc that just adds the auth token. - stub_opts[:update_metadata] = proc { |md| md.merge(kw) } + call_creds = GRPC::Core::CallCredentials.new(proc { |md| md.merge(kw) }) + stub_opts[:creds] = stub_opts[:creds].compose call_creds end if opts.test_case == 'jwt_token_creds' # don't use a scope auth_creds = Google::Auth.get_application_default - stub_opts[:update_metadata] = auth_creds.updater_proc + call_creds = GRPC::Core::CallCredentials.new(auth_creds.updater_proc) + stub_opts[:creds] = stub_opts[:creds].compose call_creds end GRPC.logger.info("... connecting securely to #{address}") @@ -160,7 +155,7 @@ end # produces a string of null chars (\0) of length l. def nulls(l) fail 'requires #{l} to be +ve' if l < 0 - [].pack('x' * l).force_encoding('utf-8') + [].pack('x' * l).force_encoding('ascii-8bit') end # a PingPongPlayer implements the ping pong bidi test. diff --git a/src/ruby/pb/test/proto/messages.rb b/src/ruby/pb/test/proto/messages.rb index 9b7f977285..5222c9824a 100644 --- a/src/ruby/pb/test/proto/messages.rb +++ b/src/ruby/pb/test/proto/messages.rb @@ -6,7 +6,7 @@ require 'google/protobuf' Google::Protobuf::DescriptorPool.generated_pool.build do add_message "grpc.testing.Payload" do optional :type, :enum, 1, "grpc.testing.PayloadType" - optional :body, :string, 2 + optional :body, :bytes, 2 end add_message "grpc.testing.EchoStatus" do optional :code, :int32, 1 diff --git a/src/ruby/pb/test/server.rb b/src/ruby/pb/test/server.rb index 67877a191f..851e815222 100755 --- a/src/ruby/pb/test/server.rb +++ b/src/ruby/pb/test/server.rb @@ -126,7 +126,7 @@ end # produces a string of null chars (\0) of length l. def nulls(l) fail 'requires #{l} to be +ve' if l < 0 - [].pack('x' * l).force_encoding('utf-8') + [].pack('x' * l).force_encoding('ascii-8bit') end # A EnumeratorQueue wraps a Queue yielding the items added to it via each_item. diff --git a/src/python/grpcio_test/setup.py b/src/ruby/spec/call_credentials_spec.rb index e9ee45a92a..32a0ad44b7 100644 --- a/src/python/grpcio_test/setup.py +++ b/src/ruby/spec/call_credentials_spec.rb @@ -27,68 +27,31 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""A setup module for the GRPC Python interop testing package.""" - -import os -import os.path - -import setuptools - -# Ensure we're in the proper directory whether or not we're being used by pip. -os.chdir(os.path.dirname(os.path.abspath(__file__))) - -# Break import-style to ensure we can actually find our commands module. -import commands - -_PACKAGES = setuptools.find_packages('.') - -_PACKAGE_DIRECTORIES = { - '': '.', -} - -_PACKAGE_DATA = { - 'grpc_interop': [ - 'credentials/ca.pem', - 'credentials/server1.key', - 'credentials/server1.pem', - ], - 'grpc_protoc_plugin': [ - 'test.proto', - ], - 'grpc_test': [ - 'credentials/ca.pem', - 'credentials/server1.key', - 'credentials/server1.pem', - ], -} - -_SETUP_REQUIRES = ( - 'pytest>=2.6', - 'pytest-cov>=2.0', - 'pytest-xdist>=1.11', - 'pytest-timeout>=0.5', -) - -_INSTALL_REQUIRES = ( - 'oauth2client>=1.4.7', - 'grpcio>=0.11.0b0', - # TODO(issue 3321): Unpin protobuf dependency. - 'protobuf==3.0.0a3', -) - -_COMMAND_CLASS = { - 'test': commands.RunTests, - 'build_proto_modules': commands.BuildProtoModules, - 'build_py': commands.BuildPy, -} - -setuptools.setup( - name='grpcio_test', - version='0.11.0b0', - packages=_PACKAGES, - package_dir=_PACKAGE_DIRECTORIES, - package_data=_PACKAGE_DATA, - install_requires=_INSTALL_REQUIRES + _SETUP_REQUIRES, - setup_requires=_SETUP_REQUIRES, - cmdclass=_COMMAND_CLASS, -) +require 'grpc' + +describe GRPC::Core::CallCredentials do + CallCredentials = GRPC::Core::CallCredentials + + let(:auth_proc) { proc { { 'plugin_key' => 'plugin_value' } } } + + describe '#new' do + it 'can successfully create a CallCredentials from a proc' do + expect { CallCredentials.new(auth_proc) }.not_to raise_error + end + end + + describe '#compose' do + it 'can compose with another CallCredentials' do + creds1 = CallCredentials.new(auth_proc) + creds2 = CallCredentials.new(auth_proc) + expect { creds1.compose creds2 }.not_to raise_error + end + + it 'can compose with multiple CallCredentials' do + creds1 = CallCredentials.new(auth_proc) + creds2 = CallCredentials.new(auth_proc) + creds3 = CallCredentials.new(auth_proc) + expect { creds1.compose(creds2, creds3) }.not_to raise_error + end + end +end diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb index dd3c45f754..6629570fba 100644 --- a/src/ruby/spec/call_spec.rb +++ b/src/ruby/spec/call_spec.rb @@ -144,6 +144,15 @@ describe GRPC::Core::Call do end end + describe '#set_credentials!' do + it 'can set a valid CallCredentials object' do + call = make_test_call + auth_proc = proc { { 'plugin_key' => 'plugin_value' } } + creds = GRPC::Core::CallCredentials.new auth_proc + expect { call.set_credentials! creds }.not_to raise_error + end + end + def make_test_call @ch.create_call(client_queue, nil, nil, 'dummy_method', nil, deadline) end diff --git a/src/ruby/spec/channel_credentials_spec.rb b/src/ruby/spec/channel_credentials_spec.rb index b2bdf7032e..0bcfc752a0 100644 --- a/src/ruby/spec/channel_credentials_spec.rb +++ b/src/ruby/spec/channel_credentials_spec.rb @@ -31,6 +31,7 @@ require 'grpc' describe GRPC::Core::ChannelCredentials do ChannelCredentials = GRPC::Core::ChannelCredentials + CallCredentials = GRPC::Core::CallCredentials def load_test_certs test_root = File.join(File.dirname(__FILE__), 'testdata') @@ -54,10 +55,43 @@ describe GRPC::Core::ChannelCredentials do expect { ChannelCredentials.new(root_cert) }.not_to raise_error end - it 'cannot be constructed with a nil server roots' do + it 'can be constructed with a nil server roots' do _, client_key, client_chain = load_test_certs blk = proc { ChannelCredentials.new(nil, client_key, client_chain) } - expect(&blk).to raise_error + expect(&blk).not_to raise_error + end + + it 'can be constructed with no params' do + blk = proc { ChannelCredentials.new(nil) } + expect(&blk).not_to raise_error + end + end + + describe '#compose' do + it 'can compose with a CallCredentials' do + certs = load_test_certs + channel_creds = ChannelCredentials.new(*certs) + auth_proc = proc { { 'plugin_key' => 'plugin_value' } } + call_creds = CallCredentials.new auth_proc + expect { channel_creds.compose call_creds }.not_to raise_error + end + + it 'can compose with multiple CallCredentials' do + certs = load_test_certs + channel_creds = ChannelCredentials.new(*certs) + auth_proc = proc { { 'plugin_key' => 'plugin_value' } } + call_creds1 = CallCredentials.new auth_proc + call_creds2 = CallCredentials.new auth_proc + expect do + channel_creds.compose(call_creds1, call_creds2) + end.not_to raise_error + end + + it 'cannot compose with ChannelCredentials' do + certs = load_test_certs + channel_creds1 = ChannelCredentials.new(*certs) + channel_creds2 = ChannelCredentials.new(*certs) + expect { channel_creds1.compose channel_creds2 }.to raise_error(TypeError) end end end diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb index 734f176e94..7cce2076c9 100644 --- a/src/ruby/spec/client_server_spec.rb +++ b/src/ruby/spec/client_server_spec.rb @@ -413,6 +413,8 @@ describe 'the http client/server' do end describe 'the secure http client/server' do + include_context 'setup: tags' + def load_test_certs test_root = File.join(File.dirname(__FILE__), 'testdata') files = ['ca.pem', 'server1.key', 'server1.pem'] @@ -443,4 +445,31 @@ describe 'the secure http client/server' do it_behaves_like 'GRPC metadata delivery works OK' do end + + it 'modifies metadata with CallCredentials' do + auth_proc = proc { { 'k1' => 'updated-v1' } } + call_creds = GRPC::Core::CallCredentials.new(auth_proc) + md = { 'k2' => 'v2' } + expected_md = { 'k1' => 'updated-v1', 'k2' => 'v2' } + recvd_rpc = nil + rcv_thread = Thread.new do + recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline) + end + + call = new_client_call + call.set_credentials! call_creds + client_ops = { + CallOps::SEND_INITIAL_METADATA => md + } + batch_result = call.run_batch(@client_queue, @client_tag, deadline, + client_ops) + expect(batch_result.send_metadata).to be true + + # confirm the server can receive the client metadata + rcv_thread.join + expect(recvd_rpc).to_not eq nil + recvd_md = recvd_rpc.metadata + replace_symbols = Hash[expected_md.each_pair.collect { |x, y| [x.to_s, y] }] + expect(recvd_md).to eq(recvd_md.merge(replace_symbols)) + end end diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb index da5bc6c9e5..40550230dd 100644 --- a/src/ruby/spec/generic/client_stub_spec.rb +++ b/src/ruby/spec/generic/client_stub_spec.rb @@ -145,34 +145,6 @@ describe 'ClientStub' do th.join end - it 'should update the sent metadata with a provided metadata updater' do - server_port = create_test_server - host = "localhost:#{server_port}" - th = run_request_response(@sent_msg, @resp, @pass, - k1: 'updated-v1', k2: 'v2') - update_md = proc do |md| - md[:k1] = 'updated-v1' - md - end - stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md) - expect(get_response(stub)).to eq(@resp) - th.join - end - - it 'should downcase the keys provided by the metadata updater' do - server_port = create_test_server - host = "localhost:#{server_port}" - th = run_request_response(@sent_msg, @resp, @pass, - k1: 'downcased-key-v1', k2: 'v2') - update_md = proc do |md| - md[:K1] = 'downcased-key-v1' - md - end - stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md) - expect(get_response(stub)).to eq(@resp) - th.join - end - it 'should send a request when configured using an override channel' do server_port = create_test_server alt_host = "localhost:#{server_port}" @@ -241,20 +213,6 @@ describe 'ClientStub' do th.join end - it 'should update the sent metadata with a provided metadata updater' do - server_port = create_test_server - host = "localhost:#{server_port}" - th = run_client_streamer(@sent_msgs, @resp, @pass, - k1: 'updated-v1', k2: 'v2') - update_md = proc do |md| - md[:k1] = 'updated-v1' - md - end - stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md) - expect(get_response(stub)).to eq(@resp) - th.join - end - it 'should raise an error if the status is not ok' do server_port = create_test_server host = "localhost:#{server_port}" @@ -323,21 +281,6 @@ describe 'ClientStub' do expect { e.collect { |r| r } }.to raise_error(GRPC::BadStatus) th.join end - - it 'should update the sent metadata with a provided metadata updater' do - server_port = create_test_server - host = "localhost:#{server_port}" - th = run_server_streamer(@sent_msg, @replys, @pass, - k1: 'updated-v1', k2: 'v2') - update_md = proc do |md| - md[:k1] = 'updated-v1' - md - end - stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md) - e = get_responses(stub) - expect(e.collect { |r| r }).to eq(@replys) - th.join - end end describe 'without a call operation' do diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb index efe07f734e..d95a021311 100644 --- a/src/ruby/spec/generic/rpc_server_spec.rb +++ b/src/ruby/spec/generic/rpc_server_spec.rb @@ -422,25 +422,6 @@ describe GRPC::RpcServer do t.join end - it 'should receive updated metadata', server: true do - service = EchoService.new - @srv.handle(service) - t = Thread.new { @srv.run } - @srv.wait_till_running - req = EchoMsg.new - client_opts[:update_metadata] = proc do |md| - md[:k1] = 'updated-v1' - md - end - stub = EchoStub.new(@host, **client_opts) - expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg) - wanted_md = [{ 'k1' => 'updated-v1', 'k2' => 'v2', - 'jwt_aud_uri' => "https://#{@host}/EchoService" }] - check_md(wanted_md, service.received_md) - @srv.stop - t.join - end - it 'should handle multiple parallel requests', server: true do @srv.handle(EchoService) t = Thread.new { @srv.run } |