• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #20320

17 Jun 2026 05:11PM UTC coverage: 88.886% (+0.01%) from 88.874%
#20320

push

github

web-flow
api: Add Grpc.newChannelBuilder accepting NameResolverRegistry (#11901)

This introduces a new `Grpc.newChannelBuilder` overload that allows callers to pass an explicit `NameResolverRegistry`. Fixes #11055

36550 of 41120 relevant lines covered (88.89%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.4
/../okhttp/src/main/java/io/grpc/okhttp/OkHttpChannelBuilder.java
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.CertificateUtils.createTrustManager;
21
import static io.grpc.internal.GrpcUtil.DEFAULT_KEEPALIVE_TIMEOUT_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 com.google.errorprone.annotations.CheckReturnValue;
27
import io.grpc.CallCredentials;
28
import io.grpc.ChannelCredentials;
29
import io.grpc.ChannelLogger;
30
import io.grpc.ChoiceChannelCredentials;
31
import io.grpc.CompositeCallCredentials;
32
import io.grpc.CompositeChannelCredentials;
33
import io.grpc.ExperimentalApi;
34
import io.grpc.ForwardingChannelBuilder2;
35
import io.grpc.InsecureChannelCredentials;
36
import io.grpc.Internal;
37
import io.grpc.ManagedChannelBuilder;
38
import io.grpc.NameResolverProvider;
39
import io.grpc.NameResolverRegistry;
40
import io.grpc.TlsChannelCredentials;
41
import io.grpc.internal.AtomicBackoff;
42
import io.grpc.internal.ClientTransportFactory;
43
import io.grpc.internal.ConnectionClientTransport;
44
import io.grpc.internal.FixedObjectPool;
45
import io.grpc.internal.GrpcUtil;
46
import io.grpc.internal.KeepAliveManager;
47
import io.grpc.internal.ManagedChannelImplBuilder;
48
import io.grpc.internal.ManagedChannelImplBuilder.ChannelBuilderDefaultPortProvider;
49
import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder;
50
import io.grpc.internal.ObjectPool;
51
import io.grpc.internal.SharedResourceHolder.Resource;
52
import io.grpc.internal.SharedResourcePool;
53
import io.grpc.internal.TransportTracer;
54
import io.grpc.okhttp.internal.CipherSuite;
55
import io.grpc.okhttp.internal.ConnectionSpec;
56
import io.grpc.okhttp.internal.Platform;
57
import io.grpc.okhttp.internal.TlsVersion;
58
import io.grpc.util.CertificateUtils;
59
import java.io.ByteArrayInputStream;
60
import java.io.IOException;
61
import java.io.InputStream;
62
import java.net.InetSocketAddress;
63
import java.net.SocketAddress;
64
import java.security.GeneralSecurityException;
65
import java.security.KeyStore;
66
import java.security.PrivateKey;
67
import java.security.cert.X509Certificate;
68
import java.util.Collection;
69
import java.util.Collections;
70
import java.util.EnumSet;
71
import java.util.Set;
72
import java.util.concurrent.Executor;
73
import java.util.concurrent.ExecutorService;
74
import java.util.concurrent.Executors;
75
import java.util.concurrent.ScheduledExecutorService;
76
import java.util.concurrent.TimeUnit;
77
import java.util.logging.Level;
78
import java.util.logging.Logger;
79
import javax.annotation.Nullable;
80
import javax.net.SocketFactory;
81
import javax.net.ssl.HostnameVerifier;
82
import javax.net.ssl.KeyManager;
83
import javax.net.ssl.KeyManagerFactory;
84
import javax.net.ssl.SSLContext;
85
import javax.net.ssl.SSLSocketFactory;
86
import javax.net.ssl.TrustManager;
87

88
/** Convenience class for building channels with the OkHttp transport. */
89
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1785")
90
public final class OkHttpChannelBuilder extends ForwardingChannelBuilder2<OkHttpChannelBuilder> {
91
  private static final Logger log = Logger.getLogger(OkHttpChannelBuilder.class.getName());
1✔
92
  public static final int DEFAULT_FLOW_CONTROL_WINDOW = 65535;
93

94
  private final ManagedChannelImplBuilder managedChannelImplBuilder;
95
  private final ChannelCredentials channelCredentials;
96
  private TransportTracer.Factory transportTracerFactory = TransportTracer.getDefaultFactory();
1✔
97

98

99
  /** Identifies the negotiation used for starting up HTTP/2. */
100
  private enum NegotiationType {
1✔
101
    /** Uses TLS ALPN/NPN negotiation, assumes an SSL connection. */
102
    TLS,
1✔
103

104
    /**
105
     * Just assume the connection is plaintext (non-SSL) and the remote endpoint supports HTTP/2
106
     * directly without an upgrade.
107
     */
108
    PLAINTEXT
1✔
109
  }
110

111
  // @VisibleForTesting
112
  static final ConnectionSpec INTERNAL_DEFAULT_CONNECTION_SPEC =
1✔
113
      new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
114
          .cipherSuites(
1✔
115
              // The following items should be sync with Netty's Http2SecurityUtil.CIPHERS.
116
              CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
117
              CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
118
              CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
119
              CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
120
              CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
121
              CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
122
              CipherSuite.TLS_AES_128_GCM_SHA256,
123
              CipherSuite.TLS_AES_256_GCM_SHA384,
124
              CipherSuite.TLS_CHACHA20_POLY1305_SHA256)
125
          .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2)
1✔
126
          .supportsTlsExtensions(true)
1✔
127
          .build();
1✔
128

129
  // @VisibleForTesting
130
  static final ConnectionSpec INTERNAL_LEGACY_CONNECTION_SPEC =
1✔
131
      new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
132
          .cipherSuites(
1✔
133
              // The following items should be sync with Netty's Http2SecurityUtil.CIPHERS.
134
              CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
135
              CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
136
              CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
137
              CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
138
              CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
139
              CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
140
          .tlsVersions(TlsVersion.TLS_1_2)
1✔
141
          .supportsTlsExtensions(true)
1✔
142
          .build();
1✔
143

144
  private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L);
1✔
145
  private static final Resource<Executor> SHARED_EXECUTOR =
1✔
146
      new Resource<Executor>() {
1✔
147
        @Override
148
        public Executor create() {
149
          return Executors.newCachedThreadPool(GrpcUtil.getThreadFactory("grpc-okhttp-%d", true));
1✔
150
        }
151

152
        @Override
153
        public void close(Executor executor) {
154
          ((ExecutorService) executor).shutdown();
1✔
155
        }
1✔
156
      };
157
  static final ObjectPool<Executor> DEFAULT_TRANSPORT_EXECUTOR_POOL =
1✔
158
      SharedResourcePool.forResource(SHARED_EXECUTOR);
1✔
159

160
  /** Creates a new builder for the given server host and port. */
161
  public static OkHttpChannelBuilder forAddress(String host, int port) {
162
    return new OkHttpChannelBuilder(host, port);
1✔
163
  }
164

165
  /** Creates a new builder with the given host and port. */
166
  public static OkHttpChannelBuilder forAddress(String host, int port, ChannelCredentials creds) {
167
    return forTarget(GrpcUtil.authorityFromHostAndPort(host, port), creds);
1✔
168
  }
169

170
  /**
171
   * Creates a new builder for the given target that will be resolved by
172
   * {@link io.grpc.NameResolver}.
173
   */
174
  public static OkHttpChannelBuilder forTarget(String target) {
175
    return new OkHttpChannelBuilder(target);
1✔
176
  }
177

178
  /**
179
   * Creates a new builder for the given target that will be resolved by
180
   * {@link io.grpc.NameResolver}.
181
   */
182
  public static OkHttpChannelBuilder forTarget(String target, ChannelCredentials creds) {
183
    SslSocketFactoryResult result = sslSocketFactoryFrom(creds);
1✔
184
    if (result.error != null) {
1✔
185
      throw new IllegalArgumentException(result.error);
×
186
    }
187
    return new OkHttpChannelBuilder(target, creds, result.callCredentials, result.factory);
1✔
188
  }
189

190
  private ObjectPool<Executor> transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL;
1✔
191
  private ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool =
1✔
192
      SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE);
1✔
193

194
  private SocketFactory socketFactory;
195
  private SSLSocketFactory sslSocketFactory;
196
  private final boolean freezeSecurityConfiguration;
197
  private HostnameVerifier hostnameVerifier;
198
  private ConnectionSpec connectionSpec = initialConnectionSpec();
1✔
199
  private NegotiationType negotiationType = NegotiationType.TLS;
1✔
200
  private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
1✔
201
  private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
1✔
202
  private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW;
1✔
203
  private boolean keepAliveWithoutCalls;
204
  private int maxInboundMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE;
1✔
205
  private int maxInboundMetadataSize = Integer.MAX_VALUE;
1✔
206

207
  /**
208
   * If true, indicates that the transport may use the GET method for RPCs, and may include the
209
   * request body in the query params.
210
   */
211
  private final boolean useGetForSafeMethods = false;
1✔
212

213
  static ConnectionSpec initialConnectionSpec() {
214
    return (OkHttpProtocolNegotiator.get() instanceof OkHttpProtocolNegotiator.AndroidNegotiator)
1✔
215
        ? INTERNAL_DEFAULT_CONNECTION_SPEC
×
216
        : INTERNAL_LEGACY_CONNECTION_SPEC;
1✔
217
  }
218

219
  private OkHttpChannelBuilder(String host, int port) {
220
    this(GrpcUtil.authorityFromHostAndPort(host, port));
1✔
221
  }
1✔
222

223
  private OkHttpChannelBuilder(String target) {
1✔
224
    managedChannelImplBuilder = new ManagedChannelImplBuilder(target,
1✔
225
        new OkHttpChannelTransportFactoryBuilder(),
226
        new OkHttpChannelDefaultPortProvider());
227
    this.freezeSecurityConfiguration = false;
1✔
228
    this.channelCredentials = null;
1✔
229
  }
1✔
230

231
  OkHttpChannelBuilder(
232
      String target, ChannelCredentials channelCreds, CallCredentials callCreds,
233
      SSLSocketFactory factory) {
234
    this(target, channelCreds, callCreds, factory, null, null);
1✔
235
  }
1✔
236

237
  OkHttpChannelBuilder(
238
      String target, ChannelCredentials channelCreds, CallCredentials callCreds,
239
      SSLSocketFactory factory,
240
      NameResolverRegistry nameResolverRegistry,
241
      NameResolverProvider nameResolverProvider) {
1✔
242
    managedChannelImplBuilder = new ManagedChannelImplBuilder(
1✔
243
        target, channelCreds, callCreds,
244
        new OkHttpChannelTransportFactoryBuilder(),
245
        new OkHttpChannelDefaultPortProvider(),
246
        nameResolverRegistry,
247
        nameResolverProvider);
248
    this.sslSocketFactory = factory;
1✔
249
    this.negotiationType = factory == null ? NegotiationType.PLAINTEXT : NegotiationType.TLS;
1✔
250
    this.freezeSecurityConfiguration = true;
1✔
251
    this.channelCredentials = channelCreds;
1✔
252
  }
1✔
253

254
  private final class OkHttpChannelTransportFactoryBuilder
1✔
255
      implements ClientTransportFactoryBuilder {
256
    @Override
257
    public ClientTransportFactory buildClientTransportFactory() {
258
      return buildTransportFactory();
1✔
259
    }
260
  }
261

262
  private final class OkHttpChannelDefaultPortProvider
1✔
263
      implements ChannelBuilderDefaultPortProvider {
264
    @Override
265
    public int getDefaultPort() {
266
      return OkHttpChannelBuilder.this.getDefaultPort();
1✔
267
    }
268
  }
269

270
  @Internal
271
  @Override
272
  protected ManagedChannelBuilder<?> delegate() {
273
    return managedChannelImplBuilder;
1✔
274
  }
275

276
  @VisibleForTesting
277
  OkHttpChannelBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) {
278
    this.transportTracerFactory = transportTracerFactory;
1✔
279
    return this;
1✔
280
  }
