使用TLS保护时,RSocket无法正常工作-服务器java.lang.UnsupportedOperationException-客户端java.nio.channels.ClosedChannelException

时间:2019-11-19 22:59:26

标签: reactor-netty rsocket rsocket-java

更新,我上传了一个sample project to Github,您可以在其中重现该问题。查看自述文件中的说明。

我有一个可用于请求流的RSocket服务器,它生成具有n个随机数的Flux:

class RequestStreamRSocketServer

@ExperimentalUnsignedTypes
fun main() {

    val latch = CountDownLatch(1)

    RSocketFactory.receive()
        .frameDecoder(PayloadDecoder.DEFAULT)
        .acceptor { setup, sendingSocket ->
            Mono.just(
                object : AbstractRSocket() {
                    override fun requestStream(payload: Payload): Flux<Payload> {
                        val randomNumberGenerator = Random(1234)
                        val numbers = payload.dataUtf8.toInt()
                        println("Generating $numbers random numbers")
                        return IntRange(1, numbers)
                            .map { DefaultPayload.create(randomNumberGenerator.nextUInt().toString().toByteArray()) }
                            .toList().toFlux()
                    }
                })
        }
        .transport(
            TcpServerTransport.create(TcpServer.create().port(7878))
        )
        .start()
        .block()

    latch.await()
}

我还创建了一个客户端,该客户端连接到RSocket并请求10个随机数:

class RequestStreamRSocketClient

@ExperimentalUnsignedTypes
fun main() {

    val latch = CountDownLatch(1)

    val path = RequestStreamRSocketClient::class.java.getResource("truststore.jks").path
    System.setProperty("javax.net.ssl.trustStore", path)
    System.setProperty("javax.net.ssl.trustStorePassword", "123456")


    val client = RSocketFactory.connect()
        .frameDecoder(PayloadDecoder.DEFAULT)
        .transport(TcpClientTransport.create(TcpClient.create().port(7878)))
        .start()
        .block()

    client.requestStream(DefaultPayload.create("10"))
        .map { it.dataUtf8 }
        //.onErrorReturn("error")
        .doOnNext(System.out::println)
        .doOnComplete { latch.countDown() }
        .doOnError { it.printStackTrace() }
        .subscribe()

    latch.await()
}

运行完美。

服务器日志:

Generating 10 random numbers

客户日志:

345130239
2958210271
3979283303
4254072378
4206518657
1432197826
3787126071
2479634382
4147073748
3864383859

Process finished with exit code 0

但是我想在RSocket通信中使用TLS,所以我为服务器创建了一个certificate.pem / key.pem并进行了配置:

.transport(
    TcpServerTransport.create(TcpServer.create().port(7878).secure {
        it.sslContext(
            SslContextBuilder.forServer(
                File(RequestStreamRSocketServer::class.java.getResource("certificate.pem").toURI()),
                File(RequestStreamRSocketServer::class.java.getResource("key.pem").toURI())
            )
        )
    })
)

在客户端,我创建了一个truststore.jks,导入了certificate.pem并将客户端配置为使用安全通信:

    val path = RequestStreamRSocketClient::class.java.getResource("truststore.jks").path
    System.setProperty("javax.net.ssl.trustStore", path)
    System.setProperty("javax.net.ssl.trustStorePassword", "123456")
    ...

        .transport(TcpClientTransport.create(TcpClient.create().port(7878).secure {
            it.sslContext(SslContextBuilder.forClient())
        }))

启动服务器后,我启动客户端。调用了服务器的接受者请求流(打印Generating 10 random numbers),但立即失败:

Generating 10 random numbers
java.lang.IllegalArgumentException: promise already done: DefaultChannelPromise@4f230679(failure: java.lang.UnsupportedOperationException)
    at io.netty.channel.AbstractChannelHandlerContext.isNotValidPromise(AbstractChannelHandlerContext.java:891)
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:773)
    at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:701)
    at io.netty.handler.ssl.SslHandler.finishWrap(SslHandler.java:899)
    at io.netty.handler.ssl.SslHandler.wrap(SslHandler.java:885)
    at io.netty.handler.ssl.SslHandler.wrapAndFlush(SslHandler.java:797)
    at io.netty.handler.ssl.SslHandler.flush(SslHandler.java:778)
    at io.netty.channel.AbstractChannelHandlerContext.invokeFlush0(AbstractChannelHandlerContext.java:749)
    at io.netty.channel.AbstractChannelHandlerContext.invokeFlush(AbstractChannelHandlerContext.java:741)
    at io.netty.channel.AbstractChannelHandlerContext.flush(AbstractChannelHandlerContext.java:727)
    at reactor.netty.channel.MonoSendMany$SendManyInner$AsyncFlush.run(MonoSendMany.java:621)
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:416)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:515)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:748)

在客户端,有一个封闭的频道例外:

