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:
parent
0155befe4a
commit
e9ca02ccff
|
@ -34,6 +34,7 @@ import org.apache.http.HttpException;
|
|||
import org.apache.http.annotation.ThreadSafe;
|
||||
import org.apache.http.client.BackoffManager;
|
||||
import org.apache.http.client.ConnectionBackoffStrategy;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpExecutionAware;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
|
@ -67,7 +68,7 @@ class BackoffStrategyExec implements ClientExecChain {
|
|||
this.backoffManager = backoffManager;
|
||||
}
|
||||
|
||||
public HttpResponseWrapper execute(
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpContext context,
|
||||
|
@ -81,7 +82,7 @@ class BackoffStrategyExec implements ClientExecChain {
|
|||
if (context == null) {
|
||||
throw new IllegalArgumentException("HTTP context may not be null");
|
||||
}
|
||||
HttpResponseWrapper out = null;
|
||||
CloseableHttpResponse out = null;
|
||||
try {
|
||||
out = this.requestExecutor.execute(route, request, context, execAware);
|
||||
} catch (Exception ex) {
|
||||
|
|
|
@ -30,6 +30,7 @@ package org.apache.http.impl.client.builder;
|
|||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpExecutionAware;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
|
@ -41,15 +42,14 @@ import org.apache.http.protocol.HttpContext;
|
|||
* <p/>
|
||||
* 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
|
||||
* by calling {@link HttpResponseWrapper#releaseConnection()} or {@link HttpResponseWrapper#close()}
|
||||
* methods in case of an I/O, protocol or runtime exception, or in case the response is not
|
||||
* propagated to the caller.
|
||||
* by calling {@link HttpResponseProxy#close()} methods in case of an I/O, protocol or runtime
|
||||
* exception, or in case the response is not propagated to the caller.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
interface ClientExecChain {
|
||||
|
||||
HttpResponseWrapper execute(
|
||||
CloseableHttpResponse execute(
|
||||
HttpRoute route,
|
||||
HttpRequestWrapper request,
|
||||
HttpContext context,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -49,6 +49,7 @@ import org.apache.http.auth.AuthState;
|
|||
import org.apache.http.client.AuthenticationStrategy;
|
||||
import org.apache.http.client.NonRepeatableRequestException;
|
||||
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.params.HttpClientParams;
|
||||
import org.apache.http.client.protocol.ClientContext;
|
||||
|
@ -167,7 +168,7 @@ class MainClientExec implements ClientExecChain {
|
|||
this.userTokenHandler = userTokenHandler;
|
||||
}
|
||||
|
||||
public HttpResponseWrapper execute(
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpContext context,
|
||||
|
@ -352,9 +353,9 @@ class MainClientExec implements ClientExecChain {
|
|||
if (entity == null || !entity.isStreaming()) {
|
||||
// connection not needed and (assumed to be) in re-usable state
|
||||
releaseTrigger.releaseConnection();
|
||||
return HttpResponseWrapper.wrap(response, null);
|
||||
return HttpResponseProxy.newProxy(response, null);
|
||||
} else {
|
||||
return HttpResponseWrapper.wrap(response, releaseTrigger);
|
||||
return HttpResponseProxy.newProxy(response, releaseTrigger);
|
||||
}
|
||||
} catch (ConnectionShutdownException ex) {
|
||||
InterruptedIOException ioex = new InterruptedIOException(
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.apache.http.ProtocolException;
|
|||
import org.apache.http.annotation.ThreadSafe;
|
||||
import org.apache.http.auth.AuthState;
|
||||
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.protocol.ClientContext;
|
||||
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 HttpRequestWrapper request,
|
||||
final HttpContext context,
|
||||
|
@ -148,7 +149,7 @@ class ProtocolExec implements ClientExecChain {
|
|||
|
||||
this.httpProcessor.process(request, context);
|
||||
|
||||
HttpResponseWrapper response = this.requestExecutor.execute(route, request, context, execAware);
|
||||
CloseableHttpResponse response = this.requestExecutor.execute(route, request, context, execAware);
|
||||
try {
|
||||
// Run response protocol interceptors
|
||||
context.setAttribute(ExecutionContext.HTTP_RESPONSE, response);
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.apache.http.auth.AuthScheme;
|
|||
import org.apache.http.auth.AuthState;
|
||||
import org.apache.http.client.RedirectException;
|
||||
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.params.ClientPNames;
|
||||
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.params.HttpParams;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
/**
|
||||
* The following parameters can be used to customize the behavior of this
|
||||
|
@ -89,7 +91,7 @@ class RedirectExec implements ClientExecChain {
|
|||
this.redirectStrategy = redirectStrategy;
|
||||
}
|
||||
|
||||
public HttpResponseWrapper execute(
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpContext context,
|
||||
|
@ -108,7 +110,7 @@ class RedirectExec implements ClientExecChain {
|
|||
HttpRoute currentRoute = route;
|
||||
HttpRequestWrapper currentRequest = request;
|
||||
for (int redirectCount = 0;;) {
|
||||
HttpResponseWrapper response = requestExecutor.execute(
|
||||
CloseableHttpResponse response = requestExecutor.execute(
|
||||
currentRoute, currentRequest, context, execAware);
|
||||
try {
|
||||
if (HttpClientParams.isRedirecting(params) &&
|
||||
|
@ -157,7 +159,8 @@ class RedirectExec implements ClientExecChain {
|
|||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug("Redirecting to '" + uri + "' via " + currentRoute);
|
||||
}
|
||||
response.releaseConnection();
|
||||
EntityUtils.consume(response.getEntity());
|
||||
response.close();
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
|
@ -171,9 +174,11 @@ class RedirectExec implements ClientExecChain {
|
|||
// Protocol exception related to a direct.
|
||||
// The underlying connection may still be salvaged.
|
||||
try {
|
||||
response.releaseConnection();
|
||||
EntityUtils.consume(response.getEntity());
|
||||
} catch (IOException ioex) {
|
||||
this.log.debug("I/O error while releasing connection", ioex);
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -37,6 +37,7 @@ import org.apache.http.HttpRequest;
|
|||
import org.apache.http.annotation.NotThreadSafe;
|
||||
import org.apache.http.client.HttpRequestRetryHandler;
|
||||
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.HttpUriRequest;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
|
@ -66,7 +67,7 @@ class RetryExec implements ClientExecChain {
|
|||
this.retryHandler = retryHandler;
|
||||
}
|
||||
|
||||
public HttpResponseWrapper execute(
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpContext context,
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
import org.apache.http.HttpException;
|
||||
import org.apache.http.annotation.ThreadSafe;
|
||||
import org.apache.http.client.ServiceUnavailableRetryStrategy;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpExecutionAware;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
|
@ -68,17 +69,17 @@ class ServiceUnavailableRetryExec implements ClientExecChain {
|
|||
this.retryStrategy = retryStrategy;
|
||||
}
|
||||
|
||||
public HttpResponseWrapper execute(
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpContext context,
|
||||
final HttpExecutionAware execAware) throws IOException, HttpException {
|
||||
for (int c = 1;; c++) {
|
||||
HttpResponseWrapper response = this.requestExecutor.execute(
|
||||
CloseableHttpResponse response = this.requestExecutor.execute(
|
||||
route, request, context, execAware);
|
||||
try {
|
||||
if (this.retryStrategy.retryRequest(response, c, context)) {
|
||||
response.releaseConnection();
|
||||
response.close();
|
||||
long nextInterval = this.retryStrategy.getRetryInterval();
|
||||
try {
|
||||
this.log.trace("Wait for " + nextInterval);
|
||||
|
|
Loading…
Reference in New Issue