[ 
https://issues.apache.org/jira/browse/HTTPCLIENT-953?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Guillaume updated HTTPCLIENT-953:
---------------------------------

    Attachment: ConnPoolFailureEx.java

Attached sample code to trigger the behaviour.

> ConnPoolByRoute driving RouteSpecificPool to IllegalState
> ---------------------------------------------------------
>
>                 Key: HTTPCLIENT-953
>                 URL: https://issues.apache.org/jira/browse/HTTPCLIENT-953
>             Project: HttpComponents HttpClient
>          Issue Type: Bug
>          Components: HttpClient
>    Affects Versions: 4.0.1
>         Environment: Java 6, WinXP (probably OS agnostic)
>            Reporter: Guillaume
>         Attachments: ConnPoolFailureEx.java
>
>
> Hi all,
> I encountered an issue on ConnPoolByRoute / RouteSpecificPool on HTTPClient 
> 4.0.1, akin to HTTPCLIENT-747 (it also leads to a 
> java.lang.IllegalStateException: No entry created for this pool. 
> HttpRoute[{}XXX] ), but it is not a concurrency issue (no race condition, 
> just a logic error if I understood it correctly).
> From my understanding, the error lies in ConnPoolByRoute#getEntryBlocking
> Quoting from the code (line 309-314) :
> RouteSpecificPool rospl = getRoutePool(route, true);
> ... 
> } else if (hasCapacity && !freeConnections.isEmpty()) {
> deleteLeastUsedEntry();
> entry = createEntry(rospl, operator);
> } else { ...
> The short version of the issue is : under certain circumstances, 
> #deleteLeastUsedEntry can remove rospl from the map of known 
> RootSpecificPool. But as this code still holds on to the rospl instance, it 
> will modify its state in a way the pool will never recover from later, not 
> having any other way to access this instance when the connection gets 
> released.
> A Step by Step guide to what's going wrong.
> 0) You have to be in a condition that leads to the execution of said code 
> extract (i.e. no free entry on the current route - but the route already is 
> registered to the global pool -, current Route has capacity, max connections 
> reached for the global pool, but there are free connections to destroy).
> 2) We arrive in deleteLeastUsedEntry(). We get the last entry from a queue. 
> It can be that this entry is bound to the same (hashCode() wise) Route that 
> the one we are getting a connection to (i.e. rospl instance held in the 
> #getEntryBlocking context)
> 3) this entry can be the last of its pool, thus at this point, 
> rospl.isUnused() == true
> 4) As a consequence, deleteEntry() will remove rospl from the routeToPool map
> 5) Back in the getEntryBlocking method, we do entry = createEntry(rospl, 
> operator), which will do createdEntry() on the "locally-scoped" rospl 
> instance that has just been removed from routeToPool 
> 6) When the connexion from this new entry is released at some point in the 
> future, the rospl instance that got the createdEntry() does not exist 
> anymore, and it is a new one that gets the freeEntry() call
> 7) App breaks : this newly created RouteSpecificPool throws 
> IllegalStateException.
> Step 0, though, is a rare condition that I only reached during stress tests, 
> and on a SSL client-auth server. This is so because this is the only 
> condition that I know of in HTTPClient, where there is a keep-alive 
> connection in the RouteSpecificPool that can not be reused (when the State is 
> set to the X500 principals of the client cert in the pool, but not in the 
> request).
> Possible fix (from what I understand) :
> The rospl instance variable in the context of getEntryBlocking() should be 
> protected against the consequences of #deleteLeastUsedEntry().
> Not being confortable with all issues at hand, nor with the code base, the 
> simplest thing I can think of would be to preemptively reset the rospl 
> variable after deleteLeastUsedEntry(), thus writing the previous code extract 
> as :
> } else if (hasCapacity && !freeConnections.isEmpty()) {
> deleteLeastUsedEntry();
> // delete may have made deprecated the RouteSpecificPool instance
> rospl = getRoutePool(route, true);
> entry = createEntry(rospl, operator);
> } else { ...
> I have a test case that I will attach to this issue ASAP.
> It is a simple example that triggers the above conditions with 3 HttpGet 
> calls, in a serial fashion. As stated previsouly, these calls need nothing 
> particular, except that one of these calls must go to a HTTPS server with 
> client-side certificate authentication (I guess NTLM would be OK, anything 
> that will place a non null state along with the route in BasicEntryPool).
> I hope code is self-explainatory. I get 100% failure in my setup. Just 
> configure your 2 URLS, configure classpath, set your keystores system 
> properties, and launch.
> Workaround :
> Best workaround I found is : do not get to step 0.
> The most robust way I found to do that (i.e. a way that does not involve 
> things like setting max pool size to a gigantic number that can never be 
> reached, ...) is to actively set the ClientContext.USER_TOKEN attribute in an 
> exec context while submitting the request to the client.
> Step 0 triggers when there is an idle connection that waits, and when this 
> idle connection can not be reused, which can only happen if the request's 
> "USER_TOKEN" does not match the BasicPoolEntry#getState(). As, in the SSL 
> case, the state is the SSL Cert's X500PrincipalName, and I know it in 
> advance, it's easy to set up front.
> By the way, this taught me that I never could benefit from connection reuse 
> strategies in this SSL case, as connections would always get into the pool 
> with a USER_TOKEN that my requests never had. Don't know if it's mentionned 
> somewhere in the documentation, but this is a noteworthy fact to me.
> Please feel free to comment / correct any mistakes.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


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

Reply via email to