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

grpc / grpc-java / #19711

24 Feb 2025 02:58PM UTC coverage: 88.545% (-0.08%) from 88.622%
#19711

push

github

web-flow
netty: Per-rpc authority verification against peer cert subject names (#11724)

Per-rpc verification of authority specified via call options or set by the LB API against peer cert's subject names.

34437 of 38892 relevant lines covered (88.55%)

0.89 hits per line

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

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

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

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

94

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

667
  static KeyManager[] createKeyManager(byte[] certChain, byte[] privateKey)
668
      throws GeneralSecurityException {
669
    InputStream certChainStream = new ByteArrayInputStream(certChain);
1✔
670
    InputStream privateKeyStream = new ByteArrayInputStream(privateKey);
1✔
671
    try {
672
      return createKeyManager(certChainStream, privateKeyStream);
1✔
673
    } finally {
674
      GrpcUtil.closeQuietly(certChainStream);
1✔
675
      GrpcUtil.closeQuietly(privateKeyStream);
1✔
676
    }
677
  }
678

679
  static KeyManager[] createKeyManager(InputStream certChain, InputStream privateKey)
680
      throws GeneralSecurityException {
681
    X509Certificate[] chain = CertificateUtils.getX509Certificates(certChain);
1✔
682
    PrivateKey key;
683
    try {
684
      key = CertificateUtils.getPrivateKey(privateKey);
1✔
685
    } catch (IOException uee) {
×
686
      throw new GeneralSecurityException("Unable to decode private key", uee);
×
687
    }
1✔
688
    KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
1✔
689
    try {
690
      ks.load(null, null);
1✔
691
    } catch (IOException ex) {
×
692
      // Shouldn't really happen, as we're not loading any data.
693
      throw new GeneralSecurityException(ex);
×
694
    }
1✔
695
    ks.setKeyEntry("key", key, new char[0], chain);
1✔
696

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

703
  static TrustManager[] createTrustManager(byte[] rootCerts) throws GeneralSecurityException {
704
    InputStream rootCertsStream = new ByteArrayInputStream(rootCerts);
1✔
705
    try {
706
      return io.grpc.internal.CertificateUtils.createTrustManager(rootCertsStream);
1✔
707
    } finally {
708
      GrpcUtil.closeQuietly(rootCertsStream);
1✔
709
    }
710
  }
711

712
  static Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
713
    return Collections.singleton(InetSocketAddress.class);
1✔
714
  }
715

716
  static final class SslSocketFactoryResult {
717
    /** {@code null} implies plaintext if {@code error == null}. */
718
    public final SSLSocketFactory factory;
719
    public final CallCredentials callCredentials;
720
    public final String error;
721

722
    private SslSocketFactoryResult(SSLSocketFactory factory, CallCredentials creds, String error) {
1✔
723
      this.factory = factory;
1✔
724
      this.callCredentials = creds;
1✔
725
      this.error = error;
1✔
726
    }
1✔
727

728
    public static SslSocketFactoryResult error(String error) {
729
      return new SslSocketFactoryResult(
1✔
730
          null, null, Preconditions.checkNotNull(error, "error"));
1✔
731
    }
732

733
    public static SslSocketFactoryResult plaintext() {
734
      return new SslSocketFactoryResult(null, null, null);
1✔
735
    }
736

737
    public static SslSocketFactoryResult factory(
738
        SSLSocketFactory factory) {
739
      return new SslSocketFactoryResult(
1✔
740
          Preconditions.checkNotNull(factory, "factory"), null, null);
1✔
741
    }
742

743
    public SslSocketFactoryResult withCallCredentials(CallCredentials callCreds) {
744
      Preconditions.checkNotNull(callCreds, "callCreds");
1✔
745
      if (error != null) {
1✔
746
        return this;
×
747
      }
748
      if (this.callCredentials != null) {
1✔
749
        callCreds = new CompositeCallCredentials(this.callCredentials, callCreds);
×
750
      }
751
      return new SslSocketFactoryResult(factory, callCreds, null);
1✔
752
    }
753
  }
754

755

756
  /**
757
   * Creates OkHttp transports. Exposed for internal use, as it should be private.
758
   */
759
  @Internal
760
  static final class OkHttpTransportFactory implements ClientTransportFactory {
761
    private final ObjectPool<Executor> executorPool;
762
    final Executor executor;
763
    private final ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool;
764
    final ScheduledExecutorService scheduledExecutorService;
765
    final TransportTracer.Factory transportTracerFactory;
766
    final SocketFactory socketFactory;
767
    @Nullable final SSLSocketFactory sslSocketFactory;
768
    @Nullable
769
    final HostnameVerifier hostnameVerifier;
770
    final ConnectionSpec connectionSpec;
771
    final int maxMessageSize;
772
    private final boolean enableKeepAlive;
773
    private final long keepAliveTimeNanos;
774
    private final AtomicBackoff keepAliveBackoff;
775
    private final long keepAliveTimeoutNanos;
776
    final int flowControlWindow;
777
    private final boolean keepAliveWithoutCalls;
778
    final int maxInboundMetadataSize;
779
    final boolean useGetForSafeMethods;
780
    private boolean closed;
781

782
    private OkHttpTransportFactory(
783
        ObjectPool<Executor> executorPool,
784
        ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool,
785
        @Nullable SocketFactory socketFactory,
786
        @Nullable SSLSocketFactory sslSocketFactory,
787
        @Nullable HostnameVerifier hostnameVerifier,
788
        ConnectionSpec connectionSpec,
789
        int maxMessageSize,
790
        boolean enableKeepAlive,
791
        long keepAliveTimeNanos,
792
        long keepAliveTimeoutNanos,
793
        int flowControlWindow,
794
        boolean keepAliveWithoutCalls,
795
        int maxInboundMetadataSize,
796
        TransportTracer.Factory transportTracerFactory,
797
        boolean useGetForSafeMethods) {
1✔
798
      this.executorPool = executorPool;
1✔
799
      this.executor = executorPool.getObject();
1✔
800
      this.scheduledExecutorServicePool = scheduledExecutorServicePool;
1✔
801
      this.scheduledExecutorService = scheduledExecutorServicePool.getObject();
1✔
802
      this.socketFactory = socketFactory;
1✔
803
      this.sslSocketFactory = sslSocketFactory;
1✔
804
      this.hostnameVerifier = hostnameVerifier;
1✔
805
      this.connectionSpec = connectionSpec;
1✔
806
      this.maxMessageSize = maxMessageSize;
1✔
807
      this.enableKeepAlive = enableKeepAlive;
1✔
808
      this.keepAliveTimeNanos = keepAliveTimeNanos;
1✔
809
      this.keepAliveBackoff = new AtomicBackoff("keepalive time nanos", keepAliveTimeNanos);
1✔
810
      this.keepAliveTimeoutNanos = keepAliveTimeoutNanos;
1✔
811
      this.flowControlWindow = flowControlWindow;
1✔
812
      this.keepAliveWithoutCalls = keepAliveWithoutCalls;
1✔
813
      this.maxInboundMetadataSize = maxInboundMetadataSize;
1✔
814
      this.useGetForSafeMethods = useGetForSafeMethods;
1✔
815

816
      this.transportTracerFactory =
1✔
817
          Preconditions.checkNotNull(transportTracerFactory, "transportTracerFactory");
1✔
818
    }
1✔
819

820
    @Override
821
    public ConnectionClientTransport newClientTransport(
822
        SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) {
823
      if (closed) {
1✔
824
        throw new IllegalStateException("The transport factory is closed.");
1✔
825
      }
826
      final AtomicBackoff.State keepAliveTimeNanosState = keepAliveBackoff.getState();
1✔
827
      Runnable tooManyPingsRunnable = new Runnable() {
1✔
828
        @Override
829
        public void run() {
830
          keepAliveTimeNanosState.backoff();
×
831
        }
×
832
      };
833
      InetSocketAddress inetSocketAddr = (InetSocketAddress) addr;
1✔
834
      // TODO(carl-mastrangelo): Pass channelLogger in.
835
      OkHttpClientTransport transport = new OkHttpClientTransport(
1✔
836
          this,
837
          inetSocketAddr,
838
          options.getAuthority(),
1✔
839
          options.getUserAgent(),
1✔
840
          options.getEagAttributes(),
1✔
841
          options.getHttpConnectProxiedSocketAddress(),
1✔
842
          tooManyPingsRunnable);
843
      if (enableKeepAlive) {
1✔
844
        transport.enableKeepAlive(
×
845
            true, keepAliveTimeNanosState.get(), keepAliveTimeoutNanos, keepAliveWithoutCalls);
×
846
      }
847
      return transport;
1✔
848
    }
849

850
    @Override
851
    public ScheduledExecutorService getScheduledExecutorService() {
852
      return scheduledExecutorService;
1✔
853
    }
854

855
    @Nullable
856
    @CheckReturnValue
857
    @Override
858
    public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) {
859
      SslSocketFactoryResult result = sslSocketFactoryFrom(channelCreds);
1✔
860
      if (result.error != null) {
1✔
861
        return null;
1✔
862
      }
863
      ClientTransportFactory factory = new OkHttpTransportFactory(
1✔
864
          executorPool,
865
          scheduledExecutorServicePool,
866
          socketFactory,
867
          result.factory,
868
          hostnameVerifier,
869
          connectionSpec,
870
          maxMessageSize,
871
          enableKeepAlive,
872
          keepAliveTimeNanos,
873
          keepAliveTimeoutNanos,
874
          flowControlWindow,
875
          keepAliveWithoutCalls,
876
          maxInboundMetadataSize,
877
          transportTracerFactory,
878
          useGetForSafeMethods);
879
      return new SwapChannelCredentialsResult(factory, result.callCredentials);
1✔
880
    }
881

882
    @Override
883
    public void close() {
884
      if (closed) {
1✔
885
        return;
1✔
886
      }
887
      closed = true;
1✔
888

889
      executorPool.returnObject(executor);
1✔
890
      scheduledExecutorServicePool.returnObject(scheduledExecutorService);
1✔
891
    }
1✔
892

893
    @Override
894
    public Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
895
      return OkHttpChannelBuilder.getSupportedSocketAddressTypes();
1✔
896
    }
897
  }
898
}
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