You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@hc.apache.org by "Sylvain POGNANT (Jira)" <ji...@apache.org> on 2021/08/13 07:29:00 UTC

[jira] [Created] (HTTPCORE-683) H2ConnPool bug (stuck poolEntry)

Sylvain POGNANT created HTTPCORE-683:
----------------------------------------

             Summary: H2ConnPool bug (stuck poolEntry)
                 Key: HTTPCORE-683
                 URL: https://issues.apache.org/jira/browse/HTTPCORE-683
             Project: HttpComponents HttpCore
          Issue Type: Bug
          Components: HttpCore
    Affects Versions: 5.1.1
         Environment: Linux / Ubuntu / openjdk version "1.8.0_292"
            Reporter: Sylvain POGNANT


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();  // {color:#FF0000}*HANGS FOREVER at Try #2*{color}
             }
             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;



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org