281

282
  /**
283
   * Override the default executor necessary for internal transport use.
284
   *
285
   * <p>The channel does not take ownership of the given executor. It is the caller' responsibility
286
   * to shutdown the executor when appropriate.
287
   */
288
  public OkHttpChannelBuilder transportExecutor(@Nullable Executor transportExecutor) {
289
    if (transportExecutor == null) {
1✔
290
      this.transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL;
×
291
    } else {
292
      this.transportExecutorPool = new FixedObjectPool<>(transportExecutor);
1✔
293
    }
294
    return this;
1✔
295
  }
296

297
  /**
298
   * Override the default {@link SocketFactory} used to create sockets. If the socket factory is not
299
   * set or set to null, a default one will be used.
300
   *
301
   * @since 1.20.0
302
   */
303
  public OkHttpChannelBuilder socketFactory(@Nullable SocketFactory socketFactory) {
304
    this.socketFactory = socketFactory;
1✔
305
    return this;
1✔
306
  }
307

308
  /**
309
   * Sets the negotiation type for the HTTP/2 connection.
310
   *
311
   * <p>If TLS is enabled a default {@link SSLSocketFactory} is created using the best
312
   * {@link java.security.Provider} available and is NOT based on
313
   * {@link SSLSocketFactory#getDefault}. To more precisely control the TLS configuration call
314
   * {@link #sslSocketFactory} to override the socket factory used.
315
   *
316
   * <p>Default: <code>TLS</code>
317
   *
318
   * @deprecated use {@link #usePlaintext()} or {@link #useTransportSecurity()} instead.
319
   */
