Skip to content
Snippets Groups Projects
Commit c968e60e authored by Mark D. Roth's avatar Mark D. Roth
Browse files

Use JSON for service config channel arg.

parent 9dd2c7da
No related branches found
No related tags found
No related merge requests found
......@@ -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 */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment