Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8326498

java.net.http.HttpClient connection leak using http/2

XMLWordPrintable

    • 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


            jpai Jaikiran Pai
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: