Use JSON for service config channel arg.

pull/8617/head
Mark D. Roth 8 years ago
parent 9dd2c7da2f
commit c968e60e2e
  1. 51
      src/core/ext/client_channel/client_channel.c
  2. 34
      src/core/lib/channel/message_size_filter.c
  3. 9
      src/core/lib/support/string.c
  4. 3
      src/core/lib/support/string.h
  5. 122
      src/core/lib/transport/method_config.c
  6. 7
      src/core/lib/transport/method_config.h

@ -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);

@ -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);
}
}

@ -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;

@ -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);

@ -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;
}

@ -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 */

Loading…
Cancel
Save