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

grpc / grpc-java / #18883

03 Nov 2023 04:57PM UTC coverage: 88.246% (+0.03%) from 88.218%
#18883

push

github

web-flow
core, netty, okhttp: implement new logic for nameResolverFactory API in channelBuilder (#10590)

* core, netty, okhttp: implement new logic for nameResolverFactory API in channelBuilder
fix ManagedChannelImpl to use NameResolverRegistry instead of NameResolverFactory
fix the ManagedChannelImplBuilder and remove nameResolverFactory

* Integrate target parsing and NameResolverProvider searching

Actually creating the name resolver is now delayed to the end of
ManagedChannelImpl.getNameResolver; we don't want to call into the name
resolver to determine if we should use the name resolver.

Added getDefaultScheme() to NameResolverRegistry to avoid needing
NameResolver.Factory.
---------

Co-authored-by: Eric Anderson <ejona@google.com>

30370 of 34415 relevant lines covered (88.25%)

0.88 hits per line

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

79.8
/../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.GrpcUtil.DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
21
import static io.grpc.internal.GrpcUtil.KEEPALIVE_TIME_NANOS_DISABLED;
22

23
import com.google.common.annotations.VisibleForTesting;
24
import com.google.common.base.Preconditions;
25
import io.grpc.CallCredentials;
26
import io.grpc.ChannelCredentials;
27
import io.grpc.ChannelLogger;
28
import io.grpc.ChoiceChannelCredentials;
29
import io.grpc.CompositeCallCredentials;
30
import io.grpc.CompositeChannelCredentials;
31
import io.grpc.ExperimentalApi;
32
import io.grpc.ForwardingChannelBuilder2;
33
import io.grpc.InsecureChannelCredentials;
34
import io.grpc.Internal;
35
import io.grpc.ManagedChannelBuilder;
36
import io.grpc.TlsChannelCredentials;
37
import io.grpc.internal.AtomicBackoff;
38
import io.grpc.internal.ClientTransportFactory;
39
import io.grpc.internal.ConnectionClientTransport;
40
import io.grpc.internal.FixedObjectPool;
41
import io.grpc.internal.GrpcUtil;
42
import io.grpc.internal.KeepAliveManager;
43
import io.grpc.internal.ManagedChannelImplBuilder;
44
import io.grpc.internal.ManagedChannelImplBuilder.ChannelBuilderDefaultPortProvider;
45
import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder;
46
import io.grpc.internal.ObjectPool;
47
import io.grpc.internal.SharedResourceHolder.Resource;
48
import io.grpc.internal.SharedResourcePool;
49
import io.grpc.internal.TransportTracer;
50
import io.grpc.okhttp.internal.CipherSuite;
51
import io.grpc.okhttp.internal.ConnectionSpec;
52
import io.grpc.okhttp.internal.Platform;
53
import io.grpc.okhttp.internal.TlsVersion;
54
import io.grpc.util.CertificateUtils;
55
import java.io.ByteArrayInputStream;
56
import java.io.IOException;
57
import java.net.InetSocketAddress;
58
import java.net.SocketAddress;
59
import java.security.GeneralSecurityException;
60
import java.security.KeyStore;
61
import java.security.PrivateKey;
62
import java.security.cert.X509Certificate;
63
import java.util.Collection;
64
import java.util.Collections;
65
import java.util.EnumSet;
66
import java.util.Set;
67
import java.util.concurrent.Executor;
68
import java.util.concurrent.ExecutorService;
69
import java.util.concurrent.Executors;
70
import java.util.concurrent.ScheduledExecutorService;
71
import java.util.concurrent.TimeUnit;
72
import java.util.logging.Level;
73
import java.util.logging.Logger;
74
import javax.annotation.CheckReturnValue;
75
import javax.annotation.Nullable;
76
import javax.net.SocketFactory;
77
import javax.net.ssl.HostnameVerifier;
78
import javax.net.ssl.KeyManager;
79
import javax.net.ssl.KeyManagerFactory;
80
import javax.net.ssl.SSLContext;
81
import javax.net.ssl.SSLSocketFactory;
82
import javax.net.ssl.TrustManager;
83
import javax.net.ssl.TrustManagerFactory;
84
import javax.security.auth.x500.X500Principal;
85

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

92
  private final ManagedChannelImplBuilder managedChannelImplBuilder;
93
  private TransportTracer.Factory transportTracerFactory = TransportTracer.getDefaultFactory();
1✔
94

95

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

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

108
  // @VisibleForTesting
109
  static final ConnectionSpec INTERNAL_DEFAULT_CONNECTION_SPEC =
1✔
110
      new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
111
          .cipherSuites(
1✔
112
              // The following items should be sync with Netty's Http2SecurityUtil.CIPHERS.
113
              CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
114
              CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
115
              CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
116
              CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
117
              CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
118
              CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
119

120
              // TLS 1.3 does not work so far. See issues:
121
              // https://github.com/grpc/grpc-java/issues/7765
122
              //
123
              // TLS 1.3
124
              //CipherSuite.TLS_AES_128_GCM_SHA256,
125
              //CipherSuite.TLS_AES_256_GCM_SHA384,
126
              //CipherSuite.TLS_CHACHA20_POLY1305_SHA256
127
              )
128
          .tlsVersions(/*TlsVersion.TLS_1_3,*/ TlsVersion.TLS_1_2)
1✔
129
          .supportsTlsExtensions(true)
1✔
130
          .build();
1✔
131

132
  private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L);
