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.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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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.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(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue