I am trying to terminate a connection if no data is being received or server is just keeping the connection open for a url by setting value for connectionTimeout and readTimeout but it is not working.

Spring boot version 2.7.1

Here is the link to SO https://stackoverflow.com/questions/73165245/setting-connectiontimeout-and-readtimeout-not-working-on-urlresource

Below code reproduces the issue.

try {
  URL url = new URL("http://httpstat.us/200?sleep=20000");
  UrlResource urlResource = new UrlResource(url) {

@Override
    protected void customizeConnection(HttpURLConnection connection) throws IOException {
      super.customizeConnection(connection);
      connection.setConnectTimeout(4000);
      connection.setReadTimeout(2000);
    }
  };

  InputStream inputStream = urlResource.getInputStream();
  InputStreamReader isr = new InputStreamReader(inputStream,
      StandardCharsets.UTF_8);
  BufferedReader br = new BufferedReader(isr);
  br.lines().forEach(line -> System.out.println(line));
} catch (MalformedURLException e) {
  e.printStackTrace();
} catch (IOException e) {
  System.out.println("IO exception");
  e.printStackTrace();
}

Expected:- both connectionTimeout and readTimeout should work as per given value and terminate the connection if time exceeds the timeout value

Comment From: snicoll

The javadoc of customizeConnection states:

Customize the given HttpURLConnection, obtained in the course of an exists(), contentLength() or lastModified() call.

This override of yours not being called is working as designed. Let's see what the rest of the team thinks on the use case above.

Comment From: bclozel

While the javadoc for AbstractFileResolvingResource only mentions methods implemented on that class, it could make sense to also call this for getInputStream and update the javadoc accordingly; this looks inconsistent as all other remote calls are using this method.

On the other hand, the default implementation sets the HTTP method to "HEAD", this would not work for getInpustream and changing that would clash with the other methods.

Comment From: artembilan

The customizeConnection() is also called from the checkReadable() which, in turn, is called from the isReadable(). And this one is not mentioned in the customizeConnection() JavaDoc.

I don't think it is a big deal to set a proper HTTP method in that getInpustream() after calling customizeConnection() to override that default HEAD.

Maybe the pattern is to use an exist() or isReadable() before calling getInpustream() therefore that readTimeout would have some effect? And perhaps the connection is reused from the cache...

Comment From: bclozel

Given the conversation above, both AbstractFileResolvingResource and UrlResource have inconsistencies:

  • the customizeConnection(HttpURLConnection) default implementation can only be used by methods that won't need to fetch the actual content of the resource because it is using HTTP HEAD requests.
  • AbstractFileResolvingResource sub-classes (like UrlResource) do not always call the customizeConnection methods which makes the experience inconsistent; depending on the method used, timeouts or request headers will be different

While this behavior is documented in the javadoc, as it lists the methods delegating to the customization method, the developers expectations can be quite different when sub-classing a Resource implementation: the name of the method itself gives a false indication.

We can change this behavior without introducing breaking changes for sub-classes: we can set the HTTP request method after the customization call for each method, as it is its responsibility in the first place.