Replaced HttpResponseWrapper with a dynamic proxy

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1405510 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2012-11-04 07:41:01 +00:00
parent 0155befe4a
commit e9ca02ccff
10 changed files with 265 additions and 309 deletions

View File

@ -34,6 +34,7 @@ import org.apache.http.HttpException;
import org.apache.http.annotation.ThreadSafe; import org.apache.http.annotation.ThreadSafe;
import org.apache.http.client.BackoffManager; import org.apache.http.client.BackoffManager;
import org.apache.http.client.ConnectionBackoffStrategy; import org.apache.http.client.ConnectionBackoffStrategy;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
@ -67,7 +68,7 @@ class BackoffStrategyExec implements ClientExecChain {
this.backoffManager = backoffManager; this.backoffManager = backoffManager;
} }
public HttpResponseWrapper execute( public CloseableHttpResponse execute(
final HttpRoute route, final HttpRoute route,
final HttpRequestWrapper request, final HttpRequestWrapper request,
final HttpContext context, final HttpContext context,
@ -81,7 +82,7 @@ class BackoffStrategyExec implements ClientExecChain {
if (context == null) { if (context == null) {
throw new IllegalArgumentException("HTTP context may not be null"); throw new IllegalArgumentException("HTTP context may not be null");
} }
HttpResponseWrapper out = null; CloseableHttpResponse out = null;
try { try {
out = this.requestExecutor.execute(route, request, context, execAware); out = this.requestExecutor.execute(route, request, context, execAware);
} catch (Exception ex) { } catch (Exception ex) {

View File

@ -30,6 +30,7 @@ package org.apache.http.impl.client.builder;
import java.io.IOException; import java.io.IOException;
import org.apache.http.HttpException; import org.apache.http.HttpException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
@ -41,15 +42,14 @@ import org.apache.http.protocol.HttpContext;
* <p/> * <p/>
* Important: please note it is required for decorators that implement post execution aspects * Important: please note it is required for decorators that implement post execution aspects
* or response post-processing of any sort to release resources associated with the response * or response post-processing of any sort to release resources associated with the response
* by calling {@link HttpResponseWrapper#releaseConnection()} or {@link HttpResponseWrapper#close()} * by calling {@link HttpResponseProxy#close()} methods in case of an I/O, protocol or runtime
* methods in case of an I/O, protocol or runtime exception, or in case the response is not * exception, or in case the response is not propagated to the caller.
* propagated to the caller.
* *
* @since 4.3 * @since 4.3
*/ */
interface ClientExecChain { interface ClientExecChain {
HttpResponseWrapper execute( CloseableHttpResponse execute(
HttpRoute route, HttpRoute route,
HttpRequestWrapper request, HttpRequestWrapper request,
HttpContext context, HttpContext context,

View File

@ -0,0 +1,100 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.builder;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.client.methods.CloseableHttpResponse;
/**
* A proxy class for {@link HttpResponse} that can be used to release client connection
* associated with the original response.
*
* @since 4.3
*/
@NotThreadSafe
class HttpResponseProxy implements InvocationHandler {
private final HttpResponse original;
private final ConnectionReleaseTriggerImpl connReleaseTrigger;
private HttpResponseProxy(
final HttpResponse original,
final ConnectionReleaseTriggerImpl connReleaseTrigger) {
super();
this.original = original;
this.connReleaseTrigger = connReleaseTrigger;
HttpEntity entity = original.getEntity();
if (entity != null && entity.isStreaming() && connReleaseTrigger != null) {
this.original.setEntity(new ResponseEntityWrapper(entity, connReleaseTrigger));
}
}
public void close() throws IOException {
if (this.connReleaseTrigger != null) {
this.connReleaseTrigger.abortConnection();
}
}
public Object invoke(
final Object proxy, final Method method, final Object[] args) throws Throwable {
String mname = method.getName();
if (mname.equals("close")) {
close();
return null;
} else {
try {
return method.invoke(original, args);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause != null) {
throw cause;
} else {
throw ex;
}
}
}
}
public static CloseableHttpResponse newProxy(
final HttpResponse original,
final ConnectionReleaseTriggerImpl connReleaseTrigger) {
return (CloseableHttpResponse) Proxy.newProxyInstance(
HttpResponseProxy.class.getClassLoader(),
new Class<?>[] { CloseableHttpResponse.class },
new HttpResponseProxy(original, connReleaseTrigger));
}
}

View File

@ -1,290 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.builder;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import java.util.Locale;
import org.apache.http.Header;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.conn.ConnectionReleaseTrigger;
import org.apache.http.conn.EofSensorInputStream;
import org.apache.http.conn.EofSensorWatcher;
import org.apache.http.entity.HttpEntityWrapper;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
/**
* A wrapper class for {@link HttpResponse} that can be used to manage client connection
* associated with the original response.
*
* @since 4.3
*/
@NotThreadSafe
class HttpResponseWrapper implements CloseableHttpResponse, ConnectionReleaseTrigger {
private final HttpResponse original;
private final ConnectionReleaseTriggerImpl connReleaseTrigger;
private HttpEntity entity;
private HttpResponseWrapper(
final HttpResponse original,
final ConnectionReleaseTriggerImpl connReleaseTrigger) {
super();
this.original = original;
this.connReleaseTrigger = connReleaseTrigger;
HttpEntity entity = original.getEntity();
if (connReleaseTrigger != null && entity != null && entity.isStreaming()) {
this.entity = new EntityWrapper(entity);
}
}
public HttpResponse getOriginal() {
return this.original;
}
public ProtocolVersion getProtocolVersion() {
return this.original.getProtocolVersion();
}
public boolean containsHeader(final String name) {
return this.original.containsHeader(name);
}
public Header[] getHeaders(final String name) {
return this.original.getHeaders(name);
}
public Header getFirstHeader(final String name) {
return this.original.getFirstHeader(name);
}
public Header getLastHeader(final String name) {
return this.original.getLastHeader(name);
}
public Header[] getAllHeaders() {
return this.original.getAllHeaders();
}
public void addHeader(final Header header) {
this.original.addHeader(header);
}
public void addHeader(final String name, final String value) {
this.original.addHeader(name, value);
}
public void setHeader(final Header header) {
this.original.setHeader(header);
}
public void setHeader(String name, String value) {
this.original.setHeader(name, value);
}
public void setHeaders(final Header[] headers) {
this.original.setHeaders(headers);
}
public void removeHeader(final Header header) {
this.original.removeHeader(header);
}
public void removeHeaders(final String name) {
this.original.removeHeaders(name);
}
public HeaderIterator headerIterator() {
return this.original.headerIterator();
}
public HeaderIterator headerIterator(final String name) {
return this.original.headerIterator(name);
}
public HttpParams getParams() {
return this.original.getParams();
}
public void setParams(final HttpParams params) {
this.original.setParams(params);
}
public StatusLine getStatusLine() {
return this.original.getStatusLine();
}
public void setStatusLine(final StatusLine statusline) {
this.original.setStatusLine(statusline);
}
public void setStatusLine(final ProtocolVersion ver, int code) {
this.original.setStatusLine(ver, code);
}
public void setStatusLine(final ProtocolVersion ver, int code, final String reason) {
this.original.setStatusLine(ver, code, reason);
}
public void setStatusCode(int code) throws IllegalStateException {
this.original.setStatusCode(code);
}
public void setReasonPhrase(final String reason) throws IllegalStateException {
this.original.setReasonPhrase(reason);
}
public Locale getLocale() {
return this.original.getLocale();
}
public void setLocale(final Locale loc) {
this.original.setLocale(loc);
}
public HttpEntity getEntity() {
return this.entity;
}
public void setEntity(final HttpEntity entity) {
this.entity = entity;
}
private void cleanup() throws IOException {
if (this.connReleaseTrigger != null) {
this.connReleaseTrigger.abortConnection();
}
}
public void releaseConnection() throws IOException {
if (this.connReleaseTrigger != null) {
try {
if (this.connReleaseTrigger.isReusable()) {
HttpEntity entity = this.original.getEntity();
if (entity != null) {
EntityUtils.consume(entity);
}
this.connReleaseTrigger.releaseConnection();
}
} finally {
cleanup();
}
}
}
public void abortConnection() throws IOException {
cleanup();
}
public void close() throws IOException {
cleanup();
}
class EntityWrapper extends HttpEntityWrapper implements EofSensorWatcher {
public EntityWrapper(final HttpEntity entity) {
super(entity);
}
@Override
public boolean isRepeatable() {
return false;
}
@Override
public InputStream getContent() throws IOException {
return new EofSensorInputStream(this.wrappedEntity.getContent(), this);
}
@Deprecated
@Override
public void consumeContent() throws IOException {
releaseConnection();
}
@Override
public void writeTo(final OutputStream outstream) throws IOException {
this.wrappedEntity.writeTo(outstream);
releaseConnection();
}
public boolean eofDetected(final InputStream wrapped) throws IOException {
try {
// there may be some cleanup required, such as
// reading trailers after the response body:
wrapped.close();
releaseConnection();
} finally {
cleanup();
}
return false;
}
public boolean streamClosed(InputStream wrapped) throws IOException {
try {
boolean open = connReleaseTrigger != null && !connReleaseTrigger.isReleased();
// this assumes that closing the stream will
// consume the remainder of the response body:
try {
wrapped.close();
releaseConnection();
} catch (SocketException ex) {
if (open) {
throw ex;
}
}
} finally {
cleanup();
}
return false;
}
public boolean streamAbort(InputStream wrapped) throws IOException {
cleanup();
return false;
}
}
public static HttpResponseWrapper wrap(
final HttpResponse response,
final ConnectionReleaseTriggerImpl connReleaseTrigger) {
return new HttpResponseWrapper(response, connReleaseTrigger);
}
}

View File

@ -49,6 +49,7 @@ import org.apache.http.auth.AuthState;
import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.AuthenticationStrategy;
import org.apache.http.client.NonRepeatableRequestException; import org.apache.http.client.NonRepeatableRequestException;
import org.apache.http.client.UserTokenHandler; import org.apache.http.client.UserTokenHandler;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.client.params.HttpClientParams; import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.ClientContext; import org.apache.http.client.protocol.ClientContext;
@ -167,7 +168,7 @@ class MainClientExec implements ClientExecChain {
this.userTokenHandler = userTokenHandler; this.userTokenHandler = userTokenHandler;
} }
public HttpResponseWrapper execute( public CloseableHttpResponse execute(
final HttpRoute route, final HttpRoute route,
final HttpRequestWrapper request, final HttpRequestWrapper request,
final HttpContext context, final HttpContext context,
@ -352,9 +353,9 @@ class MainClientExec implements ClientExecChain {
if (entity == null || !entity.isStreaming()) { if (entity == null || !entity.isStreaming()) {
// connection not needed and (assumed to be) in re-usable state // connection not needed and (assumed to be) in re-usable state
releaseTrigger.releaseConnection(); releaseTrigger.releaseConnection();
return HttpResponseWrapper.wrap(response, null); return HttpResponseProxy.newProxy(response, null);
} else { } else {
return HttpResponseWrapper.wrap(response, releaseTrigger); return HttpResponseProxy.newProxy(response, releaseTrigger);
} }
} catch (ConnectionShutdownException ex) { } catch (ConnectionShutdownException ex) {
InterruptedIOException ioex = new InterruptedIOException( InterruptedIOException ioex = new InterruptedIOException(

View File

@ -39,6 +39,7 @@ import org.apache.http.ProtocolException;
import org.apache.http.annotation.ThreadSafe; import org.apache.http.annotation.ThreadSafe;
import org.apache.http.auth.AuthState; import org.apache.http.auth.AuthState;
import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.client.protocol.ClientContext; import org.apache.http.client.protocol.ClientContext;
import org.apache.http.client.utils.URIUtils; import org.apache.http.client.utils.URIUtils;
@ -100,7 +101,7 @@ class ProtocolExec implements ClientExecChain {
} }
} }
public HttpResponseWrapper execute( public CloseableHttpResponse execute(
final HttpRoute route, final HttpRoute route,
final HttpRequestWrapper request, final HttpRequestWrapper request,
final HttpContext context, final HttpContext context,
@ -148,7 +149,7 @@ class ProtocolExec implements ClientExecChain {
this.httpProcessor.process(request, context); this.httpProcessor.process(request, context);
HttpResponseWrapper response = this.requestExecutor.execute(route, request, context, execAware); CloseableHttpResponse response = this.requestExecutor.execute(route, request, context, execAware);
try { try {
// Run response protocol interceptors // Run response protocol interceptors
context.setAttribute(ExecutionContext.HTTP_RESPONSE, response); context.setAttribute(ExecutionContext.HTTP_RESPONSE, response);

View File

@ -41,6 +41,7 @@ import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthState; import org.apache.http.auth.AuthState;
import org.apache.http.client.RedirectException; import org.apache.http.client.RedirectException;
import org.apache.http.client.RedirectStrategy; import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.client.params.ClientPNames; import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.HttpClientParams; import org.apache.http.client.params.HttpClientParams;
@ -49,6 +50,7 @@ import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.routing.HttpRoutePlanner; import org.apache.http.conn.routing.HttpRoutePlanner;
import org.apache.http.params.HttpParams; import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
/** /**
* The following parameters can be used to customize the behavior of this * The following parameters can be used to customize the behavior of this
@ -89,7 +91,7 @@ class RedirectExec implements ClientExecChain {
this.redirectStrategy = redirectStrategy; this.redirectStrategy = redirectStrategy;
} }
public HttpResponseWrapper execute( public CloseableHttpResponse execute(
final HttpRoute route, final HttpRoute route,
final HttpRequestWrapper request, final HttpRequestWrapper request,
final HttpContext context, final HttpContext context,
@ -108,7 +110,7 @@ class RedirectExec implements ClientExecChain {
HttpRoute currentRoute = route; HttpRoute currentRoute = route;
HttpRequestWrapper currentRequest = request; HttpRequestWrapper currentRequest = request;
for (int redirectCount = 0;;) { for (int redirectCount = 0;;) {
HttpResponseWrapper response = requestExecutor.execute( CloseableHttpResponse response = requestExecutor.execute(
currentRoute, currentRequest, context, execAware); currentRoute, currentRequest, context, execAware);
try { try {
if (HttpClientParams.isRedirecting(params) && if (HttpClientParams.isRedirecting(params) &&
@ -157,7 +159,8 @@ class RedirectExec implements ClientExecChain {
if (this.log.isDebugEnabled()) { if (this.log.isDebugEnabled()) {
this.log.debug("Redirecting to '" + uri + "' via " + currentRoute); this.log.debug("Redirecting to '" + uri + "' via " + currentRoute);
} }
response.releaseConnection(); EntityUtils.consume(response.getEntity());
response.close();
} else { } else {
return response; return response;
} }
@ -171,9 +174,11 @@ class RedirectExec implements ClientExecChain {
// Protocol exception related to a direct. // Protocol exception related to a direct.
// The underlying connection may still be salvaged. // The underlying connection may still be salvaged.
try { try {
response.releaseConnection(); EntityUtils.consume(response.getEntity());
} catch (IOException ioex) { } catch (IOException ioex) {
this.log.debug("I/O error while releasing connection", ioex); this.log.debug("I/O error while releasing connection", ioex);
} finally {
response.close();
} }
throw ex; throw ex;
} }

View File

@ -0,0 +1,136 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.builder;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import org.apache.http.HttpEntity;
import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.conn.EofSensorInputStream;
import org.apache.http.conn.EofSensorWatcher;
import org.apache.http.entity.HttpEntityWrapper;
import org.apache.http.util.EntityUtils;
/**
* A wrapper class for {@link HttpEntity} encloded in a response message.
*
* @since 4.3
*/
@NotThreadSafe
class ResponseEntityWrapper extends HttpEntityWrapper implements EofSensorWatcher {
private final ConnectionReleaseTriggerImpl connReleaseTrigger;
public ResponseEntityWrapper(
final HttpEntity entity,
final ConnectionReleaseTriggerImpl connReleaseTrigger) {
super(entity);
this.connReleaseTrigger = connReleaseTrigger;
}
private void cleanup() throws IOException {
if (this.connReleaseTrigger != null) {
this.connReleaseTrigger.abortConnection();
}
}
public void releaseConnection() throws IOException {
if (this.connReleaseTrigger != null) {
try {
if (this.connReleaseTrigger.isReusable()) {
EntityUtils.consume(this.wrappedEntity);
this.connReleaseTrigger.releaseConnection();
}
} finally {
cleanup();
}
}
}
@Override
public boolean isRepeatable() {
return false;
}
@Override
public InputStream getContent() throws IOException {
return new EofSensorInputStream(this.wrappedEntity.getContent(), this);
}
@Deprecated
@Override
public void consumeContent() throws IOException {
releaseConnection();
}
@Override
public void writeTo(final OutputStream outstream) throws IOException {
this.wrappedEntity.writeTo(outstream);
releaseConnection();
}
public boolean eofDetected(final InputStream wrapped) throws IOException {
try {
// there may be some cleanup required, such as
// reading trailers after the response body:
wrapped.close();
releaseConnection();
} finally {
cleanup();
}
return false;
}
public boolean streamClosed(InputStream wrapped) throws IOException {
try {
boolean open = connReleaseTrigger != null && !connReleaseTrigger.isReleased();
// this assumes that closing the stream will
// consume the remainder of the response body:
try {
wrapped.close();
releaseConnection();
} catch (SocketException ex) {
if (open) {
throw ex;
}
}
} finally {
cleanup();
}
return false;
}
public boolean streamAbort(InputStream wrapped) throws IOException {
cleanup();
return false;
}
}

View File

@ -37,6 +37,7 @@ import org.apache.http.HttpRequest;
import org.apache.http.annotation.NotThreadSafe; import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.NonRepeatableRequestException; import org.apache.http.client.NonRepeatableRequestException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.routing.HttpRoute;
@ -66,7 +67,7 @@ class RetryExec implements ClientExecChain {
this.retryHandler = retryHandler; this.retryHandler = retryHandler;
} }
public HttpResponseWrapper execute( public CloseableHttpResponse execute(
final HttpRoute route, final HttpRoute route,
final HttpRequestWrapper request, final HttpRequestWrapper request,
final HttpContext context, final HttpContext context,

View File

@ -35,6 +35,7 @@ import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpException; import org.apache.http.HttpException;
import org.apache.http.annotation.ThreadSafe; import org.apache.http.annotation.ThreadSafe;
import org.apache.http.client.ServiceUnavailableRetryStrategy; import org.apache.http.client.ServiceUnavailableRetryStrategy;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpExecutionAware; import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
@ -68,17 +69,17 @@ class ServiceUnavailableRetryExec implements ClientExecChain {
this.retryStrategy = retryStrategy; this.retryStrategy = retryStrategy;
} }
public HttpResponseWrapper execute( public CloseableHttpResponse execute(
final HttpRoute route, final HttpRoute route,
final HttpRequestWrapper request, final HttpRequestWrapper request,
final HttpContext context, final HttpContext context,
final HttpExecutionAware execAware) throws IOException, HttpException { final HttpExecutionAware execAware) throws IOException, HttpException {
for (int c = 1;; c++) { for (int c = 1;; c++) {
HttpResponseWrapper response = this.requestExecutor.execute( CloseableHttpResponse response = this.requestExecutor.execute(
route, request, context, execAware); route, request, context, execAware);
try { try {
if (this.retryStrategy.retryRequest(response, c, context)) { if (this.retryStrategy.retryRequest(response, c, context)) {
response.releaseConnection(); response.close();
long nextInterval = this.retryStrategy.getRetryInterval(); long nextInterval = this.retryStrategy.getRetryInterval();
try { try {
this.log.trace("Wait for " + nextInterval); this.log.trace("Wait for " + nextInterval);