Uploaded image for project: 'HttpComponents HttpCore'
  1. HttpComponents HttpCore
  2. HTTPCORE-683

H2ConnPool bug (stuck poolEntry)

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Major
    • Resolution: Fixed
    • 5.1.1
    • 5.1.2, 5.2-alpha1
    • HttpCore
    • None
    • Linux / Ubuntu / openjdk version "1.8.0_292"

    Description

      There is an implementation problem in AbstractIOSessionPool.getSessionInternal()

      Wrong pattern (starting line 190 in httpcore5-5.1.1) :

      if (poolEntry.sessionFuture == null) {
                          poolEntry.sessionFuture = connectSession(
                                  namedEndpoint,
                                  connectTimeout,
                                  new FutureCallback<IOSession>() {

                                     ...

                                      @Override
                                      public void failed(final Exception ex) {
                                          synchronized (poolEntry)

      {                                         poolEntry.session = null;                                         *poolEntry.sessionFuture = null;*                                         ...                                     }

                                      }
                                   ...

                                  });

      In case of a synchronous failure during connectSession (for example : UnknownHostException), the sessionFuture cleanup in the failed() callback (line 216) is executed before poolEntry.sessionFuture is assigned with the return value of connectSession (line 191)

      Thus, after the failure, a cancelled future is assigned to poolEntry.sessionFuture and never cleaned up !

      This result in a stalled entry in the pool because the if (poolEntry.sessionFuture == null) condition will no longer return true.

       

      Quick Reproducer

             H2AsyncClientBuilder httpClientBuilder = HttpAsyncClients.customHttp2()
                              .setDefaultRequestConfig(RequestConfig.custom()
                                              .setConnectTimeout(5000, TimeUnit.MILLISECONDS)
                                              .setResponseTimeout(5000, TimeUnit.MILLISECONDS)
                                              .build())
                              .setH2Config(H2Config.custom()
                                              .setPushEnabled(false)
                                              .build());
                     
              CloseableHttpAsyncClient client = httpClientBuilder.build();
              client.start();
             
              for(int n=1;n<=2;n++)
              {
                  System.out.println("Try #"+n);   
                 
                  HttpHost targetHost = new HttpHost(URIScheme.HTTPS.getId(), "invalidhost-gfhsgfxyzd.com", 443);
                  SimpleHttpRequest request = new SimpleHttpRequest("GET", targetHost, "/");
         
                  Future<SimpleHttpResponse> responseFuture = client.execute(request, null);
                  try
                  {
                      responseFuture.get();  // HANGS FOREVER at Try #2
                  }
                  catch (ExecutionException e)
                 

      {                 e.printStackTrace();             }

              }

       

      Quick Fix

      Cleaning the sessionFuture immediately after connectSession() returns in case its already done fixes the issue :

      (Line 234)

      + if (poolEntry.sessionFuture != null && poolEntry.sessionFuture.isDone())
      +                       poolEntry.sessionFuture = null;

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              s.pognant Sylvain POGNANT
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: