Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

RestClient does not close the connection when protocol information is abnormal #35331

Open
Assignees
Labels
in: webIssues in web modules (web, webmvc, webflux, websocket) status: waiting-for-triageAn issue we've not yet triaged or decided on
@a544793138

Description

Bug report

Environment

  • OpenJDK 22
  • Spring Boot 3.5.4

Process

I have two servers (A and B), both providing a POST /readyz HTTP interface.

I use RestClient + JDK HttpClient + JdkClientHttpRequestFactory to send HTTP requests.

The following simple demo uses a thread to send requests to servers A or B every 10 seconds.

public static RestClient client(String url) {
 HttpClient.Builder httpBuilder = HttpClient.newBuilder()
 .connectTimeout(Duration.ofSeconds(3))
 .version(HttpClient.Version.HTTP_1_1);
 JdkClientHttpRequestFactory factory = new JdkClientHttpRequestFactory(httpBuilder.build());
 factory.setReadTimeout(Duration.ofSeconds(3));
 return RestClient.builder()
 .baseUrl(url)
 .defaultHeader(HttpHeaders.CONNECTION, "closed")
 .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json; charset=utf-8")
 .requestFactory(factory)
 .build();
}
public static void main(String[] args) throws InterruptedException {
 String url = "server.url";
 RestClient client = client(url);
 final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(r -> {
 Thread thread = new Thread(r, "node-health-issuer");
 thread.setDaemon(true);
 return thread;
 });
 executor.scheduleWithFixedDelay(() -> {
 try {
 ResponseEntity<Void> responseEntity = client.post().uri("readyz")
 .retrieve()
 .toBodilessEntity();
 } catch (Exception e) {
 e.printStackTrace();
 }
 }, 0, 10, TimeUnit.SECONDS);
 Thread.sleep(60 * 1000);
}

Due to version differences in the programs, the responses differ. Server A responds normally, but Server B's response causes the following error:

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://192.1.1.1:11111/readyz": Invalid status line: " 200 OK"
 at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.createResourceAccessException(DefaultRestClient.java:697)
 at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:582)
 at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchange(DefaultRestClient.java:533)
 at org.springframework.web.client.RestClient$RequestHeadersSpec.exchange(RestClient.java:680)
 at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.executeAndExtract(DefaultRestClient.java:814)
 at org.springframework.web.client.DefaultRestClient$DefaultResponseSpec.toBodilessEntity(DefaultRestClient.java:792)
 at cn.keyou.cmas.entity.monitored.application.health.HealthScheduler.healthLine(HealthScheduler.java:94)
 at cn.keyou.cmas.entity.monitored.application.health.HealthScheduler.lambda$metric2ドル(HealthScheduler.java:77)
 at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
 at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
 at java.base/java.lang.VirtualThread.run(Unknown Source)
Caused by: java.net.ProtocolException: Invalid status line: " 200 OK"
 at java.net.http/jdk.internal.net.http.Http1HeaderParser.protocolException(Unknown Source)
 at java.net.http/jdk.internal.net.http.Http1HeaderParser.readStatusLineFeed(Unknown Source)
 at java.net.http/jdk.internal.net.http.Http1HeaderParser.parse(Unknown Source)
 at java.net.http/jdk.internal.net.http.Http1Response$HeadersReader.handle(Unknown Source)
 at java.net.http/jdk.internal.net.http.Http1Response$HeadersReader.handle(Unknown Source)
 at java.net.http/jdk.internal.net.http.Http1Response$Receiver.accept(Unknown Source)
 at java.net.http/jdk.internal.net.http.Http1Response$HeadersReader.tryAsyncReceive(Unknown Source)
 at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.flush(Unknown Source)
 at java.net.http/jdk.internal.net.http.common.SequentialScheduler$LockingRestartableTask.run(Unknown Source)
 at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(Unknown Source)
 at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(Unknown Source)
 at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
 at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
 at java.base/java.lang.Thread.run(Unknown Source)

For Server A, using netstat -an | grep <Aport> shows only one established connection, e.g.:

tcp6 0 0 <local-address> <A-address> ESTABLISHED

However, for Server B, the error causes connections to accumulate. Before the demo ends, the number of established connections increases continuously, e.g.:

tcp6 0 0 <local-address1> <B-address> ESTABLISHED
tcp6 0 0 <local-address2> <B-address> ESTABLISHED
tcp6 0 0 <local-address3> <B-address> ESTABLISHED
tcp6 0 0 <local-address4> <B-address> ESTABLISHED

I noticed in the exception stack trace that in org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:582), there is a finally block. Debugging for Server B shows that close is always true, but clientResponse is null.

I am unsure whether the connections with exceptions should be closed, but they clearly should not keep increasing.

Looking forward to your reply.

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket) status: waiting-for-triageAn issue we've not yet triaged or decided on

Type

No type

Projects

No projects

Milestone

No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      AltStyle によって変換されたページ (->オリジナル) /