1✔
133
  private static final Resource<Executor> SHARED_EXECUTOR =
1✔
134
      new Resource<Executor>() {
1✔
135
        @Override
136
        public Executor create() {
137
          return Executors.newCachedThreadPool(GrpcUtil.getThreadFactory("grpc-okhttp-%d", true));
1✔
138
        }
139

140
        @Override
141
        public void close(Executor executor) {
142
          ((ExecutorService) executor).shutdown();
1✔
143
        }
1✔
144
      };
145
  static final ObjectPool<Executor> DEFAULT_TRANSPORT_EXECUTOR_POOL =
1✔
146
      SharedResourcePool.forResource(SHARED_EXECUTOR);
1✔
147

148
  /** Creates a new builder for the given server host and port. */
149
  public static OkHttpChannelBuilder forAddress(String host, int port) {
150
    return new OkHttpChannelBuilder(host, port);
1✔
151
  }
152

153
  /** Creates a new builder with the given host and port. */
154
  public static OkHttpChannelBuilder forAddress(String host, int port, ChannelCredentials creds) {
155
    return forTarget(GrpcUtil.authorityFromHostAndPort(host, port), creds);
1✔
156
  }
157

158
  /**
159
   * Creates a new builder for the given target that will be resolved by
160
   * {@link io.grpc.NameResolver}.
161
   */
162
  public static OkHttpChannelBuilder forTarget(String target) {
163
    return new OkHttpChannelBuilder(target);
1✔
164
  }
165

166
  /**
167
   * Creates a new builder for the given target that will be resolved by
168
   * {@link io.grpc.NameResolver}.
169
   */
170
  public static OkHttpChannelBuilder forTarget(String target, ChannelCredentials creds) {
171
    SslSocketFactoryResult result = sslSocketFactoryFrom(creds);
1✔
172
    if (result.error != null) {
1✔
173
      throw new IllegalArgumentException(result.error);
×
174
    }
175
    return new OkHttpChannelBuilder(target, creds, result.callCredentials, result.factory);
1✔
176
  }
177

178
  private ObjectPool<Executor> transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL;
1✔
179
  private ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool =
1✔
180
      SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE);
1✔
181

182
  private SocketFactory socketFactory;
183
  private SSLSocketFactory sslSocketFactory;
184
  private final boolean freezeSecurityConfiguration;
185
  private HostnameVerifier hostnameVerifier;
186
  private ConnectionSpec connectionSpec = INTERNAL_DEFAULT_CONNECTION_SPEC;
1✔
187
  private NegotiationType negotiationType = NegotiationType.TLS;
1✔
188
  private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
1✔
189
  private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
1✔
190
  private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW;
1✔
191
  private boolean keepAliveWithoutCalls;
192
  private int maxInboundMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE;
1✔
193
  private int maxInboundMetadataSize = Integer.MAX_VALUE;
1✔
194

195
  /**
196
   * If true, indicates that the transport may use the GET method for RPCs, and may include the
197
   * request body in the query params.
198
   */
199
  private final boolean useGetForSafeMethods = false;
1✔
200

201
  private OkHttpChannelBuilder(String host, int port) {
202
    this(GrpcUtil.authorityFromHostAndPort(host, port));
1✔
203
  }
1✔
204

205
  private OkHttpChannelBuilder(String target) {
1✔
206
    managedChannelImplBuilder = new ManagedChannelImplBuilder(target,
1✔
207
        new OkHttpChannelTransportFactoryBuilder(),
208
        new OkHttpChannelDefaultPortProvider());
209
    this.freezeSecurityConfiguration = false;
1✔
210
  }
1✔
211

212
  OkHttpChannelBuilder(
213
      String target, ChannelCredentials channelCreds, CallCredentials callCreds,
214
      SSLSocketFactory factory) {
1✔
215
    managedChannelImplBuilder = new ManagedChannelImplBuilder(
1✔
216
        target, channelCreds, callCreds,
217
        new OkHttpChannelTransportFactoryBuilder(),
218
        new OkHttpChannelDefaultPortProvider());
219
    this.sslSocketFactory = factory;
1✔
220
    this.negotiationType = factory == null ? NegotiationType.PLAINTEXT : NegotiationType.TLS;
1✔
221
    this.freezeSecurityConfiguration = true;
1✔
222
  }
1✔
223

224
  private final class OkHttpChannelTransportFactoryBuilder
1✔
225
      implements ClientTransportFactoryBuilder {
226
    @Override
227
    public ClientTransportFactory buildClientTransportFactory() {
228
      return buildTransportFactory();
1✔
229
    }
230
  }
