com.intuit.karate.http.apache.ApacheHttpClient.java Source code

Java tutorial

Introduction

Here is the source code for com.intuit.karate.http.apache.ApacheHttpClient.java

Source

/*
 * The MIT License
 *
 * Copyright 2017 Intuit Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.intuit.karate.http.apache;

import org.apache.http.conn.ssl.LenientSslConnectionSocketFactory;
import com.intuit.karate.ScriptValue;
import static com.intuit.karate.http.Cookie.*;
import com.intuit.karate.http.HttpClient;
import com.intuit.karate.http.HttpConfig;
import com.intuit.karate.http.HttpResponse;
import com.intuit.karate.http.HttpUtils;
import com.intuit.karate.http.MultiPartItem;
import com.intuit.karate.http.MultiValuedMap;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLContext;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.FormBodyPartBuilder;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;

/**
 *
 * @author pthomas3
 */
public class ApacheHttpClient extends HttpClient<HttpEntity> {

    public static final String URI_CONTEXT_KEY = ApacheHttpClient.class.getName() + ".URI";

    private HttpClientBuilder clientBuilder;
    private URIBuilder uriBuilder;
    private RequestBuilder requestBuilder;
    private CookieStore cookieStore;

    private void build() {
        try {
            URI uri = uriBuilder.build();
            String method = request.getMethod();
            requestBuilder = RequestBuilder.create(method).setUri(uri);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void configure(HttpConfig config) {
        clientBuilder = HttpClientBuilder.create();
        cookieStore = new BasicCookieStore();
        clientBuilder.setDefaultCookieStore(cookieStore);
        AtomicInteger counter = new AtomicInteger();
        clientBuilder.addInterceptorLast(new RequestLoggingInterceptor(counter));
        clientBuilder.addInterceptorLast(new ResponseLoggingInterceptor(counter));
        if (config.isSslEnabled()) {
            // System.setProperty("jsse.enableSNIExtension", "false");
            String sslAlgorithm = config.getSslAlgorithm();
            logger.info("ssl enabled, initializing generic trusted certificate / key-store with algorithm: {}",
                    sslAlgorithm);
            SSLContext sslContext = HttpUtils.getSslContext(sslAlgorithm);
            SSLConnectionSocketFactory socketFactory = new LenientSslConnectionSocketFactory(sslContext,
                    new NoopHostnameVerifier());
            clientBuilder.setSSLSocketFactory(socketFactory);
        }

        RequestConfig.Builder configBuilder = RequestConfig.custom().setConnectTimeout(config.getConnectTimeout())
                .setSocketTimeout(config.getReadTimeout());
        clientBuilder.setDefaultRequestConfig(configBuilder.build());
        if (config.getProxyUri() != null) {
            try {
                URI proxyUri = new URIBuilder(config.getProxyUri()).build();
                clientBuilder.setProxy(new HttpHost(proxyUri.getHost(), proxyUri.getPort(), proxyUri.getScheme()));
                if (config.getProxyUsername() != null && config.getProxyPassword() != null) {
                    CredentialsProvider credsProvider = new BasicCredentialsProvider();
                    credsProvider.setCredentials(new AuthScope(proxyUri.getHost(), proxyUri.getPort()),
                            new UsernamePasswordCredentials(config.getProxyUsername(), config.getProxyPassword()));
                    clientBuilder.setDefaultCredentialsProvider(credsProvider);

                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    protected void buildUrl(String url) {
        try {
            uriBuilder = new URIBuilder(url);
            build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void buildPath(String path) {
        String temp = uriBuilder.getPath();
        if (!temp.endsWith("/")) {
            temp = temp + "/";
        }
        if (path.startsWith("/")) {
            path = path.substring(1);
        }
        uriBuilder.setPath(temp + path);
        build();
    }

    @Override
    protected void buildParam(String name, Object... values) {
        for (Object o : values) {
            uriBuilder.setParameter(name, o == null ? "" : o.toString());
        }
        build();
    }

    @Override
    protected void buildHeader(String name, Object value, boolean replace) {
        if (replace) {
            requestBuilder.removeHeaders(name);
        }
        requestBuilder.addHeader(name, value == null ? null : value.toString());
    }

    @Override
    protected void buildCookie(com.intuit.karate.http.Cookie c) {
        BasicClientCookie cookie = new BasicClientCookie(c.getName(), c.getValue());
        for (Entry<String, String> entry : c.entrySet()) {
            switch (entry.getKey()) {
            case DOMAIN:
                cookie.setDomain(entry.getValue());
                break;
            case PATH:
                cookie.setPath(entry.getValue());
                break;
            }
        }
        if (cookie.getDomain() == null) {
            cookie.setDomain(uriBuilder.getHost());
        }
        cookieStore.addCookie(cookie);
    }

    @Override
    protected HttpEntity getEntity(List<MultiPartItem> items, String mediaType) {
        boolean hasNullName = false;
        for (MultiPartItem item : items) {
            if (item.getName() == null) {
                hasNullName = true;
                break;
            }
        }
        if (hasNullName) { // multipart/related
            String boundary = createBoundary();
            String text = getAsStringEntity(items, boundary);
            ContentType ct = ContentType.create(mediaType)
                    .withParameters(new BasicNameValuePair("boundary", boundary));
            return new StringEntity(text, ct);
        } else {
            MultipartEntityBuilder builder = MultipartEntityBuilder.create()
                    .setContentType(ContentType.create(mediaType));
            for (MultiPartItem item : items) {
                if (item.getValue() == null || item.getValue().isNull()) {
                    logger.warn("ignoring null multipart value for key: {}", item.getName());
                    continue;
                }
                String name = item.getName();
                ScriptValue sv = item.getValue();
                if (name == null) {
                    // builder.addPart(bodyPart);
                } else {
                    FormBodyPartBuilder formBuilder = FormBodyPartBuilder.create().setName(name);
                    ContentBody contentBody;
                    switch (sv.getType()) {
                    case INPUT_STREAM:
                        InputStream is = (InputStream) sv.getValue();
                        contentBody = new InputStreamBody(is, ContentType.APPLICATION_OCTET_STREAM, name);
                        break;
                    case XML:
                        contentBody = new StringBody(sv.getAsString(), ContentType.APPLICATION_XML);
                        break;
                    case JSON:
                        contentBody = new StringBody(sv.getAsString(), ContentType.APPLICATION_JSON);
                        break;
                    default:
                        contentBody = new StringBody(sv.getAsString(), ContentType.TEXT_PLAIN);
                    }
                    formBuilder = formBuilder.setBody(contentBody);
                    builder = builder.addPart(formBuilder.build());
                }
            }
            return builder.build();
        }
    }

    @Override
    protected HttpEntity getEntity(MultiValuedMap fields, String mediaType) {
        List<NameValuePair> list = new ArrayList<>(fields.size());
        for (Entry<String, List> entry : fields.entrySet()) {
            String stringValue;
            List values = entry.getValue();
            if (values == null) {
                stringValue = null;
            } else if (values.size() == 1) {
                Object value = values.get(0);
                if (value == null) {
                    stringValue = null;
                } else if (value instanceof String) {
                    stringValue = (String) value;
                } else {
                    stringValue = value.toString();
                }
            } else {
                stringValue = StringUtils.join(values, ',');
            }
            list.add(new BasicNameValuePair(entry.getKey(), stringValue));
        }
        try {
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list);
            entity.setContentType(mediaType);
            return entity;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected HttpEntity getEntity(String value, String mediaType) {
        return new StringEntity(value, ContentType.create(mediaType));
    }

    @Override
    protected HttpEntity getEntity(InputStream value, String mediaType) {
        return new InputStreamEntity(value, ContentType.create(mediaType));
    }

    @Override
    protected HttpResponse makeHttpRequest(HttpEntity entity, long startTime) {
        if (entity != null) {
            requestBuilder.setEntity(entity);
            requestBuilder.setHeader(entity.getContentType());
        }
        HttpUriRequest httpRequest = requestBuilder.build();
        CloseableHttpClient client = clientBuilder.build();
        BasicHttpContext context = new BasicHttpContext();
        context.setAttribute(URI_CONTEXT_KEY, getRequestUri());
        CloseableHttpResponse httpResponse;
        byte[] bytes;
        try {
            httpResponse = client.execute(httpRequest, context);
            HttpEntity responseEntity = httpResponse.getEntity();
            InputStream is = responseEntity.getContent();
            bytes = IOUtils.toByteArray(is);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        long responseTime = getResponseTime(startTime);
        HttpResponse response = new HttpResponse();
        response.setTime(responseTime);
        response.setUri(getRequestUri());
        response.setBody(bytes);
        response.setStatus(httpResponse.getStatusLine().getStatusCode());
        for (Cookie c : cookieStore.getCookies()) {
            com.intuit.karate.http.Cookie cookie = new com.intuit.karate.http.Cookie(c.getName(), c.getValue());
            cookie.put(DOMAIN, c.getDomain());
            cookie.put(PATH, c.getPath());
            if (c.getExpiryDate() != null) {
                cookie.put(EXPIRES, c.getExpiryDate().getTime() + "");
            }
            cookie.put(PERSISTENT, c.isPersistent() + "");
            cookie.put(SECURE, c.isSecure() + "");
            response.addCookie(cookie);
        }
        for (Header header : httpResponse.getAllHeaders()) {
            response.addHeader(header.getName(), header.getValue());
        }
        return response;
    }

    @Override
    protected String getRequestUri() {
        return requestBuilder.getUri().toString();
    }

    private static final AtomicInteger BOUNDARY_COUNTER = new AtomicInteger();

    public static String createBoundary() {
        ;
        StringBuilder sb = new StringBuilder("boundary_");
        sb.append(BOUNDARY_COUNTER.incrementAndGet()).append('_');
        sb.append(System.currentTimeMillis());
        return sb.toString();
    }

    private static String getContentType(ScriptValue sv) {
        switch (sv.getType()) {
        case JSON:
            return APPLICATION_JSON;
        case XML:
            return APPLICATION_XML;
        case INPUT_STREAM:
            return APPLICATION_OCTET_STREAM;
        default:
            return TEXT_PLAIN;
        }
    }

    private static String getAsStringEntity(List<MultiPartItem> items, String boundary) {
        StringBuilder sb = new StringBuilder();
        boolean firstItem = true;
        for (MultiPartItem item : items) {
            if (firstItem) {
                firstItem = false;
                sb.append("--");
            } else {
                sb.append("\r\n--");
            }
            sb.append(boundary);
            sb.append("\r\n");
            ScriptValue sv = item.getValue();
            String contentType = getContentType(sv);
            sb.append("Content-Type: ").append(contentType);
            sb.append("\r\n");
            String name = item.getName();
            if (name != null) {
                sb.append("Content-Disposition: form-data");
                if (sv.getType() == ScriptValue.Type.INPUT_STREAM) {
                    sb.append("; filename=\"").append(name).append("\"");
                }
                sb.append("; name=\"").append(name).append("\"");
                sb.append("\r\n");
            }
            sb.append("\r\n");
            if (sv.getType() == ScriptValue.Type.INPUT_STREAM) {
                InputStream is = sv.getValue(InputStream.class);
                try {
                    byte[] bytes = IOUtils.toByteArray(is);
                    sb.append(new String(bytes, "utf-8"));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            } else {
                sb.append(sv.getAsString());
            }
        }
        sb.append("\r\n--");
        sb.append(boundary);
        sb.append("--\r\n");
        return sb.toString();
    }

}