-
Bug
-
Resolution: Unresolved
-
P4
-
None
-
21.0.2
-
generic
-
generic
A DESCRIPTION OF THE PROBLEM :
HttpClient doesn't release tcp connections when using http/2 when receives goaway from server.
It works on java17. Didn't try other version.
REGRESSION : Last worked in version 17.0.10
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create HttpClient with http/2. And send requests with a BIG interval, bigger than the sever idle timeout.
So that the server will send goaway to terminate the connection.
Check open connections with `netstat -n | grep "0 127.0.0.1.8080" | wc -l` and the number of connections is increasing over time.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The number of connections doesn't increase over time.
ACTUAL -
The number of connections increases over time.
---------- BEGIN SOURCE ----------
package com.example.demo.httpclient;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class HttpClientTest {
private static final AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
URI requestUri = URI.create("https://localhost:8080/hello");
String requestJson = """
{
"message": "This is the message"
}
""";
HttpClient httpClient = getHttpClient();
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(100);
IntStream.range(0, 100)
.forEach(c -> executor.scheduleAtFixedRate(() -> {
try {
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestJson))
.uri(requestUri);
HttpRequest request = requestBuilder.build();
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
counter.getAndIncrement();
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 50000, TimeUnit.MILLISECONDS));
while (true) {
try {
Thread.sleep(5000);
System.out.println("Sent: " + counter.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static HttpClient getHttpClient() {
System.setProperty("jdk.internal.httpclient.disableHostnameVerification", "true");
try {
HttpClient.Builder builder = HttpClient.newBuilder();
Duration connectionTimeout = Duration.ofSeconds(100);
builder.connectTimeout(connectionTimeout);
builder.version(HttpClient.Version.HTTP_2);
builder.followRedirects(HttpClient.Redirect.NORMAL);
SSLContext selectedSslContext;
selectedSslContext = SSLContext.getInstance("SSL");
selectedSslContext.init(null, TRUST_ALL_CERTS, new SecureRandom());
builder.sslContext(selectedSslContext);
HttpClient httpClient = builder.build();
return httpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private final static TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
}
---------- END SOURCE ----------
FREQUENCY : always
HttpClient doesn't release tcp connections when using http/2 when receives goaway from server.
It works on java17. Didn't try other version.
REGRESSION : Last worked in version 17.0.10
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create HttpClient with http/2. And send requests with a BIG interval, bigger than the sever idle timeout.
So that the server will send goaway to terminate the connection.
Check open connections with `netstat -n | grep "0 127.0.0.1.8080" | wc -l` and the number of connections is increasing over time.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The number of connections doesn't increase over time.
ACTUAL -
The number of connections increases over time.
---------- BEGIN SOURCE ----------
package com.example.demo.httpclient;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.SecureRandom;
import java.time.Duration;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
public class HttpClientTest {
private static final AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
URI requestUri = URI.create("https://localhost:8080/hello");
String requestJson = """
{
"message": "This is the message"
}
""";
HttpClient httpClient = getHttpClient();
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(100);
IntStream.range(0, 100)
.forEach(c -> executor.scheduleAtFixedRate(() -> {
try {
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestJson))
.uri(requestUri);
HttpRequest request = requestBuilder.build();
httpClient.send(request, HttpResponse.BodyHandlers.ofString());
counter.getAndIncrement();
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 50000, TimeUnit.MILLISECONDS));
while (true) {
try {
Thread.sleep(5000);
System.out.println("Sent: " + counter.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static HttpClient getHttpClient() {
System.setProperty("jdk.internal.httpclient.disableHostnameVerification", "true");
try {
HttpClient.Builder builder = HttpClient.newBuilder();
Duration connectionTimeout = Duration.ofSeconds(100);
builder.connectTimeout(connectionTimeout);
builder.version(HttpClient.Version.HTTP_2);
builder.followRedirects(HttpClient.Redirect.NORMAL);
SSLContext selectedSslContext;
selectedSslContext = SSLContext.getInstance("SSL");
selectedSslContext.init(null, TRUST_ALL_CERTS, new SecureRandom());
builder.sslContext(selectedSslContext);
HttpClient httpClient = builder.build();
return httpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private final static TrustManager[] TRUST_ALL_CERTS = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
}
---------- END SOURCE ----------
FREQUENCY : always