All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.grpc.okhttp.OkHttpChannelBuilder Maven / Gradle / Ivy

There is a newer version: 1.66.0
Show newest version
/*
 * Copyright 2014 The gRPC Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.grpc.okhttp;

import static com.google.common.base.Preconditions.checkNotNull;
import static io.grpc.internal.GrpcUtil.DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
import static io.grpc.internal.GrpcUtil.KEEPALIVE_TIME_NANOS_DISABLED;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import io.grpc.CallCredentials;
import io.grpc.ChannelCredentials;
import io.grpc.ChannelLogger;
import io.grpc.ChoiceChannelCredentials;
import io.grpc.CompositeCallCredentials;
import io.grpc.CompositeChannelCredentials;
import io.grpc.ExperimentalApi;
import io.grpc.ForwardingChannelBuilder2;
import io.grpc.InsecureChannelCredentials;
import io.grpc.Internal;
import io.grpc.ManagedChannelBuilder;
import io.grpc.TlsChannelCredentials;
import io.grpc.internal.AtomicBackoff;
import io.grpc.internal.ClientTransportFactory;
import io.grpc.internal.ConnectionClientTransport;
import io.grpc.internal.FixedObjectPool;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.KeepAliveManager;
import io.grpc.internal.ManagedChannelImplBuilder;
import io.grpc.internal.ManagedChannelImplBuilder.ChannelBuilderDefaultPortProvider;
import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder;
import io.grpc.internal.ObjectPool;
import io.grpc.internal.SharedResourceHolder.Resource;
import io.grpc.internal.SharedResourcePool;
import io.grpc.internal.TransportTracer;
import io.grpc.okhttp.internal.CipherSuite;
import io.grpc.okhttp.internal.ConnectionSpec;
import io.grpc.okhttp.internal.Platform;
import io.grpc.okhttp.internal.TlsVersion;
import io.grpc.util.CertificateUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
import javax.net.SocketFactory;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.security.auth.x500.X500Principal;

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

  private final ManagedChannelImplBuilder managedChannelImplBuilder;
  private TransportTracer.Factory transportTracerFactory = TransportTracer.getDefaultFactory();


  /** Identifies the negotiation used for starting up HTTP/2. */
  private enum NegotiationType {
    /** Uses TLS ALPN/NPN negotiation, assumes an SSL connection. */
    TLS,

    /**
     * Just assume the connection is plaintext (non-SSL) and the remote endpoint supports HTTP/2
     * directly without an upgrade.
     */
    PLAINTEXT
  }

  // @VisibleForTesting
  static final ConnectionSpec INTERNAL_DEFAULT_CONNECTION_SPEC =
      new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
          .cipherSuites(
              // The following items should be sync with Netty's Http2SecurityUtil.CIPHERS.
              CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
              CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
              CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
              CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
              CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
              CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256

              // TLS 1.3 does not work so far. See issues:
              // https://github.com/grpc/grpc-java/issues/7765
              //
              // TLS 1.3
              //CipherSuite.TLS_AES_128_GCM_SHA256,
              //CipherSuite.TLS_AES_256_GCM_SHA384,
              //CipherSuite.TLS_CHACHA20_POLY1305_SHA256
              )
          .tlsVersions(/*TlsVersion.TLS_1_3,*/ TlsVersion.TLS_1_2)
          .supportsTlsExtensions(true)
          .build();

  private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L);
  private static final Resource SHARED_EXECUTOR =
      new Resource() {
        @Override
        public Executor create() {
          return Executors.newCachedThreadPool(GrpcUtil.getThreadFactory("grpc-okhttp-%d", true));
        }

        @Override
        public void close(Executor executor) {
          ((ExecutorService) executor).shutdown();
        }
      };
  static final ObjectPool DEFAULT_TRANSPORT_EXECUTOR_POOL =
      SharedResourcePool.forResource(SHARED_EXECUTOR);

  /** Creates a new builder for the given server host and port. */
  public static OkHttpChannelBuilder forAddress(String host, int port) {
    return new OkHttpChannelBuilder(host, port);
  }

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

  /**
   * Creates a new builder for the given target that will be resolved by
   * {@link io.grpc.NameResolver}.
   */
  public static OkHttpChannelBuilder forTarget(String target) {
    return new OkHttpChannelBuilder(target);
  }

  /**
   * Creates a new builder for the given target that will be resolved by
   * {@link io.grpc.NameResolver}.
   */
  public static OkHttpChannelBuilder forTarget(String target, ChannelCredentials creds) {
    SslSocketFactoryResult result = sslSocketFactoryFrom(creds);
    if (result.error != null) {
      throw new IllegalArgumentException(result.error);
    }
    return new OkHttpChannelBuilder(target, creds, result.callCredentials, result.factory);
  }

  private ObjectPool transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL;
  private ObjectPool scheduledExecutorServicePool =
      SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE);

  private SocketFactory socketFactory;
  private SSLSocketFactory sslSocketFactory;
  private final boolean freezeSecurityConfiguration;
  private HostnameVerifier hostnameVerifier;
  private ConnectionSpec connectionSpec = INTERNAL_DEFAULT_CONNECTION_SPEC;
  private NegotiationType negotiationType = NegotiationType.TLS;
  private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
  private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
  private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW;
  private boolean keepAliveWithoutCalls;
  private int maxInboundMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE;
  private int maxInboundMetadataSize = Integer.MAX_VALUE;

  /**
   * If true, indicates that the transport may use the GET method for RPCs, and may include the
   * request body in the query params.
   */
  private final boolean useGetForSafeMethods = false;

  private OkHttpChannelBuilder(String host, int port) {
    this(GrpcUtil.authorityFromHostAndPort(host, port));
  }

  private OkHttpChannelBuilder(String target) {
    managedChannelImplBuilder = new ManagedChannelImplBuilder(target,
        new OkHttpChannelTransportFactoryBuilder(),
        new OkHttpChannelDefaultPortProvider());
    this.freezeSecurityConfiguration = false;
  }

  OkHttpChannelBuilder(
      String target, ChannelCredentials channelCreds, CallCredentials callCreds,
      SSLSocketFactory factory) {
    managedChannelImplBuilder = new ManagedChannelImplBuilder(
        target, channelCreds, callCreds,
        new OkHttpChannelTransportFactoryBuilder(),
        new OkHttpChannelDefaultPortProvider());
    this.sslSocketFactory = factory;
    this.negotiationType = factory == null ? NegotiationType.PLAINTEXT : NegotiationType.TLS;
    this.freezeSecurityConfiguration = true;
  }

  private final class OkHttpChannelTransportFactoryBuilder
      implements ClientTransportFactoryBuilder {
    @Override
    public ClientTransportFactory buildClientTransportFactory() {
      return buildTransportFactory();
    }
  }

  private final class OkHttpChannelDefaultPortProvider
      implements ChannelBuilderDefaultPortProvider {
    @Override
    public int getDefaultPort() {
      return OkHttpChannelBuilder.this.getDefaultPort();
    }
  }

  @Internal
  @Override
  protected ManagedChannelBuilder delegate() {
    return managedChannelImplBuilder;
  }

  @VisibleForTesting
  OkHttpChannelBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) {
    this.transportTracerFactory = transportTracerFactory;
    return this;
  }

  /**
   * Override the default executor necessary for internal transport use.
   *
   * 

The channel does not take ownership of the given executor. It is the caller' responsibility * to shutdown the executor when appropriate. */ public OkHttpChannelBuilder transportExecutor(@Nullable Executor transportExecutor) { if (transportExecutor == null) { this.transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL; } else { this.transportExecutorPool = new FixedObjectPool<>(transportExecutor); } return this; } /** * Override the default {@link SocketFactory} used to create sockets. If the socket factory is not * set or set to null, a default one will be used. * * @since 1.20.0 */ public OkHttpChannelBuilder socketFactory(@Nullable SocketFactory socketFactory) { this.socketFactory = socketFactory; return this; } /** * Sets the negotiation type for the HTTP/2 connection. * *

If TLS is enabled a default {@link SSLSocketFactory} is created using the best * {@link java.security.Provider} available and is NOT based on * {@link SSLSocketFactory#getDefault}. To more precisely control the TLS configuration call * {@link #sslSocketFactory} to override the socket factory used. * *

Default: TLS * * @deprecated use {@link #usePlaintext()} or {@link #useTransportSecurity()} instead. */ @Deprecated public OkHttpChannelBuilder negotiationType(io.grpc.okhttp.NegotiationType type) { Preconditions.checkState(!freezeSecurityConfiguration, "Cannot change security when using ChannelCredentials"); Preconditions.checkNotNull(type, "type"); switch (type) { case TLS: negotiationType = NegotiationType.TLS; break; case PLAINTEXT: negotiationType = NegotiationType.PLAINTEXT; break; default: throw new AssertionError("Unknown negotiation type: " + type); } return this; } /** * {@inheritDoc} * * @since 1.3.0 */ @Override public OkHttpChannelBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) { Preconditions.checkArgument(keepAliveTime > 0L, "keepalive time must be positive"); keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime); keepAliveTimeNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeNanos); if (keepAliveTimeNanos >= AS_LARGE_AS_INFINITE) { // Bump keepalive time to infinite. This disables keepalive. keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED; } return this; } /** * {@inheritDoc} * * @since 1.3.0 */ @Override public OkHttpChannelBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) { Preconditions.checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive"); keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout); keepAliveTimeoutNanos = KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutNanos); return this; } /** * Sets the flow control window in bytes. If not called, the default value * is {@link #DEFAULT_FLOW_CONTROL_WINDOW}). */ public OkHttpChannelBuilder flowControlWindow(int flowControlWindow) { Preconditions.checkState(flowControlWindow > 0, "flowControlWindow must be positive"); this.flowControlWindow = flowControlWindow; return this; } /** * {@inheritDoc} * * @since 1.3.0 * @see #keepAliveTime(long, TimeUnit) */ @Override public OkHttpChannelBuilder keepAliveWithoutCalls(boolean enable) { keepAliveWithoutCalls = enable; return this; } /** * Override the default {@link SSLSocketFactory} and enable TLS negotiation. */ public OkHttpChannelBuilder sslSocketFactory(SSLSocketFactory factory) { Preconditions.checkState(!freezeSecurityConfiguration, "Cannot change security when using ChannelCredentials"); this.sslSocketFactory = factory; negotiationType = NegotiationType.TLS; return this; } /** * Set the hostname verifier to use when using TLS negotiation. The hostnameVerifier is only used * if using TLS negotiation. If the hostname verifier is not set, a default hostname verifier is * used. * *

Be careful when setting a custom hostname verifier! By setting a non-null value, you are * replacing all default verification behavior. If the hostname verifier you supply does not * effectively supply the same checks, you may be removing the security assurances that TLS aims * to provide.

* *

This method should not be used to avoid hostname verification, even during testing, since * {@link #overrideAuthority} is a safer alternative as it does not disable any security checks. *

* * @see io.grpc.okhttp.internal.OkHostnameVerifier * * @since 1.6.0 * @return this * */ public OkHttpChannelBuilder hostnameVerifier(@Nullable HostnameVerifier hostnameVerifier) { Preconditions.checkState(!freezeSecurityConfiguration, "Cannot change security when using ChannelCredentials"); this.hostnameVerifier = hostnameVerifier; return this; } /** * For secure connection, provides a ConnectionSpec to specify Cipher suite and * TLS versions. * *

By default a modern, HTTP/2-compatible spec will be used. * *

This method is only used when building a secure connection. For plaintext * connection, use {@link #usePlaintext()} instead. * * @throws IllegalArgumentException * If {@code connectionSpec} is not with TLS */ public OkHttpChannelBuilder connectionSpec( com.squareup.okhttp.ConnectionSpec connectionSpec) { Preconditions.checkState(!freezeSecurityConfiguration, "Cannot change security when using ChannelCredentials"); Preconditions.checkArgument(connectionSpec.isTls(), "plaintext ConnectionSpec is not accepted"); this.connectionSpec = Utils.convertSpec(connectionSpec); return this; } /** * Sets the connection specification used for secure connections. * *

By default a modern, HTTP/2-compatible spec will be used. * *

This method is only used when building a secure connection. For plaintext * connection, use {@link #usePlaintext()} instead. * * @param tlsVersions List of tls versions. * @param cipherSuites List of cipher suites. * * @since 1.43.0 */ public OkHttpChannelBuilder tlsConnectionSpec( String[] tlsVersions, String[] cipherSuites) { Preconditions.checkState(!freezeSecurityConfiguration, "Cannot change security when using ChannelCredentials"); Preconditions.checkNotNull(tlsVersions, "tls versions must not null"); Preconditions.checkNotNull(cipherSuites, "ciphers must not null"); this.connectionSpec = new ConnectionSpec.Builder(true) .supportsTlsExtensions(true) .tlsVersions(tlsVersions) .cipherSuites(cipherSuites) .build(); return this; } /** Sets the negotiation type for the HTTP/2 connection to plaintext. */ @Override public OkHttpChannelBuilder usePlaintext() { Preconditions.checkState(!freezeSecurityConfiguration, "Cannot change security when using ChannelCredentials"); negotiationType = NegotiationType.PLAINTEXT; return this; } /** * Sets the negotiation type for the HTTP/2 connection to TLS (this is the default). * *

With TLS enabled, a default {@link SSLSocketFactory} is created using the best {@link * java.security.Provider} available and is NOT based on {@link SSLSocketFactory#getDefault}. To * more precisely control the TLS configuration call {@link #sslSocketFactory(SSLSocketFactory)} * to override the socket factory used. */ @Override public OkHttpChannelBuilder useTransportSecurity() { Preconditions.checkState(!freezeSecurityConfiguration, "Cannot change security when using ChannelCredentials"); negotiationType = NegotiationType.TLS; return this; } /** * Provides a custom scheduled executor service. * *

It's an optional parameter. If the user has not provided a scheduled executor service when * the channel is built, the builder will use a static cached thread pool. * * @return this * * @since 1.11.0 */ public OkHttpChannelBuilder scheduledExecutorService( ScheduledExecutorService scheduledExecutorService) { this.scheduledExecutorServicePool = new FixedObjectPool<>(checkNotNull(scheduledExecutorService, "scheduledExecutorService")); return this; } /** * Sets the maximum size of metadata allowed to be received. {@code Integer.MAX_VALUE} disables * the enforcement. Defaults to no limit ({@code Integer.MAX_VALUE}). * *

The implementation does not currently limit memory usage; this value is checked only after * the metadata is decoded from the wire. It does prevent large metadata from being passed to the * application. * * @param bytes the maximum size of received metadata * @return this * @throws IllegalArgumentException if bytes is non-positive * @since 1.17.0 */ @Override public OkHttpChannelBuilder maxInboundMetadataSize(int bytes) { Preconditions.checkArgument(bytes > 0, "maxInboundMetadataSize must be > 0"); this.maxInboundMetadataSize = bytes; return this; } /** * Sets the maximum message size allowed for a single gRPC frame. If an inbound messages * larger than this limit is received it will not be processed and the RPC will fail with * RESOURCE_EXHAUSTED. */ @Override public OkHttpChannelBuilder maxInboundMessageSize(int max) { Preconditions.checkArgument(max >= 0, "negative max"); maxInboundMessageSize = max; return this; } OkHttpTransportFactory buildTransportFactory() { boolean enableKeepAlive = keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED; return new OkHttpTransportFactory( transportExecutorPool, scheduledExecutorServicePool, socketFactory, createSslSocketFactory(), hostnameVerifier, connectionSpec, maxInboundMessageSize, enableKeepAlive, keepAliveTimeNanos, keepAliveTimeoutNanos, flowControlWindow, keepAliveWithoutCalls, maxInboundMetadataSize, transportTracerFactory, useGetForSafeMethods); } OkHttpChannelBuilder disableCheckAuthority() { this.managedChannelImplBuilder.disableCheckAuthority(); return this; } OkHttpChannelBuilder enableCheckAuthority() { this.managedChannelImplBuilder.enableCheckAuthority(); return this; } int getDefaultPort() { switch (negotiationType) { case PLAINTEXT: return GrpcUtil.DEFAULT_PORT_PLAINTEXT; case TLS: return GrpcUtil.DEFAULT_PORT_SSL; default: throw new AssertionError(negotiationType + " not handled"); } } void setStatsEnabled(boolean value) { this.managedChannelImplBuilder.setStatsEnabled(value); } @VisibleForTesting @Nullable SSLSocketFactory createSslSocketFactory() { switch (negotiationType) { case TLS: try { if (sslSocketFactory == null) { SSLContext sslContext = SSLContext.getInstance("Default", Platform.get().getProvider()); sslSocketFactory = sslContext.getSocketFactory(); } return sslSocketFactory; } catch (GeneralSecurityException gse) { throw new RuntimeException("TLS Provider failure", gse); } case PLAINTEXT: return null; default: throw new RuntimeException("Unknown negotiation type: " + negotiationType); } } private static final EnumSet understoodTlsFeatures = EnumSet.of( TlsChannelCredentials.Feature.MTLS, TlsChannelCredentials.Feature.CUSTOM_MANAGERS); static SslSocketFactoryResult sslSocketFactoryFrom(ChannelCredentials creds) { if (creds instanceof TlsChannelCredentials) { TlsChannelCredentials tlsCreds = (TlsChannelCredentials) creds; Set incomprehensible = tlsCreds.incomprehensible(understoodTlsFeatures); if (!incomprehensible.isEmpty()) { return SslSocketFactoryResult.error( "TLS features not understood: " + incomprehensible); } KeyManager[] km = null; if (tlsCreds.getKeyManagers() != null) { km = tlsCreds.getKeyManagers().toArray(new KeyManager[0]); } else if (tlsCreds.getPrivateKey() != null) { if (tlsCreds.getPrivateKeyPassword() != null) { return SslSocketFactoryResult.error("byte[]-based private key with password unsupported. " + "Use unencrypted file or KeyManager"); } try { km = createKeyManager(tlsCreds.getCertificateChain(), tlsCreds.getPrivateKey()); } catch (GeneralSecurityException gse) { log.log(Level.FINE, "Exception loading private key from credential", gse); return SslSocketFactoryResult.error("Unable to load private key: " + gse.getMessage()); } } // else don't have a client cert TrustManager[] tm = null; if (tlsCreds.getTrustManagers() != null) { tm = tlsCreds.getTrustManagers().toArray(new TrustManager[0]); } else if (tlsCreds.getRootCertificates() != null) { try { tm = createTrustManager(tlsCreds.getRootCertificates()); } catch (GeneralSecurityException gse) { log.log(Level.FINE, "Exception loading root certificates from credential", gse); return SslSocketFactoryResult.error( "Unable to load root certificates: " + gse.getMessage()); } } // else use system default SSLContext sslContext; try { sslContext = SSLContext.getInstance("TLS", Platform.get().getProvider()); sslContext.init(km, tm, null); } catch (GeneralSecurityException gse) { throw new RuntimeException("TLS Provider failure", gse); } return SslSocketFactoryResult.factory(sslContext.getSocketFactory()); } else if (creds instanceof InsecureChannelCredentials) { return SslSocketFactoryResult.plaintext(); } else if (creds instanceof CompositeChannelCredentials) { CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds; return sslSocketFactoryFrom(compCreds.getChannelCredentials()) .withCallCredentials(compCreds.getCallCredentials()); } else if (creds instanceof SslSocketFactoryChannelCredentials.ChannelCredentials) { SslSocketFactoryChannelCredentials.ChannelCredentials factoryCreds = (SslSocketFactoryChannelCredentials.ChannelCredentials) creds; return SslSocketFactoryResult.factory(factoryCreds.getFactory()); } else if (creds instanceof ChoiceChannelCredentials) { ChoiceChannelCredentials choiceCreds = (ChoiceChannelCredentials) creds; StringBuilder error = new StringBuilder(); for (ChannelCredentials innerCreds : choiceCreds.getCredentialsList()) { SslSocketFactoryResult result = sslSocketFactoryFrom(innerCreds); if (result.error == null) { return result; } error.append(", "); error.append(result.error); } return SslSocketFactoryResult.error(error.substring(2)); } else { return SslSocketFactoryResult.error( "Unsupported credential type: " + creds.getClass().getName()); } } static KeyManager[] createKeyManager(byte[] certChain, byte[] privateKey) throws GeneralSecurityException { X509Certificate[] chain; ByteArrayInputStream inCertChain = new ByteArrayInputStream(certChain); try { chain = CertificateUtils.getX509Certificates(inCertChain); } finally { GrpcUtil.closeQuietly(inCertChain); } PrivateKey key; ByteArrayInputStream inPrivateKey = new ByteArrayInputStream(privateKey); try { key = CertificateUtils.getPrivateKey(inPrivateKey); } catch (IOException uee) { throw new GeneralSecurityException("Unable to decode private key", uee); } finally { GrpcUtil.closeQuietly(inPrivateKey); } KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); try { ks.load(null, null); } catch (IOException ex) { // Shouldn't really happen, as we're not loading any data. throw new GeneralSecurityException(ex); } ks.setKeyEntry("key", key, new char[0], chain); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(ks, new char[0]); return keyManagerFactory.getKeyManagers(); } static TrustManager[] createTrustManager(byte[] rootCerts) throws GeneralSecurityException { KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); try { ks.load(null, null); } catch (IOException ex) { // Shouldn't really happen, as we're not loading any data. throw new GeneralSecurityException(ex); } X509Certificate[] certs; ByteArrayInputStream in = new ByteArrayInputStream(rootCerts); try { certs = CertificateUtils.getX509Certificates(in); } finally { GrpcUtil.closeQuietly(in); } for (X509Certificate cert : certs) { X500Principal principal = cert.getSubjectX500Principal(); ks.setCertificateEntry(principal.getName("RFC2253"), cert); } TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(ks); return trustManagerFactory.getTrustManagers(); } static Collection> getSupportedSocketAddressTypes() { return Collections.singleton(InetSocketAddress.class); } static final class SslSocketFactoryResult { /** {@code null} implies plaintext if {@code error == null}. */ public final SSLSocketFactory factory; public final CallCredentials callCredentials; public final String error; private SslSocketFactoryResult(SSLSocketFactory factory, CallCredentials creds, String error) { this.factory = factory; this.callCredentials = creds; this.error = error; } public static SslSocketFactoryResult error(String error) { return new SslSocketFactoryResult( null, null, Preconditions.checkNotNull(error, "error")); } public static SslSocketFactoryResult plaintext() { return new SslSocketFactoryResult(null, null, null); } public static SslSocketFactoryResult factory( SSLSocketFactory factory) { return new SslSocketFactoryResult( Preconditions.checkNotNull(factory, "factory"), null, null); } public SslSocketFactoryResult withCallCredentials(CallCredentials callCreds) { Preconditions.checkNotNull(callCreds, "callCreds"); if (error != null) { return this; } if (this.callCredentials != null) { callCreds = new CompositeCallCredentials(this.callCredentials, callCreds); } return new SslSocketFactoryResult(factory, callCreds, null); } } /** * Creates OkHttp transports. Exposed for internal use, as it should be private. */ @Internal static final class OkHttpTransportFactory implements ClientTransportFactory { private final ObjectPool executorPool; final Executor executor; private final ObjectPool scheduledExecutorServicePool; final ScheduledExecutorService scheduledExecutorService; final TransportTracer.Factory transportTracerFactory; final SocketFactory socketFactory; @Nullable final SSLSocketFactory sslSocketFactory; @Nullable final HostnameVerifier hostnameVerifier; final ConnectionSpec connectionSpec; final int maxMessageSize; private final boolean enableKeepAlive; private final long keepAliveTimeNanos; private final AtomicBackoff keepAliveBackoff; private final long keepAliveTimeoutNanos; final int flowControlWindow; private final boolean keepAliveWithoutCalls; final int maxInboundMetadataSize; final boolean useGetForSafeMethods; private boolean closed; private OkHttpTransportFactory( ObjectPool executorPool, ObjectPool 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) { this.executorPool = executorPool; this.executor = executorPool.getObject(); this.scheduledExecutorServicePool = scheduledExecutorServicePool; this.scheduledExecutorService = scheduledExecutorServicePool.getObject(); this.socketFactory = socketFactory; this.sslSocketFactory = sslSocketFactory; this.hostnameVerifier = hostnameVerifier; this.connectionSpec = connectionSpec; this.maxMessageSize = maxMessageSize; this.enableKeepAlive = enableKeepAlive; this.keepAliveTimeNanos = keepAliveTimeNanos; this.keepAliveBackoff = new AtomicBackoff("keepalive time nanos", keepAliveTimeNanos); this.keepAliveTimeoutNanos = keepAliveTimeoutNanos; this.flowControlWindow = flowControlWindow; this.keepAliveWithoutCalls = keepAliveWithoutCalls; this.maxInboundMetadataSize = maxInboundMetadataSize; this.useGetForSafeMethods = useGetForSafeMethods; this.transportTracerFactory = Preconditions.checkNotNull(transportTracerFactory, "transportTracerFactory"); } @Override public ConnectionClientTransport newClientTransport( SocketAddress addr, ClientTransportOptions options, ChannelLogger channelLogger) { if (closed) { throw new IllegalStateException("The transport factory is closed."); } final AtomicBackoff.State keepAliveTimeNanosState = keepAliveBackoff.getState(); Runnable tooManyPingsRunnable = new Runnable() { @Override public void run() { keepAliveTimeNanosState.backoff(); } }; InetSocketAddress inetSocketAddr = (InetSocketAddress) addr; // TODO(carl-mastrangelo): Pass channelLogger in. OkHttpClientTransport transport = new OkHttpClientTransport( this, inetSocketAddr, options.getAuthority(), options.getUserAgent(), options.getEagAttributes(), options.getHttpConnectProxiedSocketAddress(), tooManyPingsRunnable); if (enableKeepAlive) { transport.enableKeepAlive( true, keepAliveTimeNanosState.get(), keepAliveTimeoutNanos, keepAliveWithoutCalls); } return transport; } @Override public ScheduledExecutorService getScheduledExecutorService() { return scheduledExecutorService; } @Nullable @CheckReturnValue @Override public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) { SslSocketFactoryResult result = sslSocketFactoryFrom(channelCreds); if (result.error != null) { return null; } ClientTransportFactory factory = new OkHttpTransportFactory( executorPool, scheduledExecutorServicePool, socketFactory, result.factory, hostnameVerifier, connectionSpec, maxMessageSize, enableKeepAlive, keepAliveTimeNanos, keepAliveTimeoutNanos, flowControlWindow, keepAliveWithoutCalls, maxInboundMetadataSize, transportTracerFactory, useGetForSafeMethods); return new SwapChannelCredentialsResult(factory, result.callCredentials); } @Override public void close() { if (closed) { return; } closed = true; executorPool.returnObject(executor); scheduledExecutorServicePool.returnObject(scheduledExecutorService); } @Override public Collection> getSupportedSocketAddressTypes() { return OkHttpChannelBuilder.getSupportedSocketAddressTypes(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy