You are viewing a plain text version of this content. The canonical link for it is here.
Posted to httpclient-users@hc.apache.org by Sukhdev Singh <su...@gmail.com> on 2014/09/17 09:45:08 UTC

Issue with authenticating multiple users with single httpclient instance

I am using httpclient 4.1.3 in my project.

I am creating single httpclient instance with ThreadSafeClientConnManager.
I don't see alternative to ThreadSafeClientConnManager in httpclient 4.1.3.

Intermittently I am experiencing that http request being made for another
user which is not the intended user. I am setting credentials in AuthState
and using localHttpClient.

My test includes -
 - Six threads running in parallel each calling REST API 10 time
 - Endpoint URL is same for all request so HttpHost is same.
 - There are six authenticating users. Each thread correspond to 1 thread.

Pasting code below. Please let me know if i am missing anything in main
code. I am not printing my test code here.

import java.io.IOException;

import java.security.cert.CertificateException;

import java.security.cert.X509Certificate;


import org.apache.http.HttpException;

import org.apache.http.HttpHost;

import org.apache.http.HttpRequest;

import org.apache.http.HttpRequestInterceptor;

import org.apache.http.auth.AuthScope;

import org.apache.http.auth.AuthState;

import org.apache.http.client.CredentialsProvider;

import org.apache.http.client.params.HttpClientParams;

import org.apache.http.client.protocol.ClientContext;

import org.apache.http.conn.ClientConnectionManager;

import org.apache.http.conn.scheme.Scheme;

import org.apache.http.conn.scheme.SchemeRegistry;

import org.apache.http.conn.ssl.SSLSocketFactory;

import org.apache.http.conn.ssl.TrustStrategy;

import org.apache.http.impl.auth.BasicScheme;

import org.apache.http.impl.client.DefaultHttpClient;

import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;

import org.apache.http.params.HttpConnectionParams;

import org.apache.http.protocol.ExecutionContext;

import org.apache.http.protocol.HttpContext;

import org.apache.log4j.Logger;


public class BasicAuthRestClient extends AbstractRestClient{

private static int HTTP_CONN_TIMEOUT = 30*1000; //30 second

private static int HTTP_SOCKET_TIMEOUT = 5*60*1000;  //5 minute

private static int HTTP_MAX_CONN = 1000;

private static int HTTP_MAX_PER_ROUTE = 100;


    public BasicAuthRestClient(String baseUrl){


    this.baseUrl = baseUrl;

        SchemeRegistry registry = new SchemeRegistry();

        SSLSocketFactory socketFactory;

try {

socketFactory = new SSLSocketFactory(new TrustStrategy() {

@Override

public boolean isTrusted(X509Certificate[] arg0, String arg1)

throws CertificateException {

return true;

}

}, org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

         registry.register(new Scheme("https", HTTP_SSL_PORT,
socketFactory));

} catch (Exception e) {

logger.error("exception in creating SSLSocketFactory", e);

}


 ClientConnectionManager connMgr = new
ThreadSafeClientConnManager(registry);

        ((ThreadSafeClientConnManager)connMgr).setMaxTotal(HTTP_MAX_CONN);

        ((ThreadSafeClientConnManager)connMgr).setDefaultMaxPerRoute(
HTTP_MAX_PER_ROUTE);



    httpClient = new DefaultHttpClient(connMgr);

    //setting request interceptor

    ((DefaultHttpClient)httpClient).addRequestInterceptor(new
HttpBasicAuthInterceptor(), 0);


        HttpConnectionParams.setConnectionTimeout(httpClient.getParams(),
HTTP_CONN_TIMEOUT);

        HttpConnectionParams.setSoTimeout(httpClient.getParams(),
HTTP_SOCKET_TIMEOUT);

        HttpConnectionParams.setStaleCheckingEnabled(httpClient.getParams(),
true);

        HttpConnectionParams.setTcpNoDelay(httpClient.getParams(), true);


        HttpClientParams.setAuthenticating(httpClient.getParams(), true);

        HttpClientParams.setRedirecting(httpClient.getParams(), false);

    }


    static class HttpBasicAuthInterceptor implements HttpRequestInterceptor
{

        public void process(

                final HttpRequest request,

                final HttpContext context) throws HttpException,
IOException {

            AuthState authState = (AuthState)
context.getAttribute(ClientContext.TARGET_AUTH_STATE);

            CredentialsProvider credProvider =
(CredentialsProvider)context.getAttribute(

            ClientContext.CREDS_PROVIDER);



            HttpHost targetHost = (HttpHost)context.getAttribute(

            ExecutionContext.HTTP_TARGET_HOST);



            authState.setCredentials(credProvider.getCredentials(

                       new AuthScope(targetHost.getHostName(),
targetHost.getPort())));



            authState.setAuthScheme((BasicScheme)context.getAttribute(
"preemptive-auth"));

        }

    }


    private HttpContext createHttpContext(String userName, String password){

String host = Utils.getHostName(baseUrl);


BasicHttpContext localcontext = new BasicHttpContext();

BasicScheme basicAuth = new BasicScheme();

localcontext.setAttribute("preemptive-auth", basicAuth);

CredentialsProvider credProvider = new BasicCredentialsProvider();

credProvider.setCredentials(

          new AuthScope(host, HTTP_SSL_PORT),

          new UsernamePasswordCredentials(userName, password));

localcontext.setAttribute(ClientContext.CREDS_PROVIDER, credProvider);

 localcontext.setAttribute(

          ClientContext.TARGET_AUTH_STATE, new AuthState());

HttpHost targetHost = new HttpHost(host, HTTP_SSL_PORT, "https");

localcontext.setAttribute(ExecutionContext.HTTP_TARGET_HOST, targetHost);

return localcontext;

 }



    public String doGet(final String url,

    final String userName, final String password) throws
RestClientException {

        HttpGet httpget = new HttpGet(this.baseUrl + url);

        HttpResponse response = null;

try {

    HttpContext localcontext = createHttpContext(userName, password);


response = httpClient.execute(httpget, localcontext);

return handleHttpResponse(response);

} catch (ClientProtocolException e) {

throw new RestClientException(e);

} catch (IllegalStateException e) {

throw new RestClientException(e);

} catch (IOException e) {

throw new RestClientException(e);

} catch (Exception e){

throw new RestClientException(e);

}finally{

try {

EntityUtils.consume(response.getEntity());

} catch (Exception e) {

logger.warn("Exception in EntityUtils.consume - ", e);

}

 httpClient.getConnectionManager().closeIdleConnections(60000,

TimeUnit.MILLISECONDS);

httpClient.getConnectionManager().closeExpiredConnections();

}

    }

}


Regards
Sukhdev

Re: Issue with authenticating multiple users with single httpclient instance

Posted by Sukhdev Singh <su...@gmail.com>.
Further debugged the issue and investigated httpclient code.

For Basic Auth , DefaultUserTokenHandler.getAuthPrincipal(..) retrurns null
.

Should i change this implementation and return userName instead?

On Wed, Sep 17, 2014 at 6:05 PM, Sukhdev Singh <su...@gmail.com>
wrote:

> One more observation on it.
>
> I don't see this issue when I lock httpclient.execute (...) and let it run
> by 1 thread at a time.
> Looks like in httpclient 4.1.2, httpclient.execute(...) is not thread safe
> as credentials are being overwritten by another thread.
>
> How to fix this issue. Any setting in httpclient?
>
> Regards
> Sukhdev
>
> On Wed, Sep 17, 2014 at 12:45 AM, Sukhdev Singh <su...@gmail.com>
> wrote:
>
>> I am using httpclient 4.1.3 in my project.
>>
>> I am creating single httpclient instance with ThreadSafeClientConnManager.
>> I don't see alternative to ThreadSafeClientConnManager in httpclient
>> 4.1.3.
>>
>> Intermittently I am experiencing that http request being made for
>> another user which is not the intended user. I am setting credentials in
>> AuthState and using localHttpClient.
>>
>> My test includes -
>>  - Six threads running in parallel each calling REST API 10 time
>>  - Endpoint URL is same for all request so HttpHost is same.
>>  - There are six authenticating users. Each thread correspond to 1 thread.
>>
>> Pasting code below. Please let me know if i am missing anything in main
>> code. I am not printing my test code here.
>>
>> import java.io.IOException;
>>
>> import java.security.cert.CertificateException;
>>
>> import java.security.cert.X509Certificate;
>>
>>
>> import org.apache.http.HttpException;
>>
>> import org.apache.http.HttpHost;
>>
>> import org.apache.http.HttpRequest;
>>
>> import org.apache.http.HttpRequestInterceptor;
>>
>> import org.apache.http.auth.AuthScope;
>>
>> import org.apache.http.auth.AuthState;
>>
>> import org.apache.http.client.CredentialsProvider;
>>
>> import org.apache.http.client.params.HttpClientParams;
>>
>> import org.apache.http.client.protocol.ClientContext;
>>
>> import org.apache.http.conn.ClientConnectionManager;
>>
>> import org.apache.http.conn.scheme.Scheme;
>>
>> import org.apache.http.conn.scheme.SchemeRegistry;
>>
>> import org.apache.http.conn.ssl.SSLSocketFactory;
>>
>> import org.apache.http.conn.ssl.TrustStrategy;
>>
>> import org.apache.http.impl.auth.BasicScheme;
>>
>> import org.apache.http.impl.client.DefaultHttpClient;
>>
>> import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
>>
>> import org.apache.http.params.HttpConnectionParams;
>>
>> import org.apache.http.protocol.ExecutionContext;
>>
>> import org.apache.http.protocol.HttpContext;
>>
>> import org.apache.log4j.Logger;
>>
>>
>> public class BasicAuthRestClient extends AbstractRestClient{
>>
>> private static int HTTP_CONN_TIMEOUT = 30*1000; //30 second
>>
>> private static int HTTP_SOCKET_TIMEOUT = 5*60*1000;  //5 minute
>>
>> private static int HTTP_MAX_CONN = 1000;
>>
>> private static int HTTP_MAX_PER_ROUTE = 100;
>>
>>
>>     public BasicAuthRestClient(String baseUrl){
>>
>>
>>     this.baseUrl = baseUrl;
>>
>>         SchemeRegistry registry = new SchemeRegistry();
>>
>>         SSLSocketFactory socketFactory;
>>
>> try {
>>
>> socketFactory = new SSLSocketFactory(new TrustStrategy() {
>>
>> @Override
>>
>> public boolean isTrusted(X509Certificate[] arg0, String arg1)
>>
>> throws CertificateException {
>>
>> return true;
>>
>> }
>>
>> }, org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
>> );
>>
>>          registry.register(new Scheme("https", HTTP_SSL_PORT,
>> socketFactory));
>>
>> } catch (Exception e) {
>>
>> logger.error("exception in creating SSLSocketFactory", e);
>>
>> }
>>
>>
>>  ClientConnectionManager connMgr = new
>> ThreadSafeClientConnManager(registry);
>>
>>         ((ThreadSafeClientConnManager)connMgr).setMaxTotal(HTTP_MAX_CONN
>> );
>>
>>         ((ThreadSafeClientConnManager)connMgr).setDefaultMaxPerRoute(
>> HTTP_MAX_PER_ROUTE);
>>
>>
>>
>>     httpClient = new DefaultHttpClient(connMgr);
>>
>>     //setting request interceptor
>>
>>     ((DefaultHttpClient)httpClient).addRequestInterceptor(new
>> HttpBasicAuthInterceptor(), 0);
>>
>>
>>         HttpConnectionParams.setConnectionTimeout(httpClient.getParams(),
>> HTTP_CONN_TIMEOUT);
>>
>>         HttpConnectionParams.setSoTimeout(httpClient.getParams(),
>> HTTP_SOCKET_TIMEOUT);
>>
>>         HttpConnectionParams.setStaleCheckingEnabled(httpClient.getParams(),
>> true);
>>
>>         HttpConnectionParams.setTcpNoDelay(httpClient.getParams(), true);
>>
>>
>>         HttpClientParams.setAuthenticating(httpClient.getParams(), true);
>>
>>         HttpClientParams.setRedirecting(httpClient.getParams(), false);
>>
>>     }
>>
>>
>>     static class HttpBasicAuthInterceptor implements
>> HttpRequestInterceptor {
>>
>>         public void process(
>>
>>                 final HttpRequest request,
>>
>>                 final HttpContext context) throws HttpException,
>> IOException {
>>
>>             AuthState authState = (AuthState)
>> context.getAttribute(ClientContext.TARGET_AUTH_STATE);
>>
>>             CredentialsProvider credProvider =
>> (CredentialsProvider)context.getAttribute(
>>
>>             ClientContext.CREDS_PROVIDER);
>>
>>
>>
>>             HttpHost targetHost = (HttpHost)context.getAttribute(
>>
>>             ExecutionContext.HTTP_TARGET_HOST);
>>
>>
>>
>>             authState.setCredentials(credProvider.getCredentials(
>>
>>                        new AuthScope(targetHost.getHostName(),
>> targetHost.getPort())));
>>
>>
>>
>>             authState.setAuthScheme((BasicScheme)context.getAttribute(
>> "preemptive-auth"));
>>
>>         }
>>
>>     }
>>
>>
>>     private HttpContext createHttpContext(String userName, String
>> password){
>>
>> String host = Utils.getHostName(baseUrl);
>>
>>
>> BasicHttpContext localcontext = new BasicHttpContext();
>>
>> BasicScheme basicAuth = new BasicScheme();
>>
>> localcontext.setAttribute("preemptive-auth", basicAuth);
>>
>> CredentialsProvider credProvider = new BasicCredentialsProvider();
>>
>> credProvider.setCredentials(
>>
>>           new AuthScope(host, HTTP_SSL_PORT),
>>
>>           new UsernamePasswordCredentials(userName, password));
>>
>> localcontext.setAttribute(ClientContext.CREDS_PROVIDER, credProvider);
>>
>>  localcontext.setAttribute(
>>
>>           ClientContext.TARGET_AUTH_STATE, new AuthState());
>>
>> HttpHost targetHost = new HttpHost(host, HTTP_SSL_PORT, "https");
>>
>> localcontext.setAttribute(ExecutionContext.HTTP_TARGET_HOST, targetHost);
>>
>> return localcontext;
>>
>>  }
>>
>>
>>
>>     public String doGet(final String url,
>>
>>     final String userName, final String password) throws
>> RestClientException {
>>
>>         HttpGet httpget = new HttpGet(this.baseUrl + url);
>>
>>         HttpResponse response = null;
>>
>> try {
>>
>>     HttpContext localcontext = createHttpContext(userName, password);
>>
>>
>> response = httpClient.execute(httpget, localcontext);
>>
>> return handleHttpResponse(response);
>>
>> } catch (ClientProtocolException e) {
>>
>> throw new RestClientException(e);
>>
>> } catch (IllegalStateException e) {
>>
>> throw new RestClientException(e);
>>
>> } catch (IOException e) {
>>
>> throw new RestClientException(e);
>>
>> } catch (Exception e){
>>
>> throw new RestClientException(e);
>>
>> }finally{
>>
>> try {
>>
>> EntityUtils.consume(response.getEntity());
>>
>> } catch (Exception e) {
>>
>> logger.warn("Exception in EntityUtils.consume - ", e);
>>
>> }
>>
>>  httpClient.getConnectionManager().closeIdleConnections(60000,
>>
>> TimeUnit.MILLISECONDS);
>>
>> httpClient.getConnectionManager().closeExpiredConnections();
>>
>> }
>>
>>     }
>>
>> }
>>
>>
>> Regards
>> Sukhdev
>>
>
>

Re: Issue with authenticating multiple users with single httpclient instance

Posted by Sukhdev Singh <su...@gmail.com>.
One more observation on it.

I don't see this issue when I lock httpclient.execute (...) and let it run
by 1 thread at a time.
Looks like in httpclient 4.1.2, httpclient.execute(...) is not thread safe
as credentials are being overwritten by another thread.

How to fix this issue. Any setting in httpclient?

Regards
Sukhdev

On Wed, Sep 17, 2014 at 12:45 AM, Sukhdev Singh <su...@gmail.com>
wrote:

> I am using httpclient 4.1.3 in my project.
>
> I am creating single httpclient instance with ThreadSafeClientConnManager.
> I don't see alternative to ThreadSafeClientConnManager in httpclient
> 4.1.3.
>
> Intermittently I am experiencing that http request being made for another
> user which is not the intended user. I am setting credentials in
> AuthState and using localHttpClient.
>
> My test includes -
>  - Six threads running in parallel each calling REST API 10 time
>  - Endpoint URL is same for all request so HttpHost is same.
>  - There are six authenticating users. Each thread correspond to 1 thread.
>
> Pasting code below. Please let me know if i am missing anything in main
> code. I am not printing my test code here.
>
> import java.io.IOException;
>
> import java.security.cert.CertificateException;
>
> import java.security.cert.X509Certificate;
>
>
> import org.apache.http.HttpException;
>
> import org.apache.http.HttpHost;
>
> import org.apache.http.HttpRequest;
>
> import org.apache.http.HttpRequestInterceptor;
>
> import org.apache.http.auth.AuthScope;
>
> import org.apache.http.auth.AuthState;
>
> import org.apache.http.client.CredentialsProvider;
>
> import org.apache.http.client.params.HttpClientParams;
>
> import org.apache.http.client.protocol.ClientContext;
>
> import org.apache.http.conn.ClientConnectionManager;
>
> import org.apache.http.conn.scheme.Scheme;
>
> import org.apache.http.conn.scheme.SchemeRegistry;
>
> import org.apache.http.conn.ssl.SSLSocketFactory;
>
> import org.apache.http.conn.ssl.TrustStrategy;
>
> import org.apache.http.impl.auth.BasicScheme;
>
> import org.apache.http.impl.client.DefaultHttpClient;
>
> import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
>
> import org.apache.http.params.HttpConnectionParams;
>
> import org.apache.http.protocol.ExecutionContext;
>
> import org.apache.http.protocol.HttpContext;
>
> import org.apache.log4j.Logger;
>
>
> public class BasicAuthRestClient extends AbstractRestClient{
>
> private static int HTTP_CONN_TIMEOUT = 30*1000; //30 second
>
> private static int HTTP_SOCKET_TIMEOUT = 5*60*1000;  //5 minute
>
> private static int HTTP_MAX_CONN = 1000;
>
> private static int HTTP_MAX_PER_ROUTE = 100;
>
>
>     public BasicAuthRestClient(String baseUrl){
>
>
>     this.baseUrl = baseUrl;
>
>         SchemeRegistry registry = new SchemeRegistry();
>
>         SSLSocketFactory socketFactory;
>
> try {
>
> socketFactory = new SSLSocketFactory(new TrustStrategy() {
>
> @Override
>
> public boolean isTrusted(X509Certificate[] arg0, String arg1)
>
> throws CertificateException {
>
> return true;
>
> }
>
> }, org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
>
>          registry.register(new Scheme("https", HTTP_SSL_PORT,
> socketFactory));
>
> } catch (Exception e) {
>
> logger.error("exception in creating SSLSocketFactory", e);
>
> }
>
>
>  ClientConnectionManager connMgr = new
> ThreadSafeClientConnManager(registry);
>
>         ((ThreadSafeClientConnManager)connMgr).setMaxTotal(HTTP_MAX_CONN);
>
>         ((ThreadSafeClientConnManager)connMgr).setDefaultMaxPerRoute(
> HTTP_MAX_PER_ROUTE);
>
>
>
>     httpClient = new DefaultHttpClient(connMgr);
>
>     //setting request interceptor
>
>     ((DefaultHttpClient)httpClient).addRequestInterceptor(new
> HttpBasicAuthInterceptor(), 0);
>
>
>         HttpConnectionParams.setConnectionTimeout(httpClient.getParams(),
> HTTP_CONN_TIMEOUT);
>
>         HttpConnectionParams.setSoTimeout(httpClient.getParams(),
> HTTP_SOCKET_TIMEOUT);
>
>         HttpConnectionParams.setStaleCheckingEnabled(httpClient.getParams(),
> true);
>
>         HttpConnectionParams.setTcpNoDelay(httpClient.getParams(), true);
>
>
>         HttpClientParams.setAuthenticating(httpClient.getParams(), true);
>
>         HttpClientParams.setRedirecting(httpClient.getParams(), false);
>
>     }
>
>
>     static class HttpBasicAuthInterceptor implements
> HttpRequestInterceptor {
>
>         public void process(
>
>                 final HttpRequest request,
>
>                 final HttpContext context) throws HttpException,
> IOException {
>
>             AuthState authState = (AuthState)
> context.getAttribute(ClientContext.TARGET_AUTH_STATE);
>
>             CredentialsProvider credProvider =
> (CredentialsProvider)context.getAttribute(
>
>             ClientContext.CREDS_PROVIDER);
>
>
>
>             HttpHost targetHost = (HttpHost)context.getAttribute(
>
>             ExecutionContext.HTTP_TARGET_HOST);
>
>
>
>             authState.setCredentials(credProvider.getCredentials(
>
>                        new AuthScope(targetHost.getHostName(),
> targetHost.getPort())));
>
>
>
>             authState.setAuthScheme((BasicScheme)context.getAttribute(
> "preemptive-auth"));
>
>         }
>
>     }
>
>
>     private HttpContext createHttpContext(String userName, String
> password){
>
> String host = Utils.getHostName(baseUrl);
>
>
> BasicHttpContext localcontext = new BasicHttpContext();
>
> BasicScheme basicAuth = new BasicScheme();
>
> localcontext.setAttribute("preemptive-auth", basicAuth);
>
> CredentialsProvider credProvider = new BasicCredentialsProvider();
>
> credProvider.setCredentials(
>
>           new AuthScope(host, HTTP_SSL_PORT),
>
>           new UsernamePasswordCredentials(userName, password));
>
> localcontext.setAttribute(ClientContext.CREDS_PROVIDER, credProvider);
>
>  localcontext.setAttribute(
>
>           ClientContext.TARGET_AUTH_STATE, new AuthState());
>
> HttpHost targetHost = new HttpHost(host, HTTP_SSL_PORT, "https");
>
> localcontext.setAttribute(ExecutionContext.HTTP_TARGET_HOST, targetHost);
>
> return localcontext;
>
>  }
>
>
>
>     public String doGet(final String url,
>
>     final String userName, final String password) throws
> RestClientException {
>
>         HttpGet httpget = new HttpGet(this.baseUrl + url);
>
>         HttpResponse response = null;
>
> try {
>
>     HttpContext localcontext = createHttpContext(userName, password);
>
>
> response = httpClient.execute(httpget, localcontext);
>
> return handleHttpResponse(response);
>
> } catch (ClientProtocolException e) {
>
> throw new RestClientException(e);
>
> } catch (IllegalStateException e) {
>
> throw new RestClientException(e);
>
> } catch (IOException e) {
>
> throw new RestClientException(e);
>
> } catch (Exception e){
>
> throw new RestClientException(e);
>
> }finally{
>
> try {
>
> EntityUtils.consume(response.getEntity());
>
> } catch (Exception e) {
>
> logger.warn("Exception in EntityUtils.consume - ", e);
>
> }
>
>  httpClient.getConnectionManager().closeIdleConnections(60000,
>
> TimeUnit.MILLISECONDS);
>
> httpClient.getConnectionManager().closeExpiredConnections();
>
> }
>
>     }
>
> }
>
>
> Regards
> Sukhdev
>