diff --git a/src/core/ext/client_channel/client_channel.c b/src/core/ext/client_channel/client_channel.c index e569af68e93..4a1c0ae1a22 100644 --- a/src/core/ext/client_channel/client_channel.c +++ b/src/core/ext/client_channel/client_channel.c @@ -104,7 +104,8 @@ static void *method_config_convert_value(const grpc_json *json) { if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) { return NULL; } - wait_for_ready = field->type == GRPC_JSON_TRUE; + wait_for_ready = field->type == GRPC_JSON_TRUE + ? WAIT_FOR_READY_TRUE : WAIT_FOR_READY_FALSE; } 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; @@ -312,9 +313,10 @@ 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); + grpc_json_tree* json_tree = 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); + json_tree->root, method_config_convert_value, + &method_parameters_vtable); } grpc_channel_args_destroy(chand->resolver_result); chand->resolver_result = NULL; diff --git a/src/core/lib/channel/message_size_filter.c b/src/core/lib/channel/message_size_filter.c index 4723ab80981..86fdf72684d 100644 --- a/src/core/lib/channel/message_size_filter.c +++ b/src/core/lib/channel/message_size_filter.c @@ -234,9 +234,10 @@ 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); + grpc_json_tree* json_tree = 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); + json_tree->root, method_config_convert_value, + &message_size_limits_vtable); } } diff --git a/src/core/lib/json/json.c b/src/core/lib/json/json.c index 5b583a1f2e0..c1a99df6c37 100644 --- a/src/core/lib/json/json.c +++ b/src/core/lib/json/json.c @@ -34,6 +34,8 @@ #include #include +#include +#include #include "src/core/lib/json/json.h" @@ -62,3 +64,60 @@ void grpc_json_destroy(grpc_json *json) { gpr_free(json); } + +int grpc_json_cmp(const grpc_json* json1, const grpc_json* json2) { + if (json1 == NULL) { + if (json2 != NULL) return 1; + return 0; // Both NULL. + } else { + if (json2 == NULL) return -1; + } + // Compare type. + if (json1->type > json2->type) return 1; + if (json1->type < json2->type) return -1; + // Compare key. + if (json1->key == NULL) { + if (json2->key != NULL) return -1; + } else { + if (json2->key == NULL) return 1; + int retval = strcmp(json1->key, json2->key); + if (retval != 0) return retval; + } + // Compare value. + if (json1->value == NULL) { + if (json2->value != NULL) return -1; + } else { + if (json2->value == NULL) return 1; + int retval = strcmp(json1->value, json2->value); + if (retval != 0) return retval; + } + // Recursively compare the next pointer. + int retval = grpc_json_cmp(json1->next, json2->next); + if (retval != 0) return retval; + // Recursively compare the child pointer. + retval = grpc_json_cmp(json1->child, json2->child); + if (retval != 0) return retval; + // Both are the same. + return 0; +} + +grpc_json_tree* grpc_json_tree_create(const char* json_string) { + grpc_json_tree* tree = gpr_malloc(sizeof(*tree)); + tree->string = gpr_strdup(json_string); + tree->root = grpc_json_parse_string(tree->string); + gpr_ref_init(&tree->refs, 1); + return tree; +} + +grpc_json_tree* grpc_json_tree_ref(grpc_json_tree* tree) { + gpr_ref(&tree->refs); + return tree; +} + +void grpc_json_tree_unref(grpc_json_tree* tree) { + if (gpr_unref(&tree->refs)) { + grpc_json_destroy(tree->root); + gpr_free(tree->string); + gpr_free(tree); + } +} diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h index 681df4bb77d..e18ace7547a 100644 --- a/src/core/lib/json/json.h +++ b/src/core/lib/json/json.h @@ -36,6 +36,8 @@ #include +#include + #include "src/core/lib/json/json_common.h" /* A tree-like structure to hold json values. The key and value pointers @@ -85,4 +87,21 @@ char *grpc_json_dump_to_string(grpc_json *json, int indent); grpc_json *grpc_json_create(grpc_json_type type); void grpc_json_destroy(grpc_json *json); +/* Compares two JSON trees. */ +int grpc_json_cmp(const grpc_json* json1, const grpc_json* json2); + +/* A wrapper that contains the string used for underlying allocation and + is refcounted. */ +typedef struct { + grpc_json* root; + char* string; + gpr_refcount refs; +} grpc_json_tree; + +/* Creates a copy of \a json_string. */ +grpc_json_tree* grpc_json_tree_create(const char* json_string); + +grpc_json_tree* grpc_json_tree_ref(grpc_json_tree* tree); +void grpc_json_tree_unref(grpc_json_tree* tree); + #endif /* GRPC_CORE_LIB_JSON_JSON_H */ diff --git a/src/core/lib/transport/method_config.c b/src/core/lib/transport/method_config.c index 23cab2b96b0..c2bf88a2503 100644 --- a/src/core/lib/transport/method_config.c +++ b/src/core/lib/transport/method_config.c @@ -460,3 +460,26 @@ grpc_mdstr_hash_table* grpc_method_config_table_create_from_json( } return method_config_table; } + +static void* copy_json_tree(void* t) { return grpc_json_tree_ref(t); } + +static void destroy_json_tree(void* t) { grpc_json_tree_unref(t); } + +static int cmp_json_tree(void* t1, void* t2) { + grpc_json_tree* tree1 = t1; + grpc_json_tree* tree2 = t2; + return grpc_json_cmp(tree1->root, tree2->root); +} + +static grpc_arg_pointer_vtable service_config_arg_vtable = { + copy_json_tree, destroy_json_tree, cmp_json_tree}; + +grpc_arg grpc_service_config_create_channel_arg( + grpc_json_tree* service_config) { + grpc_arg arg; + arg.type = GRPC_ARG_POINTER; + arg.key = GRPC_ARG_SERVICE_CONFIG; + arg.value.pointer.p = service_config; + arg.value.pointer.vtable = &service_config_arg_vtable; + return arg; +} diff --git a/src/core/lib/transport/method_config.h b/src/core/lib/transport/method_config.h index eac05f8173a..84c6329415b 100644 --- a/src/core/lib/transport/method_config.h +++ b/src/core/lib/transport/method_config.h @@ -140,4 +140,6 @@ grpc_mdstr_hash_table* grpc_method_config_table_create_from_json( void* (*create_value)(const grpc_json* method_config_json), const grpc_mdstr_hash_table_vtable* vtable); +grpc_arg grpc_service_config_create_channel_arg(grpc_json_tree* service_config); + #endif /* GRPC_CORE_LIB_TRANSPORT_METHOD_CONFIG_H */ diff --git a/test/core/end2end/connection_refused_test.c b/test/core/end2end/connection_refused_test.c index 13414c0378b..ba9fb3d30a0 100644 --- a/test/core/end2end/connection_refused_test.c +++ b/test/core/end2end/connection_refused_test.c @@ -76,18 +76,18 @@ static void run_test(bool wait_for_ready, bool use_service_config) { grpc_channel_args *args = NULL; if (use_service_config) { GPR_ASSERT(wait_for_ready); - grpc_method_config_table_entry entry = { - grpc_mdstr_from_string("/service/method"), - grpc_method_config_create(&wait_for_ready, NULL, NULL, NULL), - }; - grpc_method_config_table *method_config_table = - grpc_method_config_table_create(1, &entry); - GRPC_MDSTR_UNREF(entry.method_name); - grpc_method_config_unref(entry.method_config); - grpc_arg arg = - grpc_method_config_table_create_channel_arg(method_config_table); + grpc_json_tree* service_config_json = grpc_json_tree_create( + "{\n" + " \"method_config\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"wait_for_ready\": true\n" + " } ]\n" + "}"); + grpc_arg arg = grpc_service_config_create_channel_arg(service_config_json); args = grpc_channel_args_copy_and_add(args, &arg, 1); - grpc_method_config_table_unref(method_config_table); + grpc_json_tree_unref(service_config_json); } /* create a call, channel to a port which will refuse connection */ diff --git a/test/core/end2end/tests/cancel_after_accept.c b/test/core/end2end/tests/cancel_after_accept.c index 104d8fd54fd..68a82560927 100644 --- a/test/core/end2end/tests/cancel_after_accept.c +++ b/test/core/end2end/tests/cancel_after_accept.c @@ -132,19 +132,18 @@ static void test_cancel_after_accept(grpc_end2end_test_config config, grpc_channel_args *args = NULL; if (use_service_config) { - gpr_timespec timeout = {5, 0, GPR_TIMESPAN}; - grpc_method_config_table_entry entry = { - grpc_mdstr_from_string("/service/method"), - grpc_method_config_create(NULL, &timeout, NULL, NULL), - }; - grpc_method_config_table *method_config_table = - grpc_method_config_table_create(1, &entry); - GRPC_MDSTR_UNREF(entry.method_name); - grpc_method_config_unref(entry.method_config); - grpc_arg arg = - grpc_method_config_table_create_channel_arg(method_config_table); + grpc_json_tree* service_config_json = grpc_json_tree_create( + "{\n" + " \"method_config\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"timeout\": { \"seconds\": 5 }\n" + " } ]\n" + "}"); + grpc_arg arg = grpc_service_config_create_channel_arg(service_config_json); args = grpc_channel_args_copy_and_add(args, &arg, 1); - grpc_method_config_table_unref(method_config_table); + grpc_json_tree_unref(service_config_json); } grpc_end2end_test_fixture f = diff --git a/test/core/end2end/tests/max_message_length.c b/test/core/end2end/tests/max_message_length.c index 3a927ddcbc6..f0bc7acdff3 100644 --- a/test/core/end2end/tests/max_message_length.c +++ b/test/core/end2end/tests/max_message_length.c @@ -137,19 +137,18 @@ static void test_max_message_length_on_request(grpc_end2end_test_config config, if (use_service_config) { // We don't currently support service configs on the server side. GPR_ASSERT(send_limit); - int32_t max_request_message_bytes = 5; - grpc_method_config_table_entry entry = { - grpc_mdstr_from_string("/service/method"), - grpc_method_config_create(NULL, NULL, &max_request_message_bytes, NULL), - }; - grpc_method_config_table *method_config_table = - grpc_method_config_table_create(1, &entry); - GRPC_MDSTR_UNREF(entry.method_name); - grpc_method_config_unref(entry.method_config); - grpc_arg arg = - grpc_method_config_table_create_channel_arg(method_config_table); + grpc_json_tree* service_config_json = grpc_json_tree_create( + "{\n" + " \"method_config\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"max_request_message_bytes\": 5\n" + " } ]\n" + "}"); + grpc_arg arg = grpc_service_config_create_channel_arg(service_config_json); client_args = grpc_channel_args_copy_and_add(NULL, &arg, 1); - grpc_method_config_table_unref(method_config_table); + grpc_json_tree_unref(service_config_json); } else { // Set limit via channel args. grpc_arg arg; @@ -309,20 +308,18 @@ static void test_max_message_length_on_response(grpc_end2end_test_config config, if (use_service_config) { // We don't currently support service configs on the server side. GPR_ASSERT(!send_limit); - int32_t max_response_message_bytes = 5; - grpc_method_config_table_entry entry = { - grpc_mdstr_from_string("/service/method"), - grpc_method_config_create(NULL, NULL, NULL, - &max_response_message_bytes), - }; - grpc_method_config_table *method_config_table = - grpc_method_config_table_create(1, &entry); - GRPC_MDSTR_UNREF(entry.method_name); - grpc_method_config_unref(entry.method_config); - grpc_arg arg = - grpc_method_config_table_create_channel_arg(method_config_table); + grpc_json_tree* service_config_json = grpc_json_tree_create( + "{\n" + " \"method_config\": [ {\n" + " \"name\": [\n" + " { \"service\": \"service\", \"method\": \"method\" }\n" + " ],\n" + " \"max_response_message_bytes\": 5\n" + " } ]\n" + "}"); + grpc_arg arg = grpc_service_config_create_channel_arg(service_config_json); client_args = grpc_channel_args_copy_and_add(NULL, &arg, 1); - grpc_method_config_table_unref(method_config_table); + grpc_json_tree_unref(service_config_json); } else { // Set limit via channel args. grpc_arg arg;