Merge pull request #7395 from markdroth/handshake_api
General-purpose handshaker API.pull/7518/head
commit
de2d9fc07b
23 changed files with 517 additions and 39 deletions
@ -0,0 +1,191 @@ |
||||
/*
|
||||
* |
||||
* 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_timespec deadline, |
||||
grpc_tcp_server_acceptor* acceptor, |
||||
grpc_handshaker_done_cb cb, void* user_data) { |
||||
handshaker->vtable->do_handshake(exec_ctx, handshaker, endpoint, args, |
||||
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_handshaker* handshaker, |
||||
grpc_handshake_manager* mgr) { |
||||
// 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) { |
||||
// FIXME: maybe check which handshaker is currently in progress, and
|
||||
// only shut down that one?
|
||||
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, void* user_data) { |
||||
grpc_handshake_manager* mgr = user_data; |
||||
GPR_ASSERT(mgr->state != NULL); |
||||
GPR_ASSERT(mgr->state->index < mgr->count); |
||||
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, 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); |
||||
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, user_data); |
||||
} 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, mgr); |
||||
} |
||||
} |
@ -0,0 +1,145 @@ |
||||
/*
|
||||
* |
||||
* 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_LIB_CHANNEL_HANDSHAKER_H |
||||
#define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H |
||||
|
||||
#include <grpc/impl/codegen/grpc_types.h> |
||||
#include <grpc/impl/codegen/time.h> |
||||
|
||||
#include "src/core/lib/iomgr/closure.h" |
||||
#include "src/core/lib/iomgr/endpoint.h" |
||||
#include "src/core/lib/iomgr/exec_ctx.h" |
||||
#include "src/core/lib/iomgr/tcp_server.h" |
||||
|
||||
/// Handshakers are used to perform initial handshakes on a connection
|
||||
/// before the client sends the initial request. Some examples of what
|
||||
/// a handshaker can be used for includes support for HTTP CONNECT on
|
||||
/// the client side and various types of security initialization.
|
||||
///
|
||||
/// In general, handshakers should be used via a handshake manager.
|
||||
|
||||
///
|
||||
/// grpc_handshaker
|
||||
///
|
||||
|
||||
typedef struct grpc_handshaker grpc_handshaker; |
||||
|
||||
/// Callback type invoked when a handshaker is done.
|
||||
/// Takes ownership of \a args.
|
||||
typedef void (*grpc_handshaker_done_cb)(grpc_exec_ctx* exec_ctx, |
||||
grpc_endpoint* endpoint, |
||||
grpc_channel_args* args, |
||||
void* user_data); |
||||
|
||||
struct grpc_handshaker_vtable { |
||||
/// Destroys the handshaker.
|
||||
void (*destroy)(grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker); |
||||
|
||||
/// Shuts down the handshaker (e.g., to clean up when the operation is
|
||||
/// aborted in the middle).
|
||||
void (*shutdown)(grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker); |
||||
|
||||
/// Performs handshaking. When finished, calls \a cb with \a user_data.
|
||||
/// Takes ownership of \a args.
|
||||
/// \a acceptor will be NULL for client-side handshakers.
|
||||
void (*do_handshake)(grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker, |
||||
grpc_endpoint* endpoint, grpc_channel_args* args, |
||||
gpr_timespec deadline, |
||||
grpc_tcp_server_acceptor* acceptor, |
||||
grpc_handshaker_done_cb cb, void* user_data); |
||||
}; |
||||
|
||||
/// Base struct. To subclass, make this the first member of the
|
||||
/// implementation struct.
|
||||
struct grpc_handshaker { |
||||
const struct grpc_handshaker_vtable* vtable; |
||||
}; |
||||
|
||||
/// Called by concrete implementations to initialize the base struct.
|
||||
void grpc_handshaker_init(const struct grpc_handshaker_vtable* vtable, |
||||
grpc_handshaker* handshaker); |
||||
|
||||
/// Convenient wrappers for invoking methods via the vtable.
|
||||
/// These probably do not need to be called from anywhere but
|
||||
/// grpc_handshake_manager.
|
||||
void grpc_handshaker_destroy(grpc_exec_ctx* exec_ctx, |
||||
grpc_handshaker* handshaker); |
||||
void grpc_handshaker_shutdown(grpc_exec_ctx* exec_ctx, |
||||
grpc_handshaker* handshaker); |
||||
void grpc_handshaker_do_handshake(grpc_exec_ctx* exec_ctx, |
||||
grpc_handshaker* handshaker, |
||||
grpc_endpoint* endpoint, |
||||
grpc_channel_args* args, |
||||
gpr_timespec deadline, |
||||
grpc_tcp_server_acceptor* acceptor, |
||||
grpc_handshaker_done_cb cb, void* user_data); |
||||
|
||||
///
|
||||
/// grpc_handshake_manager
|
||||
///
|
||||
|
||||
typedef struct grpc_handshake_manager grpc_handshake_manager; |
||||
|
||||
/// Creates a new handshake manager. Caller takes ownership.
|
||||
grpc_handshake_manager* grpc_handshake_manager_create(); |
||||
|
||||
/// Adds a handshaker to the handshake manager.
|
||||
/// Takes ownership of \a mgr.
|
||||
void grpc_handshake_manager_add(grpc_handshaker* handshaker, |
||||
grpc_handshake_manager* mgr); |
||||
|
||||
/// Destroys the handshake manager.
|
||||
void grpc_handshake_manager_destroy(grpc_exec_ctx* exec_ctx, |
||||
grpc_handshake_manager* mgr); |
||||
|
||||
/// Shuts down the handshake manager (e.g., to clean up when the operation is
|
||||
/// aborted in the middle).
|
||||
/// The caller must still call grpc_handshake_manager_destroy() after
|
||||
/// calling this function.
|
||||
void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx, |
||||
grpc_handshake_manager* mgr); |
||||
|
||||
/// Invokes handshakers in the order they were added.
|
||||
/// Does NOT take ownership of \a args. Instead, makes a copy before
|
||||
/// invoking the first handshaker.
|
||||
/// \a acceptor will be NULL for client-side handshakers.
|
||||
/// If successful, invokes \a cb with \a user_data after all handshakers
|
||||
/// have completed.
|
||||
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); |
||||
|
||||
#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H */ |
Loading…
Reference in new issue