java.nio.channels.ClosedChannelException
    at io.rsocket.RSocketRequester.terminate(RSocketRequester.java:476)
    at io.rsocket.RSocketRequester.lambda$new$0(RSocketRequester.java:94)
    at reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.runFinally(FluxDoFinally.java:156)
    at reactor.core.publisher.FluxDoFinally$DoFinallySubscriber.onComplete(FluxDoFinally.java:139)
    at reactor.core.publisher.MonoProcessor$NextInner.onComplete(MonoProcessor.java:518)
    at reactor.core.publisher.MonoProcessor.onNext(MonoProcessor.java:308)
    at reactor.core.publisher.MonoProcessor.onComplete(MonoProcessor.java:265)
    at io.rsocket.internal.BaseDuplexConnection.dispose(BaseDuplexConnection.java:23)
    at io.rsocket.transport.netty.TcpDuplexConnection.lambda$new$0(TcpDuplexConnection.java:61)
    at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:500)
    at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:493)
    at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:472)
    at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:413)
    at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:538)
    at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:527)
    at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:98)
    at io.netty.channel.DefaultChannelPromise.trySuccess(DefaultChannelPromise.java:84)
    at io.netty.channel.AbstractChannel$CloseFuture.setClosed(AbstractChannel.java:1156)
    at io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:758)
    at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:734)
    at io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:605)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.close(DefaultChannelPipeline.java:1363)
    at io.netty.channel.AbstractChannelHandlerContext.invokeClose(AbstractChannelHandlerContext.java:621)
    at io.netty.channel.AbstractChannelHandlerContext.close(AbstractChannelHandlerContext.java:605)
    at io.netty.channel.AbstractChannelHandlerContext.close(AbstractChannelHandlerContext.java:467)
    at io.netty.handler.ssl.SslHandler.exceptionCaught(SslHandler.java:1092)
    at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:297)
    at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:276)
    at io.netty.channel.AbstractChannelHandlerContext.fireExceptionCaught(AbstractChannelHandlerContext.java:268)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.exceptionCaught(DefaultChannelPipeline.java:1388)
    at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:297)
    at io.netty.channel.AbstractChannelHandlerContext.invokeExceptionCaught(AbstractChannelHandlerContext.java:276)
    at io.netty.channel.DefaultChannelPipeline.fireExceptionCaught(DefaultChannelPipeline.java:918)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.handleReadException(AbstractNioByteChannel.java:125)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:174)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:697)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:632)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:549)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:511)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:918)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(Thread.java:748)

如何修复它以使其与TLS兼容?

2 个答案:

答案 0 :(得分:0)

尝试按以下方式发送客户端证书:

.transport(() -> {
                            TcpClient tcpClient = TcpClient.create().host("localhost").port(7878);
                            return TcpClientTransport.create(keyCertChainFile != null ?
                                    tcpClient.secure(sslContextSpec -> sslContextSpec
                                        .sslContext(SslContextBuilder.forClient().keyManager("client-cert.pem", "client-key.pem")
                                        .trustManager(new File("cacert.pem")))) :
                                    tcpClient);
                        }

答案 1 :(得分:0)

在升级到RC7和较小的重构后已修复:

服务器:

    val latch = CountDownLatch(1)

    RSocketServer.create()
        .payloadDecoder(PayloadDecoder.DEFAULT)
        .acceptor { setup, sendingSocket ->
            Mono.just(
                object : AbstractRSocket() {
                    override fun requestStream(payload: Payload): Flux<Payload> {
                        val randomNumberGenerator = Random(1234)
                        val numbers = payload.dataUtf8.toInt()
                        println("Generating $numbers random numbers")
                        return IntRange(1, numbers)
                            .map { DefaultPayload.create(randomNumberGenerator.nextUInt().toString().toByteArray()) }
                            .toList().toFlux()
                    }
                })
        }.bind(
            TcpServerTransport.create(TcpServer.create().port(7878)
                .secure {
                    it.sslContext(
                        SslContextBuilder.forServer(
                            File(RequestStreamRSocketServer::class.java.getResource("certificate.pem").toURI()),
                            File(RequestStreamRSocketServer::class.java.getResource("key.pem").toURI())
                        )
                    )
                })
        )
        .block()

    latch.await()

客户:

    val latch = CountDownLatch(1)

    val client = RSocketConnector.connectWith(
        TcpClientTransport.create(TcpClient.create().port(7878)
            .secure {
                it.sslContext(
                    SslContextBuilder.forClient().trustManager(
                        File(
                            RequestStreamRSocketClient::class.java.getResource(
                                "certificate.pem"
                            ).path
                        )
                    )
                )
            })
    ).block()!!

    client.requestStream(DefaultPayload.create("10"))
        .map { it.dataUtf8 }
        .doOnNext(System.out::println)
        .doOnComplete { latch.countDown() }
        .doOnError { it.printStackTrace() }
        .subscribe()

    latch.await()