com.intuit.tank.httpclient5.TankHttpClient5.java Source code

Java tutorial

Introduction

Here is the source code for com.intuit.tank.httpclient5.TankHttpClient5.java

Source

package com.intuit.tank.httpclient5;

import java.io.ByteArrayInputStream;

/*
 * #%L
 * Intuit Tank Agent (apiharness)
 * %%
 * Copyright (C) 2011 - 2015 Intuit Inc.
 * %%
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * #L%
 */

import java.io.ByteArrayOutputStream;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;

import javax.annotation.Nonnull;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.NTCredentials;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.config.CookieSpecs;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.cookie.Cookie;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
import org.apache.hc.client5.http.impl.sync.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.sync.CloseableHttpClient;
import org.apache.hc.client5.http.impl.sync.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.sync.HttpClients;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.sync.methods.HttpDelete;
import org.apache.hc.client5.http.sync.methods.HttpGet;
import org.apache.hc.client5.http.sync.methods.HttpOptions;
import org.apache.hc.client5.http.sync.methods.HttpPost;
import org.apache.hc.client5.http.sync.methods.HttpPut;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.intuit.tank.http.AuthCredentials;
import com.intuit.tank.http.AuthScheme;
import com.intuit.tank.http.BaseRequest;
import com.intuit.tank.http.BaseResponse;
import com.intuit.tank.http.TankCookie;
import com.intuit.tank.http.TankHttpClient;
import com.intuit.tank.http.TankHttpUtil;
import com.intuit.tank.http.TankHttpUtil.PartHolder;
import com.intuit.tank.logging.LogEventType;
import com.intuit.tank.vm.settings.AgentConfig;

public class TankHttpClient5 implements TankHttpClient {

    static Logger LOG = LogManager.getLogger(TankHttpClient5.class);

    private CloseableHttpClient httpclient;
    private HttpClientContext context;
    private RequestConfig requestConfig;
    private SSLConnectionSocketFactory sslsf;
    private HttpClientConnectionManager cm;
    private boolean proxyOn = false;

    /**
     * no-arg constructor for client
     */
    public TankHttpClient5() {
        sslsf = new SSLConnectionSocketFactory(SSLContexts.createDefault(), NoopHostnameVerifier.INSTANCE);
        cm = PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(sslsf).build();

        httpclient = HttpClients.custom().setConnectionManager(cm).build();
        requestConfig = RequestConfig.custom().setSocketTimeout(30, TimeUnit.SECONDS)
                .setConnectTimeout(30, TimeUnit.SECONDS).setCircularRedirectsAllowed(true)
                .setAuthenticationEnabled(true).setRedirectsEnabled(true).setCookieSpec(CookieSpecs.STANDARD)
                .setMaxRedirects(100).build();

        // Make sure the same context is used to execute logically related
        // requests
        context = HttpClientContext.create();
        context.setCredentialsProvider(new BasicCredentialsProvider());
        context.setCookieStore(new BasicCookieStore());
        context.setRequestConfig(requestConfig);
    }

