How do you disconnect an idle client in a Spring Webflux application?

Viewed 284

I have a spring-webflux application (with Netty under the hood). There is a controller method like this:

public Mono<Response> submitBinaryData(@RequestHeader HttpHeaders headers,
                                       @RequestBody(required = false) Flux<DataBuffer> data) {
    return process(data.timeout(Duration.ofSeconds(10)), getContentLength(headers));
}

process() method is not too interesting, it just writes the binary data to a storage. An important thing is that it knows upfront how much data there is (via Content-Length header).

Then, let's imagine that we have a client that got stuck, like this:

    byte[] bufferOf1Mb = new byte[1024 * 1024];
    HttpRequest request = HttpRequest.newBuilder(new URI("http://localhost:8080/")
            .setHeader("Accept", "application/json")
            .setHeader("Content-Type", "application/octet-stream")
            .POST(BodyPublishers.fromPublisher(JdkFlowAdapter.publisherToFlowPublisher(
                    Flux.just(ByteBuffer.wrap(bufferOf1Mb)).concatWith(Flux.never())), 10 * bufferOf1Mb.length))
            .build();
    HttpResponse<String> httpResponse = client.send(request, BodyHandlers.ofString());

This client announces 10Mb of data, but only writes 1Mb and then waits forever.

I'd like such clients to be disconnected after some timeout to avoid wasting resources on clients that have probably broken forever. In the controller method, there is timeout() operator that throws a TimeoutException if data does not flow for some time. The exception is then caught and processed by an exception handler (not shown): it just returns a Response object to the client (as a JSON).

The catch is that even when the timeout happens (it can be seen in the logs), the client does not notice anything. The client is probably waiting for its data to be consumed by the server, and only then will it look at the response. But the input will never be sent. And the server does not close the connection as it wants the client to read the response. So the connection remains open forever.

Debugger also shows that when this happens, writeWith() and setComplete() are called on the response object.

Is there a way to disconnect such a client? Either using a timeout setting (although server.netty. has only one timeout-related setting, connection-timeout which is not what's needed here), or programmatically (when it's clear that the client is effectively lost, cut the connection forcibly), or in any other way?

1 Answers

If you use WebClient for http request, you could set a responseTimeout in httpClient when you build the webclient.

  @Bean
  public WebClient getWebClient(WebClient.Builder webClientBuilder) {

    ClientHttpConnector connector = new ReactorClientHttpConnector(
        HttpClient.create().responseTimeout(Duration.ofSeconds(10))
    );

    return webClientBuilder
        .clientConnector(connector)
        .build();

  }

Reference: https://docs.spring.io/spring-framework/docs/5.0.7.RELEASE/spring-framework-reference/web-reactive.html#webflux-client-builder

Related