package org.jboss.resteasy.client.jaxrs.engines;
import org.apache.commons.io.output.DeferredFileOutputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.Configurable;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.jboss.resteasy.client.jaxrs.i18n.LogMessages;
import org.jboss.resteasy.client.jaxrs.i18n.Messages;
import org.jboss.resteasy.client.jaxrs.internal.ClientInvocation;
import org.jboss.resteasy.client.jaxrs.internal.ClientResponse;
import org.jboss.resteasy.client.jaxrs.internal.FinalizedClientResponse;
import org.jboss.resteasy.spi.config.ConfigurationFactory;
import org.jboss.resteasy.util.CaseInsensitiveMap;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List;
import java.util.Map;
public class ManualClosingApacheHttpClient43Engine implements ApacheHttpClientEngine
{
private static final String processId;
static
{
try {
processId = AccessController.doPrivileged(new PrivilegedExceptionAction<String>() {
@Override
public String run() throws Exception
{ return ManagementFactory.getRuntimeMXBean().getName().replaceAll("[^0-9a-zA-Z]", ""); }
});
} catch (PrivilegedActionException pae)
{ throw new RuntimeException(pae); }
}
protected final HttpClient httpClient;
protected boolean closed;
protected final boolean allowClosingHttpClient;
protected HttpContextProvider httpContextProvider;
protected SSLContext sslContext;
protected HostnameVerifier hostnameVerifier;
protected int responseBufferSize = 8192;
protected HttpHost defaultProxy = null;
protected boolean chunked = false;
protected boolean followRedirects = false;
protected int fileUploadInMemoryThresholdLimit = 1;
protected MemoryUnit fileUploadMemoryUnit = MemoryUnit.MB;
protected File fileUploadTempFileDir = new File(ConfigurationFactory.getInstance().getConfiguration().getOptionalValue("java.io.tmpdir", String.class).orElse(null));
public ManualClosingApacheHttpClient43Engine()
{
this.httpClient = createDefaultHttpClient();
this.allowClosingHttpClient = true;
}
public ManualClosingApacheHttpClient43Engine(final HttpHost defaultProxy)
{
this.defaultProxy = defaultProxy;
this.httpClient = createDefaultHttpClient();
this.allowClosingHttpClient = true;
}
public ManualClosingApacheHttpClient43Engine(final HttpClient httpClient)
{
this.httpClient = httpClient;
this.allowClosingHttpClient = true;
}
public ManualClosingApacheHttpClient43Engine(final HttpClient httpClient, final boolean closeHttpClient)
{
if (closeHttpClient && !(httpClient instanceof CloseableHttpClient))
{
throw new IllegalArgumentException(
"httpClient must be a CloseableHttpClient instance in order for allowing engine to close it!");
}
this.httpClient = httpClient;
this.allowClosingHttpClient = closeHttpClient;
}
public ManualClosingApacheHttpClient43Engine(final HttpClient httpClient, final HttpContextProvider httpContextProvider)
{
this.httpClient = httpClient;
this.httpContextProvider = httpContextProvider;
this.allowClosingHttpClient = true;
}
public int getResponseBufferSize()
{
return responseBufferSize;
}
public void setResponseBufferSize(int responseBufferSize)
{
this.responseBufferSize = responseBufferSize;
}
public int getFileUploadInMemoryThresholdLimit()
{
return fileUploadInMemoryThresholdLimit;
}
public void setFileUploadInMemoryThresholdLimit(int fileUploadInMemoryThresholdLimit)
{
this.fileUploadInMemoryThresholdLimit = fileUploadInMemoryThresholdLimit;
}
public MemoryUnit getFileUploadMemoryUnit()
{
return fileUploadMemoryUnit;
}
public void setFileUploadMemoryUnit(MemoryUnit fileUploadMemoryUnit)
{
this.fileUploadMemoryUnit = fileUploadMemoryUnit;
}
public File getFileUploadTempFileDir()
{
return fileUploadTempFileDir;
}
public void setFileUploadTempFileDir(File fileUploadTempFileDir)
{
this.fileUploadTempFileDir = fileUploadTempFileDir;
}
public HttpClient getHttpClient()
{
return httpClient;
}
@Override
public SSLContext getSslContext()
{
return sslContext;
}
public void setSslContext(SSLContext sslContext)
{
this.sslContext = sslContext;
}
@Override
public HostnameVerifier getHostnameVerifier()
{
return hostnameVerifier;
}
public void setHostnameVerifier(HostnameVerifier hostnameVerifier)
{
this.hostnameVerifier = hostnameVerifier;
}
public static CaseInsensitiveMap<String> (HttpResponse response)
{
final CaseInsensitiveMap<String> headers = new CaseInsensitiveMap<String>();
for (Header header : response.getAllHeaders())
{
headers.add(header.getName(), header.getValue());
}
return headers;
}
protected InputStream createBufferedStream(InputStream is)
{
if (responseBufferSize == 0)
{
return is;
}
if (responseBufferSize < 0)
{
return new SelfExpandingBufferredInputStream(is);
}
return new BufferedInputStream(is, responseBufferSize);
}
@Override
public Response invoke(Invocation inv)
{
ClientInvocation request = (ClientInvocation)inv;
String uri = request.getUri().toString();
final HttpRequestBase httpMethod = createHttpMethod(uri, request.getMethod());
final HttpResponse res;
try
{
loadHttpMethod(request, httpMethod);
if (System.getSecurityManager() == null) {
res = httpClient.execute(httpMethod,
((httpContextProvider == null)? null : httpContextProvider.getContext()));
} else {
try {
res = AccessController.doPrivileged(new PrivilegedExceptionAction<HttpResponse>() {
@Override
public HttpResponse run() throws Exception {
return httpClient.execute(httpMethod,
((httpContextProvider == null)? null : httpContextProvider.getContext()));
}
});
} catch (PrivilegedActionException pae) {
throw new RuntimeException(pae);
}
}
}
catch (Exception e)
{
LogMessages.LOGGER.clientSendProcessingFailure(e);
throw new ProcessingException(Messages.MESSAGES.unableToInvokeRequest(e.toString()), e);
}
finally
{
cleanUpAfterExecute(httpMethod);
}
ClientResponse response = new FinalizedClientResponse(request.getClientConfiguration(), request.getTracingLogger())
{
InputStream stream;
InputStream hc4Stream;
@Override
protected void setInputStream(InputStream is)
{
stream = is;
resetEntity();
}
public InputStream getInputStream()
{
if (stream == null)
{
HttpEntity entity = res.getEntity();
if (entity == null)
return null;
try
{
hc4Stream = entity.getContent();
stream = createBufferedStream(hc4Stream);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
return stream;
}
@Override
public void releaseConnection() throws IOException
{
releaseConnection(true);
}
@Override
public void releaseConnection(boolean consumeInputStream) throws IOException
{
if (consumeInputStream)
{
try
{
if (stream != null)
{
stream.close();
}
else
{
InputStream is = getInputStream();
if (is != null)
{
is.close();
}
}
}
finally
{
if (hc4Stream != null)
{
try
{
hc4Stream.close();
}
catch (IOException ignored)
{
}
}
else
{
try
{
HttpEntity entity = res.getEntity();
if (entity != null)
entity.getContent().close();
}
catch (IOException ignored)
{
}
}
}
}
else if (res instanceof CloseableHttpResponse)
{
try
{
((CloseableHttpResponse) res).close();
}
catch (IOException e)
{
LogMessages.LOGGER.warn(Messages.MESSAGES.couldNotCloseHttpResponse(), e);
}
}
}
};
response.setProperties(request.getMutableProperties());
response.setStatus(res.getStatusLine().getStatusCode());
response.setReasonPhrase(res.getStatusLine().getReasonPhrase());
response.setHeaders(extractHeaders(res));
response.setClientConfiguration(request.getClientConfiguration());
return response;
}
protected HttpRequestBase createHttpMethod(String url, String restVerb)
{
if ("GET".equals(restVerb))
{
return new HttpGet(url);
}
else if ("POST".equals(restVerb))
{
return new HttpPost(url);
}
else
{
final String verb = restVerb;
return new HttpPost(url)
{
@Override
public String getMethod()
{
return verb;
}
};
}
}
protected void loadHttpMethod(final ClientInvocation request, HttpRequestBase httpMethod) throws Exception
{
if (isFollowRedirects())
{
setRedirectRequired(request, httpMethod);
}
else
{
setRedirectNotRequired(request, httpMethod);
}
if (request.getEntity() != null)
{
if (httpMethod instanceof HttpGet)
throw new ProcessingException(Messages.MESSAGES.getRequestCannotHaveBody());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
request.getDelegatingOutputStream().setDelegate(baos);
try
{
HttpEntity entity = buildEntity(request);
HttpPost post = (HttpPost) httpMethod;
commitHeaders(request, httpMethod);
post.setEntity(entity);
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
else
{
commitHeaders(request, httpMethod);
}
}
protected void (ClientInvocation request, HttpRequestBase httpMethod)
{
MultivaluedMap<String, String> headers = request.getHeaders().asMap();
for (Map.Entry<String, List<String>> header : headers.entrySet())
{
List<String> values = header.getValue();
for (String value : values)
{
httpMethod.addHeader(header.getKey(), value);
}
}
}
public boolean isChunked()
{
return chunked;
}
public void setChunked(boolean chunked)
{
this.chunked = chunked;
}
@Override
public boolean isFollowRedirects()
{
return followRedirects;
}
@Override
public void setFollowRedirects(boolean followRedirects)
{
this.followRedirects = followRedirects;
}
protected void cleanUpAfterExecute(final HttpRequestBase httpMethod)
{
if (httpMethod != null && httpMethod instanceof HttpPost)
{
HttpPost postMethod = (HttpPost) httpMethod;
HttpEntity entity = postMethod.getEntity();
if (entity != null && entity instanceof FileExposingFileEntity)
{
File tempRequestFile = ((FileExposingFileEntity) entity).getFile();
try
{
boolean isDeleted = tempRequestFile.delete();
if (!isDeleted)
{
handleFileNotDeletedError(tempRequestFile, null);
}
}
catch (Exception ex)
{
handleFileNotDeletedError(tempRequestFile, ex);
}
}
}
}
protected HttpEntity buildEntity(final ClientInvocation request) throws IOException
{
AbstractHttpEntity entityToBuild = null;
DeferredFileOutputStream memoryManagedOutStream = writeRequestBodyToOutputStream(request);
MediaType mediaType = request.getHeaders().getMediaType();
if (memoryManagedOutStream.isInMemory())
{
ByteArrayEntity entityToBuildByteArray = new ByteArrayEntity(memoryManagedOutStream.getData());
if (mediaType != null) {
entityToBuildByteArray
.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, mediaType.toString()));
}
entityToBuild = entityToBuildByteArray;
}
else
{
entityToBuild = new FileExposingFileEntity(memoryManagedOutStream.getFile(),
mediaType == null ? null : mediaType.toString());
}
if (request.isChunked())
{
entityToBuild.setChunked(true);
}
return (HttpEntity) entityToBuild;
}
private DeferredFileOutputStream writeRequestBodyToOutputStream(final ClientInvocation request) throws IOException
{
DeferredFileOutputStream memoryManagedOutStream = new DeferredFileOutputStream(
this.fileUploadInMemoryThresholdLimit * getMemoryUnitMultiplier(), getTempfilePrefix(), ".tmp",
this.fileUploadTempFileDir);
request.getDelegatingOutputStream().setDelegate(memoryManagedOutStream);
request.writeRequestBody(request.getEntityStream());
memoryManagedOutStream.close();
return memoryManagedOutStream;
}
protected String getTempfilePrefix()
{
return processId;
}
private int getMemoryUnitMultiplier()
{
switch (this.fileUploadMemoryUnit)
{
case BY :
return 1;
case KB :
return 1024;
case MB :
return 1024 * 1024;
case GB :
return 1024 * 1024 * 1024;
}
return 1;
}
private void handleFileNotDeletedError(File tempRequestFile, Exception ex)
{
LogMessages.LOGGER.warn(Messages.MESSAGES.couldNotDeleteFile(tempRequestFile.getAbsolutePath()), ex);
tempRequestFile.deleteOnExit();
}
private static class FileExposingFileEntity extends FileEntity
{
@SuppressWarnings("deprecation")
FileExposingFileEntity(final File pFile, final String pContentType)
{
super(pFile, pContentType);
}
File getFile()
{
return this.file;
}
}
protected HttpClient createDefaultHttpClient()
{
final HttpClientBuilder builder = HttpClientBuilder.create();
RequestConfig.Builder requestBuilder = RequestConfig.custom();
if (defaultProxy != null)
{
requestBuilder.setProxy(defaultProxy);
}
builder.disableContentCompression();
builder.setDefaultRequestConfig(requestBuilder.build());
return builder.build();
}
public HttpHost getDefaultProxy()
{
Configurable clientConfiguration = (Configurable) httpClient;
return clientConfiguration.getConfig().getProxy();
}
protected void setRedirectRequired(final ClientInvocation request, final HttpRequestBase httpMethod)
{
RequestConfig.Builder requestBuilder = RequestConfig.copy(getCurrentConfiguration(request, httpMethod));
requestBuilder.setRedirectsEnabled(true);
httpMethod.setConfig(requestBuilder.build());
}
protected void setRedirectNotRequired(final ClientInvocation request, final HttpRequestBase httpMethod)
{
RequestConfig.Builder requestBuilder = RequestConfig.copy(getCurrentConfiguration(request, httpMethod));
requestBuilder.setRedirectsEnabled(false);
httpMethod.setConfig(requestBuilder.build());
}
private RequestConfig getCurrentConfiguration(final ClientInvocation request, final HttpRequestBase httpMethod)
{
RequestConfig baseConfig;
if (httpMethod != null && httpMethod.getConfig() != null)
{
baseConfig = httpMethod.getConfig();
}
else
{
ManualClosingApacheHttpClient43Engine engine = ((ManualClosingApacheHttpClient43Engine) request.getClient().httpEngine());
baseConfig = ((Configurable) engine.getHttpClient()).getConfig();
if (baseConfig == null)
{
Configurable clientConfiguration = (Configurable) httpClient;
baseConfig = clientConfiguration.getConfig();
}
}
return baseConfig;
}
public boolean isClosed()
{
return closed;
}
public void close()
{
if (closed)
return;
if (allowClosingHttpClient && httpClient != null)
{
try
{
((CloseableHttpClient) httpClient).close();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
closed = true;
}
}