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 (likeUrlResource
) do not always call thecustomizeConnection
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.