• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 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.netty;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.base.Preconditions.checkState;
21 
22 import com.google.common.annotations.VisibleForTesting;
23 import com.google.common.base.Preconditions;
24 import com.google.errorprone.annotations.ForOverride;
25 import io.grpc.Attributes;
26 import io.grpc.CallCredentials;
27 import io.grpc.ChannelCredentials;
28 import io.grpc.ChannelLogger;
29 import io.grpc.ChannelLogger.ChannelLogLevel;
30 import io.grpc.ChoiceChannelCredentials;
31 import io.grpc.ChoiceServerCredentials;
32 import io.grpc.CompositeCallCredentials;
33 import io.grpc.CompositeChannelCredentials;
34 import io.grpc.Grpc;
35 import io.grpc.InsecureChannelCredentials;
36 import io.grpc.InsecureServerCredentials;
37 import io.grpc.InternalChannelz.Security;
38 import io.grpc.InternalChannelz.Tls;
39 import io.grpc.SecurityLevel;
40 import io.grpc.ServerCredentials;
41 import io.grpc.Status;
42 import io.grpc.TlsChannelCredentials;
43 import io.grpc.TlsServerCredentials;
44 import io.grpc.internal.GrpcAttributes;
45 import io.grpc.internal.GrpcUtil;
46 import io.grpc.internal.ObjectPool;
47 import io.netty.channel.ChannelDuplexHandler;
48 import io.netty.channel.ChannelFutureListener;
49 import io.netty.channel.ChannelHandler;
50 import io.netty.channel.ChannelHandlerContext;
51 import io.netty.channel.ChannelInboundHandlerAdapter;
52 import io.netty.handler.codec.http.DefaultHttpRequest;
53 import io.netty.handler.codec.http.HttpClientCodec;
54 import io.netty.handler.codec.http.HttpClientUpgradeHandler;
55 import io.netty.handler.codec.http.HttpHeaderNames;
56 import io.netty.handler.codec.http.HttpMethod;
57 import io.netty.handler.codec.http.HttpVersion;
58 import io.netty.handler.codec.http2.Http2ClientUpgradeCodec;
59 import io.netty.handler.proxy.HttpProxyHandler;
60 import io.netty.handler.proxy.ProxyConnectionEvent;
61 import io.netty.handler.ssl.OpenSsl;
62 import io.netty.handler.ssl.OpenSslEngine;
63 import io.netty.handler.ssl.SslContext;
64 import io.netty.handler.ssl.SslContextBuilder;
65 import io.netty.handler.ssl.SslHandler;
66 import io.netty.handler.ssl.SslHandshakeCompletionEvent;
67 import io.netty.handler.ssl.SslProvider;
68 import io.netty.util.AsciiString;
69 import java.io.ByteArrayInputStream;
70 import java.net.SocketAddress;
71 import java.net.URI;
72 import java.nio.channels.ClosedChannelException;
73 import java.util.Arrays;
74 import java.util.EnumSet;
75 import java.util.Set;
76 import java.util.concurrent.Executor;
77 import java.util.logging.Level;
78 import java.util.logging.Logger;
79 import javax.annotation.Nullable;
80 import javax.net.ssl.SSLEngine;
81 import javax.net.ssl.SSLException;
82 import javax.net.ssl.SSLParameters;
83 import javax.net.ssl.SSLSession;
84 
85 /**
86  * Common {@link ProtocolNegotiator}s used by gRPC.
87  */
88 final class ProtocolNegotiators {
89   private static final Logger log = Logger.getLogger(ProtocolNegotiators.class.getName());
90   private static final EnumSet<TlsChannelCredentials.Feature> understoodTlsFeatures =
91       EnumSet.of(
92           TlsChannelCredentials.Feature.MTLS, TlsChannelCredentials.Feature.CUSTOM_MANAGERS);
93   private static final EnumSet<TlsServerCredentials.Feature> understoodServerTlsFeatures =
94       EnumSet.of(
95           TlsServerCredentials.Feature.MTLS, TlsServerCredentials.Feature.CUSTOM_MANAGERS);
96 
97 
ProtocolNegotiators()98   private ProtocolNegotiators() {
99   }
100 
from(ChannelCredentials creds)101   public static FromChannelCredentialsResult from(ChannelCredentials creds) {
102     if (creds instanceof TlsChannelCredentials) {
103       TlsChannelCredentials tlsCreds = (TlsChannelCredentials) creds;
104       Set<TlsChannelCredentials.Feature> incomprehensible =
105           tlsCreds.incomprehensible(understoodTlsFeatures);
106       if (!incomprehensible.isEmpty()) {
107         return FromChannelCredentialsResult.error(
108             "TLS features not understood: " + incomprehensible);
109       }
110       SslContextBuilder builder = GrpcSslContexts.forClient();
111       if (tlsCreds.getKeyManagers() != null) {
112         builder.keyManager(new FixedKeyManagerFactory(tlsCreds.getKeyManagers()));
113       } else if (tlsCreds.getPrivateKey() != null) {
114         builder.keyManager(
115             new ByteArrayInputStream(tlsCreds.getCertificateChain()),
116             new ByteArrayInputStream(tlsCreds.getPrivateKey()),
117             tlsCreds.getPrivateKeyPassword());
118       }
119       if (tlsCreds.getTrustManagers() != null) {
120         builder.trustManager(new FixedTrustManagerFactory(tlsCreds.getTrustManagers()));
121       } else if (tlsCreds.getRootCertificates() != null) {
122         builder.trustManager(new ByteArrayInputStream(tlsCreds.getRootCertificates()));
123       } // else use system default
124       try {
125         return FromChannelCredentialsResult.negotiator(tlsClientFactory(builder.build()));
126       } catch (SSLException ex) {
127         log.log(Level.FINE, "Exception building SslContext", ex);
128         return FromChannelCredentialsResult.error(
129             "Unable to create SslContext: " + ex.getMessage());
130       }
131 
132     } else if (creds instanceof InsecureChannelCredentials) {
133       return FromChannelCredentialsResult.negotiator(plaintextClientFactory());
134 
135     } else if (creds instanceof CompositeChannelCredentials) {
136       CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds;
137       return from(compCreds.getChannelCredentials())
138           .withCallCredentials(compCreds.getCallCredentials());
139 
140     } else if (creds instanceof NettyChannelCredentials) {
141       NettyChannelCredentials nettyCreds = (NettyChannelCredentials) creds;
142       return FromChannelCredentialsResult.negotiator(nettyCreds.getNegotiator());
143 
144     } else if (creds instanceof ChoiceChannelCredentials) {
145       ChoiceChannelCredentials choiceCreds = (ChoiceChannelCredentials) creds;
146       StringBuilder error = new StringBuilder();
147       for (ChannelCredentials innerCreds : choiceCreds.getCredentialsList()) {
148         FromChannelCredentialsResult result = from(innerCreds);
149         if (result.error == null) {
150           return result;
151         }
152         error.append(", ");
153         error.append(result.error);
154       }
155       return FromChannelCredentialsResult.error(error.substring(2));
156 
157     } else {
158       return FromChannelCredentialsResult.error(
159           "Unsupported credential type: " + creds.getClass().getName());
160     }
161   }
162 
163   public static final class FromChannelCredentialsResult {
164     public final ProtocolNegotiator.ClientFactory negotiator;
165     public final CallCredentials callCredentials;
166     public final String error;
167 
FromChannelCredentialsResult(ProtocolNegotiator.ClientFactory negotiator, CallCredentials creds, String error)168     private FromChannelCredentialsResult(ProtocolNegotiator.ClientFactory negotiator,
169         CallCredentials creds, String error) {
170       this.negotiator = negotiator;
171       this.callCredentials = creds;
172       this.error = error;
173     }
174 
error(String error)175     public static FromChannelCredentialsResult error(String error) {
176       return new FromChannelCredentialsResult(
177           null, null, Preconditions.checkNotNull(error, "error"));
178     }
179 
negotiator( ProtocolNegotiator.ClientFactory factory)180     public static FromChannelCredentialsResult negotiator(
181         ProtocolNegotiator.ClientFactory factory) {
182       return new FromChannelCredentialsResult(
183           Preconditions.checkNotNull(factory, "factory"), null, null);
184     }
185 
withCallCredentials(CallCredentials callCreds)186     public FromChannelCredentialsResult withCallCredentials(CallCredentials callCreds) {
187       Preconditions.checkNotNull(callCreds, "callCreds");
188       if (error != null) {
189         return this;
190       }
191       if (this.callCredentials != null) {
192         callCreds = new CompositeCallCredentials(this.callCredentials, callCreds);
193       }
194       return new FromChannelCredentialsResult(negotiator, callCreds, null);
195     }
196   }
197 
from(ServerCredentials creds)198   public static FromServerCredentialsResult from(ServerCredentials creds) {
199     if (creds instanceof TlsServerCredentials) {
200       TlsServerCredentials tlsCreds = (TlsServerCredentials) creds;
201       Set<TlsServerCredentials.Feature> incomprehensible =
202           tlsCreds.incomprehensible(understoodServerTlsFeatures);
203       if (!incomprehensible.isEmpty()) {
204         return FromServerCredentialsResult.error(
205             "TLS features not understood: " + incomprehensible);
206       }
207       SslContextBuilder builder;
208       if (tlsCreds.getKeyManagers() != null) {
209         builder = GrpcSslContexts.configure(SslContextBuilder.forServer(
210             new FixedKeyManagerFactory(tlsCreds.getKeyManagers())));
211       } else if (tlsCreds.getPrivateKey() != null) {
212         builder = GrpcSslContexts.forServer(
213             new ByteArrayInputStream(tlsCreds.getCertificateChain()),
214             new ByteArrayInputStream(tlsCreds.getPrivateKey()),
215             tlsCreds.getPrivateKeyPassword());
216       } else {
217         throw new AssertionError("BUG! No key");
218       }
219       if (tlsCreds.getTrustManagers() != null) {
220         builder.trustManager(new FixedTrustManagerFactory(tlsCreds.getTrustManagers()));
221       } else if (tlsCreds.getRootCertificates() != null) {
222         builder.trustManager(new ByteArrayInputStream(tlsCreds.getRootCertificates()));
223       } // else use system default
224       switch (tlsCreds.getClientAuth()) {
225         case OPTIONAL:
226           builder.clientAuth(io.netty.handler.ssl.ClientAuth.OPTIONAL);
227           break;
228 
229         case REQUIRE:
230           builder.clientAuth(io.netty.handler.ssl.ClientAuth.REQUIRE);
231           break;
232 
233         case NONE:
234           builder.clientAuth(io.netty.handler.ssl.ClientAuth.NONE);
235           break;
236 
237         default:
238           return FromServerCredentialsResult.error(
239               "Unknown TlsServerCredentials.ClientAuth value: " + tlsCreds.getClientAuth());
240       }
241       SslContext sslContext;
242       try {
243         sslContext = builder.build();
244       } catch (SSLException ex) {
245         throw new IllegalArgumentException(
246             "Unexpected error converting ServerCredentials to Netty SslContext", ex);
247       }
248       return FromServerCredentialsResult.negotiator(serverTlsFactory(sslContext));
249 
250     } else if (creds instanceof InsecureServerCredentials) {
251       return FromServerCredentialsResult.negotiator(serverPlaintextFactory());
252 
253     } else if (creds instanceof NettyServerCredentials) {
254       NettyServerCredentials nettyCreds = (NettyServerCredentials) creds;
255       return FromServerCredentialsResult.negotiator(nettyCreds.getNegotiator());
256 
257     } else if (creds instanceof ChoiceServerCredentials) {
258       ChoiceServerCredentials choiceCreds = (ChoiceServerCredentials) creds;
259       StringBuilder error = new StringBuilder();
260       for (ServerCredentials innerCreds : choiceCreds.getCredentialsList()) {
261         FromServerCredentialsResult result = from(innerCreds);
262         if (result.error == null) {
263           return result;
264         }
265         error.append(", ");
266         error.append(result.error);
267       }
268       return FromServerCredentialsResult.error(error.substring(2));
269 
270     } else {
271       return FromServerCredentialsResult.error(
272           "Unsupported credential type: " + creds.getClass().getName());
273     }
274   }
275 
276   public static final class FromServerCredentialsResult {
277     public final ProtocolNegotiator.ServerFactory negotiator;
278     public final String error;
279 
FromServerCredentialsResult(ProtocolNegotiator.ServerFactory negotiator, String error)280     private FromServerCredentialsResult(ProtocolNegotiator.ServerFactory negotiator, String error) {
281       this.negotiator = negotiator;
282       this.error = error;
283     }
284 
error(String error)285     public static FromServerCredentialsResult error(String error) {
286       return new FromServerCredentialsResult(null, Preconditions.checkNotNull(error, "error"));
287     }
288 
negotiator(ProtocolNegotiator.ServerFactory factory)289     public static FromServerCredentialsResult negotiator(ProtocolNegotiator.ServerFactory factory) {
290       return new FromServerCredentialsResult(Preconditions.checkNotNull(factory, "factory"), null);
291     }
292   }
293 
fixedServerFactory( ProtocolNegotiator negotiator)294   public static ProtocolNegotiator.ServerFactory fixedServerFactory(
295       ProtocolNegotiator negotiator) {
296     return new FixedProtocolNegotiatorServerFactory(negotiator);
297   }
298 
299   private static final class FixedProtocolNegotiatorServerFactory
300       implements ProtocolNegotiator.ServerFactory {
301     private final ProtocolNegotiator protocolNegotiator;
302 
FixedProtocolNegotiatorServerFactory(ProtocolNegotiator protocolNegotiator)303     public FixedProtocolNegotiatorServerFactory(ProtocolNegotiator protocolNegotiator) {
304       this.protocolNegotiator =
305           Preconditions.checkNotNull(protocolNegotiator, "protocolNegotiator");
306     }
307 
308     @Override
newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool)309     public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
310       return protocolNegotiator;
311     }
312   }
313 
314   /**
315    * Create a server plaintext handler for gRPC.
316    */
serverPlaintext()317   public static ProtocolNegotiator serverPlaintext() {
318     return new PlaintextProtocolNegotiator();
319   }
320 
321   /**
322    * Create a server plaintext handler factory for gRPC.
323    */
serverPlaintextFactory()324   public static ProtocolNegotiator.ServerFactory serverPlaintextFactory() {
325     return new PlaintextProtocolNegotiatorServerFactory();
326   }
327 
328   @VisibleForTesting
329   static final class PlaintextProtocolNegotiatorServerFactory
330       implements ProtocolNegotiator.ServerFactory {
331     @Override
newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool)332     public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
333       return serverPlaintext();
334     }
335   }
336 
serverTlsFactory(SslContext sslContext)337   public static ProtocolNegotiator.ServerFactory serverTlsFactory(SslContext sslContext) {
338     return new TlsProtocolNegotiatorServerFactory(sslContext);
339   }
340 
341   @VisibleForTesting
342   static final class TlsProtocolNegotiatorServerFactory
343       implements ProtocolNegotiator.ServerFactory {
344     private final SslContext sslContext;
345 
TlsProtocolNegotiatorServerFactory(SslContext sslContext)346     public TlsProtocolNegotiatorServerFactory(SslContext sslContext) {
347       this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
348     }
349 
350     @Override
newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool)351     public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
352       return serverTls(sslContext, offloadExecutorPool);
353     }
354   }
355 
356   /**
357    * Create a server TLS handler for HTTP/2 capable of using ALPN/NPN.
358    * @param executorPool a dedicated {@link Executor} pool for time-consuming TLS tasks
359    */
serverTls(final SslContext sslContext, final ObjectPool<? extends Executor> executorPool)360   public static ProtocolNegotiator serverTls(final SslContext sslContext,
361       final ObjectPool<? extends Executor> executorPool) {
362     Preconditions.checkNotNull(sslContext, "sslContext");
363     final Executor executor;
364     if (executorPool != null) {
365       // The handlers here can out-live the {@link ProtocolNegotiator}.
366       // To keep their own reference to executor from executorPool, we use an extra (unused)
367       // reference here forces the executor to stay alive, which prevents it from being re-created
368       // for every connection.
369       executor = executorPool.getObject();
370     } else {
371       executor = null;
372     }
373     return new ProtocolNegotiator() {
374       @Override
375       public ChannelHandler newHandler(GrpcHttp2ConnectionHandler handler) {
376         ChannelHandler gnh = new GrpcNegotiationHandler(handler);
377         ChannelHandler sth = new ServerTlsHandler(gnh, sslContext, executorPool);
378         return new WaitUntilActiveHandler(sth, handler.getNegotiationLogger());
379       }
380 
381       @Override
382       public void close() {
383         if (executorPool != null && executor != null) {
384           executorPool.returnObject(executor);
385         }
386       }
387 
388       @Override
389       public AsciiString scheme() {
390         return Utils.HTTPS;
391       }
392     };
393   }
394 
395   /**
396    * Create a server TLS handler for HTTP/2 capable of using ALPN/NPN.
397    */
398   public static ProtocolNegotiator serverTls(final SslContext sslContext) {
399     return serverTls(sslContext, null);
400   }
401 
402   static final class ServerTlsHandler extends ChannelInboundHandlerAdapter {
403     private Executor executor;
404     private final ChannelHandler next;
405     private final SslContext sslContext;
406 
407     private ProtocolNegotiationEvent pne = ProtocolNegotiationEvent.DEFAULT;
408 
409     ServerTlsHandler(ChannelHandler next,
410         SslContext sslContext,
411         final ObjectPool<? extends Executor> executorPool) {
412       this.sslContext = checkNotNull(sslContext, "sslContext");
413       this.next = checkNotNull(next, "next");
414       if (executorPool != null) {
415         this.executor = executorPool.getObject();
416       }
417     }
418 
419     @Override
420     public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
421       super.handlerAdded(ctx);
422       SSLEngine sslEngine = sslContext.newEngine(ctx.alloc());
423       ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
424           ? new SslHandler(sslEngine, false, this.executor)
425           : new SslHandler(sslEngine, false));
426     }
427 
428     @Override
429     public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
430       if (evt instanceof ProtocolNegotiationEvent) {
431         pne = (ProtocolNegotiationEvent) evt;
432       } else if (evt instanceof SslHandshakeCompletionEvent) {
433         SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
434         if (!handshakeEvent.isSuccess()) {
435           logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed for new client.", null);
436           ctx.fireExceptionCaught(handshakeEvent.cause());
437           return;
438         }
439         SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
440         if (!sslContext.applicationProtocolNegotiator().protocols().contains(
441                 sslHandler.applicationProtocol())) {
442           logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed for new client.", null);
443           ctx.fireExceptionCaught(unavailableException(
444               "Failed protocol negotiation: Unable to find compatible protocol"));
445           return;
446         }
447         ctx.pipeline().replace(ctx.name(), null, next);
448         fireProtocolNegotiationEvent(ctx, sslHandler.engine().getSession());
449       } else {
450         super.userEventTriggered(ctx, evt);
451       }
452     }
453 
454     private void fireProtocolNegotiationEvent(ChannelHandlerContext ctx, SSLSession session) {
455       Security security = new Security(new Tls(session));
456       Attributes attrs = pne.getAttributes().toBuilder()
457           .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
458           .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
459           .build();
460       ctx.fireUserEventTriggered(pne.withAttributes(attrs).withSecurity(security));
461     }
462   }
463 
464   /**
465    * Returns a {@link ProtocolNegotiator} that does HTTP CONNECT proxy negotiation.
466    */
467   public static ProtocolNegotiator httpProxy(final SocketAddress proxyAddress,
468       final @Nullable String proxyUsername, final @Nullable String proxyPassword,
469       final ProtocolNegotiator negotiator) {
470     checkNotNull(negotiator, "negotiator");
471     checkNotNull(proxyAddress, "proxyAddress");
472     final AsciiString scheme = negotiator.scheme();
473     class ProxyNegotiator implements ProtocolNegotiator {
474       @Override
475       public ChannelHandler newHandler(GrpcHttp2ConnectionHandler http2Handler) {
476         ChannelHandler protocolNegotiationHandler = negotiator.newHandler(http2Handler);
477         ChannelLogger negotiationLogger = http2Handler.getNegotiationLogger();
478         return new ProxyProtocolNegotiationHandler(
479             proxyAddress, proxyUsername, proxyPassword, protocolNegotiationHandler,
480             negotiationLogger);
481       }
482 
483       @Override
484       public AsciiString scheme() {
485         return scheme;
486       }
487 
488       // This method is not normally called, because we use httpProxy on a per-connection basis in
489       // NettyChannelBuilder. Instead, we expect `negotiator' to be closed by NettyTransportFactory.
490       @Override
491       public void close() {
492         negotiator.close();
493       }
494     }
495 
496     return new ProxyNegotiator();
497   }
498 
499   /**
500    * A Proxy handler follows {@link ProtocolNegotiationHandler} pattern. Upon successful proxy
501    * connection, this handler will install {@code next} handler which should be a handler from
502    * other type of {@link ProtocolNegotiator} to continue negotiating protocol using proxy.
503    */
504   static final class ProxyProtocolNegotiationHandler extends ProtocolNegotiationHandler {
505 
506     private final SocketAddress address;
507     @Nullable private final String userName;
508     @Nullable private final String password;
509 
510     public ProxyProtocolNegotiationHandler(
511         SocketAddress address,
512         @Nullable String userName,
513         @Nullable String password,
514         ChannelHandler next,
515         ChannelLogger negotiationLogger) {
516       super(next, negotiationLogger);
517       this.address = checkNotNull(address, "address");
518       this.userName = userName;
519       this.password = password;
520     }
521 
522     @Override
523     protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
524       HttpProxyHandler nettyProxyHandler;
525       if (userName == null || password == null) {
526         nettyProxyHandler = new HttpProxyHandler(address);
527       } else {
528         nettyProxyHandler = new HttpProxyHandler(address, userName, password);
529       }
530       ctx.pipeline().addBefore(ctx.name(), /* name= */ null, nettyProxyHandler);
531     }
532 
533     @Override
534     protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
535       if (evt instanceof ProxyConnectionEvent) {
536         fireProtocolNegotiationEvent(ctx);
537       } else {
538         super.userEventTriggered(ctx, evt);
539       }
540     }
541   }
542 
543   static final class ClientTlsProtocolNegotiator implements ProtocolNegotiator {
544 
545     public ClientTlsProtocolNegotiator(SslContext sslContext,
546         ObjectPool<? extends Executor> executorPool) {
547       this.sslContext = checkNotNull(sslContext, "sslContext");
548       this.executorPool = executorPool;
549       if (this.executorPool != null) {
550         this.executor = this.executorPool.getObject();
551       }
552     }
553 
554     private final SslContext sslContext;
555     private final ObjectPool<? extends Executor> executorPool;
556     private Executor executor;
557 
558     @Override
559     public AsciiString scheme() {
560       return Utils.HTTPS;
561     }
562 
563     @Override
564     public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
565       ChannelHandler gnh = new GrpcNegotiationHandler(grpcHandler);
566       ChannelLogger negotiationLogger = grpcHandler.getNegotiationLogger();
567       ChannelHandler cth = new ClientTlsHandler(gnh, sslContext, grpcHandler.getAuthority(),
568           this.executor, negotiationLogger);
569       return new WaitUntilActiveHandler(cth, negotiationLogger);
570     }
571 
572     @Override
573     public void close() {
574       if (this.executorPool != null && this.executor != null) {
575         this.executorPool.returnObject(this.executor);
576       }
577     }
578   }
579 
580   static final class ClientTlsHandler extends ProtocolNegotiationHandler {
581 
582     private final SslContext sslContext;
583     private final String host;
584     private final int port;
585     private Executor executor;
586 
587     ClientTlsHandler(ChannelHandler next, SslContext sslContext, String authority,
588         Executor executor, ChannelLogger negotiationLogger) {
589       super(next, negotiationLogger);
590       this.sslContext = checkNotNull(sslContext, "sslContext");
591       HostPort hostPort = parseAuthority(authority);
592       this.host = hostPort.host;
593       this.port = hostPort.port;
594       this.executor = executor;
595     }
596 
597     @Override
598     protected void handlerAdded0(ChannelHandlerContext ctx) {
599       SSLEngine sslEngine = sslContext.newEngine(ctx.alloc(), host, port);
600       SSLParameters sslParams = sslEngine.getSSLParameters();
601       sslParams.setEndpointIdentificationAlgorithm("HTTPS");
602       sslEngine.setSSLParameters(sslParams);
603       ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
604           ? new SslHandler(sslEngine, false, this.executor)
605           : new SslHandler(sslEngine, false));
606     }
607 
608     @Override
609     protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
610       if (evt instanceof SslHandshakeCompletionEvent) {
611         SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
612         if (handshakeEvent.isSuccess()) {
613           SslHandler handler = ctx.pipeline().get(SslHandler.class);
614           if (sslContext.applicationProtocolNegotiator().protocols()
615               .contains(handler.applicationProtocol())) {
616             // Successfully negotiated the protocol.
617             logSslEngineDetails(Level.FINER, ctx, "TLS negotiation succeeded.", null);
618             propagateTlsComplete(ctx, handler.engine().getSession());
619           } else {
620             Exception ex =
621                 unavailableException("Failed ALPN negotiation: Unable to find compatible protocol");
622             logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed.", ex);
623             ctx.fireExceptionCaught(ex);
624           }
625         } else {
626           Throwable t = handshakeEvent.cause();
627           if (t instanceof ClosedChannelException) {
628             // On channelInactive(), SslHandler creates its own ClosedChannelException and
629             // propagates it before the actual channelInactive(). So we assume here that any
630             // such exception is from channelInactive() and emulate the normal behavior of
631             // WriteBufferingAndExceptionHandler
632             t = Status.UNAVAILABLE
633                 .withDescription("Connection closed while performing TLS negotiation")
634                 .withCause(t)
635                 .asRuntimeException();
636           }
637           ctx.fireExceptionCaught(t);
638         }
639       } else {
640         super.userEventTriggered0(ctx, evt);
641       }
642     }
643 
644     private void propagateTlsComplete(ChannelHandlerContext ctx, SSLSession session) {
645       Security security = new Security(new Tls(session));
646       ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
647       Attributes attrs = existingPne.getAttributes().toBuilder()
648           .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
649           .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
650           .build();
651       replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs).withSecurity(security));
652       fireProtocolNegotiationEvent(ctx);
653     }
654   }
655 
656   @VisibleForTesting
657   static HostPort parseAuthority(String authority) {
658     URI uri = GrpcUtil.authorityToUri(Preconditions.checkNotNull(authority, "authority"));
659     String host;
660     int port;
661     if (uri.getHost() != null) {
662       host = uri.getHost();
663       port = uri.getPort();
664     } else {
665       /*
666        * Implementation note: We pick -1 as the port here rather than deriving it from the
667        * original socket address.  The SSL engine doesn't use this port number when contacting the
668        * remote server, but rather it is used for other things like SSL Session caching.  When an
669        * invalid authority is provided (like "bad_cert"), picking the original port and passing it
670        * in would mean that the port might used under the assumption that it was correct.   By
671        * using -1 here, it forces the SSL implementation to treat it as invalid.
672        */
673       host = authority;
674       port = -1;
675     }
676     return new HostPort(host, port);
677   }
678 
679   /**
680    * Returns a {@link ProtocolNegotiator} that ensures the pipeline is set up so that TLS will
681    * be negotiated, the {@code handler} is added and writes to the {@link io.netty.channel.Channel}
682    * may happen immediately, even before the TLS Handshake is complete.
683    * @param executorPool a dedicated {@link Executor} pool for time-consuming TLS tasks
684    */
685   public static ProtocolNegotiator tls(SslContext sslContext,
686       ObjectPool<? extends Executor> executorPool) {
687     return new ClientTlsProtocolNegotiator(sslContext, executorPool);
688   }
689 
690   /**
691    * Returns a {@link ProtocolNegotiator} that ensures the pipeline is set up so that TLS will
692    * be negotiated, the {@code handler} is added and writes to the {@link io.netty.channel.Channel}
693    * may happen immediately, even before the TLS Handshake is complete.
694    */
695   public static ProtocolNegotiator tls(SslContext sslContext) {
696     return tls(sslContext, null);
697   }
698 
699   public static ProtocolNegotiator.ClientFactory tlsClientFactory(SslContext sslContext) {
700     return new TlsProtocolNegotiatorClientFactory(sslContext);
701   }
702 
703   @VisibleForTesting
704   static final class TlsProtocolNegotiatorClientFactory
705       implements ProtocolNegotiator.ClientFactory {
706     private final SslContext sslContext;
707 
708     public TlsProtocolNegotiatorClientFactory(SslContext sslContext) {
709       this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
710     }
711 
712     @Override public ProtocolNegotiator newNegotiator() {
713       return tls(sslContext);
714     }
715 
716     @Override public int getDefaultPort() {
717       return GrpcUtil.DEFAULT_PORT_SSL;
718     }
719   }
720 
721   /** A tuple of (host, port). */
722   @VisibleForTesting
723   static final class HostPort {
724     final String host;
725     final int port;
726 
727     public HostPort(String host, int port) {
728       this.host = host;
729       this.port = port;
730     }
731   }
732 
733   /**
734    * Returns a {@link ProtocolNegotiator} used for upgrading to HTTP/2 from HTTP/1.x.
735    */
736   public static ProtocolNegotiator plaintextUpgrade() {
737     return new PlaintextUpgradeProtocolNegotiator();
738   }
739 
740   public static ProtocolNegotiator.ClientFactory plaintextUpgradeClientFactory() {
741     return new PlaintextUpgradeProtocolNegotiatorClientFactory();
742   }
743 
744   private static final class PlaintextUpgradeProtocolNegotiatorClientFactory
745       implements ProtocolNegotiator.ClientFactory {
746     @Override public ProtocolNegotiator newNegotiator() {
747       return plaintextUpgrade();
748     }
749 
750     @Override public int getDefaultPort() {
751       return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
752     }
753   }
754 
755   static final class PlaintextUpgradeProtocolNegotiator implements ProtocolNegotiator {
756 
757     @Override
758     public AsciiString scheme() {
759       return Utils.HTTP;
760     }
761 
762     @Override
763     public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
764       ChannelHandler upgradeHandler =
765           new Http2UpgradeAndGrpcHandler(grpcHandler.getAuthority(), grpcHandler);
766       return new WaitUntilActiveHandler(upgradeHandler, grpcHandler.getNegotiationLogger());
767     }
768 
769     @Override
770     public void close() {}
771   }
772 
773   /**
774    * Acts as a combination of Http2Upgrade and {@link GrpcNegotiationHandler}.  Unfortunately,
775    * this negotiator doesn't follow the pattern of "just one handler doing negotiation at a time."
776    * This is due to the tight coupling between the upgrade handler and the HTTP/2 handler.
777    */
778   static final class Http2UpgradeAndGrpcHandler extends ChannelInboundHandlerAdapter {
779 
780     private final String authority;
781     private final GrpcHttp2ConnectionHandler next;
782     private final ChannelLogger negotiationLogger;
783 
784     private ProtocolNegotiationEvent pne;
785 
786     Http2UpgradeAndGrpcHandler(String authority, GrpcHttp2ConnectionHandler next) {
787       this.authority = checkNotNull(authority, "authority");
788       this.next = checkNotNull(next, "next");
789       this.negotiationLogger = next.getNegotiationLogger();
790     }
791 
792     @Override
793     public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
794       negotiationLogger.log(ChannelLogLevel.INFO, "Http2Upgrade started");
795       HttpClientCodec httpClientCodec = new HttpClientCodec();
796       ctx.pipeline().addBefore(ctx.name(), null, httpClientCodec);
797 
798       Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(next);
799       HttpClientUpgradeHandler upgrader =
800           new HttpClientUpgradeHandler(httpClientCodec, upgradeCodec, /*maxContentLength=*/ 1000);
801       ctx.pipeline().addBefore(ctx.name(), null, upgrader);
802 
803       // Trigger the HTTP/1.1 plaintext upgrade protocol by issuing an HTTP request
804       // which causes the upgrade headers to be added
805       DefaultHttpRequest upgradeTrigger =
806           new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
807       upgradeTrigger.headers().add(HttpHeaderNames.HOST, authority);
808       ctx.writeAndFlush(upgradeTrigger).addListener(
809           ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
810       super.handlerAdded(ctx);
811     }
812 
813     @Override
814     public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
815       if (evt instanceof ProtocolNegotiationEvent) {
816         checkState(pne == null, "negotiation already started");
817         pne = (ProtocolNegotiationEvent) evt;
818       } else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_SUCCESSFUL) {
819         checkState(pne != null, "negotiation not yet complete");
820         negotiationLogger.log(ChannelLogLevel.INFO, "Http2Upgrade finished");
821         ctx.pipeline().remove(ctx.name());
822         next.handleProtocolNegotiationCompleted(pne.getAttributes(), pne.getSecurity());
823       } else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_REJECTED) {
824         ctx.fireExceptionCaught(unavailableException("HTTP/2 upgrade rejected"));
825       } else {
826         super.userEventTriggered(ctx, evt);
827       }
828     }
829   }
830 
831   /**
832    * Returns a {@link ChannelHandler} that ensures that the {@code handler} is added to the
833    * pipeline writes to the {@link io.netty.channel.Channel} may happen immediately, even before it
834    * is active.
835    */
836   public static ProtocolNegotiator plaintext() {
837     return new PlaintextProtocolNegotiator();
838   }
839 
840   public static ProtocolNegotiator.ClientFactory plaintextClientFactory() {
841     return new PlaintextProtocolNegotiatorClientFactory();
842   }
843 
844   @VisibleForTesting
845   static final class PlaintextProtocolNegotiatorClientFactory
846       implements ProtocolNegotiator.ClientFactory {
847     @Override public ProtocolNegotiator newNegotiator() {
848       return plaintext();
849     }
850 
851     @Override public int getDefaultPort() {
852       return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
853     }
854   }
855 
856   private static RuntimeException unavailableException(String msg) {
857     return Status.UNAVAILABLE.withDescription(msg).asRuntimeException();
858   }
859 
860   @VisibleForTesting
861   static void logSslEngineDetails(Level level, ChannelHandlerContext ctx, String msg,
862       @Nullable Throwable t) {
863     if (!log.isLoggable(level)) {
864       return;
865     }
866 
867     SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
868     SSLEngine engine = sslHandler.engine();
869 
870     StringBuilder builder = new StringBuilder(msg);
871     builder.append("\nSSLEngine Details: [\n");
872     if (engine instanceof OpenSslEngine) {
873       builder.append("    OpenSSL, ");
874       builder.append("Version: 0x").append(Integer.toHexString(OpenSsl.version()));
875       builder.append(" (").append(OpenSsl.versionString()).append("), ");
876       builder.append("ALPN supported: ").append(SslProvider.isAlpnSupported(SslProvider.OPENSSL));
877     } else if (JettyTlsUtil.isJettyAlpnConfigured()) {
878       builder.append("    Jetty ALPN");
879     } else if (JettyTlsUtil.isJettyNpnConfigured()) {
880       builder.append("    Jetty NPN");
881     } else if (JettyTlsUtil.isJava9AlpnAvailable()) {
882       builder.append("    JDK9 ALPN");
883     }
884     builder.append("\n    TLS Protocol: ");
885     builder.append(engine.getSession().getProtocol());
886     builder.append("\n    Application Protocol: ");
887     builder.append(sslHandler.applicationProtocol());
888     builder.append("\n    Need Client Auth: " );
889     builder.append(engine.getNeedClientAuth());
890     builder.append("\n    Want Client Auth: ");
891     builder.append(engine.getWantClientAuth());
892     builder.append("\n    Supported protocols=");
893     builder.append(Arrays.toString(engine.getSupportedProtocols()));
894     builder.append("\n    Enabled protocols=");
895     builder.append(Arrays.toString(engine.getEnabledProtocols()));
896     builder.append("\n    Supported ciphers=");
897     builder.append(Arrays.toString(engine.getSupportedCipherSuites()));
898     builder.append("\n    Enabled ciphers=");
899     builder.append(Arrays.toString(engine.getEnabledCipherSuites()));
900     builder.append("\n]");
901 
902     log.log(level, builder.toString(), t);
903   }
904 
905   /**
906    * Adapts a {@link ProtocolNegotiationEvent} to the {@link GrpcHttp2ConnectionHandler}.
907    */
908   static final class GrpcNegotiationHandler extends ChannelInboundHandlerAdapter {
909     private final GrpcHttp2ConnectionHandler next;
910 
911     public GrpcNegotiationHandler(GrpcHttp2ConnectionHandler next) {
912       this.next = checkNotNull(next, "next");
913     }
914 
915     @Override
916     public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
917       if (evt instanceof ProtocolNegotiationEvent) {
918         ProtocolNegotiationEvent protocolNegotiationEvent = (ProtocolNegotiationEvent) evt;
919         ctx.pipeline().replace(ctx.name(), null, next);
920         next.handleProtocolNegotiationCompleted(
921             protocolNegotiationEvent.getAttributes(), protocolNegotiationEvent.getSecurity());
922       } else {
923         super.userEventTriggered(ctx, evt);
924       }
925     }
926   }
927 
928   /*
929    * Common {@link ProtocolNegotiator}s used by gRPC.  Protocol negotiation follows a pattern to
930    * simplify the pipeline.   The pipeline should look like:
931    *
932    * 1.  {@link ProtocolNegotiator#newHandler() PN.H}, created.
933    * 2.  [Tail], {@link WriteBufferingAndExceptionHandler WBAEH}, [Head]
934    * 3.  [Tail], WBAEH, PN.H, [Head]
935    *
936    * <p>Typically, PN.H with be an instance of {@link InitHandler IH}, which is a trivial handler
937    * that can return the {@code scheme()} of the negotiation.  IH, and each handler after,
938    * replaces itself with a "next" handler once its part of negotiation is complete.  This keeps
939    * the pipeline small, and limits the interaction between handlers.
940    *
941    * <p>Additionally, each handler may fire a {@link ProtocolNegotiationEvent PNE} just after
942    * replacing itself.  Handlers should capture user events of type PNE, and re-trigger the events
943    * once that handler's part of negotiation is complete.  This can be seen in the
944    * {@link WaitUntilActiveHandler WUAH}, which waits until the channel is active.  Once active, it
945    * replaces itself with the next handler, and fires a PNE containing the addresses.  Continuing
946    * with IH and WUAH:
947    *
948    * 3.  [Tail], WBAEH, IH, [Head]
949    * 4.  [Tail], WBAEH, WUAH, [Head]
950    * 5.  [Tail], WBAEH, {@link GrpcNegotiationHandler}, [Head]
951    * 6a. [Tail], WBAEH, {@link GrpcHttp2ConnectionHandler GHCH}, [Head]
952    * 6b. [Tail], GHCH, [Head]
953    */
954 
955   /**
956    * A negotiator that only does plain text.
957    */
958   static final class PlaintextProtocolNegotiator implements ProtocolNegotiator {
959 
960     @Override
961     public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
962       ChannelHandler grpcNegotiationHandler = new GrpcNegotiationHandler(grpcHandler);
963       ChannelHandler activeHandler = new WaitUntilActiveHandler(grpcNegotiationHandler,
964           grpcHandler.getNegotiationLogger());
965       return activeHandler;
966     }
967 
968     @Override
969     public void close() {}
970 
971     @Override
972     public AsciiString scheme() {
973       return Utils.HTTP;
974     }
975   }
976 
977   /**
978    * Waits for the channel to be active, and then installs the next Handler.  Using this allows
979    * subsequent handlers to assume the channel is active and ready to send.  Additionally, this a
980    * {@link ProtocolNegotiationEvent}, with the connection addresses.
981    */
982   static final class WaitUntilActiveHandler extends ProtocolNegotiationHandler {
983 
984     boolean protocolNegotiationEventReceived;
985 
986     WaitUntilActiveHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
987       super(next, negotiationLogger);
988     }
989 
990     @Override
991     public void channelActive(ChannelHandlerContext ctx) throws Exception {
992       if (protocolNegotiationEventReceived) {
993         replaceOnActive(ctx);
994         fireProtocolNegotiationEvent(ctx);
995       }
996       // Still propagate channelActive to the new handler.
997       super.channelActive(ctx);
998     }
999 
1000     @Override
1001     protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1002       protocolNegotiationEventReceived = true;
1003       if (ctx.channel().isActive()) {
1004         replaceOnActive(ctx);
1005         fireProtocolNegotiationEvent(ctx);
1006       }
1007     }
1008 
1009     private void replaceOnActive(ChannelHandlerContext ctx) {
1010       ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1011       Attributes attrs = existingPne.getAttributes().toBuilder()
1012           .set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, ctx.channel().localAddress())
1013           .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
1014           // Later handlers are expected to overwrite this.
1015           .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.NONE)
1016           .build();
1017       replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs));
1018     }
1019   }
1020 
1021   /**
1022    * ProtocolNegotiationHandler is a convenience handler that makes it easy to follow the rules for
1023    * protocol negotiation.  Handlers should strongly consider extending this handler.
1024    */
1025   static class ProtocolNegotiationHandler extends ChannelDuplexHandler {
1026 
1027     private final ChannelHandler next;
1028     private final String negotiatorName;
1029     private ProtocolNegotiationEvent pne;
1030     private final ChannelLogger negotiationLogger;
1031 
1032     protected ProtocolNegotiationHandler(ChannelHandler next, String negotiatorName,
1033         ChannelLogger negotiationLogger) {
1034       this.next = checkNotNull(next, "next");
1035       this.negotiatorName = negotiatorName;
1036       this.negotiationLogger = checkNotNull(negotiationLogger, "negotiationLogger");
1037     }
1038 
1039     protected ProtocolNegotiationHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1040       this.next = checkNotNull(next, "next");
1041       this.negotiatorName = getClass().getSimpleName().replace("Handler", "");
1042       this.negotiationLogger = checkNotNull(negotiationLogger, "negotiationLogger");
1043     }
1044 
1045     @Override
1046     public final void handlerAdded(ChannelHandlerContext ctx) throws Exception {
1047       negotiationLogger.log(ChannelLogLevel.DEBUG, "{0} started", negotiatorName);
1048       handlerAdded0(ctx);
1049     }
1050 
1051     @ForOverride
1052     protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
1053       super.handlerAdded(ctx);
1054     }
1055 
1056     @Override
1057     public final void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
1058       if (evt instanceof ProtocolNegotiationEvent) {
1059         checkState(pne == null, "pre-existing negotiation: %s < %s", pne, evt);
1060         pne = (ProtocolNegotiationEvent) evt;
1061         protocolNegotiationEventTriggered(ctx);
1062       } else {
1063         userEventTriggered0(ctx, evt);
1064       }
1065     }
1066 
1067     protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
1068       super.userEventTriggered(ctx, evt);
1069     }
1070 
1071     @ForOverride
1072     protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1073       // no-op
1074     }
1075 
1076     protected final ProtocolNegotiationEvent getProtocolNegotiationEvent() {
1077       checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1078       return pne;
1079     }
1080 
1081     protected final void replaceProtocolNegotiationEvent(ProtocolNegotiationEvent pne) {
1082       checkState(this.pne != null, "previous protocol negotiation event hasn't triggered");
1083       this.pne = checkNotNull(pne);
1084     }
1085 
1086     protected final void fireProtocolNegotiationEvent(ChannelHandlerContext ctx) {
1087       checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1088       negotiationLogger.log(ChannelLogLevel.INFO, "{0} completed", negotiatorName);
1089       ctx.pipeline().replace(ctx.name(), /* newName= */ null, next);
1090       ctx.fireUserEventTriggered(pne);
1091     }
1092   }
1093 }
1094