[ 
https://issues.apache.org/jira/browse/HTTPCLIENT-2170?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17400633#comment-17400633
 ] 

Erick  edited comment on HTTPCLIENT-2170 at 8/18/21, 12:18 AM:
---------------------------------------------------------------

[~olegk]

I have simplified the repro code to one file using http-client 5.0.4.

We are still working on getting a public NTLM endpoint, 

meanwhile if you have an NTLM service you can just replace the requestUris with 
one single endpoint and repeat the request several times as it is also 
reproducible like that. 
{code:java}
public class JavaHttpClientWrapper {
  public final CloseableHttpClient httpClient;
  
  public static void main(String[] args) {
    String username = "user";
    String password = "password";

    final String[] requestUris = new String[] {
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/0/0/0";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/63/0/0";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/16/26166/11433";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/15/13083/5716";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/1/0/0";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/16/26166/11432";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/14/6541/2858";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/15/13082/5716";
    };

    try {
      JavaHttpClientWrapper httpClientWrapper = new JavaHttpClientWrapper();
      for(String uri : requestUris) {
        new Thread(() -> {
          httpClientWrapper.makeRequest(uri, username, password);
        }).start();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

  }

  public JavaHttpClientWrapper() throws Exception {

    X509TrustManager trustManager = new X509TrustManager() {
      @Override public void checkClientTrusted(X509Certificate[] 
x509Certificates, String s)
      throws CertificateException {}
      @Override public void checkServerTrusted(X509Certificate[] 
x509Certificates, String s)
      throws CertificateException {}
      @Override public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
      }
    };
    TrustManager[] trustManagers = new TrustManager[] { trustManager };
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagers, null);

    SSLConnectionSocketFactory sslConnectionSocketFactory = 
SSLConnectionSocketFactoryBuilder.create()
        .setSslContext(sslContext)
        .setTlsVersions(TLS.V_1_2)
        .setHostnameVerifier(new DefaultHostnameVerifier())
        .build();

    Registry sslSocketConnectionRegistry = RegistryBuilder.create()
        .register("http", PlainConnectionSocketFactory.INSTANCE)
        .register("https", sslConnectionSocketFactory).build();

    PoolingHttpClientConnectionManager connectionManager = new 
PoolingHttpClientConnectionManager(sslSocketConnectionRegistry);
    connectionManager.setValidateAfterInactivity(TimeValue.of(1L, 
TimeUnit.SECONDS));
    connectionManager.setMaxTotal(50);
    connectionManager.setDefaultMaxPerRoute(50);

    CacheConfig cacheConfig = CacheConfig.custom()
        .setSharedCache(false).
        setMaxObjectSize(153600L).
        setMaxCacheEntries(600)
        .build();

    this.httpClient = CachingHttpClients.custom()
        .setCacheConfig(cacheConfig)
        .disableAutomaticRetries()
        .setConnectionManager(connectionManager)
        .setDefaultRequestConfig(NetworkDefaults.getDefaultRequestConfig())
        .setUserAgent("test user agent")
        .build();
  }


  public int makeRequest(String url, String username, String password) {
    int httpStatusCode = -1;
    HttpGet httpget = new HttpGet(url);
    try {
      System.out.println(" >>>>>> " + url);
      CloseableHttpResponse response;
        response = httpClient.execute(httpget,
            createContext(url, username, password.toCharArray()));
      httpStatusCode = response.getCode();
      System.out.println("<------" + httpStatusCode + ":" + response);
    } catch (Exception exception) {
      System.out.println("IWA : network request failed: ");
    }

    return httpStatusCode;
  }


