Add max age enforcement for server channels
diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h
index 887c176..6b380bf 100644
--- a/include/grpc/impl/codegen/grpc_types.h
+++ b/include/grpc/impl/codegen/grpc_types.h
@@ -163,6 +163,10 @@
/** Maximum message length that the channel can send. Int valued, bytes.
-1 means unlimited. */
#define GRPC_ARG_MAX_SEND_MESSAGE_LENGTH "grpc.max_send_message_length"
+
+#define GPRC_ARG_MAX_CONNECION_AGE_S "grpc.max_connection_age"
+
+#define GPRC_ARG_MAX_CONNECION_AGE_GRACE_S "grpc.max_connection_age_grace"
/** Initial sequence number for http2 transports. Int valued. */
#define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \
"grpc.http2.initial_sequence_number"
diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
index a123c9c..dee722e 100644
--- a/src/core/lib/surface/server.c
+++ b/src/core/lib/surface/server.c
@@ -45,6 +45,7 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/connected_channel.h"
#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/timer.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/support/stack_lockfree.h"
#include "src/core/lib/support/string.h"
@@ -53,9 +54,12 @@
#include "src/core/lib/surface/channel.h"
#include "src/core/lib/surface/completion_queue.h"
#include "src/core/lib/surface/init.h"
+#include "src/core/lib/transport/http2_errors.h"
#include "src/core/lib/transport/metadata.h"
#include "src/core/lib/transport/static_metadata.h"
+#define DEFAULT_MAX_CONNECTION_AGE_S INT_MAX
+
typedef struct listener {
void *arg;
void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg,
@@ -116,6 +120,9 @@
uint32_t registered_method_max_probes;
grpc_closure finish_destroy_channel_closure;
grpc_closure channel_connectivity_changed;
+ grpc_timer max_age_timer;
+ gpr_timespec max_connection_age;
+ grpc_closure close_max_age_channel;
};
typedef struct shutdown_tag {
@@ -381,6 +388,7 @@
static void server_ref(grpc_server *server) {
gpr_ref(&server->internal_refcount);
+ gpr_log(GPR_DEBUG, "server_ref");
}
static void server_delete(grpc_exec_ctx *exec_ctx, grpc_server *server) {
@@ -442,9 +450,11 @@
static void destroy_channel(grpc_exec_ctx *exec_ctx, channel_data *chand,
grpc_error *error) {
+ gpr_log(GPR_DEBUG, "destroy_channel");
if (is_channel_orphaned(chand)) return;
GPR_ASSERT(chand->server != NULL);
orphan_channel(chand);
+ grpc_timer_cancel(exec_ctx, &chand->max_age_timer);
server_ref(chand->server);
maybe_finish_shutdown(exec_ctx, chand->server);
grpc_closure_init(&chand->finish_destroy_channel_closure,
@@ -831,6 +841,7 @@
static void accept_stream(grpc_exec_ctx *exec_ctx, void *cd,
grpc_transport *transport,
const void *transport_server_data) {
+ gpr_log(GPR_DEBUG, "accept_stream");
channel_data *chand = cd;
/* create a call */
grpc_call_create_args args;
@@ -882,6 +893,7 @@
static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx,
grpc_call_element *elem,
const grpc_call_element_args *args) {
+ gpr_log(GPR_DEBUG, "init_call_elem");
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
memset(calld, 0, sizeof(call_data));
@@ -903,6 +915,7 @@
channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data;
+ gpr_log(GPR_DEBUG, "destroy_call_elem");
GPR_ASSERT(calld->state != PENDING);
if (calld->host_set) {
@@ -918,6 +931,23 @@
server_unref(exec_ctx, chand->server);
}
+static void close_max_age_channel(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_error *error) {
+ channel_data *chand = arg;
+ if (error == GRPC_ERROR_NONE) {
+ grpc_transport_op *op = grpc_make_transport_op(NULL);
+ op->goaway_error =
+ grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_age"),
+ GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR);
+ grpc_channel_element *elem = grpc_channel_stack_element(
+ grpc_channel_get_channel_stack(chand->channel), 0);
+ elem->filter->start_transport_op(exec_ctx, elem, op);
+ } else if (error != GRPC_ERROR_CANCELLED) {
+ GRPC_LOG_IF_ERROR("close_max_age_channel", error);
+ }
+ GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "max age");
+}
+
static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx,
grpc_channel_element *elem,
grpc_channel_element_args *args) {
@@ -929,6 +959,28 @@
chand->next = chand->prev = chand;
chand->registered_methods = NULL;
chand->connectivity_state = GRPC_CHANNEL_IDLE;
+ chand->max_connection_age =
+ DEFAULT_MAX_CONNECTION_AGE_S == INT_MAX
+ ? gpr_inf_future(GPR_TIMESPAN)
+ : gpr_time_from_seconds(DEFAULT_MAX_CONNECTION_AGE_S, GPR_TIMESPAN);
+ grpc_closure_init(&chand->close_max_age_channel, close_max_age_channel, chand,
+ grpc_schedule_on_exec_ctx);
+ const grpc_channel_args *channel_args = args->channel_args;
+ if (channel_args) {
+ size_t i;
+ for (i = 0; i < channel_args->num_args; i++) {
+ if (0 ==
+ strcmp(channel_args->args[i].key, GPRC_ARG_MAX_CONNECION_AGE_S)) {
+ const int value = grpc_channel_arg_get_integer(
+ &channel_args->args[i],
+ (grpc_integer_options){DEFAULT_MAX_CONNECTION_AGE_S, 1, INT_MAX});
+ chand->max_connection_age =
+ value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN)
+ : gpr_time_from_seconds(value, GPR_TIMESPAN);
+ }
+ }
+ }
+
grpc_closure_init(&chand->channel_connectivity_changed,
channel_connectivity_changed, chand,
grpc_schedule_on_exec_ctx);
@@ -1132,6 +1184,7 @@
grpc_transport *transport,
grpc_pollset *accepting_pollset,
const grpc_channel_args *args) {
+ gpr_log(GPR_DEBUG, "grpc_server_setup_transport");
size_t num_registered_methods;
size_t alloc;
registered_method *rm;
@@ -1152,6 +1205,11 @@
chand->server = s;
server_ref(s);
chand->channel = channel;
+ GRPC_CHANNEL_INTERNAL_REF(channel, "max age");
+ grpc_timer_init(
+ exec_ctx, &chand->max_age_timer,
+ gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), chand->max_connection_age),
+ &chand->close_max_age_channel, gpr_now(GPR_CLOCK_MONOTONIC));
size_t cq_idx;
grpc_completion_queue *accepting_cq = grpc_cq_from_pollset(accepting_pollset);