|  | @@ -153,6 +153,7 @@ static void retry_initiate_ping_locked(grpc_exec_ctx *exec_ctx, void *tp,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define DEFAULT_MIN_TIME_BETWEEN_PINGS_MS 0
 | 
	
		
			
				|  |  |  #define DEFAULT_MAX_PINGS_BETWEEN_DATA 3
 | 
	
		
			
				|  |  | +#define DEFAULT_MAX_PING_STRIKES 2
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /** keepalive-relevant functions */
 | 
	
		
			
				|  |  |  static void init_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
	
		
			
				|  | @@ -351,6 +352,7 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  |        .max_pings_without_data = DEFAULT_MAX_PINGS_BETWEEN_DATA,
 | 
	
		
			
				|  |  |        .min_time_between_pings =
 | 
	
		
			
				|  |  |            gpr_time_from_millis(DEFAULT_MIN_TIME_BETWEEN_PINGS_MS, GPR_TIMESPAN),
 | 
	
		
			
				|  |  | +      .max_ping_strikes = DEFAULT_MAX_PING_STRIKES,
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* client-side keepalive setting */
 | 
	
	
		
			
				|  | @@ -396,6 +398,11 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  |          t->ping_policy.max_pings_without_data = grpc_channel_arg_get_integer(
 | 
	
		
			
				|  |  |              &channel_args->args[i],
 | 
	
		
			
				|  |  |              (grpc_integer_options){DEFAULT_MAX_PINGS_BETWEEN_DATA, 0, INT_MAX});
 | 
	
		
			
				|  |  | +      } else if (0 == strcmp(channel_args->args[i].key,
 | 
	
		
			
				|  |  | +                             GRPC_ARG_HTTP2_MAX_PING_STRIKES)) {
 | 
	
		
			
				|  |  | +        t->ping_policy.max_ping_strikes = grpc_channel_arg_get_integer(
 | 
	
		
			
				|  |  | +            &channel_args->args[i],
 | 
	
		
			
				|  |  | +            (grpc_integer_options){DEFAULT_MAX_PINGS_BETWEEN_DATA, 0, INT_MAX});
 | 
	
		
			
				|  |  |        } else if (0 == strcmp(channel_args->args[i].key,
 | 
	
		
			
				|  |  |                               GRPC_ARG_HTTP2_MIN_TIME_BETWEEN_PINGS_MS)) {
 | 
	
		
			
				|  |  |          t->ping_policy.min_time_between_pings = gpr_time_from_millis(
 | 
	
	
		
			
				|  | @@ -488,6 +495,9 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  |        t->ping_policy.max_pings_without_data;
 | 
	
		
			
				|  |  |    t->ping_state.is_delayed_ping_timer_set = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  t->ping_recv_state.last_ping_recv_time = gpr_inf_past(GPR_CLOCK_MONOTONIC);
 | 
	
		
			
				|  |  | +  t->ping_recv_state.ping_strikes = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    /* Start client-side keepalive pings */
 | 
	
		
			
				|  |  |    if (t->is_client) {
 | 
	
		
			
				|  |  |      t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING;
 | 
	
	
		
			
				|  | @@ -1448,6 +1458,19 @@ static void send_goaway(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t,
 | 
	
		
			
				|  |  |    GRPC_ERROR_UNREF(error);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +void grpc_chttp2_ping_strike(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                             grpc_chttp2_transport *t) {
 | 
	
		
			
				|  |  | +  if (++t->ping_recv_state.ping_strikes > t->ping_policy.max_ping_strikes &&
 | 
	
		
			
				|  |  | +      t->ping_policy.max_ping_strikes != 0) {
 | 
	
		
			
				|  |  | +    send_goaway(exec_ctx, t,
 | 
	
		
			
				|  |  | +                grpc_error_set_int(GRPC_ERROR_CREATE("too_many_pings"),
 | 
	
		
			
				|  |  | +                                   GRPC_ERROR_INT_HTTP2_ERROR,
 | 
	
		
			
				|  |  | +                                   GRPC_HTTP2_ENHANCE_YOUR_CALM));
 | 
	
		
			
				|  |  | +    /*The transport will be closed after the write is done */
 | 
	
		
			
				|  |  | +    close_transport_locked(exec_ctx, t, GRPC_ERROR_CREATE("Too many pings"));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  |                                          void *stream_op,
 | 
	
		
			
				|  |  |                                          grpc_error *error_ignored) {
 | 
	
	
		
			
				|  | @@ -2163,7 +2186,8 @@ static void init_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg,
 | 
	
		
			
				|  |  |    grpc_chttp2_transport *t = arg;
 | 
	
		
			
				|  |  |    GPR_ASSERT(t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING);
 | 
	
		
			
				|  |  |    if (error == GRPC_ERROR_NONE && !(t->destroying || t->closed)) {
 | 
	
		
			
				|  |  | -    if (t->keepalive_permit_without_calls || t->stream_map.count > 0) {
 | 
	
		
			
				|  |  | +    if (t->keepalive_permit_without_calls ||
 | 
	
		
			
				|  |  | +        grpc_chttp2_stream_map_size(&t->stream_map) > 0) {
 | 
	
		
			
				|  |  |        t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_PINGING;
 | 
	
		
			
				|  |  |        GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive ping end");
 | 
	
		
			
				|  |  |        send_ping_locked(exec_ctx, t, GRPC_CHTTP2_PING_ON_NEXT_WRITE,
 |