231

232
  private final class OkHttpChannelDefaultPortProvider
1✔
233
      implements ChannelBuilderDefaultPortProvider {
234
    @Override
235
    public int getDefaultPort() {
236
      return OkHttpChannelBuilder.this.getDefaultPort();
1✔
237
    }
238
  }
239

240
  @Internal
241
  @Override
242
  protected ManagedChannelBuilder<?> delegate() {
243
    return managedChannelImplBuilder;
1✔
244
  }
245

246
  @VisibleForTesting
247
  OkHttpChannelBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) {
248
    this.transportTracerFactory = transportTracerFactory;
1✔
249
    return this;
1✔
250
  }
251

252
  /**
253
   * Override the default executor necessary for internal transport use.
254
   *
255
   * <p>The channel does not take ownership of the given executor. It is the caller' responsibility
256
   * to shutdown the executor when appropriate.
257
   */
258
  public OkHttpChannelBuilder transportExecutor(@Nullable Executor transportExecutor) {
259
    if (transportExecutor == null) {
1✔
260
      this.transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL;
×
261
    } else {
262
      this.transportExecutorPool = new FixedObjectPool<>(transportExecutor);
1✔
263
    }
264
    return this;
1✔
265
  }
266

267
  /**
268
   * Override the default {@link SocketFactory} used to create sockets. If the socket factory is not
269
   * set or set to null, a default one will be used.
270
   *
271
   * @since 1.20.0
272
   */
273
  public OkHttpChannelBuilder socketFactory(@Nullable SocketFactory socketFactory) {
274
    this.socketFactory = socketFactory;
1✔
275
    return this;
1✔
276
  }
277

278
  /**
279
   * Sets the negotiation type for the HTTP/2 connection.
280
   *
281
   * <p>If TLS is enabled a default {@link SSLSocketFactory} is created using the best
282
   * {@link java.security.Provider} available and is NOT based on
283
   * {@link SSLSocketFactory#getDefault}. To more precisely control the TLS configuration call
284
   * {@link #sslSocketFactory} to override the socket factory used.
285
   *
286
   * <p>Default: <code>TLS</code>
287
   *
288
   * @deprecated use {@link #usePlaintext()} or {@link #useTransportSecurity()} instead.
289
   */
290
  @Deprecated
291
  public OkHttpChannelBuilder negotiationType(io.grpc.okhttp.NegotiationType type) {
292
    Preconditions.checkState(!freezeSecurityConfiguration,
×
293
        "Cannot change security when using ChannelCredentials");
294
    Preconditions.checkNotNull(type, "type");
×
295
    switch (type) {
×
296
      case TLS:
297
        negotiationType = NegotiationType.TLS;
×
298
        break;
×
299
      case PLAINTEXT:
300
        negotiationType = NegotiationType.PLAINTEXT;
×
301
        break;
×
302
      default:
303
        throw new AssertionError("Unknown negotiation type: " + type);
×
304
    }
305
    return this;
×
306
  }
307

308
  /**
309
   * {@inheritDoc}
310
   *
311
   * @since 1.3.0
312
   */
313
  @Override
314
  public OkHttpChannelBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
315
    Preconditions.checkArgument(keepAliveTime > 0L, "keepalive time must be positive");
×
316
    keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime);
×
317
    keepAliveTimeNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeNanos);
×
318
    if (keepAliveTimeNanos >= AS_LARGE_AS_INFINITE) {
×
319
      // Bump keepalive time to infinite. This disables keepalive.
320
      keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
×
321
    }
322
    return this;
×
323
  }
324

325
  /**
326
   * {@inheritDoc}
327
   *
328
   * @since 1.3.0
329
   */
330
  @Override
331
  public OkHttpChannelBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) {
332
    Preconditions.checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive");
×
333
    keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout);
×
334
    keepAliveTimeoutNanos = KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutNanos);
×
335
    return this;
×
336
  }
337

338
  /**
339
   * Sets the flow control window in bytes. If not called, the default value
340
   * is {@link #DEFAULT_FLOW_CONTROL_WINDOW}).
341
   */
342
  public OkHttpChannelBuilder flowControlWindow(int flowControlWindow) {
343
    Preconditions.checkState(flowControlWindow > 0, "flowControlWindow must be positive");
1✔
344
    this.flowControlWindow = flowControlWindow;
1✔
345
    return this;
1✔
346
  }
347

348
  /**
349
   * {@inheritDoc}
350
   *
351
   * @since 1.3.0
352
   * @see #keepAliveTime(long, TimeUnit)
353
   */
354
  @Override
355
  public OkHttpChannelBuilder keepAliveWithoutCalls(boolean enable) {
356
    keepAliveWithoutCalls = enable;
×
357
    return this;
×
358
  }
359

360
  /**
361
   * Override the default {@link SSLSocketFactory} and enable TLS negotiation.
362
   */
363
  public OkHttpChannelBuilder sslSocketFactory(SSLSocketFactory factory) {
364
    Preconditions.checkState(!freezeSecurityConfiguration,
1✔
365
        "Cannot change security when using ChannelCredentials");
366
    this.sslSocketFactory = factory;
1✔
367
    negotiationType = NegotiationType.TLS;
1✔
368
    return this;
1✔
369
  }
370

371
  /**
372
   * Set the hostname verifier to use when using TLS negotiation. The hostnameVerifier is only used
373
   * if using TLS negotiation. If the hostname verifier is not set, a default hostname verifier is
374
   * used.
375
   *
376
   * <p>Be careful when setting a custom hostname verifier! By setting a non-null value, you are
377
   * replacing all default verification behavior. If the hostname verifier you supply does not
378
   * effectively supply the same checks, you may be removing the security assurances that TLS aims
379
   * to provide.</p>
380
   *
381
   * <p>This method should not be used to avoid hostname verification, even during testing, since
382
   * {@link #overrideAuthority} is a safer alternative as it does not disable any security checks.
383
   * </p>
384
   *
385
   * @see io.grpc.okhttp.internal.OkHostnameVerifier
386
   *
387
   * @since 1.6.0
388
   * @return this
389
   *
390
   */
391
  public OkHttpChannelBuilder hostnameVerifier(@Nullable HostnameVerifier hostnameVerifier) {
392
    Preconditions.checkState(!freezeSecurityConfiguration,
1✔
393
        "Cannot change security when using ChannelCredentials");
394
    this.hostnameVerifier = hostnameVerifier;
1✔
395
    return this;
1✔
396
  }
397

398
  /**
399
   * For secure connection, provides a ConnectionSpec to specify Cipher suite and
400
   * TLS versions.
401
   *
402
   * <p>By default a modern, HTTP/2-compatible spec will be used.
403
   *
404
   * <p>This method is only used when building a secure connection. For plaintext
405
   * connection, use {@link #usePlaintext()} instead.
406
   *
407
   * @throws IllegalArgumentException
408
   *         If {@code connectionSpec} is not with TLS
409
   */
410
  public OkHttpChannelBuilder connectionSpec(
411
      com.squareup.okhttp.ConnectionSpec connectionSpec) {
412
    Preconditions.checkState(!freezeSecurityConfiguration,
1✔
413
        "Cannot change security when using ChannelCredentials");
414
    Preconditions.checkArgument(connectionSpec.isTls(), "plaintext ConnectionSpec is not accepted");
1✔
415
    this.connectionSpec = Utils.convertSpec(connectionSpec);
1✔
416
    return this;
1✔
417
  }
418

419
  /**
420
   * Sets the connection specification used for secure connections.
421
   *
422
   * <p>By default a modern, HTTP/2-compatible spec will be used.
423
   *
424
   * <p>This method is only used when building a secure connection. For plaintext
425
   * connection, use {@link #usePlaintext()} instead.
426
   *
427
   * @param tlsVersions List of tls versions.
428
   * @param cipherSuites List of cipher suites.
429
   *
430
   * @since  1.43.0
431
   */
432
  public OkHttpChannelBuilder tlsConnectionSpec(
433
          String[] tlsVersions, String[] cipherSuites) {
434
    Preconditions.checkState(!freezeSecurityConfiguration,
×
435
            "Cannot change security when using ChannelCredentials");
436
    Preconditions.checkNotNull(tlsVersions, "tls versions must not null");
×
437
    Preconditions.checkNotNull(cipherSuites, "ciphers must not null");
×
438

439
    this.connectionSpec = new ConnectionSpec.Builder(true)
×
440
            .supportsTlsExtensions(true)
×
441
            .tlsVersions(tlsVersions)
×
442
            .cipherSuites(cipherSuites)
×
443
            .build();
×
444
    return this;
×
445
  }
446

447
  /** Sets the negotiation type for the HTTP/2 connection to plaintext. */
448
  @Override
449
  public OkHttpChannelBuilder usePlaintext() {
450
    Preconditions.checkState(!freezeSecurityConfiguration,
1✔
451
        "Cannot change security when using ChannelCredentials");
452
    negotiationType = NegotiationType.PLAINTEXT;
1✔
453
    return this;
1✔
454
  }
455

456
  /**
457
   * Sets the negotiation type for the HTTP/2 connection to TLS (this is the default).
458
   *
459
   * <p>With TLS enabled, a default {@link SSLSocketFactory} is created using the best {@link
460
   * java.security.Provider} available and is NOT based on {@link SSLSocketFactory#getDefault}. To
461
   * more precisely control the TLS configuration call {@link #sslSocketFactory(SSLSocketFactory)}
462
   * to override the socket factory used.
463
   */
464
  @Override
465
  public OkHttpChannelBuilder useTransportSecurity() {
466
    Preconditions.checkState(!freezeSecurityConfiguration,
×
467
        "Cannot change security when using ChannelCredentials");
468
    negotiationType = NegotiationType.TLS;
×
469
    return this;
×
470
  }
471

