• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.InsecureChannelCredentials;
33 import io.grpc.Internal;
34 import io.grpc.ManagedChannelBuilder;
35 import io.grpc.TlsChannelCredentials;
36 import io.grpc.internal.AbstractManagedChannelImplBuilder;
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.EnumSet;
64 import java.util.Set;
65 import java.util.concurrent.Executor;
66 import java.util.concurrent.ExecutorService;
67 import java.util.concurrent.Executors;
68 import java.util.concurrent.ScheduledExecutorService;
69 import java.util.concurrent.TimeUnit;
70 import java.util.logging.Level;
71 import java.util.logging.Logger;
72 import javax.annotation.CheckReturnValue;
73 import javax.annotation.Nullable;
74 import javax.net.SocketFactory;
75 import javax.net.ssl.HostnameVerifier;
76 import javax.net.ssl.KeyManager;
77 import javax.net.ssl.KeyManagerFactory;
78 import javax.net.ssl.SSLContext;
79 import javax.net.ssl.SSLSocketFactory;
80 import javax.net.ssl.TrustManager;
81 import javax.net.ssl.TrustManagerFactory;
82 import javax.security.auth.x500.X500Principal;
83 
84 /** Convenience class for building channels with the OkHttp transport. */
85 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1785")
86 public final class OkHttpChannelBuilder extends
87     AbstractManagedChannelImplBuilder<OkHttpChannelBuilder> {
88   private static final Logger log = Logger.getLogger(OkHttpChannelBuilder.class.getName());
89   public static final int DEFAULT_FLOW_CONTROL_WINDOW = 65535;
90 
91   private final ManagedChannelImplBuilder managedChannelImplBuilder;
92   private TransportTracer.Factory transportTracerFactory = TransportTracer.getDefaultFactory();
93 
94 
95   /** Identifies the negotiation used for starting up HTTP/2. */
96   private enum NegotiationType {
97     /** Uses TLS ALPN/NPN negotiation, assumes an SSL connection. */
98     TLS,
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
105   }
106 
107   // @VisibleForTesting
108   static final ConnectionSpec INTERNAL_DEFAULT_CONNECTION_SPEC =
109       new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
110           .cipherSuites(
111               // The following items should be sync with Netty's Http2SecurityUtil.CIPHERS.
112               CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_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)
128           .supportsTlsExtensions(true)
129           .build();
130 
131   private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L);
132   private static final Resource<Executor> SHARED_EXECUTOR =
133       new Resource<Executor>() {
134         @Override
135         public Executor create() {
136           return Executors.newCachedThreadPool(GrpcUtil.getThreadFactory("grpc-okhttp-%d", true));
137         }
138 
139         @Override
140         public void close(Executor executor) {
141           ((ExecutorService) executor).shutdown();
142         }
143       };
144   static final ObjectPool<Executor> DEFAULT_TRANSPORT_EXECUTOR_POOL =
145       SharedResourcePool.forResource(SHARED_EXECUTOR);
146 
147   /** Creates a new builder for the given server host and port. */
forAddress(String host, int port)148   public static OkHttpChannelBuilder forAddress(String host, int port) {
149     return new OkHttpChannelBuilder(host, port);
150   }
151 
152   /** Creates a new builder with the given host and port. */
forAddress(String host, int port, ChannelCredentials creds)153   public static OkHttpChannelBuilder forAddress(String host, int port, ChannelCredentials creds) {
154     return forTarget(GrpcUtil.authorityFromHostAndPort(host, port), creds);
155   }
156 
157   /**
158    * Creates a new builder for the given target that will be resolved by
159    * {@link io.grpc.NameResolver}.
160    */
forTarget(String target)161   public static OkHttpChannelBuilder forTarget(String target) {
162     return new OkHttpChannelBuilder(target);
163   }
164 
165   /**
166    * Creates a new builder for the given target that will be resolved by
167    * {@link io.grpc.NameResolver}.
168    */
forTarget(String target, ChannelCredentials creds)169   public static OkHttpChannelBuilder forTarget(String target, ChannelCredentials creds) {
170     SslSocketFactoryResult result = sslSocketFactoryFrom(creds);
171     if (result.error != null) {
172       throw new IllegalArgumentException(result.error);
173     }
174     return new OkHttpChannelBuilder(target, creds, result.callCredentials, result.factory);
175   }
176 
177   private ObjectPool<Executor> transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL;
178   private ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool =
179       SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE);
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;
186   private NegotiationType negotiationType = NegotiationType.TLS;
187   private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
188   private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
189   private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW;
190   private boolean keepAliveWithoutCalls;
191   private int maxInboundMetadataSize = Integer.MAX_VALUE;
192 
193   /**
194    * If true, indicates that the transport may use the GET method for RPCs, and may include the
195    * request body in the query params.
196    */
197   private final boolean useGetForSafeMethods = false;
198 
OkHttpChannelBuilder(String host, int port)199   private OkHttpChannelBuilder(String host, int port) {
200     this(GrpcUtil.authorityFromHostAndPort(host, port));
201   }
202 
OkHttpChannelBuilder(String target)203   private OkHttpChannelBuilder(String target) {
204     managedChannelImplBuilder = new ManagedChannelImplBuilder(target,
205         new OkHttpChannelTransportFactoryBuilder(),
206         new OkHttpChannelDefaultPortProvider());
207     this.freezeSecurityConfiguration = false;
208   }
209 
OkHttpChannelBuilder( String target, ChannelCredentials channelCreds, CallCredentials callCreds, SSLSocketFactory factory)210   OkHttpChannelBuilder(
211       String target, ChannelCredentials channelCreds, CallCredentials callCreds,
212       SSLSocketFactory factory) {
213     managedChannelImplBuilder = new ManagedChannelImplBuilder(
214         target, channelCreds, callCreds,
215         new OkHttpChannelTransportFactoryBuilder(),
216         new OkHttpChannelDefaultPortProvider());
217     this.sslSocketFactory = factory;
218     this.negotiationType = factory == null ? NegotiationType.PLAINTEXT : NegotiationType.TLS;
219     this.freezeSecurityConfiguration = true;
220   }
221 
222   private final class OkHttpChannelTransportFactoryBuilder
223       implements ClientTransportFactoryBuilder {
224     @Override
buildClientTransportFactory()225     public ClientTransportFactory buildClientTransportFactory() {
226       return buildTransportFactory();
227     }
228   }
229 
230   private final class OkHttpChannelDefaultPortProvider
231       implements ChannelBuilderDefaultPortProvider {
232     @Override
getDefaultPort()233     public int getDefaultPort() {
234       return OkHttpChannelBuilder.this.getDefaultPort();
235     }
236   }
237 
238   @Internal
239   @Override
delegate()240   protected ManagedChannelBuilder<?> delegate() {
241     return managedChannelImplBuilder;
242   }
243 
244   @VisibleForTesting
setTransportTracerFactory(TransportTracer.Factory transportTracerFactory)245   OkHttpChannelBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) {
246     this.transportTracerFactory = transportTracerFactory;
247     return this;
248   }
249 
250   /**
251    * Override the default executor necessary for internal transport use.
252    *
253    * <p>The channel does not take ownership of the given executor. It is the caller' responsibility
254    * to shutdown the executor when appropriate.
255    */
transportExecutor(@ullable Executor transportExecutor)256   public OkHttpChannelBuilder transportExecutor(@Nullable Executor transportExecutor) {
257     if (transportExecutor == null) {
258       this.transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL;
259     } else {
260       this.transportExecutorPool = new FixedObjectPool<>(transportExecutor);
261     }
262     return this;
263   }
264 
265   /**
266    * Override the default {@link SocketFactory} used to create sockets. If the socket factory is not
267    * set or set to null, a default one will be used.
268    *
269    * @since 1.20.0
270    */
socketFactory(@ullable SocketFactory socketFactory)271   public OkHttpChannelBuilder socketFactory(@Nullable SocketFactory socketFactory) {
272     this.socketFactory = socketFactory;
273     return this;
274   }
275 
276   /**
277    * Sets the negotiation type for the HTTP/2 connection.
278    *
279    * <p>If TLS is enabled a default {@link SSLSocketFactory} is created using the best
280    * {@link java.security.Provider} available and is NOT based on
281    * {@link SSLSocketFactory#getDefault}. To more precisely control the TLS configuration call
282    * {@link #sslSocketFactory} to override the socket factory used.
283    *
284    * <p>Default: <code>TLS</code>
285    *
286    * @deprecated use {@link #usePlaintext()} or {@link #useTransportSecurity()} instead.
287    */
288   @Deprecated
negotiationType(io.grpc.okhttp.NegotiationType type)289   public OkHttpChannelBuilder negotiationType(io.grpc.okhttp.NegotiationType type) {
290     Preconditions.checkState(!freezeSecurityConfiguration,
291         "Cannot change security when using ChannelCredentials");
292     Preconditions.checkNotNull(type, "type");
293     switch (type) {
294       case TLS:
295         negotiationType = NegotiationType.TLS;
296         break;
297       case PLAINTEXT:
298         negotiationType = NegotiationType.PLAINTEXT;
299         break;
300       default:
301         throw new AssertionError("Unknown negotiation type: " + type);
302     }
303     return this;
304   }
305 
306   /**
307    * {@inheritDoc}
308    *
309    * @since 1.3.0
310    */
311   @Override
keepAliveTime(long keepAliveTime, TimeUnit timeUnit)312   public OkHttpChannelBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
313     Preconditions.checkArgument(keepAliveTime > 0L, "keepalive time must be positive");
314     keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime);
315     keepAliveTimeNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeNanos);
316     if (keepAliveTimeNanos >= AS_LARGE_AS_INFINITE) {
317       // Bump keepalive time to infinite. This disables keepalive.
318       keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
319     }
320     return this;
321   }
322 
323   /**
324    * {@inheritDoc}
325    *
326    * @since 1.3.0
327    */
328   @Override
keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit)329   public OkHttpChannelBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) {
330     Preconditions.checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive");
331     keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout);
332     keepAliveTimeoutNanos = KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutNanos);
333     return this;
334   }
335 
336   /**
337    * Sets the flow control window in bytes. If not called, the default value
338    * is {@link #DEFAULT_FLOW_CONTROL_WINDOW}).
339    */
flowControlWindow(int flowControlWindow)340   public OkHttpChannelBuilder flowControlWindow(int flowControlWindow) {
341     Preconditions.checkState(flowControlWindow > 0, "flowControlWindow must be positive");
342     this.flowControlWindow = flowControlWindow;
343     return this;
344   }
345 
346   /**
347    * {@inheritDoc}
348    *
349    * @since 1.3.0
350    * @see #keepAliveTime(long, TimeUnit)
351    */
352   @Override
keepAliveWithoutCalls(boolean enable)353   public OkHttpChannelBuilder keepAliveWithoutCalls(boolean enable) {
354     keepAliveWithoutCalls = enable;
355     return this;
356   }
357 
358   /**
359    * Override the default {@link SSLSocketFactory} and enable TLS negotiation.
360    */
sslSocketFactory(SSLSocketFactory factory)361   public OkHttpChannelBuilder sslSocketFactory(SSLSocketFactory factory) {
362     Preconditions.checkState(!freezeSecurityConfiguration,
363         "Cannot change security when using ChannelCredentials");
364     this.sslSocketFactory = factory;
365     negotiationType = NegotiationType.TLS;
366     return this;
367   }
368 
369   /**
370    * Set the hostname verifier to use when using TLS negotiation. The hostnameVerifier is only used
371    * if using TLS negotiation. If the hostname verifier is not set, a default hostname verifier is
372    * used.
373    *
374    * <p>Be careful when setting a custom hostname verifier! By setting a non-null value, you are
375    * replacing all default verification behavior. If the hostname verifier you supply does not
376    * effectively supply the same checks, you may be removing the security assurances that TLS aims
377    * to provide.</p>
378    *
379    * <p>This method should not be used to avoid hostname verification, even during testing, since
380    * {@link #overrideAuthority} is a safer alternative as it does not disable any security checks.
381    * </p>
382    *
383    * @see io.grpc.okhttp.internal.OkHostnameVerifier
384    *
385    * @since 1.6.0
386    * @return this
387    *
388    */
hostnameVerifier(@ullable HostnameVerifier hostnameVerifier)389   public OkHttpChannelBuilder hostnameVerifier(@Nullable HostnameVerifier hostnameVerifier) {
390     Preconditions.checkState(!freezeSecurityConfiguration,
391         "Cannot change security when using ChannelCredentials");
392     this.hostnameVerifier = hostnameVerifier;
393     return this;
394   }
395 
396   /**
397    * For secure connection, provides a ConnectionSpec to specify Cipher suite and
398    * TLS versions.
399    *
400    * <p>By default a modern, HTTP/2-compatible spec will be used.
401    *
402    * <p>This method is only used when building a secure connection. For plaintext
403    * connection, use {@link #usePlaintext()} instead.
404    *
405    * @throws IllegalArgumentException
406    *         If {@code connectionSpec} is not with TLS
407    */
connectionSpec( com.squareup.okhttp.ConnectionSpec connectionSpec)408   public OkHttpChannelBuilder connectionSpec(
409       com.squareup.okhttp.ConnectionSpec connectionSpec) {
410     Preconditions.checkState(!freezeSecurityConfiguration,
411         "Cannot change security when using ChannelCredentials");
412     Preconditions.checkArgument(connectionSpec.isTls(), "plaintext ConnectionSpec is not accepted");
413     this.connectionSpec = Utils.convertSpec(connectionSpec);
414     return this;
415   }
416 
417   /**
418    * Sets the connection specification used for secure connections.
419    *
420    * <p>By default a modern, HTTP/2-compatible spec will be used.
421    *
422    * <p>This method is only used when building a secure connection. For plaintext
423    * connection, use {@link #usePlaintext()} instead.
424    *
425    * @param tlsVersions List of tls versions.
426    * @param cipherSuites List of cipher suites.
427    *
428    * @since  1.43.0
429    */
tlsConnectionSpec( String[] tlsVersions, String[] cipherSuites)430   public OkHttpChannelBuilder tlsConnectionSpec(
431           String[] tlsVersions, String[] cipherSuites) {
432     Preconditions.checkState(!freezeSecurityConfiguration,
433             "Cannot change security when using ChannelCredentials");
434     Preconditions.checkNotNull(tlsVersions, "tls versions must not null");
435     Preconditions.checkNotNull(cipherSuites, "ciphers must not null");
436 
437     this.connectionSpec = new ConnectionSpec.Builder(true)
438             .supportsTlsExtensions(true)
439             .tlsVersions(tlsVersions)
440             .cipherSuites(cipherSuites)
441             .build();
442     return this;
443   }
444 
445   /** Sets the negotiation type for the HTTP/2 connection to plaintext. */
446   @Override
usePlaintext()447   public OkHttpChannelBuilder usePlaintext() {
448     Preconditions.checkState(!freezeSecurityConfiguration,
449         "Cannot change security when using ChannelCredentials");
450     negotiationType = NegotiationType.PLAINTEXT;
451     return this;
452   }
453 
454   /**
455    * Sets the negotiation type for the HTTP/2 connection to TLS (this is the default).
456    *
457    * <p>With TLS enabled, a default {@link SSLSocketFactory} is created using the best {@link
458    * java.security.Provider} available and is NOT based on {@link SSLSocketFactory#getDefault}. To
459    * more precisely control the TLS configuration call {@link #sslSocketFactory(SSLSocketFactory)}
460    * to override the socket factory used.
461    */
462   @Override
useTransportSecurity()463   public OkHttpChannelBuilder useTransportSecurity() {
464     Preconditions.checkState(!freezeSecurityConfiguration,
465         "Cannot change security when using ChannelCredentials");
466     negotiationType = NegotiationType.TLS;
467     return this;
468   }
469 
470   /**
471    * Provides a custom scheduled executor service.
472    *
473    * <p>It's an optional parameter. If the user has not provided a scheduled executor service when
474    * the channel is built, the builder will use a static cached thread pool.
475    *
476    * @return this
477    *
478    * @since 1.11.0
479    */
scheduledExecutorService( ScheduledExecutorService scheduledExecutorService)480   public OkHttpChannelBuilder scheduledExecutorService(
481       ScheduledExecutorService scheduledExecutorService) {
482     this.scheduledExecutorServicePool =
483         new FixedObjectPool<>(checkNotNull(scheduledExecutorService, "scheduledExecutorService"));
484     return this;
485   }
486 
487   /**
488    * Sets the maximum size of metadata allowed to be received. {@code Integer.MAX_VALUE} disables
489    * the enforcement. Defaults to no limit ({@code Integer.MAX_VALUE}).
490    *
491    * <p>The implementation does not currently limit memory usage; this value is checked only after
492    * the metadata is decoded from the wire. It does prevent large metadata from being passed to the
493    * application.
494    *
495    * @param bytes the maximum size of received metadata
496    * @return this
497    * @throws IllegalArgumentException if bytes is non-positive
498    * @since 1.17.0
499    */
500   @Override
maxInboundMetadataSize(int bytes)501   public OkHttpChannelBuilder maxInboundMetadataSize(int bytes) {
502     Preconditions.checkArgument(bytes > 0, "maxInboundMetadataSize must be > 0");
503     this.maxInboundMetadataSize = bytes;
504     return this;
505   }
506 
507   /**
508    * Sets the maximum message size allowed for a single gRPC frame. If an inbound messages
509    * larger than this limit is received it will not be processed and the RPC will fail with
510    * RESOURCE_EXHAUSTED.
511    */
512   @Override
maxInboundMessageSize(int max)513   public OkHttpChannelBuilder maxInboundMessageSize(int max) {
514     Preconditions.checkArgument(max >= 0, "negative max");
515     maxInboundMessageSize = max;
516     return this;
517   }
518 
buildTransportFactory()519   OkHttpTransportFactory buildTransportFactory() {
520     boolean enableKeepAlive = keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED;
521     return new OkHttpTransportFactory(
522         transportExecutorPool,
523         scheduledExecutorServicePool,
524         socketFactory,
525         createSslSocketFactory(),
526         hostnameVerifier,
527         connectionSpec,
528         maxInboundMessageSize,
529         enableKeepAlive,
530         keepAliveTimeNanos,
531         keepAliveTimeoutNanos,
532         flowControlWindow,
533         keepAliveWithoutCalls,
534         maxInboundMetadataSize,
535         transportTracerFactory,
536         useGetForSafeMethods);
537   }
538 
disableCheckAuthority()539   OkHttpChannelBuilder disableCheckAuthority() {
540     this.managedChannelImplBuilder.disableCheckAuthority();
541     return this;
542   }
543 
enableCheckAuthority()544   OkHttpChannelBuilder enableCheckAuthority() {
545     this.managedChannelImplBuilder.enableCheckAuthority();
546     return this;
547   }
548 
getDefaultPort()549   int getDefaultPort() {
550     switch (negotiationType) {
551       case PLAINTEXT:
552         return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
553       case TLS:
554         return GrpcUtil.DEFAULT_PORT_SSL;
555       default:
556         throw new AssertionError(negotiationType + " not handled");
557     }
558   }
559 
setStatsEnabled(boolean value)560   void setStatsEnabled(boolean value) {
561     this.managedChannelImplBuilder.setStatsEnabled(value);
562   }
563 
564   @VisibleForTesting
565   @Nullable
createSslSocketFactory()566   SSLSocketFactory createSslSocketFactory() {
567     switch (negotiationType) {
568       case TLS:
569         try {
570           if (sslSocketFactory == null) {
571             SSLContext sslContext = SSLContext.getInstance("Default", Platform.get().getProvider());
572             sslSocketFactory = sslContext.getSocketFactory();
573           }
574           return sslSocketFactory;
575         } catch (GeneralSecurityException gse) {
576           throw new RuntimeException("TLS Provider failure", gse);
577         }
578       case PLAINTEXT:
579         return null;
580       default:
581         throw new RuntimeException("Unknown negotiation type: " + negotiationType);
582     }
583   }
584 
585   private static final EnumSet<TlsChannelCredentials.Feature> understoodTlsFeatures =
586       EnumSet.of(
587           TlsChannelCredentials.Feature.MTLS, TlsChannelCredentials.Feature.CUSTOM_MANAGERS);
588 
sslSocketFactoryFrom(ChannelCredentials creds)589   static SslSocketFactoryResult sslSocketFactoryFrom(ChannelCredentials creds) {
590     if (creds instanceof TlsChannelCredentials) {
591       TlsChannelCredentials tlsCreds = (TlsChannelCredentials) creds;
592       Set<TlsChannelCredentials.Feature> incomprehensible =
593           tlsCreds.incomprehensible(understoodTlsFeatures);
594       if (!incomprehensible.isEmpty()) {
595         return SslSocketFactoryResult.error(
596             "TLS features not understood: " + incomprehensible);
597       }
598       KeyManager[] km = null;
599       if (tlsCreds.getKeyManagers() != null) {
600         km = tlsCreds.getKeyManagers().toArray(new KeyManager[0]);
601       } else if (tlsCreds.getPrivateKey() != null) {
602         if (tlsCreds.getPrivateKeyPassword() != null) {
603           return SslSocketFactoryResult.error("byte[]-based private key with password unsupported. "
604               + "Use unencrypted file or KeyManager");
605         }
606         try {
607           km = createKeyManager(tlsCreds.getCertificateChain(), tlsCreds.getPrivateKey());
608         } catch (GeneralSecurityException gse) {
609           log.log(Level.FINE, "Exception loading private key from credential", gse);
610           return SslSocketFactoryResult.error("Unable to load private key: " + gse.getMessage());
611         }
612       } // else don't have a client cert
613       TrustManager[] tm = null;
614       if (tlsCreds.getTrustManagers() != null) {
615         tm = tlsCreds.getTrustManagers().toArray(new TrustManager[0]);
616       } else if (tlsCreds.getRootCertificates() != null) {
617         try {
618           tm = createTrustManager(tlsCreds.getRootCertificates());
619         } catch (GeneralSecurityException gse) {
620           log.log(Level.FINE, "Exception loading root certificates from credential", gse);
621           return SslSocketFactoryResult.error(
622               "Unable to load root certificates: " + gse.getMessage());
623         }
624       } // else use system default
625       SSLContext sslContext;
626       try {
627         sslContext = SSLContext.getInstance("TLS", Platform.get().getProvider());
628         sslContext.init(km, tm, null);
629       } catch (GeneralSecurityException gse) {
630         throw new RuntimeException("TLS Provider failure", gse);
631       }
632       return SslSocketFactoryResult.factory(sslContext.getSocketFactory());
633 
634     } else if (creds instanceof InsecureChannelCredentials) {
635       return SslSocketFactoryResult.plaintext();
636 
637     } else if (creds instanceof CompositeChannelCredentials) {
638       CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds;
639       return sslSocketFactoryFrom(compCreds.getChannelCredentials())
640           .withCallCredentials(compCreds.getCallCredentials());
641 
642     } else if (creds instanceof SslSocketFactoryChannelCredentials.ChannelCredentials) {
643       SslSocketFactoryChannelCredentials.ChannelCredentials factoryCreds =
644           (SslSocketFactoryChannelCredentials.ChannelCredentials) creds;
645       return SslSocketFactoryResult.factory(factoryCreds.getFactory());
646 
647     } else if (creds instanceof ChoiceChannelCredentials) {
648       ChoiceChannelCredentials choiceCreds = (ChoiceChannelCredentials) creds;
649       StringBuilder error = new StringBuilder();
650       for (ChannelCredentials innerCreds : choiceCreds.getCredentialsList()) {
651         SslSocketFactoryResult result = sslSocketFactoryFrom(innerCreds);
652         if (result.error == null) {
653           return result;
654         }
655         error.append(", ");
656         error.append(result.error);
657       }
658       return SslSocketFactoryResult.error(error.substring(2));
659 
660     } else {
661       return SslSocketFactoryResult.error(
662           "Unsupported credential type: " + creds.getClass().getName());
663     }
664   }
665 
createKeyManager(byte[] certChain, byte[] privateKey)666   static KeyManager[] createKeyManager(byte[] certChain, byte[] privateKey)
667       throws GeneralSecurityException {
668     X509Certificate[] chain;
669     ByteArrayInputStream inCertChain = new ByteArrayInputStream(certChain);
670     try {
671       chain = CertificateUtils.getX509Certificates(inCertChain);
672     } finally {
673       GrpcUtil.closeQuietly(inCertChain);
674     }
675     PrivateKey key;
676     ByteArrayInputStream inPrivateKey = new ByteArrayInputStream(privateKey);
677     try {
678       key = CertificateUtils.getPrivateKey(inPrivateKey);
679     } catch (IOException uee) {
680       throw new GeneralSecurityException("Unable to decode private key", uee);
681     } finally {
682       GrpcUtil.closeQuietly(inPrivateKey);
683     }
684     KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
685     try {
686       ks.load(null, null);
687     } catch (IOException ex) {
688       // Shouldn't really happen, as we're not loading any data.
689       throw new GeneralSecurityException(ex);
690     }
691     ks.setKeyEntry("key", key, new char[0], chain);
692 
693     KeyManagerFactory keyManagerFactory =
694         KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
695     keyManagerFactory.init(ks, new char[0]);
696     return keyManagerFactory.getKeyManagers();
697   }
698 
createTrustManager(byte[] rootCerts)699   static TrustManager[] createTrustManager(byte[] rootCerts) throws GeneralSecurityException {
700     KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
701     try {
702       ks.load(null, null);
703     } catch (IOException ex) {
704       // Shouldn't really happen, as we're not loading any data.
705       throw new GeneralSecurityException(ex);
706     }
707     X509Certificate[] certs;
708     ByteArrayInputStream in = new ByteArrayInputStream(rootCerts);
709     try {
710       certs = CertificateUtils.getX509Certificates(in);
711     } finally {
712       GrpcUtil.closeQuietly(in);
713     }
714     for (X509Certificate cert : certs) {
715       X500Principal principal = cert.getSubjectX500Principal();
716       ks.setCertificateEntry(principal.getName("RFC2253"), cert);
717     }
718 
719     TrustManagerFactory trustManagerFactory =
720         TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
721     trustManagerFactory.init(ks);
722     return trustManagerFactory.getTrustManagers();
723   }
724 
725   static final class SslSocketFactoryResult {
726     /** {@code null} implies plaintext if {@code error == null}. */
727     public final SSLSocketFactory factory;
728     public final CallCredentials callCredentials;
729     public final String error;
730 
SslSocketFactoryResult(SSLSocketFactory factory, CallCredentials creds, String error)731     private SslSocketFactoryResult(SSLSocketFactory factory, CallCredentials creds, String error) {
732       this.factory = factory;
733       this.callCredentials = creds;
734       this.error = error;
735     }
736 
error(String error)737     public static SslSocketFactoryResult error(String error) {
738       return new SslSocketFactoryResult(
739           null, null, Preconditions.checkNotNull(error, "error"));
740     }
741 
plaintext()742     public static SslSocketFactoryResult plaintext() {
743       return new SslSocketFactoryResult(null, null, null);
744     }
745 
factory( SSLSocketFactory factory)746     public static SslSocketFactoryResult factory(
747         SSLSocketFactory factory) {
748       return new SslSocketFactoryResult(
749           Preconditions.checkNotNull(factory, "factory"), null, null);
750     }
751 
withCallCredentials(CallCredentials callCreds)752     public SslSocketFactoryResult withCallCredentials(CallCredentials callCreds) {
753       Preconditions.checkNotNull(callCreds, "callCreds");
754       if (error != null) {
755         return this;
756       }
757       if (this.callCredentials != null) {
758         callCreds = new CompositeCallCredentials(this.callCredentials, callCreds);
759       }
760       return new SslSocketFactoryResult(factory, callCreds, null);
761     }
762   }
763 
764 
765   /**
766    * Creates OkHttp transports. Exposed for internal use, as it should be private.
767    */
768   @Internal
769   static final class OkHttpTransportFactory implements ClientTransportFactory {
770     private final ObjectPool<Executor> executorPool;
771     final Executor executor;
772     private final ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool;
773     final ScheduledExecutorService scheduledExecutorService;
774     final TransportTracer.Factory transportTracerFactory;
775     final SocketFactory socketFactory;
776     @Nullable final SSLSocketFactory sslSocketFactory;
777     @Nullable
778     final HostnameVerifier hostnameVerifier;
779     final ConnectionSpec connectionSpec;
780     final int maxMessageSize;
781     private final boolean enableKeepAlive;
782     private final long keepAliveTimeNanos;
783     private final AtomicBackoff keepAliveBackoff;
784     private final long keepAliveTimeoutNanos;
785     final int flowControlWindow;
786     private final boolean keepAliveWithoutCalls;
787     final int maxInboundMetadataSize;
788     final boolean useGetForSafeMethods;
789     private boolean closed;
790 
OkHttpTransportFactory( ObjectPool<Executor> executorPool, ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool, @Nullable SocketFactory socketFactory, @Nullable SSLSocketFactory sslSocketFactory, @Nullable HostnameVerifier hostnameVerifier, ConnectionSpec connectionSpec, int maxMessageSize, boolean enableKeepAlive, long keepAliveTimeNanos, long keepAliveTimeoutNanos, int flowControlWindow, boolean keepAliveWithoutCalls, int maxInboundMetadataSize, TransportTracer.Factory transportTracerFactory, boolean useGetForSafeMethods)791     private OkHttpTransportFactory(
792         ObjectPool<Executor> executorPool,
793         ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool,
794         @Nullable SocketFactory socketFactory,
795         @Nullable SSLSocketFactory sslSocketFactory,
796         @Nullable HostnameVerifier hostnameVerifier,
797         ConnectionSpec connectionSpec,
798         int maxMessageSize,
799         boolean enableKeepAlive,
800         long keepAliveTimeNanos,
801         long keepAliveTimeoutNanos,
802         int flowControlWindow,
803         boolean keepAliveWithoutCalls,
804         int maxInboundMetadataSize,
805         TransportTracer.Factory transportTracerFactory,
806         boolean useGetForSafeMethods) {
807       this.executorPool = executorPool;
808       this.executor = executorPool.getObject();
809       this.scheduledExecutorServicePool = scheduledExecutorServicePool;
810       this.scheduledExecutorService = scheduledExecutorServicePool.getObject();
811       this.socketFactory = socketFactory;
812       this.sslSocketFactory = sslSocketFactory;
813       this.hostnameVerifier = hostnameVerifier;
814       this.connectionSpec = connectionSpec;
815       this.maxMessageSize = maxMessageSize;
816       this.enableKeepAlive = enableKeepAlive;
817       this.keepAliveTimeNanos = keepAliveTimeNanos;
818       this.keepAliveBackoff = new AtomicBackoff("keepalive time nanos", keepAliveTimeNanos);
819       this.keepAliveTimeoutNanos = keepAliveTimeoutNanos;
820       this.flowControlWindow = flowControlWindow;
821       this.keepAliveWithoutCalls = keepAliveWithoutCalls;
822       this.maxInboundMetadataSize = maxInboundMetadataSize;
823       this.useGetForSafeMethods = useGetForSafeMethods;
824 
825       this.transportTracerFactory =
826           Preconditions.checkNotNull(transportTracerFactory, "transportTracerFactory");
827     }
828 
829     @Override
newClientTransport( SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger)830     public ConnectionClientTransport newClientTransport(
831         SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) {
832       if (closed) {
833         throw new IllegalStateException("The transport factory is closed.");
834       }
835       final AtomicBackoff.State keepAliveTimeNanosState = keepAliveBackoff.getState();
836       Runnable tooManyPingsRunnable = new Runnable() {
837         @Override
838         public void run() {
839           keepAliveTimeNanosState.backoff();
840         }
841       };
842       InetSocketAddress inetSocketAddr = (InetSocketAddress) addr;
843       // TODO(carl-mastrangelo): Pass channelLogger in.
844       OkHttpClientTransport transport = new OkHttpClientTransport(
845           this,
846           inetSocketAddr,
847           options.getAuthority(),
848           options.getUserAgent(),
849           options.getEagAttributes(),
850           options.getHttpConnectProxiedSocketAddress(),
851           tooManyPingsRunnable);
852       if (enableKeepAlive) {
853         transport.enableKeepAlive(
854             true, keepAliveTimeNanosState.get(), keepAliveTimeoutNanos, keepAliveWithoutCalls);
855       }
856       return transport;
857     }
858 
859     @Override
getScheduledExecutorService()860     public ScheduledExecutorService getScheduledExecutorService() {
861       return scheduledExecutorService;
862     }
863 
864     @Nullable
865     @CheckReturnValue
866     @Override
swapChannelCredentials(ChannelCredentials channelCreds)867     public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) {
868       SslSocketFactoryResult result = sslSocketFactoryFrom(channelCreds);
869       if (result.error != null) {
870         return null;
871       }
872       ClientTransportFactory factory = new OkHttpTransportFactory(
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       return new SwapChannelCredentialsResult(factory, result.callCredentials);
889     }
890 
891     @Override
close()892     public void close() {
893       if (closed) {
894         return;
895       }
896       closed = true;
897 
898       executorPool.returnObject(executor);
899       scheduledExecutorServicePool.returnObject(scheduledExecutorService);
900     }
901   }
902 }
903