Andrey K created HTTPCLIENT-1762:
------------------------------------
Summary: Proxy reuses a fully established routed connection in
attempt to CONNECT to target again causing 400 and NPE
Key: HTTPCLIENT-1762
URL: https://issues.apache.org/jira/browse/HTTPCLIENT-1762
Project: HttpComponents HttpClient
Issue Type: Bug
Components: HttpClient
Reporter: Andrey K
First I noticed I was getting sporadic errors when attempting to make an HTTPS
call via HTTP Proxy that requires authentication:
java.lang.IllegalArgumentException: Auth scheme may not be null
at org.apache.http.util.Args.notNull(Args.java:54)
at
org.apache.http.impl.client.AuthenticationStrategyImpl.authSucceeded(AuthenticationStrategyImpl.java:215)
at
org.apache.http.impl.client.ProxyAuthenticationStrategy.authSucceeded(ProxyAuthenticationStrategy.java:43)
at
org.apache.http.impl.auth.HttpAuthenticator.isAuthenticationRequested(HttpAuthenticator.java:88)
at
org.apache.http.impl.nio.client.MainClientExec.needAuthentication(MainClientExec.java:629)
at
org.apache.http.impl.nio.client.MainClientExec.handleResponse(MainClientExec.java:569)
at
org.apache.http.impl.nio.client.MainClientExec.responseReceived(MainClientExec.java:309)
at
org.apache.http.impl.nio.client.DefaultClientExchangeHandlerImpl.responseReceived(DefaultClientExchangeHandlerImpl.java:147)
at
org.apache.http.nio.protocol.HttpAsyncRequestExecutor.responseReceived(HttpAsyncRequestExecutor.java:303)
at
org.apache.http.impl.nio.DefaultNHttpClientConnection.consumeInput(DefaultNHttpClientConnection.java:255)
at
org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:81)
at
org.apache.http.impl.nio.client.InternalIODispatch.onInputReady(InternalIODispatch.java:39)
at
org.apache.http.impl.nio.reactor.AbstractIODispatch.inputReady(AbstractIODispatch.java:121)
at
org.apache.http.impl.nio.reactor.BaseIOReactor.readable(BaseIOReactor.java:162)
at
org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvent(AbstractIOReactor.java:337)
at
org.apache.http.impl.nio.reactor.AbstractIOReactor.processEvents(AbstractIOReactor.java:315)
at
org.apache.http.impl.nio.reactor.AbstractIOReactor.execute(AbstractIOReactor.java:276)
at
org.apache.http.impl.nio.reactor.BaseIOReactor.execute(BaseIOReactor.java:104)
at
org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor$Worker.run(AbstractMultiworkerIOReactor.java:588)
Upon closer look with trace debug, I saw that it was breaking on an unexpected
400 status code coming back against a CONNECT+AUTH request. When I traced the
connection that was used for this connect, I saw that it was a fully connected
routed connection. What happened was the HttpClient got a 407 after attempting
to do a CONNECT and that 407 contained instruction to close connection.
HttpClient discarded the connection and obtained a new one from the pool but
the one that was leased was already fully authenticated and tunneled. Thus the
CONNECT request was sent to the actual tunneled target and returned with a 400.
As a workaround I have enhanced a custom ProxyAuthenticationStrategy to rename
the USER_TOKEN context value to a temporary unique value to prevent reusing a
fully established connection and then revert it back after authentication is
sucessful:
static AtomicLong counter = new AtomicLong(0);
static final String ORIGINAL_USER_TOKEN = "ORIGINAL_USER_TOKEN";
@Override
public Map<String, Header> getChallenges(HttpHost authhost, HttpResponse
response, HttpContext context) throws MalformedChallengeException {
String originalUserToken =
(String)context.getAttribute(ORIGINAL_USER_TOKEN);
if (originalUserToken == null) {
originalUserToken =
(String)context.getAttribute(HttpClientContext.USER_TOKEN);
context.setAttribute(ORIGINAL_USER_TOKEN, originalUserToken);
String temporaryName = "NON-REUSABLE-" + counter.incrementAndGet();
context.setAttribute(HttpClientContext.USER_TOKEN, temporaryName);
log.trace("OnAuth: Renaming connection from {} to {}", originalUserToken,
temporaryName);
}
return super.getChallenges(authhost, response, context);
}
@Override
public void authSucceeded(HttpHost authhost, AuthScheme authScheme,
HttpContext context) {
String originalUserToken =
(String)context.getAttribute(ORIGINAL_USER_TOKEN);
if (originalUserToken != null) {
String tempUserToken =
(String)context.getAttribute(HttpClientContext.USER_TOKEN);
context.setAttribute(HttpClientContext.USER_TOKEN, originalUserToken);
context.removeAttribute(ORIGINAL_USER_TOKEN);
log.trace("OnAuthSucc: Renaming connection from {} to {}", tempUserToken,
originalUserToken);
}
super.authSucceeded(authhost, authScheme, context);
}
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]