  public HttpClientContext createContext(String surl, String username, char[] 
password) throws Exception{
    URL url = new URL(surl);
    String host = url.getHost();
    int port = url.getPort();
    AuthScope authScope = new AuthScope(host, port);
    int finalPort = port >= 0 ? port : 443;
    AuthScope ntlmAuthScope = new AuthScope(null, host, finalPort, null, 
"NTLM");
    HttpClientContext httpClientContext = HttpClientContext.create();

    UsernamePasswordCredentials upc = new UsernamePasswordCredentials(username, 
password);
    NTCredentials ntc = new NTCredentials(this.processUsername(username), 
password, host, null);
    BasicCredentialsProvider provider = new BasicCredentialsProvider();

    provider.setCredentials(authScope, upc);
    provider.setCredentials(ntlmAuthScope, ntc);
    httpClientContext.setCredentialsProvider(provider);
    return httpClientContext;
  }

  public String processUsername(String username) {
    String[] split = username.split("\\\\", 2);
    if (split.length == 2) {
      return split[1] + "@" + split[0];
    }
    return username;
  }
}



{code}
 


was (Author: erick_001):
[~olegk]

I have simplified the repro code to one file using http-client 5.0.4.

We are still working on getting a public NTLM endpoint, 

meanwhile if you have an NTLM service you can just replace the requestUris with 
one single endpoint and repeat the request several times. 
{code:java}
public class JavaHttpClientWrapper {
  public final CloseableHttpClient httpClient;
  
  public static void main(String[] args) {
    String username = "user";
    String password = "password";

    final String[] requestUris = new String[] {
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/0/0/0";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/63/0/0";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/16/26166/11433";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/15/13083/5716";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/1/0/0";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/16/26166/11432";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/14/6541/2858";,
        
"https://localhost/server/rest/services/CERT_Secured_Basemap/MapServer/tile/15/13082/5716";
    };

    try {
      JavaHttpClientWrapper httpClientWrapper = new JavaHttpClientWrapper();
      for(String uri : requestUris) {
        new Thread(() -> {
          httpClientWrapper.makeRequest(uri, username, password);
        }).start();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

  }

  public JavaHttpClientWrapper() throws Exception {

    X509TrustManager trustManager = new X509TrustManager() {
      @Override public void checkClientTrusted(X509Certificate[] 
x509Certificates, String s)
      throws CertificateException {}
      @Override public void checkServerTrusted(X509Certificate[] 
x509Certificates, String s)
      throws CertificateException {}
      @Override public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
      }
    };
    TrustManager[] trustManagers = new TrustManager[] { trustManager };
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagers, null);

    SSLConnectionSocketFactory sslConnectionSocketFactory = 
SSLConnectionSocketFactoryBuilder.create()
        .setSslContext(sslContext)
        .setTlsVersions(TLS.V_1_2)
        .setHostnameVerifier(new DefaultHostnameVerifier())
        .build();

    Registry sslSocketConnectionRegistry = RegistryBuilder.create()
        .register("http", PlainConnectionSocketFactory.INSTANCE)
        .register("https", sslConnectionSocketFactory).build();

    PoolingHttpClientConnectionManager connectionManager = new 
PoolingHttpClientConnectionManager(sslSocketConnectionRegistry);
    connectionManager.setValidateAfterInactivity(TimeValue.of(1L, 
TimeUnit.SECONDS));
    connectionManager.setMaxTotal(50);
    connectionManager.setDefaultMaxPerRoute(50);

    CacheConfig cacheConfig = CacheConfig.custom()
        .setSharedCache(false).
        setMaxObjectSize(153600L).
        setMaxCacheEntries(600)
        .build();

    this.httpClient = CachingHttpClients.custom()
        .setCacheConfig(cacheConfig)
        .disableAutomaticRetries()
        .setConnectionManager(connectionManager)
        .setDefaultRequestConfig(NetworkDefaults.getDefaultRequestConfig())
        .setUserAgent("test user agent")
        .build();
  }