320
  @Deprecated
321
  public OkHttpChannelBuilder negotiationType(io.grpc.okhttp.NegotiationType type) {
322
    Preconditions.checkState(!freezeSecurityConfiguration,
×
323
        "Cannot change security when using ChannelCredentials");
324
    Preconditions.checkNotNull(type, "type");
×
325
    switch (type) {
×
326
      case TLS:
327
        negotiationType = NegotiationType.TLS;
×
328
        break;
×
329
      case PLAINTEXT:
330
        negotiationType = NegotiationType.PLAINTEXT;
×
331
        break;
×
332
      default:
333
        throw new AssertionError("Unknown negotiation type: " + type);
×
334
    }
335
    return this;
×
336
  }
337

338
  /**
339
   * {@inheritDoc}
340
   *
341
   * @since 1.3.0
342
   */
343
  @Override
344
  public OkHttpChannelBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
345
    Preconditions.checkArgument(keepAliveTime > 0L, "keepalive time must be positive");
×
346
    keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime);
×
347
    keepAliveTimeNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeNanos);
×
348
    if (keepAliveTimeNanos >= AS_LARGE_AS_INFINITE) {
×
349
      // Bump keepalive time to infinite. This disables keepalive.
350
      keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
×
351
    }
352
    return this;
×
353
  }
354

355
  /**
356
   * {@inheritDoc}
357
   *
358
   * @since 1.3.0
359
   */
360
  @Override
361
  public OkHttpChannelBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) {
362
    Preconditions.checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive");
×
363
    keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout);
×
364
    keepAliveTimeoutNanos = KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutNanos);
×
365
    return this;
×
366
  }
367

368
  /**
369
   * Sets the flow control window in bytes. If not called, the default value
370
   * is {@link #DEFAULT_FLOW_CONTROL_WINDOW}).
371
   */
372
  public OkHttpChannelBuilder flowControlWindow(int flowControlWindow) {
373
    Preconditions.checkState(flowControlWindow > 0, "flowControlWindow must be positive");
1✔
374
    this.flowControlWindow = flowControlWindow;
1✔
375
    return this;
1✔
376
  }
377

378
  /**
379
   * {@inheritDoc}
380
   *
381
   * @since 1.3.0
382
   * @see #keepAliveTime(long, TimeUnit)
383
   */
384
  @Override
385
  public OkHttpChannelBuilder keepAliveWithoutCalls(boolean enable) {
386
    keepAliveWithoutCalls = enable;
×
387
    return this;
×
388
  }
389

390
  /**
391
   * Override the default {@link SSLSocketFactory} and enable TLS negotiation.
392
   */
393
  public OkHttpChannelBuilder sslSocketFactory(SSLSocketFactory factory) {
394
    Preconditions.checkState(!freezeSecurityConfiguration,
1✔
395
        "Cannot change security when using ChannelCredentials");
396
    this.sslSocketFactory = factory;
1✔
397
    negotiationType = NegotiationType.TLS;
1✔
398
    return this;
1✔
399
  }
400

401
  /**
402
   * Set the hostname verifier to use when using TLS negotiation. The hostnameVerifier is only used
403
   * if using TLS negotiation. If the hostname verifier is not set, a default hostname verifier is
404
   * used.
405
   *
406
   * <p>Be careful when setting a custom hostname verifier! By setting a non-null value, you are
407
   * replacing all default verification behavior. If the hostname verifier you supply does not
408
   * effectively supply the same checks, you may be removing the security assurances that TLS aims
409
   * to provide.</p>
410
   *
411
   * <p>This method should not be used to avoid hostname verification, even during testing, since
412
   * {@link #overrideAuthority} is a safer alternative as it does not disable any security checks.
413
   * </p>
414
   *
415
   * @see io.grpc.okhttp.internal.OkHostnameVerifier
416
   *
417
   * @since 1.6.0
418
   * @return this
419
   *
420
   */
421
  public OkHttpChannelBuilder hostnameVerifier(@Nullable HostnameVerifier hostnameVerifier) {
422
    Preconditions.checkState(!freezeSecurityConfiguration,
1✔
423
        "Cannot change security when using ChannelCredentials");
424
    this.hostnameVerifier = hostnameVerifier;
1✔
425
    return this;
1✔
426
  }
427

428
  /**
429
   * For secure connection, provides a ConnectionSpec to specify Cipher suite and
430
   * TLS versions.
431
   *
432
   * <p>By default a modern, HTTP/2-compatible spec will be used.
433
   *
434
   * <p>This method is only used when building a secure connection. For plaintext
435
   * connection, use {@link #usePlaintext()} instead.
436
   *
437
   * @throws IllegalArgumentException
438
   *         If {@code connectionSpec} is not with TLS
439
   */
440
  public OkHttpChannelBuilder connectionSpec(
441
      com.squareup.okhttp.ConnectionSpec connectionSpec) {
442
    Preconditions.checkState(!freezeSecurityConfiguration,
1✔
443
        "Cannot change security when using ChannelCredentials");
444
    Preconditions.checkArgument(connectionSpec.isTls(), "plaintext ConnectionSpec is not accepted");
1✔
445
    this.connectionSpec = Utils.convertSpec(connectionSpec);
1✔
446
    return this;
1✔
447
  }
448

449
  /**
450
   * Sets the connection specification used for secure connections.
451
   *
452
   * <p>By default a modern, HTTP/2-compatible spec will be used.
453
   *
454
   * <p>This method is only used when building a secure connection. For plaintext
455
   * connection, use {@link #usePlaintext()} instead.
456
   *
457
   * @param tlsVersions List of tls versions.
458
   * @param cipherSuites List of cipher suites.
459
   *
460
   * @since  1.43.0
461
   */
462
  public OkHttpChannelBuilder tlsConnectionSpec(
463
          String[] tlsVersions, String[] cipherSuites) {
464
    Preconditions.checkState(!freezeSecurityConfiguration,
×
465
            "Cannot change security when using ChannelCredentials");
466
    Preconditions.checkNotNull(tlsVersions, "tls versions must not null");
×
467
    Preconditions.checkNotNull(cipherSuites, "ciphers must not null");
×
468

469
    this.connectionSpec = new ConnectionSpec.Builder(true)
×
470
            .supportsTlsExtensions(true)
×
471
            .tlsVersions(tlsVersions)
×
472
            .cipherSuites(cipherSuites)
×
473
            .build();
×
474
    return this;
×
475
  }
476

477
  /** Sets the negotiation type for the HTTP/2 connection to plaintext. */
478
  @Override
479
  public OkHttpChannelBuilder usePlaintext() {
480
    Preconditions.checkState(!freezeSecurityConfiguration,
1✔
481
        "Cannot change security when using ChannelCredentials");
482
    negotiationType = NegotiationType.PLAINTEXT;
1✔
483
    return this;
1✔
484
  }
