aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Mark D. Roth <roth@google.com>2016-11-02 14:07:36 -0700
committerGravatar Mark D. Roth <roth@google.com>2016-11-02 14:07:36 -0700
commitc968e60e2ef60cd20353c34fb6260a63ce9db031 (patch)
tree5bb0aeeadd49a7566d6500c63099ebfb783ed488
parent9dd2c7da2f82eb27dfbef01a9c133e403dcffc45 (diff)
Use JSON for service config channel arg.
-rw-r--r--src/core/ext/client_channel/client_channel.c51
-rw-r--r--src/core/lib/channel/message_size_filter.c34
-rw-r--r--src/core/lib/support/string.c9
-rw-r--r--src/core/lib/support/string.h3
-rw-r--r--src/core/lib/transport/method_config.c122
-rw-r--r--src/core/lib/transport/method_config.h7
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 */