  public int makeRequest(String url, String username, String password) {
    int httpStatusCode = -1;
    HttpGet httpget = new HttpGet(url);
    try {
      System.out.println(" >>>>>> " + url);
      CloseableHttpResponse response;
        response = httpClient.execute(httpget,
            createContext(url, username, password.toCharArray()));
      httpStatusCode = response.getCode();
      System.out.println("<------" + httpStatusCode + ":" + response);
    } catch (Exception exception) {
      System.out.println("IWA : network request failed: ");
    }

    return httpStatusCode;
  }


  public HttpClientContext createContext(String surl, String username, char[] 
password) throws Exception{
    URL url = new URL(surl);
    String host = url.getHost();
    int port = url.getPort();
    AuthScope authScope = new AuthScope(host, port);
    int finalPort = port >= 0 ? port : 443;
    AuthScope ntlmAuthScope = new AuthScope(null, host, finalPort, null, 
"NTLM");
    HttpClientContext httpClientContext = HttpClientContext.create();

    UsernamePasswordCredentials upc = new UsernamePasswordCredentials(username, 
password);
    NTCredentials ntc = new NTCredentials(this.processUsername(username), 
password, host, null);
    BasicCredentialsProvider provider = new BasicCredentialsProvider();

    provider.setCredentials(authScope, upc);
    provider.setCredentials(ntlmAuthScope, ntc);
    httpClientContext.setCredentialsProvider(provider);
    return httpClientContext;
  }

  public String processUsername(String username) {
    String[] split = username.split("\\\\", 2);
    if (split.length == 2) {
      return split[1] + "@" + split[0];
    }
    return username;
  }
}



{code}
 

> NTLM Authentication not working when sending multiple request concurrently
> --------------------------------------------------------------------------
>
>                 Key: HTTPCLIENT-2170
>                 URL: https://issues.apache.org/jira/browse/HTTPCLIENT-2170
>             Project: HttpComponents HttpClient
>          Issue Type: Bug
>          Components: HttpClient (classic)
>    Affects Versions: 5.0.4, 5.1
>         Environment: Java 1.8 and Android 
>            Reporter: Erick 
>            Priority: Minor
>              Labels: 5.04, 5.1, NTLM, http-client
>         Attachments: NTLMTestSample.zip
>
>
> We migrated our apache version from 4.5.x to 5.0.4 and we have encountered an 
> authentication error using NTLM. 
> We are making multiple requests in different threads to a NTLM secured server 
> concurrently such as: 
> {code:java}
> private fun test_Standalone() {    val username = "username"    val password 
> = "password"    val serverName = "localhost"
>     val requestUrls = arrayOf(        
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/0/0/0";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/63/0/0";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/16/26166/11433";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/15/13083/5716";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/1/0/0";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/16/26166/11432";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/14/6541/2858";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/15/13082/5716";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/15/13083/5715";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/16/26166/11431";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/15/13082/5715";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/14/6541/2857";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/16/26165/11434";,
>         
> "https://${serverName}/server/rest/services/CERT_Secured_Basemap/MapServer/tile/15/13082/5717";
>     )
>     for(url in requestUrls) {        makeRequestAsync(url, username, 
> password)    }}
> {code}
> Some of the data request succeed but others fail with a *401 Unauthorized.*
> **the output looks something like this:
>  
> {code:java}
> <------[Thread-7] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-1] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-4] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-6] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-3] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-10] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-2] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-9] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-13] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-12] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-0] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-11] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-5] 401 :401 Unauthorized HTTP/1.1
> <------[Thread-8] 200 :200 OK HTTP/1.1
> {code}
>  
> Looking at the logs, it seems that the NTLM handshake fails for some request 
> **We found that by synchronizing the method 
> [ProtocolExec.execute()|https://github.com/apache/httpcomponents-client/blob/5.0.x/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProtocolExec.java#L103],
>  all the NTLM authentication requests succeed and we are able to fetch the 
> data successfully. 
>  
> Attached is the repro java project that we are using. 
> The project only needs an NTLM server and credentials to run. 
>  
> We tested version 5.1 and it was also reproducible. 
>  



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

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to