    public void setConnectionTimeout(long connectionTimeout) {
        requestConfig = RequestConfig.custom().setSocketTimeout(30, TimeUnit.SECONDS)
                .setConnectTimeout((int) connectionTimeout, TimeUnit.MILLISECONDS).setCircularRedirectsAllowed(true)
                .setAuthenticationEnabled(true).setRedirectsEnabled(true).setCookieSpec(CookieSpecs.STANDARD)
                .setMaxRedirects(100).build();
        context.setRequestConfig(requestConfig);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.intuit.tank.httpclient3.TankHttpClient#doGet(com.intuit.tank.http.
     * BaseRequest)
     */
    @Override
    public void doGet(BaseRequest request) {
        HttpGet httpget = new HttpGet(request.getRequestUrl());
        sendRequest(request, httpget, request.getBody());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.intuit.tank.httpclient3.TankHttpClient#doPut(com.intuit.tank.http.
     * BaseRequest)
     */
    @Override
    public void doPut(BaseRequest request) {
        HttpPut httpput = new HttpPut(request.getRequestUrl());
        // Multiple calls can be expensive, so get it once
        String requestBody = request.getBody();
        HttpEntity entity = new StringEntity(requestBody,
                ContentType.create(request.getContentType(), request.getContentTypeCharSet()));
        httpput.setEntity(entity);
        sendRequest(request, httpput, requestBody);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.intuit.tank.httpclient3.TankHttpClient#doDelete(com.intuit.tank.http.
     * BaseRequest)
     */
    @Override
    public void doDelete(BaseRequest request) {
        HttpDelete httpdelete = new HttpDelete(request.getRequestUrl());
        // Multiple calls can be expensive, so get it once
        String requestBody = request.getBody();
        String type = request.getHeaderInformation().get("Content-Type");
        if (StringUtils.isBlank(type)) {
            request.getHeaderInformation().put("Content-Type", "application/json");
        }
        sendRequest(request, httpdelete, requestBody);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.intuit.tank.httpclient3.TankHttpClient#doOptions(com.intuit.tank.http.
     * BaseRequest)
     */
    @Override
    public void doOptions(BaseRequest request) {
        HttpOptions httpoptions = new HttpOptions(request.getRequestUrl());
        // Multiple calls can be expensive, so get it once
        String requestBody = request.getBody();
        String type = request.getHeaderInformation().get("Content-Type");
        if (StringUtils.isBlank(type)) {
            request.getHeaderInformation().put("Content-Type", "application/json");
        }
        sendRequest(request, httpoptions, requestBody);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.intuit.tank.httpclient3.TankHttpClient#doPost(com.intuit.tank.http.
     * BaseRequest)
     */
    @Override
    public void doPost(BaseRequest request) {
        HttpPost httppost = new HttpPost(request.getRequestUrl());
        String requestBody = request.getBody();
        HttpEntity entity = null;
        if (BaseRequest.CONTENT_TYPE_MULTIPART.equalsIgnoreCase(request.getContentType())) {
            entity = buildParts(request);
        } else {
            entity = new StringEntity(requestBody,
                    ContentType.create(request.getContentType(), request.getContentTypeCharSet()));
        }
        httppost.setEntity(entity);
        sendRequest(request, httppost, requestBody);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.intuit.tank.httpclient3.TankHttpClient#addAuth(com.intuit.tank.http.
     * AuthCredentials)
     */
    @Override
    public void addAuth(AuthCredentials creds) {
        String host = (StringUtils.isBlank(creds.getHost()) || "*".equals(creds.getHost())) ? AuthScope.ANY_HOST
                : creds.getHost();
        String realm = (StringUtils.isBlank(creds.getRealm()) || "*".equals(creds.getRealm())) ? AuthScope.ANY_REALM
                : creds.getRealm();
        int port = NumberUtils.toInt(creds.getPortString(), AuthScope.ANY_PORT);
        String scheme = creds.getScheme() != null ? creds.getScheme().getRepresentation() : AuthScope.ANY_SCHEME;
        AuthScope scope = new AuthScope(host, port, realm, scheme);
        BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        if (AuthScheme.NTLM == creds.getScheme()) {
            credentialsProvider.setCredentials(scope, new NTCredentials(creds.getUserName(),
                    creds.getPassword().toCharArray(), "tank-test", creds.getRealm()));
        } else {
            credentialsProvider.setCredentials(scope,
                    new UsernamePasswordCredentials(creds.getUserName(), creds.getPassword().toCharArray()));
        }
        context.setCredentialsProvider(credentialsProvider);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.intuit.tank.httpclient3.TankHttpClient#clearSession()
     */
    @Override
    public void clearSession() {
        context.getCookieStore().clear();
    }

    /**
     * 
     */
    @Override
    public void setCookie(TankCookie cookie) {
        BasicClientCookie c = new BasicClientCookie(cookie.getName(), cookie.getValue());
        c.setDomain(cookie.getDomain());
        c.setPath(cookie.getPath());
        context.getCookieStore().addCookie(c);

    }

    @Override
    public void setProxy(String proxyhost, int proxyport) {
        if (StringUtils.isNotBlank(proxyhost)) {
            HttpHost proxy = new HttpHost(proxyhost, proxyport);
            DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
            httpclient = HttpClients.custom().setConnectionManager(cm).setRoutePlanner(routePlanner).build();
            proxyOn = true;
        } else if (proxyOn) {
            httpclient = HttpClients.custom().setConnectionManager(cm).build();
            proxyOn = false;
        }
    }

    public void setConnectionTimeout(long connectionTimeout, long socketTimeout) {
        requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout, TimeUnit.MILLISECONDS)
                .setConnectTimeout(connectionTimeout, TimeUnit.MILLISECONDS).setCircularRedirectsAllowed(true)
                .setAuthenticationEnabled(true).setRedirectsEnabled(true).setCookieSpec(CookieSpecs.STANDARD)
                .setMaxRedirects(100).build();
        context.setRequestConfig(requestConfig);
    }

    private void sendRequest(BaseRequest request, @Nonnull ClassicHttpRequest method, String requestBody) {
        String uri = null;
        long waitTime = 0L;
        CloseableHttpResponse response = null;
        try {
            uri = method.getRequestUri();
            LOG.debug(request.getLogUtil().getLogMessage(
                    "About to " + method.getMethod() + " request to " + uri + " with requestBody  " + requestBody,
                    LogEventType.Informational));
            List<String> cookies = new ArrayList<String>();
            if (context.getCookieStore().getCookies() != null) {
                for (Cookie cookie : context.getCookieStore().getCookies()) {
                    cookies.add("REQUEST COOKIE: " + cookie.toString());
                }
            }
            request.logRequest(uri, requestBody, method.getMethod(), request.getHeaderInformation(), cookies,
                    false);
            setHeaders(request, method, request.getHeaderInformation());
            long startTime = System.currentTimeMillis();
            request.setTimestamp(new Date(startTime));
            response = httpclient.execute(method, context);

            // read response body
            byte[] responseBody = new byte[0];
            // check for no content headers
            if (response.getCode() != 203 && response.getCode() != 202 && response.getCode() != 204) {
                try {
                    responseBody = IOUtils.toByteArray(response.getEntity().getContent());
                } catch (Exception e) {
                    LOG.warn("could not get response body: " + e);
                }
            }
            waitTime = System.currentTimeMillis() - startTime;
            processResponse(responseBody, waitTime, request, response.getReasonPhrase(), response.getCode(),
                    response.getAllHeaders());

        } catch (UnknownHostException uhex) {
            LOG.error(request.getLogUtil().getLogMessage(
                    "UnknownHostException to url: " + uri + " |  error: " + uhex.toString(), LogEventType.IO),
                    uhex);
        } catch (SocketException sex) {
            LOG.error(
                    request.getLogUtil().getLogMessage(
                            "SocketException to url: " + uri + " |  error: " + sex.toString(), LogEventType.IO),
                    sex);
        } catch (Exception ex) {
            LOG.error(request.getLogUtil().getLogMessage(
                    "Could not do " + method.getMethod() + " to url " + uri + " |  error: " + ex.toString(),
                    LogEventType.IO), ex);
            throw new RuntimeException(ex);
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (Exception e) {
                LOG.warn("Could not release connection: " + e, e);
            }
            if (method.getMethod().equalsIgnoreCase("post")
                    && request.getLogUtil().getAgentConfig().getLogPostResponse()) {
                LOG.info(request.getLogUtil()
                        .getLogMessage("Response from POST to " + request.getRequestUrl() + " got status code "
                                + request.getResponse().getHttpCode() + " BODY { " + request.getResponse().getBody()
                                + " }", LogEventType.Informational));
            }
        }
        if (waitTime != 0) {
            doWaitDueToLongResponse(request, waitTime, uri);
        }
    }

    /**
     * Wait for the amount of time it took to get a response from the system if
     * the response time is over some threshold specified in the properties
     * file. This will ensure users don't bunch up together after a blip on the
     * system under test
     * 
     * @param responseTime
     *            - response time of the request; this will also be the time to
     *            sleep
     * @param uri
     */
    private void doWaitDueToLongResponse(BaseRequest request, long responseTime, String uri) {
        try {
            AgentConfig config = request.getLogUtil().getAgentConfig();
            long maxAgentResponseTime = config.getMaxAgentResponseTime();
            if (maxAgentResponseTime < responseTime) {
                long waitTime = Math.min(config.getMaxAgentWaitTime(), responseTime);
                LOG.warn(request.getLogUtil().getLogMessage(
                        "Response time to slow | delaying " + waitTime + " ms | url --> " + uri,
                        LogEventType.Script));
                Thread.sleep(waitTime);
            }
        } catch (InterruptedException e) {
            LOG.warn("Interrupted", e);
        }
    }

    /**
     * Process the response data
     */
    private void processResponse(byte[] bResponse, long waitTime, BaseRequest request, String message, int httpCode,
            Header[] headers) {
        BaseResponse response = request.getResponse();
        try {
            if (response == null) {
                // Get response header information
                String contentType = "";
                for (Header h : headers) {
                    if ("ContentType".equalsIgnoreCase(h.getName())) {
                        contentType = h.getValue();
                        break;
                    }
                }
                response = TankHttpUtil.newResponseObject(contentType);
                request.setResponse(response);
            }

            // Get response detail information
            response.setHttpMessage(message);
            response.setHttpCode(httpCode);

            // Get response header information
            for (int h = 0; h < headers.length; h++) {
                response.setHeader(headers[h].getName(), headers[h].getValue());
            }

            if (context.getCookieStore().getCookies() != null) {
                for (Cookie cookie : context.getCookieStore().getCookies()) {
                    response.setCookie(cookie.getName(), cookie.getValue());
                }
            }
            response.setResponseTime(waitTime);
            String contentType = response.getHttpHeader("Content-Type");
            String contentEncode = response.getHttpHeader("Content-Encoding");
            if (BaseResponse.isDataType(contentType) && contentEncode != null
                    && contentEncode.toLowerCase().contains("gzip")) {
                // decode gzip for data types
                try {
                    GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(bResponse));
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    IOUtils.copy(in, out);
                    bResponse = out.toByteArray();
                } catch (Exception e) {
                    LOG.warn(request.getLogUtil().getLogMessage("cannot decode gzip stream: " + e,
                            LogEventType.System));
                }
            }
            response.setResponseBody(bResponse);

        } catch (Exception ex) {
            LOG.warn("Unable to get response: " + ex.getMessage());
        } finally {
            response.logResponse();
        }
    }

    /**
     * Set all the header keys
     * 
     * @param connection
     */
    @SuppressWarnings("rawtypes")
    private void setHeaders(BaseRequest request, ClassicHttpRequest method,
            HashMap<String, String> headerInformation) {
        try {
            Set set = headerInformation.entrySet();
            Iterator iter = set.iterator();

            while (iter.hasNext()) {
                Map.Entry mapEntry = (Map.Entry) iter.next();
                method.setHeader((String) mapEntry.getKey(), (String) mapEntry.getValue());
            }
        } catch (Exception ex) {
            LOG.warn(request.getLogUtil().getLogMessage("Unable to set header: " + ex.getMessage(),
                    LogEventType.System));
        }
    }

    private HttpEntity buildParts(BaseRequest request) {
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        for (PartHolder h : TankHttpUtil.getPartsFromBody(request)) {
            if (h.getFileName() == null) {
                if (h.isContentTypeSet()) {
                    builder.addTextBody(h.getPartName(), new String(h.getBodyAsString()),
                            ContentType.create(h.getContentType()));
                } else {
                    builder.addTextBody(h.getPartName(), new String(h.getBodyAsString()));
                }
            } else {
                if (h.isContentTypeSet()) {
                    builder.addBinaryBody(h.getPartName(), h.getBody(), ContentType.create(h.getContentType()),
                            h.getFileName());
                } else {
                    builder.addBinaryBody(h.getFileName(), h.getBody());
                }
            }
        }
        return builder.build();
    }

}