[go: up one dir, main page]

Skip to content

Commit

Permalink
Merge pull request #13336 from markdroth/server_connection_timeout
Browse files Browse the repository at this point in the history
On server, include receiving HTTP/2 settings in handshake timeout
  • Loading branch information
markdroth authored Dec 4, 2017
2 parents f3b73e5 + 2c619be commit 6fa206d
Show file tree
Hide file tree
Showing 27 changed files with 581 additions and 66 deletions.
39 changes: 39 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,7 @@ add_dependencies(buildtests_cxx bm_pollset)
endif()
add_dependencies(buildtests_cxx channel_arguments_test)
add_dependencies(buildtests_cxx channel_filter_test)
add_dependencies(buildtests_cxx chttp2_settings_timeout_test)
add_dependencies(buildtests_cxx cli_call_test)
add_dependencies(buildtests_cxx client_channel_stress_test)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
Expand Down Expand Up @@ -9863,6 +9864,44 @@ target_link_libraries(channel_filter_test
endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)

add_executable(chttp2_settings_timeout_test
test/core/transport/chttp2/settings_timeout_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)


target_include_directories(chttp2_settings_timeout_test
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE ${BORINGSSL_ROOT_DIR}/include
PRIVATE ${PROTOBUF_ROOT_DIR}/src
PRIVATE ${BENCHMARK_ROOT_DIR}/include
PRIVATE ${ZLIB_ROOT_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
PRIVATE ${CARES_INCLUDE_DIR}
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
PRIVATE third_party/googletest/googletest/include
PRIVATE third_party/googletest/googletest
PRIVATE third_party/googletest/googlemock/include
PRIVATE third_party/googletest/googlemock
PRIVATE ${_gRPC_PROTO_GENS_DIR}
)

target_link_libraries(chttp2_settings_timeout_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
grpc
gpr_test_util
gpr
${_gRPC_GFLAGS_LIBRARIES}
)

endif (gRPC_BUILD_TESTS)
if (gRPC_BUILD_TESTS)

add_executable(cli_call_test
test/cpp/util/cli_call_test.cc
third_party/googletest/googletest/src/gtest-all.cc
Expand Down
48 changes: 48 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,7 @@ bm_metadata: $(BINDIR)/$(CONFIG)/bm_metadata
bm_pollset: $(BINDIR)/$(CONFIG)/bm_pollset
channel_arguments_test: $(BINDIR)/$(CONFIG)/channel_arguments_test
channel_filter_test: $(BINDIR)/$(CONFIG)/channel_filter_test
chttp2_settings_timeout_test: $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test
cli_call_test: $(BINDIR)/$(CONFIG)/cli_call_test
client_channel_stress_test: $(BINDIR)/$(CONFIG)/client_channel_stress_test
client_crash_test: $(BINDIR)/$(CONFIG)/client_crash_test
Expand Down Expand Up @@ -1557,6 +1558,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/bm_pollset \
$(BINDIR)/$(CONFIG)/channel_arguments_test \
$(BINDIR)/$(CONFIG)/channel_filter_test \
$(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test \
$(BINDIR)/$(CONFIG)/cli_call_test \
$(BINDIR)/$(CONFIG)/client_channel_stress_test \
$(BINDIR)/$(CONFIG)/client_crash_test \
Expand Down Expand Up @@ -1684,6 +1686,7 @@ buildtests_cxx: privatelibs_cxx \
$(BINDIR)/$(CONFIG)/bm_pollset \
$(BINDIR)/$(CONFIG)/channel_arguments_test \
$(BINDIR)/$(CONFIG)/channel_filter_test \
$(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test \
$(BINDIR)/$(CONFIG)/cli_call_test \
$(BINDIR)/$(CONFIG)/client_channel_stress_test \
$(BINDIR)/$(CONFIG)/client_crash_test \
Expand Down Expand Up @@ -2069,6 +2072,8 @@ test_cxx: buildtests_cxx
$(Q) $(BINDIR)/$(CONFIG)/channel_arguments_test || ( echo test channel_arguments_test failed ; exit 1 )
$(E) "[RUN] Testing channel_filter_test"
$(Q) $(BINDIR)/$(CONFIG)/channel_filter_test || ( echo test channel_filter_test failed ; exit 1 )
$(E) "[RUN] Testing chttp2_settings_timeout_test"
$(Q) $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test || ( echo test chttp2_settings_timeout_test failed ; exit 1 )
$(E) "[RUN] Testing cli_call_test"
$(Q) $(BINDIR)/$(CONFIG)/cli_call_test || ( echo test cli_call_test failed ; exit 1 )
$(E) "[RUN] Testing client_channel_stress_test"
Expand Down Expand Up @@ -14369,6 +14374,49 @@ endif
endif


CHTTP2_SETTINGS_TIMEOUT_TEST_SRC = \
test/core/transport/chttp2/settings_timeout_test.cc \

CHTTP2_SETTINGS_TIMEOUT_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(CHTTP2_SETTINGS_TIMEOUT_TEST_SRC))))
ifeq ($(NO_SECURE),true)

# You can't build secure targets if you don't have OpenSSL.

$(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test: openssl_dep_error

else




ifeq ($(NO_PROTOBUF),true)

# You can't build the protoc plugins or protobuf-enabled targets if you don't have protobuf 3.0.0+.

$(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test: protobuf_dep_error

else

$(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test: $(PROTOBUF_DEP) $(CHTTP2_SETTINGS_TIMEOUT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a
$(E) "[LD] Linking $@"
$(Q) mkdir -p `dirname $@`
$(Q) $(LDXX) $(LDFLAGS) $(CHTTP2_SETTINGS_TIMEOUT_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LDLIBSXX) $(LDLIBS_PROTOBUF) $(LDLIBS) $(LDLIBS_SECURE) $(GTEST_LIB) -o $(BINDIR)/$(CONFIG)/chttp2_settings_timeout_test

endif

endif

$(OBJDIR)/$(CONFIG)/test/core/transport/chttp2/settings_timeout_test.o: $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a

deps_chttp2_settings_timeout_test: $(CHTTP2_SETTINGS_TIMEOUT_TEST_OBJS:.o=.dep)

ifneq ($(NO_SECURE),true)
ifneq ($(NO_DEPS),true)
-include $(CHTTP2_SETTINGS_TIMEOUT_TEST_OBJS:.o=.dep)
endif
endif


CLI_CALL_TEST_SRC = \
test/cpp/util/cli_call_test.cc \

Expand Down
12 changes: 12 additions & 0 deletions build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3824,6 +3824,18 @@ targets:
- grpc
- gpr
uses_polling: false
- name: chttp2_settings_timeout_test
gtest: true
build: test
language: c++
src:
- test/core/transport/chttp2/settings_timeout_test.cc
deps:
- grpc_test_util
- grpc
- gpr_test_util
- gpr
uses_polling: true
- name: cli_call_test
gtest: true
build: test
Expand Down
3 changes: 3 additions & 0 deletions include/grpc/impl/codegen/grpc_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ typedef struct {
/** The time between the first and second connection attempts, in ms */
#define GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS \
"grpc.initial_reconnect_backoff_ms"
/** The timeout used on servers for finishing handshaking on an incoming
connection. Defaults to 120 seconds. */
#define GRPC_ARG_SERVER_HANDSHAKE_TIMEOUT_MS "grpc.server_handshake_timeout_ms"
/** This *should* be used for testing only.
The caller of the secure_channel_create functions may override the target
name used for SSL host name checking using this channel argument which is of
Expand Down
30 changes: 27 additions & 3 deletions src/core/ext/transport/chttp2/client/chttp2_connector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,35 @@ static void on_handshake_done(grpc_exec_ctx* exec_ctx, void* arg,
} else {
grpc_endpoint_delete_from_pollset_set(exec_ctx, args->endpoint,
c->args.interested_parties);
c->result->transport =
grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 1);
c->result->transport = grpc_create_chttp2_transport(exec_ctx, args->args,
args->endpoint, true);
GPR_ASSERT(c->result->transport);
// TODO(roth): We ideally want to wait until we receive HTTP/2
// settings from the server before we consider the connection
// established. If that doesn't happen before the connection
// timeout expires, then we should consider the connection attempt a
// failure and feed that information back into the backoff code.
// We could pass a notify_on_receive_settings callback to
// grpc_chttp2_transport_start_reading() to let us know when
// settings are received, but we would need to figure out how to use
// that information here.
//
// Unfortunately, we don't currently have a way to split apart the two
// effects of scheduling c->notify: we start sending RPCs immediately
// (which we want to do) and we consider the connection attempt successful
// (which we don't want to do until we get the notify_on_receive_settings
// callback from the transport). If we could split those things
// apart, then we could start sending RPCs but then wait for our
// timeout before deciding if the connection attempt is successful.
// If the attempt is not successful, then we would tear down the
// transport and feed the failure back into the backoff code.
//
// In addition, even if we did that, we would probably not want to do
// so until after transparent retries is implemented. Otherwise, any
// RPC that we attempt to send on the connection before the timeout
// would fail instead of being retried on a subsequent attempt.
grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport,
args->read_buffer);
args->read_buffer, nullptr);
c->result->channel_args = args->args;
}
grpc_closure* notify = c->notify;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ grpc_channel* grpc_insecure_channel_create_from_fd(
&exec_ctx, grpc_fd_create(fd, "client"), args, "fd-client");

grpc_transport* transport =
grpc_create_chttp2_transport(&exec_ctx, final_args, client, 1);
grpc_create_chttp2_transport(&exec_ctx, final_args, client, true);
GPR_ASSERT(transport);
grpc_channel* channel = grpc_channel_create(
&exec_ctx, target, final_args, GRPC_CLIENT_DIRECT_CHANNEL, transport);
grpc_channel_args_destroy(&exec_ctx, final_args);
grpc_chttp2_transport_start_reading(&exec_ctx, transport, nullptr);
grpc_chttp2_transport_start_reading(&exec_ctx, transport, nullptr, nullptr);

grpc_exec_ctx_finish(&exec_ctx);

Expand Down
87 changes: 74 additions & 13 deletions src/core/ext/transport/chttp2/server/chttp2_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <grpc/grpc.h>

#include <inttypes.h>
#include <limits.h>
#include <string.h>

#include <grpc/support/alloc.h>
Expand All @@ -31,6 +32,7 @@

#include "src/core/ext/filters/http/server/http_server_filter.h"
#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
#include "src/core/ext/transport/chttp2/transport/internal.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/handshaker.h"
#include "src/core/lib/channel/handshaker_registry.h"
Expand All @@ -53,12 +55,52 @@ typedef struct {
} server_state;

typedef struct {
gpr_refcount refs;
server_state* svr_state;
grpc_pollset* accepting_pollset;
grpc_tcp_server_acceptor* acceptor;
grpc_handshake_manager* handshake_mgr;
// State for enforcing handshake timeout on receiving HTTP/2 settings.
grpc_chttp2_transport* transport;
grpc_millis deadline;
grpc_timer timer;
grpc_closure on_timeout;
grpc_closure on_receive_settings;
} server_connection_state;

static void server_connection_state_unref(
grpc_exec_ctx* exec_ctx, server_connection_state* connection_state) {
if (gpr_unref(&connection_state->refs)) {
if (connection_state->transport != nullptr) {
GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, connection_state->transport,
"receive settings timeout");
}
gpr_free(connection_state);
}
}

static void on_timeout(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {
server_connection_state* connection_state = (server_connection_state*)arg;
// Note that we may be called with GRPC_ERROR_NONE when the timer fires
// or with an error indicating that the timer system is being shut down.
if (error != GRPC_ERROR_CANCELLED) {
grpc_transport_op* op = grpc_make_transport_op(nullptr);
op->disconnect_with_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"Did not receive HTTP/2 settings before handshake timeout");
grpc_transport_perform_op(exec_ctx, &connection_state->transport->base, op);
}
server_connection_state_unref(exec_ctx, connection_state);
}

static void on_receive_settings(grpc_exec_ctx* exec_ctx, void* arg,
grpc_error* error) {
server_connection_state* connection_state = (server_connection_state*)arg;
if (error == GRPC_ERROR_NONE) {
grpc_timer_cancel(exec_ctx, &connection_state->timer);
}
server_connection_state_unref(exec_ctx, connection_state);
}

static void on_handshake_done(grpc_exec_ctx* exec_ctx, void* arg,
grpc_error* error) {
grpc_handshaker_args* args = (grpc_handshaker_args*)arg;
Expand All @@ -68,7 +110,6 @@ static void on_handshake_done(grpc_exec_ctx* exec_ctx, void* arg,
if (error != GRPC_ERROR_NONE || connection_state->svr_state->shutdown) {
const char* error_str = grpc_error_string(error);
gpr_log(GPR_DEBUG, "Handshaking failed: %s", error_str);

if (error == GRPC_ERROR_NONE && args->endpoint != nullptr) {
// We were shut down after handshaking completed successfully, so
// destroy the endpoint here.
Expand All @@ -87,24 +128,40 @@ static void on_handshake_done(grpc_exec_ctx* exec_ctx, void* arg,
// handshaker may have handed off the connection to some external
// code, so we can just clean up here without creating a transport.
if (args->endpoint != nullptr) {
grpc_transport* transport =
grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 0);
grpc_transport* transport = grpc_create_chttp2_transport(
exec_ctx, args->args, args->endpoint, false);
grpc_server_setup_transport(
exec_ctx, connection_state->svr_state->server, transport,
connection_state->accepting_pollset, args->args);
grpc_chttp2_transport_start_reading(exec_ctx, transport,
args->read_buffer);
// Use notify_on_receive_settings callback to enforce the
// handshake deadline.
connection_state->transport = (grpc_chttp2_transport*)transport;
gpr_ref(&connection_state->refs);
GRPC_CLOSURE_INIT(&connection_state->on_receive_settings,
on_receive_settings, connection_state,
grpc_schedule_on_exec_ctx);
grpc_chttp2_transport_start_reading(
exec_ctx, transport, args->read_buffer,
&connection_state->on_receive_settings);
grpc_channel_args_destroy(exec_ctx, args->args);
gpr_ref(&connection_state->refs);
GRPC_CHTTP2_REF_TRANSPORT((grpc_chttp2_transport*)transport,
"receive settings timeout");
GRPC_CLOSURE_INIT(&connection_state->on_timeout, on_timeout,
connection_state, grpc_schedule_on_exec_ctx);
grpc_timer_init(exec_ctx, &connection_state->timer,
connection_state->deadline,
&connection_state->on_timeout);
}
}
grpc_handshake_manager_pending_list_remove(
&connection_state->svr_state->pending_handshake_mgrs,
connection_state->handshake_mgr);
gpr_mu_unlock(&connection_state->svr_state->mu);
grpc_handshake_manager_destroy(exec_ctx, connection_state->handshake_mgr);
grpc_tcp_server_unref(exec_ctx, connection_state->svr_state->tcp_server);
gpr_free(connection_state->acceptor);
gpr_free(connection_state);
grpc_tcp_server_unref(exec_ctx, connection_state->svr_state->tcp_server);
server_connection_state_unref(exec_ctx, connection_state);
}

static void on_accept(grpc_exec_ctx* exec_ctx, void* arg, grpc_endpoint* tcp,
Expand All @@ -125,19 +182,23 @@ static void on_accept(grpc_exec_ctx* exec_ctx, void* arg, grpc_endpoint* tcp,
gpr_mu_unlock(&state->mu);
grpc_tcp_server_ref(state->tcp_server);
server_connection_state* connection_state =
(server_connection_state*)gpr_malloc(sizeof(*connection_state));
(server_connection_state*)gpr_zalloc(sizeof(*connection_state));
gpr_ref_init(&connection_state->refs, 1);
connection_state->svr_state = state;
connection_state->accepting_pollset = accepting_pollset;
connection_state->acceptor = acceptor;
connection_state->handshake_mgr = handshake_mgr;
grpc_handshakers_add(exec_ctx, HANDSHAKER_SERVER, state->args,
connection_state->handshake_mgr);
// TODO(roth): We should really get this timeout value from channel
// args instead of hard-coding it.
const grpc_millis deadline =
grpc_exec_ctx_now(exec_ctx) + 120 * GPR_MS_PER_SEC;
const grpc_arg* timeout_arg =
grpc_channel_args_find(state->args, GRPC_ARG_SERVER_HANDSHAKE_TIMEOUT_MS);
connection_state->deadline =
grpc_exec_ctx_now(exec_ctx) +
grpc_channel_arg_get_integer(timeout_arg,
{120 * GPR_MS_PER_SEC, 1, INT_MAX});
grpc_handshake_manager_do_handshake(exec_ctx, connection_state->handshake_mgr,
tcp, state->args, deadline, acceptor,
tcp, state->args,
connection_state->deadline, acceptor,
on_handshake_done, connection_state);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ void grpc_server_add_insecure_channel_from_fd(grpc_server* server,

const grpc_channel_args* server_args = grpc_server_get_channel_args(server);
grpc_transport* transport = grpc_create_chttp2_transport(
&exec_ctx, server_args, server_endpoint, 0 /* is_client */);
&exec_ctx, server_args, server_endpoint, false /* is_client */);

grpc_pollset** pollsets;
size_t num_pollsets = 0;
Expand All @@ -62,7 +62,7 @@ void grpc_server_add_insecure_channel_from_fd(grpc_server* server,

grpc_server_setup_transport(&exec_ctx, server, transport, nullptr,
server_args);
grpc_chttp2_transport_start_reading(&exec_ctx, transport, nullptr);
grpc_chttp2_transport_start_reading(&exec_ctx, transport, nullptr, nullptr);
grpc_exec_ctx_finish(&exec_ctx);
}

Expand Down
Loading

0 comments on commit 6fa206d

Please sign in to comment.