All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.clarolab.bamboo.client.HttpClient Maven / Gradle / Ivy

/*
 * Copyright (c) 2019, Clarolab. All rights reserved.
 */

package com.clarolab.bamboo.client;

import com.clarolab.bamboo.utils.Constants;
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.Builder;
import lombok.Data;
import lombok.extern.java.Log;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.TruncatedChunkException;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.json.XML;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;

@Data
@Log
public class HttpClient {

    //TODO: Make improvements using https://www.baeldung.com/httpclient-connection-management

    private String baseUrl;
    private String userName;
    private String userPassword;
    private CloseableHttpClient client;
    private HttpGet get;
    private CloseableHttpResponse response;

    @Builder
    public HttpClient(String baseUrl, String userName, String userPassword) {
        this.baseUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl+"/";
        this.userName = userName;
        this.userPassword = userPassword;
        client = HttpClients.custom()
                .setRetryHandler(this.retryHandler())
                .setSSLContext(baseUrl.startsWith("https") ? this.getSslContext() : null)
                .setSSLHostnameVerifier(baseUrl.startsWith("https") ? new NoopHostnameVerifier() : null)
                .build();
    }

    private SSLContext getSslContext(){
        SSLContext sslContext = null;
        try {
            sslContext = new SSLContextBuilder().loadTrustMaterial(null, (certificate, authType) -> true).build();
        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
            log.log(Level.SEVERE, "Error getting: " + baseUrl, e);
        }
        return sslContext;
    }

    private HttpRequestRetryHandler retryHandler(){
        return (exception, executionCount, context) -> {
            log.info("Try request: " + executionCount);
            if (executionCount >= Constants.DEFAULT_HTTP_MAX_RETRY) {
                // Do not retry if over max retry count
                return false;
            }
            if (exception instanceof InterruptedIOException ||
                exception instanceof UnknownHostException ||
                exception instanceof SSLException ||
                exception instanceof ConnectException ||
//                exception instanceof ConnectorServiceException ||
//                exception instanceof ContainerServiceException ||
//                exception instanceof ExecutorServiceException ||
//                exception instanceof BuildServiceException ||
//                exception instanceof ReportServiceException ||
                exception instanceof TruncatedChunkException) {
                // Timeout || Unknown host || SSL handshake || Services
                return false;
            }
            if (!(HttpClientContext.adapt(context).getRequest() instanceof HttpEntityEnclosingRequest)) {
                // Retry if the request is considered idempotent
                return true;
            }
            return false;
        };
    }

    public String get(String baseUrl, String getRequest) throws Exception {
        this.prepareRequest(baseUrl, getRequest);
        return this.executeRequest();
    }

    public  T get(String request, Class classOfT) throws Exception {
        return this.get(null, request, classOfT);
    }

    public  T get(GsonBuilder builder, String request, Class classOfT) throws Exception {
        if (builder == null)
            return new Gson().fromJson(this.get(request), classOfT);
        else
            return builder.create().fromJson(this.get(request), classOfT);
    }

    public String get(String getRequest) throws IOException, AuthenticationException, URISyntaxException {
        this.prepareRequest(this.baseUrl, getRequest);
        return this.executeRequest();
    }

    private String executeRequest() throws IOException {
        HttpEntity entity = null;
        String entityStr;
        try {
            log.info("Curl to execute: "+generateCurlSentence(this.get));
            this.performRequest();
            this.validateResponse();
            entity = this.getEntity(this.response);
            entityStr = EntityUtils.toString(entity);
            //entityStr = this.convertEntityToString(entity);
        } finally {
            EntityUtils.consume(entity);
            this.clean();
            //client.close();
        }
        return entityStr;
    }

    public void prepareRequest(String getRequest) throws Exception {
        this.prepareRequest(this.baseUrl, getRequest);
    }

    public void prepareRequest(String baseUrl, String getRequest) throws AuthenticationException, URISyntaxException {
        String requestURL = this.normalizeRequest(baseUrl, getRequest);
        HttpGet httpGetRequest = new HttpGet(new URI(requestURL));
        httpGetRequest.addHeader(new BasicScheme(StandardCharsets.UTF_8).authenticate(new UsernamePasswordCredentials(userName, userPassword), httpGetRequest, null));
        httpGetRequest.addHeader("Accept", "application/json");
        this.get = httpGetRequest;
    }

    public void performRequest() throws IOException {
        this.response = client.execute(get);
    }

    public void validateResponse() throws HttpResponseException {
        int status = this.response.getStatusLine().getStatusCode();
        if (status < 200 || status >= 400) {
            throw new HttpResponseException(status, this.response.getStatusLine().getReasonPhrase());
        }
    }

    public HttpEntity getEntity(CloseableHttpResponse response){
        HttpEntity entity = this.response.getEntity();
        return entity;
    }

    public void clean() throws IOException {
        if(this.response != null)
            this.response.close();
        if(this.get != null)
            this.get.releaseConnection();
    }

    public String getJson(String baseUrl, String request) throws Exception {
        String json = null;
        for(int i = 0; i<5; i++) {
            json = XML.toJSONObject(this.get(baseUrl, request)).toString(4);
            if(!Strings.isNullOrEmpty(json))
                break;
        }
        return json;
    }


    /**
     * Return an equivalent curl sentence of the Http request
     */
    private String generateCurlSentence(HttpUriRequest request) {
        StringBuilder curlSentence = generateString(request);
        String url = request.getURI().toString();
        String method = request.getMethod();
        curlSentence.append(" -X ").append(method).append(" ");
        curlSentence.append("\"").append(url).append("\"").append(" --insecure");
        return curlSentence.toString();
    }

    private StringBuilder generateString(HttpUriRequest request) {
        StringBuilder curlSentence = new StringBuilder();
        curlSentence.append("curl");

        Header authHeader = request.getFirstHeader("Authorization");
        if (authHeader != null) {
            try {
                String credentials = new String(Base64.decodeBase64(authHeader.getValue().replace("Basic ", "")),"UTF-8");
                curlSentence.append(" -u ");
                curlSentence.append(credentials);
            }
            catch (UnsupportedEncodingException e) {
            }
        }

        Header[] allHeaders = request.getAllHeaders();
        curlSentence.append(" --header '");
        for (Header h : allHeaders) {
            if (!h.getName().contains("Authorization")) {
                curlSentence.append(h.toString()).append(";");
            }
        }
        curlSentence.append("'");
        return curlSentence;
    }

    public void close(){
        try {
            this.client.close();
        } catch (IOException e) {
            log.log(Level.WARNING, "I/O exception closing client", e);
        }
    }

    private String normalizeRequest(String baseUrl, String getRequest){
        String retUrl;
        if(!baseUrl.endsWith("/") && !getRequest.startsWith("/"))
            retUrl = baseUrl + "/" + getRequest;
        else if(baseUrl.endsWith("/") && getRequest.startsWith("/"))
            retUrl = baseUrl + getRequest.substring(1);
        else
            retUrl = baseUrl + getRequest;
        return retUrl.replace(" ", "%20");
    }

//    private String convertEntityToString(HttpEntity entity) throws IOException {
//        String out = null;
//        if (entity != null) {
//            InputStream inStream = entity.getContent();
//            byte[] bytes = IOUtils.toByteArray(inStream);
//            out = new String(bytes, "UTF-8");
//            inStream.close();
//        }
//        return out;
//    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy