Skip to content

Connection reset exception from RestTemplate call in Spring Web MVC controller is ignored #34264

Closed
@forketyfork

Description

@forketyfork

Consider a Spring Web MVC controller method that calls some downstream http service:

@PostMapping("/hello")
public String hello() {
    return new RestTemplate().postForObject("http://localhost:8888", "hello", String.class);
}

Starting from Spring Boot 3.4.0 (Spring Web 6.2.0), if calling the downstream service produces a "Connection reset" error, it is silently ignored. This endpoint would return 200 OK in such case.

Before Spring Web 6.2.0, calling this endpoint http://localhost:8080/hello would result in HTTP error code 500, with the following logs:

2025-01-15T12:47:57.658+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : POST "/hello", parameters={}
2025-01-15T12:47:57.658+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demo.DemoApplication#hello()
2025-01-15T12:47:57.660+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] o.s.web.client.RestTemplate              : HTTP POST http://localhost:8888
2025-01-15T12:47:57.660+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] o.s.web.client.RestTemplate              : Accept=[text/plain, application/json, application/*+json, */*]
2025-01-15T12:47:57.660+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] o.s.web.client.RestTemplate              : Writing [hello] with org.springframework.http.converter.StringHttpMessageConverter
2025-01-15T12:47:57.662+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Failed to complete request: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8888": Connection reset
2025-01-15T12:47:57.662+01:00 ERROR 15980 --- [demo] [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8888": Connection reset] with root cause

java.net.SocketException: Connection reset
	at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:318) ~[na:na]
        ...
2025-01-15T12:47:57.663+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for POST "/error", parameters={}
2025-01-15T12:47:57.664+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-01-15T12:47:57.664+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Using 'application/json', given [*/*] and supported [application/json, application/*+json]
2025-01-15T12:47:57.664+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Writing [{timestamp=Wed Jan 15 12:47:57 CET 2025, status=500, error=Internal Server Error, path=/hello}]
2025-01-15T12:47:57.665+01:00 DEBUG 15980 --- [demo] [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : Exiting from "ERROR" dispatch, status 500

After Spring Web 6.2.0 (Spring Boot 3.4.0), the logs look as follows, note the line "Looks like the client has gone away" — it's coming from DisconnectedClientHelper that assumes for some reason that the "Connection reset" exception comes from the client. Note that the exception is completely ignored, and the endpoint returns 200 OK:

2025-01-15T12:42:28.211+01:00 DEBUG 15887 --- [demo] [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet        : POST "/hello", parameters={}
2025-01-15T12:42:28.213+01:00 DEBUG 15887 --- [demo] [nio-8080-exec-7] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.example.demo.DemoApplication#hello()
2025-01-15T12:42:28.214+01:00 DEBUG 15887 --- [demo] [nio-8080-exec-7] o.s.web.client.RestTemplate              : HTTP POST http://localhost:8888
2025-01-15T12:42:28.215+01:00 DEBUG 15887 --- [demo] [nio-8080-exec-7] o.s.web.client.RestTemplate              : Accept=[text/plain, application/json, application/*+json, */*]
2025-01-15T12:42:28.215+01:00 DEBUG 15887 --- [demo] [nio-8080-exec-7] o.s.web.client.RestTemplate              : Writing [hello] with org.springframework.http.converter.StringHttpMessageConverter
2025-01-15T12:42:28.216+01:00 DEBUG 15887 --- [demo] [nio-8080-exec-7] o.s.w.s.handler.DisconnectedClient       : Looks like the client has gone away: org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://localhost:8888": Connection reset (For a full stack trace, set the log category 'org.apache.commons.logging.LogAdapter$Slf4jLocationAwareLog@4d9f6662' to TRACE level.)
2025-01-15T12:42:28.216+01:00 DEBUG 15887 --- [demo] [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet        : Completed 200 OK

A simple reproducer follows.

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

@SpringBootApplication
@Controller
public class DemoApplication {

    @PostMapping("/hello")
    public String hello() {
        return new RestTemplate().postForObject("http://localhost:8888", "hello", String.class);
    }
    
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
            try (ServerSocket socket = new ServerSocket(8888)) {
                while (true) {
                    try (Socket clientSocket = socket.accept()) {
                        // simulate TCP connection reset
                        clientSocket.getInputStream().read();
                        clientSocket.setSoLinger(true, 0);
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }).start();

        Thread.sleep(2000);

        SpringApplication.run(DemoApplication.class, args);
    }

}

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)status: backportedAn issue that has been backported to maintenance branchestype: regressionA bug that is also a regression

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions