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

grpc / grpc-java / #19758

02 Apr 2025 04:40AM UTC coverage: 88.103% (-0.5%) from 88.593%
#19758

push

github

web-flow
okhttp: Per-rpc call option authority verification (#11754)

30021 of 34075 relevant lines covered (88.1%)

1.02 hits per line

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

80.07
/../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.TlsChannelCredentials;
39
import io.grpc.internal.AtomicBackoff;
40
import io.grpc.internal.ClientTransportFactory;
41
import io.grpc.internal.ConnectionClientTransport;
42
import io.grpc.internal.FixedObjectPool;
43
import io.grpc.internal.GrpcUtil;
44
import io.grpc.internal.KeepAliveManager;
45
import io.grpc.internal.ManagedChannelImplBuilder;
46
import io.grpc.internal.ManagedChannelImplBuilder.ChannelBuilderDefaultPortProvider;
47
import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder;
48
import io.grpc.internal.ObjectPool;
49
import io.grpc.internal.SharedResourceHolder.Resource;
50
import io.grpc.internal.SharedResourcePool;
51
import io.grpc.internal.TransportTracer;
52
import io.grpc.okhttp.internal.CipherSuite;
53
import io.grpc.okhttp.internal.ConnectionSpec;
54
import io.grpc.okhttp.internal.Platform;
55
import io.grpc.okhttp.internal.TlsVersion;
56
import io.grpc.util.CertificateUtils;
57
import java.io.ByteArrayInputStream;
58
import java.io.IOException;
59
import java.io.InputStream;
60
import java.net.InetSocketAddress;
61
import java.net.SocketAddress;
62
import java.security.GeneralSecurityException;
63
import java.security.KeyStore;
64
import java.security.PrivateKey;
65
import java.security.cert.X509Certificate;
66
import java.util.Collection;
67
import java.util.Collections;
68
import java.util.EnumSet;
69
import java.util.Set;
70
import java.util.concurrent.Executor;
71
import java.util.concurrent.ExecutorService;
72
import java.util.concurrent.Executors;
73
import java.util.concurrent.ScheduledExecutorService;
74
import java.util.concurrent.TimeUnit;
75
import java.util.logging.Level;
76
import java.util.logging.Logger;
77
import javax.annotation.Nullable;
78
import javax.net.SocketFactory;
79
import javax.net.ssl.HostnameVerifier;
80
import javax.net.ssl.KeyManager;
81
import javax.net.ssl.KeyManagerFactory;
82
import javax.net.ssl.SSLContext;
83
import javax.net.ssl.SSLSocketFactory;
84
import javax.net.ssl.TrustManager;
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 final ChannelCredentials channelCredentials;
94
  private TransportTracer.Factory transportTracerFactory = TransportTracer.getDefaultFactory();
1✔
95

96

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

243
  @Internal
244
  @Override
245
  protected ManagedChannelBuilder<?> delegate() {
246
    return managedChannelImplBuilder;
1✔
247
  }
248

249
  @VisibleForTesting
250
  OkHttpChannelBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) {
251
    this.transportTracerFactory = transportTracerFactory;
1✔
252
    return this;
1✔
253
  }
254

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

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

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

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

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

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

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

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

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

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

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

442
    this.connectionSpec = new ConnectionSpec.Builder(true)
×
443
            .supportsTlsExtensions(true)
×
444
            .tlsVersions(tlsVersions)
×
445
            .cipherSuites(cipherSuites)
×
446
            .build();
×
447
    return this;
×
448
  }
449

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

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

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

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

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

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

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

550
  OkHttpChannelBuilder enableCheckAuthority() {
551
    this.managedChannelImplBuilder.enableCheckAuthority();
1✔
552
    return this;
1✔
553
  }
554

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

566
  void setStatsEnabled(boolean value) {
567
    this.managedChannelImplBuilder.setStatsEnabled(value);
1✔
568
  }
1✔
569

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

591
  private static final EnumSet<TlsChannelCredentials.Feature> understoodTlsFeatures =
1✔
592
      EnumSet.of(
1✔
593
          TlsChannelCredentials.Feature.MTLS, TlsChannelCredentials.Feature.CUSTOM_MANAGERS);
594

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

640
    } else if (creds instanceof InsecureChannelCredentials) {
1✔
641
      return SslSocketFactoryResult.plaintext();
1✔
642

643
    } else if (creds instanceof CompositeChannelCredentials) {
1✔
644
      CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds;
1✔
645
      return sslSocketFactoryFrom(compCreds.getChannelCredentials())
1✔
646
          .withCallCredentials(compCreds.getCallCredentials());
1✔
647

648
    } else if (creds instanceof SslSocketFactoryChannelCredentials.ChannelCredentials) {
1✔
649
      SslSocketFactoryChannelCredentials.ChannelCredentials factoryCreds =
1✔
650
          (SslSocketFactoryChannelCredentials.ChannelCredentials) creds;
651
      return SslSocketFactoryResult.factory(factoryCreds.getFactory());
1✔
652

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

666
    } else {
667
      return SslSocketFactoryResult.error(
1✔
668
          "Unsupported credential type: " + creds.getClass().getName());
1✔
669
    }
670
  }
671

672
  static KeyManager[] createKeyManager(byte[] certChain, byte[] privateKey)
673
      throws GeneralSecurityException {
674
    InputStream certChainStream = new ByteArrayInputStream(certChain);
1✔
675
    InputStream privateKeyStream = new ByteArrayInputStream(privateKey);
1✔
676
    try {
677
      return createKeyManager(certChainStream, privateKeyStream);
1✔
678
    } finally {
679
      GrpcUtil.closeQuietly(certChainStream);
1✔
680
      GrpcUtil.closeQuietly(privateKeyStream);
1✔
681
    }
682
  }
683

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

702
    KeyManagerFactory keyManagerFactory =
703
        KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
1✔
704
    keyManagerFactory.init(ks, new char[0]);
1✔
705
    return keyManagerFactory.getKeyManagers();
1✔
706
  }
707

708
  static TrustManager[] createTrustManager(byte[] rootCerts) throws GeneralSecurityException {
709
    InputStream rootCertsStream = new ByteArrayInputStream(rootCerts);
1✔
710
    try {
711
      return io.grpc.internal.CertificateUtils.createTrustManager(rootCertsStream);
1✔
712
    } finally {
713
      GrpcUtil.closeQuietly(rootCertsStream);
1✔
714
    }
715
  }
716

717
  static Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
718
    return Collections.singleton(InetSocketAddress.class);
1✔
719
  }
720

721
  static final class SslSocketFactoryResult {
722
    /** {@code null} implies plaintext if {@code error == null}. */
723
    public final SSLSocketFactory factory;
724
    public final CallCredentials callCredentials;
725
    public final String error;
726

727
    private SslSocketFactoryResult(SSLSocketFactory factory, CallCredentials creds, String error) {
1✔
728
      this.factory = factory;
1✔
729
      this.callCredentials = creds;
1✔
730
      this.error = error;
1✔
731
    }
1✔
732

733
    public static SslSocketFactoryResult error(String error) {
734
      return new SslSocketFactoryResult(
1✔
735
          null, null, Preconditions.checkNotNull(error, "error"));
1✔
736
    }
737

738
    public static SslSocketFactoryResult plaintext() {
739
      return new SslSocketFactoryResult(null, null, null);
1✔
740
    }
741

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

748
    public SslSocketFactoryResult withCallCredentials(CallCredentials callCreds) {
749
      Preconditions.checkNotNull(callCreds, "callCreds");
1✔
750
      if (error != null) {
1✔
751
        return this;
×
752
      }
753
      if (this.callCredentials != null) {
1✔
754
        callCreds = new CompositeCallCredentials(this.callCredentials, callCreds);
×
755
      }
756
      return new SslSocketFactoryResult(factory, callCreds, null);
1✔
757
    }
758
  }
759

760

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

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

824
      this.transportTracerFactory =
1✔
825
          Preconditions.checkNotNull(transportTracerFactory, "transportTracerFactory");
1✔
826
    }
1✔
827

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

859
    @Override
860
    public ScheduledExecutorService getScheduledExecutorService() {
861
      return scheduledExecutorService;
1✔
862
    }
863

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

892
    @Override
893
    public void close() {
894
      if (closed) {
1✔
895
        return;
1✔
896
      }
897
      closed = true;
1✔
898

899
      executorPool.returnObject(executor);
1✔
900
      scheduledExecutorServicePool.returnObject(scheduledExecutorService);
1✔
901
    }
1✔
902

903
    @Override
904
    public Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
905
      return OkHttpChannelBuilder.getSupportedSocketAddressTypes();
1✔
906
    }
907
  }
908
}
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

© 2025 Coveralls, Inc