Issue
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 connectionTimeout and readTimeout.
I have create anonymous class of URLResource and fetch the data from url. code block below is of spring project. spring boot version is 2.7.1
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();
}
I am using a service (http://httpstat.us/200?sleep=20000) that allows to hold connection for specified amount of time to check out the connection termination but the connection is not getting terminate after specified amount of time
Is there any other way to customize urlResource so that timeout can be set
Solution
It looks like the UrlResource.getInputStream()
is missing to call customizeConnection(con);
in its logic:
public InputStream getInputStream() throws IOException {
URLConnection con = this.url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
try {
return con.getInputStream();
}
catch (IOException ex) {
// Close the HTTP connection (if applicable).
if (con instanceof HttpURLConnection httpConn) {
httpConn.disconnect();
}
throw ex;
}
}
Please, raise a GH issue for Spring Framework to address this problem.
As a workaround I see this:
UrlResource urlResource = new UrlResource(url) {
@Override
public InputStream getInputStream() throws IOException {
URLConnection con = getURL().openConnection();
customizeConnection(con);
try {
return con.getInputStream();
}
catch (IOException ex) {
// Close the HTTP connection (if applicable).
if (con instanceof HttpURLConnection httpConn) {
httpConn.disconnect();
}
throw ex;
}
}
@Override
protected void customizeConnection(HttpURLConnection connection) throws IOException {
super.customizeConnection(connection);
connection.setReadTimeout(2000);
}
};
So, I override that getInputStream()
with the same logic, but also apply our customizeConnection()
on it. With that fix your test fails like this:
java.net.SocketTimeoutException: Read timed out
at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:283)
at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:309)
at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350)
at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966)
at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:244)
at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:284)
at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:343)
at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:791)
at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:726)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1688)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1589)
Answered By - Artem Bilan
Answer Checked By - Candace Johnson (JavaFixing Volunteer)