1616
1717package io .grpc .okhttp ;
1818
19+ import static io .grpc .okhttp .OkHttpServerBuilder .MAX_CONNECTION_AGE_NANOS_DISABLED ;
1920import static io .grpc .okhttp .OkHttpServerBuilder .MAX_CONNECTION_IDLE_NANOS_DISABLED ;
2021
2122import com .google .common .base .Preconditions ;
3132import io .grpc .internal .GrpcUtil ;
3233import io .grpc .internal .KeepAliveEnforcer ;
3334import io .grpc .internal .KeepAliveManager ;
35+ import io .grpc .internal .LogExceptionRunnable ;
3436import io .grpc .internal .MaxConnectionIdleManager ;
3537import io .grpc .internal .ObjectPool ;
3638import io .grpc .internal .SerializingExecutor ;
@@ -96,6 +98,7 @@ final class OkHttpServerTransport implements ServerTransport,
9698 private Attributes attributes ;
9799 private KeepAliveManager keepAliveManager ;
98100 private MaxConnectionIdleManager maxConnectionIdleManager ;
101+ private ScheduledFuture <?> maxConnectionAgeMonitor ;
99102 private final KeepAliveEnforcer keepAliveEnforcer ;
100103
101104 private final Object lock = new Object ();
@@ -223,6 +226,15 @@ public void data(boolean outFinished, int streamId, Buffer source, int byteCount
223226 maxConnectionIdleManager .start (this ::shutdown , scheduledExecutorService );
224227 }
225228
229+ if (config .maxConnectionAgeInNanos != MAX_CONNECTION_AGE_NANOS_DISABLED ) {
230+ long maxConnectionAgeInNanos =
231+ (long ) ((.9D + Math .random () * .2D ) * config .maxConnectionAgeInNanos );
232+ maxConnectionAgeMonitor = scheduledExecutorService .schedule (
233+ new LogExceptionRunnable (() -> shutdown (config .maxConnectionAgeGraceInNanos )),
234+ maxConnectionAgeInNanos ,
235+ TimeUnit .NANOSECONDS );
236+ }
237+
226238 transportExecutor .execute (
227239 new FrameHandler (variant .newReader (Okio .buffer (Okio .source (socket )), false )));
228240 } catch (Error | IOException | RuntimeException ex ) {
@@ -238,6 +250,10 @@ public void data(boolean outFinished, int streamId, Buffer source, int byteCount
238250
239251 @ Override
240252 public void shutdown () {
253+ shutdown (TimeUnit .SECONDS .toNanos (1L ));
254+ }
255+
256+ private void shutdown (Long graceTimeInNanos ) {
241257 synchronized (lock ) {
242258 if (gracefulShutdown || abruptShutdown ) {
243259 return ;
@@ -251,7 +267,7 @@ public void shutdown() {
251267 // we also set a timer to limit the upper bound in case the PING is excessively stalled or
252268 // the client is malicious.
253269 secondGoawayTimer = scheduledExecutorService .schedule (
254- this ::triggerGracefulSecondGoaway , 1 , TimeUnit .SECONDS );
270+ this ::triggerGracefulSecondGoaway , graceTimeInNanos , TimeUnit .NANOSECONDS );
255271 frameWriter .goAway (Integer .MAX_VALUE , ErrorCode .NO_ERROR , new byte [0 ]);
256272 frameWriter .ping (false , 0 , GRACEFUL_SHUTDOWN_PING );
257273 frameWriter .flush ();
@@ -348,6 +364,10 @@ private void terminated() {
348364 if (maxConnectionIdleManager != null ) {
349365 maxConnectionIdleManager .onTransportTermination ();
350366 }
367+
368+ if (maxConnectionAgeMonitor != null ) {
369+ maxConnectionAgeMonitor .cancel (false );
370+ }
351371 transportExecutor = config .transportExecutorPool .returnObject (transportExecutor );
352372 scheduledExecutorService =
353373 config .scheduledExecutorServicePool .returnObject (scheduledExecutorService );
@@ -479,6 +499,8 @@ static final class Config {
479499 final long maxConnectionIdleNanos ;
480500 final boolean permitKeepAliveWithoutCalls ;
481501 final long permitKeepAliveTimeInNanos ;
502+ final long maxConnectionAgeInNanos ;
503+ final long maxConnectionAgeGraceInNanos ;
482504
483505 public Config (
484506 OkHttpServerBuilder builder ,
@@ -501,6 +523,8 @@ public Config(
501523 maxConnectionIdleNanos = builder .maxConnectionIdleInNanos ;
502524 permitKeepAliveWithoutCalls = builder .permitKeepAliveWithoutCalls ;
503525 permitKeepAliveTimeInNanos = builder .permitKeepAliveTimeInNanos ;
526+ maxConnectionAgeInNanos = builder .maxConnectionAgeInNanos ;
527+ maxConnectionAgeGraceInNanos = builder .maxConnectionAgeGraceInNanos ;
504528 }
505529 }
506530
0 commit comments