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

org.dstadler.commons.http.HttpAsyncClientWrapper Maven / Gradle / Ivy

There is a newer version: 1.3.5
Show newest version
package org.dstadler.commons.http;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.util.EntityUtils;
import org.dstadler.commons.logging.jdk.LoggerFactory;

import javax.net.ssl.SSLContext;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;

public class HttpAsyncClientWrapper extends AbstractClientWrapper implements Closeable {
    private final static Logger log = LoggerFactory.make();

    private final CloseableHttpAsyncClient httpClient;

    /**
     * Construct the {@link HttpClient} with the given authentication values
     * and all timeouts set to the given number of milliseconds
     *
     * @param user The username for basic authentication, use an empty string when no authentication is required
     * @param password The password for basic authentication, null when no authentication is required
     * @param timeoutMs The timeout for socket connection and reading, specified in milliseconds
     */
    public HttpAsyncClientWrapper(String user, String password, int timeoutMs) {
        super(timeoutMs, true);

        RequestConfig reqConfig = RequestConfig.custom()
                .setSocketTimeout(timeoutMs)
                .setConnectTimeout(timeoutMs)
                .setConnectionRequestTimeout(timeoutMs)
                .build();

        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(
                new AuthScope(null, -1),
                new UsernamePasswordCredentials(user, password));

        HttpAsyncClientBuilder builder = HttpAsyncClients.custom()
                .setDefaultCredentialsProvider(credsProvider)
                .setDefaultRequestConfig(reqConfig);

        try {
            // create permissive ssl context
            final SSLContext sslcontext = createSSLContext();
            builder.setSSLContext(sslcontext);
        } catch (GeneralSecurityException e) {
            log.log(Level.WARNING, "Could not create SSLSocketFactory for accepting all certificates", e);
        }

        // finally create the HttpClient instance and start it
        this.httpClient = builder.build();
        httpClient.start();
    }

    /**
     * Construct the {@link HttpClient} without using authentication values
     * and all timeouts set to the given number of milliseconds
     *
     * @param timeoutMs The timeout for socket connection and reading, specified in milliseconds
     */
    public HttpAsyncClientWrapper(int timeoutMs) {
        super(timeoutMs, false);


        RequestConfig reqConfig = RequestConfig.custom()
                .setSocketTimeout(timeoutMs)
                .setConnectTimeout(timeoutMs)
                .setConnectionRequestTimeout(timeoutMs)
                .build();

        HttpAsyncClientBuilder builder = HttpAsyncClients.custom()
                .setDefaultRequestConfig(reqConfig);

        try {
            // create permissive ssl context
            final SSLContext sslcontext = createSSLContext();
            builder.setSSLContext(sslcontext);
        } catch (GeneralSecurityException e) {
            log.log(Level.WARNING, "Could not create SSLSocketFactory for accepting all certificates", e);
        }

        // finally create the HttpClient instance and start it
        this.httpClient = builder.build();
        httpClient.start();
    }

    /**
     * Return the current {@link HttpClient} instance.
     *
     * @return The internally used instance of the {@link HttpClient}
     */
    public CloseableHttpAsyncClient getHttpClient() {
        return httpClient;
    }

    @Override
    public void close() throws IOException {
        httpClient.close();
    }

    protected void simpleGetInternal(String url, Consumer consumer, String body) throws IOException {
        final HttpUriRequest httpGet = getHttpGet(url, body);

        final HttpResponse execute;
        try {
            if (withAuth) {
                HttpClientContext context = HttpClientContext.create();
                HttpHost targetHost = getHttpHostWithAuth(url, context);

                execute = httpClient.execute(targetHost, httpGet, context, null).get();
            } else {
                execute = httpClient.execute(httpGet, null).get();
            }
        } catch (InterruptedException | ExecutionException e) {
            if (e.getCause() instanceof IOException) {
                throw (IOException)e.getCause();
            }

            throw new IOException(e);
        }

        HttpEntity entity = checkAndFetch(execute, url);
        try {
            consumer.accept(entity.getContent());
        } finally {
            // ensure all content is taken out to free resources
            EntityUtils.consume(entity);
        }
    }

    public String simplePost(String url, String body) throws IOException {
        HttpClientContext context = HttpClientContext.create();
        HttpHost targetHost = getHttpHostWithAuth(url, context);

        final HttpPost httpPost = new HttpPost(url);
        if(body != null) {
            httpPost.setEntity(new StringEntity(body));
        }

        try {
            HttpResponse response = httpClient.execute(targetHost, httpPost, context, null).get();
            HttpEntity entity = HttpClientWrapper.checkAndFetch(response, url);

            try {
                return IOUtils.toString(entity.getContent(), StandardCharsets.UTF_8);
            } finally {
                // ensure all content is taken out to free resources
                EntityUtils.consume(entity);
            }
        } catch (InterruptedException | ExecutionException e) {
            if (e.getCause() instanceof IOException) {
                throw (IOException)e.getCause();
            }

            throw new IOException(e);
        }
    }

    /**
     * Small helper method to simply query the URL without password and
     * return the resulting data.
     *
     * @param url The URL to query data from.
     * @return The resulting data read from the URL
     * @throws IOException If the URL is not accessible or the query returns
     * 		a HTTP code other than 200.
     */
    public static String retrieveData(String url) throws IOException {
        return retrieveData(url, "", null, 10_000);
    }

    /**
     * Small helper method to simply query the URL without password and
     * return the resulting data.
     *
     * @param url The URL to query data from.
     * @param user The username to send
     * @param password The password to send
     * @param timeoutMs How long in milliseconds to wait for the request
     * @return The resulting data read from the URL
     * @throws IOException If the URL is not accessible or the query returns
     * 		a HTTP code other than 200.
     */
    public static String retrieveData(String url, String user, String password, int timeoutMs) throws IOException {
        try (HttpAsyncClientWrapper wrapper = new HttpAsyncClientWrapper(user, password, timeoutMs)) {
            return wrapper.simpleGet(url);
        }
    }

    /**
     * Helper method to check the status code of the response and throw an IOException if it is
     * an error or moved state.
     *
     * @param response A HttpResponse that is resulting from executing a HttpMethod.
     * @param url The url, only used for building the error message of the exception.
     *
     * @return The {@link HttpEntity} returned from response.getEntity().
     *
     * @throws IOException if the HTTP status code is higher than 206.
     */
    public static HttpEntity checkAndFetch(HttpResponse response, String url) throws IOException {
        // simply re-use the implementation from the original HttpClientWrapper for now
        return HttpClientWrapper.checkAndFetch(response, url);
    }

    /**
     * Download the data from the given URL to a local file.
     *
     * Copying is done incrementally to allow to download large files
     * without exceed memory.
     *
     * Creates directories if necessary.
     *
     * @param url The URL to download
     * @param destination The destination for the file
     * @param timeoutMs Socket/HTTP-timeout in milliseconds
     *
     * @throws IOException If accessing the URL or downloading data fails
     * @throws IllegalStateException If writing the file fails
     */
    public static void downloadFile(String url, File destination, int timeoutMs) throws IOException, IllegalStateException {
        // simply re-use the implementation from the original HttpClientWrapper for now
        HttpClientWrapper.downloadFile(url, destination, timeoutMs);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy