
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