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

org.sourcelab.github.client.http.HttpComponentsClient Maven / Gradle / Ivy

The newest version!

package org.sourcelab.github.client.http;

import org.apache.hc.client5.http.auth.AuthCache;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.auth.CredentialsStore;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.classic.methods.HttpDelete;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpPut;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.auth.BasicScheme;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.core5.http.ClassicHttpRequest;
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.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.util.Timeout;
import org.sourcelab.github.client.Configuration;
import org.sourcelab.github.client.exception.HttpRequestException;
import org.sourcelab.github.client.request.Request;
import org.sourcelab.github.client.request.RequestParameter;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * Underlying HTTP Client implementation making use of HttpComponents 5.x library.
 */
public class HttpComponentsClient implements Client {
    /**
     * User supplied API Client configuration.
     */
    private final Configuration configuration;

    private final HttpClientConfigHooks configHooks;

    /**
     * Constructor.
     *
     * @param configuration User supplied API Client configuration.
     * @param configHooks Config hooks
     */
    public HttpComponentsClient(final Configuration configuration, final HttpClientConfigHooks configHooks) {
        this.configuration = configuration;
        this.configHooks = configHooks;
    }

    /**
     * Get reference to underlying HttpClient to make requests against.
     *
     * @return HttpClient instance.
     */
    private CloseableHttpClient getClient() {
        HttpClientBuilder builder = configHooks.createHttpClientBuilder(configuration);

        // Create https context builder utility.
        final HttpsContextBuilder httpsContextBuilder = new HttpsContextBuilder(configuration);

        RequestConfig.Builder requestConfigBuilder = configHooks.createRequestConfigBuilder(configuration)
            .setConnectionRequestTimeout(Timeout.of(configuration.getRequestTimeoutInSeconds(), TimeUnit.SECONDS))
            .setDefaultKeepAlive(configuration.getConnectionTimeToLiveInSeconds(), TimeUnit.SECONDS)
            .setResponseTimeout(Timeout.of(configuration.getRequestTimeoutInSeconds(), TimeUnit.SECONDS));


        builder
            // Define SSL Socket Factory instance.
            .setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create()
                .setSSLSocketFactory(httpsContextBuilder.createSslSocketFactory())
                .build());

        // Inject Auth Header
        final List
defaultHeaders = new ArrayList<>(); defaultHeaders.add(new BasicHeader("Authorization", "Bearer " + configuration.getApiToken(), true)); defaultHeaders.add(new BasicHeader("Accept", "application/vnd.github+json", false)); defaultHeaders.add(new BasicHeader("X-GitHub-Api-Version", "2022-11-28", false)); defaultHeaders.add(new BasicHeader("User-Agent", "GithubRestApiClient-0.0.1", false)); builder.setDefaultHeaders(defaultHeaders); // Call Modify hooks final AuthCache authCache = configHooks.createAuthCache(configuration); // Define our Credentials Provider CredentialsStore credsProvider = Objects.requireNonNull( configHooks.createCredentialsProvider(configuration), "HttpClientConfigHook::createCredentialsProvider() must return non-null instance." ); // If we have a configured proxy host if (configuration.getProxyHost() != null) { // Define proxy host final HttpHost proxyHost = new HttpHost( configuration.getProxyScheme(), configuration.getProxyHost(), configuration.getProxyPort() ); // If we have proxy auth enabled if (configuration.getProxyUsername() != null) { // Add proxy credentials credsProvider.setCredentials( new AuthScope(configuration.getProxyHost(), configuration.getProxyPort()), new UsernamePasswordCredentials(configuration.getProxyUsername(), configuration.getProxyPassword().toCharArray()) ); // Preemptive load context with authentication. authCache.put( new HttpHost(configuration.getProxyScheme(), configuration.getProxyHost(), configuration.getProxyPort()), new BasicScheme() ); } // Attach Proxy to request config builder requestConfigBuilder.setProxy(proxyHost); } requestConfigBuilder = Objects.requireNonNull( configHooks.modifyRequestConfig(configuration, requestConfigBuilder), "HttpClientConfigHook::modifyRequestConfig() must return non-null instance." ); // Attach Credentials provider to client builder. builder.setDefaultCredentialsProvider(credsProvider); builder.setDefaultRequestConfig(requestConfigBuilder.build()); builder = configHooks.modifyHttpClientBuilder(configuration, builder); // Construct builder and return. return builder.build(); } /** * Execute the given request and return the parsed response. * @param request The request to execute. * @return Response from the API. */ @Override public HttpResult executeRequest(final Request request) { try (final CloseableHttpClient httpClient = getClient()) { switch (request.getMethod()) { case GET: return executeGetRequest(request, httpClient); case DELETE: return executeDeleteRequest(request, httpClient); case PUT: return executePutRequest(request, httpClient); case POST: return executePostRequest(request, httpClient); default: throw new IllegalArgumentException("Invalid HttpType: " + request.getMethod()); } } catch (final IOException ioException) { throw new HttpRequestException(ioException.getMessage(), ioException); } } @Override public void close() { // Not required in this implementation. } private HttpResult executePostRequest(final Request request, final CloseableHttpClient httpClient) { try { final HttpPost httpPost = new HttpPost(generateRequestUri(request)); httpPost.setEntity(new StringEntity(request.getRequestBody())); return submitRequest(httpPost, httpClient); } catch (final Exception exception) { throw new HttpRequestException(exception.getMessage(), exception); } } private HttpResult executePutRequest(final Request request, final CloseableHttpClient httpClient) { try { final HttpPut httpPut = new HttpPut(generateRequestUri(request)); return submitRequest(httpPut, httpClient); } catch (final Exception exception) { throw new HttpRequestException(exception.getMessage(), exception); } } private HttpResult executeGetRequest(final Request request, final CloseableHttpClient httpClient) { try { final HttpGet httpGet = new HttpGet(generateRequestUri(request)); return submitRequest(httpGet, httpClient); } catch (final Exception exception) { throw new HttpRequestException(exception.getMessage(), exception); } } private HttpResult executeDeleteRequest(final Request request, final CloseableHttpClient httpClient) { final HttpDelete httpDelete = new HttpDelete(generateRequestUri(request)); return submitRequest(httpDelete, httpClient); } /** * Generate URI for the request, including any request parameters. * * @param request The request to generate URI for. * @return Generate URI for the request, including any request parameters. * @throws HttpRequestException on URI exceptions. */ private URI generateRequestUri(final Request request) { // Construct URI including our request parameters. try { final String path = configuration.getApiUrl() + request.getPath(); final URIBuilder uriBuilder = new URIBuilder(path) .setCharset(StandardCharsets.UTF_8); // Attach request parameters for (final RequestParameter requestParameter : request.getRequestParameters().getParameters()) { for (final String value : requestParameter.getValues()) { uriBuilder.setParameter(requestParameter.getName(), value); } } return uriBuilder.build(); } catch (final URISyntaxException uriSyntaxException) { throw new HttpRequestException(uriSyntaxException.getMessage(), uriSyntaxException); } } private HttpResult submitRequest(final ClassicHttpRequest httpRequest, final CloseableHttpClient httpClient) { try (final CloseableHttpResponse response = httpClient.execute(httpRequest)) { final HttpEntity entity = response.getEntity(); final String responseStr; if (entity != null) { responseStr = EntityUtils.toString(entity); EntityUtils.consume(entity); } else { responseStr = ""; } // Collect response headers. final List allHeaders = new ArrayList<>(); for (final Header header : response.getHeaders()) { allHeaders.add(new HttpHeader(header.getName(), header.getValue())); } // Build final abstracted result final HttpResult result = new HttpResult( response.getCode(), responseStr, new HttpHeaders(allHeaders) ); // and return it. return result; } catch (final IOException | ParseException e) { throw new HttpRequestException(e.getMessage(), e); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy