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