@Test public void tlsNegotiationFailurePropagatesToStatus() throws Exception { File serverCert = TestUtils.loadCert("server1.pem"); File serverKey = TestUtils.loadCert("server1.key"); SslContext sslContext = GrpcSslContexts.forServer(serverCert, serverKey) .ciphers(TestUtils.preferredTestCiphers(), SupportedCipherSuiteFilter.INSTANCE) .clientAuth(ClientAuth.REQUIRE) .build(); negotiator = ProtocolNegotiators.serverTls(sslContext); startServer(); File caCert = TestUtils.loadCert("ca.pem"); File clientCert = TestUtils.loadCert("client.pem"); File clientKey = TestUtils.loadCert("client.key"); SslContext clientContext = GrpcSslContexts.forClient() .trustManager(caCert) .ciphers(TestUtils.preferredTestCiphers(), SupportedCipherSuiteFilter.INSTANCE) .keyManager(clientCert, clientKey) .build(); ProtocolNegotiator negotiator = ProtocolNegotiators.tls(clientContext); final NettyClientTransport transport = newTransport(negotiator); callMeMaybe(transport.start(clientTransportListener)); Rpc rpc = new Rpc(transport).halfClose(); try { rpc.waitForClose(); fail("expected exception"); } catch (ExecutionException ex) { StatusException sre = (StatusException) ex.getCause(); assertEquals(Status.Code.UNAVAILABLE, sre.getStatus().getCode()); assertThat(sre.getCause()).isInstanceOf(SSLHandshakeException.class); assertThat(sre.getCause().getMessage()).contains("SSLV3_ALERT_HANDSHAKE_FAILURE"); } }
@SuppressWarnings("unchecked") @Override public Runnable start(Listener transportListener) { lifecycleManager = new ClientTransportLifecycleManager( Preconditions.checkNotNull(transportListener, "listener")); EventLoop eventLoop = group.next(); if (keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED) { keepAliveManager = new KeepAliveManager( new ClientKeepAlivePinger(this), eventLoop, keepAliveTimeNanos, keepAliveTimeoutNanos, keepAliveWithoutCalls); } handler = NettyClientHandler.newHandler( lifecycleManager, keepAliveManager, flowControlWindow, maxHeaderListSize, GrpcUtil.STOPWATCH_SUPPLIER, tooManyPingsRunnable, transportTracer, eagAttributes, authorityString); NettyHandlerSettings.setAutoWindow(handler); negotiationHandler = negotiator.newHandler(handler); Bootstrap b = new Bootstrap(); b.group(eventLoop); b.channel(channelType); if (NioSocketChannel.class.isAssignableFrom(channelType)) { b.option(SO_KEEPALIVE, true); } for (Map.Entry<ChannelOption<?>, ?> entry : channelOptions.entrySet()) { b.option((ChannelOption<Object>) entry.getKey(), entry.getValue()); } b.handler(negotiationHandler); ChannelFuture regFuture = b.register(); if (regFuture.isDone() && !regFuture.isSuccess()) { channel = null; Throwable t = regFuture.cause(); if (t == null) { t = new IllegalStateException("Channel is null, but future doesn't have a cause"); } statusExplainingWhyTheChannelIsNull = Utils.statusFromThrowable(t); return new Runnable() { @Override public void run() { lifecycleManager.notifyTerminated(statusExplainingWhyTheChannelIsNull); } }; } channel = regFuture.channel(); handler.startWriteQueue(channel); channel.writeAndFlush(NettyClientHandler.NOOP_MESSAGE).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { lifecycleManager.notifyTerminated(Utils.statusFromThrowable(future.cause())); } } }); SocketAddress localAddress = localSocketPicker.createSocketAddress(remoteAddress, eagAttributes); if (localAddress != null) { channel.connect(remoteAddress, localAddress); } else { channel.connect(remoteAddress); } if (keepAliveManager != null) { keepAliveManager.onTransportStarted(); } return null; }
NettyClientTransport implements ConnectionClientTransport { @SuppressWarnings("unchecked") @Override public Runnable start(Listener transportListener) { lifecycleManager = new ClientTransportLifecycleManager( Preconditions.checkNotNull(transportListener, "listener")); EventLoop eventLoop = group.next(); if (keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED) { keepAliveManager = new KeepAliveManager( new ClientKeepAlivePinger(this), eventLoop, keepAliveTimeNanos, keepAliveTimeoutNanos, keepAliveWithoutCalls); } handler = NettyClientHandler.newHandler( lifecycleManager, keepAliveManager, flowControlWindow, maxHeaderListSize, GrpcUtil.STOPWATCH_SUPPLIER, tooManyPingsRunnable, transportTracer, eagAttributes, authorityString); NettyHandlerSettings.setAutoWindow(handler); negotiationHandler = negotiator.newHandler(handler); Bootstrap b = new Bootstrap(); b.group(eventLoop); b.channel(channelType); if (NioSocketChannel.class.isAssignableFrom(channelType)) { b.option(SO_KEEPALIVE, true); } for (Map.Entry<ChannelOption<?>, ?> entry : channelOptions.entrySet()) { b.option((ChannelOption<Object>) entry.getKey(), entry.getValue()); } b.handler(negotiationHandler); ChannelFuture regFuture = b.register(); if (regFuture.isDone() && !regFuture.isSuccess()) { channel = null; Throwable t = regFuture.cause(); if (t == null) { t = new IllegalStateException("Channel is null, but future doesn't have a cause"); } statusExplainingWhyTheChannelIsNull = Utils.statusFromThrowable(t); return new Runnable() { @Override public void run() { lifecycleManager.notifyTerminated(statusExplainingWhyTheChannelIsNull); } }; } channel = regFuture.channel(); handler.startWriteQueue(channel); channel.writeAndFlush(NettyClientHandler.NOOP_MESSAGE).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { lifecycleManager.notifyTerminated(Utils.statusFromThrowable(future.cause())); } } }); SocketAddress localAddress = localSocketPicker.createSocketAddress(remoteAddress, eagAttributes); if (localAddress != null) { channel.connect(remoteAddress, localAddress); } else { channel.connect(remoteAddress); } if (keepAliveManager != null) { keepAliveManager.onTransportStarted(); } return null; } }
NettyClientTransport implements ConnectionClientTransport { @SuppressWarnings("unchecked") @Override public Runnable start(Listener transportListener) { lifecycleManager = new ClientTransportLifecycleManager( Preconditions.checkNotNull(transportListener, "listener")); EventLoop eventLoop = group.next(); if (keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED) { keepAliveManager = new KeepAliveManager( new ClientKeepAlivePinger(this), eventLoop, keepAliveTimeNanos, keepAliveTimeoutNanos, keepAliveWithoutCalls); } handler = NettyClientHandler.newHandler( lifecycleManager, keepAliveManager, flowControlWindow, maxHeaderListSize, GrpcUtil.STOPWATCH_SUPPLIER, tooManyPingsRunnable, transportTracer, eagAttributes, authorityString); NettyHandlerSettings.setAutoWindow(handler); negotiationHandler = negotiator.newHandler(handler); Bootstrap b = new Bootstrap(); b.group(eventLoop); b.channel(channelType); if (NioSocketChannel.class.isAssignableFrom(channelType)) { b.option(SO_KEEPALIVE, true); } for (Map.Entry<ChannelOption<?>, ?> entry : channelOptions.entrySet()) { b.option((ChannelOption<Object>) entry.getKey(), entry.getValue()); } b.handler(negotiationHandler); ChannelFuture regFuture = b.register(); if (regFuture.isDone() && !regFuture.isSuccess()) { channel = null; Throwable t = regFuture.cause(); if (t == null) { t = new IllegalStateException("Channel is null, but future doesn't have a cause"); } statusExplainingWhyTheChannelIsNull = Utils.statusFromThrowable(t); return new Runnable() { @Override public void run() { lifecycleManager.notifyTerminated(statusExplainingWhyTheChannelIsNull); } }; } channel = regFuture.channel(); handler.startWriteQueue(channel); channel.writeAndFlush(NettyClientHandler.NOOP_MESSAGE).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { lifecycleManager.notifyTerminated(Utils.statusFromThrowable(future.cause())); } } }); SocketAddress localAddress = localSocketPicker.createSocketAddress(remoteAddress, eagAttributes); if (localAddress != null) { channel.connect(remoteAddress, localAddress); } else { channel.connect(remoteAddress); } if (keepAliveManager != null) { keepAliveManager.onTransportStarted(); } return null; } NettyClientTransport(
      SocketAddress address, Class<? extends Channel> channelType,
      Map<ChannelOption<?>, ?> channelOptions, EventLoopGroup group,
      ProtocolNegotiator negotiator, int flowControlWindow, int maxMessageSize,
      int maxHeaderListSize, long keepAliveTimeNanos, long keepAliveTimeoutNanos,
      boolean keepAliveWithoutCalls, String authority, @Nullable String userAgent,
      Runnable tooManyPingsRunnable, TransportTracer transportTracer, Attributes eagAttributes,
      LocalSocketPicker localSocketPicker); }
NettyClientTransport implements ConnectionClientTransport { @SuppressWarnings("unchecked") @Override public Runnable start(Listener transportListener) { lifecycleManager = new ClientTransportLifecycleManager( Preconditions.checkNotNull(transportListener, "listener")); EventLoop eventLoop = group.next(); if (keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED) { keepAliveManager = new KeepAliveManager( new ClientKeepAlivePinger(this), eventLoop, keepAliveTimeNanos, keepAliveTimeoutNanos, keepAliveWithoutCalls); } handler = NettyClientHandler.newHandler( lifecycleManager, keepAliveManager, flowControlWindow, maxHeaderListSize, GrpcUtil.STOPWATCH_SUPPLIER, tooManyPingsRunnable, transportTracer, eagAttributes, authorityString); NettyHandlerSettings.setAutoWindow(handler); negotiationHandler = negotiator.newHandler(handler); Bootstrap b = new Bootstrap(); b.group(eventLoop); b.channel(channelType); if (NioSocketChannel.class.isAssignableFrom(channelType)) { b.option(SO_KEEPALIVE, true); } for (Map.Entry<ChannelOption<?>, ?> entry : channelOptions.entrySet()) { b.option((ChannelOption<Object>) entry.getKey(), entry.getValue()); } b.handler(negotiationHandler); ChannelFuture regFuture = b.register(); if (regFuture.isDone() && !regFuture.isSuccess()) { channel = null; Throwable t = regFuture.cause(); if (t == null) { t = new IllegalStateException("Channel is null, but future doesn't have a cause"); } statusExplainingWhyTheChannelIsNull = Utils.statusFromThrowable(t); return new Runnable() { @Override public void run() { lifecycleManager.notifyTerminated(statusExplainingWhyTheChannelIsNull); } }; } channel = regFuture.channel(); handler.startWriteQueue(channel); channel.writeAndFlush(NettyClientHandler.NOOP_MESSAGE).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { lifecycleManager.notifyTerminated(Utils.statusFromThrowable(future.cause())); } } }); SocketAddress localAddress = localSocketPicker.createSocketAddress(remoteAddress, eagAttributes); if (localAddress != null) { channel.connect(remoteAddress, localAddress); } else { channel.connect(remoteAddress); } if (keepAliveManager != null) { keepAliveManager.onTransportStarted(); } return null; } NettyClientTransport(
      SocketAddress address, Class<? extends Channel> channelType,
      Map<ChannelOption<?>, ?> channelOptions, EventLoopGroup group,
      ProtocolNegotiator negotiator, int flowControlWindow, int maxMessageSize,
      int maxHeaderListSize, long keepAliveTimeNanos, long keepAliveTimeoutNanos,
      boolean keepAliveWithoutCalls, String authority, @Nullable String userAgent,
      Runnable tooManyPingsRunnable, TransportTracer transportTracer, Attributes eagAttributes,
      LocalSocketPicker localSocketPicker); @Override void ping(final PingCallback callback, final Executor executor); @Override ClientStream newStream(
      MethodDescriptor<?, ?> method, Metadata headers, CallOptions callOptions); @SuppressWarnings("unchecked") @Override Runnable start(Listener transportListener); @Override void shutdown(Status reason); @Override void shutdownNow(final Status reason); @Override String toString(); @Override InternalLogId getLogId(); @Override Attributes getAttributes(); @Override ListenableFuture<SocketStats> getStats(); @Override SocketAddress getAddress(); }
NettyClientTransport implements ConnectionClientTransport { @SuppressWarnings("unchecked") @Override public Runnable start(Listener transportListener) { lifecycleManager = new ClientTransportLifecycleManager( Preconditions.checkNotNull(transportListener, "listener")); EventLoop eventLoop = group.next(); if (keepAliveTimeNanos != KEEPALIVE_TIME_NANOS_DISABLED) { keepAliveManager = new KeepAliveManager( new ClientKeepAlivePinger(this), eventLoop, keepAliveTimeNanos, keepAliveTimeoutNanos, keepAliveWithoutCalls); } handler = NettyClientHandler.newHandler( lifecycleManager, keepAliveManager, flowControlWindow, maxHeaderListSize, GrpcUtil.STOPWATCH_SUPPLIER, tooManyPingsRunnable, transportTracer, eagAttributes, authorityString); NettyHandlerSettings.setAutoWindow(handler); negotiationHandler = negotiator.newHandler(handler); Bootstrap b = new Bootstrap(); b.group(eventLoop); b.channel(channelType); if (NioSocketChannel.class.isAssignableFrom(channelType)) { b.option(SO_KEEPALIVE, true); } for (Map.Entry<ChannelOption<?>, ?> entry : channelOptions.entrySet()) { b.option((ChannelOption<Object>) entry.getKey(), entry.getValue()); } b.handler(negotiationHandler); ChannelFuture regFuture = b.register(); if (regFuture.isDone() && !regFuture.isSuccess()) { channel = null; Throwable t = regFuture.cause(); if (t == null) { t = new IllegalStateException("Channel is null, but future doesn't have a cause"); } statusExplainingWhyTheChannelIsNull = Utils.statusFromThrowable(t); return new Runnable() { @Override public void run() { lifecycleManager.notifyTerminated(statusExplainingWhyTheChannelIsNull); } }; } channel = regFuture.channel(); handler.startWriteQueue(channel); channel.writeAndFlush(NettyClientHandler.NOOP_MESSAGE).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { lifecycleManager.notifyTerminated(Utils.statusFromThrowable(future.cause())); } } }); SocketAddress localAddress = localSocketPicker.createSocketAddress(remoteAddress, eagAttributes); if (localAddress != null) { channel.connect(remoteAddress, localAddress); } else { channel.connect(remoteAddress); } if (keepAliveManager != null) { keepAliveManager.onTransportStarted(); } return null; } NettyClientTransport(
      SocketAddress address, Class<? extends Channel> channelType,
      Map<ChannelOption<?>, ?> channelOptions, EventLoopGroup group,
      ProtocolNegotiator negotiator, int flowControlWindow, int maxMessageSize,
      int maxHeaderListSize, long keepAliveTimeNanos, long keepAliveTimeoutNanos,
      boolean keepAliveWithoutCalls, String authority, @Nullable String userAgent,
      Runnable tooManyPingsRunnable, TransportTracer transportTracer, Attributes eagAttributes,
      LocalSocketPicker localSocketPicker); @Override void ping(final PingCallback callback, final Executor executor); @Override ClientStream newStream(
      MethodDescriptor<?, ?> method, Metadata headers, CallOptions callOptions); @SuppressWarnings("unchecked") @Override Runnable start(Listener transportListener); @Override void shutdown(Status reason); @Override void shutdownNow(final Status reason); @Override String toString(); @Override InternalLogId getLogId(); @Override Attributes getAttributes(); @Override ListenableFuture<SocketStats> getStats(); @Override SocketAddress getAddress();  }