diff options
Diffstat (limited to 'src/core/lib/channel/handshaker.c')
-rw-r--r-- | src/core/lib/channel/handshaker.c | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/src/core/lib/channel/handshaker.c b/src/core/lib/channel/handshaker.c new file mode 100644 index 0000000000..c0979f5e80 --- /dev/null +++ b/src/core/lib/channel/handshaker.c @@ -0,0 +1,203 @@ +/* + * + * 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. + * + */ + +#include <string.h> + +#include <grpc/impl/codegen/alloc.h> +#include <grpc/impl/codegen/log.h> + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" + +// +// grpc_handshaker +// + +void grpc_handshaker_init(const struct grpc_handshaker_vtable* vtable, + grpc_handshaker* handshaker) { + handshaker->vtable = vtable; +} + +void grpc_handshaker_destroy(grpc_exec_ctx* exec_ctx, + grpc_handshaker* handshaker) { + handshaker->vtable->destroy(exec_ctx, handshaker); +} + +void grpc_handshaker_shutdown(grpc_exec_ctx* exec_ctx, + grpc_handshaker* handshaker) { + handshaker->vtable->shutdown(exec_ctx, handshaker); +} + +void grpc_handshaker_do_handshake(grpc_exec_ctx* exec_ctx, + grpc_handshaker* handshaker, + grpc_endpoint* endpoint, + grpc_channel_args* args, + gpr_slice_buffer* read_buffer, + gpr_timespec deadline, + grpc_tcp_server_acceptor* acceptor, + grpc_handshaker_done_cb cb, void* user_data) { + handshaker->vtable->do_handshake(exec_ctx, handshaker, endpoint, args, + read_buffer, deadline, acceptor, cb, + user_data); +} + +// +// grpc_handshake_manager +// + +// State used while chaining handshakers. +struct grpc_handshaker_state { + // The index of the handshaker to invoke next. + size_t index; + // The deadline for all handshakers. + gpr_timespec deadline; + // The acceptor to call the handshakers with. + grpc_tcp_server_acceptor* acceptor; + // The final callback and user_data to invoke after the last handshaker. + grpc_handshaker_done_cb final_cb; + void* final_user_data; +}; + +struct grpc_handshake_manager { + // An array of handshakers added via grpc_handshake_manager_add(). + size_t count; + grpc_handshaker** handshakers; + // State used while chaining handshakers. + struct grpc_handshaker_state* state; +}; + +grpc_handshake_manager* grpc_handshake_manager_create() { + grpc_handshake_manager* mgr = gpr_malloc(sizeof(grpc_handshake_manager)); + memset(mgr, 0, sizeof(*mgr)); + return mgr; +} + +static bool is_power_of_2(size_t n) { return (n & (n - 1)) == 0; } + +void grpc_handshake_manager_add(grpc_handshake_manager* mgr, + grpc_handshaker* handshaker) { + // To avoid allocating memory for each handshaker we add, we double + // the number of elements every time we need more. + size_t realloc_count = 0; + if (mgr->count == 0) { + realloc_count = 2; + } else if (mgr->count >= 2 && is_power_of_2(mgr->count)) { + realloc_count = mgr->count * 2; + } + if (realloc_count > 0) { + mgr->handshakers = + gpr_realloc(mgr->handshakers, realloc_count * sizeof(grpc_handshaker*)); + } + mgr->handshakers[mgr->count++] = handshaker; +} + +void grpc_handshake_manager_destroy(grpc_exec_ctx* exec_ctx, + grpc_handshake_manager* mgr) { + for (size_t i = 0; i < mgr->count; ++i) { + grpc_handshaker_destroy(exec_ctx, mgr->handshakers[i]); + } + gpr_free(mgr->handshakers); + gpr_free(mgr); +} + +void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx, + grpc_handshake_manager* mgr) { + for (size_t i = 0; i < mgr->count; ++i) { + grpc_handshaker_shutdown(exec_ctx, mgr->handshakers[i]); + } + if (mgr->state != NULL) { + gpr_free(mgr->state); + mgr->state = NULL; + } +} + +// A function used as the handshaker-done callback when chaining +// handshakers together. +static void call_next_handshaker(grpc_exec_ctx* exec_ctx, + grpc_endpoint* endpoint, + grpc_channel_args* args, + gpr_slice_buffer* read_buffer, void* user_data, + grpc_error* error) { + grpc_handshake_manager* mgr = user_data; + GPR_ASSERT(mgr->state != NULL); + GPR_ASSERT(mgr->state->index < mgr->count); + // If we got an error, skip all remaining handshakers and invoke the + // caller-supplied callback immediately. + if (error != GRPC_ERROR_NONE) { + mgr->state->final_cb(exec_ctx, endpoint, args, read_buffer, + mgr->state->final_user_data, error); + return; + } + grpc_handshaker_done_cb cb = call_next_handshaker; + // If this is the last handshaker, use the caller-supplied callback + // and user_data instead of chaining back to this function again. + if (mgr->state->index == mgr->count - 1) { + cb = mgr->state->final_cb; + user_data = mgr->state->final_user_data; + } + // Invoke handshaker. + grpc_handshaker_do_handshake( + exec_ctx, mgr->handshakers[mgr->state->index], endpoint, args, + read_buffer, mgr->state->deadline, mgr->state->acceptor, cb, user_data); + ++mgr->state->index; + // If this is the last handshaker, clean up state. + if (mgr->state->index == mgr->count) { + gpr_free(mgr->state); + mgr->state = NULL; + } +} + +void grpc_handshake_manager_do_handshake( + grpc_exec_ctx* exec_ctx, grpc_handshake_manager* mgr, + grpc_endpoint* endpoint, const grpc_channel_args* args, + gpr_timespec deadline, grpc_tcp_server_acceptor* acceptor, + grpc_handshaker_done_cb cb, void* user_data) { + grpc_channel_args* args_copy = grpc_channel_args_copy(args); + gpr_slice_buffer* read_buffer = malloc(sizeof(*read_buffer)); + gpr_slice_buffer_init(read_buffer); + if (mgr->count == 0) { + // No handshakers registered, so we just immediately call the done + // callback with the passed-in endpoint. + cb(exec_ctx, endpoint, args_copy, read_buffer, user_data, GRPC_ERROR_NONE); + } else { + GPR_ASSERT(mgr->state == NULL); + mgr->state = gpr_malloc(sizeof(struct grpc_handshaker_state)); + memset(mgr->state, 0, sizeof(*mgr->state)); + mgr->state->deadline = deadline; + mgr->state->acceptor = acceptor; + mgr->state->final_cb = cb; + mgr->state->final_user_data = user_data; + call_next_handshaker(exec_ctx, endpoint, args_copy, read_buffer, mgr, + GRPC_ERROR_NONE); + } +} |