472
  /**
473
   * Provides a custom scheduled executor service.
474
   *
475
   * <p>It's an optional parameter. If the user has not provided a scheduled executor service when
476
   * the channel is built, the builder will use a static cached thread pool.
477
   *
478
   * @return this
479
   *
480
   * @since 1.11.0
481
   */
482
  public OkHttpChannelBuilder scheduledExecutorService(
483
      ScheduledExecutorService scheduledExecutorService) {
484
    this.scheduledExecutorServicePool =
1✔
485
        new FixedObjectPool<>(checkNotNull(scheduledExecutorService, "scheduledExecutorService"));
1✔
486
    return this;
1✔
487
  }
488

489
  /**
490
   * Sets the maximum size of metadata allowed to be received. {@code Integer.MAX_VALUE} disables
491
   * the enforcement. Defaults to no limit ({@code Integer.MAX_VALUE}).
492
   *
493
   * <p>The implementation does not currently limit memory usage; this value is checked only after
494
   * the metadata is decoded from the wire. It does prevent large metadata from being passed to the
495
   * application.
496
   *
497
   * @param bytes the maximum size of received metadata
498
   * @return this
499
   * @throws IllegalArgumentException if bytes is non-positive
500
   * @since 1.17.0
501
   */
502
  @Override
503
  public OkHttpChannelBuilder maxInboundMetadataSize(int bytes) {
504
    Preconditions.checkArgument(bytes > 0, "maxInboundMetadataSize must be > 0");
1✔
505
    this.maxInboundMetadataSize = bytes;
1✔
506
    return this;
1✔
507
  }
508

509
  /**
510
   * Sets the maximum message size allowed for a single gRPC frame. If an inbound messages
511
   * larger than this limit is received it will not be processed and the RPC will fail with
512
   * RESOURCE_EXHAUSTED.
513
   */
514
  @Override
515
  public OkHttpChannelBuilder maxInboundMessageSize(int max) {
516
    Preconditions.checkArgument(max >= 0, "negative max");
1✔
517
    maxInboundMessageSize = max;
1✔
518
    return this;
1✔
519
  }
520

521
  OkHttpTransportFactory buildTransportFactory() {
522
    boolean enableKeepAlive = keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED;
1✔
523
    return new OkHttpTransportFactory(
1✔
524
        transportExecutorPool,
525
        scheduledExecutorServicePool,
526
        socketFactory,
527
        createSslSocketFactory(),
1✔
528
        hostnameVerifier,
529
        connectionSpec,
530
        maxInboundMessageSize,
531
        enableKeepAlive,
532
        keepAliveTimeNanos,
533
        keepAliveTimeoutNanos,
534
        flowControlWindow,
535
        keepAliveWithoutCalls,
536
        maxInboundMetadataSize,
537
        transportTracerFactory,
538
        useGetForSafeMethods);
539
  }
540

541
  OkHttpChannelBuilder disableCheckAuthority() {
542
    this.managedChannelImplBuilder.disableCheckAuthority();
1✔
543
    return this;
1✔
544
  }
545

546
  OkHttpChannelBuilder enableCheckAuthority() {
547
    this.managedChannelImplBuilder.enableCheckAuthority();
1✔
548
    return this;
1✔
549
  }
550

551
  int getDefaultPort() {
552
    switch (negotiationType) {
1✔
553
      case PLAINTEXT:
554
        return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
1✔
555
      case TLS:
556
        return GrpcUtil.DEFAULT_PORT_SSL;
1✔
557
      default:
558
        throw new AssertionError(negotiationType + " not handled");
×
559
    }
560
  }
561

562
  void setStatsEnabled(boolean value) {
563
    this.managedChannelImplBuilder.setStatsEnabled(value);
1✔
564
  }
1✔
565

566
  @VisibleForTesting
567
  @Nullable
568
  SSLSocketFactory createSslSocketFactory() {
569
    switch (negotiationType) {
1✔
570
      case TLS:
571
        try {
572
          if (sslSocketFactory == null) {
1✔
573
            SSLContext sslContext = SSLContext.getInstance("Default", Platform.get().getProvider());
1✔
574
            sslSocketFactory = sslContext.getSocketFactory();
1✔
575
          }
576
          return sslSocketFactory;
1✔
577
        } catch (GeneralSecurityException gse) {
×
578
          throw new RuntimeException("TLS Provider failure", gse);
×
579
        }
580
      case PLAINTEXT:
581
        return null;
1✔
582
      default:
583
        throw new RuntimeException("Unknown negotiation type: " + negotiationType);
×
584
    }
585
  }
586

587
  private static final EnumSet<TlsChannelCredentials.Feature> understoodTlsFeatures =
1✔
588
      EnumSet.of(
1✔
589
          TlsChannelCredentials.Feature.MTLS, TlsChannelCredentials.Feature.CUSTOM_MANAGERS);
590

