diff --git a/src/core/ext/client_config/method_config.c b/src/core/ext/client_config/method_config.c new file mode 100644 index 00000000000..e3589153fb6 --- /dev/null +++ b/src/core/ext/client_config/method_config.c @@ -0,0 +1,214 @@ +// +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#include "src/core/ext/client_config/method_config.h" + +#include + +#include +#include +#include + +#include "src/core/lib/transport/metadata.h" + +// +// grpc_method_config +// + +struct grpc_method_config { + gpr_refcount refs; + bool* wait_for_ready; + gpr_timespec* timeout; + int32_t* max_request_message_bytes; + int32_t* max_response_message_bytes; +}; + +grpc_method_config* grpc_method_config_create( + bool* wait_for_ready, gpr_timespec* timeout, + int32_t* max_request_message_bytes, int32_t* max_response_message_bytes) { + grpc_method_config* config = gpr_malloc(sizeof(*config)); + memset(config, 0, sizeof(*config)); + gpr_ref_init(&config->refs, 1); + if (wait_for_ready != NULL) { + config->wait_for_ready = gpr_malloc(sizeof(*wait_for_ready)); + *config->wait_for_ready = *wait_for_ready; + } + if (timeout != NULL) { + config->timeout = gpr_malloc(sizeof(*timeout)); + *config->timeout = *timeout; + } + if (max_request_message_bytes != NULL) { + config->max_request_message_bytes = + gpr_malloc(sizeof(*max_request_message_bytes)); + *config->max_request_message_bytes = *max_request_message_bytes; + } + if (max_response_message_bytes != NULL) { + config->max_response_message_bytes = + gpr_malloc(sizeof(*max_response_message_bytes)); + *config->max_response_message_bytes = *max_response_message_bytes; + } + return config; +} + +grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config) { + gpr_ref(&method_config->refs); + return method_config; +} + +void grpc_method_config_unref(grpc_method_config* method_config) { + if (gpr_unref(&method_config->refs)) { + gpr_free(method_config->wait_for_ready); + gpr_free(method_config->timeout); + gpr_free(method_config->max_request_message_bytes); + gpr_free(method_config->max_response_message_bytes); + gpr_free(method_config); + } +} + +bool* grpc_method_config_get_wait_for_ready(grpc_method_config* method_config) { + return method_config->wait_for_ready; +} + +gpr_timespec* grpc_method_config_get_timeout( + grpc_method_config* method_config) { + return method_config->timeout; +} + +int32_t* grpc_method_config_get_max_request_message_bytes( + grpc_method_config* method_config) { + return method_config->max_request_message_bytes; +} + +int32_t* grpc_method_config_get_max_response_message_bytes( + grpc_method_config* method_config) { + return method_config->max_response_message_bytes; +} + +// +// grpc_method_config_table +// + +typedef struct grpc_method_config_table_entry { + grpc_mdstr* path; + grpc_method_config* method_config; +} grpc_method_config_table_entry; + +#define METHOD_CONFIG_TABLE_SIZE 128 +struct grpc_method_config_table { + gpr_refcount refs; + grpc_method_config_table_entry entries[METHOD_CONFIG_TABLE_SIZE]; +}; + +grpc_method_config_table* grpc_method_config_table_create() { + grpc_method_config_table* table = gpr_malloc(sizeof(*table)); + memset(table, 0, sizeof(*table)); + gpr_ref_init(&table->refs, 1); + return table; +} + +grpc_method_config_table* grpc_method_config_table_ref( + grpc_method_config_table* table) { + if (table != NULL) gpr_ref(&table->refs); + return table; +} + +void grpc_method_config_table_unref(grpc_method_config_table* table) { + if (table != NULL && gpr_unref(&table->refs)) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(table->entries); ++i) { + grpc_method_config_table_entry* entry = &table->entries[i]; + if (entry->path != NULL) { + GRPC_MDSTR_UNREF(entry->path); + grpc_method_config_unref(entry->method_config); + } + } + } +} + +// Helper function for insert and get operations that performs quadratic +// probing (https://en.wikipedia.org/wiki/Quadratic_probing). +static size_t grpc_method_config_table_find_index( + grpc_method_config_table* table, grpc_mdstr* path, bool find_empty) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(table->entries); ++i) { + const size_t idx = (path->hash + i * i) % GPR_ARRAY_SIZE(table->entries); + if (table->entries[idx].path == NULL) + return find_empty ? idx : GPR_ARRAY_SIZE(table->entries); + if (table->entries[idx].path == path) return idx; + } + return GPR_ARRAY_SIZE(table->entries) + 1; // Not found. +} + +static void grpc_method_config_table_insert(grpc_method_config_table* table, + grpc_mdstr* path, + grpc_method_config* config) { + const size_t idx = + grpc_method_config_table_find_index(table, path, true /* find_empty */); + // This can happen if the table is full. + GPR_ASSERT(idx != GPR_ARRAY_SIZE(table->entries)); + grpc_method_config_table_entry* entry = &table->entries[idx]; + entry->path = GRPC_MDSTR_REF(path); + entry->method_config = grpc_method_config_ref(config); +} + +static grpc_method_config* grpc_method_config_table_get( + grpc_method_config_table* table, grpc_mdstr* path) { + const size_t idx = + grpc_method_config_table_find_index(table, path, false /* find_empty */); + if (idx == GPR_ARRAY_SIZE(table->entries)) return NULL; // Not found. + return table->entries[idx].method_config; +} + +void grpc_method_config_table_add_method_config( + grpc_method_config_table* table, grpc_mdstr** paths, size_t num_paths, + grpc_method_config* method_config) { + for (size_t i = 0; i < num_paths; ++i) { + grpc_method_config_table_insert(table, paths[i], method_config); + } +} + +grpc_method_config* grpc_method_config_table_get_method_config( + grpc_method_config_table* table, grpc_mdstr* path) { + grpc_method_config* method_config = grpc_method_config_table_get(table, path); + // If we didn't find a match for the path, try looking for a wildcard + // entry (i.e., change "/service/method" to "/service/*"). + if (method_config == NULL) { + const char* path_str = grpc_mdstr_as_c_string(path); + const char* sep = strrchr(path_str, '/') + 1; + const size_t len = (size_t)(sep - path_str); + char buf[len + 2]; // '*' and NUL + memcpy(buf, path_str, len); + buf[len] = '*'; + buf[len + 1] = '\0'; + grpc_mdstr* wildcard_path = grpc_mdstr_from_string(buf); + method_config = grpc_method_config_table_get(table, wildcard_path); + GRPC_MDSTR_UNREF(wildcard_path); + } + return grpc_method_config_ref(method_config); +} diff --git a/src/core/ext/client_config/method_config.h b/src/core/ext/client_config/method_config.h new file mode 100644 index 00000000000..9490e296b27 --- /dev/null +++ b/src/core/ext/client_config/method_config.h @@ -0,0 +1,85 @@ +// +// Copyright 2016, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GRPC_CORE_EXT_CLIENT_CONFIG_METHOD_CONFIG_H +#define GRPC_CORE_EXT_CLIENT_CONFIG_METHOD_CONFIG_H + +#include + +#include + +#include "src/core/lib/transport/metadata.h" + +/// Per-method configuration. +typedef struct grpc_method_config grpc_method_config; + +/// Any parameter may be NULL to indicate that the value is unset. +grpc_method_config* grpc_method_config_create( + bool* wait_for_ready, gpr_timespec* timeout, + int32_t* max_request_message_bytes, int32_t* max_response_message_bytes); + +grpc_method_config* grpc_method_config_ref(grpc_method_config* method_config); +void grpc_method_config_unref(grpc_method_config* method_config); + +/// These methods return NULL if the requested field is unset. +/// The caller does NOT take ownership of the result. +bool* grpc_method_config_get_wait_for_ready(grpc_method_config* method_config); +gpr_timespec* grpc_method_config_get_timeout(grpc_method_config* method_config); +int32_t* grpc_method_config_get_max_request_message_bytes( + grpc_method_config* method_config); +int32_t* grpc_method_config_get_max_response_message_bytes( + grpc_method_config* method_config); + +/// A table of method configs. +typedef struct grpc_method_config_table grpc_method_config_table; + +grpc_method_config_table* grpc_method_config_table_create(); + +grpc_method_config_table* grpc_method_config_table_ref( + grpc_method_config_table* table); +void grpc_method_config_table_unref(grpc_method_config_table* table); + +/// Adds \a method_config to \a table. \a paths indicates the set of path +/// names for which this config applies. Each name is of one of the +/// following forms: +/// service/method -- specifies exact service and method name +/// service/* -- matches all methods for the specified service +/// Takes new references to all elements of \a paths and to \a method_config. +void grpc_method_config_table_add_method_config( + grpc_method_config_table* table, grpc_mdstr** paths, size_t num_paths, + grpc_method_config* method_config); + +/// Returns NULL if the method has no config. +/// Caller owns a reference to result. +grpc_method_config* grpc_method_config_table_get_method_config( + grpc_method_config_table* table, grpc_mdstr* path); + +#endif /* GRPC_CORE_EXT_CLIENT_CONFIG_METHOD_CONFIG_H */