1 /* 2 * Copyright 2014 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.okhttp; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 import static io.grpc.internal.GrpcUtil.DEFAULT_KEEPALIVE_TIMEOUT_NANOS; 21 import static io.grpc.internal.GrpcUtil.DEFAULT_KEEPALIVE_TIME_NANOS; 22 import static io.grpc.internal.GrpcUtil.KEEPALIVE_TIME_NANOS_DISABLED; 23 24 import com.google.common.annotations.VisibleForTesting; 25 import com.google.common.base.Preconditions; 26 import io.grpc.Attributes; 27 import io.grpc.ExperimentalApi; 28 import io.grpc.Internal; 29 import io.grpc.NameResolver; 30 import io.grpc.internal.AbstractManagedChannelImplBuilder; 31 import io.grpc.internal.AtomicBackoff; 32 import io.grpc.internal.ClientTransportFactory; 33 import io.grpc.internal.ConnectionClientTransport; 34 import io.grpc.internal.GrpcUtil; 35 import io.grpc.internal.KeepAliveManager; 36 import io.grpc.internal.SharedResourceHolder; 37 import io.grpc.internal.SharedResourceHolder.Resource; 38 import io.grpc.internal.TransportTracer; 39 import io.grpc.okhttp.internal.CipherSuite; 40 import io.grpc.okhttp.internal.ConnectionSpec; 41 import io.grpc.okhttp.internal.Platform; 42 import io.grpc.okhttp.internal.TlsVersion; 43 import java.net.InetSocketAddress; 44 import java.net.SocketAddress; 45 import java.security.GeneralSecurityException; 46 import java.security.KeyStore; 47 import java.security.SecureRandom; 48 import java.util.concurrent.Executor; 49 import java.util.concurrent.ExecutorService; 50 import java.util.concurrent.Executors; 51 import java.util.concurrent.ScheduledExecutorService; 52 import java.util.concurrent.TimeUnit; 53 import javax.annotation.Nullable; 54 import javax.net.ssl.HostnameVerifier; 55 import javax.net.ssl.SSLContext; 56 import javax.net.ssl.SSLSocketFactory; 57 import javax.net.ssl.TrustManagerFactory; 58 59 /** Convenience class for building channels with the OkHttp transport. */ 60 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1785") 61 public class OkHttpChannelBuilder extends 62 AbstractManagedChannelImplBuilder<OkHttpChannelBuilder> { 63 64 /** Identifies the negotiation used for starting up HTTP/2. */ 65 private enum NegotiationType { 66 /** Uses TLS ALPN/NPN negotiation, assumes an SSL connection. */ 67 TLS, 68 69 /** 70 * Just assume the connection is plaintext (non-SSL) and the remote endpoint supports HTTP/2 71 * directly without an upgrade. 72 */ 73 PLAINTEXT 74 } 75 76 /** 77 * ConnectionSpec closely matching the default configuration that could be used as a basis for 78 * modification. 79 * 80 * <p>Since this field is the only reference in gRPC to ConnectionSpec that may not be ProGuarded, 81 * we are removing the field to reduce method count. We've been unable to find any existing users 82 * of the field, and any such user would highly likely at least be changing the cipher suites, 83 * which is sort of the only part that's non-obvious. Any existing user should instead create 84 * their own spec from scratch or base it off ConnectionSpec.MODERN_TLS if believed to be 85 * necessary. If this was providing you with value and don't want to see it removed, open a GitHub 86 * issue to discuss keeping it. 87 * 88 * @deprecated Deemed of little benefit and users weren't using it. Just define one yourself 89 */ 90 @Deprecated 91 public static final com.squareup.okhttp.ConnectionSpec DEFAULT_CONNECTION_SPEC = 92 new com.squareup.okhttp.ConnectionSpec.Builder(com.squareup.okhttp.ConnectionSpec.MODERN_TLS) 93 .cipherSuites( 94 // The following items should be sync with Netty's Http2SecurityUtil.CIPHERS. 95 com.squareup.okhttp.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 96 com.squareup.okhttp.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 97 com.squareup.okhttp.CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 98 com.squareup.okhttp.CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 99 com.squareup.okhttp.CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 100 com.squareup.okhttp.CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, 101 com.squareup.okhttp.CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 102 com.squareup.okhttp.CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) 103 .tlsVersions(com.squareup.okhttp.TlsVersion.TLS_1_2) 104 .supportsTlsExtensions(true) 105 .build(); 106 107 @VisibleForTesting 108 static final ConnectionSpec INTERNAL_DEFAULT_CONNECTION_SPEC = 109 new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 110 .cipherSuites( 111 // The following items should be sync with Netty's Http2SecurityUtil.CIPHERS. 112 CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 113 CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 114 CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 115 CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 116 CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 117 CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, 118 CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 119 CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) 120 .tlsVersions(TlsVersion.TLS_1_2) 121 .supportsTlsExtensions(true) 122 .build(); 123 124 private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L); 125 private static final Resource<ExecutorService> SHARED_EXECUTOR = 126 new Resource<ExecutorService>() { 127 @Override 128 public ExecutorService create() { 129 return Executors.newCachedThreadPool(GrpcUtil.getThreadFactory("grpc-okhttp-%d", true)); 130 } 131 132 @Override 133 public void close(ExecutorService executor) { 134 executor.shutdown(); 135 } 136 }; 137 138 /** Creates a new builder for the given server host and port. */ forAddress(String host, int port)139 public static OkHttpChannelBuilder forAddress(String host, int port) { 140 return new OkHttpChannelBuilder(host, port); 141 } 142 143 /** 144 * Creates a new builder for the given target that will be resolved by 145 * {@link io.grpc.NameResolver}. 146 */ forTarget(String target)147 public static OkHttpChannelBuilder forTarget(String target) { 148 return new OkHttpChannelBuilder(target); 149 } 150 151 private Executor transportExecutor; 152 private ScheduledExecutorService scheduledExecutorService; 153 154 private SSLSocketFactory sslSocketFactory; 155 private HostnameVerifier hostnameVerifier; 156 private ConnectionSpec connectionSpec = INTERNAL_DEFAULT_CONNECTION_SPEC; 157 private NegotiationType negotiationType = NegotiationType.TLS; 158 private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED; 159 private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS; 160 private boolean keepAliveWithoutCalls; 161 OkHttpChannelBuilder(String host, int port)162 protected OkHttpChannelBuilder(String host, int port) { 163 this(GrpcUtil.authorityFromHostAndPort(host, port)); 164 } 165 OkHttpChannelBuilder(String target)166 private OkHttpChannelBuilder(String target) { 167 super(target); 168 } 169 170 @VisibleForTesting setTransportTracerFactory( TransportTracer.Factory transportTracerFactory)171 final OkHttpChannelBuilder setTransportTracerFactory( 172 TransportTracer.Factory transportTracerFactory) { 173 this.transportTracerFactory = transportTracerFactory; 174 return this; 175 } 176 177 /** 178 * Override the default executor necessary for internal transport use. 179 * 180 * <p>The channel does not take ownership of the given executor. It is the caller' responsibility 181 * to shutdown the executor when appropriate. 182 */ transportExecutor(@ullable Executor transportExecutor)183 public final OkHttpChannelBuilder transportExecutor(@Nullable Executor transportExecutor) { 184 this.transportExecutor = transportExecutor; 185 return this; 186 } 187 188 /** 189 * Sets the negotiation type for the HTTP/2 connection. 190 * 191 * <p>If TLS is enabled a default {@link SSLSocketFactory} is created using the best 192 * {@link java.security.Provider} available and is NOT based on 193 * {@link SSLSocketFactory#getDefault}. To more precisely control the TLS configuration call 194 * {@link #sslSocketFactory} to override the socket factory used. 195 * 196 * <p>Default: <code>TLS</code> 197 * 198 * @deprecated use {@link #usePlaintext()} or {@link #useTransportSecurity()} instead. 199 */ 200 @Deprecated negotiationType(io.grpc.okhttp.NegotiationType type)201 public final OkHttpChannelBuilder negotiationType(io.grpc.okhttp.NegotiationType type) { 202 Preconditions.checkNotNull(type, "type"); 203 switch (type) { 204 case TLS: 205 negotiationType = NegotiationType.TLS; 206 break; 207 case PLAINTEXT: 208 negotiationType = NegotiationType.PLAINTEXT; 209 break; 210 default: 211 throw new AssertionError("Unknown negotiation type: " + type); 212 } 213 return this; 214 } 215 216 /** 217 * Enable keepalive with default delay and timeout. 218 * 219 * @deprecated Use {@link #keepAliveTime} instead 220 */ 221 @Deprecated enableKeepAlive(boolean enable)222 public final OkHttpChannelBuilder enableKeepAlive(boolean enable) { 223 if (enable) { 224 return keepAliveTime(DEFAULT_KEEPALIVE_TIME_NANOS, TimeUnit.NANOSECONDS); 225 } else { 226 return keepAliveTime(KEEPALIVE_TIME_NANOS_DISABLED, TimeUnit.NANOSECONDS); 227 } 228 } 229 230 /** 231 * Enable keepalive with custom delay and timeout. 232 * 233 * @deprecated Use {@link #keepAliveTime} and {@link #keepAliveTimeout} instead 234 */ 235 @Deprecated enableKeepAlive(boolean enable, long keepAliveTime, TimeUnit delayUnit, long keepAliveTimeout, TimeUnit timeoutUnit)236 public final OkHttpChannelBuilder enableKeepAlive(boolean enable, long keepAliveTime, 237 TimeUnit delayUnit, long keepAliveTimeout, TimeUnit timeoutUnit) { 238 if (enable) { 239 return keepAliveTime(keepAliveTime, delayUnit) 240 .keepAliveTimeout(keepAliveTimeout, timeoutUnit); 241 } else { 242 return keepAliveTime(KEEPALIVE_TIME_NANOS_DISABLED, TimeUnit.NANOSECONDS); 243 } 244 } 245 246 /** 247 * {@inheritDoc} 248 * 249 * @since 1.3.0 250 */ 251 @Override keepAliveTime(long keepAliveTime, TimeUnit timeUnit)252 public OkHttpChannelBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) { 253 Preconditions.checkArgument(keepAliveTime > 0L, "keepalive time must be positive"); 254 keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime); 255 keepAliveTimeNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeNanos); 256 if (keepAliveTimeNanos >= AS_LARGE_AS_INFINITE) { 257 // Bump keepalive time to infinite. This disables keepalive. 258 keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED; 259 } 260 return this; 261 } 262 263 /** 264 * {@inheritDoc} 265 * 266 * @since 1.3.0 267 */ 268 @Override keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit)269 public OkHttpChannelBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) { 270 Preconditions.checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive"); 271 keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout); 272 keepAliveTimeoutNanos = KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutNanos); 273 return this; 274 } 275 276 /** 277 * {@inheritDoc} 278 * 279 * @since 1.3.0 280 * @see #keepAliveTime(long, TimeUnit) 281 */ 282 @Override keepAliveWithoutCalls(boolean enable)283 public OkHttpChannelBuilder keepAliveWithoutCalls(boolean enable) { 284 keepAliveWithoutCalls = enable; 285 return this; 286 } 287 288 /** 289 * Override the default {@link SSLSocketFactory} and enable TLS negotiation. 290 */ sslSocketFactory(SSLSocketFactory factory)291 public final OkHttpChannelBuilder sslSocketFactory(SSLSocketFactory factory) { 292 this.sslSocketFactory = factory; 293 negotiationType = NegotiationType.TLS; 294 return this; 295 } 296 297 /** 298 * Set the hostname verifier to use when using TLS negotiation. The hostnameVerifier is only used 299 * if using TLS negotiation. If the hostname verifier is not set, a default hostname verifier is 300 * used. 301 * 302 * <p>Be careful when setting a custom hostname verifier! By setting a non-null value, you are 303 * replacing all default verification behavior. If the hostname verifier you supply does not 304 * effectively supply the same checks, you may be removing the security assurances that TLS aims 305 * to provide.</p> 306 * 307 * <p>This method should not be used to avoid hostname verification, even during testing, since 308 * {@link #overrideAuthority} is a safer alternative as it does not disable any security checks. 309 * </p> 310 * 311 * @see io.grpc.okhttp.internal.OkHostnameVerifier 312 * 313 * @since 1.6.0 314 * @return this 315 * 316 */ hostnameVerifier(@ullable HostnameVerifier hostnameVerifier)317 public final OkHttpChannelBuilder hostnameVerifier(@Nullable HostnameVerifier hostnameVerifier) { 318 this.hostnameVerifier = hostnameVerifier; 319 return this; 320 } 321 322 /** 323 * For secure connection, provides a ConnectionSpec to specify Cipher suite and 324 * TLS versions. 325 * 326 * <p>By default a modern, HTTP/2-compatible spec will be used. 327 * 328 * <p>This method is only used when building a secure connection. For plaintext 329 * connection, use {@link #usePlaintext()} instead. 330 * 331 * @throws IllegalArgumentException 332 * If {@code connectionSpec} is not with TLS 333 */ connectionSpec( com.squareup.okhttp.ConnectionSpec connectionSpec)334 public final OkHttpChannelBuilder connectionSpec( 335 com.squareup.okhttp.ConnectionSpec connectionSpec) { 336 Preconditions.checkArgument(connectionSpec.isTls(), "plaintext ConnectionSpec is not accepted"); 337 this.connectionSpec = Utils.convertSpec(connectionSpec); 338 return this; 339 } 340 341 /** 342 * Equivalent to using {@link #negotiationType} with {@code PLAINTEXT}. 343 * 344 * @deprecated use {@link #usePlaintext()} instead. 345 */ 346 @Override 347 @Deprecated usePlaintext(boolean skipNegotiation)348 public final OkHttpChannelBuilder usePlaintext(boolean skipNegotiation) { 349 if (skipNegotiation) { 350 negotiationType(io.grpc.okhttp.NegotiationType.PLAINTEXT); 351 } else { 352 throw new IllegalArgumentException("Plaintext negotiation not currently supported"); 353 } 354 return this; 355 } 356 357 /** Sets the negotiation type for the HTTP/2 connection to plaintext. */ 358 @Override usePlaintext()359 public final OkHttpChannelBuilder usePlaintext() { 360 negotiationType = NegotiationType.PLAINTEXT; 361 return this; 362 } 363 364 /** 365 * Sets the negotiation type for the HTTP/2 connection to TLS (this is the default). 366 * 367 * <p>With TLS enabled, a default {@link SSLSocketFactory} is created using the best {@link 368 * java.security.Provider} available and is NOT based on {@link SSLSocketFactory#getDefault}. To 369 * more precisely control the TLS configuration call {@link #sslSocketFactory} to override the 370 * socket factory used. 371 */ 372 @Override useTransportSecurity()373 public final OkHttpChannelBuilder useTransportSecurity() { 374 negotiationType = NegotiationType.TLS; 375 return this; 376 } 377 378 /** 379 * Provides a custom scheduled executor service. 380 * 381 * <p>It's an optional parameter. If the user has not provided a scheduled executor service when 382 * the channel is built, the builder will use a static cached thread pool. 383 * 384 * @return this 385 * 386 * @since 1.11.0 387 */ scheduledExecutorService( ScheduledExecutorService scheduledExecutorService)388 public final OkHttpChannelBuilder scheduledExecutorService( 389 ScheduledExecutorService scheduledExecutorService) { 390 this.scheduledExecutorService = 391 checkNotNull(scheduledExecutorService, "scheduledExecutorService"); 392 return this; 393 } 394 395 @Override 396 @Internal buildTransportFactory()397 protected final ClientTransportFactory buildTransportFactory() { 398 boolean enableKeepAlive = keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED; 399 return new OkHttpTransportFactory(transportExecutor, scheduledExecutorService, 400 createSocketFactory(), hostnameVerifier, connectionSpec, maxInboundMessageSize(), 401 enableKeepAlive, keepAliveTimeNanos, keepAliveTimeoutNanos, keepAliveWithoutCalls, 402 transportTracerFactory); 403 } 404 405 @Override getNameResolverParams()406 protected Attributes getNameResolverParams() { 407 int defaultPort; 408 switch (negotiationType) { 409 case PLAINTEXT: 410 defaultPort = GrpcUtil.DEFAULT_PORT_PLAINTEXT; 411 break; 412 case TLS: 413 defaultPort = GrpcUtil.DEFAULT_PORT_SSL; 414 break; 415 default: 416 throw new AssertionError(negotiationType + " not handled"); 417 } 418 return Attributes.newBuilder() 419 .set(NameResolver.Factory.PARAMS_DEFAULT_PORT, defaultPort).build(); 420 } 421 422 @VisibleForTesting 423 @Nullable createSocketFactory()424 SSLSocketFactory createSocketFactory() { 425 switch (negotiationType) { 426 case TLS: 427 try { 428 if (sslSocketFactory == null) { 429 SSLContext sslContext; 430 if (GrpcUtil.IS_RESTRICTED_APPENGINE) { 431 // The following auth code circumvents the following AccessControlException: 432 // access denied ("java.util.PropertyPermission" "javax.net.ssl.keyStore" "read") 433 // Conscrypt will attempt to load the default KeyStore if a trust manager is not 434 // provided, which is forbidden on AppEngine 435 sslContext = SSLContext.getInstance("TLS", Platform.get().getProvider()); 436 TrustManagerFactory trustManagerFactory = 437 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 438 trustManagerFactory.init((KeyStore) null); 439 sslContext.init( 440 null, 441 trustManagerFactory.getTrustManagers(), 442 // Use an algorithm that doesn't need /dev/urandom 443 SecureRandom.getInstance("SHA1PRNG", Platform.get().getProvider())); 444 445 } else { 446 sslContext = SSLContext.getInstance("Default", Platform.get().getProvider()); 447 } 448 sslSocketFactory = sslContext.getSocketFactory(); 449 } 450 return sslSocketFactory; 451 } catch (GeneralSecurityException gse) { 452 throw new RuntimeException("TLS Provider failure", gse); 453 } 454 case PLAINTEXT: 455 return null; 456 default: 457 throw new RuntimeException("Unknown negotiation type: " + negotiationType); 458 } 459 } 460 461 /** 462 * Creates OkHttp transports. Exposed for internal use, as it should be private. 463 */ 464 @Internal 465 static final class OkHttpTransportFactory implements ClientTransportFactory { 466 private final Executor executor; 467 private final boolean usingSharedExecutor; 468 private final boolean usingSharedScheduler; 469 private final TransportTracer.Factory transportTracerFactory; 470 @Nullable 471 private final SSLSocketFactory socketFactory; 472 @Nullable 473 private final HostnameVerifier hostnameVerifier; 474 private final ConnectionSpec connectionSpec; 475 private final int maxMessageSize; 476 private final boolean enableKeepAlive; 477 private final AtomicBackoff keepAliveTimeNanos; 478 private final long keepAliveTimeoutNanos; 479 private final boolean keepAliveWithoutCalls; 480 private final ScheduledExecutorService timeoutService; 481 private boolean closed; 482 OkHttpTransportFactory(Executor executor, @Nullable ScheduledExecutorService timeoutService, @Nullable SSLSocketFactory socketFactory, @Nullable HostnameVerifier hostnameVerifier, ConnectionSpec connectionSpec, int maxMessageSize, boolean enableKeepAlive, long keepAliveTimeNanos, long keepAliveTimeoutNanos, boolean keepAliveWithoutCalls, TransportTracer.Factory transportTracerFactory)483 private OkHttpTransportFactory(Executor executor, 484 @Nullable ScheduledExecutorService timeoutService, 485 @Nullable SSLSocketFactory socketFactory, 486 @Nullable HostnameVerifier hostnameVerifier, 487 ConnectionSpec connectionSpec, 488 int maxMessageSize, 489 boolean enableKeepAlive, 490 long keepAliveTimeNanos, 491 long keepAliveTimeoutNanos, 492 boolean keepAliveWithoutCalls, 493 TransportTracer.Factory transportTracerFactory) { 494 usingSharedScheduler = timeoutService == null; 495 this.timeoutService = usingSharedScheduler 496 ? SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE) : timeoutService; 497 this.socketFactory = socketFactory; 498 this.hostnameVerifier = hostnameVerifier; 499 this.connectionSpec = connectionSpec; 500 this.maxMessageSize = maxMessageSize; 501 this.enableKeepAlive = enableKeepAlive; 502 this.keepAliveTimeNanos = new AtomicBackoff("keepalive time nanos", keepAliveTimeNanos); 503 this.keepAliveTimeoutNanos = keepAliveTimeoutNanos; 504 this.keepAliveWithoutCalls = keepAliveWithoutCalls; 505 506 usingSharedExecutor = executor == null; 507 this.transportTracerFactory = 508 Preconditions.checkNotNull(transportTracerFactory, "transportTracerFactory"); 509 if (usingSharedExecutor) { 510 // The executor was unspecified, using the shared executor. 511 this.executor = SharedResourceHolder.get(SHARED_EXECUTOR); 512 } else { 513 this.executor = executor; 514 } 515 } 516 517 @Override newClientTransport( SocketAddress addr, ClientTransportOptions options)518 public ConnectionClientTransport newClientTransport( 519 SocketAddress addr, ClientTransportOptions options) { 520 if (closed) { 521 throw new IllegalStateException("The transport factory is closed."); 522 } 523 final AtomicBackoff.State keepAliveTimeNanosState = keepAliveTimeNanos.getState(); 524 Runnable tooManyPingsRunnable = new Runnable() { 525 @Override 526 public void run() { 527 keepAliveTimeNanosState.backoff(); 528 } 529 }; 530 InetSocketAddress inetSocketAddr = (InetSocketAddress) addr; 531 OkHttpClientTransport transport = new OkHttpClientTransport( 532 inetSocketAddr, 533 options.getAuthority(), 534 options.getUserAgent(), 535 executor, 536 socketFactory, 537 hostnameVerifier, 538 connectionSpec, 539 maxMessageSize, 540 options.getProxyParameters(), 541 tooManyPingsRunnable, 542 transportTracerFactory.create()); 543 if (enableKeepAlive) { 544 transport.enableKeepAlive( 545 true, keepAliveTimeNanosState.get(), keepAliveTimeoutNanos, keepAliveWithoutCalls); 546 } 547 return transport; 548 } 549 550 @Override getScheduledExecutorService()551 public ScheduledExecutorService getScheduledExecutorService() { 552 return timeoutService; 553 } 554 555 @Override close()556 public void close() { 557 if (closed) { 558 return; 559 } 560 closed = true; 561 562 if (usingSharedScheduler) { 563 SharedResourceHolder.release(GrpcUtil.TIMER_SERVICE, timeoutService); 564 } 565 566 if (usingSharedExecutor) { 567 SharedResourceHolder.release(SHARED_EXECUTOR, (ExecutorService) executor); 568 } 569 } 570 } 571 } 572