591
  static SslSocketFactoryResult sslSocketFactoryFrom(ChannelCredentials creds) {
592
    if (creds instanceof TlsChannelCredentials) {
1✔
593
      TlsChannelCredentials tlsCreds = (TlsChannelCredentials) creds;
1✔
594
      Set<TlsChannelCredentials.Feature> incomprehensible =
1✔
595
          tlsCreds.incomprehensible(understoodTlsFeatures);
1✔
596
      if (!incomprehensible.isEmpty()) {
1✔
597
        return SslSocketFactoryResult.error(
1✔
598
            "TLS features not understood: " + incomprehensible);
599
      }
600
      KeyManager[] km = null;
1✔
601
      if (tlsCreds.getKeyManagers() != null) {
1✔
602
        km = tlsCreds.getKeyManagers().toArray(new KeyManager[0]);
1✔
603
      } else if (tlsCreds.getPrivateKey() != null) {
1✔
604
        if (tlsCreds.getPrivateKeyPassword() != null) {
1✔
605
          return SslSocketFactoryResult.error("byte[]-based private key with password unsupported. "
1✔
606
              + "Use unencrypted file or KeyManager");
607
        }
608
        try {
609
          km = createKeyManager(tlsCreds.getCertificateChain(), tlsCreds.getPrivateKey());
1✔
610
        } catch (GeneralSecurityException gse) {
×
611
          log.log(Level.FINE, "Exception loading private key from credential", gse);
×
612
          return SslSocketFactoryResult.error("Unable to load private key: " + gse.getMessage());
×
613
        }
1✔
614
      } // else don't have a client cert
615
      TrustManager[] tm = null;
1✔
616
      if (tlsCreds.getTrustManagers() != null) {
1✔
617
        tm = tlsCreds.getTrustManagers().toArray(new TrustManager[0]);
1✔
618
      } else if (tlsCreds.getRootCertificates() != null) {
1✔
619
        try {
620
          tm = createTrustManager(tlsCreds.getRootCertificates());
1✔
621
        } catch (GeneralSecurityException gse) {
×
622
          log.log(Level.FINE, "Exception loading root certificates from credential", gse);
×
623
          return SslSocketFactoryResult.error(
×
624
              "Unable to load root certificates: " + gse.getMessage());
×
625
        }
1✔
626
      } // else use system default
627
      SSLContext sslContext;
628
      try {
629
        sslContext = SSLContext.getInstance("TLS", Platform.get().getProvider());
1✔
630
        sslContext.init(km, tm, null);
1✔
631
      } catch (GeneralSecurityException gse) {
×
632
        throw new RuntimeException("TLS Provider failure", gse);
×
633
      }
1✔
634
      return SslSocketFactoryResult.factory(sslContext.getSocketFactory());
1✔
635

636
    } else if (creds instanceof InsecureChannelCredentials) {
1✔
637
      return SslSocketFactoryResult.plaintext();
1✔
638

639
    } else if (creds instanceof CompositeChannelCredentials) {
1✔
640
      CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds;
1✔
641
      return sslSocketFactoryFrom(compCreds.getChannelCredentials())
1✔
642
          .withCallCredentials(compCreds.getCallCredentials());
1✔
643

644
    } else if (creds instanceof SslSocketFactoryChannelCredentials.ChannelCredentials) {
1✔
645
      SslSocketFactoryChannelCredentials.ChannelCredentials factoryCreds =
1✔
646
          (SslSocketFactoryChannelCredentials.ChannelCredentials) creds;
647
      return SslSocketFactoryResult.factory(factoryCreds.getFactory());
1✔
648

649
    } else if (creds instanceof ChoiceChannelCredentials) {
1✔
650
      ChoiceChannelCredentials choiceCreds = (ChoiceChannelCredentials) creds;
1✔
651
      StringBuilder error = new StringBuilder();
1✔
652
      for (ChannelCredentials innerCreds : choiceCreds.getCredentialsList()) {
1✔
653
        SslSocketFactoryResult result = sslSocketFactoryFrom(innerCreds);
1✔
654
        if (result.error == null) {
1✔
655
          return result;
1✔
656
        }
657
        error.append(", ");
1✔
658
        error.append(result.error);
1✔
659
      }
1✔
660
      return SslSocketFactoryResult.error(error.substring(2));
1✔
661

662
    } else {
663
      return SslSocketFactoryResult.error(
1✔
664
          "Unsupported credential type: " + creds.getClass().getName());
1✔
665
    }
666
  }
667

668
  static KeyManager[] createKeyManager(byte[] certChain, byte[] privateKey)
