question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Ability to close connection on WebClient responses

See original GitHub issue

Affects: 5.2.5.RELEASE

This feature request arises from this StackOverflow post. It could arguably fit either with spring-framework or reactor-netty, posting in spring-framework to start the discussion.


Context

Some content delivery networks recommend to re-resolve DNS on certain types of status codes (e.g. 500 internal server error). To achieve this, I’ve added a custom Netty DnsNameResolver and DnsCache, but I also need to close the connection, otherwise it will be released back to the pool and DNS will not be re-resolved.

Two approaches have come to light so far:

Approach 1: using Webclient alone

This was suggested by Violeta Georgieva in this answer:

return this.webClient
           .get()
           .uri("/500")
           .retrieve()
           .onStatus(status -> status.equals(HttpStatus.INTERNAL_SERVER_ERROR), clientResponse -> {
                clientResponse.bodyToFlux(DataBuffer.class)
                              .subscribe(new BaseSubscriber<DataBuffer>() {
                                  @Override
                                  protected void hookOnSubscribe(Subscription subscription) {
                                      subscription.cancel();
                                  }
                              });
                return Mono.error(new IllegalStateException("..."));
           })
           .bodyToMono(String.class);

Basically, subscribing to the response body and cancelling it immediately seems to close the connection. However, this relies on Spring’s undocumented internal behaviour and causes bodies of error responses to be lost.

Approach 2: using Reactor Netty’s TcpClient

This approach was my initial attempt, configuring the underlying TcpClient used by WebClient:

TcpClient tcpClient = TcpClient.create()
        .observe((connection, newState) -> {
            if (newState == State.RELEASED && connection instanceof HttpClientResponse) {
                HttpResponseStatus status = ((HttpClientResponse) connection).status();
                if (status.codeClass() != HttpStatusClass.SUCCESS) {
                    connection.dispose();
                }
            }
        });

This approach also feels clunky and leads to a potential race condition: if the connection is released back to the pool after an error status code, and the observer is notified to close that connection, a new request could acquire that pooled connection in parallel.

Other approaches?

Are there any other approaches that one could implement to close the connection on error status codes? Could some more convenient interfaces be added to Spring or Reactor Netty’s APIs?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
rstoyanchevcommented, Apr 7, 2020

Yes, it would mean the body is ignored.

I don’t see any good options at the WebClient level I’m afraid. We’d have to allow all the usual ways to read the body but there is no way to expose the connection at the end of that. Moreover a Connection abstraction over Netty, Jetty, (as well as Apache HttpClient, and JDK 11 HttpClient to come in 5.3) is unlikely to work out well. It’s not something we want to pursue. This level of control is best at the HTTP client library level.

@violetagg what are your thoughts on doAfterResponseSuccess? As this is meant to be after HttpClientState.RESPONSE_COMPLETED I wonder if this here shouldn’t be in reverse order? I think the closing of the connection prevents the onComplete signal to the subscriber.

0reactions
PyvesBcommented, Apr 8, 2020

Thanks a lot, great collaboration! 👍

Read more comments on GitHub >

github_iconTop Results From Across the Web

WebClient open and close connection on every element call ...
In this case, it's likely returning Connection: close headers in its response. This will correctly force Netty to close the connection in ...
Read more >
Testing Spring Boot WebClient With MockWebServer
Mocking the Spring Boot WebClient can be difficult. Learn how to replace the remote service with a mock service using MockWebServer.
Read more >
Sending HTTP requests with Spring WebClient - Reflectoring
How to Handle an HTTP Response with WebClient. Once we've made a request, we usually want to read the contents of the response....
Read more >
Performant and optimal Spring WebClient | Dhaval Shah
Great article, I would like to know, does .retrieve() automatically close connections? and exchangeToMono, if I use .toEntity(foo) and .block(), ...
Read more >
Configuring timeouts in Spring reactive WebClient
This article is about configuring the read and connect timeout values when using Spring WebClient. It explains the difference between reactive signal ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found