diff options
author | Mark D. Roth <roth@google.com> | 2016-11-02 14:07:36 -0700 |
---|---|---|
committer | Mark D. Roth <roth@google.com> | 2016-11-02 14:07:36 -0700 |
commit | c968e60e2ef60cd20353c34fb6260a63ce9db031 (patch) | |
tree | 5bb0aeeadd49a7566d6500c63099ebfb783ed488 | |
parent | 9dd2c7da2f82eb27dfbef01a9c133e403dcffc45 (diff) |
Use JSON for service config channel arg.
-rw-r--r-- | src/core/ext/client_channel/client_channel.c | 51 | ||||
-rw-r--r-- | src/core/lib/channel/message_size_filter.c | 34 | ||||
-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/transport/method_config.c | 122 | ||||
-rw-r--r-- | src/core/lib/transport/method_config.h | 7 |
6 files changed, 202 insertions, 24 deletions
diff --git a/src/core/ext/client_channel/client_channel.c b/src/core/ext/client_channel/client_channel.c index ff773ac334..e569af68e9 100644 --- a/src/core/ext/client_channel/client_channel.c +++ b/src/core/ext/client_channel/client_channel.c @@ -94,17 +94,44 @@ static int method_parameters_cmp(void *value1, void *value2) { static const grpc_mdstr_hash_table_vtable method_parameters_vtable = { gpr_free, method_parameters_copy, method_parameters_cmp}; -static void *method_config_convert_value( - const grpc_method_config *method_config) { +static void *method_config_convert_value(const grpc_json *json) { + wait_for_ready_value wait_for_ready = WAIT_FOR_READY_UNSET; + gpr_timespec timeout = { 0, 0, GPR_TIMESPAN }; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key == NULL) continue; + if (strcmp(field->key, "wait_for_ready") == 0) { + if (wait_for_ready != WAIT_FOR_READY_UNSET) return NULL; // Duplicate. + if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) { + return NULL; + } + wait_for_ready = field->type == GRPC_JSON_TRUE; + } else if (strcmp(field->key, "timeout") == 0) { + if (timeout.tv_sec > 0 || timeout.tv_nsec > 0) return NULL; // Duplicate. + if (field->type != GRPC_JSON_OBJECT) return NULL; + if (field->child == NULL) return NULL; + for (grpc_json* subfield = field->child; subfield != NULL; + subfield = subfield->next) { + if (subfield->key == NULL) return NULL; + if (strcmp(subfield->key, "seconds") == 0) { + if (timeout.tv_sec > 0) return NULL; // Duplicate. + if (subfield->type != GRPC_JSON_NUMBER) return NULL; + timeout.tv_sec = gpr_parse_nonnegative_number(subfield->value); + if (timeout.tv_sec == -1) return NULL; + } else if (strcmp(subfield->key, "nanos") == 0) { + if (timeout.tv_nsec > 0) return NULL; // Duplicate. + if (subfield->type != GRPC_JSON_NUMBER) return NULL; + timeout.tv_nsec = gpr_parse_nonnegative_number(subfield->value); + if (timeout.tv_nsec == -1) return NULL; + } else { + // Unknown key. + return NULL; + } + } + } + } method_parameters *value = gpr_malloc(sizeof(method_parameters)); - const gpr_timespec *timeout = grpc_method_config_get_timeout(method_config); - value->timeout = timeout != NULL ? *timeout : gpr_time_0(GPR_TIMESPAN); - const bool *wait_for_ready = - grpc_method_config_get_wait_for_ready(method_config); - value->wait_for_ready = - wait_for_ready == NULL - ? WAIT_FOR_READY_UNSET - : (wait_for_ready ? WAIT_FOR_READY_TRUE : WAIT_FOR_READY_FALSE); + value->timeout = timeout; + value->wait_for_ready = wait_for_ready; return value; } @@ -285,8 +312,8 @@ static void on_resolver_result_changed(grpc_exec_ctx *exec_ctx, void *arg, grpc_channel_args_find(lb_policy_args.args, GRPC_ARG_SERVICE_CONFIG); if (channel_arg != NULL) { GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); - method_params_table = grpc_method_config_table_convert( - (grpc_method_config_table *)channel_arg->value.pointer.p, + method_params_table = grpc_method_config_table_create_from_json( + (grpc_json *)channel_arg->value.pointer.p, method_config_convert_value, &method_parameters_vtable); } grpc_channel_args_destroy(chand->resolver_result); diff --git a/src/core/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c index 7dc5ae0df1..4723ab8098 100644 --- a/src/core/lib/channel/message_size_filter.c +++ b/src/core/lib/channel/message_size_filter.c @@ -39,6 +39,7 @@ #include <grpc/support/string_util.h> #include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/support/string.h" #include "src/core/lib/transport/method_config.h" #define DEFAULT_MAX_SEND_MESSAGE_LENGTH -1 // Unlimited. @@ -69,17 +70,26 @@ static int message_size_limits_cmp(void* value1, void* value2) { static const grpc_mdstr_hash_table_vtable message_size_limits_vtable = { gpr_free, message_size_limits_copy, message_size_limits_cmp}; -static void* method_config_convert_value( - const grpc_method_config* method_config) { +static void* method_config_convert_value(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, "max_request_message_bytes") == 0) { + if (max_request_message_bytes >= 0) return NULL; // Duplicate. + if (field->type != GRPC_JSON_NUMBER) return NULL; + max_request_message_bytes = gpr_parse_nonnegative_number(field->value); + if (max_request_message_bytes == -1) return NULL; + } else if (strcmp(field->key, "max_response_message_bytes") == 0) { + if (max_response_message_bytes >= 0) return NULL; // Duplicate. + if (field->type != GRPC_JSON_NUMBER) return NULL; + max_response_message_bytes = gpr_parse_nonnegative_number(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; } @@ -224,8 +234,8 @@ static void init_channel_elem(grpc_exec_ctx* exec_ctx, grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG); if (channel_arg != NULL) { GPR_ASSERT(channel_arg->type == GRPC_ARG_POINTER); - chand->method_limit_table = grpc_method_config_table_convert( - (grpc_method_config_table*)channel_arg->value.pointer.p, + chand->method_limit_table = grpc_method_config_table_create_from_json( + (grpc_json*)channel_arg->value.pointer.p, method_config_convert_value, &message_size_limits_vtable); } } diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c index d17fb9da4b..56f29492bf 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> @@ -194,6 +196,13 @@ int int64_ttoa(int64_t value, char *string) { return i; } +int gpr_parse_nonnegative_number(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 9a94e9471c..3a5a8ed826 100644 --- a/src/core/lib/support/string.h +++ b/src/core/lib/support/string.h @@ -80,6 +80,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_number(const char* value); + /* Reverse a run of bytes */ void gpr_reverse_bytes(char *str, int len); diff --git a/src/core/lib/transport/method_config.c b/src/core/lib/transport/method_config.c index 57d97700bf..23cab2b96b 100644 --- a/src/core/lib/transport/method_config.c +++ b/src/core/lib/transport/method_config.c @@ -39,6 +39,8 @@ #include <grpc/support/string_util.h> #include <grpc/support/time.h> +#include "src/core/lib/json/json.h" +#include "src/core/lib/support/string.h" #include "src/core/lib/transport/mdstr_hash_table.h" #include "src/core/lib/transport/metadata.h" @@ -338,3 +340,123 @@ grpc_mdstr_hash_table* grpc_method_config_table_convert( // Return the new table. return new_table; } + +// 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 name 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. +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 NULL; + // Construct list of paths. + bool retval = 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; + } + retval = true; +done: + vtable->destroy_value(method_config); + gpr_strvec_destroy(&paths); + return retval; +} + +grpc_mdstr_hash_table* grpc_method_config_table_create_from_json( + const grpc_json* json, + void* (*create_value)(const grpc_json* method_config_json), + const grpc_mdstr_hash_table_vtable* vtable) { + // 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, "method_config") == 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_method_config_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; +} diff --git a/src/core/lib/transport/method_config.h b/src/core/lib/transport/method_config.h index 58fedd9436..eac05f8173 100644 --- a/src/core/lib/transport/method_config.h +++ b/src/core/lib/transport/method_config.h @@ -37,6 +37,7 @@ #include <grpc/impl/codegen/gpr_types.h> #include <grpc/impl/codegen/grpc_types.h> +#include "src/core/lib/json/json.h" #include "src/core/lib/transport/mdstr_hash_table.h" #include "src/core/lib/transport/metadata.h" @@ -133,4 +134,10 @@ grpc_mdstr_hash_table* grpc_method_config_table_convert( void* (*convert_value)(const grpc_method_config* method_config), const grpc_mdstr_hash_table_vtable* vtable); +// FIXME: document +grpc_mdstr_hash_table* grpc_method_config_table_create_from_json( + const grpc_json* json, + void* (*create_value)(const grpc_json* method_config_json), + const grpc_mdstr_hash_table_vtable* vtable); + #endif /* GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H */ |