669
      throws GeneralSecurityException {
670
    X509Certificate[] chain;
671
    ByteArrayInputStream inCertChain = new ByteArrayInputStream(certChain);
1✔
672
    try {
673
      chain = CertificateUtils.getX509Certificates(inCertChain);
1✔
674
    } finally {
675
      GrpcUtil.closeQuietly(inCertChain);
1✔
676
    }
677
    PrivateKey key;
678
    ByteArrayInputStream inPrivateKey = new ByteArrayInputStream(privateKey);
1✔
679
    try {
680
      key = CertificateUtils.getPrivateKey(inPrivateKey);
1✔
681
    } catch (IOException uee) {
×
682
      throw new GeneralSecurityException("Unable to decode private key", uee);
×
683
    } finally {
684
      GrpcUtil.closeQuietly(inPrivateKey);
1✔
685
    }
686
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
1✔
687
    try {
688
      ks.load(null, null);
1✔
689
    } catch (IOException ex) {
×
690
      // Shouldn't really happen, as we're not loading any data.
691
      throw new GeneralSecurityException(ex);
×
692
    }
1✔
693
    ks.setKeyEntry("key", key, new char[0], chain);
1✔
694

695
    KeyManagerFactory keyManagerFactory =
696
        KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
1✔
697
    keyManagerFactory.init(ks, new char[0]);
1✔
698
    return keyManagerFactory.getKeyManagers();
1✔
699
  }
700

701
  static TrustManager[] createTrustManager(byte[] rootCerts) throws GeneralSecurityException {
702
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
1✔
703
    try {
704
      ks.load(null, null);
1✔
705
    } catch (IOException ex) {
×
706
      // Shouldn't really happen, as we're not loading any data.
707
      throw new GeneralSecurityException(ex);
×
708
    }
1✔
709
    X509Certificate[] certs;
710
    ByteArrayInputStream in = new ByteArrayInputStream(rootCerts);
1✔
711
    try {
712
      certs = CertificateUtils.getX509Certificates(in);
1✔
713
    } finally {
714
      GrpcUtil.closeQuietly(in);
1✔
715
    }
716
    for (X509Certificate cert : certs) {
1✔
717
      X500Principal principal = cert.getSubjectX500Principal();
1✔
718
      ks.setCertificateEntry(principal.getName("RFC2253"), cert);
1✔
719
    }
720

721
    TrustManagerFactory trustManagerFactory =
722
        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
1✔
723
    trustManagerFactory.init(ks);
1✔
724
    return trustManagerFactory.getTrustManagers();
1✔
725
  }
726

727
  static Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
728
    return Collections.singleton(InetSocketAddress.class);
1✔
729
  }
730

731
  static final class SslSocketFactoryResult {
732
    /** {@code null} implies plaintext if {@code error == null}. */
733
    public final SSLSocketFactory factory;
734
    public final CallCredentials callCredentials;
735
    public final String error;
736

737
    private SslSocketFactoryResult(SSLSocketFactory factory, CallCredentials creds, String error) {
1✔
738
      this.factory = factory;
1✔
739
      this.callCredentials = creds;
1✔
740
      this.error = error;
1✔
741
    }
1✔
742

743
    public static SslSocketFactoryResult error(String error) {
744
      return new SslSocketFactoryResult(
1✔
745
          null, null, Preconditions.checkNotNull(error, "error"));
1✔
746
    }
747

748
    public static SslSocketFactoryResult plaintext() {
749
      return new SslSocketFactoryResult(null, null, null);
1✔
750
    }
751

752
    public static SslSocketFactoryResult factory(
753
        SSLSocketFactory factory) {
754
      return new SslSocketFactoryResult(
1✔
755
          Preconditions.checkNotNull(factory, "factory"), null, null);
1✔
756
    }
757

758
    public SslSocketFactoryResult withCallCredentials(CallCredentials callCreds) {
759
      Preconditions.checkNotNull(callCreds, "callCreds");
1✔
760
      if (error != null) {
1✔
761
        return this;
×
762
      }
763
      if (this.callCredentials != null) {
1✔
764
        callCreds = new CompositeCallCredentials(this.callCredentials, callCreds);
×
765
      }
766
      return new SslSocketFactoryResult(factory, callCreds, null);
1✔
767
    }
768
  }
769

770

771
  /**
772
   * Creates OkHttp transports. Exposed for internal use, as it should be private.
773
   */
774
  @Internal
