diff options
Diffstat (limited to 'src/core/lib')
-rw-r--r-- | src/core/lib/channel/http_client_filter.c | 57 | ||||
-rw-r--r-- | src/core/lib/channel/http_server_filter.c | 28 | ||||
-rw-r--r-- | src/core/lib/channel/message_size_filter.c | 75 | ||||
-rw-r--r-- | src/core/lib/iomgr/ev_epoll_linux.c | 31 | ||||
-rw-r--r-- | src/core/lib/iomgr/ev_poll_posix.c | 29 | ||||
-rw-r--r-- | src/core/lib/iomgr/ev_posix.h | 1 | ||||
-rw-r--r-- | src/core/lib/iomgr/tcp_client_posix.c | 7 | ||||
-rw-r--r-- | src/core/lib/iomgr/tcp_windows.c | 2 | ||||
-rw-r--r-- | src/core/lib/json/json.c | 6 | ||||
-rw-r--r-- | src/core/lib/json/json.h | 22 | ||||
-rw-r--r-- | src/core/lib/slice/slice.c | 8 | ||||
-rw-r--r-- | src/core/lib/support/string.c | 9 | ||||
-rw-r--r-- | src/core/lib/support/string.h | 3 | ||||
-rw-r--r-- | src/core/lib/surface/call.c | 5 | ||||
-rw-r--r-- | src/core/lib/surface/server.c | 11 | ||||
-rw-r--r-- | src/core/lib/transport/mdstr_hash_table.c | 42 | ||||
-rw-r--r-- | src/core/lib/transport/mdstr_hash_table.h | 18 | ||||
-rw-r--r-- | src/core/lib/transport/method_config.c | 340 | ||||
-rw-r--r-- | src/core/lib/transport/method_config.h | 136 | ||||
-rw-r--r-- | src/core/lib/transport/service_config.c | 249 | ||||
-rw-r--r-- | src/core/lib/transport/service_config.h | 70 |
21 files changed, 512 insertions, 637 deletions
diff --git a/src/core/lib/channel/http_client_filter.c b/src/core/lib/channel/http_client_filter.c index 35528e1b8c..056f6ae394 100644 --- a/src/core/lib/channel/http_client_filter.c +++ b/src/core/lib/channel/http_client_filter.c @@ -36,6 +36,7 @@ #include <grpc/support/string_util.h> #include <string.h> #include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/percent_encoding.h" #include "src/core/lib/support/string.h" #include "src/core/lib/transport/static_metadata.h" #include "src/core/lib/transport/transport_impl.h" @@ -56,6 +57,7 @@ typedef struct call_data { grpc_linked_mdelem payload_bin; grpc_metadata_batch *recv_initial_metadata; + grpc_metadata_batch *recv_trailing_metadata; uint8_t *payload_bytes; /* Vars to read data off of send_message */ @@ -69,14 +71,16 @@ typedef struct call_data { bool send_message_blocked; /** Closure to call when finished with the hc_on_recv hook */ - grpc_closure *on_done_recv; + grpc_closure *on_done_recv_initial_metadata; + grpc_closure *on_done_recv_trailing_metadata; grpc_closure *on_complete; grpc_closure *post_send; /** Receive closures are chained: we inject this closure as the on_done_recv up-call on transport_op, and remember to call our on_done_recv member after handling it. */ - grpc_closure hc_on_recv; + grpc_closure hc_on_recv_initial_metadata; + grpc_closure hc_on_recv_trailing_metadata; grpc_closure hc_on_complete; grpc_closure got_slice; grpc_closure send_done; @@ -106,6 +110,16 @@ static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) { grpc_call_element_send_close_with_message(a->exec_ctx, a->elem, GRPC_STATUS_CANCELLED, &message); return NULL; + } else if (md->key == GRPC_MDSTR_GRPC_MESSAGE) { + grpc_slice pct_decoded_msg = + grpc_permissive_percent_decode_slice(md->value->slice); + if (grpc_slice_is_equivalent(pct_decoded_msg, md->value->slice)) { + grpc_slice_unref(pct_decoded_msg); + return md; + } else { + return grpc_mdelem_from_metadata_strings( + GRPC_MDSTR_GRPC_MESSAGE, grpc_mdstr_from_slice(pct_decoded_msg)); + } } else if (md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) { return NULL; } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) { @@ -129,8 +143,8 @@ static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) { return md; } -static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_error *error) { +static void hc_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, + void *user_data, grpc_error *error) { grpc_call_element *elem = user_data; call_data *calld = elem->call_data; client_recv_filter_args a; @@ -138,7 +152,21 @@ static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, a.exec_ctx = exec_ctx; grpc_metadata_batch_filter(calld->recv_initial_metadata, client_recv_filter, &a); - calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error); + grpc_closure_run(exec_ctx, calld->on_done_recv_initial_metadata, + GRPC_ERROR_REF(error)); +} + +static void hc_on_recv_trailing_metadata(grpc_exec_ctx *exec_ctx, + void *user_data, grpc_error *error) { + grpc_call_element *elem = user_data; + call_data *calld = elem->call_data; + client_recv_filter_args a; + a.elem = elem; + a.exec_ctx = exec_ctx; + grpc_metadata_batch_filter(calld->recv_trailing_metadata, client_recv_filter, + &a); + grpc_closure_run(exec_ctx, calld->on_done_recv_trailing_metadata, + GRPC_ERROR_REF(error)); } static void hc_on_complete(grpc_exec_ctx *exec_ctx, void *user_data, @@ -281,8 +309,15 @@ static void hc_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, if (op->recv_initial_metadata != NULL) { /* substitute our callback for the higher callback */ calld->recv_initial_metadata = op->recv_initial_metadata; - calld->on_done_recv = op->recv_initial_metadata_ready; - op->recv_initial_metadata_ready = &calld->hc_on_recv; + calld->on_done_recv_initial_metadata = op->recv_initial_metadata_ready; + op->recv_initial_metadata_ready = &calld->hc_on_recv_initial_metadata; + } + + if (op->recv_trailing_metadata != NULL) { + /* substitute our callback for the higher callback */ + calld->recv_trailing_metadata = op->recv_trailing_metadata; + calld->on_done_recv_trailing_metadata = op->on_complete; + op->on_complete = &calld->hc_on_recv_trailing_metadata; } } @@ -308,11 +343,15 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_call_element_args *args) { call_data *calld = elem->call_data; - calld->on_done_recv = NULL; + calld->on_done_recv_initial_metadata = NULL; + calld->on_done_recv_trailing_metadata = NULL; calld->on_complete = NULL; calld->payload_bytes = NULL; grpc_slice_buffer_init(&calld->slices); - grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem); + grpc_closure_init(&calld->hc_on_recv_initial_metadata, + hc_on_recv_initial_metadata, elem); + grpc_closure_init(&calld->hc_on_recv_trailing_metadata, + hc_on_recv_trailing_metadata, elem); grpc_closure_init(&calld->hc_on_complete, hc_on_complete, elem); grpc_closure_init(&calld->got_slice, got_slice, elem); grpc_closure_init(&calld->send_done, send_done, elem); diff --git a/src/core/lib/channel/http_server_filter.c b/src/core/lib/channel/http_server_filter.c index 4b976ed8d5..a5134ee21b 100644 --- a/src/core/lib/channel/http_server_filter.c +++ b/src/core/lib/channel/http_server_filter.c @@ -37,6 +37,7 @@ #include <grpc/support/log.h> #include <string.h> #include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/percent_encoding.h" #include "src/core/lib/transport/static_metadata.h" #define EXPECTED_CONTENT_TYPE "application/grpc" @@ -86,6 +87,23 @@ typedef struct { grpc_exec_ctx *exec_ctx; } server_filter_args; +static grpc_mdelem *server_filter_outgoing_metadata(void *user_data, + grpc_mdelem *md) { + if (md->key == GRPC_MDSTR_GRPC_MESSAGE) { + grpc_slice pct_encoded_msg = grpc_percent_encode_slice( + md->value->slice, grpc_compatible_percent_encoding_unreserved_bytes); + if (grpc_slice_is_equivalent(pct_encoded_msg, md->value->slice)) { + grpc_slice_unref(pct_encoded_msg); + return md; + } else { + return grpc_mdelem_from_metadata_strings( + GRPC_MDSTR_GRPC_MESSAGE, grpc_mdstr_from_slice(pct_encoded_msg)); + } + } else { + return md; + } +} + static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { server_filter_args *a = user_data; grpc_call_element *elem = a->elem; @@ -254,7 +272,7 @@ static void hs_recv_message_ready(grpc_exec_ctx *exec_ctx, void *user_data, } } -static void hs_mutate_op(grpc_call_element *elem, +static void hs_mutate_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_transport_stream_op *op) { /* grab pointers to our data from the call element */ call_data *calld = elem->call_data; @@ -290,6 +308,12 @@ static void hs_mutate_op(grpc_call_element *elem, op->on_complete = &calld->hs_on_complete; } } + + if (op->send_trailing_metadata) { + server_filter_args a = {elem, exec_ctx}; + grpc_metadata_batch_filter(op->send_trailing_metadata, + server_filter_outgoing_metadata, &a); + } } static void hs_start_transport_op(grpc_exec_ctx *exec_ctx, @@ -297,7 +321,7 @@ static void hs_start_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport_stream_op *op) { GRPC_CALL_LOG_OP(GPR_INFO, elem, op); GPR_TIMER_BEGIN("hs_start_transport_op", 0); - hs_mutate_op(elem, op); + hs_mutate_op(exec_ctx, elem, op); grpc_call_next_op(exec_ctx, elem, op); GPR_TIMER_END("hs_start_transport_op", 0); } diff --git a/src/core/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c index c6bfb80d78..f05c789010 100644 --- a/src/core/lib/channel/message_size_filter.c +++ b/src/core/lib/channel/message_size_filter.c @@ -34,16 +34,14 @@ #include <limits.h> #include <string.h> +#include <grpc/impl/codegen/grpc_types.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/string_util.h> #include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/transport/method_config.h" - -#define DEFAULT_MAX_SEND_MESSAGE_LENGTH -1 // Unlimited. -// The protobuf library will (by default) start warning at 100 megs. -#define DEFAULT_MAX_RECV_MESSAGE_LENGTH (4 * 1024 * 1024) +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/service_config.h" typedef struct message_size_limits { int max_send_size; @@ -56,30 +54,29 @@ static void* message_size_limits_copy(void* value) { return new_value; } -static int message_size_limits_cmp(void* value1, void* value2) { - const message_size_limits* v1 = value1; - const message_size_limits* v2 = value2; - if (v1->max_send_size > v2->max_send_size) return 1; - if (v1->max_send_size < v2->max_send_size) return -1; - if (v1->max_recv_size > v2->max_recv_size) return 1; - if (v1->max_recv_size < v2->max_recv_size) return -1; - return 0; -} - static const grpc_mdstr_hash_table_vtable message_size_limits_vtable = { - gpr_free, message_size_limits_copy, message_size_limits_cmp}; + gpr_free, message_size_limits_copy}; -static void* method_config_convert_value( - const grpc_method_config* method_config) { +static void* message_size_limits_create_from_json(const grpc_json* json) { + int max_request_message_bytes = -1; + int max_response_message_bytes = -1; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key == NULL) continue; + if (strcmp(field->key, "maxRequestMessageBytes") == 0) { + if (max_request_message_bytes >= 0) return NULL; // Duplicate. + if (field->type != GRPC_JSON_STRING) return NULL; + max_request_message_bytes = gpr_parse_nonnegative_int(field->value); + if (max_request_message_bytes == -1) return NULL; + } else if (strcmp(field->key, "maxResponseMessageBytes") == 0) { + if (max_response_message_bytes >= 0) return NULL; // Duplicate. + if (field->type != GRPC_JSON_STRING) return NULL; + max_response_message_bytes = gpr_parse_nonnegative_int(field->value); + if (max_response_message_bytes == -1) return NULL; + } + } message_size_limits* value = gpr_malloc(sizeof(message_size_limits)); - const int32_t* max_request_message_bytes = - grpc_method_config_get_max_request_message_bytes(method_config); - value->max_send_size = - max_request_message_bytes != NULL ? *max_request_message_bytes : -1; - const int32_t* max_response_message_bytes = - grpc_method_config_get_max_response_message_bytes(method_config); - value->max_recv_size = - max_response_message_bytes != NULL ? *max_response_message_bytes : -1; + value->max_send_size = max_request_message_bytes; + value->max_recv_size = max_response_message_bytes; return value; } @@ -201,20 +198,20 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, GPR_ASSERT(!args->is_last); channel_data* chand = elem->channel_data; memset(chand, 0, sizeof(*chand)); - chand->max_send_size = DEFAULT_MAX_SEND_MESSAGE_LENGTH; - chand->max_recv_size = DEFAULT_MAX_RECV_MESSAGE_LENGTH; + chand->max_send_size = GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH; + chand->max_recv_size = GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH; for (size_t i = 0; i < args->channel_args->num_args; ++i) { if (strcmp(args->channel_args->args[i].key, GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) == 0) { - const grpc_integer_options options = {DEFAULT_MAX_SEND_MESSAGE_LENGTH, 0, - INT_MAX}; + const grpc_integer_options options = { + GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH, 0, INT_MAX}; chand->max_send_size = grpc_channel_arg_get_integer(&args->channel_args->args[i], options); } if (strcmp(args->channel_args->args[i].key, GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) == 0) { - const grpc_integer_options options = {DEFAULT_MAX_RECV_MESSAGE_LENGTH, 0, - INT_MAX}; + const grpc_integer_options options = { + GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH, 0, INT_MAX}; chand->max_recv_size = grpc_channel_arg_get_integer(&args->channel_args->args[i], options); } @@ -223,10 +220,16 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, const grpc_arg* channel_arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG); if (channel_arg != NULL) { - GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); - chand->method_limit_table = grpc_method_config_table_convert( - (grpc_method_config_table*)channel_arg->value.pointer.p, - method_config_convert_value, &message_size_limits_vtable); + GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); + grpc_service_config* service_config = + grpc_service_config_create(channel_arg->value.string); + if (service_config != NULL) { + chand->method_limit_table = + grpc_service_config_create_method_config_table( + service_config, message_size_limits_create_from_json, + &message_size_limits_vtable); + grpc_service_config_destroy(service_config); + } } return GRPC_ERROR_NONE; } diff --git a/src/core/lib/iomgr/ev_epoll_linux.c b/src/core/lib/iomgr/ev_epoll_linux.c index 91041a7c28..07fbfd849e 100644 --- a/src/core/lib/iomgr/ev_epoll_linux.c +++ b/src/core/lib/iomgr/ev_epoll_linux.c @@ -72,6 +72,11 @@ static int grpc_polling_trace = 0; /* Disabled by default */ static int grpc_wakeup_signal = -1; static bool is_grpc_wakeup_signal_initialized = false; +/* TODO: sreek: Right now, this wakes up all pollers. In future we should make + * sure to wake up one polling thread (which can wake up other threads if + * needed) */ +static grpc_wakeup_fd global_wakeup_fd; + /* Implements the function defined in grpc_posix.h. This function might be * called before even calling grpc_init() to set either a different signal to * use. If signum == -1, then the use of signals is disabled */ @@ -437,9 +442,8 @@ static void polling_island_add_wakeup_fd_locked(polling_island *pi, gpr_asprintf(&err_msg, "epoll_ctl (epoll_fd: %d) add wakeup fd: %d failed with " "error: %d (%s)", - pi->epoll_fd, - GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), errno, - strerror(errno)); + pi->epoll_fd, GRPC_WAKEUP_FD_GET_READ_FD(&global_wakeup_fd), + errno, strerror(errno)); append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); gpr_free(err_msg); } @@ -541,7 +545,7 @@ static polling_island *polling_island_create(grpc_exec_ctx *exec_ctx, goto done; } - polling_island_add_wakeup_fd_locked(pi, &grpc_global_wakeup_fd, error); + polling_island_add_wakeup_fd_locked(pi, &global_wakeup_fd, error); polling_island_add_wakeup_fd_locked(pi, &pi->workqueue_wakeup_fd, error); if (initial_fd != NULL) { @@ -843,11 +847,6 @@ static void polling_island_global_shutdown() { * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a * case occurs. */ -/* TODO: sreek: Right now, this wakes up all pollers. In future we should make - * sure to wake up one polling thread (which can wake up other threads if - * needed) */ -grpc_wakeup_fd grpc_global_wakeup_fd; - static grpc_fd *fd_freelist = NULL; static gpr_mu fd_freelist_mu; @@ -1163,11 +1162,11 @@ static grpc_error *pollset_global_init(void) { gpr_tls_init(&g_current_thread_pollset); gpr_tls_init(&g_current_thread_worker); poller_kick_init(); - return grpc_wakeup_fd_init(&grpc_global_wakeup_fd); + return grpc_wakeup_fd_init(&global_wakeup_fd); } static void pollset_global_shutdown(void) { - grpc_wakeup_fd_destroy(&grpc_global_wakeup_fd); + grpc_wakeup_fd_destroy(&global_wakeup_fd); gpr_tls_destroy(&g_current_thread_pollset); gpr_tls_destroy(&g_current_thread_worker); } @@ -1274,7 +1273,7 @@ static grpc_error *pollset_kick(grpc_pollset *p, } static grpc_error *kick_poller(void) { - return grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd); + return grpc_wakeup_fd_wakeup(&global_wakeup_fd); } static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { @@ -1501,13 +1500,11 @@ static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, for (int i = 0; i < ep_rv; ++i) { void *data_ptr = ep_ev[i].data.ptr; - if (data_ptr == &grpc_global_wakeup_fd) { - append_error(error, - grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd), + if (data_ptr == &global_wakeup_fd) { + append_error(error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd), err_desc); } else if (data_ptr == &pi->workqueue_wakeup_fd) { - append_error(error, - grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd), + append_error(error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd), err_desc); maybe_do_workqueue_work(exec_ctx, pi); } else if (data_ptr == &polling_island_wakeup_fd) { diff --git a/src/core/lib/iomgr/ev_poll_posix.c b/src/core/lib/iomgr/ev_poll_posix.c index e1d620cfff..21b28e5554 100644 --- a/src/core/lib/iomgr/ev_poll_posix.c +++ b/src/core/lib/iomgr/ev_poll_posix.c @@ -120,6 +120,8 @@ struct grpc_fd { grpc_pollset *read_notifier_pollset; }; +static grpc_wakeup_fd global_wakeup_fd; + /* Begin polling on an fd. Registers that the given pollset is interested in this fd - so that if read or writability interest changes, the pollset can be kicked to pick up that @@ -769,17 +771,17 @@ static grpc_error *pollset_kick(grpc_pollset *p, static grpc_error *pollset_global_init(void) { gpr_tls_init(&g_current_thread_poller); gpr_tls_init(&g_current_thread_worker); - return grpc_wakeup_fd_init(&grpc_global_wakeup_fd); + return grpc_wakeup_fd_init(&global_wakeup_fd); } static void pollset_global_shutdown(void) { - grpc_wakeup_fd_destroy(&grpc_global_wakeup_fd); + grpc_wakeup_fd_destroy(&global_wakeup_fd); gpr_tls_destroy(&g_current_thread_poller); gpr_tls_destroy(&g_current_thread_worker); } static grpc_error *kick_poller(void) { - return grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd); + return grpc_wakeup_fd_wakeup(&global_wakeup_fd); } /* main interface */ @@ -947,7 +949,7 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, fd_count = 0; pfd_count = 2; - pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd); + pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&global_wakeup_fd); pfds[0].events = POLLIN; pfds[0].revents = 0; pfds[1].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker.wakeup_fd->fd); @@ -1001,8 +1003,8 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, } } else { if (pfds[0].revents & POLLIN_CHECK) { - work_combine_error( - &error, grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd)); + work_combine_error(&error, + grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd)); } if (pfds[1].revents & POLLIN_CHECK) { work_combine_error( @@ -1343,6 +1345,7 @@ static int cvfd_poll(struct pollfd *fds, nfds_t nfds, int timeout) { int res, idx; gpr_cv *pollcv; cv_node *cvn, *prev; + int skip_poll = 0; nfds_t nsockfds = 0; gpr_thd_id t_id; gpr_thd_options opt; @@ -1358,17 +1361,17 @@ static int cvfd_poll(struct pollfd *fds, nfds_t nfds, int timeout) { cvn->cv = pollcv; cvn->next = g_cvfds.cvfds[idx].cvs; g_cvfds.cvfds[idx].cvs = cvn; - // We should return immediately if there are pending events, - // but we still need to call poll() to check for socket events + // Don't bother polling if a wakeup fd is ready if (g_cvfds.cvfds[idx].is_set) { - timeout = 0; + skip_poll = 1; } } else if (fds[i].fd >= 0) { nsockfds++; } } - if (nsockfds > 0) { + res = 0; + if (!skip_poll && nsockfds > 0) { pargs = gpr_malloc(sizeof(struct poll_args)); // Both the main thread and calling thread get a reference gpr_ref_init(&pargs->refcount, 2); @@ -1398,16 +1401,14 @@ static int cvfd_poll(struct pollfd *fds, nfds_t nfds, int timeout) { res = pargs->retval; errno = pargs->err; } else { - res = 0; errno = 0; gpr_atm_no_barrier_store(&pargs->status, CANCELLED); } - } else { + } else if (!skip_poll) { gpr_timespec deadline = gpr_now(GPR_CLOCK_REALTIME); deadline = gpr_time_add(deadline, gpr_time_from_millis(timeout, GPR_TIMESPAN)); gpr_cv_wait(pollcv, &g_cvfds.mu, deadline); - res = 0; } idx = 0; @@ -1431,7 +1432,7 @@ static int cvfd_poll(struct pollfd *fds, nfds_t nfds, int timeout) { fds[i].revents = POLLIN; if (res >= 0) res++; } - } else if (fds[i].fd >= 0 && + } else if (!skip_poll && fds[i].fd >= 0 && gpr_atm_no_barrier_load(&pargs->status) == COMPLETED) { fds[i].revents = pargs->fds[idx].revents; idx++; diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h index 2fdef06838..cb5832539d 100644 --- a/src/core/lib/iomgr/ev_posix.h +++ b/src/core/lib/iomgr/ev_posix.h @@ -183,6 +183,5 @@ void grpc_pollset_set_del_fd(grpc_exec_ctx *exec_ctx, /* override to allow tests to hook poll() usage */ typedef int (*grpc_poll_function_type)(struct pollfd *, nfds_t, int); extern grpc_poll_function_type grpc_poll_function; -extern grpc_wakeup_fd grpc_global_wakeup_fd; #endif /* GRPC_CORE_LIB_IOMGR_EV_POSIX_H */ diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c index 13347735df..a3a70a8ed7 100644 --- a/src/core/lib/iomgr/tcp_client_posix.c +++ b/src/core/lib/iomgr/tcp_client_posix.c @@ -251,8 +251,11 @@ finish: done = (--ac->refs == 0); gpr_mu_unlock(&ac->mu); if (error != GRPC_ERROR_NONE) { - error = grpc_error_set_str(error, GRPC_ERROR_STR_DESCRIPTION, - "Failed to connect to remote host"); + char *error_descr; + gpr_asprintf(&error_descr, "Failed to connect to remote host: %s", + grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION)); + error = grpc_error_set_str(error, GRPC_ERROR_STR_DESCRIPTION, error_descr); + gpr_free(error_descr); error = grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, ac->addr_str); } diff --git a/src/core/lib/iomgr/tcp_windows.c b/src/core/lib/iomgr/tcp_windows.c index 62afbcef51..d4613b674e 100644 --- a/src/core/lib/iomgr/tcp_windows.c +++ b/src/core/lib/iomgr/tcp_windows.c @@ -423,7 +423,7 @@ grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket, tcp->base.vtable = &vtable; tcp->socket = socket; gpr_mu_init(&tcp->mu); - gpr_ref_init(&tcp->refcount, 2); + gpr_ref_init(&tcp->refcount, 1); grpc_closure_init(&tcp->on_read, on_read, tcp); grpc_closure_init(&tcp->on_write, on_write, tcp); tcp->peer_string = gpr_strdup(peer_string); diff --git a/src/core/lib/json/json.c b/src/core/lib/json/json.c index 5b583a1f2e..48b13686d7 100644 --- a/src/core/lib/json/json.c +++ b/src/core/lib/json/json.c @@ -37,15 +37,15 @@ #include "src/core/lib/json/json.h" -grpc_json *grpc_json_create(grpc_json_type type) { - grpc_json *json = gpr_malloc(sizeof(*json)); +grpc_json* grpc_json_create(grpc_json_type type) { + grpc_json* json = gpr_malloc(sizeof(*json)); memset(json, 0, sizeof(*json)); json->type = type; return json; } -void grpc_json_destroy(grpc_json *json) { +void grpc_json_destroy(grpc_json* json) { while (json->child) { grpc_json_destroy(json->child); } diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h index 681df4bb77..7111db0b52 100644 --- a/src/core/lib/json/json.h +++ b/src/core/lib/json/json.h @@ -42,14 +42,14 @@ * are not owned by it. */ typedef struct grpc_json { - struct grpc_json *next; - struct grpc_json *prev; - struct grpc_json *child; - struct grpc_json *parent; + struct grpc_json* next; + struct grpc_json* prev; + struct grpc_json* child; + struct grpc_json* parent; grpc_json_type type; - const char *key; - const char *value; + const char* key; + const char* value; } grpc_json; /* The next two functions are going to parse the input string, and @@ -65,8 +65,8 @@ typedef struct grpc_json { * * Delete the allocated tree afterward using grpc_json_destroy(). */ -grpc_json *grpc_json_parse_string_with_len(char *input, size_t size); -grpc_json *grpc_json_parse_string(char *input); +grpc_json* grpc_json_parse_string_with_len(char* input, size_t size); +grpc_json* grpc_json_parse_string(char* input); /* This function will create a new string using gpr_realloc, and will * deserialize the grpc_json tree into it. It'll be zero-terminated, @@ -76,13 +76,13 @@ grpc_json *grpc_json_parse_string(char *input); * If indent is 0, then newlines will be suppressed as well, and the * output will be condensed at its maximum. */ -char *grpc_json_dump_to_string(grpc_json *json, int indent); +char* grpc_json_dump_to_string(grpc_json* json, int indent); /* Use these to create or delete a grpc_json object. * Deletion is recursive. We will not attempt to free any of the strings * in any of the objects of that tree. */ -grpc_json *grpc_json_create(grpc_json_type type); -void grpc_json_destroy(grpc_json *json); +grpc_json* grpc_json_create(grpc_json_type type); +void grpc_json_destroy(grpc_json* json); #endif /* GRPC_CORE_LIB_JSON_JSON_H */ diff --git a/src/core/lib/slice/slice.c b/src/core/lib/slice/slice.c index 3dac18df61..52977e6d9a 100644 --- a/src/core/lib/slice/slice.c +++ b/src/core/lib/slice/slice.c @@ -348,3 +348,11 @@ int grpc_slice_str_cmp(grpc_slice a, const char *b) { if (d != 0) return d; return memcmp(GRPC_SLICE_START_PTR(a), b, b_length); } + +int grpc_slice_is_equivalent(grpc_slice a, grpc_slice b) { + if (a.refcount == NULL || b.refcount == NULL) { + return grpc_slice_cmp(a, b) == 0; + } + return a.data.refcounted.length == b.data.refcounted.length && + a.data.refcounted.bytes == b.data.refcounted.bytes; +} diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c index dc243bf0bf..f10a30f0fd 100644 --- a/src/core/lib/support/string.c +++ b/src/core/lib/support/string.c @@ -34,7 +34,9 @@ #include "src/core/lib/support/string.h" #include <ctype.h> +#include <limits.h> #include <stddef.h> +#include <stdlib.h> #include <string.h> #include <grpc/support/alloc.h> @@ -189,6 +191,13 @@ int int64_ttoa(int64_t value, char *string) { return i; } +int gpr_parse_nonnegative_int(const char *value) { + char *end; + long result = strtol(value, &end, 0); + if (*end != '\0' || result < 0 || result > INT_MAX) return -1; + return (int)result; +} + char *gpr_leftpad(const char *str, char flag, size_t length) { const size_t str_length = strlen(str); const size_t out_length = str_length > length ? str_length : length; diff --git a/src/core/lib/support/string.h b/src/core/lib/support/string.h index 13891d0b9e..e933e2eb46 100644 --- a/src/core/lib/support/string.h +++ b/src/core/lib/support/string.h @@ -77,6 +77,9 @@ NOTE: This function ensures sufficient bit width even on Win x64, where long is 32bit is size.*/ int int64_ttoa(int64_t value, char *output); +// Parses a non-negative number from a value string. Returns -1 on error. +int gpr_parse_nonnegative_int(const char *value); + /* Reverse a run of bytes */ void gpr_reverse_bytes(char *str, int len); diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c index 8d2386e6a0..1e0f3eeca5 100644 --- a/src/core/lib/surface/call.c +++ b/src/core/lib/surface/call.c @@ -637,9 +637,6 @@ static int prepare_application_metadata(grpc_call *call, int count, if (call->send_extra_metadata_count == 0) { prepend_extra_metadata = 0; } else { - for (i = 0; i < call->send_extra_metadata_count; i++) { - GRPC_MDELEM_REF(call->send_extra_metadata[i].md); - } for (i = 1; i < call->send_extra_metadata_count; i++) { call->send_extra_metadata[i].prev = &call->send_extra_metadata[i - 1]; } @@ -685,6 +682,7 @@ static int prepare_application_metadata(grpc_call *call, int count, &call->send_extra_metadata[call->send_extra_metadata_count - 1]; batch->list.head->prev = NULL; batch->list.tail->next = NULL; + call->send_extra_metadata_count = 0; break; case 3: { /* prepend AND md */ @@ -700,6 +698,7 @@ static int prepare_application_metadata(grpc_call *call, int count, batch->list.tail = linked_from_md(last_md); batch->list.head->prev = NULL; batch->list.tail->next = NULL; + call->send_extra_metadata_count = 0; break; } default: diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c index 78bd13bbbd..62d7afc8da 100644 --- a/src/core/lib/surface/server.c +++ b/src/core/lib/surface/server.c @@ -195,6 +195,7 @@ struct grpc_server { grpc_completion_queue **cqs; grpc_pollset **pollsets; size_t cq_count; + size_t pollset_count; bool started; /* The two following mutexes control access to server-state @@ -1086,7 +1087,7 @@ void grpc_server_start(grpc_server *server) { GRPC_API_TRACE("grpc_server_start(server=%p)", 1, (server)); server->started = true; - size_t pollset_count = 0; + server->pollset_count = 0; server->pollsets = gpr_malloc(sizeof(grpc_pollset *) * server->cq_count); server->request_freelist_per_cq = gpr_malloc(sizeof(*server->request_freelist_per_cq) * server->cq_count); @@ -1094,7 +1095,8 @@ void grpc_server_start(grpc_server *server) { gpr_malloc(sizeof(*server->requested_calls_per_cq) * server->cq_count); for (i = 0; i < server->cq_count; i++) { if (!grpc_cq_is_non_listening_server_cq(server->cqs[i])) { - server->pollsets[pollset_count++] = grpc_cq_pollset(server->cqs[i]); + server->pollsets[server->pollset_count++] = + grpc_cq_pollset(server->cqs[i]); } server->request_freelist_per_cq[i] = gpr_stack_lockfree_create((size_t)server->max_requested_calls_per_cq); @@ -1113,7 +1115,8 @@ void grpc_server_start(grpc_server *server) { } for (l = server->listeners; l; l = l->next) { - l->start(&exec_ctx, server, l->arg, server->pollsets, pollset_count); + l->start(&exec_ctx, server, l->arg, server->pollsets, + server->pollset_count); } grpc_exec_ctx_finish(&exec_ctx); @@ -1121,7 +1124,7 @@ void grpc_server_start(grpc_server *server) { void grpc_server_get_pollsets(grpc_server *server, grpc_pollset ***pollsets, size_t *pollset_count) { - *pollset_count = server->cq_count; + *pollset_count = server->pollset_count; *pollsets = server->pollsets; } diff --git a/src/core/lib/transport/mdstr_hash_table.c b/src/core/lib/transport/mdstr_hash_table.c index 8e914c420b..3d01e56df7 100644 --- a/src/core/lib/transport/mdstr_hash_table.c +++ b/src/core/lib/transport/mdstr_hash_table.c @@ -41,7 +41,6 @@ struct grpc_mdstr_hash_table { gpr_refcount refs; - size_t num_entries; size_t size; grpc_mdstr_hash_table_entry* entries; }; @@ -77,7 +76,6 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_create( grpc_mdstr_hash_table* table = gpr_malloc(sizeof(*table)); memset(table, 0, sizeof(*table)); gpr_ref_init(&table->refs, 1); - table->num_entries = num_entries; // Quadratic probing gets best performance when the table is no more // than half full. table->size = num_entries * 2; @@ -96,7 +94,7 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table) { return table; } -int grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table) { +void grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table) { if (table != NULL && gpr_unref(&table->refs)) { for (size_t i = 0; i < table->size; ++i) { grpc_mdstr_hash_table_entry* entry = &table->entries[i]; @@ -107,13 +105,7 @@ int grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table) { } gpr_free(table->entries); gpr_free(table); - return 1; } - return 0; -} - -size_t grpc_mdstr_hash_table_num_entries(const grpc_mdstr_hash_table* table) { - return table->num_entries; } void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table, @@ -123,35 +115,3 @@ void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table, if (idx == table->size) return NULL; // Not found. return table->entries[idx].value; } - -int grpc_mdstr_hash_table_cmp(const grpc_mdstr_hash_table* table1, - const grpc_mdstr_hash_table* table2) { - // Compare by num_entries. - if (table1->num_entries < table2->num_entries) return -1; - if (table1->num_entries > table2->num_entries) return 1; - for (size_t i = 0; i < table1->num_entries; ++i) { - grpc_mdstr_hash_table_entry* e1 = &table1->entries[i]; - grpc_mdstr_hash_table_entry* e2 = &table2->entries[i]; - // Compare keys by hash value. - if (e1->key->hash < e2->key->hash) return -1; - if (e1->key->hash > e2->key->hash) return 1; - // Compare by vtable (pointer equality). - if (e1->vtable < e2->vtable) return -1; - if (e1->vtable > e2->vtable) return 1; - // Compare values via vtable. - const int value_result = e1->vtable->compare_value(e1->value, e2->value); - if (value_result != 0) return value_result; - } - return 0; -} - -void grpc_mdstr_hash_table_iterate( - const grpc_mdstr_hash_table* table, - void (*func)(const grpc_mdstr_hash_table_entry* entry, void* user_data), - void* user_data) { - for (size_t i = 0; i < table->size; ++i) { - if (table->entries[i].key != NULL) { - func(&table->entries[i], user_data); - } - } -} diff --git a/src/core/lib/transport/mdstr_hash_table.h b/src/core/lib/transport/mdstr_hash_table.h index bceb4df93d..8982ec3a8d 100644 --- a/src/core/lib/transport/mdstr_hash_table.h +++ b/src/core/lib/transport/mdstr_hash_table.h @@ -51,7 +51,6 @@ typedef struct grpc_mdstr_hash_table grpc_mdstr_hash_table; typedef struct grpc_mdstr_hash_table_vtable { void (*destroy_value)(void* value); void* (*copy_value)(void* value); - int (*compare_value)(void* value1, void* value2); } grpc_mdstr_hash_table_vtable; typedef struct grpc_mdstr_hash_table_entry { @@ -67,26 +66,11 @@ grpc_mdstr_hash_table* grpc_mdstr_hash_table_create( size_t num_entries, grpc_mdstr_hash_table_entry* entries); grpc_mdstr_hash_table* grpc_mdstr_hash_table_ref(grpc_mdstr_hash_table* table); -/** Returns 1 when \a table is destroyed. */ -int grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table); - -/** Returns the number of entries in \a table. */ -size_t grpc_mdstr_hash_table_num_entries(const grpc_mdstr_hash_table* table); +void grpc_mdstr_hash_table_unref(grpc_mdstr_hash_table* table); /** Returns the value from \a table associated with \a key. Returns NULL if \a key is not found. */ void* grpc_mdstr_hash_table_get(const grpc_mdstr_hash_table* table, const grpc_mdstr* key); -/** Compares two hash tables. - The sort order is stable but undefined. */ -int grpc_mdstr_hash_table_cmp(const grpc_mdstr_hash_table* table1, - const grpc_mdstr_hash_table* table2); - -/** Iterates over the entries in \a table, calling \a func for each entry. */ -void grpc_mdstr_hash_table_iterate( - const grpc_mdstr_hash_table* table, - void (*func)(const grpc_mdstr_hash_table_entry* entry, void* user_data), - void* user_data); - #endif /* GRPC_CORE_LIB_TRANSPORT_MDSTR_HASH_TABLE_H */ diff --git a/src/core/lib/transport/method_config.c b/src/core/lib/transport/method_config.c deleted file mode 100644 index 57d97700bf..0000000000 --- a/src/core/lib/transport/method_config.c +++ /dev/null @@ -1,340 +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/lib/transport/method_config.h" - -#include <string.h> - -#include <grpc/impl/codegen/grpc_types.h> -#include <grpc/support/alloc.h> -#include <grpc/support/log.h> -#include <grpc/support/string_util.h> -#include <grpc/support/time.h> - -#include "src/core/lib/transport/mdstr_hash_table.h" -#include "src/core/lib/transport/metadata.h" - -// -// grpc_method_config -// - -// bool vtable - -static void* bool_copy(void* valuep) { - bool value = *(bool*)valuep; - bool* new_value = gpr_malloc(sizeof(bool)); - *new_value = value; - return new_value; -} - -static int bool_cmp(void* v1, void* v2) { - bool b1 = *(bool*)v1; - bool b2 = *(bool*)v2; - if (!b1 && b2) return -1; - if (b1 && !b2) return 1; - return 0; -} - -static grpc_mdstr_hash_table_vtable bool_vtable = {gpr_free, bool_copy, - bool_cmp}; - -// timespec vtable - -static void* timespec_copy(void* valuep) { - gpr_timespec value = *(gpr_timespec*)valuep; - gpr_timespec* new_value = gpr_malloc(sizeof(gpr_timespec)); - *new_value = value; - return new_value; -} - -static int timespec_cmp(void* v1, void* v2) { - return gpr_time_cmp(*(gpr_timespec*)v1, *(gpr_timespec*)v2); -} - -static grpc_mdstr_hash_table_vtable timespec_vtable = {gpr_free, timespec_copy, - timespec_cmp}; - -// int32 vtable - -static void* int32_copy(void* valuep) { - int32_t value = *(int32_t*)valuep; - int32_t* new_value = gpr_malloc(sizeof(int32_t)); - *new_value = value; - return new_value; -} - -static int int32_cmp(void* v1, void* v2) { - int32_t i1 = *(int32_t*)v1; - int32_t i2 = *(int32_t*)v2; - if (i1 < i2) return -1; - if (i1 > i2) return 1; - return 0; -} - -static grpc_mdstr_hash_table_vtable int32_vtable = {gpr_free, int32_copy, - int32_cmp}; - -// Hash table keys. -#define GRPC_METHOD_CONFIG_WAIT_FOR_READY "grpc.wait_for_ready" // bool -#define GRPC_METHOD_CONFIG_TIMEOUT "grpc.timeout" // gpr_timespec -#define GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES \ - "grpc.max_request_message_bytes" // int32 -#define GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES \ - "grpc.max_response_message_bytes" // int32 - -struct grpc_method_config { - grpc_mdstr_hash_table* table; - grpc_mdstr* wait_for_ready_key; - grpc_mdstr* timeout_key; - grpc_mdstr* max_request_message_bytes_key; - grpc_mdstr* max_response_message_bytes_key; -}; - -grpc_method_config* grpc_method_config_create( - bool* wait_for_ready, gpr_timespec* timeout, - int32_t* max_request_message_bytes, int32_t* max_response_message_bytes) { - grpc_method_config* method_config = gpr_malloc(sizeof(grpc_method_config)); - memset(method_config, 0, sizeof(grpc_method_config)); - method_config->wait_for_ready_key = - grpc_mdstr_from_string(GRPC_METHOD_CONFIG_WAIT_FOR_READY); - method_config->timeout_key = - grpc_mdstr_from_string(GRPC_METHOD_CONFIG_TIMEOUT); - method_config->max_request_message_bytes_key = - grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_REQUEST_MESSAGE_BYTES); - method_config->max_response_message_bytes_key = - grpc_mdstr_from_string(GRPC_METHOD_CONFIG_MAX_RESPONSE_MESSAGE_BYTES); - grpc_mdstr_hash_table_entry entries[4]; - size_t num_entries = 0; - if (wait_for_ready != NULL) { - entries[num_entries].key = method_config->wait_for_ready_key; - entries[num_entries].value = wait_for_ready; - entries[num_entries].vtable = &bool_vtable; - ++num_entries; - } - if (timeout != NULL) { - entries[num_entries].key = method_config->timeout_key; - entries[num_entries].value = timeout; - entries[num_entries].vtable = ×pec_vtable; - ++num_entries; - } - if (max_request_message_bytes != NULL) { - entries[num_entries].key = method_config->max_request_message_bytes_key; - entries[num_entries].value = max_request_message_bytes; - entries[num_entries].vtable = &int32_vtable; - ++num_entries; - } - if (max_response_message_bytes != NULL) { - entries[num_entries].key = method_config->max_response_message_bytes_key; - entries[num_entries].value = max_response_message_bytes; - entries[num_entries].vtable = &int32_vtable; - ++num_entries; - } - method_config->table = grpc_mdstr_hash_table_create(num_entries, entries); - return method_config; -} - -grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config) { - grpc_mdstr_hash_table_ref(method_config->table); - return method_config; -} - -void grpc_method_config_unref(grpc_method_config* method_config) { - if (grpc_mdstr_hash_table_unref(method_config->table)) { - GRPC_MDSTR_UNREF(method_config->wait_for_ready_key); - GRPC_MDSTR_UNREF(method_config->timeout_key); - GRPC_MDSTR_UNREF(method_config->max_request_message_bytes_key); - GRPC_MDSTR_UNREF(method_config->max_response_message_bytes_key); - gpr_free(method_config); - } -} - -int grpc_method_config_cmp(const grpc_method_config* method_config1, - const grpc_method_config* method_config2) { - return grpc_mdstr_hash_table_cmp(method_config1->table, - method_config2->table); -} - -const bool* grpc_method_config_get_wait_for_ready( - const grpc_method_config* method_config) { - return grpc_mdstr_hash_table_get(method_config->table, - method_config->wait_for_ready_key); -} - -const gpr_timespec* grpc_method_config_get_timeout( - const grpc_method_config* method_config) { - return grpc_mdstr_hash_table_get(method_config->table, - method_config->timeout_key); -} - -const int32_t* grpc_method_config_get_max_request_message_bytes( - const grpc_method_config* method_config) { - return grpc_mdstr_hash_table_get( - method_config->table, method_config->max_request_message_bytes_key); -} - -const int32_t* grpc_method_config_get_max_response_message_bytes( - const grpc_method_config* method_config) { - return grpc_mdstr_hash_table_get( - method_config->table, method_config->max_response_message_bytes_key); -} - -// -// grpc_method_config_table -// - -static void method_config_unref(void* valuep) { - grpc_method_config_unref(valuep); -} - -static void* method_config_ref(void* valuep) { - return grpc_method_config_ref(valuep); -} - -static int method_config_cmp(void* valuep1, void* valuep2) { - return grpc_method_config_cmp(valuep1, valuep2); -} - -static const grpc_mdstr_hash_table_vtable method_config_table_vtable = { - method_config_unref, method_config_ref, method_config_cmp}; - -grpc_method_config_table* grpc_method_config_table_create( - size_t num_entries, grpc_method_config_table_entry* entries) { - grpc_mdstr_hash_table_entry* hash_table_entries = - gpr_malloc(sizeof(grpc_mdstr_hash_table_entry) * num_entries); - for (size_t i = 0; i < num_entries; ++i) { - hash_table_entries[i].key = entries[i].method_name; - hash_table_entries[i].value = entries[i].method_config; - hash_table_entries[i].vtable = &method_config_table_vtable; - } - grpc_method_config_table* method_config_table = - grpc_mdstr_hash_table_create(num_entries, hash_table_entries); - gpr_free(hash_table_entries); - return method_config_table; -} - -grpc_method_config_table* grpc_method_config_table_ref( - grpc_method_config_table* table) { - return grpc_mdstr_hash_table_ref(table); -} - -void grpc_method_config_table_unref(grpc_method_config_table* table) { - grpc_mdstr_hash_table_unref(table); -} - -int grpc_method_config_table_cmp(const grpc_method_config_table* table1, - const grpc_method_config_table* table2) { - return grpc_mdstr_hash_table_cmp(table1, table2); -} - -void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table, - const grpc_mdstr* path) { - void* value = grpc_mdstr_hash_table_get(table, path); - // If we didn't find a match for the path, try looking for a wildcard - // entry (i.e., change "/service/method" to "/service/*"). - if (value == NULL) { - const char* path_str = grpc_mdstr_as_c_string(path); - const char* sep = strrchr(path_str, '/') + 1; - const size_t len = (size_t)(sep - path_str); - char* buf = gpr_malloc(len + 2); // '*' and NUL - memcpy(buf, path_str, len); - buf[len] = '*'; - buf[len + 1] = '\0'; - grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf); - gpr_free(buf); - value = grpc_mdstr_hash_table_get(table, wildcard_path); - GRPC_MDSTR_UNREF(wildcard_path); - } - return value; -} - -static void* copy_arg(void* p) { return grpc_method_config_table_ref(p); } - -static void destroy_arg(void* p) { grpc_method_config_table_unref(p); } - -static int cmp_arg(void* p1, void* p2) { - return grpc_method_config_table_cmp(p1, p2); -} - -static grpc_arg_pointer_vtable arg_vtable = {copy_arg, destroy_arg, cmp_arg}; - -grpc_arg grpc_method_config_table_create_channel_arg( - grpc_method_config_table* table) { - grpc_arg arg; - arg.type = GRPC_ARG_POINTER; - arg.key = GRPC_ARG_SERVICE_CONFIG; - arg.value.pointer.p = table; - arg.value.pointer.vtable = &arg_vtable; - return arg; -} - -// State used by convert_entry() below. -typedef struct conversion_state { - void* (*convert_value)(const grpc_method_config* method_config); - const grpc_mdstr_hash_table_vtable* vtable; - size_t num_entries; - grpc_mdstr_hash_table_entry* entries; -} conversion_state; - -// A function to be passed to grpc_mdstr_hash_table_iterate() to create -// a copy of the entries. -static void convert_entry(const grpc_mdstr_hash_table_entry* entry, - void* user_data) { - conversion_state* state = user_data; - state->entries[state->num_entries].key = GRPC_MDSTR_REF(entry->key); - state->entries[state->num_entries].value = state->convert_value(entry->value); - state->entries[state->num_entries].vtable = state->vtable; - ++state->num_entries; -} - -grpc_mdstr_hash_table* grpc_method_config_table_convert( - const grpc_method_config_table* table, - void* (*convert_value)(const grpc_method_config* method_config), - const grpc_mdstr_hash_table_vtable* vtable) { - // Create an array of the entries in the table with converted values. - conversion_state state; - state.convert_value = convert_value; - state.vtable = vtable; - state.num_entries = 0; - state.entries = gpr_malloc(sizeof(grpc_mdstr_hash_table_entry) * - grpc_mdstr_hash_table_num_entries(table)); - grpc_mdstr_hash_table_iterate(table, convert_entry, &state); - // Create a new table based on the array we just constructed. - grpc_mdstr_hash_table* new_table = - grpc_mdstr_hash_table_create(state.num_entries, state.entries); - // Clean up the array. - for (size_t i = 0; i < state.num_entries; ++i) { - GRPC_MDSTR_UNREF(state.entries[i].key); - vtable->destroy_value(state.entries[i].value); - } - gpr_free(state.entries); - // Return the new table. - return new_table; -} diff --git a/src/core/lib/transport/method_config.h b/src/core/lib/transport/method_config.h deleted file mode 100644 index 58fedd9436..0000000000 --- a/src/core/lib/transport/method_config.h +++ /dev/null @@ -1,136 +0,0 @@ -// -// Copyright 2016, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -#ifndef GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H -#define GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H - -#include <stdbool.h> - -#include <grpc/impl/codegen/gpr_types.h> -#include <grpc/impl/codegen/grpc_types.h> - -#include "src/core/lib/transport/mdstr_hash_table.h" -#include "src/core/lib/transport/metadata.h" - -/// Per-method configuration. -typedef struct grpc_method_config grpc_method_config; - -/// Creates a grpc_method_config with the specified parameters. -/// Any parameter may be NULL to indicate that the value is unset. -/// -/// \a wait_for_ready indicates whether the client should wait until the -/// request deadline for the channel to become ready, even if there is a -/// temporary failure before the deadline while attempting to connect. -/// -/// \a timeout indicates the timeout for calls. -/// -/// \a max_request_message_bytes and \a max_response_message_bytes -/// indicate the maximum sizes of the request (checked when sending) and -/// response (checked when receiving) messages. -grpc_method_config* grpc_method_config_create( - bool* wait_for_ready, gpr_timespec* timeout, - int32_t* max_request_message_bytes, int32_t* max_response_message_bytes); - -grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config); -void grpc_method_config_unref(grpc_method_config* method_config); - -/// Compares two grpc_method_configs. -/// The sort order is stable but undefined. -int grpc_method_config_cmp(const grpc_method_config* method_config1, - const grpc_method_config* method_config2); - -/// These methods return NULL if the requested field is unset. -/// The caller does NOT take ownership of the result. -const bool* grpc_method_config_get_wait_for_ready( - const grpc_method_config* method_config); -const gpr_timespec* grpc_method_config_get_timeout( - const grpc_method_config* method_config); -const int32_t* grpc_method_config_get_max_request_message_bytes( - const grpc_method_config* method_config); -const int32_t* grpc_method_config_get_max_response_message_bytes( - const grpc_method_config* method_config); - -/// A table of method configs. -typedef grpc_mdstr_hash_table grpc_method_config_table; - -typedef struct grpc_method_config_table_entry { - /// The name is of one of the following forms: - /// service/method -- specifies exact service and method name - /// service/* -- matches all methods for the specified service - grpc_mdstr* method_name; - grpc_method_config* method_config; -} grpc_method_config_table_entry; - -/// Takes new references to all keys and values in \a entries. -grpc_method_config_table* grpc_method_config_table_create( - size_t num_entries, grpc_method_config_table_entry* entries); - -grpc_method_config_table* grpc_method_config_table_ref( - grpc_method_config_table* table); -void grpc_method_config_table_unref(grpc_method_config_table* table); - -/// Compares two grpc_method_config_tables. -/// The sort order is stable but undefined. -int grpc_method_config_table_cmp(const grpc_method_config_table* table1, - const grpc_method_config_table* table2); - -/// Gets the method config for the specified \a path, which should be of -/// the form "/service/method". -/// Returns NULL if the method has no config. -/// Caller does NOT own a reference to the result. -/// -/// Note: This returns a void* instead of a grpc_method_config* so that -/// it can also be used for tables constructed via -/// grpc_method_config_table_convert(). -void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table, - const grpc_mdstr* path); - -/// Returns a channel arg containing \a table. -grpc_arg grpc_method_config_table_create_channel_arg( - grpc_method_config_table* table); - -/// Generates a new table from \a table whose values are converted to a -/// new form via the \a convert_value function. The new table will use -/// \a vtable for its values. -/// -/// This is generally used to convert the table's value type from -/// grpc_method_config to a simple struct containing only the parameters -/// relevant to a particular filter, thus avoiding the need for a hash -/// table lookup on the fast path. In that scenario, \a convert_value -/// will return a new instance of the struct containing the values from -/// the grpc_method_config, and \a vtable provides the methods for -/// operating on the struct type. -grpc_mdstr_hash_table* grpc_method_config_table_convert( - const grpc_method_config_table* table, - void* (*convert_value)(const grpc_method_config* method_config), - const grpc_mdstr_hash_table_vtable* vtable); - -#endif /* GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H */ diff --git a/src/core/lib/transport/service_config.c b/src/core/lib/transport/service_config.c new file mode 100644 index 0000000000..2e2b59e3f7 --- /dev/null +++ b/src/core/lib/transport/service_config.c @@ -0,0 +1,249 @@ +// +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "src/core/lib/transport/service_config.h" + +#include <string.h> + +#include <grpc/impl/codegen/grpc_types.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/string_util.h> + +#include "src/core/lib/json/json.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/mdstr_hash_table.h" + +// The main purpose of the code here is to parse the service config in +// JSON form, which will look like this: +// +// { +// "loadBalancingPolicy": "string", // optional +// "methodConfig": [ // array of one or more method_config objects +// { +// "name": [ // array of one or more name objects +// { +// "service": "string", // required +// "method": "string", // optional +// } +// ], +// // remaining fields are optional. +// // see https://developers.google.com/protocol-buffers/docs/proto3#json +// // for format details. +// "waitForReady": bool, +// "timeout": "duration_string", +// "maxRequestMessageBytes": "int64_string", +// "maxResponseMessageBytes": "int64_string", +// } +// ] +// } + +struct grpc_service_config { + char* json_string; // Underlying storage for json_tree. + grpc_json* json_tree; +}; + +grpc_service_config* grpc_service_config_create(const char* json_string) { + grpc_service_config* service_config = gpr_malloc(sizeof(*service_config)); + service_config->json_string = gpr_strdup(json_string); + service_config->json_tree = + grpc_json_parse_string(service_config->json_string); + if (service_config->json_tree == NULL) { + gpr_log(GPR_INFO, "failed to parse JSON for service config"); + gpr_free(service_config->json_string); + gpr_free(service_config); + return NULL; + } + return service_config; +} + +void grpc_service_config_destroy(grpc_service_config* service_config) { + grpc_json_destroy(service_config->json_tree); + gpr_free(service_config->json_string); + gpr_free(service_config); +} + +const char* grpc_service_config_get_lb_policy_name( + const grpc_service_config* service_config) { + const grpc_json* json = service_config->json_tree; + if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return NULL; + const char* lb_policy_name = NULL; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key == NULL) return NULL; + if (strcmp(field->key, "loadBalancingPolicy") == 0) { + if (lb_policy_name != NULL) return NULL; // Duplicate. + if (field->type != GRPC_JSON_STRING) return NULL; + lb_policy_name = field->value; + } + } + return lb_policy_name; +} + +// Returns the number of names specified in the method config \a json. +static size_t count_names_in_method_config_json(grpc_json* json) { + size_t num_names = 0; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key != NULL && strcmp(field->key, "name") == 0) ++num_names; + } + return num_names; +} + +// Returns a path string for the JSON name object specified by \a json. +// Returns NULL on error. Caller takes ownership of result. +static char* parse_json_method_name(grpc_json* json) { + if (json->type != GRPC_JSON_OBJECT) return NULL; + const char* service_name = NULL; + const char* method_name = NULL; + for (grpc_json* child = json->child; child != NULL; child = child->next) { + if (child->key == NULL) return NULL; + if (child->type != GRPC_JSON_STRING) return NULL; + if (strcmp(child->key, "service") == 0) { + if (service_name != NULL) return NULL; // Duplicate. + if (child->value == NULL) return NULL; + service_name = child->value; + } else if (strcmp(child->key, "method") == 0) { + if (method_name != NULL) return NULL; // Duplicate. + if (child->value == NULL) return NULL; + method_name = child->value; + } + } + if (service_name == NULL) return NULL; // Required field. + char* path; + gpr_asprintf(&path, "/%s/%s", service_name, + method_name == NULL ? "*" : method_name); + return path; +} + +// Parses the method config from \a json. Adds an entry to \a entries for +// each name found, incrementing \a idx for each entry added. +// Returns false on error. +static bool parse_json_method_config( + grpc_json* json, void* (*create_value)(const grpc_json* method_config_json), + const grpc_mdstr_hash_table_vtable* vtable, + grpc_mdstr_hash_table_entry* entries, size_t* idx) { + // Construct value. + void* method_config = create_value(json); + if (method_config == NULL) return false; + // Construct list of paths. + bool success = false; + gpr_strvec paths; + gpr_strvec_init(&paths); + for (grpc_json* child = json->child; child != NULL; child = child->next) { + if (child->key == NULL) continue; + if (strcmp(child->key, "name") == 0) { + if (child->type != GRPC_JSON_ARRAY) goto done; + for (grpc_json* name = child->child; name != NULL; name = name->next) { + char* path = parse_json_method_name(name); + gpr_strvec_add(&paths, path); + } + } + } + if (paths.count == 0) goto done; // No names specified. + // Add entry for each path. + for (size_t i = 0; i < paths.count; ++i) { + entries[*idx].key = grpc_mdstr_from_string(paths.strs[i]); + entries[*idx].value = vtable->copy_value(method_config); + entries[*idx].vtable = vtable; + ++*idx; + } + success = true; +done: + vtable->destroy_value(method_config); + gpr_strvec_destroy(&paths); + return success; +} + +grpc_mdstr_hash_table* grpc_service_config_create_method_config_table( + const grpc_service_config* service_config, + void* (*create_value)(const grpc_json* method_config_json), + const grpc_mdstr_hash_table_vtable* vtable) { + const grpc_json* json = service_config->json_tree; + // Traverse parsed JSON tree. + if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return NULL; + size_t num_entries = 0; + grpc_mdstr_hash_table_entry* entries = NULL; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key == NULL) return NULL; + if (strcmp(field->key, "methodConfig") == 0) { + if (entries != NULL) return NULL; // Duplicate. + if (field->type != GRPC_JSON_ARRAY) return NULL; + // Find number of entries. + for (grpc_json* method = field->child; method != NULL; + method = method->next) { + num_entries += count_names_in_method_config_json(method); + } + // Populate method config table entries. + entries = gpr_malloc(num_entries * sizeof(grpc_mdstr_hash_table_entry)); + size_t idx = 0; + for (grpc_json* method = field->child; method != NULL; + method = method->next) { + if (!parse_json_method_config(method, create_value, vtable, entries, + &idx)) { + return NULL; + } + } + GPR_ASSERT(idx == num_entries); + } + } + // Instantiate method config table. + grpc_mdstr_hash_table* method_config_table = NULL; + if (entries != NULL) { + method_config_table = grpc_mdstr_hash_table_create(num_entries, entries); + // Clean up. + for (size_t i = 0; i < num_entries; ++i) { + GRPC_MDSTR_UNREF(entries[i].key); + vtable->destroy_value(entries[i].value); + } + gpr_free(entries); + } + return method_config_table; +} + +void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table, + const grpc_mdstr* path) { + void* value = grpc_mdstr_hash_table_get(table, path); + // If we didn't find a match for the path, try looking for a wildcard + // entry (i.e., change "/service/method" to "/service/*"). + if (value == NULL) { + const char* path_str = grpc_mdstr_as_c_string(path); + const char* sep = strrchr(path_str, '/') + 1; + const size_t len = (size_t)(sep - path_str); + char* buf = gpr_malloc(len + 2); // '*' and NUL + memcpy(buf, path_str, len); + buf[len] = '*'; + buf[len + 1] = '\0'; + grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf); + gpr_free(buf); + value = grpc_mdstr_hash_table_get(table, wildcard_path); + GRPC_MDSTR_UNREF(wildcard_path); + } + return value; +} diff --git a/src/core/lib/transport/service_config.h b/src/core/lib/transport/service_config.h new file mode 100644 index 0000000000..2ffe475193 --- /dev/null +++ b/src/core/lib/transport/service_config.h @@ -0,0 +1,70 @@ +// +// Copyright 2016, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H +#define GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H + +#include <grpc/impl/codegen/grpc_types.h> + +#include "src/core/lib/json/json.h" +#include "src/core/lib/transport/mdstr_hash_table.h" + +typedef struct grpc_service_config grpc_service_config; + +grpc_service_config* grpc_service_config_create(const char* json_string); +void grpc_service_config_destroy(grpc_service_config* service_config); + +/// Gets the LB policy name from \a service_config. +/// Returns NULL if no LB policy name was specified. +/// Caller does NOT take ownership. +const char* grpc_service_config_get_lb_policy_name( + const grpc_service_config* service_config); + +/// Creates a method config table based on the data in \a json. +/// The table's keys are request paths. The table's value type is +/// returned by \a create_value(), based on data parsed from the JSON tree. +/// \a vtable provides methods used to manage the values. +/// Returns NULL on error. +grpc_mdstr_hash_table* grpc_service_config_create_method_config_table( + const grpc_service_config* service_config, + void* (*create_value)(const grpc_json* method_config_json), + const grpc_mdstr_hash_table_vtable* vtable); + +/// A helper function for looking up values in the table returned by +/// \a grpc_service_config_create_method_config_table(). +/// Gets the method config for the specified \a path, which should be of +/// the form "/service/method". +/// Returns NULL if the method has no config. +/// Caller does NOT own a reference to the result. +void* grpc_method_config_table_get(const grpc_mdstr_hash_table* table, + const grpc_mdstr* path); + +#endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */ |