mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 00:08:39 +09:00
Issue #1053 - Drop support Android and remove Fennec - Part 1a: Remove mobile/android
This commit is contained in:
parent
85045bf6be
commit
c9b411d6cd
3891 changed files with 0 additions and 428084 deletions
|
|
@ -1,104 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.annotation.Immutable;
|
||||
import ch.boye.httpclientandroidlib.client.BackoffManager;
|
||||
import ch.boye.httpclientandroidlib.client.ConnectionBackoffStrategy;
|
||||
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
|
||||
import ch.boye.httpclientandroidlib.util.Args;
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
@Immutable
|
||||
public class BackoffStrategyExec implements ClientExecChain {
|
||||
|
||||
private final ClientExecChain requestExecutor;
|
||||
private final ConnectionBackoffStrategy connectionBackoffStrategy;
|
||||
private final BackoffManager backoffManager;
|
||||
|
||||
public BackoffStrategyExec(
|
||||
final ClientExecChain requestExecutor,
|
||||
final ConnectionBackoffStrategy connectionBackoffStrategy,
|
||||
final BackoffManager backoffManager) {
|
||||
super();
|
||||
Args.notNull(requestExecutor, "HTTP client request executor");
|
||||
Args.notNull(connectionBackoffStrategy, "Connection backoff strategy");
|
||||
Args.notNull(backoffManager, "Backoff manager");
|
||||
this.requestExecutor = requestExecutor;
|
||||
this.connectionBackoffStrategy = connectionBackoffStrategy;
|
||||
this.backoffManager = backoffManager;
|
||||
}
|
||||
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpClientContext context,
|
||||
final HttpExecutionAware execAware) throws IOException, HttpException {
|
||||
Args.notNull(route, "HTTP route");
|
||||
Args.notNull(request, "HTTP request");
|
||||
Args.notNull(context, "HTTP context");
|
||||
CloseableHttpResponse out = null;
|
||||
try {
|
||||
out = this.requestExecutor.execute(route, request, context, execAware);
|
||||
} catch (final Exception ex) {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
if (this.connectionBackoffStrategy.shouldBackoff(ex)) {
|
||||
this.backoffManager.backOff(route);
|
||||
}
|
||||
if (ex instanceof RuntimeException) {
|
||||
throw (RuntimeException) ex;
|
||||
}
|
||||
if (ex instanceof HttpException) {
|
||||
throw (HttpException) ex;
|
||||
}
|
||||
if (ex instanceof IOException) {
|
||||
throw (IOException) ex;
|
||||
}
|
||||
throw new UndeclaredThrowableException(ex);
|
||||
}
|
||||
if (this.connectionBackoffStrategy.shouldBackoff(out)) {
|
||||
this.backoffManager.backOff(route);
|
||||
} else {
|
||||
this.backoffManager.probe(route);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,74 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
|
||||
|
||||
/**
|
||||
* This interface represents an element in the HTTP request execution chain. Each element can
|
||||
* either be a decorator around another element that implements a cross cutting aspect or
|
||||
* a self-contained executor capable of producing a response for the given request.
|
||||
* <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 CloseableHttpResponse#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
|
||||
*/
|
||||
public interface ClientExecChain {
|
||||
|
||||
/**
|
||||
* Executes th request either by transmitting it to the target server or
|
||||
* by passing it onto the next executor in the request execution chain.
|
||||
*
|
||||
* @param route connection route.
|
||||
* @param request current request.
|
||||
* @param clientContext current HTTP context.
|
||||
* @param execAware receiver of notifications of blocking I/O operations.
|
||||
* @return HTTP response either received from the opposite endpoint
|
||||
* or generated locally.
|
||||
* @throws IOException in case of a I/O error.
|
||||
* (this type of exceptions are potentially recoverable).
|
||||
* @throws HttpException in case of an HTTP protocol error
|
||||
* (usually this type of exceptions are non-recoverable).
|
||||
*/
|
||||
CloseableHttpResponse execute(
|
||||
HttpRoute route,
|
||||
HttpRequestWrapper request,
|
||||
HttpClientContext clientContext,
|
||||
HttpExecutionAware execAware) throws IOException, HttpException;
|
||||
|
||||
}
|
||||
|
|
@ -1,153 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
|
||||
import ch.boye.httpclientandroidlib.HttpClientConnection;
|
||||
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
|
||||
import ch.boye.httpclientandroidlib.concurrent.Cancellable;
|
||||
import ch.boye.httpclientandroidlib.conn.ConnectionReleaseTrigger;
|
||||
import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager;
|
||||
|
||||
/**
|
||||
* Internal connection holder.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@ThreadSafe
|
||||
class ConnectionHolder implements ConnectionReleaseTrigger, Cancellable, Closeable {
|
||||
|
||||
public HttpClientAndroidLog log;
|
||||
|
||||
private final HttpClientConnectionManager manager;
|
||||
private final HttpClientConnection managedConn;
|
||||
private volatile boolean reusable;
|
||||
private volatile Object state;
|
||||
private volatile long validDuration;
|
||||
private volatile TimeUnit tunit;
|
||||
|
||||
private volatile boolean released;
|
||||
|
||||
public ConnectionHolder(
|
||||
final HttpClientAndroidLog log,
|
||||
final HttpClientConnectionManager manager,
|
||||
final HttpClientConnection managedConn) {
|
||||
super();
|
||||
this.log = log;
|
||||
this.manager = manager;
|
||||
this.managedConn = managedConn;
|
||||
}
|
||||
|
||||
public boolean isReusable() {
|
||||
return this.reusable;
|
||||
}
|
||||
|
||||
public void markReusable() {
|
||||
this.reusable = true;
|
||||
}
|
||||
|
||||
public void markNonReusable() {
|
||||
this.reusable = false;
|
||||
}
|
||||
|
||||
public void setState(final Object state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void setValidFor(final long duration, final TimeUnit tunit) {
|
||||
synchronized (this.managedConn) {
|
||||
this.validDuration = duration;
|
||||
this.tunit = tunit;
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseConnection() {
|
||||
synchronized (this.managedConn) {
|
||||
if (this.released) {
|
||||
return;
|
||||
}
|
||||
this.released = true;
|
||||
if (this.reusable) {
|
||||
this.manager.releaseConnection(this.managedConn,
|
||||
this.state, this.validDuration, this.tunit);
|
||||
} else {
|
||||
try {
|
||||
this.managedConn.close();
|
||||
log.debug("Connection discarded");
|
||||
} catch (final IOException ex) {
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug(ex.getMessage(), ex);
|
||||
}
|
||||
} finally {
|
||||
this.manager.releaseConnection(
|
||||
this.managedConn, null, 0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void abortConnection() {
|
||||
synchronized (this.managedConn) {
|
||||
if (this.released) {
|
||||
return;
|
||||
}
|
||||
this.released = true;
|
||||
try {
|
||||
this.managedConn.shutdown();
|
||||
log.debug("Connection discarded");
|
||||
} catch (final IOException ex) {
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug(ex.getMessage(), ex);
|
||||
}
|
||||
} finally {
|
||||
this.manager.releaseConnection(
|
||||
this.managedConn, null, 0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean cancel() {
|
||||
final boolean alreadyReleased = this.released;
|
||||
log.debug("Cancelling request execution");
|
||||
abortConnection();
|
||||
return !alreadyReleased;
|
||||
}
|
||||
|
||||
public boolean isReleased() {
|
||||
return this.released;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
abortConnection();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,185 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
import ch.boye.httpclientandroidlib.Header;
|
||||
import ch.boye.httpclientandroidlib.HeaderIterator;
|
||||
import ch.boye.httpclientandroidlib.HttpEntity;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.ProtocolVersion;
|
||||
import ch.boye.httpclientandroidlib.StatusLine;
|
||||
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
|
||||
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
|
||||
import ch.boye.httpclientandroidlib.params.HttpParams;
|
||||
|
||||
/**
|
||||
* A proxy class for {@link ch.boye.httpclientandroidlib.HttpResponse} that can be used to release client connection
|
||||
* associated with the original response.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@NotThreadSafe
|
||||
class HttpResponseProxy implements CloseableHttpResponse {
|
||||
|
||||
private final HttpResponse original;
|
||||
private final ConnectionHolder connHolder;
|
||||
|
||||
public HttpResponseProxy(final HttpResponse original, final ConnectionHolder connHolder) {
|
||||
this.original = original;
|
||||
this.connHolder = connHolder;
|
||||
ResponseEntityProxy.enchance(original, connHolder);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (this.connHolder != null) {
|
||||
this.connHolder.abortConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public StatusLine getStatusLine() {
|
||||
return original.getStatusLine();
|
||||
}
|
||||
|
||||
public void setStatusLine(final StatusLine statusline) {
|
||||
original.setStatusLine(statusline);
|
||||
}
|
||||
|
||||
public void setStatusLine(final ProtocolVersion ver, final int code) {
|
||||
original.setStatusLine(ver, code);
|
||||
}
|
||||
|
||||
public void setStatusLine(final ProtocolVersion ver, final int code, final String reason) {
|
||||
original.setStatusLine(ver, code, reason);
|
||||
}
|
||||
|
||||
public void setStatusCode(final int code) throws IllegalStateException {
|
||||
original.setStatusCode(code);
|
||||
}
|
||||
|
||||
public void setReasonPhrase(final String reason) throws IllegalStateException {
|
||||
original.setReasonPhrase(reason);
|
||||
}
|
||||
|
||||
public HttpEntity getEntity() {
|
||||
return original.getEntity();
|
||||
}
|
||||
|
||||
public void setEntity(final HttpEntity entity) {
|
||||
original.setEntity(entity);
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return original.getLocale();
|
||||
}
|
||||
|
||||
public void setLocale(final Locale loc) {
|
||||
original.setLocale(loc);
|
||||
}
|
||||
|
||||
public ProtocolVersion getProtocolVersion() {
|
||||
return original.getProtocolVersion();
|
||||
}
|
||||
|
||||
public boolean containsHeader(final String name) {
|
||||
return original.containsHeader(name);
|
||||
}
|
||||
|
||||
public Header[] getHeaders(final String name) {
|
||||
return original.getHeaders(name);
|
||||
}
|
||||
|
||||
public Header getFirstHeader(final String name) {
|
||||
return original.getFirstHeader(name);
|
||||
}
|
||||
|
||||
public Header getLastHeader(final String name) {
|
||||
return original.getLastHeader(name);
|
||||
}
|
||||
|
||||
public Header[] getAllHeaders() {
|
||||
return original.getAllHeaders();
|
||||
}
|
||||
|
||||
public void addHeader(final Header header) {
|
||||
original.addHeader(header);
|
||||
}
|
||||
|
||||
public void addHeader(final String name, final String value) {
|
||||
original.addHeader(name, value);
|
||||
}
|
||||
|
||||
public void setHeader(final Header header) {
|
||||
original.setHeader(header);
|
||||
}
|
||||
|
||||
public void setHeader(final String name, final String value) {
|
||||
original.setHeader(name, value);
|
||||
}
|
||||
|
||||
public void setHeaders(final Header[] headers) {
|
||||
original.setHeaders(headers);
|
||||
}
|
||||
|
||||
public void removeHeader(final Header header) {
|
||||
original.removeHeader(header);
|
||||
}
|
||||
|
||||
public void removeHeaders(final String name) {
|
||||
original.removeHeaders(name);
|
||||
}
|
||||
|
||||
public HeaderIterator headerIterator() {
|
||||
return original.headerIterator();
|
||||
}
|
||||
|
||||
public HeaderIterator headerIterator(final String name) {
|
||||
return original.headerIterator(name);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public HttpParams getParams() {
|
||||
return original.getParams();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setParams(final HttpParams params) {
|
||||
original.setParams(params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("HttpResponseProxy{");
|
||||
sb.append(original);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,568 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
|
||||
/* LogFactory removed by HttpClient for Android script. */
|
||||
import ch.boye.httpclientandroidlib.ConnectionReuseStrategy;
|
||||
import ch.boye.httpclientandroidlib.HttpClientConnection;
|
||||
import ch.boye.httpclientandroidlib.HttpEntity;
|
||||
import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.HttpHost;
|
||||
import ch.boye.httpclientandroidlib.HttpRequest;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.annotation.Immutable;
|
||||
import ch.boye.httpclientandroidlib.auth.AUTH;
|
||||
import ch.boye.httpclientandroidlib.auth.AuthProtocolState;
|
||||
import ch.boye.httpclientandroidlib.auth.AuthState;
|
||||
import ch.boye.httpclientandroidlib.client.AuthenticationStrategy;
|
||||
import ch.boye.httpclientandroidlib.client.NonRepeatableRequestException;
|
||||
import ch.boye.httpclientandroidlib.client.UserTokenHandler;
|
||||
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
|
||||
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl;
|
||||
import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy;
|
||||
import ch.boye.httpclientandroidlib.conn.ConnectionRequest;
|
||||
import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.BasicRouteDirector;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRouteDirector;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.RouteTracker;
|
||||
import ch.boye.httpclientandroidlib.entity.BufferedHttpEntity;
|
||||
import ch.boye.httpclientandroidlib.impl.auth.HttpAuthenticator;
|
||||
import ch.boye.httpclientandroidlib.impl.conn.ConnectionShutdownException;
|
||||
import ch.boye.httpclientandroidlib.message.BasicHttpRequest;
|
||||
import ch.boye.httpclientandroidlib.protocol.HttpCoreContext;
|
||||
import ch.boye.httpclientandroidlib.protocol.HttpProcessor;
|
||||
import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor;
|
||||
import ch.boye.httpclientandroidlib.protocol.ImmutableHttpProcessor;
|
||||
import ch.boye.httpclientandroidlib.protocol.RequestTargetHost;
|
||||
import ch.boye.httpclientandroidlib.util.Args;
|
||||
import ch.boye.httpclientandroidlib.util.EntityUtils;
|
||||
|
||||
/**
|
||||
* The last request executor in the HTTP request execution chain
|
||||
* that is responsible for execution of request / response
|
||||
* exchanges with the opposite endpoint.
|
||||
* This executor will automatically retry the request in case
|
||||
* of an authentication challenge by an intermediate proxy or
|
||||
* by the target server.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@Immutable
|
||||
public class MainClientExec implements ClientExecChain {
|
||||
|
||||
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
|
||||
|
||||
private final HttpRequestExecutor requestExecutor;
|
||||
private final HttpClientConnectionManager connManager;
|
||||
private final ConnectionReuseStrategy reuseStrategy;
|
||||
private final ConnectionKeepAliveStrategy keepAliveStrategy;
|
||||
private final HttpProcessor proxyHttpProcessor;
|
||||
private final AuthenticationStrategy targetAuthStrategy;
|
||||
private final AuthenticationStrategy proxyAuthStrategy;
|
||||
private final HttpAuthenticator authenticator;
|
||||
private final UserTokenHandler userTokenHandler;
|
||||
private final HttpRouteDirector routeDirector;
|
||||
|
||||
|
||||
public MainClientExec(
|
||||
final HttpRequestExecutor requestExecutor,
|
||||
final HttpClientConnectionManager connManager,
|
||||
final ConnectionReuseStrategy reuseStrategy,
|
||||
final ConnectionKeepAliveStrategy keepAliveStrategy,
|
||||
final AuthenticationStrategy targetAuthStrategy,
|
||||
final AuthenticationStrategy proxyAuthStrategy,
|
||||
final UserTokenHandler userTokenHandler) {
|
||||
Args.notNull(requestExecutor, "HTTP request executor");
|
||||
Args.notNull(connManager, "Client connection manager");
|
||||
Args.notNull(reuseStrategy, "Connection reuse strategy");
|
||||
Args.notNull(keepAliveStrategy, "Connection keep alive strategy");
|
||||
Args.notNull(targetAuthStrategy, "Target authentication strategy");
|
||||
Args.notNull(proxyAuthStrategy, "Proxy authentication strategy");
|
||||
Args.notNull(userTokenHandler, "User token handler");
|
||||
this.authenticator = new HttpAuthenticator();
|
||||
this.proxyHttpProcessor = new ImmutableHttpProcessor(
|
||||
new RequestTargetHost(), new RequestClientConnControl());
|
||||
this.routeDirector = new BasicRouteDirector();
|
||||
this.requestExecutor = requestExecutor;
|
||||
this.connManager = connManager;
|
||||
this.reuseStrategy = reuseStrategy;
|
||||
this.keepAliveStrategy = keepAliveStrategy;
|
||||
this.targetAuthStrategy = targetAuthStrategy;
|
||||
this.proxyAuthStrategy = proxyAuthStrategy;
|
||||
this.userTokenHandler = userTokenHandler;
|
||||
}
|
||||
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpClientContext context,
|
||||
final HttpExecutionAware execAware) throws IOException, HttpException {
|
||||
Args.notNull(route, "HTTP route");
|
||||
Args.notNull(request, "HTTP request");
|
||||
Args.notNull(context, "HTTP context");
|
||||
|
||||
AuthState targetAuthState = context.getTargetAuthState();
|
||||
if (targetAuthState == null) {
|
||||
targetAuthState = new AuthState();
|
||||
context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, targetAuthState);
|
||||
}
|
||||
AuthState proxyAuthState = context.getProxyAuthState();
|
||||
if (proxyAuthState == null) {
|
||||
proxyAuthState = new AuthState();
|
||||
context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState);
|
||||
}
|
||||
|
||||
if (request instanceof HttpEntityEnclosingRequest) {
|
||||
RequestEntityProxy.enhance((HttpEntityEnclosingRequest) request);
|
||||
}
|
||||
|
||||
Object userToken = context.getUserToken();
|
||||
|
||||
final ConnectionRequest connRequest = connManager.requestConnection(route, userToken);
|
||||
if (execAware != null) {
|
||||
if (execAware.isAborted()) {
|
||||
connRequest.cancel();
|
||||
throw new RequestAbortedException("Request aborted");
|
||||
} else {
|
||||
execAware.setCancellable(connRequest);
|
||||
}
|
||||
}
|
||||
|
||||
final RequestConfig config = context.getRequestConfig();
|
||||
|
||||
final HttpClientConnection managedConn;
|
||||
try {
|
||||
final int timeout = config.getConnectionRequestTimeout();
|
||||
managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
|
||||
} catch(final InterruptedException interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RequestAbortedException("Request aborted", interrupted);
|
||||
} catch(final ExecutionException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause == null) {
|
||||
cause = ex;
|
||||
}
|
||||
throw new RequestAbortedException("Request execution failed", cause);
|
||||
}
|
||||
|
||||
context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn);
|
||||
|
||||
if (config.isStaleConnectionCheckEnabled()) {
|
||||
// validate connection
|
||||
if (managedConn.isOpen()) {
|
||||
this.log.debug("Stale connection check");
|
||||
if (managedConn.isStale()) {
|
||||
this.log.debug("Stale connection detected");
|
||||
managedConn.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn);
|
||||
try {
|
||||
if (execAware != null) {
|
||||
execAware.setCancellable(connHolder);
|
||||
}
|
||||
|
||||
HttpResponse response;
|
||||
for (int execCount = 1;; execCount++) {
|
||||
|
||||
if (execCount > 1 && !RequestEntityProxy.isRepeatable(request)) {
|
||||
throw new NonRepeatableRequestException("Cannot retry request " +
|
||||
"with a non-repeatable request entity.");
|
||||
}
|
||||
|
||||
if (execAware != null && execAware.isAborted()) {
|
||||
throw new RequestAbortedException("Request aborted");
|
||||
}
|
||||
|
||||
if (!managedConn.isOpen()) {
|
||||
this.log.debug("Opening connection " + route);
|
||||
try {
|
||||
establishRoute(proxyAuthState, managedConn, route, request, context);
|
||||
} catch (final TunnelRefusedException ex) {
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug(ex.getMessage());
|
||||
}
|
||||
response = ex.getResponse();
|
||||
break;
|
||||
}
|
||||
}
|
||||
final int timeout = config.getSocketTimeout();
|
||||
if (timeout >= 0) {
|
||||
managedConn.setSocketTimeout(timeout);
|
||||
}
|
||||
|
||||
if (execAware != null && execAware.isAborted()) {
|
||||
throw new RequestAbortedException("Request aborted");
|
||||
}
|
||||
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug("Executing request " + request.getRequestLine());
|
||||
}
|
||||
|
||||
if (!request.containsHeader(AUTH.WWW_AUTH_RESP)) {
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug("Target auth state: " + targetAuthState.getState());
|
||||
}
|
||||
this.authenticator.generateAuthResponse(request, targetAuthState, context);
|
||||
}
|
||||
if (!request.containsHeader(AUTH.PROXY_AUTH_RESP) && !route.isTunnelled()) {
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug("Proxy auth state: " + proxyAuthState.getState());
|
||||
}
|
||||
this.authenticator.generateAuthResponse(request, proxyAuthState, context);
|
||||
}
|
||||
|
||||
response = requestExecutor.execute(request, managedConn, context);
|
||||
|
||||
// The connection is in or can be brought to a re-usable state.
|
||||
if (reuseStrategy.keepAlive(response, context)) {
|
||||
// Set the idle duration of this connection
|
||||
final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
|
||||
if (this.log.isDebugEnabled()) {
|
||||
final String s;
|
||||
if (duration > 0) {
|
||||
s = "for " + duration + " " + TimeUnit.MILLISECONDS;
|
||||
} else {
|
||||
s = "indefinitely";
|
||||
}
|
||||
this.log.debug("Connection can be kept alive " + s);
|
||||
}
|
||||
connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);
|
||||
connHolder.markReusable();
|
||||
} else {
|
||||
connHolder.markNonReusable();
|
||||
}
|
||||
|
||||
if (needAuthentication(
|
||||
targetAuthState, proxyAuthState, route, response, context)) {
|
||||
// Make sure the response body is fully consumed, if present
|
||||
final HttpEntity entity = response.getEntity();
|
||||
if (connHolder.isReusable()) {
|
||||
EntityUtils.consume(entity);
|
||||
} else {
|
||||
managedConn.close();
|
||||
if (proxyAuthState.getState() == AuthProtocolState.SUCCESS
|
||||
&& proxyAuthState.getAuthScheme() != null
|
||||
&& proxyAuthState.getAuthScheme().isConnectionBased()) {
|
||||
this.log.debug("Resetting proxy auth state");
|
||||
proxyAuthState.reset();
|
||||
}
|
||||
if (targetAuthState.getState() == AuthProtocolState.SUCCESS
|
||||
&& targetAuthState.getAuthScheme() != null
|
||||
&& targetAuthState.getAuthScheme().isConnectionBased()) {
|
||||
this.log.debug("Resetting target auth state");
|
||||
targetAuthState.reset();
|
||||
}
|
||||
}
|
||||
// discard previous auth headers
|
||||
final HttpRequest original = request.getOriginal();
|
||||
if (!original.containsHeader(AUTH.WWW_AUTH_RESP)) {
|
||||
request.removeHeaders(AUTH.WWW_AUTH_RESP);
|
||||
}
|
||||
if (!original.containsHeader(AUTH.PROXY_AUTH_RESP)) {
|
||||
request.removeHeaders(AUTH.PROXY_AUTH_RESP);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (userToken == null) {
|
||||
userToken = userTokenHandler.getUserToken(context);
|
||||
context.setAttribute(HttpClientContext.USER_TOKEN, userToken);
|
||||
}
|
||||
if (userToken != null) {
|
||||
connHolder.setState(userToken);
|
||||
}
|
||||
|
||||
// check for entity, release connection if possible
|
||||
final HttpEntity entity = response.getEntity();
|
||||
if (entity == null || !entity.isStreaming()) {
|
||||
// connection not needed and (assumed to be) in re-usable state
|
||||
connHolder.releaseConnection();
|
||||
return new HttpResponseProxy(response, null);
|
||||
} else {
|
||||
return new HttpResponseProxy(response, connHolder);
|
||||
}
|
||||
} catch (final ConnectionShutdownException ex) {
|
||||
final InterruptedIOException ioex = new InterruptedIOException(
|
||||
"Connection has been shut down");
|
||||
ioex.initCause(ex);
|
||||
throw ioex;
|
||||
} catch (final HttpException ex) {
|
||||
connHolder.abortConnection();
|
||||
throw ex;
|
||||
} catch (final IOException ex) {
|
||||
connHolder.abortConnection();
|
||||
throw ex;
|
||||
} catch (final RuntimeException ex) {
|
||||
connHolder.abortConnection();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes the target route.
|
||||
*/
|
||||
void establishRoute(
|
||||
final AuthState proxyAuthState,
|
||||
final HttpClientConnection managedConn,
|
||||
final HttpRoute route,
|
||||
final HttpRequest request,
|
||||
final HttpClientContext context) throws HttpException, IOException {
|
||||
final RequestConfig config = context.getRequestConfig();
|
||||
final int timeout = config.getConnectTimeout();
|
||||
final RouteTracker tracker = new RouteTracker(route);
|
||||
int step;
|
||||
do {
|
||||
final HttpRoute fact = tracker.toRoute();
|
||||
step = this.routeDirector.nextStep(route, fact);
|
||||
|
||||
switch (step) {
|
||||
|
||||
case HttpRouteDirector.CONNECT_TARGET:
|
||||
this.connManager.connect(
|
||||
managedConn,
|
||||
route,
|
||||
timeout > 0 ? timeout : 0,
|
||||
context);
|
||||
tracker.connectTarget(route.isSecure());
|
||||
break;
|
||||
case HttpRouteDirector.CONNECT_PROXY:
|
||||
this.connManager.connect(
|
||||
managedConn,
|
||||
route,
|
||||
timeout > 0 ? timeout : 0,
|
||||
context);
|
||||
final HttpHost proxy = route.getProxyHost();
|
||||
tracker.connectProxy(proxy, false);
|
||||
break;
|
||||
case HttpRouteDirector.TUNNEL_TARGET: {
|
||||
final boolean secure = createTunnelToTarget(
|
||||
proxyAuthState, managedConn, route, request, context);
|
||||
this.log.debug("Tunnel to target created.");
|
||||
tracker.tunnelTarget(secure);
|
||||
} break;
|
||||
|
||||
case HttpRouteDirector.TUNNEL_PROXY: {
|
||||
// The most simple example for this case is a proxy chain
|
||||
// of two proxies, where P1 must be tunnelled to P2.
|
||||
// route: Source -> P1 -> P2 -> Target (3 hops)
|
||||
// fact: Source -> P1 -> Target (2 hops)
|
||||
final int hop = fact.getHopCount()-1; // the hop to establish
|
||||
final boolean secure = createTunnelToProxy(route, hop, context);
|
||||
this.log.debug("Tunnel to proxy created.");
|
||||
tracker.tunnelProxy(route.getHopTarget(hop), secure);
|
||||
} break;
|
||||
|
||||
case HttpRouteDirector.LAYER_PROTOCOL:
|
||||
this.connManager.upgrade(managedConn, route, context);
|
||||
tracker.layerProtocol(route.isSecure());
|
||||
break;
|
||||
|
||||
case HttpRouteDirector.UNREACHABLE:
|
||||
throw new HttpException("Unable to establish route: " +
|
||||
"planned = " + route + "; current = " + fact);
|
||||
case HttpRouteDirector.COMPLETE:
|
||||
this.connManager.routeComplete(managedConn, route, context);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown step indicator "
|
||||
+ step + " from RouteDirector.");
|
||||
}
|
||||
|
||||
} while (step > HttpRouteDirector.COMPLETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tunnel to the target server.
|
||||
* The connection must be established to the (last) proxy.
|
||||
* A CONNECT request for tunnelling through the proxy will
|
||||
* be created and sent, the response received and checked.
|
||||
* This method does <i>not</i> update the connection with
|
||||
* information about the tunnel, that is left to the caller.
|
||||
*/
|
||||
private boolean createTunnelToTarget(
|
||||
final AuthState proxyAuthState,
|
||||
final HttpClientConnection managedConn,
|
||||
final HttpRoute route,
|
||||
final HttpRequest request,
|
||||
final HttpClientContext context) throws HttpException, IOException {
|
||||
|
||||
final RequestConfig config = context.getRequestConfig();
|
||||
final int timeout = config.getConnectTimeout();
|
||||
|
||||
final HttpHost target = route.getTargetHost();
|
||||
final HttpHost proxy = route.getProxyHost();
|
||||
HttpResponse response = null;
|
||||
|
||||
final String authority = target.toHostString();
|
||||
final HttpRequest connect = new BasicHttpRequest("CONNECT", authority, request.getProtocolVersion());
|
||||
|
||||
this.requestExecutor.preProcess(connect, this.proxyHttpProcessor, context);
|
||||
|
||||
while (response == null) {
|
||||
if (!managedConn.isOpen()) {
|
||||
this.connManager.connect(
|
||||
managedConn,
|
||||
route,
|
||||
timeout > 0 ? timeout : 0,
|
||||
context);
|
||||
}
|
||||
|
||||
connect.removeHeaders(AUTH.PROXY_AUTH_RESP);
|
||||
this.authenticator.generateAuthResponse(connect, proxyAuthState, context);
|
||||
|
||||
response = this.requestExecutor.execute(connect, managedConn, context);
|
||||
|
||||
final int status = response.getStatusLine().getStatusCode();
|
||||
if (status < 200) {
|
||||
throw new HttpException("Unexpected response to CONNECT request: " +
|
||||
response.getStatusLine());
|
||||
}
|
||||
|
||||
if (config.isAuthenticationEnabled()) {
|
||||
if (this.authenticator.isAuthenticationRequested(proxy, response,
|
||||
this.proxyAuthStrategy, proxyAuthState, context)) {
|
||||
if (this.authenticator.handleAuthChallenge(proxy, response,
|
||||
this.proxyAuthStrategy, proxyAuthState, context)) {
|
||||
// Retry request
|
||||
if (this.reuseStrategy.keepAlive(response, context)) {
|
||||
this.log.debug("Connection kept alive");
|
||||
// Consume response content
|
||||
final HttpEntity entity = response.getEntity();
|
||||
EntityUtils.consume(entity);
|
||||
} else {
|
||||
managedConn.close();
|
||||
}
|
||||
response = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final int status = response.getStatusLine().getStatusCode();
|
||||
|
||||
if (status > 299) {
|
||||
|
||||
// Buffer response content
|
||||
final HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
response.setEntity(new BufferedHttpEntity(entity));
|
||||
}
|
||||
|
||||
managedConn.close();
|
||||
throw new TunnelRefusedException("CONNECT refused by proxy: " +
|
||||
response.getStatusLine(), response);
|
||||
}
|
||||
|
||||
// How to decide on security of the tunnelled connection?
|
||||
// The socket factory knows only about the segment to the proxy.
|
||||
// Even if that is secure, the hop to the target may be insecure.
|
||||
// Leave it to derived classes, consider insecure by default here.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tunnel to an intermediate proxy.
|
||||
* This method is <i>not</i> implemented in this class.
|
||||
* It just throws an exception here.
|
||||
*/
|
||||
private boolean createTunnelToProxy(
|
||||
final HttpRoute route,
|
||||
final int hop,
|
||||
final HttpClientContext context) throws HttpException {
|
||||
|
||||
// Have a look at createTunnelToTarget and replicate the parts
|
||||
// you need in a custom derived class. If your proxies don't require
|
||||
// authentication, it is not too hard. But for the stock version of
|
||||
// HttpClient, we cannot make such simplifying assumptions and would
|
||||
// have to include proxy authentication code. The HttpComponents team
|
||||
// is currently not in a position to support rarely used code of this
|
||||
// complexity. Feel free to submit patches that refactor the code in
|
||||
// createTunnelToTarget to facilitate re-use for proxy tunnelling.
|
||||
|
||||
throw new HttpException("Proxy chains are not supported.");
|
||||
}
|
||||
|
||||
private boolean needAuthentication(
|
||||
final AuthState targetAuthState,
|
||||
final AuthState proxyAuthState,
|
||||
final HttpRoute route,
|
||||
final HttpResponse response,
|
||||
final HttpClientContext context) {
|
||||
final RequestConfig config = context.getRequestConfig();
|
||||
if (config.isAuthenticationEnabled()) {
|
||||
HttpHost target = context.getTargetHost();
|
||||
if (target == null) {
|
||||
target = route.getTargetHost();
|
||||
}
|
||||
if (target.getPort() < 0) {
|
||||
target = new HttpHost(
|
||||
target.getHostName(),
|
||||
route.getTargetHost().getPort(),
|
||||
target.getSchemeName());
|
||||
}
|
||||
final boolean targetAuthRequested = this.authenticator.isAuthenticationRequested(
|
||||
target, response, this.targetAuthStrategy, targetAuthState, context);
|
||||
|
||||
HttpHost proxy = route.getProxyHost();
|
||||
// if proxy is not set use target host instead
|
||||
if (proxy == null) {
|
||||
proxy = route.getTargetHost();
|
||||
}
|
||||
final boolean proxyAuthRequested = this.authenticator.isAuthenticationRequested(
|
||||
proxy, response, this.proxyAuthStrategy, proxyAuthState, context);
|
||||
|
||||
if (targetAuthRequested) {
|
||||
return this.authenticator.handleAuthChallenge(target, response,
|
||||
this.targetAuthStrategy, targetAuthState, context);
|
||||
}
|
||||
if (proxyAuthRequested) {
|
||||
return this.authenticator.handleAuthChallenge(proxy, response,
|
||||
this.proxyAuthStrategy, proxyAuthState, context);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,251 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
|
||||
/* LogFactory removed by HttpClient for Android script. */
|
||||
import ch.boye.httpclientandroidlib.ConnectionReuseStrategy;
|
||||
import ch.boye.httpclientandroidlib.HttpClientConnection;
|
||||
import ch.boye.httpclientandroidlib.HttpEntity;
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.HttpHost;
|
||||
import ch.boye.httpclientandroidlib.HttpRequest;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.ProtocolException;
|
||||
import ch.boye.httpclientandroidlib.annotation.Immutable;
|
||||
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
|
||||
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.RequestClientConnControl;
|
||||
import ch.boye.httpclientandroidlib.client.utils.URIUtils;
|
||||
import ch.boye.httpclientandroidlib.conn.ConnectionKeepAliveStrategy;
|
||||
import ch.boye.httpclientandroidlib.conn.ConnectionRequest;
|
||||
import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
|
||||
import ch.boye.httpclientandroidlib.impl.conn.ConnectionShutdownException;
|
||||
import ch.boye.httpclientandroidlib.protocol.HttpCoreContext;
|
||||
import ch.boye.httpclientandroidlib.protocol.HttpProcessor;
|
||||
import ch.boye.httpclientandroidlib.protocol.HttpRequestExecutor;
|
||||
import ch.boye.httpclientandroidlib.protocol.ImmutableHttpProcessor;
|
||||
import ch.boye.httpclientandroidlib.protocol.RequestContent;
|
||||
import ch.boye.httpclientandroidlib.protocol.RequestTargetHost;
|
||||
import ch.boye.httpclientandroidlib.protocol.RequestUserAgent;
|
||||
import ch.boye.httpclientandroidlib.util.Args;
|
||||
import ch.boye.httpclientandroidlib.util.VersionInfo;
|
||||
|
||||
/**
|
||||
* Request executor that implements the most fundamental aspects of
|
||||
* the HTTP specification and the most straight-forward request / response
|
||||
* exchange with the target server. This executor does not support
|
||||
* execution via proxy and will make no attempts to retry the request
|
||||
* in case of a redirect, authentication challenge or I/O error.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@Immutable
|
||||
public class MinimalClientExec implements ClientExecChain {
|
||||
|
||||
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
|
||||
|
||||
private final HttpRequestExecutor requestExecutor;
|
||||
private final HttpClientConnectionManager connManager;
|
||||
private final ConnectionReuseStrategy reuseStrategy;
|
||||
private final ConnectionKeepAliveStrategy keepAliveStrategy;
|
||||
private final HttpProcessor httpProcessor;
|
||||
|
||||
public MinimalClientExec(
|
||||
final HttpRequestExecutor requestExecutor,
|
||||
final HttpClientConnectionManager connManager,
|
||||
final ConnectionReuseStrategy reuseStrategy,
|
||||
final ConnectionKeepAliveStrategy keepAliveStrategy) {
|
||||
Args.notNull(requestExecutor, "HTTP request executor");
|
||||
Args.notNull(connManager, "Client connection manager");
|
||||
Args.notNull(reuseStrategy, "Connection reuse strategy");
|
||||
Args.notNull(keepAliveStrategy, "Connection keep alive strategy");
|
||||
this.httpProcessor = new ImmutableHttpProcessor(
|
||||
new RequestContent(),
|
||||
new RequestTargetHost(),
|
||||
new RequestClientConnControl(),
|
||||
new RequestUserAgent(VersionInfo.getUserAgent(
|
||||
"Apache-HttpClient", "ch.boye.httpclientandroidlib.client", getClass())));
|
||||
this.requestExecutor = requestExecutor;
|
||||
this.connManager = connManager;
|
||||
this.reuseStrategy = reuseStrategy;
|
||||
this.keepAliveStrategy = keepAliveStrategy;
|
||||
}
|
||||
|
||||
static void rewriteRequestURI(
|
||||
final HttpRequestWrapper request,
|
||||
final HttpRoute route) throws ProtocolException {
|
||||
try {
|
||||
URI uri = request.getURI();
|
||||
if (uri != null) {
|
||||
// Make sure the request URI is relative
|
||||
if (uri.isAbsolute()) {
|
||||
uri = URIUtils.rewriteURI(uri, null, true);
|
||||
} else {
|
||||
uri = URIUtils.rewriteURI(uri);
|
||||
}
|
||||
request.setURI(uri);
|
||||
}
|
||||
} catch (final URISyntaxException ex) {
|
||||
throw new ProtocolException("Invalid URI: " + request.getRequestLine().getUri(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpClientContext context,
|
||||
final HttpExecutionAware execAware) throws IOException, HttpException {
|
||||
Args.notNull(route, "HTTP route");
|
||||
Args.notNull(request, "HTTP request");
|
||||
Args.notNull(context, "HTTP context");
|
||||
|
||||
rewriteRequestURI(request, route);
|
||||
|
||||
final ConnectionRequest connRequest = connManager.requestConnection(route, null);
|
||||
if (execAware != null) {
|
||||
if (execAware.isAborted()) {
|
||||
connRequest.cancel();
|
||||
throw new RequestAbortedException("Request aborted");
|
||||
} else {
|
||||
execAware.setCancellable(connRequest);
|
||||
}
|
||||
}
|
||||
|
||||
final RequestConfig config = context.getRequestConfig();
|
||||
|
||||
final HttpClientConnection managedConn;
|
||||
try {
|
||||
final int timeout = config.getConnectionRequestTimeout();
|
||||
managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
|
||||
} catch(final InterruptedException interrupted) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RequestAbortedException("Request aborted", interrupted);
|
||||
} catch(final ExecutionException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause == null) {
|
||||
cause = ex;
|
||||
}
|
||||
throw new RequestAbortedException("Request execution failed", cause);
|
||||
}
|
||||
|
||||
final ConnectionHolder releaseTrigger = new ConnectionHolder(log, connManager, managedConn);
|
||||
try {
|
||||
if (execAware != null) {
|
||||
if (execAware.isAborted()) {
|
||||
releaseTrigger.close();
|
||||
throw new RequestAbortedException("Request aborted");
|
||||
} else {
|
||||
execAware.setCancellable(releaseTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
if (!managedConn.isOpen()) {
|
||||
final int timeout = config.getConnectTimeout();
|
||||
this.connManager.connect(
|
||||
managedConn,
|
||||
route,
|
||||
timeout > 0 ? timeout : 0,
|
||||
context);
|
||||
this.connManager.routeComplete(managedConn, route, context);
|
||||
}
|
||||
final int timeout = config.getSocketTimeout();
|
||||
if (timeout >= 0) {
|
||||
managedConn.setSocketTimeout(timeout);
|
||||
}
|
||||
|
||||
HttpHost target = null;
|
||||
final HttpRequest original = request.getOriginal();
|
||||
if (original instanceof HttpUriRequest) {
|
||||
final URI uri = ((HttpUriRequest) original).getURI();
|
||||
if (uri.isAbsolute()) {
|
||||
target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
|
||||
}
|
||||
}
|
||||
if (target == null) {
|
||||
target = route.getTargetHost();
|
||||
}
|
||||
|
||||
context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target);
|
||||
context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
|
||||
context.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn);
|
||||
context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
|
||||
|
||||
httpProcessor.process(request, context);
|
||||
final HttpResponse response = requestExecutor.execute(request, managedConn, context);
|
||||
httpProcessor.process(response, context);
|
||||
|
||||
// The connection is in or can be brought to a re-usable state.
|
||||
if (reuseStrategy.keepAlive(response, context)) {
|
||||
// Set the idle duration of this connection
|
||||
final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
|
||||
releaseTrigger.setValidFor(duration, TimeUnit.MILLISECONDS);
|
||||
releaseTrigger.markReusable();
|
||||
} else {
|
||||
releaseTrigger.markNonReusable();
|
||||
}
|
||||
|
||||
// check for entity, release connection if possible
|
||||
final HttpEntity entity = response.getEntity();
|
||||
if (entity == null || !entity.isStreaming()) {
|
||||
// connection not needed and (assumed to be) in re-usable state
|
||||
releaseTrigger.releaseConnection();
|
||||
return new HttpResponseProxy(response, null);
|
||||
} else {
|
||||
return new HttpResponseProxy(response, releaseTrigger);
|
||||
}
|
||||
} catch (final ConnectionShutdownException ex) {
|
||||
final InterruptedIOException ioex = new InterruptedIOException(
|
||||
"Connection has been shut down");
|
||||
ioex.initCause(ex);
|
||||
throw ioex;
|
||||
} catch (final HttpException ex) {
|
||||
releaseTrigger.abortConnection();
|
||||
throw ex;
|
||||
} catch (final IOException ex) {
|
||||
releaseTrigger.abortConnection();
|
||||
throw ex;
|
||||
} catch (final RuntimeException ex) {
|
||||
releaseTrigger.abortConnection();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,214 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
|
||||
/* LogFactory removed by HttpClient for Android script. */
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.HttpHost;
|
||||
import ch.boye.httpclientandroidlib.HttpRequest;
|
||||
import ch.boye.httpclientandroidlib.ProtocolException;
|
||||
import ch.boye.httpclientandroidlib.annotation.Immutable;
|
||||
import ch.boye.httpclientandroidlib.auth.AuthScope;
|
||||
import ch.boye.httpclientandroidlib.auth.UsernamePasswordCredentials;
|
||||
import ch.boye.httpclientandroidlib.client.CredentialsProvider;
|
||||
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
|
||||
import ch.boye.httpclientandroidlib.client.params.ClientPNames;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
|
||||
import ch.boye.httpclientandroidlib.client.utils.URIUtils;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
|
||||
import ch.boye.httpclientandroidlib.impl.client.BasicCredentialsProvider;
|
||||
import ch.boye.httpclientandroidlib.params.HttpParams;
|
||||
import ch.boye.httpclientandroidlib.protocol.HttpCoreContext;
|
||||
import ch.boye.httpclientandroidlib.protocol.HttpProcessor;
|
||||
import ch.boye.httpclientandroidlib.util.Args;
|
||||
|
||||
/**
|
||||
* Request executor in the request execution chain that is responsible
|
||||
* for implementation of HTTP specification requirements.
|
||||
* Internally this executor relies on a {@link HttpProcessor} to populate
|
||||
* requisite HTTP request headers, process HTTP response headers and update
|
||||
* session state in {@link HttpClientContext}.
|
||||
* <p/>
|
||||
* Further responsibilities such as communication with the opposite
|
||||
* endpoint is delegated to the next executor in the request execution
|
||||
* chain.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@Immutable
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ProtocolExec implements ClientExecChain {
|
||||
|
||||
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
|
||||
|
||||
private final ClientExecChain requestExecutor;
|
||||
private final HttpProcessor httpProcessor;
|
||||
|
||||
public ProtocolExec(final ClientExecChain requestExecutor, final HttpProcessor httpProcessor) {
|
||||
Args.notNull(requestExecutor, "HTTP client request executor");
|
||||
Args.notNull(httpProcessor, "HTTP protocol processor");
|
||||
this.requestExecutor = requestExecutor;
|
||||
this.httpProcessor = httpProcessor;
|
||||
}
|
||||
|
||||
void rewriteRequestURI(
|
||||
final HttpRequestWrapper request,
|
||||
final HttpRoute route) throws ProtocolException {
|
||||
try {
|
||||
URI uri = request.getURI();
|
||||
if (uri != null) {
|
||||
if (route.getProxyHost() != null && !route.isTunnelled()) {
|
||||
// Make sure the request URI is absolute
|
||||
if (!uri.isAbsolute()) {
|
||||
final HttpHost target = route.getTargetHost();
|
||||
uri = URIUtils.rewriteURI(uri, target, true);
|
||||
} else {
|
||||
uri = URIUtils.rewriteURI(uri);
|
||||
}
|
||||
} else {
|
||||
// Make sure the request URI is relative
|
||||
if (uri.isAbsolute()) {
|
||||
uri = URIUtils.rewriteURI(uri, null, true);
|
||||
} else {
|
||||
uri = URIUtils.rewriteURI(uri);
|
||||
}
|
||||
}
|
||||
request.setURI(uri);
|
||||
}
|
||||
} catch (final URISyntaxException ex) {
|
||||
throw new ProtocolException("Invalid URI: " + request.getRequestLine().getUri(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpClientContext context,
|
||||
final HttpExecutionAware execAware) throws IOException,
|
||||
HttpException {
|
||||
Args.notNull(route, "HTTP route");
|
||||
Args.notNull(request, "HTTP request");
|
||||
Args.notNull(context, "HTTP context");
|
||||
|
||||
final HttpRequest original = request.getOriginal();
|
||||
URI uri = null;
|
||||
if (original instanceof HttpUriRequest) {
|
||||
uri = ((HttpUriRequest) original).getURI();
|
||||
} else {
|
||||
final String uriString = original.getRequestLine().getUri();
|
||||
try {
|
||||
uri = URI.create(uriString);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug("Unable to parse '" + uriString + "' as a valid URI; " +
|
||||
"request URI and Host header may be inconsistent", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
request.setURI(uri);
|
||||
|
||||
// Re-write request URI if needed
|
||||
rewriteRequestURI(request, route);
|
||||
|
||||
final HttpParams params = request.getParams();
|
||||
HttpHost virtualHost = (HttpHost) params.getParameter(ClientPNames.VIRTUAL_HOST);
|
||||
// HTTPCLIENT-1092 - add the port if necessary
|
||||
if (virtualHost != null && virtualHost.getPort() == -1) {
|
||||
final int port = route.getTargetHost().getPort();
|
||||
if (port != -1) {
|
||||
virtualHost = new HttpHost(virtualHost.getHostName(), port,
|
||||
virtualHost.getSchemeName());
|
||||
}
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug("Using virtual host" + virtualHost);
|
||||
}
|
||||
}
|
||||
|
||||
HttpHost target = null;
|
||||
if (virtualHost != null) {
|
||||
target = virtualHost;
|
||||
} else {
|
||||
if (uri != null && uri.isAbsolute() && uri.getHost() != null) {
|
||||
target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
|
||||
}
|
||||
}
|
||||
if (target == null) {
|
||||
target = route.getTargetHost();
|
||||
}
|
||||
|
||||
// Get user info from the URI
|
||||
if (uri != null) {
|
||||
final String userinfo = uri.getUserInfo();
|
||||
if (userinfo != null) {
|
||||
CredentialsProvider credsProvider = context.getCredentialsProvider();
|
||||
if (credsProvider == null) {
|
||||
credsProvider = new BasicCredentialsProvider();
|
||||
context.setCredentialsProvider(credsProvider);
|
||||
}
|
||||
credsProvider.setCredentials(
|
||||
new AuthScope(target),
|
||||
new UsernamePasswordCredentials(userinfo));
|
||||
}
|
||||
}
|
||||
|
||||
// Run request protocol interceptors
|
||||
context.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target);
|
||||
context.setAttribute(HttpClientContext.HTTP_ROUTE, route);
|
||||
context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
|
||||
|
||||
this.httpProcessor.process(request, context);
|
||||
|
||||
final CloseableHttpResponse response = this.requestExecutor.execute(route, request,
|
||||
context, execAware);
|
||||
try {
|
||||
// Run response protocol interceptors
|
||||
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
|
||||
this.httpProcessor.process(response, context);
|
||||
return response;
|
||||
} catch (final RuntimeException ex) {
|
||||
response.close();
|
||||
throw ex;
|
||||
} catch (final IOException ex) {
|
||||
response.close();
|
||||
throw ex;
|
||||
} catch (final HttpException ex) {
|
||||
response.close();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,185 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
|
||||
/* LogFactory removed by HttpClient for Android script. */
|
||||
import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.HttpHost;
|
||||
import ch.boye.httpclientandroidlib.HttpRequest;
|
||||
import ch.boye.httpclientandroidlib.ProtocolException;
|
||||
import ch.boye.httpclientandroidlib.annotation.ThreadSafe;
|
||||
import ch.boye.httpclientandroidlib.auth.AuthScheme;
|
||||
import ch.boye.httpclientandroidlib.auth.AuthState;
|
||||
import ch.boye.httpclientandroidlib.client.RedirectException;
|
||||
import ch.boye.httpclientandroidlib.client.RedirectStrategy;
|
||||
import ch.boye.httpclientandroidlib.client.config.RequestConfig;
|
||||
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
|
||||
import ch.boye.httpclientandroidlib.client.utils.URIUtils;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner;
|
||||
import ch.boye.httpclientandroidlib.util.Args;
|
||||
import ch.boye.httpclientandroidlib.util.EntityUtils;
|
||||
|
||||
/**
|
||||
* Request executor in the request execution chain that is responsible
|
||||
* for handling of request redirects.
|
||||
* <p/>
|
||||
* Further responsibilities such as communication with the opposite
|
||||
* endpoint is delegated to the next executor in the request execution
|
||||
* chain.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@ThreadSafe
|
||||
public class RedirectExec implements ClientExecChain {
|
||||
|
||||
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
|
||||
|
||||
private final ClientExecChain requestExecutor;
|
||||
private final RedirectStrategy redirectStrategy;
|
||||
private final HttpRoutePlanner routePlanner;
|
||||
|
||||
public RedirectExec(
|
||||
final ClientExecChain requestExecutor,
|
||||
final HttpRoutePlanner routePlanner,
|
||||
final RedirectStrategy redirectStrategy) {
|
||||
super();
|
||||
Args.notNull(requestExecutor, "HTTP client request executor");
|
||||
Args.notNull(routePlanner, "HTTP route planner");
|
||||
Args.notNull(redirectStrategy, "HTTP redirect strategy");
|
||||
this.requestExecutor = requestExecutor;
|
||||
this.routePlanner = routePlanner;
|
||||
this.redirectStrategy = redirectStrategy;
|
||||
}
|
||||
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpClientContext context,
|
||||
final HttpExecutionAware execAware) throws IOException, HttpException {
|
||||
Args.notNull(route, "HTTP route");
|
||||
Args.notNull(request, "HTTP request");
|
||||
Args.notNull(context, "HTTP context");
|
||||
|
||||
final List<URI> redirectLocations = context.getRedirectLocations();
|
||||
if (redirectLocations != null) {
|
||||
redirectLocations.clear();
|
||||
}
|
||||
|
||||
final RequestConfig config = context.getRequestConfig();
|
||||
final int maxRedirects = config.getMaxRedirects() > 0 ? config.getMaxRedirects() : 50;
|
||||
HttpRoute currentRoute = route;
|
||||
HttpRequestWrapper currentRequest = request;
|
||||
for (int redirectCount = 0;;) {
|
||||
final CloseableHttpResponse response = requestExecutor.execute(
|
||||
currentRoute, currentRequest, context, execAware);
|
||||
try {
|
||||
if (config.isRedirectsEnabled() &&
|
||||
this.redirectStrategy.isRedirected(currentRequest, response, context)) {
|
||||
|
||||
if (redirectCount >= maxRedirects) {
|
||||
throw new RedirectException("Maximum redirects ("+ maxRedirects + ") exceeded");
|
||||
}
|
||||
redirectCount++;
|
||||
|
||||
final HttpRequest redirect = this.redirectStrategy.getRedirect(
|
||||
currentRequest, response, context);
|
||||
if (!redirect.headerIterator().hasNext()) {
|
||||
final HttpRequest original = request.getOriginal();
|
||||
redirect.setHeaders(original.getAllHeaders());
|
||||
}
|
||||
currentRequest = HttpRequestWrapper.wrap(redirect);
|
||||
|
||||
if (currentRequest instanceof HttpEntityEnclosingRequest) {
|
||||
RequestEntityProxy.enhance((HttpEntityEnclosingRequest) currentRequest);
|
||||
}
|
||||
|
||||
final URI uri = currentRequest.getURI();
|
||||
final HttpHost newTarget = URIUtils.extractHost(uri);
|
||||
if (newTarget == null) {
|
||||
throw new ProtocolException("Redirect URI does not specify a valid host name: " +
|
||||
uri);
|
||||
}
|
||||
|
||||
// Reset virtual host and auth states if redirecting to another host
|
||||
if (!currentRoute.getTargetHost().equals(newTarget)) {
|
||||
final AuthState targetAuthState = context.getTargetAuthState();
|
||||
if (targetAuthState != null) {
|
||||
this.log.debug("Resetting target auth state");
|
||||
targetAuthState.reset();
|
||||
}
|
||||
final AuthState proxyAuthState = context.getProxyAuthState();
|
||||
if (proxyAuthState != null) {
|
||||
final AuthScheme authScheme = proxyAuthState.getAuthScheme();
|
||||
if (authScheme != null && authScheme.isConnectionBased()) {
|
||||
this.log.debug("Resetting proxy auth state");
|
||||
proxyAuthState.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentRoute = this.routePlanner.determineRoute(newTarget, currentRequest, context);
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug("Redirecting to '" + uri + "' via " + currentRoute);
|
||||
}
|
||||
EntityUtils.consume(response.getEntity());
|
||||
response.close();
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
} catch (final RuntimeException ex) {
|
||||
response.close();
|
||||
throw ex;
|
||||
} catch (final IOException ex) {
|
||||
response.close();
|
||||
throw ex;
|
||||
} catch (final HttpException ex) {
|
||||
// Protocol exception related to a direct.
|
||||
// The underlying connection may still be salvaged.
|
||||
try {
|
||||
EntityUtils.consume(response.getEntity());
|
||||
} catch (final IOException ioex) {
|
||||
this.log.debug("I/O error while releasing connection", ioex);
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,55 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
import ch.boye.httpclientandroidlib.annotation.Immutable;
|
||||
|
||||
/**
|
||||
* Signals that the request has been aborted.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@Immutable
|
||||
public class RequestAbortedException extends InterruptedIOException {
|
||||
|
||||
private static final long serialVersionUID = 4973849966012490112L;
|
||||
|
||||
public RequestAbortedException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public RequestAbortedException(final String message, final Throwable cause) {
|
||||
super(message);
|
||||
if (cause != null) {
|
||||
initCause(cause);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,137 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import ch.boye.httpclientandroidlib.Header;
|
||||
import ch.boye.httpclientandroidlib.HttpEntity;
|
||||
import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest;
|
||||
import ch.boye.httpclientandroidlib.HttpRequest;
|
||||
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
|
||||
|
||||
/**
|
||||
* A Proxy class for {@link ch.boye.httpclientandroidlib.HttpEntity} enclosed in a request message.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@NotThreadSafe
|
||||
class RequestEntityProxy implements HttpEntity {
|
||||
|
||||
static void enhance(final HttpEntityEnclosingRequest request) {
|
||||
final HttpEntity entity = request.getEntity();
|
||||
if (entity != null && !entity.isRepeatable() && !isEnhanced(entity)) {
|
||||
request.setEntity(new RequestEntityProxy(entity));
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isEnhanced(final HttpEntity entity) {
|
||||
return entity instanceof RequestEntityProxy;
|
||||
}
|
||||
|
||||
static boolean isRepeatable(final HttpRequest request) {
|
||||
if (request instanceof HttpEntityEnclosingRequest) {
|
||||
final HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
|
||||
if (entity != null) {
|
||||
if (isEnhanced(entity)) {
|
||||
final RequestEntityProxy proxy = (RequestEntityProxy) entity;
|
||||
if (!proxy.isConsumed()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return entity.isRepeatable();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private final HttpEntity original;
|
||||
private boolean consumed = false;
|
||||
|
||||
RequestEntityProxy(final HttpEntity original) {
|
||||
super();
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
public HttpEntity getOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
public boolean isConsumed() {
|
||||
return consumed;
|
||||
}
|
||||
|
||||
public boolean isRepeatable() {
|
||||
return original.isRepeatable();
|
||||
}
|
||||
|
||||
public boolean isChunked() {
|
||||
return original.isChunked();
|
||||
}
|
||||
|
||||
public long getContentLength() {
|
||||
return original.getContentLength();
|
||||
}
|
||||
|
||||
public Header getContentType() {
|
||||
return original.getContentType();
|
||||
}
|
||||
|
||||
public Header getContentEncoding() {
|
||||
return original.getContentEncoding();
|
||||
}
|
||||
|
||||
public InputStream getContent() throws IOException, IllegalStateException {
|
||||
return original.getContent();
|
||||
}
|
||||
|
||||
public void writeTo(final OutputStream outstream) throws IOException {
|
||||
consumed = true;
|
||||
original.writeTo(outstream);
|
||||
}
|
||||
|
||||
public boolean isStreaming() {
|
||||
return original.isStreaming();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void consumeContent() throws IOException {
|
||||
consumed = true;
|
||||
original.consumeContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("RequestEntityProxy{");
|
||||
sb.append(original);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,152 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.SocketException;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpEntity;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
|
||||
import ch.boye.httpclientandroidlib.conn.EofSensorInputStream;
|
||||
import ch.boye.httpclientandroidlib.conn.EofSensorWatcher;
|
||||
import ch.boye.httpclientandroidlib.entity.HttpEntityWrapper;
|
||||
|
||||
/**
|
||||
* A wrapper class for {@link HttpEntity} enclosed in a response message.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@NotThreadSafe
|
||||
class ResponseEntityProxy extends HttpEntityWrapper implements EofSensorWatcher {
|
||||
|
||||
private final ConnectionHolder connHolder;
|
||||
|
||||
public static void enchance(final HttpResponse response, final ConnectionHolder connHolder) {
|
||||
final HttpEntity entity = response.getEntity();
|
||||
if (entity != null && entity.isStreaming() && connHolder != null) {
|
||||
response.setEntity(new ResponseEntityProxy(entity, connHolder));
|
||||
}
|
||||
}
|
||||
|
||||
ResponseEntityProxy(final HttpEntity entity, final ConnectionHolder connHolder) {
|
||||
super(entity);
|
||||
this.connHolder = connHolder;
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
if (this.connHolder != null) {
|
||||
this.connHolder.abortConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public void releaseConnection() throws IOException {
|
||||
if (this.connHolder != null) {
|
||||
try {
|
||||
if (this.connHolder.isReusable()) {
|
||||
this.connHolder.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 {
|
||||
try {
|
||||
this.wrappedEntity.writeTo(outstream);
|
||||
releaseConnection();
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
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(final InputStream wrapped) throws IOException {
|
||||
try {
|
||||
final boolean open = connHolder != null && !connHolder.isReleased();
|
||||
// this assumes that closing the stream will
|
||||
// consume the remainder of the response body:
|
||||
try {
|
||||
wrapped.close();
|
||||
releaseConnection();
|
||||
} catch (final SocketException ex) {
|
||||
if (open) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean streamAbort(final InputStream wrapped) throws IOException {
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("ResponseEntityProxy{");
|
||||
sb.append(wrappedEntity);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,126 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
|
||||
/* LogFactory removed by HttpClient for Android script. */
|
||||
import ch.boye.httpclientandroidlib.Header;
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.NoHttpResponseException;
|
||||
import ch.boye.httpclientandroidlib.annotation.Immutable;
|
||||
import ch.boye.httpclientandroidlib.client.HttpRequestRetryHandler;
|
||||
import ch.boye.httpclientandroidlib.client.NonRepeatableRequestException;
|
||||
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
|
||||
import ch.boye.httpclientandroidlib.util.Args;
|
||||
|
||||
/**
|
||||
* Request executor in the request execution chain that is responsible
|
||||
* for making a decision whether a request failed due to an I/O error
|
||||
* should be re-executed.
|
||||
* <p/>
|
||||
* Further responsibilities such as communication with the opposite
|
||||
* endpoint is delegated to the next executor in the request execution
|
||||
* chain.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@Immutable
|
||||
public class RetryExec implements ClientExecChain {
|
||||
|
||||
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
|
||||
|
||||
private final ClientExecChain requestExecutor;
|
||||
private final HttpRequestRetryHandler retryHandler;
|
||||
|
||||
public RetryExec(
|
||||
final ClientExecChain requestExecutor,
|
||||
final HttpRequestRetryHandler retryHandler) {
|
||||
Args.notNull(requestExecutor, "HTTP request executor");
|
||||
Args.notNull(retryHandler, "HTTP request retry handler");
|
||||
this.requestExecutor = requestExecutor;
|
||||
this.retryHandler = retryHandler;
|
||||
}
|
||||
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpClientContext context,
|
||||
final HttpExecutionAware execAware) throws IOException, HttpException {
|
||||
Args.notNull(route, "HTTP route");
|
||||
Args.notNull(request, "HTTP request");
|
||||
Args.notNull(context, "HTTP context");
|
||||
final Header[] origheaders = request.getAllHeaders();
|
||||
for (int execCount = 1;; execCount++) {
|
||||
try {
|
||||
return this.requestExecutor.execute(route, request, context, execAware);
|
||||
} catch (final IOException ex) {
|
||||
if (execAware != null && execAware.isAborted()) {
|
||||
this.log.debug("Request has been aborted");
|
||||
throw ex;
|
||||
}
|
||||
if (retryHandler.retryRequest(ex, execCount, context)) {
|
||||
if (this.log.isInfoEnabled()) {
|
||||
this.log.info("I/O exception ("+ ex.getClass().getName() +
|
||||
") caught when processing request to "
|
||||
+ route +
|
||||
": "
|
||||
+ ex.getMessage());
|
||||
}
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug(ex.getMessage(), ex);
|
||||
}
|
||||
if (!RequestEntityProxy.isRepeatable(request)) {
|
||||
this.log.debug("Cannot retry non-repeatable request");
|
||||
throw new NonRepeatableRequestException("Cannot retry request " +
|
||||
"with a non-repeatable request entity", ex);
|
||||
}
|
||||
request.setHeaders(origheaders);
|
||||
if (this.log.isInfoEnabled()) {
|
||||
this.log.info("Retrying request to " + route);
|
||||
}
|
||||
} else {
|
||||
if (ex instanceof NoHttpResponseException) {
|
||||
final NoHttpResponseException updatedex = new NoHttpResponseException(
|
||||
route.getTargetHost().toHostString() + " failed to respond");
|
||||
updatedex.setStackTrace(ex.getStackTrace());
|
||||
throw updatedex;
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,108 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
|
||||
import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog;
|
||||
/* LogFactory removed by HttpClient for Android script. */
|
||||
import ch.boye.httpclientandroidlib.Header;
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.annotation.Immutable;
|
||||
import ch.boye.httpclientandroidlib.client.ServiceUnavailableRetryStrategy;
|
||||
import ch.boye.httpclientandroidlib.client.methods.CloseableHttpResponse;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpExecutionAware;
|
||||
import ch.boye.httpclientandroidlib.client.methods.HttpRequestWrapper;
|
||||
import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext;
|
||||
import ch.boye.httpclientandroidlib.conn.routing.HttpRoute;
|
||||
import ch.boye.httpclientandroidlib.util.Args;
|
||||
|
||||
/**
|
||||
* Request executor in the request execution chain that is responsible
|
||||
* for making a decision whether a request that received a non-2xx response
|
||||
* from the target server should be re-executed.
|
||||
* <p/>
|
||||
* Further responsibilities such as communication with the opposite
|
||||
* endpoint is delegated to the next executor in the request execution
|
||||
* chain.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
@Immutable
|
||||
public class ServiceUnavailableRetryExec implements ClientExecChain {
|
||||
|
||||
public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass());
|
||||
|
||||
private final ClientExecChain requestExecutor;
|
||||
private final ServiceUnavailableRetryStrategy retryStrategy;
|
||||
|
||||
public ServiceUnavailableRetryExec(
|
||||
final ClientExecChain requestExecutor,
|
||||
final ServiceUnavailableRetryStrategy retryStrategy) {
|
||||
super();
|
||||
Args.notNull(requestExecutor, "HTTP request executor");
|
||||
Args.notNull(retryStrategy, "Retry strategy");
|
||||
this.requestExecutor = requestExecutor;
|
||||
this.retryStrategy = retryStrategy;
|
||||
}
|
||||
|
||||
public CloseableHttpResponse execute(
|
||||
final HttpRoute route,
|
||||
final HttpRequestWrapper request,
|
||||
final HttpClientContext context,
|
||||
final HttpExecutionAware execAware) throws IOException, HttpException {
|
||||
final Header[] origheaders = request.getAllHeaders();
|
||||
for (int c = 1;; c++) {
|
||||
final CloseableHttpResponse response = this.requestExecutor.execute(
|
||||
route, request, context, execAware);
|
||||
try {
|
||||
if (this.retryStrategy.retryRequest(response, c, context)) {
|
||||
response.close();
|
||||
final long nextInterval = this.retryStrategy.getRetryInterval();
|
||||
if (nextInterval > 0) {
|
||||
try {
|
||||
this.log.trace("Wait for " + nextInterval);
|
||||
Thread.sleep(nextInterval);
|
||||
} catch (final InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new InterruptedIOException();
|
||||
}
|
||||
}
|
||||
request.setHeaders(origheaders);
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
} catch (final RuntimeException ex) {
|
||||
response.close();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,55 +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 ch.boye.httpclientandroidlib.impl.execchain;
|
||||
|
||||
import ch.boye.httpclientandroidlib.HttpException;
|
||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||
import ch.boye.httpclientandroidlib.annotation.Immutable;
|
||||
|
||||
/**
|
||||
* Signals that the tunnel request was rejected by the proxy host.
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
@Immutable
|
||||
public class TunnelRefusedException extends HttpException {
|
||||
|
||||
private static final long serialVersionUID = -8646722842745617323L;
|
||||
|
||||
private final HttpResponse response;
|
||||
|
||||
public TunnelRefusedException(final String message, final HttpResponse response) {
|
||||
super(message);
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public HttpResponse getResponse() {
|
||||
return this.response;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,31 +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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* HTTP request execution chain APIs.
|
||||
*/
|
||||
package ch.boye.httpclientandroidlib.impl.execchain;
|
||||
Loading…
Add table
Add a link
Reference in a new issue