485

486
  /**
487
   * Sets the negotiation type for the HTTP/2 connection to TLS (this is the default).
488
   *
489
   * <p>With TLS enabled, a default {@link SSLSocketFactory} is created using the best {@link
490
   * java.security.Provider} available and is NOT based on {@link SSLSocketFactory#getDefault}. To
491
   * more precisely control the TLS configuration call {@link #sslSocketFactory(SSLSocketFactory)}
492
   * to override the socket factory used.
493
   */
494
  @Override
495
  public OkHttpChannelBuilder useTransportSecurity() {
496
    Preconditions.checkState(!freezeSecurityConfiguration,
×
497
        "Cannot change security when using ChannelCredentials");
498
    negotiationType = NegotiationType.TLS;
×
499
    return this;
×
500
  }
501

502
  /**
503
   * Provides a custom scheduled executor service.
504
   *
505
   * <p>It's an optional parameter. If the user has not provided a scheduled executor service when
506
   * the channel is built, the builder will use a static cached thread pool.
507
   *
508
   * @return this
509
   *
510
   * @since 1.11.0
511
   */
512
  public OkHttpChannelBuilder scheduledExecutorService(
513
      ScheduledExecutorService scheduledExecutorService) {
514
    this.scheduledExecutorServicePool =
1✔
515
        new FixedObjectPool<>(checkNotNull(scheduledExecutorService, "scheduledExecutorService"));
1✔
516
    return this;
1✔
517
  }
518

519
  /**
520
   * Sets the maximum size of metadata allowed to be received. {@code Integer.MAX_VALUE} disables
521
   * the enforcement. Defaults to no limit ({@code Integer.MAX_VALUE}).
522
   *
523
   * <p>The implementation does not currently limit memory usage; this value is checked only after
524
   * the metadata is decoded from the wire. It does prevent large metadata from being passed to the
525
   * application.
526
   *
527
   * @param bytes the maximum size of received metadata
528
   * @return this
529
   * @throws IllegalArgumentException if bytes is non-positive
530
   * @since 1.17.0
531
   */
532
  @Override
533
  public OkHttpChannelBuilder maxInboundMetadataSize(int bytes) {
534
    Preconditions.checkArgument(bytes > 0, "maxInboundMetadataSize must be > 0");
1✔
535
    this.maxInboundMetadataSize = bytes;
1✔
536
    return this;
1✔
537
  }
538

539
  /**
540
   * Sets the maximum message size allowed for a single gRPC frame. If an inbound messages
541
   * larger than this limit is received it will not be processed and the RPC will fail with
542
   * RESOURCE_EXHAUSTED.
543
   */
544
  @Override
545
  public OkHttpChannelBuilder maxInboundMessageSize(int max) {
546
    Preconditions.checkArgument(max >= 0, "negative max");
1✔
547
    maxInboundMessageSize = max;
1✔
548
    return this;
1✔
549
  }
550

551
  OkHttpTransportFactory buildTransportFactory() {
552
    boolean enableKeepAlive = keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED;
1✔
553
    return new OkHttpTransportFactory(
1✔
554
        transportExecutorPool,
555
        scheduledExecutorServicePool,
556
        socketFactory,
557
        createSslSocketFactory(),
1✔
558
        hostnameVerifier,
559
        connectionSpec,
560
        maxInboundMessageSize,
561
        enableKeepAlive,
562
        keepAliveTimeNanos,
563
        keepAliveTimeoutNanos,
564
        flowControlWindow,
565
        keepAliveWithoutCalls,
566
        maxInboundMetadataSize,
567
        transportTracerFactory,
568
        useGetForSafeMethods,
569
        channelCredentials);
570
  }
571

572
  OkHttpChannelBuilder disableCheckAuthority() {
573
    this.managedChannelImplBuilder.disableCheckAuthority();
1✔
574
    return this;
1✔
575
  }
576

577
  OkHttpChannelBuilder enableCheckAuthority() {
578
    this.managedChannelImplBuilder.enableCheckAuthority();
1✔
579
    return this;
1✔
580
  }
581

582
  int getDefaultPort() {
583
    switch (negotiationType) {
1✔
584
      case PLAINTEXT:
585
        return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
1✔
586
      case TLS:
587
        return GrpcUtil.DEFAULT_PORT_SSL;
1✔
588
      default:
589
        throw new AssertionError(negotiationType + " not handled");
×
590
    }
591
  }
592

593
  void setStatsEnabled(boolean value) {
594
    this.managedChannelImplBuilder.setStatsEnabled(value);
1✔
595
  }
1✔
596

597
  @VisibleForTesting
598
  @Nullable
599
  SSLSocketFactory createSslSocketFactory() {
600
    switch (negotiationType) {
1✔
601
      case TLS:
602
        try {
603
          if (sslSocketFactory == null) {
1✔
604
            SSLContext sslContext = SSLContext.getInstance("Default", Platform.get().getProvider());
1✔
605
            sslSocketFactory = sslContext.getSocketFactory();
1✔
606
          }
607
          return sslSocketFactory;
1✔
608
        } catch (GeneralSecurityException gse) {
×
609
          throw new RuntimeException("TLS Provider failure", gse);
×
610
        }
611
      case PLAINTEXT:
612
        return null;
1✔
613
      default:
614
        throw new RuntimeException("Unknown negotiation type: " + negotiationType);
×
615
    }
616
  }
617

618

619

620
  private static final EnumSet<TlsChannelCredentials.Feature> understoodTlsFeatures =
1✔
621
      EnumSet.of(
1✔
622
          TlsChannelCredentials.Feature.MTLS, TlsChannelCredentials.Feature.CUSTOM_MANAGERS);
623

624
  static SslSocketFactoryResult sslSocketFactoryFrom(ChannelCredentials creds) {
625
    if (creds instanceof TlsChannelCredentials) {
1✔
626
      TlsChannelCredentials tlsCreds = (TlsChannelCredentials) creds;
1✔
627
      Set<TlsChannelCredentials.Feature> incomprehensible =
1✔
628
          tlsCreds.incomprehensible(understoodTlsFeatures);
1✔
629
      if (!incomprehensible.isEmpty()) {
1✔
630
        return SslSocketFactoryResult.error(
1✔
631
            "TLS features not understood: " + incomprehensible);
632
      }
633
      KeyManager[] km = null;
1✔
634
      if (tlsCreds.getKeyManagers() != null) {
1✔
635
        km = tlsCreds.getKeyManagers().toArray(new KeyManager[0]);
1✔
636
      } else if (tlsCreds.getPrivateKey() != null) {
1✔
637
        if (tlsCreds.getPrivateKeyPassword() != null) {
1✔
638
          return SslSocketFactoryResult.error("byte[]-based private key with password unsupported. "
1✔
639
              + "Use unencrypted file or KeyManager");
640
        }
641
        try {
642
          km = createKeyManager(tlsCreds.getCertificateChain(), tlsCreds.getPrivateKey());
1✔
643
        } catch (GeneralSecurityException gse) {
×
644
          log.log(Level.FINE, "Exception loading private key from credential", gse);
×
645
          return SslSocketFactoryResult.error("Unable to load private key: " + gse.getMessage());
×
646
        }
1✔
647
      } // else don't have a client cert
648
      TrustManager[] tm = null;
1✔
649
      if (tlsCreds.getTrustManagers() != null) {
1✔
650
        tm = tlsCreds.getTrustManagers().toArray(new TrustManager[0]);
1✔
651
      } else if (tlsCreds.getRootCertificates() != null) {
1✔
652
        try {
653
          tm = createTrustManager(tlsCreds.getRootCertificates());
1✔
654
        } catch (GeneralSecurityException gse) {
×
655
          log.log(Level.FINE, "Exception loading root certificates from credential", gse);
×
656
          return SslSocketFactoryResult.error(
×
657
              "Unable to load root certificates: " + gse.getMessage());
×
658
        }
1✔
659
      } // else use system default
660
      SSLContext sslContext;
661
      try {
662
        sslContext = SSLContext.getInstance("TLS", Platform.get().getProvider());
1✔
663
        sslContext.init(km, tm, null);
1✔
664
      } catch (GeneralSecurityException gse) {
×
665
        throw new RuntimeException("TLS Provider failure", gse);
×
666
      }
1✔
667
      return SslSocketFactoryResult.factory(sslContext.getSocketFactory());
1✔
668

669
    } else if (creds instanceof InsecureChannelCredentials) {
1✔
670
      return SslSocketFactoryResult.plaintext();
1✔
671

672
    } else if (creds instanceof CompositeChannelCredentials) {
1✔
673
      CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds;
1✔
674
      return sslSocketFactoryFrom(compCreds.getChannelCredentials())
1✔
675
          .withCallCredentials(compCreds.getCallCredentials());
1✔
676

677
    } else if (creds instanceof SslSocketFactoryChannelCredentials.ChannelCredentials) {
1✔
678
      SslSocketFactoryChannelCredentials.ChannelCredentials factoryCreds =
1✔
679
          (SslSocketFactoryChannelCredentials.ChannelCredentials) creds;
680
      return SslSocketFactoryResult.factory(factoryCreds.getFactory());
1✔
681

682
    } else if (creds instanceof ChoiceChannelCredentials) {
1✔
683
      ChoiceChannelCredentials choiceCreds = (ChoiceChannelCredentials) creds;
1✔
684
      StringBuilder error = new StringBuilder();
1✔
685
      for (ChannelCredentials innerCreds : choiceCreds.getCredentialsList()) {
1✔
686
        SslSocketFactoryResult result = sslSocketFactoryFrom(innerCreds);
1✔
687
        if (result.error == null) {
1✔
688
          return result;
1✔
689
        }
690
        error.append(", ");
1✔
691
        error.append(result.error);
1✔
692
      }
1✔
693
      return SslSocketFactoryResult.error(error.substring(2));
1✔
694

695
    } else {
696
      return SslSocketFactoryResult.error(
1✔
697
          "Unsupported credential type: " + creds.getClass().getName());
1✔
698
    }
699
  }
700

701
  static KeyManager[] createKeyManager(byte[] certChain, byte[] privateKey)
702
      throws GeneralSecurityException {
703
    InputStream certChainStream = new ByteArrayInputStream(certChain);
1✔
704
    InputStream privateKeyStream = new ByteArrayInputStream(privateKey);
1✔
705
    try {
706
      return createKeyManager(certChainStream, privateKeyStream);
1✔
707
    } finally {
708
      GrpcUtil.closeQuietly(certChainStream);
1✔
709
      GrpcUtil.closeQuietly(privateKeyStream);
1✔
710
    }
711
  }
712

713
  static KeyManager[] createKeyManager(InputStream certChain, InputStream privateKey)
714
      throws GeneralSecurityException {
715
    X509Certificate[] chain = CertificateUtils.getX509Certificates(certChain);
1✔
716
    PrivateKey key;
717
    try {
718
      key = CertificateUtils.getPrivateKey(privateKey);
1✔
719
    } catch (IOException uee) {
×
720
      throw new GeneralSecurityException("Unable to decode private key", uee);
×
721
    }
1✔
722
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
1✔
723
    try {
724
      ks.load(null, null);
1✔
725
    } catch (IOException ex) {
×
726
      // Shouldn't really happen, as we're not loading any data.
727
      throw new GeneralSecurityException(ex);
×
728
    }
1✔
729
    ks.setKeyEntry("key", key, new char[0], chain);
1✔
730

731
    KeyManagerFactory keyManagerFactory =
732
        KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
1✔
733
    keyManagerFactory.init(ks, new char[0]);
1✔
734
    return keyManagerFactory.getKeyManagers();
1✔
735
  }
736

737
  static TrustManager[] createTrustManager(byte[] rootCerts) throws GeneralSecurityException {
738
    InputStream rootCertsStream = new ByteArrayInputStream(rootCerts);
1✔
739
    try {
740
      return io.grpc.internal.CertificateUtils.createTrustManager(rootCertsStream);
1✔
741
    } finally {
742
      GrpcUtil.closeQuietly(rootCertsStream);
1✔
743
    }
744
  }
745

746
  static Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
747
    return Collections.singleton(InetSocketAddress.class);
1✔
748
  }
749

750
  static final class SslSocketFactoryResult {
751
    /** {@code null} implies plaintext if {@code error == null}. */
752
    public final SSLSocketFactory factory;
753
    public final CallCredentials callCredentials;
754
    public final String error;
755

756
    private SslSocketFactoryResult(SSLSocketFactory factory, CallCredentials creds, String error) {
1✔
757
      this.factory = factory;
1✔
758
      this.callCredentials = creds;
1✔
759
      this.error = error;
1✔
760
    }
1✔
761

762
    public static SslSocketFactoryResult error(String error) {
763
      return new SslSocketFactoryResult(
1✔
764
          null, null, Preconditions.checkNotNull(error, "error"));
1✔
765
    }
766

767
    public static SslSocketFactoryResult plaintext() {
768
      return new SslSocketFactoryResult(null, null, null);
1✔
769
    }
770

771
    public static SslSocketFactoryResult factory(
772
        SSLSocketFactory factory) {
773
      return new SslSocketFactoryResult(
1✔
774
          Preconditions.checkNotNull(factory, "factory"), null, null);
1✔
775
    }
776

777
    public SslSocketFactoryResult withCallCredentials(CallCredentials callCreds) {
778
      Preconditions.checkNotNull(callCreds, "callCreds");
1✔
779
      if (error != null) {
1✔
780
        return this;
×
781
      }
782
      if (this.callCredentials != null) {
1✔
783
        callCreds = new CompositeCallCredentials(this.callCredentials, callCreds);
×
784
      }
785
      return new SslSocketFactoryResult(factory, callCreds, null);
1✔
786
    }
787
  }
788

789

790
  /**
791
   * Creates OkHttp transports. Exposed for internal use, as it should be private.
792
   */
793
  @Internal
794
  static final class OkHttpTransportFactory implements ClientTransportFactory {
795
    private final ObjectPool<Executor> executorPool;
796
    final Executor executor;
797
    private final ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool;
798
    final ScheduledExecutorService scheduledExecutorService;
799
    final TransportTracer.Factory transportTracerFactory;
800
    final SocketFactory socketFactory;
801
    @Nullable final SSLSocketFactory sslSocketFactory;
802
    @Nullable
803
    final HostnameVerifier hostnameVerifier;
804
    final ConnectionSpec connectionSpec;
805
    final int maxMessageSize;
806
    private final boolean enableKeepAlive;
807
    private final long keepAliveTimeNanos;
808
    private final AtomicBackoff keepAliveBackoff;
809
    private final long keepAliveTimeoutNanos;
810
    final int flowControlWindow;
811
    private final boolean keepAliveWithoutCalls;
812
    final int maxInboundMetadataSize;
813
    final boolean useGetForSafeMethods;
814
    private final ChannelCredentials channelCredentials;
815
    private boolean closed;
816

817
    private OkHttpTransportFactory(
818
        ObjectPool<Executor> executorPool,
819
        ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool,
820
        @Nullable SocketFactory socketFactory,
821
        @Nullable SSLSocketFactory sslSocketFactory,
822
        @Nullable HostnameVerifier hostnameVerifier,
823
        ConnectionSpec connectionSpec,
824
        int maxMessageSize,
825
        boolean enableKeepAlive,
826
        long keepAliveTimeNanos,
827
        long keepAliveTimeoutNanos,
828
        int flowControlWindow,
829
        boolean keepAliveWithoutCalls,
830
        int maxInboundMetadataSize,
831
        TransportTracer.Factory transportTracerFactory,
832
        boolean useGetForSafeMethods,
833
        ChannelCredentials channelCredentials) {
1✔
834
      this.executorPool = executorPool;
1✔
835
      this.executor = executorPool.getObject();
1✔
836
      this.scheduledExecutorServicePool = scheduledExecutorServicePool;
1✔
837
      this.scheduledExecutorService = scheduledExecutorServicePool.getObject();
1✔
838
      this.socketFactory = socketFactory;
1✔
839
      this.sslSocketFactory = sslSocketFactory;
1✔
840
      this.hostnameVerifier = hostnameVerifier;
1✔
841
      this.connectionSpec = connectionSpec;
1✔
842
      this.maxMessageSize = maxMessageSize;
1✔
843
      this.enableKeepAlive = enableKeepAlive;
1✔
844
      this.keepAliveTimeNanos = keepAliveTimeNanos;
1✔
845
      this.keepAliveBackoff = new AtomicBackoff("keepalive time nanos", keepAliveTimeNanos);
1✔
846
      this.keepAliveTimeoutNanos = keepAliveTimeoutNanos;
1✔
847
      this.flowControlWindow = flowControlWindow;
1✔
848
      this.keepAliveWithoutCalls = keepAliveWithoutCalls;
1✔
849
      this.maxInboundMetadataSize = maxInboundMetadataSize;
1✔
850
      this.useGetForSafeMethods = useGetForSafeMethods;
1✔
851
      this.channelCredentials = channelCredentials;
1✔
852

853
      this.transportTracerFactory =
1✔
854
          Preconditions.checkNotNull(transportTracerFactory, "transportTracerFactory");
1✔
855
    }
1✔
856

857
    @Override
858
    public ConnectionClientTransport newClientTransport(
859
        SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) {
860
      if (closed) {
1✔
861
        throw new IllegalStateException("The transport factory is closed.");
1✔
862
      }
863
      final AtomicBackoff.State keepAliveTimeNanosState = keepAliveBackoff.getState();
1✔
864
      Runnable tooManyPingsRunnable = new Runnable() {
1✔
865
        @Override
866
        public void run() {
867
          keepAliveTimeNanosState.backoff();
×
868
        }
×
869
      };
870
      InetSocketAddress inetSocketAddr = (InetSocketAddress) addr;
1✔
871
      // TODO(carl-mastrangelo): Pass channelLogger in.
872
      OkHttpClientTransport transport = new OkHttpClientTransport(
1✔
873
          this,
874
          inetSocketAddr,
875
          options.getAuthority(),
1✔
876
          options.getUserAgent(),
1✔
877
          options.getEagAttributes(),
1✔
878
          options.getHttpConnectProxiedSocketAddress(),
1✔
879
          tooManyPingsRunnable,
880
          channelCredentials);
881
      if (enableKeepAlive) {
1✔
882
        transport.enableKeepAlive(
×
883
            true, keepAliveTimeNanosState.get(), keepAliveTimeoutNanos, keepAliveWithoutCalls);
×
884
      }
885
      return transport;
1✔
886
    }
887

888
    @Override
889
    public ScheduledExecutorService getScheduledExecutorService() {
890
      return scheduledExecutorService;
1✔
891
    }
892

893
    @Nullable
894
    @CheckReturnValue
895
    @Override
896
    public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) {
897
      SslSocketFactoryResult result = sslSocketFactoryFrom(channelCreds);
1✔
898
      if (result.error != null) {
1✔
899
        return null;
1✔
900
      }
901
      ClientTransportFactory factory = new OkHttpTransportFactory(
1✔
902
          executorPool,
903
          scheduledExecutorServicePool,
904
          socketFactory,
905
          result.factory,
906
          hostnameVerifier,
907
          connectionSpec,
908
          maxMessageSize,
909
          enableKeepAlive,
910
          keepAliveTimeNanos,
911
          keepAliveTimeoutNanos,
912
          flowControlWindow,
913
          keepAliveWithoutCalls,
914
          maxInboundMetadataSize,
915
          transportTracerFactory,
916
          useGetForSafeMethods,
917
          channelCredentials);
918
      return new SwapChannelCredentialsResult(factory, result.callCredentials);
1✔
919
    }
920

921
    @Override
922
    public void close() {
923
      if (closed) {
1✔
924
        return;
1✔
925
      }
926
      closed = true;
1✔
927

928
      executorPool.returnObject(executor);
1✔
929
      scheduledExecutorServicePool.returnObject(scheduledExecutorService);
1✔
930
    }
1✔
931

932
    @Override
933
    public Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
934
      return OkHttpChannelBuilder.getSupportedSocketAddressTypes();
1✔
935
    }
936
  }
937
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc