jessey
jessey

Reputation: 495

For Spring WebFlux's WebClient how to catch Netty's Exception such as ProxyConnectException

I'd like to change Apache HttpComponents Client to Spring WebFlux's WebClient in my application.But I can't catch the exceptions from Netty,such as io.netty.handler.proxy.ProxyConnectException.

I have try the doOnError operator,but for these exceptions,they are can't be catched.

var timeout = Duration.ofSeconds(3);
        if (proxy != null) {
            HttpClient httpClient = HttpClient.create()
                .tcpConfiguration(tcpClient ->
                    tcpClient.proxy(py -> py.type(ProxyProvider.Proxy.HTTP).host(proxy.getHost()).port(proxy.getPort()).username(proxy.getUser()).password(tmp -> proxy.getPass())).doOnConnected(conn -> conn
                        .addHandlerLast(new ReadTimeoutHandler((int) (timeout.toSeconds() / 2)))
                        .addHandlerLast(new WriteTimeoutHandler((int) (timeout.toSeconds() / 2)))));
            ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);

            ExchangeStrategies strategies = ExchangeStrategies
                .builder()
                .codecs(clientDefaultCodecsConfigurer -> {
                    clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON));
                    clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON));

                }).build();

            WebClient webClient = WebClient
                .builder()
                .exchangeStrategies(strategies)
                .clientConnector(connector)
                .build();
            ClientResponse httpException = ClientResponse.create(org.springframework.http.HttpStatus.REQUEST_TIMEOUT).build();


      webClient
      .get()
      .uri("my_uri")
      .accept(MediaType.ALL)
      .header("my_header_key", "my_header_value")
      .exchange().doOnError(ProxyConnectException.class, throwable -> {
                    logger.warn("ProxyConnectException");
                })
      .onErrorReturn(httpException)
      .doOnError(ConnectTimeoutException.class, e -> {
                    logger.warn("ConnectTimeoutException");
                })
      .doOnError(ReadTimeoutException.class, e -> {
                    logger.warn("ReadTimeoutException");
                })
      .doOnError(SSLException.class, e -> {
                    logger.warn("SSLException");
                })
      .doOnError(Exception.class, e -> {
                    logger.warn("Exception");
                })
      .onErrorReturn(httpException)
      .block();

What I want is just print the log and return the httpException object.But the result is not only what I expected,my logs is printed, my object is returned,but I can also see some exception info as follows.Is there any way to avoid these exceptions?

io.netty.handler.proxy.ProxyConnectException: http, none, /XXXX:XX => XXXX:XX, timeout
    at io.netty.handler.proxy.ProxyHandler$2.run(ProxyHandler.java:201)
    at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
    at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:127)
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
    at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:335)
    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
    at java.base/java.lang.Thread.run(Thread.java:834)
ERROR [reactor-http-epoll-5] HttpClientConnect --- [id: 0x3610bfd0, L:/XXXX:XX ! R:XXXX/XXXX:XX] The connection observed an error
java.nio.channels.ClosedChannelException: null
    at io.netty.handler.ssl.SslHandler.channelInactive(...)(Unknown Source)```

Upvotes: 6

Views: 3131

Answers (2)

HereAndBeyond
HereAndBeyond

Reputation: 1454

For those who are experiencing a similar issue with Mono

I was struggling with catching reactor.netty.http.client.PrematureCloseException and in my case only onErrorResume is able to catch it. I put it after bodyToMono(...) method. None of doOnError, onErrorMap, onErrorReturn helped me.

.onErrorResume(throwable -> {
                 // put your fallback actions here
                 // E.g. I just rethrow another exception
                 throw new YourException("Request failed due to " + throwable.getMessage());
               })

Note: I'm also using block method for subscription

Versions:

  • org.springframework.boot:spring-boot-starter-webflux:3.0.4
    • org.springframework.boot:spring-boot-starter-reactor-netty:3.0.4
      • io.projectreactor.netty:reactor-netty-http:1.1.4
    • org.springframework:spring-webflux:6.0.6
      • io.projectreactor:reactor-core:3.5.3

Upvotes: 0

jessey
jessey

Reputation: 495

After read the source code,I found the exception is printed by reactor.netty.http.client.And I am sure that I have catched the exception,so the whole exception stack info is useless for me.What I need to do is just disable the log.

<logger name="reactor.netty.http.client" level="OFF"/>

Upvotes: 1

Related Questions