reactor-netty: SpringBoot 2.4.2 BlockHound: blocking call when making outbound HTTPS request

Hello Reactor Netty Team,

During a test we were doing, we encountered an issue, and wanted to let you know. This blocking call only happens when making outbound (I am the client, making a request to another server) https call. And https is important, as it is not reproducible with http.

Here is the stack trace:

Caused by: reactor.blockhound.BlockingOperationError: Blocking call! java.io.FileInputStream#readBytes
	at java.base/java.io.FileInputStream.readBytes(FileInputStream.java) ~[na:na]
	at java.base/java.io.FileInputStream.read(FileInputStream.java:279) ~[na:na]
	at java.base/java.io.FilterInputStream.read(FilterInputStream.java:133) ~[na:na]
	at java.base/sun.security.provider.NativePRNG$RandomIO.readFully(NativePRNG.java:424) ~[na:na]
	at java.base/sun.security.provider.NativePRNG$RandomIO.ensureBufferValid(NativePRNG.java:526) ~[na:na]
	at java.base/sun.security.provider.NativePRNG$RandomIO.implNextBytes(NativePRNG.java:545) ~[na:na]
	at java.base/sun.security.provider.NativePRNG.engineNextBytes(NativePRNG.java:220) ~[na:na]
	at java.base/java.security.SecureRandom.nextBytes(SecureRandom.java:741) ~[na:na]
	at java.base/java.security.SecureRandom.next(SecureRandom.java:798) ~[na:na]
	at java.base/java.util.Random.nextInt(Random.java:329) ~[na:na]
	at java.base/sun.security.ssl.SSLContextImpl.engineInit(SSLContextImpl.java:117) ~[na:na]
	at java.base/javax.net.ssl.SSLContext.init(SSLContext.java:297) ~[na:na]
	at io.netty.handler.ssl.JdkSslClientContext.newSSLContext(JdkSslClientContext.java:294) ~[netty-handler-4.1.59.Final.jar:4.1.59.Final]
	at io.netty.handler.ssl.JdkSslClientContext.<init>(JdkSslClientContext.java:272) ~[netty-handler-4.1.59.Final.jar:4.1.59.Final]
	at io.netty.handler.ssl.SslContext.newClientContextInternal(SslContext.java:824) ~[netty-handler-4.1.59.Final.jar:4.1.59.Final]
	at io.netty.handler.ssl.SslContextBuilder.build(SslContextBuilder.java:614) ~[netty-handler-4.1.59.Final.jar:4.1.59.Final]
	at reactor.netty.tcp.SslProvider.<init>(SslProvider.java:366) ~[reactor-netty-core-1.0.4.jar:1.0.4]
	at reactor.netty.tcp.SslProvider.updateDefaultConfiguration(SslProvider.java:95) ~[reactor-netty-core-1.0.4.jar:1.0.4]
	at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect.lambda$subscribe$0(HttpClientConnect.java:234) ~[reactor-netty-http-1.0.4.jar:1.0.4]
	at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:57) ~[reactor-core-3.4.3.jar:3.4.3]
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.4.3.jar:3.4.3]
	at reactor.core.publisher.FluxRetryWhen.subscribe(FluxRetryWhen.java:76) ~[reactor-core-3.4.3.jar:3.4.3]
	at reactor.core.publisher.MonoRetryWhen.subscribeOrReturn(MonoRetryWhen.java:46) ~[reactor-core-3.4.3.jar:3.4.3]
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57) ~[reactor-core-3.4.3.jar:3.4.3]
	at reactor.netty.http.client.HttpClientConnect$MonoHttpConnect.subscribe(HttpClientConnect.java:269) ~[reactor-netty-http-1.0.4.jar:1.0.4]
	at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.4.3.jar:3.4.3]

May I ask what might be the root cause of this blocking call in a reactive stack for https please?

Thank you

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 1
  • Comments: 20 (8 by maintainers)

Most upvoted comments

@patpatpat123 With PR #1573, we exposed a new API that you can use in order to utilise the default configuration and also to solve the blocking call issue:

/**
 * SslContext builder that provides, specific for the protocol, default configuration
 * e.g. {@link DefaultSslContextSpec}, {@link TcpSslContextSpec} etc.
 * As opposed to {@link #sslContext(SslContextBuilder)}, the default configuration is applied before
 * any other custom configuration.
 *
 * @param spec SslContext builder that provides, specific for the protocol, default configuration
 * @return {@literal this}
 * @since 1.0.6
 */
reactor.netty.tcp.SslProvider.SslContextSpec#sslContext(reactor.netty.tcp.SslProvider.ProtocolSslContextSpec)

Examples

HTTP/1.1

Http11SslContextSpec http11SslContextSpec =
        Http11SslContextSpec.forServer(cert, key)
                            .configure(sslContextBuilder -> sslContextBuilder...);

HttpServer.create()
          .secure(spec -> spec.sslContext(http11SslContextSpec))

HTTP/2

Http2SslContextSpec http11SslContextSpec =
        Http2SslContextSpec.forServer(cert, key)
                           .configure(sslContextBuilder -> sslContextBuilder...);

HttpServer.create()
          .secure(spec -> spec.sslContext(http11SslContextSpec))

@rp199 with .secure() you eagerly initialise the default SslContext, while without it the default SslContext will be resolved lazy when there is a url with https scheme.

@rp199 In your use case, you expect the HttpClient to decide lazy based on the scheme whether you need security or not. Can you try the snippet below (it is in java, but the idea is important)

WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create().compress(true).secure())).build();

@patpatpat123 Let’s keep this opened for the moment.

Thanks @violetagg, I can confirm that snippet works, I’m no longer getting any exception from blockhound and the https call is working fine 🙂

Just for sanity check, what was happening before was that if nothing is specified, the HttpClient will try to evaluate if we need security for the call and that’s where the blocking call was happening (that’s why it worked on http calls and not https). With that snippet, we enforced the HttpClient to use security.

But the end result is the same, ultimately the settings that we set would be the ones it would use if it decided for itself, right? If understand correctly, is just like a “shortcut” for the same result.

Thanks again for the help.