
feign.hc5.ApacheHttp5Client Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of feign-hc5 Show documentation
Show all versions of feign-hc5 Show documentation
Feign Apache HttpComponents Client 5
The newest version!
/*
* Copyright © 2012 The Feign Authors ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package feign.hc5;
import static feign.Util.UTF_8;
import static feign.Util.enumForName;
import feign.Client;
import feign.Request;
import feign.Response;
import feign.Util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.config.Configurable;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.GzipCompressingEntity;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
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.NameValuePair;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.net.URLEncodedUtils;
/**
* This module directs Feign's http requests to Apache's
* HttpClient 5. Ex.
*
*
* GitHub github = Feign.builder().client(new ApacheHttp5Client()).target(GitHub.class,
* "https://api.github.com");
*/
/*
*/
public final class ApacheHttp5Client implements Client {
private static final String ACCEPT_HEADER_NAME = "Accept";
private final HttpClient client;
public ApacheHttp5Client() {
this(HttpClientBuilder.create().build());
}
public ApacheHttp5Client(HttpClient client) {
this.client = client;
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
ClassicHttpRequest httpUriRequest;
try {
httpUriRequest = toClassicHttpRequest(request, options);
} catch (final URISyntaxException e) {
throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", e);
}
final HttpHost target = HttpHost.create(URI.create(request.url()));
final HttpClientContext context = configureTimeoutsAndRedirection(options);
final ClassicHttpResponse httpResponse =
(ClassicHttpResponse) client.execute(target, httpUriRequest, context);
return toFeignResponse(httpResponse, request);
}
protected HttpClientContext configureTimeoutsAndRedirection(Request.Options options) {
final HttpClientContext context = new HttpClientContext();
// per request timeouts
final RequestConfig requestConfig =
(client instanceof Configurable
? RequestConfig.copy(((Configurable) client).getConfig())
: RequestConfig.custom())
.setConnectTimeout(options.connectTimeout(), options.connectTimeoutUnit())
.setResponseTimeout(options.readTimeout(), options.readTimeoutUnit())
.setRedirectsEnabled(options.isFollowRedirects())
.build();
context.setRequestConfig(requestConfig);
return context;
}
ClassicHttpRequest toClassicHttpRequest(Request request, Request.Options options)
throws URISyntaxException {
final ClassicRequestBuilder requestBuilder =
ClassicRequestBuilder.create(request.httpMethod().name());
final URI uri = new URIBuilder(request.url()).build();
requestBuilder.setUri(uri.getScheme() + "://" + uri.getAuthority() + uri.getRawPath());
// request query params
final List queryParams = URLEncodedUtils.parse(uri, requestBuilder.getCharset());
for (final NameValuePair queryParam : queryParams) {
requestBuilder.addParameter(queryParam);
}
// request headers
boolean hasAcceptHeader = false;
boolean isGzip = false;
for (final Map.Entry> headerEntry : request.headers().entrySet()) {
final String headerName = headerEntry.getKey();
if (headerName.equalsIgnoreCase(ACCEPT_HEADER_NAME)) {
hasAcceptHeader = true;
}
if (headerName.equalsIgnoreCase(Util.CONTENT_LENGTH)) {
// The 'Content-Length' header is always set by the Apache client and it
// doesn't like us to set it as well.
continue;
}
if (headerName.equalsIgnoreCase(Util.CONTENT_ENCODING)) {
isGzip = headerEntry.getValue().stream().anyMatch(Util.ENCODING_GZIP::equalsIgnoreCase);
boolean isDeflate =
headerEntry.getValue().stream().anyMatch(Util.ENCODING_DEFLATE::equalsIgnoreCase);
if (isDeflate) {
// DeflateCompressingEntity not available in hc5 yet
throw new IllegalArgumentException(
"Deflate Content-Encoding is not supported by feign-hc5");
}
}
for (final String headerValue : headerEntry.getValue()) {
requestBuilder.addHeader(headerName, headerValue);
}
}
// some servers choke on the default accept string, so we'll set it to anything
if (!hasAcceptHeader) {
requestBuilder.addHeader(ACCEPT_HEADER_NAME, "*/*");
}
// request body
// final Body requestBody = request.requestBody();
byte[] data = request.body();
if (data != null) {
HttpEntity entity;
if (request.isBinary()) {
entity = new ByteArrayEntity(data, null);
} else {
final ContentType contentType = getContentType(request);
String content;
if (request.charset() != null) {
content = new String(data, request.charset());
} else {
content = new String(data);
}
entity = new StringEntity(content, contentType);
}
if (isGzip) {
entity = new GzipCompressingEntity(entity);
}
requestBuilder.setEntity(entity);
} else {
requestBuilder.setEntity(new ByteArrayEntity(new byte[0], null));
}
return requestBuilder.build();
}
private ContentType getContentType(Request request) {
ContentType contentType = null;
for (final Map.Entry> entry : request.headers().entrySet()) {
if (entry.getKey().equalsIgnoreCase("Content-Type")) {
final Collection values = entry.getValue();
if (values != null && !values.isEmpty()) {
contentType = ContentType.parse(values.iterator().next());
if (contentType.getCharset() == null) {
contentType = contentType.withCharset(request.charset());
}
break;
}
}
}
return contentType;
}
Response toFeignResponse(ClassicHttpResponse httpResponse, Request request) throws IOException {
final int statusCode = httpResponse.getCode();
final String reason = httpResponse.getReasonPhrase();
final Map> headers = new HashMap<>();
for (final Header header : httpResponse.getHeaders()) {
final String name = header.getName();
final String value = header.getValue();
Collection headerValues = headers.get(name);
if (headerValues == null) {
headerValues = new ArrayList<>();
headers.put(name, headerValues);
}
headerValues.add(value);
}
return Response.builder()
.protocolVersion(
enumForName(Request.ProtocolVersion.class, httpResponse.getVersion().format()))
.status(statusCode)
.reason(reason)
.headers(headers)
.request(request)
.body(toFeignBody(httpResponse))
.build();
}
Response.Body toFeignBody(ClassicHttpResponse httpResponse) {
final HttpEntity entity = httpResponse.getEntity();
if (entity == null) {
return null;
}
return new Response.Body() {
@Override
public Integer length() {
return entity.getContentLength() >= 0 && entity.getContentLength() <= Integer.MAX_VALUE
? (int) entity.getContentLength()
: null;
}
@Override
public boolean isRepeatable() {
return entity.isRepeatable();
}
@Override
public InputStream asInputStream() throws IOException {
return entity.getContent();
}
@Override
public Reader asReader() throws IOException {
return new InputStreamReader(asInputStream(), UTF_8);
}
@Override
public Reader asReader(Charset charset) throws IOException {
Util.checkNotNull(charset, "charset should not be null");
return new InputStreamReader(asInputStream(), charset);
}
@Override
public void close() throws IOException {
try {
EntityUtils.consume(entity);
} finally {
httpResponse.close();
}
}
};
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy