0

I have created one HTTP Post request with Basic authentication using httpclient (org.apache.httpcomponents).

        CredentialsProvider provider = new BasicCredentialsProvider();
        provider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials("user","pass"));

        RequestConfig requestConfig = RequestConfig.custom().
                setConnectTimeout(2000).
                setSocketTimeout(2000).
                build();
       try (CloseableHttpClient httpClient = HttpClients.custom().
                setDefaultCredentialsProvider(provider).
                setDefaultRequestConfig(requestConfig).
                build()) {
            
            String reqString = mapper.writeValueAsString(obj); // here obj is a Java class of request payload
            StringEntity params = new StringEntity(reqString);
            HttpPost httpPost = new HttpPost(url); // server url where double requests are going
            httpPost.setHeader("Accept", "application/json");
            httpPost.setHeader("Content-type", "application/json");
            httpPost.setEntity(params);    

            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {

                final String jsonString = EntityUtils.toString(response.getEntity());

                if (200 == response.getStatusLine().getStatusCode()) {
                    mapper = new ObjectMapper();
                    
                } 
            }
        }

When CloseableHttpResponse response = httpClient.execute(httpPost) is executed 2 request went to the server, First one without Basic Authorizatin in header and 2nd one with Basic Authorizatin in header. What is the reason for this?

I dig down into the httpclient-4.5.3 library and found that actual point of double request generation is org.apache.http.impl.execchain.MainClientExec. On that class execute method contains a for loop which is actually executed 2 times. I am giving that for loop below...

    for (int execCount = 1;; execCount++) {

            if (execCount > 1 && !RequestEntityProxy.isRepeatable(request)) {
                throw new NonRepeatableRequestException("Cannot retry request " +
                        "with a non-repeatable request entity.");
            }

            if (execAware != null && execAware.isAborted()) {
                throw new RequestAbortedException("Request aborted");
            }

            if (!managedConn.isOpen()) {
                this.log.debug("Opening connection " + route);
                try {
                    establishRoute(proxyAuthState, managedConn, route, request, context);
                } catch (final TunnelRefusedException ex) {
                    if (this.log.isDebugEnabled()) {
                        this.log.debug(ex.getMessage());
                    }
                    response = ex.getResponse();
                    break;
                }
            }
            final int timeout = config.getSocketTimeout();
            if (timeout >= 0) {
                managedConn.setSocketTimeout(timeout);
            }

            if (execAware != null && execAware.isAborted()) {
                throw new RequestAbortedException("Request aborted");
            }

            if (this.log.isDebugEnabled()) {
                this.log.debug("Executing request " + request.getRequestLine());
            }

            if (!request.containsHeader(AUTH.WWW_AUTH_RESP)) {
                if (this.log.isDebugEnabled()) {
                    this.log.debug("Target auth state: " + targetAuthState.getState());
                }
                this.authenticator.generateAuthResponse(request, targetAuthState, context);
            }
            if (!request.containsHeader(AUTH.PROXY_AUTH_RESP) && !route.isTunnelled()) {
                if (this.log.isDebugEnabled()) {
                    this.log.debug("Proxy auth state: " + proxyAuthState.getState());
                }
                this.authenticator.generateAuthResponse(request, proxyAuthState, context);
            }

            response = requestExecutor.execute(request, managedConn, context);

            // The connection is in or can be brought to a re-usable state.
            if (reuseStrategy.keepAlive(response, context)) {
                // Set the idle duration of this connection
                final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
                if (this.log.isDebugEnabled()) {
                    final String s;
                    if (duration > 0) {
                        s = "for " + duration + " " + TimeUnit.MILLISECONDS;
                    } else {
                        s = "indefinitely";
                    }
                    this.log.debug("Connection can be kept alive " + s);
                }
                connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
                connHolder.markReusable();
            } else {
                connHolder.markNonReusable();
            }

            if (needAuthentication(
                    targetAuthState, proxyAuthState, route, response, context)) {
                // Make sure the response body is fully consumed, if present
                final HttpEntity entity = response.getEntity();
                if (connHolder.isReusable()) {
                    EntityUtils.consume(entity);
                } else {
                    managedConn.close();
                    if (proxyAuthState.getState() == AuthProtocolState.SUCCESS
                            && proxyAuthState.getAuthScheme() != null
                            && proxyAuthState.getAuthScheme().isConnectionBased()) {
                        this.log.debug("Resetting proxy auth state");
                        proxyAuthState.reset();
                    }
                    if (targetAuthState.getState() == AuthProtocolState.SUCCESS
                            && targetAuthState.getAuthScheme() != null
                            && targetAuthState.getAuthScheme().isConnectionBased()) {
                        this.log.debug("Resetting target auth state");
                        targetAuthState.reset();
                    }
                }
                // discard previous auth headers
                final HttpRequest original = request.getOriginal();
                if (!original.containsHeader(AUTH.WWW_AUTH_RESP)) {
                    request.removeHeaders(AUTH.WWW_AUTH_RESP);
                }
                if (!original.containsHeader(AUTH.PROXY_AUTH_RESP)) {
                    request.removeHeaders(AUTH.PROXY_AUTH_RESP);
                }
            } else {
                break;
            }
        }

Can any one give me some idea why the first request does not contain any basic Authorization in header but the second one contains that?

1 Answer 1

0

After debugging further within the httpclient-4.5.3 library, I found that, there is two states (targetAuthState, proxyAuthState) there. Initially both of the states have value UNCHALLEGED.

So the library at first send a request without any Authorization value in header to check if any authentication scheme is required or not. If the returned value is 4XX, then targetAuthState value is changed to CHALLEGED. And this time library send request with Authorization value in header.

But if any one do not want this behavior, in that case basic Authorization have to be set directly without using CredentialsProvider.

byte[] credentials = Base64.encodeBase64(("user" + ":" + "pass").getBytes(StandardCharsets.UTF_8)); httpPost.setHeader("Authorization", "Basic " + new String(credentials, StandardCharsets.UTF_8));

The complete code will be like follwoing.

    RequestConfig requestConfig = RequestConfig.custom().
            setConnectTimeout(2000).
            setSocketTimeout(2000).
            build();
   try (CloseableHttpClient httpClient = HttpClients.custom().
            setDefaultRequestConfig(requestConfig).
            build()) {
        
        String reqString = mapper.writeValueAsString(obj); // here obj is a Java class of request payload
        StringEntity params = new StringEntity(reqString);
        HttpPost httpPost = new HttpPost(url); // server url where double requests are going
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-type", "application/json");
        byte[] credentials = Base64.encodeBase64(("user" + ":" + "pass").getBytes(StandardCharsets.UTF_8));
        httpPost.setHeader("Authorization", "Basic " + new String(credentials, StandardCharsets.UTF_8));
        httpPost.setEntity(params);    

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {

            final String jsonString = EntityUtils.toString(response.getEntity());

            if (200 == response.getStatusLine().getStatusCode()) {
                mapper = new ObjectMapper();
                
            } 
        }
    }    
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.