775
  static final class OkHttpTransportFactory implements ClientTransportFactory {
776
    private final ObjectPool<Executor> executorPool;
777
    final Executor executor;
778
    private final ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool;
779
    final ScheduledExecutorService scheduledExecutorService;
780
    final TransportTracer.Factory transportTracerFactory;
781
    final SocketFactory socketFactory;
782
    @Nullable final SSLSocketFactory sslSocketFactory;
783
    @Nullable
784
    final HostnameVerifier hostnameVerifier;
785
    final ConnectionSpec connectionSpec;
786
    final int maxMessageSize;
787
    private final boolean enableKeepAlive;
788
    private final long keepAliveTimeNanos;
789
    private final AtomicBackoff keepAliveBackoff;
790
    private final long keepAliveTimeoutNanos;
791
    final int flowControlWindow;
792
    private final boolean keepAliveWithoutCalls;
793
    final int maxInboundMetadataSize;
794
    final boolean useGetForSafeMethods;
795
    private boolean closed;
796

797
    private OkHttpTransportFactory(
798
        ObjectPool<Executor> executorPool,
799
        ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool,
800
        @Nullable SocketFactory socketFactory,
801
        @Nullable SSLSocketFactory sslSocketFactory,
802
        @Nullable HostnameVerifier hostnameVerifier,
803
        ConnectionSpec connectionSpec,
804
        int maxMessageSize,
805
        boolean enableKeepAlive,
806
        long keepAliveTimeNanos,
807
        long keepAliveTimeoutNanos,
808
        int flowControlWindow,
809
        boolean keepAliveWithoutCalls,
810
        int maxInboundMetadataSize,
811
        TransportTracer.Factory transportTracerFactory,
812
        boolean useGetForSafeMethods) {
1✔
813
      this.executorPool = executorPool;
1✔
814
      this.executor = executorPool.getObject();
1✔
815
      this.scheduledExecutorServicePool = scheduledExecutorServicePool;
1✔
816
      this.scheduledExecutorService = scheduledExecutorServicePool.getObject();
1✔
817
      this.socketFactory = socketFactory;
1✔
818
      this.sslSocketFactory = sslSocketFactory;
1✔
819
      this.hostnameVerifier = hostnameVerifier;
1✔
820
      this.connectionSpec = connectionSpec;
1✔
821
      this.maxMessageSize = maxMessageSize;
1✔
822
      this.enableKeepAlive = enableKeepAlive;
1✔
823
      this.keepAliveTimeNanos = keepAliveTimeNanos;
1✔
824
      this.keepAliveBackoff = new AtomicBackoff("keepalive time nanos", keepAliveTimeNanos);
1✔
825
      this.keepAliveTimeoutNanos = keepAliveTimeoutNanos;
1✔
826
      this.flowControlWindow = flowControlWindow;
1✔
827
      this.keepAliveWithoutCalls = keepAliveWithoutCalls;
1✔
828
      this.maxInboundMetadataSize = maxInboundMetadataSize;
1✔
829
      this.useGetForSafeMethods = useGetForSafeMethods;
1✔
830

831
      this.transportTracerFactory =
1✔
832
          Preconditions.checkNotNull(transportTracerFactory, "transportTracerFactory");
1✔
833
    }
1✔
834

835
    @Override
836
    public ConnectionClientTransport newClientTransport(
837
        SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) {
838
      if (closed) {
1✔
839
        throw new IllegalStateException("The transport factory is closed.");
1✔
840
      }
841
      final AtomicBackoff.State keepAliveTimeNanosState = keepAliveBackoff.getState();
1✔
842
      Runnable tooManyPingsRunnable = new Runnable() {
1✔
843
        @Override
844
        public void run() {
845
          keepAliveTimeNanosState.backoff();
×
846
        }
×
847
      };
848
      InetSocketAddress inetSocketAddr = (InetSocketAddress) addr;
1✔
849
      // TODO(carl-mastrangelo): Pass channelLogger in.
850
      OkHttpClientTransport transport = new OkHttpClientTransport(
1✔
851
          this,
852
          inetSocketAddr,
853
          options.getAuthority(),
1✔
854
          options.getUserAgent(),
1✔
855
          options.getEagAttributes(),
1✔
856
          options.getHttpConnectProxiedSocketAddress(),
1✔
857
          tooManyPingsRunnable);
858
      if (enableKeepAlive) {
1✔
859
        transport.enableKeepAlive(
×
860
            true, keepAliveTimeNanosState.get(), keepAliveTimeoutNanos, keepAliveWithoutCalls);
×
861
      }
862
      return transport;
1✔
863
    }
864

865
    @Override
866
    public ScheduledExecutorService getScheduledExecutorService() {
867
      return scheduledExecutorService;
1✔
868
    }
869

870
    @Nullable
871
    @CheckReturnValue
872
    @Override
873
    public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) {
874
      SslSocketFactoryResult result = sslSocketFactoryFrom(channelCreds);
1✔
875
      if (result.error != null) {
1✔
876
        return null;
1✔
877
      }
878
      ClientTransportFactory factory = new OkHttpTransportFactory(
1✔
879
          executorPool,
880
          scheduledExecutorServicePool,
881
          socketFactory,
882
          result.factory,
883
          hostnameVerifier,
884
          connectionSpec,
885
          maxMessageSize,
886
          enableKeepAlive,
887
          keepAliveTimeNanos,
888
          keepAliveTimeoutNanos,
889
          flowControlWindow,
890
          keepAliveWithoutCalls,
891
          maxInboundMetadataSize,
892
          transportTracerFactory,
893
          useGetForSafeMethods);
894
      return new SwapChannelCredentialsResult(factory, result.callCredentials);
1✔
895
    }
896

897
    @Override
898
    public void close() {
899
      if (closed) {
1✔
900
        return;
1✔
901
      }
902
      closed = true;
1✔
903

904
      executorPool.returnObject(executor);
1✔
905
      scheduledExecutorServicePool.returnObject(scheduledExecutorService);
1✔
906
    }
1✔
907

908
    @Override
909
    public Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
910
      return OkHttpChannelBuilder.getSupportedSocketAddressTypes();
1✔
911
    }
912
  }
913
}
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