RxNetty
RxNetty copied to clipboard
Question about SSLEngineFactory usage
When I look at all the examples they just use DefaultFactories.trustAll() and say don't do this, when I try to not do this things seem to hang and eventually throw an exception.
Clearly I'm doing something wrong but I can't really work out what given the documentation, error and my lack of experience:
String host = "www.flickr.com";
SSLContext sslContext = SSLContext.getDefault();
SSLEngineFactory sslEngineFactory = DefaultFactories.fromSSLContext(sslContext);
HttpClient<ByteBuf, ByteBuf> rxClient = RxNetty.<ByteBuf, ByteBuf>newHttpClientBuilder(host , 443)
.withSslEngineFactory(sslEngineFactory)
.build();
Observable<HttpClientResponse<ByteBuf>> o = rxClient.submit(HttpClientRequest.createGet("/"));
Func1<HttpClientResponse<ByteBuf>, Observable<ByteBuf>> getContent = new Func1<HttpClientResponse<ByteBuf>, Observable<ByteBuf>>() {
@Override
public Observable<ByteBuf> call(HttpClientResponse<ByteBuf> t1) {
System.out.println("------------------------");
HttpResponseStatus status = t1.getStatus();
System.out.println("" + status.code() + " " + status.reasonPhrase());
System.out.println("------------------------");
HttpResponseHeaders headers = t1.getHeaders();
for (Entry<String,String> entry : headers.entries()) {
System.out.println(entry.getKey() + " - " + entry.getValue());
}
System.out.println("------------------------");
return t1.getContent();
}
};
o.flatMap(getContent)
.map(byteBuf -> byteBuf.toString(Charset.defaultCharset()))
.toBlocking()
.forEach(System.out::println);
And the (eventual) error:
Exception in thread "main" java.lang.IllegalStateException: complete already: DefaultChannelPromise@6e3d5a9e(failure(java.util.concurrent.CancellationException)
at io.netty.util.concurrent.DefaultPromise.setFailure(DefaultPromise.java:419)
at io.netty.channel.DefaultChannelPromise.setFailure(DefaultChannelPromise.java:87)
at io.netty.channel.DefaultChannelPromise.setFailure(DefaultChannelPromise.java:28)
at io.netty.util.internal.PendingWrite.failAndRecycle(PendingWrite.java:66)
at io.netty.handler.ssl.SslHandler.wrap(SslHandler.java:485)
at io.netty.handler.ssl.SslHandler.flush(SslHandler.java:442)
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:688)
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:669)
at io.netty.handler.logging.LoggingHandler.flush(LoggingHandler.java:297)
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:688)
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:669)
at io.netty.channel.ChannelOutboundHandlerAdapter.flush(ChannelOutboundHandlerAdapter.java:115)
at io.netty.channel.CombinedChannelDuplexHandler.flush(CombinedChannelDuplexHandler.java:198)
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:688)
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:669)
at io.netty.channel.ChannelDuplexHandler.flush(ChannelDuplexHandler.java:117)
at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:688)
at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:669)
at io.netty.channel.DefaultChannelPipeline.flush(DefaultChannelPipeline.java:838)
at io.netty.channel.AbstractChannel.flush(AbstractChannel.java:189)
at io.reactivex.netty.channel.DefaultChannelWriter.flush(DefaultChannelWriter.java:128)
at io.reactivex.netty.channel.DefaultChannelWriter.writeAndFlush(DefaultChannelWriter.java:69)
at io.reactivex.netty.protocol.http.client.RequestProcessingOperator$1.onNext(RequestProcessingOperator.java:90)
at io.reactivex.netty.protocol.http.client.RequestProcessingOperator$1.onNext(RequestProcessingOperator.java:57)
at rx.internal.operators.OperatorTake$1.onNext(OperatorTake.java:63)
at io.reactivex.netty.client.ConnectionPoolImpl$2.call(ConnectionPoolImpl.java:262)
at io.reactivex.netty.client.ConnectionPoolImpl$2.call(ConnectionPoolImpl.java:245)
at rx.observers.Subscribers$3.onNext(Subscribers.java:146)
at io.reactivex.netty.client.ClientChannelFactoryImpl.onNewConnection(ClientChannelFactoryImpl.java:119)
at io.reactivex.netty.client.ClientChannelFactoryImpl$2$1.operationComplete(ClientChannelFactoryImpl.java:102)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:679)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:565)
at io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:425)
at io.netty.handler.ssl.SslHandler.notifyHandshakeFailure(SslHandler.java:1147)
at io.netty.handler.ssl.SslHandler.setHandshakeFailure(SslHandler.java:1136)
at io.netty.handler.ssl.SslHandler.channelInactive(SslHandler.java:624)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:233)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:219)
at io.netty.channel.ChannelInboundHandlerAdapter.channelInactive(ChannelInboundHandlerAdapter.java:75)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:233)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:219)
at io.netty.channel.DefaultChannelPipeline.fireChannelInactive(DefaultChannelPipeline.java:769)
at io.netty.channel.AbstractChannel$AbstractUnsafe$5.run(AbstractChannel.java:568)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:370)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:744)
Caused by: javax.net.ssl.SSLException: SSLEngine closed already
DefaultFactories are meant to be used only for testing. For production deployment you must provide your own io.reactivex.netty.pipeline.ssl.SSLEngineFactory factory implementation that will create properly configured SSLEngine. This simplistic API was a design choice as in general SSL secure context creation is a complex process and can be done in many different ways. With io.reactivex.netty.pipeline.ssl.SSLEngineFactory interface we leave it out of RxNetty.
The usual way with Java would be to generate private/public key pair with keytool and sign it by some trusted party. More on this you can read here: http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html.
I would have to look more into SSLContext.getDefault() context, and why it does not work in your code. Assuming it is configured with JDK default trust store (cacerts file) it should be good enough for client side connection. On server side you would miss a private key.
@thaggie Did you get this to work?
Why does it have to be more complicated with RxNetty than with Apache HttpClient? Apache works out of the box with SSL and it doesn't use a "TRUST ALL" SSL engine afaik. I'm thinking about switching to http client instead because of this.
I've managed to get it working by supplying an instance of this class:
private static class DefaultSSLEngineFactory implements SSLEngineFactory {
private final SslContext sslCtx;
private DefaultSSLEngineFactory() {
try {
SslProvider sslProvider = SslContext.defaultClientProvider();
sslCtx = SslContext.newClientContext(sslProvider);
} catch (SSLException e) {
throw new IllegalStateException("Failed to create default SSL context", e);
}
}
@Override
public SSLEngine createSSLEngine(ByteBufAllocator allocator) {
return sslCtx.newEngine(allocator);
}
}
Not sure if it's safe for production though.
@johanhaleby Thanks! The above solution worked for me, the RxNetty example said to use: SSLContext sslContext = SSLContext.getDefault(); SSLEngineFactory sslEngineFactory = DefaultFactories.fromSSLContext(sslContext);
Which did not work, not even to connect to google, your solution worked. Agreed, not sure why it's so complicated to make an ssl connection when it's transparent in any other client, thanks for the solution.