diff options
Diffstat (limited to 'mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn')
51 files changed, 9636 insertions, 0 deletions
diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractClientConnAdapter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractClientConnAdapter.java new file mode 100644 index 000000000..ea2524f6a --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractClientConnAdapter.java @@ -0,0 +1,369 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Abstract adapter from {@link OperatedClientConnection operated} to + * {@link ManagedClientConnection managed} client connections. + * Read and write methods are delegated to the wrapped connection. + * Operations affecting the connection state have to be implemented + * by derived classes. Operations for querying the connection state + * are delegated to the wrapped connection if there is one, or + * return a default value if there is none. + * <p> + * This adapter tracks the checkpoints for reusable communication states, + * as indicated by {@link #markReusable markReusable} and queried by + * {@link #isMarkedReusable isMarkedReusable}. + * All send and receive operations will automatically clear the mark. + * <p> + * Connection release calls are delegated to the connection manager, + * if there is one. {@link #abortConnection abortConnection} will + * clear the reusability mark first. The connection manager is + * expected to tolerate multiple calls to the release method. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +@NotThreadSafe +public abstract class AbstractClientConnAdapter implements ManagedClientConnection, HttpContext { + + /** + * The connection manager. + */ + private final ClientConnectionManager connManager; + + /** The wrapped connection. */ + private volatile OperatedClientConnection wrappedConnection; + + /** The reusability marker. */ + private volatile boolean markedReusable; + + /** True if the connection has been shut down or released. */ + private volatile boolean released; + + /** The duration this is valid for while idle (in ms). */ + private volatile long duration; + + /** + * Creates a new connection adapter. + * The adapter is initially <i>not</i> + * {@link #isMarkedReusable marked} as reusable. + * + * @param mgr the connection manager, or <code>null</code> + * @param conn the connection to wrap, or <code>null</code> + */ + protected AbstractClientConnAdapter(final ClientConnectionManager mgr, + final OperatedClientConnection conn) { + super(); + connManager = mgr; + wrappedConnection = conn; + markedReusable = false; + released = false; + duration = Long.MAX_VALUE; + } + + /** + * Detaches this adapter from the wrapped connection. + * This adapter becomes useless. + */ + protected synchronized void detach() { + wrappedConnection = null; + duration = Long.MAX_VALUE; + } + + protected OperatedClientConnection getWrappedConnection() { + return wrappedConnection; + } + + protected ClientConnectionManager getManager() { + return connManager; + } + + /** + * @deprecated (4.1) use {@link #assertValid(OperatedClientConnection)} + */ + @Deprecated + protected final void assertNotAborted() throws InterruptedIOException { + if (isReleased()) { + throw new InterruptedIOException("Connection has been shut down"); + } + } + + /** + * @since 4.1 + * @return value of released flag + */ + protected boolean isReleased() { + return released; + } + + /** + * Asserts that there is a valid wrapped connection to delegate to. + * + * @throws ConnectionShutdownException if there is no wrapped connection + * or connection has been aborted + */ + protected final void assertValid( + final OperatedClientConnection wrappedConn) throws ConnectionShutdownException { + if (isReleased() || wrappedConn == null) { + throw new ConnectionShutdownException(); + } + } + + public boolean isOpen() { + final OperatedClientConnection conn = getWrappedConnection(); + if (conn == null) { + return false; + } + + return conn.isOpen(); + } + + public boolean isStale() { + if (isReleased()) { + return true; + } + final OperatedClientConnection conn = getWrappedConnection(); + if (conn == null) { + return true; + } + + return conn.isStale(); + } + + public void setSocketTimeout(final int timeout) { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + conn.setSocketTimeout(timeout); + } + + public int getSocketTimeout() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getSocketTimeout(); + } + + public HttpConnectionMetrics getMetrics() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getMetrics(); + } + + public void flush() throws IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + conn.flush(); + } + + public boolean isResponseAvailable(final int timeout) throws IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.isResponseAvailable(timeout); + } + + public void receiveResponseEntity(final HttpResponse response) + throws HttpException, IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + unmarkReusable(); + conn.receiveResponseEntity(response); + } + + public HttpResponse receiveResponseHeader() + throws HttpException, IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + unmarkReusable(); + return conn.receiveResponseHeader(); + } + + public void sendRequestEntity(final HttpEntityEnclosingRequest request) + throws HttpException, IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + unmarkReusable(); + conn.sendRequestEntity(request); + } + + public void sendRequestHeader(final HttpRequest request) + throws HttpException, IOException { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + unmarkReusable(); + conn.sendRequestHeader(request); + } + + public InetAddress getLocalAddress() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getLocalAddress(); + } + + public int getLocalPort() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getLocalPort(); + } + + public InetAddress getRemoteAddress() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getRemoteAddress(); + } + + public int getRemotePort() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.getRemotePort(); + } + + public boolean isSecure() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + return conn.isSecure(); + } + + public void bind(final Socket socket) throws IOException { + throw new UnsupportedOperationException(); + } + + public Socket getSocket() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (!isOpen()) { + return null; + } + return conn.getSocket(); + } + + public SSLSession getSSLSession() { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (!isOpen()) { + return null; + } + + SSLSession result = null; + final Socket sock = conn.getSocket(); + if (sock instanceof SSLSocket) { + result = ((SSLSocket)sock).getSession(); + } + return result; + } + + public void markReusable() { + markedReusable = true; + } + + public void unmarkReusable() { + markedReusable = false; + } + + public boolean isMarkedReusable() { + return markedReusable; + } + + public void setIdleDuration(final long duration, final TimeUnit unit) { + if(duration > 0) { + this.duration = unit.toMillis(duration); + } else { + this.duration = -1; + } + } + + public synchronized void releaseConnection() { + if (released) { + return; + } + released = true; + connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS); + } + + public synchronized void abortConnection() { + if (released) { + return; + } + released = true; + unmarkReusable(); + try { + shutdown(); + } catch (final IOException ignore) { + } + connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS); + } + + public Object getAttribute(final String id) { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).getAttribute(id); + } else { + return null; + } + } + + public Object removeAttribute(final String id) { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).removeAttribute(id); + } else { + return null; + } + } + + public void setAttribute(final String id, final Object obj) { + final OperatedClientConnection conn = getWrappedConnection(); + assertValid(conn); + if (conn instanceof HttpContext) { + ((HttpContext) conn).setAttribute(id, obj); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPoolEntry.java new file mode 100644 index 000000000..cbbe727b5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPoolEntry.java @@ -0,0 +1,262 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * A pool entry for use by connection manager implementations. + * Pool entries work in conjunction with an + * {@link AbstractClientConnAdapter adapter}. + * The adapter is handed out to applications that obtain a connection. + * The pool entry stores the underlying connection and tracks the + * {@link HttpRoute route} established. + * The adapter delegates methods for establishing the route to + * its pool entry. + * <p> + * If the managed connections is released or revoked, the adapter + * gets disconnected, but the pool entry still contains the + * underlying connection and the established route. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public abstract class AbstractPoolEntry { + + /** The connection operator. */ + protected final ClientConnectionOperator connOperator; + + /** The underlying connection being pooled or used. */ + protected final OperatedClientConnection connection; + + /** The route for which this entry gets allocated. */ + //@@@ currently accessed from connection manager(s) as attribute + //@@@ avoid that, derived classes should decide whether update is allowed + //@@@ SCCM: yes, TSCCM: no + protected volatile HttpRoute route; + + /** Connection state object */ + protected volatile Object state; + + /** The tracked route, or <code>null</code> before tracking starts. */ + protected volatile RouteTracker tracker; + + + /** + * Creates a new pool entry. + * + * @param connOperator the Connection Operator for this entry + * @param route the planned route for the connection, + * or <code>null</code> + */ + protected AbstractPoolEntry(final ClientConnectionOperator connOperator, + final HttpRoute route) { + super(); + Args.notNull(connOperator, "Connection operator"); + this.connOperator = connOperator; + this.connection = connOperator.createConnection(); + this.route = route; + this.tracker = null; + } + + /** + * Returns the state object associated with this pool entry. + * + * @return The state object + */ + public Object getState() { + return state; + } + + /** + * Assigns a state object to this pool entry. + * + * @param state The state object + */ + public void setState(final Object state) { + this.state = state; + } + + /** + * Opens the underlying connection. + * + * @param route the route along which to open the connection + * @param context the context for opening the connection + * @param params the parameters for opening the connection + * + * @throws IOException in case of a problem + */ + public void open(final HttpRoute route, + final HttpContext context, final HttpParams params) + throws IOException { + + Args.notNull(route, "Route"); + Args.notNull(params, "HTTP parameters"); + if (this.tracker != null) { + Asserts.check(!this.tracker.isConnected(), "Connection already open"); + } + // - collect the arguments + // - call the operator + // - update the tracking data + // In this order, we can be sure that only a successful + // opening of the connection will be tracked. + + this.tracker = new RouteTracker(route); + final HttpHost proxy = route.getProxyHost(); + + connOperator.openConnection + (this.connection, + (proxy != null) ? proxy : route.getTargetHost(), + route.getLocalAddress(), + context, params); + + final RouteTracker localTracker = tracker; // capture volatile + + // If this tracker was reset while connecting, + // fail early. + if (localTracker == null) { + throw new InterruptedIOException("Request aborted"); + } + + if (proxy == null) { + localTracker.connectTarget(this.connection.isSecure()); + } else { + localTracker.connectProxy(proxy, this.connection.isSecure()); + } + + } + + /** + * Tracks tunnelling of the connection to the target. + * The tunnel has to be established outside by sending a CONNECT + * request to the (last) proxy. + * + * @param secure <code>true</code> if the tunnel should be + * considered secure, <code>false</code> otherwise + * @param params the parameters for tunnelling the connection + * + * @throws IOException in case of a problem + */ + public void tunnelTarget(final boolean secure, final HttpParams params) + throws IOException { + + Args.notNull(params, "HTTP parameters"); + Asserts.notNull(this.tracker, "Route tracker"); + Asserts.check(this.tracker.isConnected(), "Connection not open"); + Asserts.check(!this.tracker.isTunnelled(), "Connection is already tunnelled"); + + this.connection.update(null, tracker.getTargetHost(), + secure, params); + this.tracker.tunnelTarget(secure); + } + + /** + * Tracks tunnelling of the connection to a chained proxy. + * The tunnel has to be established outside by sending a CONNECT + * request to the previous proxy. + * + * @param next the proxy to which the tunnel was established. + * See {@link ch.boye.httpclientandroidlib.conn.ManagedClientConnection#tunnelProxy + * ManagedClientConnection.tunnelProxy} + * for details. + * @param secure <code>true</code> if the tunnel should be + * considered secure, <code>false</code> otherwise + * @param params the parameters for tunnelling the connection + * + * @throws IOException in case of a problem + */ + public void tunnelProxy(final HttpHost next, final boolean secure, final HttpParams params) + throws IOException { + + Args.notNull(next, "Next proxy"); + Args.notNull(params, "Parameters"); + + Asserts.notNull(this.tracker, "Route tracker"); + Asserts.check(this.tracker.isConnected(), "Connection not open"); + + this.connection.update(null, next, secure, params); + this.tracker.tunnelProxy(next, secure); + } + + /** + * Layers a protocol on top of an established tunnel. + * + * @param context the context for layering + * @param params the parameters for layering + * + * @throws IOException in case of a problem + */ + public void layerProtocol(final HttpContext context, final HttpParams params) + throws IOException { + + //@@@ is context allowed to be null? depends on operator? + Args.notNull(params, "HTTP parameters"); + Asserts.notNull(this.tracker, "Route tracker"); + Asserts.check(this.tracker.isConnected(), "Connection not open"); + Asserts.check(this.tracker.isTunnelled(), "Protocol layering without a tunnel not supported"); + Asserts.check(!this.tracker.isLayered(), "Multiple protocol layering not supported"); + // - collect the arguments + // - call the operator + // - update the tracking data + // In this order, we can be sure that only a successful + // layering on top of the connection will be tracked. + + final HttpHost target = tracker.getTargetHost(); + + connOperator.updateSecureConnection(this.connection, target, + context, params); + + this.tracker.layerProtocol(this.connection.isSecure()); + + } + + /** + * Shuts down the entry. + * + * If {@link #open(HttpRoute, HttpContext, HttpParams)} is in progress, + * this will cause that open to possibly throw an {@link IOException}. + */ + protected void shutdownEntry() { + tracker = null; + state = null; + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPooledConnAdapter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPooledConnAdapter.java new file mode 100644 index 000000000..47f0feda5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/AbstractPooledConnAdapter.java @@ -0,0 +1,191 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Abstract adapter from pool {@link AbstractPoolEntry entries} to + * {@link ch.boye.httpclientandroidlib.conn.ManagedClientConnection managed} + * client connections. + * The connection in the pool entry is used to initialize the base class. + * In addition, methods to establish a route are delegated to the + * pool entry. {@link #shutdown shutdown} and {@link #close close} + * will clear the tracked route in the pool entry and call the + * respective method of the wrapped connection. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public abstract class AbstractPooledConnAdapter extends AbstractClientConnAdapter { + + /** The wrapped pool entry. */ + protected volatile AbstractPoolEntry poolEntry; + + /** + * Creates a new connection adapter. + * + * @param manager the connection manager + * @param entry the pool entry for the connection being wrapped + */ + protected AbstractPooledConnAdapter(final ClientConnectionManager manager, + final AbstractPoolEntry entry) { + super(manager, entry.connection); + this.poolEntry = entry; + } + + public String getId() { + return null; + } + + /** + * Obtains the pool entry. + * + * @return the pool entry, or <code>null</code> if detached + * + * @deprecated (4.0.1) + */ + @Deprecated + protected AbstractPoolEntry getPoolEntry() { + return this.poolEntry; + } + + /** + * Asserts that there is a valid pool entry. + * + * @throws ConnectionShutdownException if there is no pool entry + * or connection has been aborted + * + * @see #assertValid(OperatedClientConnection) + */ + protected void assertValid(final AbstractPoolEntry entry) { + if (isReleased() || entry == null) { + throw new ConnectionShutdownException(); + } + } + + /** + * @deprecated (4.1) use {@link #assertValid(AbstractPoolEntry)} + */ + @Deprecated + protected final void assertAttached() { + if (poolEntry == null) { + throw new ConnectionShutdownException(); + } + } + + /** + * Detaches this adapter from the wrapped connection. + * This adapter becomes useless. + */ + @Override + protected synchronized void detach() { + poolEntry = null; + super.detach(); + } + + public HttpRoute getRoute() { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + return (entry.tracker == null) ? null : entry.tracker.toRoute(); + } + + public void open(final HttpRoute route, + final HttpContext context, final HttpParams params) + throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.open(route, context, params); + } + + public void tunnelTarget(final boolean secure, final HttpParams params) + throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.tunnelTarget(secure, params); + } + + public void tunnelProxy(final HttpHost next, final boolean secure, final HttpParams params) + throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.tunnelProxy(next, secure, params); + } + + public void layerProtocol(final HttpContext context, final HttpParams params) + throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.layerProtocol(context, params); + } + + public void close() throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + if (entry != null) { + entry.shutdownEntry(); + } + + final OperatedClientConnection conn = getWrappedConnection(); + if (conn != null) { + conn.close(); + } + } + + public void shutdown() throws IOException { + final AbstractPoolEntry entry = getPoolEntry(); + if (entry != null) { + entry.shutdownEntry(); + } + + final OperatedClientConnection conn = getWrappedConnection(); + if (conn != null) { + conn.shutdown(); + } + } + + public Object getState() { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + return entry.getState(); + } + + public void setState(final Object state) { + final AbstractPoolEntry entry = getPoolEntry(); + assertValid(entry); + entry.setState(state); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicClientConnectionManager.java new file mode 100644 index 000000000..176e28150 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicClientConnectionManager.java @@ -0,0 +1,276 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * A connection manager for a single connection. This connection manager maintains only one active + * connection. Even though this class is fully thread-safe it ought to be used by one execution + * thread only, as only one thread a time can lease the connection at a time. + * <p/> + * This connection manager will make an effort to reuse the connection for subsequent requests + * with the same {@link HttpRoute route}. It will, however, close the existing connection and + * open it for the given route, if the route of the persistent connection does not match that + * of the connection request. If the connection has been already been allocated + * {@link IllegalStateException} is thrown. + * <p/> + * This connection manager implementation should be used inside an EJB container instead of + * {@link PoolingClientConnectionManager}. + * + * @since 4.2 + * + * @deprecated (4.3) use {@link BasicHttpClientConnectionManager}. + */ +@ThreadSafe +@Deprecated +public class BasicClientConnectionManager implements ClientConnectionManager { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private static final AtomicLong COUNTER = new AtomicLong(); + + /** The message to be logged on multiple allocation. */ + public final static String MISUSE_MESSAGE = + "Invalid use of BasicClientConnManager: connection still allocated.\n" + + "Make sure to release the connection before allocating another one."; + + /** The schemes supported by this connection manager. */ + private final SchemeRegistry schemeRegistry; + + /** The operator for opening and updating connections. */ + private final ClientConnectionOperator connOperator; + + /** The one and only entry in this pool. */ + @GuardedBy("this") + private HttpPoolEntry poolEntry; + + /** The currently issued managed connection, if any. */ + @GuardedBy("this") + private ManagedClientConnectionImpl conn; + + /** Indicates whether this connection manager is shut down. */ + @GuardedBy("this") + private volatile boolean shutdown; + + /** + * Creates a new simple connection manager. + * + * @param schreg the scheme registry + */ + public BasicClientConnectionManager(final SchemeRegistry schreg) { + Args.notNull(schreg, "Scheme registry"); + this.schemeRegistry = schreg; + this.connOperator = createConnectionOperator(schreg); + } + + public BasicClientConnectionManager() { + this(SchemeRegistryFactory.createDefault()); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { // Make sure we call overridden method even if shutdown barfs + super.finalize(); + } + } + + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) { + return new DefaultClientConnectionOperator(schreg); + } + + public final ClientConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + + return new ClientConnectionRequest() { + + public void abortRequest() { + // Nothing to abort, since requests are immediate. + } + + public ManagedClientConnection getConnection( + final long timeout, final TimeUnit tunit) { + return BasicClientConnectionManager.this.getConnection( + route, state); + } + + }; + } + + private void assertNotShutdown() { + Asserts.check(!this.shutdown, "Connection manager has been shut down"); + } + + ManagedClientConnection getConnection(final HttpRoute route, final Object state) { + Args.notNull(route, "Route"); + synchronized (this) { + assertNotShutdown(); + if (this.log.isDebugEnabled()) { + this.log.debug("Get connection for route " + route); + } + Asserts.check(this.conn == null, MISUSE_MESSAGE); + if (this.poolEntry != null && !this.poolEntry.getPlannedRoute().equals(route)) { + this.poolEntry.close(); + this.poolEntry = null; + } + if (this.poolEntry == null) { + final String id = Long.toString(COUNTER.getAndIncrement()); + final OperatedClientConnection conn = this.connOperator.createConnection(); + this.poolEntry = new HttpPoolEntry(this.log, id, route, conn, 0, TimeUnit.MILLISECONDS); + } + final long now = System.currentTimeMillis(); + if (this.poolEntry.isExpired(now)) { + this.poolEntry.close(); + this.poolEntry.getTracker().reset(); + } + this.conn = new ManagedClientConnectionImpl(this, this.connOperator, this.poolEntry); + return this.conn; + } + } + + private void shutdownConnection(final HttpClientConnection conn) { + try { + conn.shutdown(); + } catch (final IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception shutting down connection", iox); + } + } + } + + public void releaseConnection(final ManagedClientConnection conn, final long keepalive, final TimeUnit tunit) { + Args.check(conn instanceof ManagedClientConnectionImpl, "Connection class mismatch, " + + "connection not obtained from this manager"); + final ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn; + synchronized (managedConn) { + if (this.log.isDebugEnabled()) { + this.log.debug("Releasing connection " + conn); + } + if (managedConn.getPoolEntry() == null) { + return; // already released + } + final ClientConnectionManager manager = managedConn.getManager(); + Asserts.check(manager == this, "Connection not obtained from this manager"); + synchronized (this) { + if (this.shutdown) { + shutdownConnection(managedConn); + return; + } + try { + if (managedConn.isOpen() && !managedConn.isMarkedReusable()) { + shutdownConnection(managedConn); + } + if (managedConn.isMarkedReusable()) { + this.poolEntry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); + if (this.log.isDebugEnabled()) { + final String s; + if (keepalive > 0) { + s = "for " + keepalive + " " + tunit; + } else { + s = "indefinitely"; + } + this.log.debug("Connection can be kept alive " + s); + } + } + } finally { + managedConn.detach(); + this.conn = null; + if (this.poolEntry.isClosed()) { + this.poolEntry = null; + } + } + } + } + } + + public void closeExpiredConnections() { + synchronized (this) { + assertNotShutdown(); + final long now = System.currentTimeMillis(); + if (this.poolEntry != null && this.poolEntry.isExpired(now)) { + this.poolEntry.close(); + this.poolEntry.getTracker().reset(); + } + } + } + + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + Args.notNull(tunit, "Time unit"); + synchronized (this) { + assertNotShutdown(); + long time = tunit.toMillis(idletime); + if (time < 0) { + time = 0; + } + final long deadline = System.currentTimeMillis() - time; + if (this.poolEntry != null && this.poolEntry.getUpdated() <= deadline) { + this.poolEntry.close(); + this.poolEntry.getTracker().reset(); + } + } + } + + public void shutdown() { + synchronized (this) { + this.shutdown = true; + try { + if (this.poolEntry != null) { + this.poolEntry.close(); + } + } finally { + this.poolEntry = null; + this.conn = null; + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicHttpClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicHttpClientConnectionManager.java new file mode 100644 index 000000000..1049a4151 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/BasicHttpClientConnectionManager.java @@ -0,0 +1,370 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Date; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.config.Registry; +import ch.boye.httpclientandroidlib.config.RegistryBuilder; +import ch.boye.httpclientandroidlib.config.SocketConfig; +import ch.boye.httpclientandroidlib.conn.ConnectionRequest; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.PlainConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.ssl.SSLConnectionSocketFactory; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.LangUtils; + +/** + * A connection manager for a single connection. This connection manager maintains only one active + * connection. Even though this class is fully thread-safe it ought to be used by one execution + * thread only, as only one thread a time can lease the connection at a time. + * <p/> + * This connection manager will make an effort to reuse the connection for subsequent requests + * with the same {@link HttpRoute route}. It will, however, close the existing connection and + * open it for the given route, if the route of the persistent connection does not match that + * of the connection request. If the connection has been already been allocated + * {@link IllegalStateException} is thrown. + * <p/> + * This connection manager implementation should be used inside an EJB container instead of + * {@link PoolingHttpClientConnectionManager}. + * + * @since 4.3 + */ +@ThreadSafe +public class BasicHttpClientConnectionManager implements HttpClientConnectionManager, Closeable { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final HttpClientConnectionOperator connectionOperator; + private final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory; + + @GuardedBy("this") + private ManagedHttpClientConnection conn; + + @GuardedBy("this") + private HttpRoute route; + + @GuardedBy("this") + private Object state; + + @GuardedBy("this") + private long updated; + + @GuardedBy("this") + private long expiry; + + @GuardedBy("this") + private boolean leased; + + @GuardedBy("this") + private SocketConfig socketConfig; + + @GuardedBy("this") + private ConnectionConfig connConfig; + + private final AtomicBoolean isShutdown; + + private static Registry<ConnectionSocketFactory> getDefaultRegistry() { + return RegistryBuilder.<ConnectionSocketFactory>create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", SSLConnectionSocketFactory.getSocketFactory()) + .build(); + } + + public BasicHttpClientConnectionManager( + final Lookup<ConnectionSocketFactory> socketFactoryRegistry, + final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory, + final SchemePortResolver schemePortResolver, + final DnsResolver dnsResolver) { + super(); + this.connectionOperator = new HttpClientConnectionOperator( + socketFactoryRegistry, schemePortResolver, dnsResolver); + this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE; + this.expiry = Long.MAX_VALUE; + this.socketConfig = SocketConfig.DEFAULT; + this.connConfig = ConnectionConfig.DEFAULT; + this.isShutdown = new AtomicBoolean(false); + } + + public BasicHttpClientConnectionManager( + final Lookup<ConnectionSocketFactory> socketFactoryRegistry, + final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory) { + this(socketFactoryRegistry, connFactory, null, null); + } + + public BasicHttpClientConnectionManager( + final Lookup<ConnectionSocketFactory> socketFactoryRegistry) { + this(socketFactoryRegistry, null, null, null); + } + + public BasicHttpClientConnectionManager() { + this(getDefaultRegistry(), null, null, null); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { // Make sure we call overridden method even if shutdown barfs + super.finalize(); + } + } + + public void close() { + shutdown(); + } + + HttpRoute getRoute() { + return route; + } + + Object getState() { + return state; + } + + public synchronized SocketConfig getSocketConfig() { + return socketConfig; + } + + public synchronized void setSocketConfig(final SocketConfig socketConfig) { + this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT; + } + + public synchronized ConnectionConfig getConnectionConfig() { + return connConfig; + } + + public synchronized void setConnectionConfig(final ConnectionConfig connConfig) { + this.connConfig = connConfig != null ? connConfig : ConnectionConfig.DEFAULT; + } + + public final ConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + Args.notNull(route, "Route"); + return new ConnectionRequest() { + + public boolean cancel() { + // Nothing to abort, since requests are immediate. + return false; + } + + public HttpClientConnection get(final long timeout, final TimeUnit tunit) { + return BasicHttpClientConnectionManager.this.getConnection( + route, state); + } + + }; + } + + private void closeConnection() { + if (this.conn != null) { + this.log.debug("Closing connection"); + try { + this.conn.close(); + } catch (final IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception closing connection", iox); + } + } + this.conn = null; + } + } + + private void shutdownConnection() { + if (this.conn != null) { + this.log.debug("Shutting down connection"); + try { + this.conn.shutdown(); + } catch (final IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception shutting down connection", iox); + } + } + this.conn = null; + } + } + + private void checkExpiry() { + if (this.conn != null && System.currentTimeMillis() >= this.expiry) { + if (this.log.isDebugEnabled()) { + this.log.debug("Connection expired @ " + new Date(this.expiry)); + } + closeConnection(); + } + } + + synchronized HttpClientConnection getConnection(final HttpRoute route, final Object state) { + Asserts.check(!this.isShutdown.get(), "Connection manager has been shut down"); + if (this.log.isDebugEnabled()) { + this.log.debug("Get connection for route " + route); + } + Asserts.check(!this.leased, "Connection is still allocated"); + if (!LangUtils.equals(this.route, route) || !LangUtils.equals(this.state, state)) { + closeConnection(); + } + this.route = route; + this.state = state; + checkExpiry(); + if (this.conn == null) { + this.conn = this.connFactory.create(route, this.connConfig); + } + this.leased = true; + return this.conn; + } + + public synchronized void releaseConnection( + final HttpClientConnection conn, + final Object state, + final long keepalive, final TimeUnit tunit) { + Args.notNull(conn, "Connection"); + Asserts.check(conn == this.conn, "Connection not obtained from this manager"); + if (this.log.isDebugEnabled()) { + this.log.debug("Releasing connection " + conn); + } + if (this.isShutdown.get()) { + return; + } + try { + this.updated = System.currentTimeMillis(); + if (!this.conn.isOpen()) { + this.conn = null; + this.route = null; + this.conn = null; + this.expiry = Long.MAX_VALUE; + } else { + this.state = state; + if (this.log.isDebugEnabled()) { + final String s; + if (keepalive > 0) { + s = "for " + keepalive + " " + tunit; + } else { + s = "indefinitely"; + } + this.log.debug("Connection can be kept alive " + s); + } + if (keepalive > 0) { + this.expiry = this.updated + tunit.toMillis(keepalive); + } else { + this.expiry = Long.MAX_VALUE; + } + } + } finally { + this.leased = false; + } + } + + public void connect( + final HttpClientConnection conn, + final HttpRoute route, + final int connectTimeout, + final HttpContext context) throws IOException { + Args.notNull(conn, "Connection"); + Args.notNull(route, "HTTP route"); + Asserts.check(conn == this.conn, "Connection not obtained from this manager"); + final HttpHost host; + if (route.getProxyHost() != null) { + host = route.getProxyHost(); + } else { + host = route.getTargetHost(); + } + final InetSocketAddress localAddress = route.getLocalSocketAddress(); + this.connectionOperator.connect(this.conn, host, localAddress, + connectTimeout, this.socketConfig, context); + } + + public void upgrade( + final HttpClientConnection conn, + final HttpRoute route, + final HttpContext context) throws IOException { + Args.notNull(conn, "Connection"); + Args.notNull(route, "HTTP route"); + Asserts.check(conn == this.conn, "Connection not obtained from this manager"); + this.connectionOperator.upgrade(this.conn, route.getTargetHost(), context); + } + + public void routeComplete( + final HttpClientConnection conn, + final HttpRoute route, + final HttpContext context) throws IOException { + } + + public synchronized void closeExpiredConnections() { + if (this.isShutdown.get()) { + return; + } + if (!this.leased) { + checkExpiry(); + } + } + + public synchronized void closeIdleConnections(final long idletime, final TimeUnit tunit) { + Args.notNull(tunit, "Time unit"); + if (this.isShutdown.get()) { + return; + } + if (!this.leased) { + long time = tunit.toMillis(idletime); + if (time < 0) { + time = 0; + } + final long deadline = System.currentTimeMillis() - time; + if (this.updated <= deadline) { + closeConnection(); + } + } + } + + public synchronized void shutdown() { + if (this.isShutdown.compareAndSet(false, true)) { + shutdownConnection(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPool.java new file mode 100644 index 000000000..040960c71 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPool.java @@ -0,0 +1,67 @@ +/* + * ==================================================================== + * 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.conn; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.pool.AbstractConnPool; +import ch.boye.httpclientandroidlib.pool.ConnFactory; + +/** + * @since 4.3 + */ +@ThreadSafe +class CPool extends AbstractConnPool<HttpRoute, ManagedHttpClientConnection, CPoolEntry> { + + private static final AtomicLong COUNTER = new AtomicLong(); + + public HttpClientAndroidLog log = new HttpClientAndroidLog(CPool.class); + private final long timeToLive; + private final TimeUnit tunit; + + public CPool( + final ConnFactory<HttpRoute, ManagedHttpClientConnection> connFactory, + final int defaultMaxPerRoute, final int maxTotal, + final long timeToLive, final TimeUnit tunit) { + super(connFactory, defaultMaxPerRoute, maxTotal); + this.timeToLive = timeToLive; + this.tunit = tunit; + } + + @Override + protected CPoolEntry createEntry(final HttpRoute route, final ManagedHttpClientConnection conn) { + final String id = Long.toString(COUNTER.getAndIncrement()); + return new CPoolEntry(this.log, id, route, conn, this.timeToLive, this.tunit); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolEntry.java new file mode 100644 index 000000000..f5ecfdc5e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolEntry.java @@ -0,0 +1,101 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.util.Date; +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.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.pool.PoolEntry; + +/** + * @since 4.3 + */ +@ThreadSafe +class CPoolEntry extends PoolEntry<HttpRoute, ManagedHttpClientConnection> { + + public HttpClientAndroidLog log; + private volatile boolean routeComplete; + + public CPoolEntry( + final HttpClientAndroidLog log, + final String id, + final HttpRoute route, + final ManagedHttpClientConnection conn, + final long timeToLive, final TimeUnit tunit) { + super(id, route, conn, timeToLive, tunit); + this.log = log; + } + + public void markRouteComplete() { + this.routeComplete = true; + } + + public boolean isRouteComplete() { + return this.routeComplete; + } + + public void closeConnection() throws IOException { + final HttpClientConnection conn = getConnection(); + conn.close(); + } + + public void shutdownConnection() throws IOException { + final HttpClientConnection conn = getConnection(); + conn.shutdown(); + } + + @Override + public boolean isExpired(final long now) { + final boolean expired = super.isExpired(now); + if (expired && this.log.isDebugEnabled()) { + this.log.debug("Connection " + this + " expired @ " + new Date(getExpiry())); + } + return expired; + } + + @Override + public boolean isClosed() { + final HttpClientConnection conn = getConnection(); + return !conn.isOpen(); + } + + @Override + public void close() { + try { + closeConnection(); + } catch (final IOException ex) { + this.log.debug("I/O error closing connection", ex); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolProxy.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolProxy.java new file mode 100644 index 000000000..9ac67a10d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/CPoolProxy.java @@ -0,0 +1,245 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +import javax.net.ssl.SSLSession; + +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * @since 4.3 + */ +@NotThreadSafe +class CPoolProxy implements ManagedHttpClientConnection, HttpContext { + + private volatile CPoolEntry poolEntry; + + CPoolProxy(final CPoolEntry entry) { + super(); + this.poolEntry = entry; + } + + CPoolEntry getPoolEntry() { + return this.poolEntry; + } + + CPoolEntry detach() { + final CPoolEntry local = this.poolEntry; + this.poolEntry = null; + return local; + } + + ManagedHttpClientConnection getConnection() { + final CPoolEntry local = this.poolEntry; + if (local == null) { + return null; + } + return local.getConnection(); + } + + ManagedHttpClientConnection getValidConnection() { + final ManagedHttpClientConnection conn = getConnection(); + if (conn == null) { + throw new ConnectionShutdownException(); + } + return conn; + } + + public void close() throws IOException { + final CPoolEntry local = this.poolEntry; + if (local != null) { + local.closeConnection(); + } + } + + public void shutdown() throws IOException { + final CPoolEntry local = this.poolEntry; + if (local != null) { + local.shutdownConnection(); + } + } + + public boolean isOpen() { + final CPoolEntry local = this.poolEntry; + if (local != null) { + return !local.isClosed(); + } else { + return false; + } + } + + public boolean isStale() { + final HttpClientConnection conn = getConnection(); + if (conn != null) { + return conn.isStale(); + } else { + return true; + } + } + + public void setSocketTimeout(final int timeout) { + getValidConnection().setSocketTimeout(timeout); + } + + public int getSocketTimeout() { + return getValidConnection().getSocketTimeout(); + } + + public String getId() { + return getValidConnection().getId(); + } + + public void bind(final Socket socket) throws IOException { + getValidConnection().bind(socket); + } + + public Socket getSocket() { + return getValidConnection().getSocket(); + } + + public SSLSession getSSLSession() { + return getValidConnection().getSSLSession(); + } + + public boolean isResponseAvailable(final int timeout) throws IOException { + return getValidConnection().isResponseAvailable(timeout); + } + + public void sendRequestHeader(final HttpRequest request) throws HttpException, IOException { + getValidConnection().sendRequestHeader(request); + } + + public void sendRequestEntity(final HttpEntityEnclosingRequest request) throws HttpException, IOException { + getValidConnection().sendRequestEntity(request); + } + + public HttpResponse receiveResponseHeader() throws HttpException, IOException { + return getValidConnection().receiveResponseHeader(); + } + + public void receiveResponseEntity(final HttpResponse response) throws HttpException, IOException { + getValidConnection().receiveResponseEntity(response); + } + + public void flush() throws IOException { + getValidConnection().flush(); + } + + public HttpConnectionMetrics getMetrics() { + return getValidConnection().getMetrics(); + } + + public InetAddress getLocalAddress() { + return getValidConnection().getLocalAddress(); + } + + public int getLocalPort() { + return getValidConnection().getLocalPort(); + } + + public InetAddress getRemoteAddress() { + return getValidConnection().getRemoteAddress(); + } + + public int getRemotePort() { + return getValidConnection().getRemotePort(); + } + + public Object getAttribute(final String id) { + final ManagedHttpClientConnection conn = getValidConnection(); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).getAttribute(id); + } else { + return null; + } + } + + public void setAttribute(final String id, final Object obj) { + final ManagedHttpClientConnection conn = getValidConnection(); + if (conn instanceof HttpContext) { + ((HttpContext) conn).setAttribute(id, obj); + } + } + + public Object removeAttribute(final String id) { + final ManagedHttpClientConnection conn = getValidConnection(); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).removeAttribute(id); + } else { + return null; + } + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("CPoolProxy{"); + final ManagedHttpClientConnection conn = getConnection(); + if (conn != null) { + sb.append(conn); + } else { + sb.append("detached"); + } + sb.append('}'); + return sb.toString(); + } + + public static HttpClientConnection newProxy(final CPoolEntry poolEntry) { + return new CPoolProxy(poolEntry); + } + + private static CPoolProxy getProxy(final HttpClientConnection conn) { + if (!CPoolProxy.class.isInstance(conn)) { + throw new IllegalStateException("Unexpected connection proxy class: " + conn.getClass()); + } + return CPoolProxy.class.cast(conn); + } + + public static CPoolEntry getPoolEntry(final HttpClientConnection proxy) { + final CPoolEntry entry = getProxy(proxy).getPoolEntry(); + if (entry == null) { + throw new ConnectionShutdownException(); + } + return entry; + } + + public static CPoolEntry detach(final HttpClientConnection conn) { + return getProxy(conn).detach(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ConnectionShutdownException.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ConnectionShutdownException.java new file mode 100644 index 000000000..03820a38f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ConnectionShutdownException.java @@ -0,0 +1,50 @@ +/* + * ==================================================================== + * 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.conn; + +import ch.boye.httpclientandroidlib.annotation.Immutable; + +/** + * Signals that the connection has been shut down or released back to the + * the connection pool + * + * @since 4.1 + */ +@Immutable +public class ConnectionShutdownException extends IllegalStateException { + + private static final long serialVersionUID = 5868657401162844497L; + + /** + * Creates a new ConnectionShutdownException with a <tt>null</tt> detail message. + */ + public ConnectionShutdownException() { + super(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnection.java new file mode 100644 index 000000000..f9003b144 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnection.java @@ -0,0 +1,292 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +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.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.impl.SocketHttpClientConnection; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.params.BasicHttpParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.params.HttpProtocolParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of an operated client connection. + * + * @since 4.0 + * + * @deprecated (4.3) use {@link ManagedHttpClientConnectionFactory}. + */ +@NotThreadSafe // connSecure, targetHost +@Deprecated +public class DefaultClientConnection extends SocketHttpClientConnection + implements OperatedClientConnection, ManagedHttpClientConnection, HttpContext { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + public HttpClientAndroidLog headerLog = new HttpClientAndroidLog("ch.boye.httpclientandroidlib.headers"); + public HttpClientAndroidLog wireLog = new HttpClientAndroidLog("ch.boye.httpclientandroidlib.wire"); + + /** The unconnected socket */ + private volatile Socket socket; + + /** The target host of this connection. */ + private HttpHost targetHost; + + /** Whether this connection is secure. */ + private boolean connSecure; + + /** True if this connection was shutdown. */ + private volatile boolean shutdown; + + /** connection specific attributes */ + private final Map<String, Object> attributes; + + public DefaultClientConnection() { + super(); + this.attributes = new HashMap<String, Object>(); + } + + public String getId() { + return null; + } + + public final HttpHost getTargetHost() { + return this.targetHost; + } + + public final boolean isSecure() { + return this.connSecure; + } + + @Override + public final Socket getSocket() { + return this.socket; + } + + public SSLSession getSSLSession() { + if (this.socket instanceof SSLSocket) { + return ((SSLSocket) this.socket).getSession(); + } else { + return null; + } + } + + public void opening(final Socket sock, final HttpHost target) throws IOException { + assertNotOpen(); + this.socket = sock; + this.targetHost = target; + + // Check for shutdown after assigning socket, so that + if (this.shutdown) { + sock.close(); // allow this to throw... + // ...but if it doesn't, explicitly throw one ourselves. + throw new InterruptedIOException("Connection already shutdown"); + } + } + + public void openCompleted(final boolean secure, final HttpParams params) throws IOException { + Args.notNull(params, "Parameters"); + assertNotOpen(); + this.connSecure = secure; + bind(this.socket, params); + } + + /** + * Force-closes this connection. + * If the connection is still in the process of being open (the method + * {@link #opening opening} was already called but + * {@link #openCompleted openCompleted} was not), the associated + * socket that is being connected to a remote address will be closed. + * That will interrupt a thread that is blocked on connecting + * the socket. + * If the connection is not yet open, this will prevent the connection + * from being opened. + * + * @throws IOException in case of a problem + */ + @Override + public void shutdown() throws IOException { + shutdown = true; + try { + super.shutdown(); + if (log.isDebugEnabled()) { + log.debug("Connection " + this + " shut down"); + } + final Socket sock = this.socket; // copy volatile attribute + if (sock != null) { + sock.close(); + } + } catch (final IOException ex) { + log.debug("I/O error shutting down connection", ex); + } + } + + @Override + public void close() throws IOException { + try { + super.close(); + if (log.isDebugEnabled()) { + log.debug("Connection " + this + " closed"); + } + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + + @Override + protected SessionInputBuffer createSessionInputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + SessionInputBuffer inbuffer = super.createSessionInputBuffer( + socket, + buffersize > 0 ? buffersize : 8192, + params); + if (wireLog.isDebugEnabled()) { + inbuffer = new LoggingSessionInputBuffer( + inbuffer, + new Wire(wireLog), + HttpProtocolParams.getHttpElementCharset(params)); + } + return inbuffer; + } + + @Override + protected SessionOutputBuffer createSessionOutputBuffer( + final Socket socket, + final int buffersize, + final HttpParams params) throws IOException { + SessionOutputBuffer outbuffer = super.createSessionOutputBuffer( + socket, + buffersize > 0 ? buffersize : 8192, + params); + if (wireLog.isDebugEnabled()) { + outbuffer = new LoggingSessionOutputBuffer( + outbuffer, + new Wire(wireLog), + HttpProtocolParams.getHttpElementCharset(params)); + } + return outbuffer; + } + + @Override + protected HttpMessageParser<HttpResponse> createResponseParser( + final SessionInputBuffer buffer, + final HttpResponseFactory responseFactory, + final HttpParams params) { + // override in derived class to specify a line parser + return new DefaultHttpResponseParser + (buffer, null, responseFactory, params); + } + + public void bind(final Socket socket) throws IOException { + bind(socket, new BasicHttpParams()); + } + + public void update(final Socket sock, final HttpHost target, + final boolean secure, final HttpParams params) + throws IOException { + + assertOpen(); + Args.notNull(target, "Target host"); + Args.notNull(params, "Parameters"); + + if (sock != null) { + this.socket = sock; + bind(sock, params); + } + targetHost = target; + connSecure = secure; + } + + @Override + public HttpResponse receiveResponseHeader() throws HttpException, IOException { + final HttpResponse response = super.receiveResponseHeader(); + if (log.isDebugEnabled()) { + log.debug("Receiving response: " + response.getStatusLine()); + } + if (headerLog.isDebugEnabled()) { + headerLog.debug("<< " + response.getStatusLine().toString()); + final Header[] headers = response.getAllHeaders(); + for (final Header header : headers) { + headerLog.debug("<< " + header.toString()); + } + } + return response; + } + + @Override + public void sendRequestHeader(final HttpRequest request) throws HttpException, IOException { + if (log.isDebugEnabled()) { + log.debug("Sending request: " + request.getRequestLine()); + } + super.sendRequestHeader(request); + if (headerLog.isDebugEnabled()) { + headerLog.debug(">> " + request.getRequestLine().toString()); + final Header[] headers = request.getAllHeaders(); + for (final Header header : headers) { + headerLog.debug(">> " + header.toString()); + } + } + } + + public Object getAttribute(final String id) { + return this.attributes.get(id); + } + + public Object removeAttribute(final String id) { + return this.attributes.remove(id); + } + + public void setAttribute(final String id, final Object obj) { + this.attributes.put(id, obj); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnectionOperator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnectionOperator.java new file mode 100644 index 000000000..3cdf706ec --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultClientConnectionOperator.java @@ -0,0 +1,263 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.client.protocol.ClientContext; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.HttpInetSocketAddress; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeLayeredSocketFactory; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeSocketFactory; +import ch.boye.httpclientandroidlib.params.HttpConnectionParams; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Default implementation of a {@link ClientConnectionOperator}. It uses a {@link SchemeRegistry} + * to look up {@link SchemeSocketFactory} objects. + * <p> + * This connection operator is multihome network aware and will attempt to retry failed connects + * against all known IP addresses sequentially until the connect is successful or all known + * addresses fail to respond. Please note the same + * {@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT} value will be used + * for each connection attempt, so in the worst case the total elapsed time before timeout + * can be <code>CONNECTION_TIMEOUT * n</code> where <code>n</code> is the number of IP addresses + * of the given host. One can disable multihome support by overriding + * the {@link #resolveHostname(String)} method and returning only one IP address for the given + * host name. + * <p> + * The following parameters can be used to customize the behavior of this + * class: + * <ul> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_TIMEOUT}</li> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_LINGER}</li> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SO_REUSEADDR}</li> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#TCP_NODELAY}</li> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}</li> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#CONNECTION_TIMEOUT}</li> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li> + * </ul> + * + * @since 4.0 + * + * @deprecated (4.3) use {@link PoolingHttpClientConnectionManager}. + */ +@Deprecated +@ThreadSafe +public class DefaultClientConnectionOperator implements ClientConnectionOperator { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** The scheme registry for looking up socket factories. */ + protected final SchemeRegistry schemeRegistry; // @ThreadSafe + + /** the custom-configured DNS lookup mechanism. */ + protected final DnsResolver dnsResolver; + + /** + * Creates a new client connection operator for the given scheme registry. + * + * @param schemes the scheme registry + * + * @since 4.2 + */ + public DefaultClientConnectionOperator(final SchemeRegistry schemes) { + Args.notNull(schemes, "Scheme registry"); + this.schemeRegistry = schemes; + this.dnsResolver = new SystemDefaultDnsResolver(); + } + + /** + * Creates a new client connection operator for the given scheme registry + * and the given custom DNS lookup mechanism. + * + * @param schemes + * the scheme registry + * @param dnsResolver + * the custom DNS lookup mechanism + */ + public DefaultClientConnectionOperator(final SchemeRegistry schemes,final DnsResolver dnsResolver) { + Args.notNull(schemes, "Scheme registry"); + + Args.notNull(dnsResolver, "DNS resolver"); + + this.schemeRegistry = schemes; + this.dnsResolver = dnsResolver; + } + + public OperatedClientConnection createConnection() { + return new DefaultClientConnection(); + } + + private SchemeRegistry getSchemeRegistry(final HttpContext context) { + SchemeRegistry reg = (SchemeRegistry) context.getAttribute( + ClientContext.SCHEME_REGISTRY); + if (reg == null) { + reg = this.schemeRegistry; + } + return reg; + } + + public void openConnection( + final OperatedClientConnection conn, + final HttpHost target, + final InetAddress local, + final HttpContext context, + final HttpParams params) throws IOException { + Args.notNull(conn, "Connection"); + Args.notNull(target, "Target host"); + Args.notNull(params, "HTTP parameters"); + Asserts.check(!conn.isOpen(), "Connection must not be open"); + + final SchemeRegistry registry = getSchemeRegistry(context); + final Scheme schm = registry.getScheme(target.getSchemeName()); + final SchemeSocketFactory sf = schm.getSchemeSocketFactory(); + + final InetAddress[] addresses = resolveHostname(target.getHostName()); + final int port = schm.resolvePort(target.getPort()); + for (int i = 0; i < addresses.length; i++) { + final InetAddress address = addresses[i]; + final boolean last = i == addresses.length - 1; + + Socket sock = sf.createSocket(params); + conn.opening(sock, target); + + final InetSocketAddress remoteAddress = new HttpInetSocketAddress(target, address, port); + InetSocketAddress localAddress = null; + if (local != null) { + localAddress = new InetSocketAddress(local, 0); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connecting to " + remoteAddress); + } + try { + final Socket connsock = sf.connectSocket(sock, remoteAddress, localAddress, params); + if (sock != connsock) { + sock = connsock; + conn.opening(sock, target); + } + prepareSocket(sock, context, params); + conn.openCompleted(sf.isSecure(sock), params); + return; + } catch (final ConnectException ex) { + if (last) { + throw ex; + } + } catch (final ConnectTimeoutException ex) { + if (last) { + throw ex; + } + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connect to " + remoteAddress + " timed out. " + + "Connection will be retried using another IP address"); + } + } + } + + public void updateSecureConnection( + final OperatedClientConnection conn, + final HttpHost target, + final HttpContext context, + final HttpParams params) throws IOException { + Args.notNull(conn, "Connection"); + Args.notNull(target, "Target host"); + Args.notNull(params, "Parameters"); + Asserts.check(conn.isOpen(), "Connection must be open"); + + final SchemeRegistry registry = getSchemeRegistry(context); + final Scheme schm = registry.getScheme(target.getSchemeName()); + Asserts.check(schm.getSchemeSocketFactory() instanceof SchemeLayeredSocketFactory, + "Socket factory must implement SchemeLayeredSocketFactory"); + final SchemeLayeredSocketFactory lsf = (SchemeLayeredSocketFactory) schm.getSchemeSocketFactory(); + final Socket sock = lsf.createLayeredSocket( + conn.getSocket(), target.getHostName(), schm.resolvePort(target.getPort()), params); + prepareSocket(sock, context, params); + conn.update(sock, target, lsf.isSecure(sock), params); + } + + /** + * Performs standard initializations on a newly created socket. + * + * @param sock the socket to prepare + * @param context the context for the connection + * @param params the parameters from which to prepare the socket + * + * @throws IOException in case of an IO problem + */ + protected void prepareSocket( + final Socket sock, + final HttpContext context, + final HttpParams params) throws IOException { + sock.setTcpNoDelay(HttpConnectionParams.getTcpNoDelay(params)); + sock.setSoTimeout(HttpConnectionParams.getSoTimeout(params)); + + final int linger = HttpConnectionParams.getLinger(params); + if (linger >= 0) { + sock.setSoLinger(linger > 0, linger); + } + } + + /** + * Resolves the given host name to an array of corresponding IP addresses, based on the + * configured name service on the provided DNS resolver. If one wasn't provided, the system + * configuration is used. + * + * @param host host name to resolve + * @return array of IP addresses + * @exception UnknownHostException if no IP address for the host could be determined. + * + * @see DnsResolver + * @see SystemDefaultDnsResolver + * + * @since 4.1 + */ + protected InetAddress[] resolveHostname(final String host) throws UnknownHostException { + return dnsResolver.resolve(host); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParser.java new file mode 100644 index 000000000..9f249e5f7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParser.java @@ -0,0 +1,168 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.NoHttpResponseException; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory; +import ch.boye.httpclientandroidlib.impl.io.AbstractMessageParser; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.LineParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Lenient HTTP response parser implementation that can skip malformed data until + * a valid HTTP response message head is encountered. + * + * @since 4.2 + */ +@SuppressWarnings("deprecation") +@NotThreadSafe +public class DefaultHttpResponseParser extends AbstractMessageParser<HttpResponse> { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final HttpResponseFactory responseFactory; + private final CharArrayBuffer lineBuf; + + /** + * @deprecated (4.3) use {@link DefaultHttpResponseParser#DefaultHttpResponseParser( + * SessionInputBuffer, LineParser, HttpResponseFactory, MessageConstraints)} + */ + @Deprecated + public DefaultHttpResponseParser( + final SessionInputBuffer buffer, + final LineParser parser, + final HttpResponseFactory responseFactory, + final HttpParams params) { + super(buffer, parser, params); + Args.notNull(responseFactory, "Response factory"); + this.responseFactory = responseFactory; + this.lineBuf = new CharArrayBuffer(128); + } + + /** + * Creates new instance of DefaultHttpResponseParser. + * + * @param buffer the session input buffer. + * @param lineParser the line parser. If <code>null</code> + * {@link ch.boye.httpclientandroidlib.message.BasicLineParser#INSTANCE} will be used. + * @param responseFactory HTTP response factory. If <code>null</code> + * {@link DefaultHttpResponseFactory#INSTANCE} will be used. + * @param constraints the message constraints. If <code>null</code> + * {@link MessageConstraints#DEFAULT} will be used. + * + * @since 4.3 + */ + public DefaultHttpResponseParser( + final SessionInputBuffer buffer, + final LineParser lineParser, + final HttpResponseFactory responseFactory, + final MessageConstraints constraints) { + super(buffer, lineParser, constraints); + this.responseFactory = responseFactory != null ? responseFactory : + DefaultHttpResponseFactory.INSTANCE; + this.lineBuf = new CharArrayBuffer(128); + } + + /** + * Creates new instance of DefaultHttpResponseParser. + * + * @param buffer the session input buffer. + * @param constraints the message constraints. If <code>null</code> + * {@link MessageConstraints#DEFAULT} will be used. + * + * @since 4.3 + */ + public DefaultHttpResponseParser( + final SessionInputBuffer buffer, final MessageConstraints constraints) { + this(buffer, null, null, constraints); + } + + /** + * Creates new instance of DefaultHttpResponseParser. + * + * @param buffer the session input buffer. + * + * @since 4.3 + */ + public DefaultHttpResponseParser(final SessionInputBuffer buffer) { + this(buffer, null, null, MessageConstraints.DEFAULT); + } + + @Override + protected HttpResponse parseHead( + final SessionInputBuffer sessionBuffer) throws IOException, HttpException { + //read out the HTTP status string + int count = 0; + ParserCursor cursor = null; + do { + // clear the buffer + this.lineBuf.clear(); + final int i = sessionBuffer.readLine(this.lineBuf); + if (i == -1 && count == 0) { + // The server just dropped connection on us + throw new NoHttpResponseException("The target server failed to respond"); + } + cursor = new ParserCursor(0, this.lineBuf.length()); + if (lineParser.hasProtocolVersion(this.lineBuf, cursor)) { + // Got one + break; + } else if (i == -1 || reject(this.lineBuf, count)) { + // Giving up + throw new ProtocolException("The server failed to respond with a " + + "valid HTTP response"); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Garbage in response: " + this.lineBuf.toString()); + } + count++; + } while(true); + //create the status line from the status string + final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor); + return this.responseFactory.newHttpResponse(statusline, null); + } + + protected boolean reject(final CharArrayBuffer line, final int count) { + return false; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParserFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParserFactory.java new file mode 100644 index 000000000..a229b2ca6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpResponseParserFactory.java @@ -0,0 +1,77 @@ +/* + * ==================================================================== + * 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.conn; + +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.impl.DefaultHttpResponseFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageParser; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.BasicLineParser; +import ch.boye.httpclientandroidlib.message.LineParser; + +/** + * Default factory for response message parsers. + * + * @since 4.3 + */ +@Immutable +public class DefaultHttpResponseParserFactory implements HttpMessageParserFactory<HttpResponse> { + + public static final DefaultHttpResponseParserFactory INSTANCE = new DefaultHttpResponseParserFactory(); + + private final LineParser lineParser; + private final HttpResponseFactory responseFactory; + + public DefaultHttpResponseParserFactory( + final LineParser lineParser, + final HttpResponseFactory responseFactory) { + super(); + this.lineParser = lineParser != null ? lineParser : BasicLineParser.INSTANCE; + this.responseFactory = responseFactory != null ? responseFactory + : DefaultHttpResponseFactory.INSTANCE; + } + + public DefaultHttpResponseParserFactory( + final HttpResponseFactory responseFactory) { + this(null, responseFactory); + } + + public DefaultHttpResponseParserFactory() { + this(null, null); + } + + public HttpMessageParser<HttpResponse> create(final SessionInputBuffer buffer, + final MessageConstraints constraints) { + return new DefaultHttpResponseParser(buffer, lineParser, responseFactory, constraints); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpRoutePlanner.java new file mode 100644 index 000000000..4b3525329 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultHttpRoutePlanner.java @@ -0,0 +1,123 @@ +/* + * ==================================================================== + * 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.conn; + + +import java.net.InetAddress; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.params.ConnRouteParams; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Default implementation of an {@link HttpRoutePlanner}. This implementation + * is based on {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames parameters}. + * It will not make use of any Java system properties, nor of system or + * browser proxy settings. + * <p> + * The following parameters can be used to customize the behavior of this + * class: + * <ul> + * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY}</li> + * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}</li> + * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}</li> + * </ul> + * + * @since 4.0 + * + * @deprecated (4.3) use {@link DefaultRoutePlanner} + */ +@ThreadSafe +@Deprecated +public class DefaultHttpRoutePlanner implements HttpRoutePlanner { + + /** The scheme registry. */ + protected final SchemeRegistry schemeRegistry; // class is @ThreadSafe + + /** + * Creates a new default route planner. + * + * @param schreg the scheme registry + */ + public DefaultHttpRoutePlanner(final SchemeRegistry schreg) { + Args.notNull(schreg, "Scheme registry"); + schemeRegistry = schreg; + } + + public HttpRoute determineRoute(final HttpHost target, + final HttpRequest request, + final HttpContext context) + throws HttpException { + + Args.notNull(request, "HTTP request"); + + // If we have a forced route, we can do without a target. + HttpRoute route = + ConnRouteParams.getForcedRoute(request.getParams()); + if (route != null) { + return route; + } + + // If we get here, there is no forced route. + // So we need a target to compute a route. + + Asserts.notNull(target, "Target host"); + + final InetAddress local = + ConnRouteParams.getLocalAddress(request.getParams()); + final HttpHost proxy = + ConnRouteParams.getDefaultProxy(request.getParams()); + + final Scheme schm; + try { + schm = this.schemeRegistry.getScheme(target.getSchemeName()); + } catch (final IllegalStateException ex) { + throw new HttpException(ex.getMessage()); + } + // as it is typically used for TLS/SSL, we assume that + // a layered scheme implies a secure connection + final boolean secure = schm.isLayered(); + + if (proxy == null) { + route = new HttpRoute(target, local, secure); + } else { + route = new HttpRoute(target, local, proxy, secure); + } + return route; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultManagedHttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultManagedHttpClientConnection.java new file mode 100644 index 000000000..c46b93405 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultManagedHttpClientConnection.java @@ -0,0 +1,135 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.Socket; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.impl.DefaultBHttpClientConnection; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * Default {@link ManagedHttpClientConnection} implementation. + * @since 4.3 + */ +@NotThreadSafe +public class DefaultManagedHttpClientConnection extends DefaultBHttpClientConnection + implements ManagedHttpClientConnection, HttpContext { + + private final String id; + private final Map<String, Object> attributes; + + private volatile boolean shutdown; + + public DefaultManagedHttpClientConnection( + final String id, + final int buffersize, + final int fragmentSizeHint, + final CharsetDecoder chardecoder, + final CharsetEncoder charencoder, + final MessageConstraints constraints, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy, + final HttpMessageWriterFactory<HttpRequest> requestWriterFactory, + final HttpMessageParserFactory<HttpResponse> responseParserFactory) { + super(buffersize, fragmentSizeHint, chardecoder, charencoder, + constraints, incomingContentStrategy, outgoingContentStrategy, + requestWriterFactory, responseParserFactory); + this.id = id; + this.attributes = new ConcurrentHashMap<String, Object>(); + } + + public DefaultManagedHttpClientConnection( + final String id, + final int buffersize) { + this(id, buffersize, buffersize, null, null, null, null, null, null, null); + } + + public String getId() { + return this.id; + } + + @Override + public void shutdown() throws IOException { + this.shutdown = true; + super.shutdown(); + } + + public Object getAttribute(final String id) { + return this.attributes.get(id); + } + + public Object removeAttribute(final String id) { + return this.attributes.remove(id); + } + + public void setAttribute(final String id, final Object obj) { + this.attributes.put(id, obj); + } + + @Override + public void bind(final Socket socket) throws IOException { + if (this.shutdown) { + socket.close(); // allow this to throw... + // ...but if it doesn't, explicitly throw one ourselves. + throw new InterruptedIOException("Connection already shutdown"); + } + super.bind(socket); + } + + @Override + public Socket getSocket() { + return super.getSocket(); + } + + public SSLSession getSSLSession() { + final Socket socket = super.getSocket(); + if (socket instanceof SSLSocket) { + return ((SSLSocket) socket).getSession(); + } else { + return null; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultProxyRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultProxyRoutePlanner.java new file mode 100644 index 000000000..fd569556c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultProxyRoutePlanner.java @@ -0,0 +1,66 @@ +/* + * ==================================================================== + * 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.conn; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Implementation of an {@link ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner} + * that routes requests through a default proxy. + * + * @since 4.3 + */ +@Immutable +public class DefaultProxyRoutePlanner extends DefaultRoutePlanner { + + private final HttpHost proxy; + + public DefaultProxyRoutePlanner(final HttpHost proxy, final SchemePortResolver schemePortResolver) { + super(schemePortResolver); + this.proxy = Args.notNull(proxy, "Proxy host"); + } + + public DefaultProxyRoutePlanner(final HttpHost proxy) { + this(proxy, null); + } + + @Override + protected HttpHost determineProxy( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws HttpException { + return proxy; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultResponseParser.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultResponseParser.java new file mode 100644 index 000000000..e82452ff5 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultResponseParser.java @@ -0,0 +1,125 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpMessage; +import ch.boye.httpclientandroidlib.HttpResponseFactory; +import ch.boye.httpclientandroidlib.NoHttpResponseException; +import ch.boye.httpclientandroidlib.ProtocolException; +import ch.boye.httpclientandroidlib.StatusLine; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.impl.io.AbstractMessageParser; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.message.LineParser; +import ch.boye.httpclientandroidlib.message.ParserCursor; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Default HTTP response parser implementation. + * <p> + * The following parameters can be used to customize the behavior of this + * class: + * <ul> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li> + * <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li> + * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnConnectionPNames#MAX_STATUS_LINE_GARBAGE}</li> + * </ul> + * + * @since 4.0 + * + * @deprecated (4.2) use {@link DefaultHttpResponseParser} + */ +@Deprecated +@ThreadSafe // no public methods +public class DefaultResponseParser extends AbstractMessageParser<HttpMessage> { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final HttpResponseFactory responseFactory; + private final CharArrayBuffer lineBuf; + private final int maxGarbageLines; + + public DefaultResponseParser( + final SessionInputBuffer buffer, + final LineParser parser, + final HttpResponseFactory responseFactory, + final HttpParams params) { + super(buffer, parser, params); + Args.notNull(responseFactory, "Response factory"); + this.responseFactory = responseFactory; + this.lineBuf = new CharArrayBuffer(128); + this.maxGarbageLines = getMaxGarbageLines(params); + } + + protected int getMaxGarbageLines(final HttpParams params) { + return params.getIntParameter( + ch.boye.httpclientandroidlib.conn.params.ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, + Integer.MAX_VALUE); + } + + @Override + protected HttpMessage parseHead( + final SessionInputBuffer sessionBuffer) throws IOException, HttpException { + //read out the HTTP status string + int count = 0; + ParserCursor cursor = null; + do { + // clear the buffer + this.lineBuf.clear(); + final int i = sessionBuffer.readLine(this.lineBuf); + if (i == -1 && count == 0) { + // The server just dropped connection on us + throw new NoHttpResponseException("The target server failed to respond"); + } + cursor = new ParserCursor(0, this.lineBuf.length()); + if (lineParser.hasProtocolVersion(this.lineBuf, cursor)) { + // Got one + break; + } else if (i == -1 || count >= this.maxGarbageLines) { + // Giving up + throw new ProtocolException("The server failed to respond with a " + + "valid HTTP response"); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Garbage in response: " + this.lineBuf.toString()); + } + count++; + } while(true); + //create the status line from the status string + final StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor); + return this.responseFactory.newHttpResponse(statusline, null); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultRoutePlanner.java new file mode 100644 index 000000000..2c08a7118 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultRoutePlanner.java @@ -0,0 +1,107 @@ +/* + * ==================================================================== + * 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.conn; + +import java.net.InetAddress; + +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.client.config.RequestConfig; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.UnsupportedSchemeException; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default implementation of an {@link HttpRoutePlanner}. It will not make use of + * any Java system properties, nor of system or browser proxy settings. + * + * @since 4.3 + */ +@Immutable +public class DefaultRoutePlanner implements HttpRoutePlanner { + + private final SchemePortResolver schemePortResolver; + + public DefaultRoutePlanner(final SchemePortResolver schemePortResolver) { + super(); + this.schemePortResolver = schemePortResolver != null ? schemePortResolver : + DefaultSchemePortResolver.INSTANCE; + } + + public HttpRoute determineRoute( + final HttpHost host, + final HttpRequest request, + final HttpContext context) throws HttpException { + Args.notNull(request, "Request"); + if (host == null) { + throw new ProtocolException("Target host is not specified"); + } + final HttpClientContext clientContext = HttpClientContext.adapt(context); + final RequestConfig config = clientContext.getRequestConfig(); + final InetAddress local = config.getLocalAddress(); + HttpHost proxy = config.getProxy(); + if (proxy == null) { + proxy = determineProxy(host, request, context); + } + + final HttpHost target; + if (host.getPort() <= 0) { + try { + target = new HttpHost( + host.getHostName(), + this.schemePortResolver.resolve(host), + host.getSchemeName()); + } catch (final UnsupportedSchemeException ex) { + throw new HttpException(ex.getMessage()); + } + } else { + target = host; + } + final boolean secure = target.getSchemeName().equalsIgnoreCase("https"); + if (proxy == null) { + return new HttpRoute(target, local, secure); + } else { + return new HttpRoute(target, local, proxy, secure); + } + } + + protected HttpHost determineProxy( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws HttpException { + return null; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultSchemePortResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultSchemePortResolver.java new file mode 100644 index 000000000..4b384f681 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/DefaultSchemePortResolver.java @@ -0,0 +1,61 @@ +/* + * ==================================================================== + * 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.conn; + +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.UnsupportedSchemeException; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Default {@link SchemePortResolver}. + * + * @since 4.3 + */ +@Immutable +public class DefaultSchemePortResolver implements SchemePortResolver { + + public static final DefaultSchemePortResolver INSTANCE = new DefaultSchemePortResolver(); + + public int resolve(final HttpHost host) throws UnsupportedSchemeException { + Args.notNull(host, "HTTP host"); + final int port = host.getPort(); + if (port > 0) { + return port; + } + final String name = host.getSchemeName(); + if (name.equalsIgnoreCase("http")) { + return 80; + } else if (name.equalsIgnoreCase("https")) { + return 443; + } else { + throw new UnsupportedSchemeException(name + " protocol is not supported"); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpClientConnectionOperator.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpClientConnectionOperator.java new file mode 100644 index 000000000..a13368237 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpClientConnectionOperator.java @@ -0,0 +1,173 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.net.ConnectException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.client.protocol.HttpClientContext; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.config.SocketConfig; +import ch.boye.httpclientandroidlib.conn.ConnectTimeoutException; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.HttpHostConnectException; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.UnsupportedSchemeException; +import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.LayeredConnectionSocketFactory; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; + +@Immutable +class HttpClientConnectionOperator { + + static final String SOCKET_FACTORY_REGISTRY = "http.socket-factory-registry"; + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final Lookup<ConnectionSocketFactory> socketFactoryRegistry; + private final SchemePortResolver schemePortResolver; + private final DnsResolver dnsResolver; + + HttpClientConnectionOperator( + final Lookup<ConnectionSocketFactory> socketFactoryRegistry, + final SchemePortResolver schemePortResolver, + final DnsResolver dnsResolver) { + super(); + Args.notNull(socketFactoryRegistry, "Socket factory registry"); + this.socketFactoryRegistry = socketFactoryRegistry; + this.schemePortResolver = schemePortResolver != null ? schemePortResolver : + DefaultSchemePortResolver.INSTANCE; + this.dnsResolver = dnsResolver != null ? dnsResolver : + SystemDefaultDnsResolver.INSTANCE; + } + + @SuppressWarnings("unchecked") + private Lookup<ConnectionSocketFactory> getSocketFactoryRegistry(final HttpContext context) { + Lookup<ConnectionSocketFactory> reg = (Lookup<ConnectionSocketFactory>) context.getAttribute( + SOCKET_FACTORY_REGISTRY); + if (reg == null) { + reg = this.socketFactoryRegistry; + } + return reg; + } + + public void connect( + final ManagedHttpClientConnection conn, + final HttpHost host, + final InetSocketAddress localAddress, + final int connectTimeout, + final SocketConfig socketConfig, + final HttpContext context) throws IOException { + final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(context); + final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); + if (sf == null) { + throw new UnsupportedSchemeException(host.getSchemeName() + + " protocol is not supported"); + } + final InetAddress[] addresses = this.dnsResolver.resolve(host.getHostName()); + final int port = this.schemePortResolver.resolve(host); + for (int i = 0; i < addresses.length; i++) { + final InetAddress address = addresses[i]; + final boolean last = i == addresses.length - 1; + + Socket sock = sf.createSocket(context); + sock.setSoTimeout(socketConfig.getSoTimeout()); + sock.setReuseAddress(socketConfig.isSoReuseAddress()); + sock.setTcpNoDelay(socketConfig.isTcpNoDelay()); + sock.setKeepAlive(socketConfig.isSoKeepAlive()); + final int linger = socketConfig.getSoLinger(); + if (linger >= 0) { + sock.setSoLinger(linger > 0, linger); + } + conn.bind(sock); + + final InetSocketAddress remoteAddress = new InetSocketAddress(address, port); + if (this.log.isDebugEnabled()) { + this.log.debug("Connecting to " + remoteAddress); + } + try { + sock = sf.connectSocket( + connectTimeout, sock, host, remoteAddress, localAddress, context); + conn.bind(sock); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection established " + conn); + } + return; + } catch (final SocketTimeoutException ex) { + if (last) { + throw new ConnectTimeoutException(ex, host, addresses); + } + } catch (final ConnectException ex) { + if (last) { + final String msg = ex.getMessage(); + if ("Connection timed out".equals(msg)) { + throw new ConnectTimeoutException(ex, host, addresses); + } else { + throw new HttpHostConnectException(ex, host, addresses); + } + } + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connect to " + remoteAddress + " timed out. " + + "Connection will be retried using another IP address"); + } + } + } + + public void upgrade( + final ManagedHttpClientConnection conn, + final HttpHost host, + final HttpContext context) throws IOException { + final HttpClientContext clientContext = HttpClientContext.adapt(context); + final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(clientContext); + final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName()); + if (sf == null) { + throw new UnsupportedSchemeException(host.getSchemeName() + + " protocol is not supported"); + } + if (!(sf instanceof LayeredConnectionSocketFactory)) { + throw new UnsupportedSchemeException(host.getSchemeName() + + " protocol does not support connection upgrade"); + } + final LayeredConnectionSocketFactory lsf = (LayeredConnectionSocketFactory) sf; + Socket sock = conn.getSocket(); + final int port = this.schemePortResolver.resolve(host); + sock = lsf.createLayeredSocket(sock, host.getHostName(), port, context); + conn.bind(sock); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpConnPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpConnPool.java new file mode 100644 index 000000000..e10bd6cf6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpConnPool.java @@ -0,0 +1,84 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.pool.AbstractConnPool; +import ch.boye.httpclientandroidlib.pool.ConnFactory; + +/** + * @since 4.2 + * + * @deprecated (4.3) no longer used. + */ +@Deprecated +class HttpConnPool extends AbstractConnPool<HttpRoute, OperatedClientConnection, HttpPoolEntry> { + + private static final AtomicLong COUNTER = new AtomicLong(); + + public HttpClientAndroidLog log; + private final long timeToLive; + private final TimeUnit tunit; + + public HttpConnPool(final HttpClientAndroidLog log, + final ClientConnectionOperator connOperator, + final int defaultMaxPerRoute, final int maxTotal, + final long timeToLive, final TimeUnit tunit) { + super(new InternalConnFactory(connOperator), defaultMaxPerRoute, maxTotal); + this.log = log; + this.timeToLive = timeToLive; + this.tunit = tunit; + } + + @Override + protected HttpPoolEntry createEntry(final HttpRoute route, final OperatedClientConnection conn) { + final String id = Long.toString(COUNTER.getAndIncrement()); + return new HttpPoolEntry(this.log, id, route, conn, this.timeToLive, this.tunit); + } + + static class InternalConnFactory implements ConnFactory<HttpRoute, OperatedClientConnection> { + + private final ClientConnectionOperator connOperator; + + InternalConnFactory(final ClientConnectionOperator connOperator) { + this.connOperator = connOperator; + } + + public OperatedClientConnection create(final HttpRoute route) throws IOException { + return connOperator.createConnection(); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpPoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpPoolEntry.java new file mode 100644 index 000000000..d04023126 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/HttpPoolEntry.java @@ -0,0 +1,98 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; +import ch.boye.httpclientandroidlib.pool.PoolEntry; + +/** + * @since 4.2 + * + * @deprecated (4.3) no longer used. + */ +@Deprecated +class HttpPoolEntry extends PoolEntry<HttpRoute, OperatedClientConnection> { + + public HttpClientAndroidLog log; + private final RouteTracker tracker; + + public HttpPoolEntry( + final HttpClientAndroidLog log, + final String id, + final HttpRoute route, + final OperatedClientConnection conn, + final long timeToLive, final TimeUnit tunit) { + super(id, route, conn, timeToLive, tunit); + this.log = log; + this.tracker = new RouteTracker(route); + } + + @Override + public boolean isExpired(final long now) { + final boolean expired = super.isExpired(now); + if (expired && this.log.isDebugEnabled()) { + this.log.debug("Connection " + this + " expired @ " + new Date(getExpiry())); + } + return expired; + } + + RouteTracker getTracker() { + return this.tracker; + } + + HttpRoute getPlannedRoute() { + return getRoute(); + } + + HttpRoute getEffectiveRoute() { + return this.tracker.toRoute(); + } + + @Override + public boolean isClosed() { + final OperatedClientConnection conn = getConnection(); + return !conn.isOpen(); + } + + @Override + public void close() { + final OperatedClientConnection conn = getConnection(); + try { + conn.close(); + } catch (final IOException ex) { + this.log.debug("I/O error closing connection", ex); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/IdleConnectionHandler.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/IdleConnectionHandler.java new file mode 100644 index 000000000..cbcd5d04f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/IdleConnectionHandler.java @@ -0,0 +1,181 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpConnection; + +// Currently only used by AbstractConnPool +/** + * A helper class for connection managers to track idle connections. + * + * <p>This class is not synchronized.</p> + * + * @see ch.boye.httpclientandroidlib.conn.ClientConnectionManager#closeIdleConnections + * + * @since 4.0 + * + * @deprecated (4.1) no longer used + */ +@Deprecated +public class IdleConnectionHandler { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** Holds connections and the time they were added. */ + private final Map<HttpConnection,TimeValues> connectionToTimes; + + + public IdleConnectionHandler() { + super(); + connectionToTimes = new HashMap<HttpConnection,TimeValues>(); + } + + /** + * Registers the given connection with this handler. The connection will be held until + * {@link #remove} or {@link #closeIdleConnections} is called. + * + * @param connection the connection to add + * + * @see #remove + */ + public void add(final HttpConnection connection, final long validDuration, final TimeUnit unit) { + + final long timeAdded = System.currentTimeMillis(); + + if (log.isDebugEnabled()) { + log.debug("Adding connection at: " + timeAdded); + } + + connectionToTimes.put(connection, new TimeValues(timeAdded, validDuration, unit)); + } + + /** + * Removes the given connection from the list of connections to be closed when idle. + * This will return true if the connection is still valid, and false + * if the connection should be considered expired and not used. + * + * @param connection + * @return True if the connection is still valid. + */ + public boolean remove(final HttpConnection connection) { + final TimeValues times = connectionToTimes.remove(connection); + if(times == null) { + log.warn("Removing a connection that never existed!"); + return true; + } else { + return System.currentTimeMillis() <= times.timeExpires; + } + } + + /** + * Removes all connections referenced by this handler. + */ + public void removeAll() { + this.connectionToTimes.clear(); + } + + /** + * Closes connections that have been idle for at least the given amount of time. + * + * @param idleTime the minimum idle time, in milliseconds, for connections to be closed + */ + public void closeIdleConnections(final long idleTime) { + + // the latest time for which connections will be closed + final long idleTimeout = System.currentTimeMillis() - idleTime; + + if (log.isDebugEnabled()) { + log.debug("Checking for connections, idle timeout: " + idleTimeout); + } + + for (final Entry<HttpConnection, TimeValues> entry : connectionToTimes.entrySet()) { + final HttpConnection conn = entry.getKey(); + final TimeValues times = entry.getValue(); + final long connectionTime = times.timeAdded; + if (connectionTime <= idleTimeout) { + if (log.isDebugEnabled()) { + log.debug("Closing idle connection, connection time: " + connectionTime); + } + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + } + } + + + public void closeExpiredConnections() { + final long now = System.currentTimeMillis(); + if (log.isDebugEnabled()) { + log.debug("Checking for expired connections, now: " + now); + } + + for (final Entry<HttpConnection, TimeValues> entry : connectionToTimes.entrySet()) { + final HttpConnection conn = entry.getKey(); + final TimeValues times = entry.getValue(); + if(times.timeExpires <= now) { + if (log.isDebugEnabled()) { + log.debug("Closing connection, expired @: " + times.timeExpires); + } + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + } + } + + private static class TimeValues { + private final long timeAdded; + private final long timeExpires; + + /** + * @param now The current time in milliseconds + * @param validDuration The duration this connection is valid for + * @param validUnit The unit of time the duration is specified in. + */ + TimeValues(final long now, final long validDuration, final TimeUnit validUnit) { + this.timeAdded = now; + if(validDuration > 0) { + this.timeExpires = now + validUnit.toMillis(validDuration); + } else { + this.timeExpires = Long.MAX_VALUE; + } + } + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/InMemoryDnsResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/InMemoryDnsResolver.java new file mode 100644 index 000000000..34f02b11b --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/InMemoryDnsResolver.java @@ -0,0 +1,94 @@ +/* + * ==================================================================== + * 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.conn; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * In-memory {@link DnsResolver} implementation. + * + * @since 4.2 + */ +public class InMemoryDnsResolver implements DnsResolver { + + /** Logger associated to this class. */ + public HttpClientAndroidLog log = new HttpClientAndroidLog(InMemoryDnsResolver.class); + + /** + * In-memory collection that will hold the associations between a host name + * and an array of InetAddress instances. + */ + private final Map<String, InetAddress[]> dnsMap; + + /** + * Builds a DNS resolver that will resolve the host names against a + * collection held in-memory. + */ + public InMemoryDnsResolver() { + dnsMap = new ConcurrentHashMap<String, InetAddress[]>(); + } + + /** + * Associates the given array of IP addresses to the given host in this DNS overrider. + * The IP addresses are assumed to be already resolved. + * + * @param host + * The host name to be associated with the given IP. + * @param ips + * array of IP addresses to be resolved by this DNS overrider to the given + * host name. + */ + public void add(final String host, final InetAddress... ips) { + Args.notNull(host, "Host name"); + Args.notNull(ips, "Array of IP addresses"); + dnsMap.put(host, ips); + } + + /** + * {@inheritDoc} + */ + public InetAddress[] resolve(final String host) throws UnknownHostException { + final InetAddress[] resolvedAddresses = dnsMap.get(host); + if (log.isInfoEnabled()) { + log.info("Resolving " + host + " to " + Arrays.deepToString(resolvedAddresses)); + } + if(resolvedAddresses == null){ + throw new UnknownHostException(host + " cannot be resolved"); + } + return resolvedAddresses; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingInputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingInputStream.java new file mode 100644 index 000000000..0c117395c --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingInputStream.java @@ -0,0 +1,145 @@ +/* + * ==================================================================== + * 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.conn; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Internal class. + * + * @since 4.3 + */ +@NotThreadSafe +class LoggingInputStream extends InputStream { + + private final InputStream in; + private final Wire wire; + + public LoggingInputStream(final InputStream in, final Wire wire) { + super(); + this.in = in; + this.wire = wire; + } + + @Override + public int read() throws IOException { + try { + final int b = in.read(); + if (b == -1) { + wire.input("end of stream"); + } else { + wire.input(b); + } + return b; + } catch (IOException ex) { + wire.input("[read] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public int read(final byte[] b) throws IOException { + try { + final int bytesRead = in.read(b); + if (bytesRead == -1) { + wire.input("end of stream"); + } else if (bytesRead > 0) { + wire.input(b, 0, bytesRead); + } + return bytesRead; + } catch (IOException ex) { + wire.input("[read] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + try { + final int bytesRead = in.read(b, off, len); + if (bytesRead == -1) { + wire.input("end of stream"); + } else if (bytesRead > 0) { + wire.input(b, off, bytesRead); + } + return bytesRead; + } catch (IOException ex) { + wire.input("[read] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public long skip(final long n) throws IOException { + try { + return super.skip(n); + } catch (IOException ex) { + wire.input("[skip] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public int available() throws IOException { + try { + return in.available(); + } catch (IOException ex) { + wire.input("[available] I/O error : " + ex.getMessage()); + throw ex; + } + } + + @Override + public void mark(final int readlimit) { + super.mark(readlimit); + } + + @Override + public void reset() throws IOException { + super.reset(); + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public void close() throws IOException { + try { + in.close(); + } catch (IOException ex) { + wire.input("[close] I/O error: " + ex.getMessage()); + throw ex; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingManagedHttpClientConnection.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingManagedHttpClientConnection.java new file mode 100644 index 000000000..5b06179c0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingManagedHttpClientConnection.java @@ -0,0 +1,132 @@ +/* + * ==================================================================== + * 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.conn; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.Header; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.config.MessageConstraints; +import ch.boye.httpclientandroidlib.entity.ContentLengthStrategy; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +@NotThreadSafe +class LoggingManagedHttpClientConnection extends DefaultManagedHttpClientConnection { + + public HttpClientAndroidLog log; + private final HttpClientAndroidLog headerlog; + private final Wire wire; + + public LoggingManagedHttpClientConnection( + final String id, + final HttpClientAndroidLog log, + final HttpClientAndroidLog headerlog, + final HttpClientAndroidLog wirelog, + final int buffersize, + final int fragmentSizeHint, + final CharsetDecoder chardecoder, + final CharsetEncoder charencoder, + final MessageConstraints constraints, + final ContentLengthStrategy incomingContentStrategy, + final ContentLengthStrategy outgoingContentStrategy, + final HttpMessageWriterFactory<HttpRequest> requestWriterFactory, + final HttpMessageParserFactory<HttpResponse> responseParserFactory) { + super(id, buffersize, fragmentSizeHint, chardecoder, charencoder, + constraints, incomingContentStrategy, outgoingContentStrategy, + requestWriterFactory, responseParserFactory); + this.log = log; + this.headerlog = headerlog; + this.wire = new Wire(wirelog, id); + } + + @Override + public void close() throws IOException { + if (this.log.isDebugEnabled()) { + this.log.debug(getId() + ": Close connection"); + } + super.close(); + } + + @Override + public void shutdown() throws IOException { + if (this.log.isDebugEnabled()) { + this.log.debug(getId() + ": Shutdown connection"); + } + super.shutdown(); + } + + @Override + protected InputStream getSocketInputStream(final Socket socket) throws IOException { + InputStream in = super.getSocketInputStream(socket); + if (this.wire.enabled()) { + in = new LoggingInputStream(in, this.wire); + } + return in; + } + + @Override + protected OutputStream getSocketOutputStream(final Socket socket) throws IOException { + OutputStream out = super.getSocketOutputStream(socket); + if (this.wire.enabled()) { + out = new LoggingOutputStream(out, this.wire); + } + return out; + } + + @Override + protected void onResponseReceived(final HttpResponse response) { + if (response != null && this.headerlog.isDebugEnabled()) { + this.headerlog.debug(getId() + " << " + response.getStatusLine().toString()); + final Header[] headers = response.getAllHeaders(); + for (final Header header : headers) { + this.headerlog.debug(getId() + " << " + header.toString()); + } + } + } + + @Override + protected void onRequestSubmitted(final HttpRequest request) { + if (request != null && this.headerlog.isDebugEnabled()) { + this.headerlog.debug(getId() + " >> " + request.getRequestLine().toString()); + final Header[] headers = request.getAllHeaders(); + for (final Header header : headers) { + this.headerlog.debug(getId() + " >> " + header.toString()); + } + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingOutputStream.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingOutputStream.java new file mode 100644 index 000000000..86c47b502 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingOutputStream.java @@ -0,0 +1,104 @@ +/* + * ==================================================================== + * 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.conn; + +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Internal class. + * + * @since 4.3 + */ +@NotThreadSafe +class LoggingOutputStream extends OutputStream { + + private final OutputStream out; + private final Wire wire; + + public LoggingOutputStream(final OutputStream out, final Wire wire) { + super(); + this.out = out; + this.wire = wire; + } + + @Override + public void write(final int b) throws IOException { + try { + wire.output(b); + } catch (IOException ex) { + wire.output("[write] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public void write(final byte[] b) throws IOException { + try { + wire.output(b); + out.write(b); + } catch (IOException ex) { + wire.output("[write] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public void write(final byte[] b, final int off, final int len) throws IOException { + try { + wire.output(b, off, len); + out.write(b, off, len); + } catch (IOException ex) { + wire.output("[write] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public void flush() throws IOException { + try { + out.flush(); + } catch (IOException ex) { + wire.output("[flush] I/O error: " + ex.getMessage()); + throw ex; + } + } + + @Override + public void close() throws IOException { + try { + out.close(); + } catch (IOException ex) { + wire.output("[close] I/O error: " + ex.getMessage()); + throw ex; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionInputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionInputBuffer.java new file mode 100644 index 000000000..95617206e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionInputBuffer.java @@ -0,0 +1,138 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.io.EofSensor; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionInputBuffer; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Logs all data read to the wire LOG. + * + * @since 4.0 + * + * @deprecated (4.3) no longer used. + */ +@Immutable +@Deprecated +public class LoggingSessionInputBuffer implements SessionInputBuffer, EofSensor { + + /** Original session input buffer. */ + private final SessionInputBuffer in; + + private final EofSensor eofSensor; + + /** The wire log to use for writing. */ + private final Wire wire; + + private final String charset; + + /** + * Create an instance that wraps the specified session input buffer. + * @param in The session input buffer. + * @param wire The wire log to use. + * @param charset protocol charset, <code>ASCII</code> if <code>null</code> + */ + public LoggingSessionInputBuffer( + final SessionInputBuffer in, final Wire wire, final String charset) { + super(); + this.in = in; + this.eofSensor = in instanceof EofSensor ? (EofSensor) in : null; + this.wire = wire; + this.charset = charset != null ? charset : Consts.ASCII.name(); + } + + public LoggingSessionInputBuffer(final SessionInputBuffer in, final Wire wire) { + this(in, wire, null); + } + + public boolean isDataAvailable(final int timeout) throws IOException { + return this.in.isDataAvailable(timeout); + } + + public int read(final byte[] b, final int off, final int len) throws IOException { + final int l = this.in.read(b, off, len); + if (this.wire.enabled() && l > 0) { + this.wire.input(b, off, l); + } + return l; + } + + public int read() throws IOException { + final int l = this.in.read(); + if (this.wire.enabled() && l != -1) { + this.wire.input(l); + } + return l; + } + + public int read(final byte[] b) throws IOException { + final int l = this.in.read(b); + if (this.wire.enabled() && l > 0) { + this.wire.input(b, 0, l); + } + return l; + } + + public String readLine() throws IOException { + final String s = this.in.readLine(); + if (this.wire.enabled() && s != null) { + final String tmp = s + "\r\n"; + this.wire.input(tmp.getBytes(this.charset)); + } + return s; + } + + public int readLine(final CharArrayBuffer buffer) throws IOException { + final int l = this.in.readLine(buffer); + if (this.wire.enabled() && l >= 0) { + final int pos = buffer.length() - l; + final String s = new String(buffer.buffer(), pos, l); + final String tmp = s + "\r\n"; + this.wire.input(tmp.getBytes(this.charset)); + } + return l; + } + + public HttpTransportMetrics getMetrics() { + return this.in.getMetrics(); + } + + public boolean isEof() { + if (this.eofSensor != null) { + return this.eofSensor.isEof(); + } else { + return false; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionOutputBuffer.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionOutputBuffer.java new file mode 100644 index 000000000..bf1100b4e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/LoggingSessionOutputBuffer.java @@ -0,0 +1,118 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; + +import ch.boye.httpclientandroidlib.Consts; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.io.HttpTransportMetrics; +import ch.boye.httpclientandroidlib.io.SessionOutputBuffer; +import ch.boye.httpclientandroidlib.util.CharArrayBuffer; + +/** + * Logs all data written to the wire LOG. + * @since 4.0 + * @deprecated (4.3) no longer used. + */ +@Immutable +@Deprecated +public class LoggingSessionOutputBuffer implements SessionOutputBuffer { + + /** Original data transmitter. */ + private final SessionOutputBuffer out; + + /** The wire log to use. */ + private final Wire wire; + + private final String charset; + + /** + * Create an instance that wraps the specified session output buffer. + * @param out The session output buffer. + * @param wire The Wire log to use. + * @param charset protocol charset, <code>ASCII</code> if <code>null</code> + */ + public LoggingSessionOutputBuffer( + final SessionOutputBuffer out, final Wire wire, final String charset) { + super(); + this.out = out; + this.wire = wire; + this.charset = charset != null ? charset : Consts.ASCII.name(); + } + + public LoggingSessionOutputBuffer(final SessionOutputBuffer out, final Wire wire) { + this(out, wire, null); + } + + public void write(final byte[] b, final int off, final int len) throws IOException { + this.out.write(b, off, len); + if (this.wire.enabled()) { + this.wire.output(b, off, len); + } + } + + public void write(final int b) throws IOException { + this.out.write(b); + if (this.wire.enabled()) { + this.wire.output(b); + } + } + + public void write(final byte[] b) throws IOException { + this.out.write(b); + if (this.wire.enabled()) { + this.wire.output(b); + } + } + + public void flush() throws IOException { + this.out.flush(); + } + + public void writeLine(final CharArrayBuffer buffer) throws IOException { + this.out.writeLine(buffer); + if (this.wire.enabled()) { + final String s = new String(buffer.buffer(), 0, buffer.length()); + final String tmp = s + "\r\n"; + this.wire.output(tmp.getBytes(this.charset)); + } + } + + public void writeLine(final String s) throws IOException { + this.out.writeLine(s); + if (this.wire.enabled()) { + final String tmp = s + "\r\n"; + this.wire.output(tmp.getBytes(this.charset)); + } + } + + public HttpTransportMetrics getMetrics() { + return this.out.getMetrics(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedClientConnectionImpl.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedClientConnectionImpl.java new file mode 100644 index 000000000..d1db2d8eb --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedClientConnectionImpl.java @@ -0,0 +1,461 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.InetAddress; +import java.net.Socket; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import ch.boye.httpclientandroidlib.HttpConnectionMetrics; +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.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * @since 4.2 + * + * @deprecated (4.3) use {@link ManagedHttpClientConnectionFactory}. + */ +@Deprecated +@NotThreadSafe +class ManagedClientConnectionImpl implements ManagedClientConnection { + + private final ClientConnectionManager manager; + private final ClientConnectionOperator operator; + private volatile HttpPoolEntry poolEntry; + private volatile boolean reusable; + private volatile long duration; + + ManagedClientConnectionImpl( + final ClientConnectionManager manager, + final ClientConnectionOperator operator, + final HttpPoolEntry entry) { + super(); + Args.notNull(manager, "Connection manager"); + Args.notNull(operator, "Connection operator"); + Args.notNull(entry, "HTTP pool entry"); + this.manager = manager; + this.operator = operator; + this.poolEntry = entry; + this.reusable = false; + this.duration = Long.MAX_VALUE; + } + + public String getId() { + return null; + } + + HttpPoolEntry getPoolEntry() { + return this.poolEntry; + } + + HttpPoolEntry detach() { + final HttpPoolEntry local = this.poolEntry; + this.poolEntry = null; + return local; + } + + public ClientConnectionManager getManager() { + return this.manager; + } + + private OperatedClientConnection getConnection() { + final HttpPoolEntry local = this.poolEntry; + if (local == null) { + return null; + } + return local.getConnection(); + } + + private OperatedClientConnection ensureConnection() { + final HttpPoolEntry local = this.poolEntry; + if (local == null) { + throw new ConnectionShutdownException(); + } + return local.getConnection(); + } + + private HttpPoolEntry ensurePoolEntry() { + final HttpPoolEntry local = this.poolEntry; + if (local == null) { + throw new ConnectionShutdownException(); + } + return local; + } + + public void close() throws IOException { + final HttpPoolEntry local = this.poolEntry; + if (local != null) { + final OperatedClientConnection conn = local.getConnection(); + local.getTracker().reset(); + conn.close(); + } + } + + public void shutdown() throws IOException { + final HttpPoolEntry local = this.poolEntry; + if (local != null) { + final OperatedClientConnection conn = local.getConnection(); + local.getTracker().reset(); + conn.shutdown(); + } + } + + public boolean isOpen() { + final OperatedClientConnection conn = getConnection(); + if (conn != null) { + return conn.isOpen(); + } else { + return false; + } + } + + public boolean isStale() { + final OperatedClientConnection conn = getConnection(); + if (conn != null) { + return conn.isStale(); + } else { + return true; + } + } + + public void setSocketTimeout(final int timeout) { + final OperatedClientConnection conn = ensureConnection(); + conn.setSocketTimeout(timeout); + } + + public int getSocketTimeout() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getSocketTimeout(); + } + + public HttpConnectionMetrics getMetrics() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getMetrics(); + } + + public void flush() throws IOException { + final OperatedClientConnection conn = ensureConnection(); + conn.flush(); + } + + public boolean isResponseAvailable(final int timeout) throws IOException { + final OperatedClientConnection conn = ensureConnection(); + return conn.isResponseAvailable(timeout); + } + + public void receiveResponseEntity( + final HttpResponse response) throws HttpException, IOException { + final OperatedClientConnection conn = ensureConnection(); + conn.receiveResponseEntity(response); + } + + public HttpResponse receiveResponseHeader() throws HttpException, IOException { + final OperatedClientConnection conn = ensureConnection(); + return conn.receiveResponseHeader(); + } + + public void sendRequestEntity( + final HttpEntityEnclosingRequest request) throws HttpException, IOException { + final OperatedClientConnection conn = ensureConnection(); + conn.sendRequestEntity(request); + } + + public void sendRequestHeader( + final HttpRequest request) throws HttpException, IOException { + final OperatedClientConnection conn = ensureConnection(); + conn.sendRequestHeader(request); + } + + public InetAddress getLocalAddress() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getLocalAddress(); + } + + public int getLocalPort() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getLocalPort(); + } + + public InetAddress getRemoteAddress() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getRemoteAddress(); + } + + public int getRemotePort() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getRemotePort(); + } + + public boolean isSecure() { + final OperatedClientConnection conn = ensureConnection(); + return conn.isSecure(); + } + + public void bind(final Socket socket) throws IOException { + throw new UnsupportedOperationException(); + } + + public Socket getSocket() { + final OperatedClientConnection conn = ensureConnection(); + return conn.getSocket(); + } + + public SSLSession getSSLSession() { + final OperatedClientConnection conn = ensureConnection(); + SSLSession result = null; + final Socket sock = conn.getSocket(); + if (sock instanceof SSLSocket) { + result = ((SSLSocket)sock).getSession(); + } + return result; + } + + public Object getAttribute(final String id) { + final OperatedClientConnection conn = ensureConnection(); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).getAttribute(id); + } else { + return null; + } + } + + public Object removeAttribute(final String id) { + final OperatedClientConnection conn = ensureConnection(); + if (conn instanceof HttpContext) { + return ((HttpContext) conn).removeAttribute(id); + } else { + return null; + } + } + + public void setAttribute(final String id, final Object obj) { + final OperatedClientConnection conn = ensureConnection(); + if (conn instanceof HttpContext) { + ((HttpContext) conn).setAttribute(id, obj); + } + } + + public HttpRoute getRoute() { + final HttpPoolEntry local = ensurePoolEntry(); + return local.getEffectiveRoute(); + } + + public void open( + final HttpRoute route, + final HttpContext context, + final HttpParams params) throws IOException { + Args.notNull(route, "Route"); + Args.notNull(params, "HTTP parameters"); + final OperatedClientConnection conn; + synchronized (this) { + if (this.poolEntry == null) { + throw new ConnectionShutdownException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + Asserts.notNull(tracker, "Route tracker"); + Asserts.check(!tracker.isConnected(), "Connection already open"); + conn = this.poolEntry.getConnection(); + } + + final HttpHost proxy = route.getProxyHost(); + this.operator.openConnection( + conn, + (proxy != null) ? proxy : route.getTargetHost(), + route.getLocalAddress(), + context, params); + + synchronized (this) { + if (this.poolEntry == null) { + throw new InterruptedIOException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + if (proxy == null) { + tracker.connectTarget(conn.isSecure()); + } else { + tracker.connectProxy(proxy, conn.isSecure()); + } + } + } + + public void tunnelTarget( + final boolean secure, final HttpParams params) throws IOException { + Args.notNull(params, "HTTP parameters"); + final HttpHost target; + final OperatedClientConnection conn; + synchronized (this) { + if (this.poolEntry == null) { + throw new ConnectionShutdownException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + Asserts.notNull(tracker, "Route tracker"); + Asserts.check(tracker.isConnected(), "Connection not open"); + Asserts.check(!tracker.isTunnelled(), "Connection is already tunnelled"); + target = tracker.getTargetHost(); + conn = this.poolEntry.getConnection(); + } + + conn.update(null, target, secure, params); + + synchronized (this) { + if (this.poolEntry == null) { + throw new InterruptedIOException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + tracker.tunnelTarget(secure); + } + } + + public void tunnelProxy( + final HttpHost next, final boolean secure, final HttpParams params) throws IOException { + Args.notNull(next, "Next proxy"); + Args.notNull(params, "HTTP parameters"); + final OperatedClientConnection conn; + synchronized (this) { + if (this.poolEntry == null) { + throw new ConnectionShutdownException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + Asserts.notNull(tracker, "Route tracker"); + Asserts.check(tracker.isConnected(), "Connection not open"); + conn = this.poolEntry.getConnection(); + } + + conn.update(null, next, secure, params); + + synchronized (this) { + if (this.poolEntry == null) { + throw new InterruptedIOException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + tracker.tunnelProxy(next, secure); + } + } + + public void layerProtocol( + final HttpContext context, final HttpParams params) throws IOException { + Args.notNull(params, "HTTP parameters"); + final HttpHost target; + final OperatedClientConnection conn; + synchronized (this) { + if (this.poolEntry == null) { + throw new ConnectionShutdownException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + Asserts.notNull(tracker, "Route tracker"); + Asserts.check(tracker.isConnected(), "Connection not open"); + Asserts.check(tracker.isTunnelled(), "Protocol layering without a tunnel not supported"); + Asserts.check(!tracker.isLayered(), "Multiple protocol layering not supported"); + target = tracker.getTargetHost(); + conn = this.poolEntry.getConnection(); + } + this.operator.updateSecureConnection(conn, target, context, params); + + synchronized (this) { + if (this.poolEntry == null) { + throw new InterruptedIOException(); + } + final RouteTracker tracker = this.poolEntry.getTracker(); + tracker.layerProtocol(conn.isSecure()); + } + } + + public Object getState() { + final HttpPoolEntry local = ensurePoolEntry(); + return local.getState(); + } + + public void setState(final Object state) { + final HttpPoolEntry local = ensurePoolEntry(); + local.setState(state); + } + + public void markReusable() { + this.reusable = true; + } + + public void unmarkReusable() { + this.reusable = false; + } + + public boolean isMarkedReusable() { + return this.reusable; + } + + public void setIdleDuration(final long duration, final TimeUnit unit) { + if(duration > 0) { + this.duration = unit.toMillis(duration); + } else { + this.duration = -1; + } + } + + public void releaseConnection() { + synchronized (this) { + if (this.poolEntry == null) { + return; + } + this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS); + this.poolEntry = null; + } + } + + public void abortConnection() { + synchronized (this) { + if (this.poolEntry == null) { + return; + } + this.reusable = false; + final OperatedClientConnection conn = this.poolEntry.getConnection(); + try { + conn.shutdown(); + } catch (final IOException ignore) { + } + this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS); + this.poolEntry = null; + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedHttpClientConnectionFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedHttpClientConnectionFactory.java new file mode 100644 index 000000000..51b3844f9 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ManagedHttpClientConnectionFactory.java @@ -0,0 +1,121 @@ +/* + * ==================================================================== + * 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.conn; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.util.concurrent.atomic.AtomicLong; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.HttpResponse; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.conn.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.impl.io.DefaultHttpRequestWriterFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageParserFactory; +import ch.boye.httpclientandroidlib.io.HttpMessageWriterFactory; + +/** + * Factory for {@link ManagedHttpClientConnection} instances. + * @since 4.3 + */ +@Immutable +public class ManagedHttpClientConnectionFactory + implements HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> { + + private static final AtomicLong COUNTER = new AtomicLong(); + + public static final ManagedHttpClientConnectionFactory INSTANCE = new ManagedHttpClientConnectionFactory(); + + public HttpClientAndroidLog log = new HttpClientAndroidLog(DefaultManagedHttpClientConnection.class); + public HttpClientAndroidLog headerlog = new HttpClientAndroidLog("ch.boye.httpclientandroidlib.headers"); + public HttpClientAndroidLog wirelog = new HttpClientAndroidLog("ch.boye.httpclientandroidlib.wire"); + + private final HttpMessageWriterFactory<HttpRequest> requestWriterFactory; + private final HttpMessageParserFactory<HttpResponse> responseParserFactory; + + public ManagedHttpClientConnectionFactory( + final HttpMessageWriterFactory<HttpRequest> requestWriterFactory, + final HttpMessageParserFactory<HttpResponse> responseParserFactory) { + super(); + this.requestWriterFactory = requestWriterFactory != null ? requestWriterFactory : + DefaultHttpRequestWriterFactory.INSTANCE; + this.responseParserFactory = responseParserFactory != null ? responseParserFactory : + DefaultHttpResponseParserFactory.INSTANCE; + } + + public ManagedHttpClientConnectionFactory( + final HttpMessageParserFactory<HttpResponse> responseParserFactory) { + this(null, responseParserFactory); + } + + public ManagedHttpClientConnectionFactory() { + this(null, null); + } + + public ManagedHttpClientConnection create(final HttpRoute route, final ConnectionConfig config) { + final ConnectionConfig cconfig = config != null ? config : ConnectionConfig.DEFAULT; + CharsetDecoder chardecoder = null; + CharsetEncoder charencoder = null; + final Charset charset = cconfig.getCharset(); + final CodingErrorAction malformedInputAction = cconfig.getMalformedInputAction() != null ? + cconfig.getMalformedInputAction() : CodingErrorAction.REPORT; + final CodingErrorAction unmappableInputAction = cconfig.getUnmappableInputAction() != null ? + cconfig.getUnmappableInputAction() : CodingErrorAction.REPORT; + if (charset != null) { + chardecoder = charset.newDecoder(); + chardecoder.onMalformedInput(malformedInputAction); + chardecoder.onUnmappableCharacter(unmappableInputAction); + charencoder = charset.newEncoder(); + charencoder.onMalformedInput(malformedInputAction); + charencoder.onUnmappableCharacter(unmappableInputAction); + } + final String id = "http-outgoing-" + Long.toString(COUNTER.getAndIncrement()); + return new LoggingManagedHttpClientConnection( + id, + log, + headerlog, + wirelog, + cconfig.getBufferSize(), + cconfig.getFragmentSizeHint(), + chardecoder, + charencoder, + cconfig.getMessageConstraints(), + null, + null, + requestWriterFactory, + responseParserFactory); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingClientConnectionManager.java new file mode 100644 index 000000000..f9685b81e --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingClientConnectionManager.java @@ -0,0 +1,328 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.pool.ConnPoolControl; +import ch.boye.httpclientandroidlib.pool.PoolStats; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Manages a pool of {@link ch.boye.httpclientandroidlib.conn.OperatedClientConnection} + * and is able to service connection requests from multiple execution threads. + * Connections are pooled on a per route basis. A request for a route which + * already the manager has persistent connections for available in the pool + * will be services by leasing a connection from the pool rather than + * creating a brand new connection. + * <p> + * PoolingConnectionManager maintains a maximum limit of connection on + * a per route basis and in total. Per default this implementation will + * create no more than than 2 concurrent connections per given route + * and no more 20 connections in total. For many real-world applications + * these limits may prove too constraining, especially if they use HTTP + * as a transport protocol for their services. Connection limits, however, + * can be adjusted using HTTP parameters. + * + * @since 4.2 + * + * @deprecated (4.3) use {@link PoolingHttpClientConnectionManager}. + */ +@Deprecated +@ThreadSafe +public class PoolingClientConnectionManager implements ClientConnectionManager, ConnPoolControl<HttpRoute> { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final SchemeRegistry schemeRegistry; + + private final HttpConnPool pool; + + private final ClientConnectionOperator operator; + + /** the custom-configured DNS lookup mechanism. */ + private final DnsResolver dnsResolver; + + public PoolingClientConnectionManager(final SchemeRegistry schreg) { + this(schreg, -1, TimeUnit.MILLISECONDS); + } + + public PoolingClientConnectionManager(final SchemeRegistry schreg,final DnsResolver dnsResolver) { + this(schreg, -1, TimeUnit.MILLISECONDS,dnsResolver); + } + + public PoolingClientConnectionManager() { + this(SchemeRegistryFactory.createDefault()); + } + + public PoolingClientConnectionManager( + final SchemeRegistry schemeRegistry, + final long timeToLive, final TimeUnit tunit) { + this(schemeRegistry, timeToLive, tunit, new SystemDefaultDnsResolver()); + } + + public PoolingClientConnectionManager(final SchemeRegistry schemeRegistry, + final long timeToLive, final TimeUnit tunit, + final DnsResolver dnsResolver) { + super(); + Args.notNull(schemeRegistry, "Scheme registry"); + Args.notNull(dnsResolver, "DNS resolver"); + this.schemeRegistry = schemeRegistry; + this.dnsResolver = dnsResolver; + this.operator = createConnectionOperator(schemeRegistry); + this.pool = new HttpConnPool(this.log, this.operator, 2, 20, timeToLive, tunit); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { + super.finalize(); + } + } + + /** + * Hook for creating the connection operator. + * It is called by the constructor. + * Derived classes can override this method to change the + * instantiation of the operator. + * The default implementation here instantiates + * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. + * + * @param schreg the scheme registry. + * + * @return the connection operator to use + */ + protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) { + return new DefaultClientConnectionOperator(schreg, this.dnsResolver); + } + + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + private String format(final HttpRoute route, final Object state) { + final StringBuilder buf = new StringBuilder(); + buf.append("[route: ").append(route).append("]"); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + private String formatStats(final HttpRoute route) { + final StringBuilder buf = new StringBuilder(); + final PoolStats totals = this.pool.getTotalStats(); + final PoolStats stats = this.pool.getStats(route); + buf.append("[total kept alive: ").append(totals.getAvailable()).append("; "); + buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable()); + buf.append(" of ").append(stats.getMax()).append("; "); + buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); + buf.append(" of ").append(totals.getMax()).append("]"); + return buf.toString(); + } + + private String format(final HttpPoolEntry entry) { + final StringBuilder buf = new StringBuilder(); + buf.append("[id: ").append(entry.getId()).append("]"); + buf.append("[route: ").append(entry.getRoute()).append("]"); + final Object state = entry.getState(); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + public ClientConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + Args.notNull(route, "HTTP route"); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection request: " + format(route, state) + formatStats(route)); + } + final Future<HttpPoolEntry> future = this.pool.lease(route, state); + + return new ClientConnectionRequest() { + + public void abortRequest() { + future.cancel(true); + } + + public ManagedClientConnection getConnection( + final long timeout, + final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + return leaseConnection(future, timeout, tunit); + } + + }; + + } + + ManagedClientConnection leaseConnection( + final Future<HttpPoolEntry> future, + final long timeout, + final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException { + final HttpPoolEntry entry; + try { + entry = future.get(timeout, tunit); + if (entry == null || future.isCancelled()) { + throw new InterruptedException(); + } + Asserts.check(entry.getConnection() != null, "Pool entry with no connection"); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute())); + } + return new ManagedClientConnectionImpl(this, this.operator, entry); + } catch (final ExecutionException ex) { + Throwable cause = ex.getCause(); + if (cause == null) { + cause = ex; + } + this.log.error("Unexpected exception leasing connection from pool", cause); + // Should never happen + throw new InterruptedException(); + } catch (final TimeoutException ex) { + throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool"); + } + } + + public void releaseConnection( + final ManagedClientConnection conn, final long keepalive, final TimeUnit tunit) { + + Args.check(conn instanceof ManagedClientConnectionImpl, "Connection class mismatch, " + + "connection not obtained from this manager"); + final ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn; + Asserts.check(managedConn.getManager() == this, "Connection not obtained from this manager"); + synchronized (managedConn) { + final HttpPoolEntry entry = managedConn.detach(); + if (entry == null) { + return; + } + try { + if (managedConn.isOpen() && !managedConn.isMarkedReusable()) { + try { + managedConn.shutdown(); + } catch (final IOException iox) { + if (this.log.isDebugEnabled()) { + this.log.debug("I/O exception shutting down released connection", iox); + } + } + } + // Only reusable connections can be kept alive + if (managedConn.isMarkedReusable()) { + entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); + if (this.log.isDebugEnabled()) { + final String s; + if (keepalive > 0) { + s = "for " + keepalive + " " + tunit; + } else { + s = "indefinitely"; + } + this.log.debug("Connection " + format(entry) + " can be kept alive " + s); + } + } + } finally { + this.pool.release(entry, managedConn.isMarkedReusable()); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute())); + } + } + } + + public void shutdown() { + this.log.debug("Connection manager is shutting down"); + try { + this.pool.shutdown(); + } catch (final IOException ex) { + this.log.debug("I/O exception shutting down connection manager", ex); + } + this.log.debug("Connection manager shut down"); + } + + public void closeIdleConnections(final long idleTimeout, final TimeUnit tunit) { + if (this.log.isDebugEnabled()) { + this.log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit); + } + this.pool.closeIdle(idleTimeout, tunit); + } + + public void closeExpiredConnections() { + this.log.debug("Closing expired connections"); + this.pool.closeExpired(); + } + + public int getMaxTotal() { + return this.pool.getMaxTotal(); + } + + public void setMaxTotal(final int max) { + this.pool.setMaxTotal(max); + } + + public int getDefaultMaxPerRoute() { + return this.pool.getDefaultMaxPerRoute(); + } + + public void setDefaultMaxPerRoute(final int max) { + this.pool.setDefaultMaxPerRoute(max); + } + + public int getMaxPerRoute(final HttpRoute route) { + return this.pool.getMaxPerRoute(route); + } + + public void setMaxPerRoute(final HttpRoute route, final int max) { + this.pool.setMaxPerRoute(route, max); + } + + public PoolStats getTotalStats() { + return this.pool.getTotalStats(); + } + + public PoolStats getStats(final HttpRoute route) { + return this.pool.getStats(route); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingHttpClientConnectionManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingHttpClientConnectionManager.java new file mode 100644 index 000000000..a2606b4e6 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/PoolingHttpClientConnectionManager.java @@ -0,0 +1,516 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.HttpClientConnection; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.config.ConnectionConfig; +import ch.boye.httpclientandroidlib.config.Lookup; +import ch.boye.httpclientandroidlib.config.Registry; +import ch.boye.httpclientandroidlib.config.RegistryBuilder; +import ch.boye.httpclientandroidlib.config.SocketConfig; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.ConnectionRequest; +import ch.boye.httpclientandroidlib.conn.DnsResolver; +import ch.boye.httpclientandroidlib.conn.HttpClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.HttpConnectionFactory; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.conn.ManagedHttpClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.socket.ConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.socket.PlainConnectionSocketFactory; +import ch.boye.httpclientandroidlib.conn.ssl.SSLConnectionSocketFactory; +import ch.boye.httpclientandroidlib.pool.ConnFactory; +import ch.boye.httpclientandroidlib.pool.ConnPoolControl; +import ch.boye.httpclientandroidlib.pool.PoolStats; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * <tt>ClientConnectionPoolManager</tt> maintains a pool of + * {@link HttpClientConnection}s and is able to service connection requests + * from multiple execution threads. Connections are pooled on a per route + * basis. A request for a route which already the manager has persistent + * connections for available in the pool will be services by leasing + * a connection from the pool rather than creating a brand new connection. + * <p/> + * <tt>ClientConnectionPoolManager</tt> maintains a maximum limit of connection + * on a per route basis and in total. Per default this implementation will + * create no more than than 2 concurrent connections per given route + * and no more 20 connections in total. For many real-world applications + * these limits may prove too constraining, especially if they use HTTP + * as a transport protocol for their services. Connection limits, however, + * can be adjusted using {@link ConnPoolControl} methods. + * + * @since 4.3 + */ +@ThreadSafe +public class PoolingHttpClientConnectionManager + implements HttpClientConnectionManager, ConnPoolControl<HttpRoute>, Closeable { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final ConfigData configData; + private final CPool pool; + private final HttpClientConnectionOperator connectionOperator; + private final AtomicBoolean isShutDown; + + private static Registry<ConnectionSocketFactory> getDefaultRegistry() { + return RegistryBuilder.<ConnectionSocketFactory>create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", SSLConnectionSocketFactory.getSocketFactory()) + .build(); + } + + public PoolingHttpClientConnectionManager() { + this(getDefaultRegistry()); + } + + public PoolingHttpClientConnectionManager(final long timeToLive, final TimeUnit tunit) { + this(getDefaultRegistry(), null, null ,null, timeToLive, tunit); + } + + public PoolingHttpClientConnectionManager( + final Registry<ConnectionSocketFactory> socketFactoryRegistry) { + this(socketFactoryRegistry, null, null); + } + + public PoolingHttpClientConnectionManager( + final Registry<ConnectionSocketFactory> socketFactoryRegistry, + final DnsResolver dnsResolver) { + this(socketFactoryRegistry, null, dnsResolver); + } + + public PoolingHttpClientConnectionManager( + final Registry<ConnectionSocketFactory> socketFactoryRegistry, + final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory) { + this(socketFactoryRegistry, connFactory, null); + } + + public PoolingHttpClientConnectionManager( + final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory) { + this(getDefaultRegistry(), connFactory, null); + } + + public PoolingHttpClientConnectionManager( + final Registry<ConnectionSocketFactory> socketFactoryRegistry, + final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory, + final DnsResolver dnsResolver) { + this(socketFactoryRegistry, connFactory, null, dnsResolver, -1, TimeUnit.MILLISECONDS); + } + + public PoolingHttpClientConnectionManager( + final Registry<ConnectionSocketFactory> socketFactoryRegistry, + final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory, + final SchemePortResolver schemePortResolver, + final DnsResolver dnsResolver, + final long timeToLive, final TimeUnit tunit) { + super(); + this.configData = new ConfigData(); + this.pool = new CPool( + new InternalConnectionFactory(this.configData, connFactory), 2, 20, timeToLive, tunit); + this.connectionOperator = new HttpClientConnectionOperator( + socketFactoryRegistry, schemePortResolver, dnsResolver); + this.isShutDown = new AtomicBoolean(false); + } + + PoolingHttpClientConnectionManager( + final CPool pool, + final Lookup<ConnectionSocketFactory> socketFactoryRegistry, + final SchemePortResolver schemePortResolver, + final DnsResolver dnsResolver) { + super(); + this.configData = new ConfigData(); + this.pool = pool; + this.connectionOperator = new HttpClientConnectionOperator( + socketFactoryRegistry, schemePortResolver, dnsResolver); + this.isShutDown = new AtomicBoolean(false); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { + super.finalize(); + } + } + + public void close() { + shutdown(); + } + + private String format(final HttpRoute route, final Object state) { + final StringBuilder buf = new StringBuilder(); + buf.append("[route: ").append(route).append("]"); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + private String formatStats(final HttpRoute route) { + final StringBuilder buf = new StringBuilder(); + final PoolStats totals = this.pool.getTotalStats(); + final PoolStats stats = this.pool.getStats(route); + buf.append("[total kept alive: ").append(totals.getAvailable()).append("; "); + buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable()); + buf.append(" of ").append(stats.getMax()).append("; "); + buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable()); + buf.append(" of ").append(totals.getMax()).append("]"); + return buf.toString(); + } + + private String format(final CPoolEntry entry) { + final StringBuilder buf = new StringBuilder(); + buf.append("[id: ").append(entry.getId()).append("]"); + buf.append("[route: ").append(entry.getRoute()).append("]"); + final Object state = entry.getState(); + if (state != null) { + buf.append("[state: ").append(state).append("]"); + } + return buf.toString(); + } + + public ConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + Args.notNull(route, "HTTP route"); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection request: " + format(route, state) + formatStats(route)); + } + final Future<CPoolEntry> future = this.pool.lease(route, state, null); + return new ConnectionRequest() { + + public boolean cancel() { + return future.cancel(true); + } + + public HttpClientConnection get( + final long timeout, + final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { + return leaseConnection(future, timeout, tunit); + } + + }; + + } + + protected HttpClientConnection leaseConnection( + final Future<CPoolEntry> future, + final long timeout, + final TimeUnit tunit) throws InterruptedException, ExecutionException, ConnectionPoolTimeoutException { + final CPoolEntry entry; + try { + entry = future.get(timeout, tunit); + if (entry == null || future.isCancelled()) { + throw new InterruptedException(); + } + Asserts.check(entry.getConnection() != null, "Pool entry with no connection"); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute())); + } + return CPoolProxy.newProxy(entry); + } catch (final TimeoutException ex) { + throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool"); + } + } + + public void releaseConnection( + final HttpClientConnection managedConn, + final Object state, + final long keepalive, final TimeUnit tunit) { + Args.notNull(managedConn, "Managed connection"); + synchronized (managedConn) { + final CPoolEntry entry = CPoolProxy.detach(managedConn); + if (entry == null) { + return; + } + final ManagedHttpClientConnection conn = entry.getConnection(); + try { + if (conn.isOpen()) { + entry.setState(state); + entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS); + if (this.log.isDebugEnabled()) { + final String s; + if (keepalive > 0) { + s = "for " + (double) keepalive / 1000 + " seconds"; + } else { + s = "indefinitely"; + } + this.log.debug("Connection " + format(entry) + " can be kept alive " + s); + } + } + } finally { + this.pool.release(entry, conn.isOpen() && entry.isRouteComplete()); + if (this.log.isDebugEnabled()) { + this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute())); + } + } + } + } + + public void connect( + final HttpClientConnection managedConn, + final HttpRoute route, + final int connectTimeout, + final HttpContext context) throws IOException { + Args.notNull(managedConn, "Managed Connection"); + Args.notNull(route, "HTTP route"); + final ManagedHttpClientConnection conn; + synchronized (managedConn) { + final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + conn = entry.getConnection(); + } + final HttpHost host; + if (route.getProxyHost() != null) { + host = route.getProxyHost(); + } else { + host = route.getTargetHost(); + } + final InetSocketAddress localAddress = route.getLocalSocketAddress(); + SocketConfig socketConfig = this.configData.getSocketConfig(host); + if (socketConfig == null) { + socketConfig = this.configData.getDefaultSocketConfig(); + } + if (socketConfig == null) { + socketConfig = SocketConfig.DEFAULT; + } + this.connectionOperator.connect( + conn, host, localAddress, connectTimeout, socketConfig, context); + } + + public void upgrade( + final HttpClientConnection managedConn, + final HttpRoute route, + final HttpContext context) throws IOException { + Args.notNull(managedConn, "Managed Connection"); + Args.notNull(route, "HTTP route"); + final ManagedHttpClientConnection conn; + synchronized (managedConn) { + final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + conn = entry.getConnection(); + } + this.connectionOperator.upgrade(conn, route.getTargetHost(), context); + } + + public void routeComplete( + final HttpClientConnection managedConn, + final HttpRoute route, + final HttpContext context) throws IOException { + Args.notNull(managedConn, "Managed Connection"); + Args.notNull(route, "HTTP route"); + synchronized (managedConn) { + final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn); + entry.markRouteComplete(); + } + } + + public void shutdown() { + if (this.isShutDown.compareAndSet(false, true)) { + this.log.debug("Connection manager is shutting down"); + try { + this.pool.shutdown(); + } catch (final IOException ex) { + this.log.debug("I/O exception shutting down connection manager", ex); + } + this.log.debug("Connection manager shut down"); + } + } + + public void closeIdleConnections(final long idleTimeout, final TimeUnit tunit) { + if (this.log.isDebugEnabled()) { + this.log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit); + } + this.pool.closeIdle(idleTimeout, tunit); + } + + public void closeExpiredConnections() { + this.log.debug("Closing expired connections"); + this.pool.closeExpired(); + } + + public int getMaxTotal() { + return this.pool.getMaxTotal(); + } + + public void setMaxTotal(final int max) { + this.pool.setMaxTotal(max); + } + + public int getDefaultMaxPerRoute() { + return this.pool.getDefaultMaxPerRoute(); + } + + public void setDefaultMaxPerRoute(final int max) { + this.pool.setDefaultMaxPerRoute(max); + } + + public int getMaxPerRoute(final HttpRoute route) { + return this.pool.getMaxPerRoute(route); + } + + public void setMaxPerRoute(final HttpRoute route, final int max) { + this.pool.setMaxPerRoute(route, max); + } + + public PoolStats getTotalStats() { + return this.pool.getTotalStats(); + } + + public PoolStats getStats(final HttpRoute route) { + return this.pool.getStats(route); + } + + public SocketConfig getDefaultSocketConfig() { + return this.configData.getDefaultSocketConfig(); + } + + public void setDefaultSocketConfig(final SocketConfig defaultSocketConfig) { + this.configData.setDefaultSocketConfig(defaultSocketConfig); + } + + public ConnectionConfig getDefaultConnectionConfig() { + return this.configData.getDefaultConnectionConfig(); + } + + public void setDefaultConnectionConfig(final ConnectionConfig defaultConnectionConfig) { + this.configData.setDefaultConnectionConfig(defaultConnectionConfig); + } + + public SocketConfig getSocketConfig(final HttpHost host) { + return this.configData.getSocketConfig(host); + } + + public void setSocketConfig(final HttpHost host, final SocketConfig socketConfig) { + this.configData.setSocketConfig(host, socketConfig); + } + + public ConnectionConfig getConnectionConfig(final HttpHost host) { + return this.configData.getConnectionConfig(host); + } + + public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) { + this.configData.setConnectionConfig(host, connectionConfig); + } + + static class ConfigData { + + private final Map<HttpHost, SocketConfig> socketConfigMap; + private final Map<HttpHost, ConnectionConfig> connectionConfigMap; + private volatile SocketConfig defaultSocketConfig; + private volatile ConnectionConfig defaultConnectionConfig; + + ConfigData() { + super(); + this.socketConfigMap = new ConcurrentHashMap<HttpHost, SocketConfig>(); + this.connectionConfigMap = new ConcurrentHashMap<HttpHost, ConnectionConfig>(); + } + + public SocketConfig getDefaultSocketConfig() { + return this.defaultSocketConfig; + } + + public void setDefaultSocketConfig(final SocketConfig defaultSocketConfig) { + this.defaultSocketConfig = defaultSocketConfig; + } + + public ConnectionConfig getDefaultConnectionConfig() { + return this.defaultConnectionConfig; + } + + public void setDefaultConnectionConfig(final ConnectionConfig defaultConnectionConfig) { + this.defaultConnectionConfig = defaultConnectionConfig; + } + + public SocketConfig getSocketConfig(final HttpHost host) { + return this.socketConfigMap.get(host); + } + + public void setSocketConfig(final HttpHost host, final SocketConfig socketConfig) { + this.socketConfigMap.put(host, socketConfig); + } + + public ConnectionConfig getConnectionConfig(final HttpHost host) { + return this.connectionConfigMap.get(host); + } + + public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) { + this.connectionConfigMap.put(host, connectionConfig); + } + + } + + static class InternalConnectionFactory implements ConnFactory<HttpRoute, ManagedHttpClientConnection> { + + private final ConfigData configData; + private final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory; + + InternalConnectionFactory( + final ConfigData configData, + final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory) { + super(); + this.configData = configData != null ? configData : new ConfigData(); + this.connFactory = connFactory != null ? connFactory : + ManagedHttpClientConnectionFactory.INSTANCE; + } + + public ManagedHttpClientConnection create(final HttpRoute route) throws IOException { + ConnectionConfig config = null; + if (route.getProxyHost() != null) { + config = this.configData.getConnectionConfig(route.getProxyHost()); + } + if (config == null) { + config = this.configData.getConnectionConfig(route.getTargetHost()); + } + if (config == null) { + config = this.configData.getDefaultConnectionConfig(); + } + if (config == null) { + config = ConnectionConfig.DEFAULT; + } + return this.connFactory.create(route, config); + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java new file mode 100644 index 000000000..8c68e5e18 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/ProxySelectorRoutePlanner.java @@ -0,0 +1,279 @@ +/* + * ==================================================================== + * 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.conn; + + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.NotThreadSafe; +import ch.boye.httpclientandroidlib.conn.params.ConnRouteParams; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.protocol.HttpContext; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + + +/** + * Default implementation of an {@link HttpRoutePlanner}. + * This implementation is based on {@link java.net.ProxySelector}. + * By default, it will pick up the proxy settings of the JVM, either + * from system properties or from the browser running the application. + * Additionally, it interprets some + * {@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames parameters}, + * though not the {@link + * ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#DEFAULT_PROXY DEFAULT_PROXY}. + * <p> + * The following parameters can be used to customize the behavior of this + * class: + * <ul> + * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#LOCAL_ADDRESS}</li> + * <li>{@link ch.boye.httpclientandroidlib.conn.params.ConnRoutePNames#FORCED_ROUTE}</li> + * </ul> + * + * @since 4.0 + * + * @deprecated (4.3) use {@link SystemDefaultRoutePlanner} + */ +@NotThreadSafe // e.g [gs]etProxySelector() +@Deprecated +public class ProxySelectorRoutePlanner implements HttpRoutePlanner { + + /** The scheme registry. */ + protected final SchemeRegistry schemeRegistry; // @ThreadSafe + + /** The proxy selector to use, or <code>null</code> for system default. */ + protected ProxySelector proxySelector; + + /** + * Creates a new proxy selector route planner. + * + * @param schreg the scheme registry + * @param prosel the proxy selector, or + * <code>null</code> for the system default + */ + public ProxySelectorRoutePlanner(final SchemeRegistry schreg, + final ProxySelector prosel) { + Args.notNull(schreg, "SchemeRegistry"); + schemeRegistry = schreg; + proxySelector = prosel; + } + + /** + * Obtains the proxy selector to use. + * + * @return the proxy selector, or <code>null</code> for the system default + */ + public ProxySelector getProxySelector() { + return this.proxySelector; + } + + /** + * Sets the proxy selector to use. + * + * @param prosel the proxy selector, or + * <code>null</code> to use the system default + */ + public void setProxySelector(final ProxySelector prosel) { + this.proxySelector = prosel; + } + + public HttpRoute determineRoute(final HttpHost target, + final HttpRequest request, + final HttpContext context) + throws HttpException { + + Args.notNull(request, "HTTP request"); + + // If we have a forced route, we can do without a target. + HttpRoute route = + ConnRouteParams.getForcedRoute(request.getParams()); + if (route != null) { + return route; + } + + // If we get here, there is no forced route. + // So we need a target to compute a route. + + Asserts.notNull(target, "Target host"); + + final InetAddress local = + ConnRouteParams.getLocalAddress(request.getParams()); + final HttpHost proxy = determineProxy(target, request, context); + + final Scheme schm = + this.schemeRegistry.getScheme(target.getSchemeName()); + // as it is typically used for TLS/SSL, we assume that + // a layered scheme implies a secure connection + final boolean secure = schm.isLayered(); + + if (proxy == null) { + route = new HttpRoute(target, local, secure); + } else { + route = new HttpRoute(target, local, proxy, secure); + } + return route; + } + + /** + * Determines a proxy for the given target. + * + * @param target the planned target, never <code>null</code> + * @param request the request to be sent, never <code>null</code> + * @param context the context, or <code>null</code> + * + * @return the proxy to use, or <code>null</code> for a direct route + * + * @throws HttpException + * in case of system proxy settings that cannot be handled + */ + protected HttpHost determineProxy(final HttpHost target, + final HttpRequest request, + final HttpContext context) + throws HttpException { + + // the proxy selector can be 'unset', so we better deal with null here + ProxySelector psel = this.proxySelector; + if (psel == null) { + psel = ProxySelector.getDefault(); + } + if (psel == null) { + return null; + } + + URI targetURI = null; + try { + targetURI = new URI(target.toURI()); + } catch (final URISyntaxException usx) { + throw new HttpException + ("Cannot convert host to URI: " + target, usx); + } + final List<Proxy> proxies = psel.select(targetURI); + + final Proxy p = chooseProxy(proxies, target, request, context); + + HttpHost result = null; + if (p.type() == Proxy.Type.HTTP) { + // convert the socket address to an HttpHost + if (!(p.address() instanceof InetSocketAddress)) { + throw new HttpException + ("Unable to handle non-Inet proxy address: "+p.address()); + } + final InetSocketAddress isa = (InetSocketAddress) p.address(); + // assume default scheme (http) + result = new HttpHost(getHost(isa), isa.getPort()); + } + + return result; + } + + /** + * Obtains a host from an {@link InetSocketAddress}. + * + * @param isa the socket address + * + * @return a host string, either as a symbolic name or + * as a literal IP address string + * <br/> + * (TODO: determine format for IPv6 addresses, with or without [brackets]) + */ + protected String getHost(final InetSocketAddress isa) { + + //@@@ Will this work with literal IPv6 addresses, or do we + //@@@ need to wrap these in [] for the string representation? + //@@@ Having it in this method at least allows for easy workarounds. + return isa.isUnresolved() ? + isa.getHostName() : isa.getAddress().getHostAddress(); + + } + + /** + * Chooses a proxy from a list of available proxies. + * The default implementation just picks the first non-SOCKS proxy + * from the list. If there are only SOCKS proxies, + * {@link Proxy#NO_PROXY Proxy.NO_PROXY} is returned. + * Derived classes may implement more advanced strategies, + * such as proxy rotation if there are multiple options. + * + * @param proxies the list of proxies to choose from, + * never <code>null</code> or empty + * @param target the planned target, never <code>null</code> + * @param request the request to be sent, never <code>null</code> + * @param context the context, or <code>null</code> + * + * @return a proxy type + */ + protected Proxy chooseProxy(final List<Proxy> proxies, + final HttpHost target, + final HttpRequest request, + final HttpContext context) { + Args.notEmpty(proxies, "List of proxies"); + + Proxy result = null; + + // check the list for one we can use + for (int i=0; (result == null) && (i < proxies.size()); i++) { + + final Proxy p = proxies.get(i); + switch (p.type()) { + + case DIRECT: + case HTTP: + result = p; + break; + + case SOCKS: + // SOCKS hosts are not handled on the route level. + // The socket may make use of the SOCKS host though. + break; + } + } + + if (result == null) { + //@@@ log as warning or info that only a socks proxy is available? + // result can only be null if all proxies are socks proxies + // socks proxies are not handled on the route planning level + result = Proxy.NO_PROXY; + } + + return result; + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SchemeRegistryFactory.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SchemeRegistryFactory.java new file mode 100644 index 000000000..0a17993d1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SchemeRegistryFactory.java @@ -0,0 +1,90 @@ +/* + * ==================================================================== + * 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.conn; + +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.scheme.PlainSocketFactory; +import ch.boye.httpclientandroidlib.conn.scheme.Scheme; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.conn.ssl.SSLSocketFactory; + +/** + * @since 4.1 + * + * @deprecated (4.3) use {@link ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder}. + */ +@ThreadSafe +@Deprecated +public final class SchemeRegistryFactory { + + /** + * Initializes default scheme registry based on JSSE defaults. System properties will + * not be taken into consideration. + */ + public static SchemeRegistry createDefault() { + final SchemeRegistry registry = new SchemeRegistry(); + registry.register( + new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); + registry.register( + new Scheme("https", 443, SSLSocketFactory.getSocketFactory())); + return registry; + } + + /** + * Initializes default scheme registry using system properties as described in + * <a href="http://download.oracle.com/javase/1,5.0/docs/guide/security/jsse/JSSERefGuide.html"> + * "JavaTM Secure Socket Extension (JSSE) Reference Guide for the JavaTM 2 Platform + * Standard Edition 5</a> + * <p> + * The following system properties are taken into account by this method: + * <ul> + * <li>ssl.TrustManagerFactory.algorithm</li> + * <li>javax.net.ssl.trustStoreType</li> + * <li>javax.net.ssl.trustStore</li> + * <li>javax.net.ssl.trustStoreProvider</li> + * <li>javax.net.ssl.trustStorePassword</li> + * <li>java.home</li> + * <li>ssl.KeyManagerFactory.algorithm</li> + * <li>javax.net.ssl.keyStoreType</li> + * <li>javax.net.ssl.keyStore</li> + * <li>javax.net.ssl.keyStoreProvider</li> + * <li>javax.net.ssl.keyStorePassword</li> + * </ul> + * <p> + * + * @since 4.2 + */ + public static SchemeRegistry createSystemDefault() { + final SchemeRegistry registry = new SchemeRegistry(); + registry.register( + new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); + registry.register( + new Scheme("https", 443, SSLSocketFactory.getSystemSocketFactory())); + return registry; + } +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java new file mode 100644 index 000000000..c2b6c8799 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SingleClientConnManager.java @@ -0,0 +1,427 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * A connection manager for a single connection. This connection manager + * maintains only one active connection at a time. Even though this class + * is thread-safe it ought to be used by one execution thread only. + * <p> + * SingleClientConnManager will make an effort to reuse the connection + * for subsequent requests with the same {@link HttpRoute route}. + * It will, however, close the existing connection and open it + * for the given route, if the route of the persistent connection does + * not match that of the connection request. If the connection has been + * already been allocated {@link IllegalStateException} is thrown. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link BasicClientConnectionManager} + */ +@ThreadSafe +@Deprecated +public class SingleClientConnManager implements ClientConnectionManager { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** The message to be logged on multiple allocation. */ + public final static String MISUSE_MESSAGE = + "Invalid use of SingleClientConnManager: connection still allocated.\n" + + "Make sure to release the connection before allocating another one."; + + /** The schemes supported by this connection manager. */ + protected final SchemeRegistry schemeRegistry; + + /** The operator for opening and updating connections. */ + protected final ClientConnectionOperator connOperator; + + /** Whether the connection should be shut down on release. */ + protected final boolean alwaysShutDown; + + /** The one and only entry in this pool. */ + @GuardedBy("this") + protected volatile PoolEntry uniquePoolEntry; + + /** The currently issued managed connection, if any. */ + @GuardedBy("this") + protected volatile ConnAdapter managedConn; + + /** The time of the last connection release, or -1. */ + @GuardedBy("this") + protected volatile long lastReleaseTime; + + /** The time the last released connection expires and shouldn't be reused. */ + @GuardedBy("this") + protected volatile long connectionExpiresTime; + + /** Indicates whether this connection manager is shut down. */ + protected volatile boolean isShutDown; + + /** + * Creates a new simple connection manager. + * + * @param params the parameters for this manager + * @param schreg the scheme registry + * + * @deprecated (4.1) use {@link SingleClientConnManager#SingleClientConnManager(SchemeRegistry)} + */ + @Deprecated + public SingleClientConnManager(final HttpParams params, + final SchemeRegistry schreg) { + this(schreg); + } + /** + * Creates a new simple connection manager. + * + * @param schreg the scheme registry + */ + public SingleClientConnManager(final SchemeRegistry schreg) { + Args.notNull(schreg, "Scheme registry"); + this.schemeRegistry = schreg; + this.connOperator = createConnectionOperator(schreg); + this.uniquePoolEntry = new PoolEntry(); + this.managedConn = null; + this.lastReleaseTime = -1L; + this.alwaysShutDown = false; //@@@ from params? as argument? + this.isShutDown = false; + } + + /** + * @since 4.1 + */ + public SingleClientConnManager() { + this(SchemeRegistryFactory.createDefault()); + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { // Make sure we call overridden method even if shutdown barfs + super.finalize(); + } + } + + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + /** + * Hook for creating the connection operator. + * It is called by the constructor. + * Derived classes can override this method to change the + * instantiation of the operator. + * The default implementation here instantiates + * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. + * + * @param schreg the scheme registry to use, or <code>null</code> + * + * @return the connection operator to use + */ + protected ClientConnectionOperator + createConnectionOperator(final SchemeRegistry schreg) { + return new DefaultClientConnectionOperator(schreg); + } + + /** + * Asserts that this manager is not shut down. + * + * @throws IllegalStateException if this manager is shut down + */ + protected final void assertStillUp() throws IllegalStateException { + Asserts.check(!this.isShutDown, "Manager is shut down"); + } + + public final ClientConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + + return new ClientConnectionRequest() { + + public void abortRequest() { + // Nothing to abort, since requests are immediate. + } + + public ManagedClientConnection getConnection( + final long timeout, final TimeUnit tunit) { + return SingleClientConnManager.this.getConnection( + route, state); + } + + }; + } + + /** + * Obtains a connection. + * + * @param route where the connection should point to + * + * @return a connection that can be used to communicate + * along the given route + */ + public ManagedClientConnection getConnection(final HttpRoute route, final Object state) { + Args.notNull(route, "Route"); + assertStillUp(); + + if (log.isDebugEnabled()) { + log.debug("Get connection for route " + route); + } + + synchronized (this) { + + Asserts.check(managedConn == null, MISUSE_MESSAGE); + + // check re-usability of the connection + boolean recreate = false; + boolean shutdown = false; + + // Kill the connection if it expired. + closeExpiredConnections(); + + if (uniquePoolEntry.connection.isOpen()) { + final RouteTracker tracker = uniquePoolEntry.tracker; + shutdown = (tracker == null || // can happen if method is aborted + !tracker.toRoute().equals(route)); + } else { + // If the connection is not open, create a new PoolEntry, + // as the connection may have been marked not reusable, + // due to aborts -- and the PoolEntry should not be reused + // either. There's no harm in recreating an entry if + // the connection is closed. + recreate = true; + } + + if (shutdown) { + recreate = true; + try { + uniquePoolEntry.shutdown(); + } catch (final IOException iox) { + log.debug("Problem shutting down connection.", iox); + } + } + + if (recreate) { + uniquePoolEntry = new PoolEntry(); + } + + managedConn = new ConnAdapter(uniquePoolEntry, route); + + return managedConn; + } + } + + public void releaseConnection( + final ManagedClientConnection conn, + final long validDuration, final TimeUnit timeUnit) { + Args.check(conn instanceof ConnAdapter, "Connection class mismatch, " + + "connection not obtained from this manager"); + assertStillUp(); + + if (log.isDebugEnabled()) { + log.debug("Releasing connection " + conn); + } + + final ConnAdapter sca = (ConnAdapter) conn; + synchronized (sca) { + if (sca.poolEntry == null) + { + return; // already released + } + final ClientConnectionManager manager = sca.getManager(); + Asserts.check(manager == this, "Connection not obtained from this manager"); + try { + // make sure that the response has been read completely + if (sca.isOpen() && (this.alwaysShutDown || + !sca.isMarkedReusable()) + ) { + if (log.isDebugEnabled()) { + log.debug + ("Released connection open but not reusable."); + } + + // make sure this connection will not be re-used + // we might have gotten here because of a shutdown trigger + // shutdown of the adapter also clears the tracked route + sca.shutdown(); + } + } catch (final IOException iox) { + if (log.isDebugEnabled()) { + log.debug("Exception shutting down released connection.", + iox); + } + } finally { + sca.detach(); + synchronized (this) { + managedConn = null; + lastReleaseTime = System.currentTimeMillis(); + if(validDuration > 0) { + connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime; + } else { + connectionExpiresTime = Long.MAX_VALUE; + } + } + } + } + } + + public void closeExpiredConnections() { + final long time = connectionExpiresTime; + if (System.currentTimeMillis() >= time) { + closeIdleConnections(0, TimeUnit.MILLISECONDS); + } + } + + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + assertStillUp(); + + // idletime can be 0 or negative, no problem there + Args.notNull(tunit, "Time unit"); + + synchronized (this) { + if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) { + final long cutoff = + System.currentTimeMillis() - tunit.toMillis(idletime); + if (lastReleaseTime <= cutoff) { + try { + uniquePoolEntry.close(); + } catch (final IOException iox) { + // ignore + log.debug("Problem closing idle connection.", iox); + } + } + } + } + } + + public void shutdown() { + this.isShutDown = true; + synchronized (this) { + try { + if (uniquePoolEntry != null) { + uniquePoolEntry.shutdown(); + } + } catch (final IOException iox) { + // ignore + log.debug("Problem while shutting down manager.", iox); + } finally { + uniquePoolEntry = null; + managedConn = null; + } + } + } + + protected void revokeConnection() { + final ConnAdapter conn = managedConn; + if (conn == null) { + return; + } + conn.detach(); + + synchronized (this) { + try { + uniquePoolEntry.shutdown(); + } catch (final IOException iox) { + // ignore + log.debug("Problem while shutting down connection.", iox); + } + } + } + + /** + * The pool entry for this connection manager. + */ + protected class PoolEntry extends AbstractPoolEntry { + + /** + * Creates a new pool entry. + * + */ + protected PoolEntry() { + super(SingleClientConnManager.this.connOperator, null); + } + + /** + * Closes the connection in this pool entry. + */ + protected void close() throws IOException { + shutdownEntry(); + if (connection.isOpen()) { + connection.close(); + } + } + + /** + * Shuts down the connection in this pool entry. + */ + protected void shutdown() throws IOException { + shutdownEntry(); + if (connection.isOpen()) { + connection.shutdown(); + } + } + + } + + /** + * The connection adapter used by this manager. + */ + protected class ConnAdapter extends AbstractPooledConnAdapter { + + /** + * Creates a new connection adapter. + * + * @param entry the pool entry for the connection being wrapped + * @param route the planned route for this connection + */ + protected ConnAdapter(final PoolEntry entry, final HttpRoute route) { + super(SingleClientConnManager.this, entry); + markReusable(); + entry.route = route; + } + + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultDnsResolver.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultDnsResolver.java new file mode 100644 index 000000000..bb6b7c60d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultDnsResolver.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * 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.conn; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import ch.boye.httpclientandroidlib.conn.DnsResolver; + +/** + * DNS resolver that uses the default OS implementation for resolving host names. + * + * @since 4.2 + */ +public class SystemDefaultDnsResolver implements DnsResolver { + + public static final SystemDefaultDnsResolver INSTANCE = new SystemDefaultDnsResolver(); + + public InetAddress[] resolve(final String host) throws UnknownHostException { + return InetAddress.getAllByName(host); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultRoutePlanner.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultRoutePlanner.java new file mode 100644 index 000000000..27ea2a3d0 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/SystemDefaultRoutePlanner.java @@ -0,0 +1,132 @@ +/* + * ==================================================================== + * 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.conn; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import ch.boye.httpclientandroidlib.HttpException; +import ch.boye.httpclientandroidlib.HttpHost; +import ch.boye.httpclientandroidlib.HttpRequest; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.conn.SchemePortResolver; +import ch.boye.httpclientandroidlib.protocol.HttpContext; + +/** + * {@link ch.boye.httpclientandroidlib.conn.routing.HttpRoutePlanner} implementation + * based on {@link ProxySelector}. By default, this class will pick up + * the proxy settings of the JVM, either from system properties + * or from the browser running the application. + * + * @since 4.3 + */ +@Immutable +public class SystemDefaultRoutePlanner extends DefaultRoutePlanner { + + private final ProxySelector proxySelector; + + public SystemDefaultRoutePlanner( + final SchemePortResolver schemePortResolver, + final ProxySelector proxySelector) { + super(schemePortResolver); + this.proxySelector = proxySelector != null ? proxySelector : ProxySelector.getDefault(); + } + + public SystemDefaultRoutePlanner(final ProxySelector proxySelector) { + this(null, proxySelector); + } + + @Override + protected HttpHost determineProxy( + final HttpHost target, + final HttpRequest request, + final HttpContext context) throws HttpException { + final URI targetURI; + try { + targetURI = new URI(target.toURI()); + } catch (final URISyntaxException ex) { + throw new HttpException("Cannot convert host to URI: " + target, ex); + } + final List<Proxy> proxies = this.proxySelector.select(targetURI); + final Proxy p = chooseProxy(proxies); + HttpHost result = null; + if (p.type() == Proxy.Type.HTTP) { + // convert the socket address to an HttpHost + if (!(p.address() instanceof InetSocketAddress)) { + throw new HttpException("Unable to handle non-Inet proxy address: " + p.address()); + } + final InetSocketAddress isa = (InetSocketAddress) p.address(); + // assume default scheme (http) + result = new HttpHost(getHost(isa), isa.getPort()); + } + + return result; + } + + private String getHost(final InetSocketAddress isa) { + + //@@@ Will this work with literal IPv6 addresses, or do we + //@@@ need to wrap these in [] for the string representation? + //@@@ Having it in this method at least allows for easy workarounds. + return isa.isUnresolved() ? + isa.getHostName() : isa.getAddress().getHostAddress(); + + } + + private Proxy chooseProxy(final List<Proxy> proxies) { + Proxy result = null; + // check the list for one we can use + for (int i=0; (result == null) && (i < proxies.size()); i++) { + final Proxy p = proxies.get(i); + switch (p.type()) { + + case DIRECT: + case HTTP: + result = p; + break; + + case SOCKS: + // SOCKS hosts are not handled on the route level. + // The socket may make use of the SOCKS host though. + break; + } + } + if (result == null) { + //@@@ log as warning or info that only a socks proxy is available? + // result can only be null if all proxies are socks proxies + // socks proxies are not handled on the route planning level + result = Proxy.NO_PROXY; + } + return result; + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/Wire.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/Wire.java new file mode 100644 index 000000000..1a678f05d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/Wire.java @@ -0,0 +1,152 @@ +/* + * ==================================================================== + * 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.conn; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +import ch.boye.httpclientandroidlib.annotation.Immutable; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Logs data to the wire LOG. + * TODO: make package private. Should not be part of the public API. + * + * @since 4.0 + */ +@Immutable +public class Wire { + + public HttpClientAndroidLog log; + private final String id; + + /** + * @since 4.3 + */ + public Wire(final HttpClientAndroidLog log, final String id) { + this.log = log; + this.id = id; + } + + public Wire(final HttpClientAndroidLog log) { + this(log, ""); + } + + private void wire(final String header, final InputStream instream) + throws IOException { + final StringBuilder buffer = new StringBuilder(); + int ch; + while ((ch = instream.read()) != -1) { + if (ch == 13) { + buffer.append("[\\r]"); + } else if (ch == 10) { + buffer.append("[\\n]\""); + buffer.insert(0, "\""); + buffer.insert(0, header); + log.debug(id + " " + buffer.toString()); + buffer.setLength(0); + } else if ((ch < 32) || (ch > 127)) { + buffer.append("[0x"); + buffer.append(Integer.toHexString(ch)); + buffer.append("]"); + } else { + buffer.append((char) ch); + } + } + if (buffer.length() > 0) { + buffer.append('\"'); + buffer.insert(0, '\"'); + buffer.insert(0, header); + log.debug(id + " " + buffer.toString()); + } + } + + + public boolean enabled() { + return log.isDebugEnabled(); + } + + public void output(final InputStream outstream) + throws IOException { + Args.notNull(outstream, "Output"); + wire(">> ", outstream); + } + + public void input(final InputStream instream) + throws IOException { + Args.notNull(instream, "Input"); + wire("<< ", instream); + } + + public void output(final byte[] b, final int off, final int len) + throws IOException { + Args.notNull(b, "Output"); + wire(">> ", new ByteArrayInputStream(b, off, len)); + } + + public void input(final byte[] b, final int off, final int len) + throws IOException { + Args.notNull(b, "Input"); + wire("<< ", new ByteArrayInputStream(b, off, len)); + } + + public void output(final byte[] b) + throws IOException { + Args.notNull(b, "Output"); + wire(">> ", new ByteArrayInputStream(b)); + } + + public void input(final byte[] b) + throws IOException { + Args.notNull(b, "Input"); + wire("<< ", new ByteArrayInputStream(b)); + } + + public void output(final int b) + throws IOException { + output(new byte[] {(byte) b}); + } + + public void input(final int b) + throws IOException { + input(new byte[] {(byte) b}); + } + + public void output(final String s) + throws IOException { + Args.notNull(s, "Output"); + output(s.getBytes()); + } + + public void input(final String s) + throws IOException { + Args.notNull(s, "Input"); + input(s.getBytes()); + } +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/package-info.java new file mode 100644 index 000000000..49305e4b7 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/package-info.java @@ -0,0 +1,32 @@ +/* + * ==================================================================== + * 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/>. + * + */ + +/** + * Default implementations of client connection management + * functions. + */ +package ch.boye.httpclientandroidlib.impl.conn; diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/AbstractConnPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/AbstractConnPool.java new file mode 100644 index 000000000..f85539c2d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/AbstractConnPool.java @@ -0,0 +1,234 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + +import java.io.IOException; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.GuardedBy; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.impl.conn.IdleConnectionHandler; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * An abstract connection pool. + * It is used by the {@link ThreadSafeClientConnManager}. + * The abstract pool includes a {@link #poolLock}, which is used to + * synchronize access to the internal pool datastructures. + * Don't use <code>synchronized</code> for that purpose! + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.pool.AbstractConnPool} + */ +@Deprecated +public abstract class AbstractConnPool { + + public HttpClientAndroidLog log; + + /** + * The global lock for this pool. + */ + protected final Lock poolLock; + + /** References to issued connections */ + @GuardedBy("poolLock") + protected Set<BasicPoolEntry> leasedConnections; + + /** The current total number of connections. */ + @GuardedBy("poolLock") + protected int numConnections; + + /** Indicates whether this pool is shut down. */ + protected volatile boolean isShutDown; + + protected Set<BasicPoolEntryRef> issuedConnections; + + protected ReferenceQueue<Object> refQueue; + + protected IdleConnectionHandler idleConnHandler; + + /** + * Creates a new connection pool. + */ + protected AbstractConnPool() { + super(); + this.log = new HttpClientAndroidLog(getClass()); + this.leasedConnections = new HashSet<BasicPoolEntry>(); + this.idleConnHandler = new IdleConnectionHandler(); + this.poolLock = new ReentrantLock(); + } + + public void enableConnectionGC() + throws IllegalStateException { + } + + /** + * Obtains a pool entry with a connection within the given timeout. + * + * @param route the route for which to get the connection + * @param timeout the timeout, 0 or negative for no timeout + * @param tunit the unit for the <code>timeout</code>, + * may be <code>null</code> only if there is no timeout + * + * @return pool entry holding a connection for the route + * + * @throws ConnectionPoolTimeoutException + * if the timeout expired + * @throws InterruptedException + * if the calling thread was interrupted + */ + public final + BasicPoolEntry getEntry( + final HttpRoute route, + final Object state, + final long timeout, + final TimeUnit tunit) + throws ConnectionPoolTimeoutException, InterruptedException { + return requestPoolEntry(route, state).getPoolEntry(timeout, tunit); + } + + /** + * Returns a new {@link PoolEntryRequest}, from which a {@link BasicPoolEntry} + * can be obtained, or the request can be aborted. + */ + public abstract PoolEntryRequest requestPoolEntry(HttpRoute route, Object state); + + + /** + * Returns an entry into the pool. + * The connection of the entry is expected to be in a suitable state, + * either open and re-usable, or closed. The pool will not make any + * attempt to determine whether it can be re-used or not. + * + * @param entry the entry for the connection to release + * @param reusable <code>true</code> if the entry is deemed + * reusable, <code>false</code> otherwise. + * @param validDuration The duration that the entry should remain free and reusable. + * @param timeUnit The unit of time the duration is measured in. + */ + public abstract void freeEntry(BasicPoolEntry entry, boolean reusable, long validDuration, TimeUnit timeUnit) + ; + + public void handleReference(final Reference<?> ref) { + } + + protected abstract void handleLostEntry(HttpRoute route); + + /** + * Closes idle connections. + * + * @param idletime the time the connections should have been idle + * in order to be closed now + * @param tunit the unit for the <code>idletime</code> + */ + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + + // idletime can be 0 or negative, no problem there + Args.notNull(tunit, "Time unit"); + + poolLock.lock(); + try { + idleConnHandler.closeIdleConnections(tunit.toMillis(idletime)); + } finally { + poolLock.unlock(); + } + } + + public void closeExpiredConnections() { + poolLock.lock(); + try { + idleConnHandler.closeExpiredConnections(); + } finally { + poolLock.unlock(); + } + } + + + /** + * Deletes all entries for closed connections. + */ + public abstract void deleteClosedConnections(); + + /** + * Shuts down this pool and all associated resources. + * Overriding methods MUST call the implementation here! + */ + public void shutdown() { + + poolLock.lock(); + try { + + if (isShutDown) { + return; + } + + // close all connections that are issued to an application + final Iterator<BasicPoolEntry> iter = leasedConnections.iterator(); + while (iter.hasNext()) { + final BasicPoolEntry entry = iter.next(); + iter.remove(); + closeConnection(entry.getConnection()); + } + idleConnHandler.removeAll(); + + isShutDown = true; + + } finally { + poolLock.unlock(); + } + } + + + /** + * Closes a connection from this pool. + * + * @param conn the connection to close, or <code>null</code> + */ + protected void closeConnection(final OperatedClientConnection conn) { + if (conn != null) { + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + } + +} // class AbstractConnPool + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntry.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntry.java new file mode 100644 index 000000000..2c5bf05ce --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntry.java @@ -0,0 +1,163 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + +import java.lang.ref.ReferenceQueue; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.impl.conn.AbstractPoolEntry; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Basic implementation of a connection pool entry. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.pool.PoolEntry} + */ +@Deprecated +public class BasicPoolEntry extends AbstractPoolEntry { + + private final long created; + + private long updated; + private final long validUntil; + private long expiry; + + public BasicPoolEntry(final ClientConnectionOperator op, + final HttpRoute route, + final ReferenceQueue<Object> queue) { + super(op, route); + Args.notNull(route, "HTTP route"); + this.created = System.currentTimeMillis(); + this.validUntil = Long.MAX_VALUE; + this.expiry = this.validUntil; + } + + /** + * Creates a new pool entry. + * + * @param op the connection operator + * @param route the planned route for the connection + */ + public BasicPoolEntry(final ClientConnectionOperator op, + final HttpRoute route) { + this(op, route, -1, TimeUnit.MILLISECONDS); + } + + /** + * Creates a new pool entry with a specified maximum lifetime. + * + * @param op the connection operator + * @param route the planned route for the connection + * @param connTTL maximum lifetime of this entry, <=0 implies "infinity" + * @param timeunit TimeUnit of connTTL + * + * @since 4.1 + */ + public BasicPoolEntry(final ClientConnectionOperator op, + final HttpRoute route, final long connTTL, final TimeUnit timeunit) { + super(op, route); + Args.notNull(route, "HTTP route"); + this.created = System.currentTimeMillis(); + if (connTTL > 0) { + this.validUntil = this.created + timeunit.toMillis(connTTL); + } else { + this.validUntil = Long.MAX_VALUE; + } + this.expiry = this.validUntil; + } + + protected final OperatedClientConnection getConnection() { + return super.connection; + } + + protected final HttpRoute getPlannedRoute() { + return super.route; + } + + protected final BasicPoolEntryRef getWeakRef() { + return null; + } + + @Override + protected void shutdownEntry() { + super.shutdownEntry(); + } + + /** + * @since 4.1 + */ + public long getCreated() { + return this.created; + } + + /** + * @since 4.1 + */ + public long getUpdated() { + return this.updated; + } + + /** + * @since 4.1 + */ + public long getExpiry() { + return this.expiry; + } + + public long getValidUntil() { + return this.validUntil; + } + + /** + * @since 4.1 + */ + public void updateExpiry(final long time, final TimeUnit timeunit) { + this.updated = System.currentTimeMillis(); + final long newExpiry; + if (time > 0) { + newExpiry = this.updated + timeunit.toMillis(time); + } else { + newExpiry = Long.MAX_VALUE; + } + this.expiry = Math.min(validUntil, newExpiry); + } + + /** + * @since 4.1 + */ + public boolean isExpired(final long now) { + return now >= this.expiry; + } + +} + + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntryRef.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntryRef.java new file mode 100644 index 000000000..202e6b102 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPoolEntryRef.java @@ -0,0 +1,76 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.util.Args; + +/** + * A weak reference to a {@link BasicPoolEntry BasicPoolEntry}. + * This reference explicitly keeps the planned route, so the connection + * can be reclaimed if it is lost to garbage collection. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public class BasicPoolEntryRef extends WeakReference<BasicPoolEntry> { + + /** The planned route of the entry. */ + private final HttpRoute route; // HttpRoute is @Immutable + + + /** + * Creates a new reference to a pool entry. + * + * @param entry the pool entry, must not be <code>null</code> + * @param queue the reference queue, or <code>null</code> + */ + public BasicPoolEntryRef(final BasicPoolEntry entry, + final ReferenceQueue<Object> queue) { + super(entry, queue); + Args.notNull(entry, "Pool entry"); + route = entry.getPlannedRoute(); + } + + + /** + * Obtain the planned route for the referenced entry. + * The planned route is still available, even if the entry is gone. + * + * @return the planned route + */ + public final HttpRoute getRoute() { + return this.route; + } + +} // class BasicPoolEntryRef + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPooledConnAdapter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPooledConnAdapter.java new file mode 100644 index 000000000..3d6433a2d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/BasicPooledConnAdapter.java @@ -0,0 +1,75 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.impl.conn.AbstractPoolEntry; +import ch.boye.httpclientandroidlib.impl.conn.AbstractPooledConnAdapter; + +/** + * A connection wrapper and callback handler. + * All connections given out by the manager are wrappers which + * can be {@link #detach detach}ed to prevent further use on release. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public class BasicPooledConnAdapter extends AbstractPooledConnAdapter { + + /** + * Creates a new adapter. + * + * @param tsccm the connection manager + * @param entry the pool entry for the connection being wrapped + */ + protected BasicPooledConnAdapter(final ThreadSafeClientConnManager tsccm, + final AbstractPoolEntry entry) { + super(tsccm, entry); + markReusable(); + } + + @Override + protected ClientConnectionManager getManager() { + // override needed only to make method visible in this package + return super.getManager(); + } + + @Override + protected AbstractPoolEntry getPoolEntry() { + // override needed only to make method visible in this package + return super.getPoolEntry(); + } + + @Override + protected void detach() { + // override needed only to make method visible in this package + super.detach(); + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ConnPoolByRoute.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ConnPoolByRoute.java new file mode 100644 index 000000000..22f6a089d --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ConnPoolByRoute.java @@ -0,0 +1,829 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.params.ConnManagerParams; +import ch.boye.httpclientandroidlib.conn.params.ConnPerRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * A connection pool that maintains connections by route. + * This class is derived from <code>MultiThreadedHttpConnectionManager</code> + * in HttpClient 3.x, see there for original authors. It implements the same + * algorithm for connection re-use and connection-per-host enforcement: + * <ul> + * <li>connections are re-used only for the exact same route</li> + * <li>connection limits are enforced per route rather than per host</li> + * </ul> + * Note that access to the pool data structures is synchronized via the + * {@link AbstractConnPool#poolLock poolLock} in the base class, + * not via <code>synchronized</code> methods. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.pool.AbstractConnPool} + */ +@Deprecated +public class ConnPoolByRoute extends AbstractConnPool { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + private final Lock poolLock; + + /** Connection operator for this pool */ + protected final ClientConnectionOperator operator; + + /** Connections per route lookup */ + protected final ConnPerRoute connPerRoute; + + /** References to issued connections */ + protected final Set<BasicPoolEntry> leasedConnections; + + /** The list of free connections */ + protected final Queue<BasicPoolEntry> freeConnections; + + /** The list of WaitingThreads waiting for a connection */ + protected final Queue<WaitingThread> waitingThreads; + + /** Map of route-specific pools */ + protected final Map<HttpRoute, RouteSpecificPool> routeToPool; + + private final long connTTL; + + private final TimeUnit connTTLTimeUnit; + + protected volatile boolean shutdown; + + protected volatile int maxTotalConnections; + + protected volatile int numConnections; + + /** + * Creates a new connection pool, managed by route. + * + * @since 4.1 + */ + public ConnPoolByRoute( + final ClientConnectionOperator operator, + final ConnPerRoute connPerRoute, + final int maxTotalConnections) { + this(operator, connPerRoute, maxTotalConnections, -1, TimeUnit.MILLISECONDS); + } + + /** + * @since 4.1 + */ + public ConnPoolByRoute( + final ClientConnectionOperator operator, + final ConnPerRoute connPerRoute, + final int maxTotalConnections, + final long connTTL, + final TimeUnit connTTLTimeUnit) { + super(); + Args.notNull(operator, "Connection operator"); + Args.notNull(connPerRoute, "Connections per route"); + this.poolLock = super.poolLock; + this.leasedConnections = super.leasedConnections; + this.operator = operator; + this.connPerRoute = connPerRoute; + this.maxTotalConnections = maxTotalConnections; + this.freeConnections = createFreeConnQueue(); + this.waitingThreads = createWaitingThreadQueue(); + this.routeToPool = createRouteToPoolMap(); + this.connTTL = connTTL; + this.connTTLTimeUnit = connTTLTimeUnit; + } + + protected Lock getLock() { + return this.poolLock; + } + + /** + * Creates a new connection pool, managed by route. + * + * @deprecated (4.1) use {@link ConnPoolByRoute#ConnPoolByRoute(ClientConnectionOperator, ConnPerRoute, int)} + */ + @Deprecated + public ConnPoolByRoute(final ClientConnectionOperator operator, final HttpParams params) { + this(operator, + ConnManagerParams.getMaxConnectionsPerRoute(params), + ConnManagerParams.getMaxTotalConnections(params)); + } + + /** + * Creates the queue for {@link #freeConnections}. + * Called once by the constructor. + * + * @return a queue + */ + protected Queue<BasicPoolEntry> createFreeConnQueue() { + return new LinkedList<BasicPoolEntry>(); + } + + /** + * Creates the queue for {@link #waitingThreads}. + * Called once by the constructor. + * + * @return a queue + */ + protected Queue<WaitingThread> createWaitingThreadQueue() { + return new LinkedList<WaitingThread>(); + } + + /** + * Creates the map for {@link #routeToPool}. + * Called once by the constructor. + * + * @return a map + */ + protected Map<HttpRoute, RouteSpecificPool> createRouteToPoolMap() { + return new HashMap<HttpRoute, RouteSpecificPool>(); + } + + + /** + * Creates a new route-specific pool. + * Called by {@link #getRoutePool} when necessary. + * + * @param route the route + * + * @return the new pool + */ + protected RouteSpecificPool newRouteSpecificPool(final HttpRoute route) { + return new RouteSpecificPool(route, this.connPerRoute); + } + + + /** + * Creates a new waiting thread. + * Called by {@link #getRoutePool} when necessary. + * + * @param cond the condition to wait for + * @param rospl the route specific pool, or <code>null</code> + * + * @return a waiting thread representation + */ + protected WaitingThread newWaitingThread(final Condition cond, + final RouteSpecificPool rospl) { + return new WaitingThread(cond, rospl); + } + + private void closeConnection(final BasicPoolEntry entry) { + final OperatedClientConnection conn = entry.getConnection(); + if (conn != null) { + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + } + } + + /** + * Get a route-specific pool of available connections. + * + * @param route the route + * @param create whether to create the pool if it doesn't exist + * + * @return the pool for the argument route, + * never <code>null</code> if <code>create</code> is <code>true</code> + */ + protected RouteSpecificPool getRoutePool(final HttpRoute route, + final boolean create) { + RouteSpecificPool rospl = null; + poolLock.lock(); + try { + + rospl = routeToPool.get(route); + if ((rospl == null) && create) { + // no pool for this route yet (or anymore) + rospl = newRouteSpecificPool(route); + routeToPool.put(route, rospl); + } + + } finally { + poolLock.unlock(); + } + + return rospl; + } + + public int getConnectionsInPool(final HttpRoute route) { + poolLock.lock(); + try { + // don't allow a pool to be created here! + final RouteSpecificPool rospl = getRoutePool(route, false); + return (rospl != null) ? rospl.getEntryCount() : 0; + + } finally { + poolLock.unlock(); + } + } + + public int getConnectionsInPool() { + poolLock.lock(); + try { + return numConnections; + } finally { + poolLock.unlock(); + } + } + + @Override + public PoolEntryRequest requestPoolEntry( + final HttpRoute route, + final Object state) { + + final WaitingThreadAborter aborter = new WaitingThreadAborter(); + + return new PoolEntryRequest() { + + public void abortRequest() { + poolLock.lock(); + try { + aborter.abort(); + } finally { + poolLock.unlock(); + } + } + + public BasicPoolEntry getPoolEntry( + final long timeout, + final TimeUnit tunit) + throws InterruptedException, ConnectionPoolTimeoutException { + return getEntryBlocking(route, state, timeout, tunit, aborter); + } + + }; + } + + /** + * Obtains a pool entry with a connection within the given timeout. + * If a {@link WaitingThread} is used to block, {@link WaitingThreadAborter#setWaitingThread(WaitingThread)} + * must be called before blocking, to allow the thread to be interrupted. + * + * @param route the route for which to get the connection + * @param timeout the timeout, 0 or negative for no timeout + * @param tunit the unit for the <code>timeout</code>, + * may be <code>null</code> only if there is no timeout + * @param aborter an object which can abort a {@link WaitingThread}. + * + * @return pool entry holding a connection for the route + * + * @throws ConnectionPoolTimeoutException + * if the timeout expired + * @throws InterruptedException + * if the calling thread was interrupted + */ + protected BasicPoolEntry getEntryBlocking( + final HttpRoute route, final Object state, + final long timeout, final TimeUnit tunit, + final WaitingThreadAborter aborter) + throws ConnectionPoolTimeoutException, InterruptedException { + + Date deadline = null; + if (timeout > 0) { + deadline = new Date + (System.currentTimeMillis() + tunit.toMillis(timeout)); + } + + BasicPoolEntry entry = null; + poolLock.lock(); + try { + + RouteSpecificPool rospl = getRoutePool(route, true); + WaitingThread waitingThread = null; + + while (entry == null) { + Asserts.check(!shutdown, "Connection pool shut down"); + + if (log.isDebugEnabled()) { + log.debug("[" + route + "] total kept alive: " + freeConnections.size() + + ", total issued: " + leasedConnections.size() + + ", total allocated: " + numConnections + " out of " + maxTotalConnections); + } + + // the cases to check for: + // - have a free connection for that route + // - allowed to create a free connection for that route + // - can delete and replace a free connection for another route + // - need to wait for one of the things above to come true + + entry = getFreeEntry(rospl, state); + if (entry != null) { + break; + } + + final boolean hasCapacity = rospl.getCapacity() > 0; + + if (log.isDebugEnabled()) { + log.debug("Available capacity: " + rospl.getCapacity() + + " out of " + rospl.getMaxEntries() + + " [" + route + "][" + state + "]"); + } + + if (hasCapacity && numConnections < maxTotalConnections) { + + entry = createEntry(rospl, operator); + + } else if (hasCapacity && !freeConnections.isEmpty()) { + + deleteLeastUsedEntry(); + // if least used entry's route was the same as rospl, + // rospl is now out of date : we preemptively refresh + rospl = getRoutePool(route, true); + entry = createEntry(rospl, operator); + + } else { + + if (log.isDebugEnabled()) { + log.debug("Need to wait for connection" + + " [" + route + "][" + state + "]"); + } + + if (waitingThread == null) { + waitingThread = + newWaitingThread(poolLock.newCondition(), rospl); + aborter.setWaitingThread(waitingThread); + } + + boolean success = false; + try { + rospl.queueThread(waitingThread); + waitingThreads.add(waitingThread); + success = waitingThread.await(deadline); + + } finally { + // In case of 'success', we were woken up by the + // connection pool and should now have a connection + // waiting for us, or else we're shutting down. + // Just continue in the loop, both cases are checked. + rospl.removeThread(waitingThread); + waitingThreads.remove(waitingThread); + } + + // check for spurious wakeup vs. timeout + if (!success && (deadline != null) && + (deadline.getTime() <= System.currentTimeMillis())) { + throw new ConnectionPoolTimeoutException + ("Timeout waiting for connection from pool"); + } + } + } // while no entry + + } finally { + poolLock.unlock(); + } + return entry; + } + + @Override + public void freeEntry(final BasicPoolEntry entry, final boolean reusable, final long validDuration, final TimeUnit timeUnit) { + + final HttpRoute route = entry.getPlannedRoute(); + if (log.isDebugEnabled()) { + log.debug("Releasing connection" + + " [" + route + "][" + entry.getState() + "]"); + } + + poolLock.lock(); + try { + if (shutdown) { + // the pool is shut down, release the + // connection's resources and get out of here + closeConnection(entry); + return; + } + + // no longer issued, we keep a hard reference now + leasedConnections.remove(entry); + + final RouteSpecificPool rospl = getRoutePool(route, true); + + if (reusable && rospl.getCapacity() >= 0) { + if (log.isDebugEnabled()) { + final String s; + if (validDuration > 0) { + s = "for " + validDuration + " " + timeUnit; + } else { + s = "indefinitely"; + } + log.debug("Pooling connection" + + " [" + route + "][" + entry.getState() + "]; keep alive " + s); + } + rospl.freeEntry(entry); + entry.updateExpiry(validDuration, timeUnit); + freeConnections.add(entry); + } else { + closeConnection(entry); + rospl.dropEntry(); + numConnections--; + } + + notifyWaitingThread(rospl); + + } finally { + poolLock.unlock(); + } + } + + /** + * If available, get a free pool entry for a route. + * + * @param rospl the route-specific pool from which to get an entry + * + * @return an available pool entry for the given route, or + * <code>null</code> if none is available + */ + protected BasicPoolEntry getFreeEntry(final RouteSpecificPool rospl, final Object state) { + + BasicPoolEntry entry = null; + poolLock.lock(); + try { + boolean done = false; + while(!done) { + + entry = rospl.allocEntry(state); + + if (entry != null) { + if (log.isDebugEnabled()) { + log.debug("Getting free connection" + + " [" + rospl.getRoute() + "][" + state + "]"); + + } + freeConnections.remove(entry); + if (entry.isExpired(System.currentTimeMillis())) { + // If the free entry isn't valid anymore, get rid of it + // and loop to find another one that might be valid. + if (log.isDebugEnabled()) { + log.debug("Closing expired free connection" + + " [" + rospl.getRoute() + "][" + state + "]"); + } + closeConnection(entry); + // We use dropEntry instead of deleteEntry because the entry + // is no longer "free" (we just allocated it), and deleteEntry + // can only be used to delete free entries. + rospl.dropEntry(); + numConnections--; + } else { + leasedConnections.add(entry); + done = true; + } + + } else { + done = true; + if (log.isDebugEnabled()) { + log.debug("No free connections" + + " [" + rospl.getRoute() + "][" + state + "]"); + } + } + } + } finally { + poolLock.unlock(); + } + return entry; + } + + + /** + * Creates a new pool entry. + * This method assumes that the new connection will be handed + * out immediately. + * + * @param rospl the route-specific pool for which to create the entry + * @param op the operator for creating a connection + * + * @return the new pool entry for a new connection + */ + protected BasicPoolEntry createEntry(final RouteSpecificPool rospl, + final ClientConnectionOperator op) { + + if (log.isDebugEnabled()) { + log.debug("Creating new connection [" + rospl.getRoute() + "]"); + } + + // the entry will create the connection when needed + final BasicPoolEntry entry = new BasicPoolEntry(op, rospl.getRoute(), connTTL, connTTLTimeUnit); + + poolLock.lock(); + try { + rospl.createdEntry(entry); + numConnections++; + leasedConnections.add(entry); + } finally { + poolLock.unlock(); + } + + return entry; + } + + + /** + * Deletes a given pool entry. + * This closes the pooled connection and removes all references, + * so that it can be GCed. + * + * <p><b>Note:</b> Does not remove the entry from the freeConnections list. + * It is assumed that the caller has already handled this step.</p> + * <!-- @@@ is that a good idea? or rather fix it? --> + * + * @param entry the pool entry for the connection to delete + */ + protected void deleteEntry(final BasicPoolEntry entry) { + + final HttpRoute route = entry.getPlannedRoute(); + + if (log.isDebugEnabled()) { + log.debug("Deleting connection" + + " [" + route + "][" + entry.getState() + "]"); + } + + poolLock.lock(); + try { + + closeConnection(entry); + + final RouteSpecificPool rospl = getRoutePool(route, true); + rospl.deleteEntry(entry); + numConnections--; + if (rospl.isUnused()) { + routeToPool.remove(route); + } + + } finally { + poolLock.unlock(); + } + } + + + /** + * Delete an old, free pool entry to make room for a new one. + * Used to replace pool entries with ones for a different route. + */ + protected void deleteLeastUsedEntry() { + poolLock.lock(); + try { + + final BasicPoolEntry entry = freeConnections.remove(); + + if (entry != null) { + deleteEntry(entry); + } else if (log.isDebugEnabled()) { + log.debug("No free connection to delete"); + } + + } finally { + poolLock.unlock(); + } + } + + @Override + protected void handleLostEntry(final HttpRoute route) { + + poolLock.lock(); + try { + + final RouteSpecificPool rospl = getRoutePool(route, true); + rospl.dropEntry(); + if (rospl.isUnused()) { + routeToPool.remove(route); + } + + numConnections--; + notifyWaitingThread(rospl); + + } finally { + poolLock.unlock(); + } + } + + /** + * Notifies a waiting thread that a connection is available. + * This will wake a thread waiting in the specific route pool, + * if there is one. + * Otherwise, a thread in the connection pool will be notified. + * + * @param rospl the pool in which to notify, or <code>null</code> + */ + protected void notifyWaitingThread(final RouteSpecificPool rospl) { + + //@@@ while this strategy provides for best connection re-use, + //@@@ is it fair? only do this if the connection is open? + // Find the thread we are going to notify. We want to ensure that + // each waiting thread is only interrupted once, so we will remove + // it from all wait queues before interrupting. + WaitingThread waitingThread = null; + + poolLock.lock(); + try { + + if ((rospl != null) && rospl.hasThread()) { + if (log.isDebugEnabled()) { + log.debug("Notifying thread waiting on pool" + + " [" + rospl.getRoute() + "]"); + } + waitingThread = rospl.nextThread(); + } else if (!waitingThreads.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("Notifying thread waiting on any pool"); + } + waitingThread = waitingThreads.remove(); + } else if (log.isDebugEnabled()) { + log.debug("Notifying no-one, there are no waiting threads"); + } + + if (waitingThread != null) { + waitingThread.wakeup(); + } + + } finally { + poolLock.unlock(); + } + } + + + @Override + public void deleteClosedConnections() { + poolLock.lock(); + try { + final Iterator<BasicPoolEntry> iter = freeConnections.iterator(); + while (iter.hasNext()) { + final BasicPoolEntry entry = iter.next(); + if (!entry.getConnection().isOpen()) { + iter.remove(); + deleteEntry(entry); + } + } + } finally { + poolLock.unlock(); + } + } + + /** + * Closes idle connections. + * + * @param idletime the time the connections should have been idle + * in order to be closed now + * @param tunit the unit for the <code>idletime</code> + */ + @Override + public void closeIdleConnections(final long idletime, final TimeUnit tunit) { + Args.notNull(tunit, "Time unit"); + final long t = idletime > 0 ? idletime : 0; + if (log.isDebugEnabled()) { + log.debug("Closing connections idle longer than " + t + " " + tunit); + } + // the latest time for which connections will be closed + final long deadline = System.currentTimeMillis() - tunit.toMillis(t); + poolLock.lock(); + try { + final Iterator<BasicPoolEntry> iter = freeConnections.iterator(); + while (iter.hasNext()) { + final BasicPoolEntry entry = iter.next(); + if (entry.getUpdated() <= deadline) { + if (log.isDebugEnabled()) { + log.debug("Closing connection last used @ " + new Date(entry.getUpdated())); + } + iter.remove(); + deleteEntry(entry); + } + } + } finally { + poolLock.unlock(); + } + } + + @Override + public void closeExpiredConnections() { + log.debug("Closing expired connections"); + final long now = System.currentTimeMillis(); + + poolLock.lock(); + try { + final Iterator<BasicPoolEntry> iter = freeConnections.iterator(); + while (iter.hasNext()) { + final BasicPoolEntry entry = iter.next(); + if (entry.isExpired(now)) { + if (log.isDebugEnabled()) { + log.debug("Closing connection expired @ " + new Date(entry.getExpiry())); + } + iter.remove(); + deleteEntry(entry); + } + } + } finally { + poolLock.unlock(); + } + } + + @Override + public void shutdown() { + poolLock.lock(); + try { + if (shutdown) { + return; + } + shutdown = true; + + // close all connections that are issued to an application + final Iterator<BasicPoolEntry> iter1 = leasedConnections.iterator(); + while (iter1.hasNext()) { + final BasicPoolEntry entry = iter1.next(); + iter1.remove(); + closeConnection(entry); + } + + // close all free connections + final Iterator<BasicPoolEntry> iter2 = freeConnections.iterator(); + while (iter2.hasNext()) { + final BasicPoolEntry entry = iter2.next(); + iter2.remove(); + + if (log.isDebugEnabled()) { + log.debug("Closing connection" + + " [" + entry.getPlannedRoute() + "][" + entry.getState() + "]"); + } + closeConnection(entry); + } + + // wake up all waiting threads + final Iterator<WaitingThread> iwth = waitingThreads.iterator(); + while (iwth.hasNext()) { + final WaitingThread waiter = iwth.next(); + iwth.remove(); + waiter.wakeup(); + } + + routeToPool.clear(); + + } finally { + poolLock.unlock(); + } + } + + /** + * since 4.1 + */ + public void setMaxTotalConnections(final int max) { + poolLock.lock(); + try { + maxTotalConnections = max; + } finally { + poolLock.unlock(); + } + } + + + /** + * since 4.1 + */ + public int getMaxTotalConnections() { + return maxTotalConnections; + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/PoolEntryRequest.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/PoolEntryRequest.java new file mode 100644 index 000000000..bf7eb147f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/PoolEntryRequest.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; + +/** + * Encapsulates a request for a {@link BasicPoolEntry}. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link java.util.concurrent.Future} + */ +@Deprecated +public interface PoolEntryRequest { + + /** + * Obtains a pool entry with a connection within the given timeout. + * If {@link #abortRequest()} is called before this completes + * an {@link InterruptedException} is thrown. + * + * @param timeout the timeout, 0 or negative for no timeout + * @param tunit the unit for the <code>timeout</code>, + * may be <code>null</code> only if there is no timeout + * + * @return pool entry holding a connection for the route + * + * @throws ConnectionPoolTimeoutException + * if the timeout expired + * @throws InterruptedException + * if the calling thread was interrupted or the request was aborted + */ + BasicPoolEntry getPoolEntry( + long timeout, + TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException; + + /** + * Aborts the active or next call to + * {@link #getPoolEntry(long, TimeUnit)}. + */ + void abortRequest(); + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/RouteSpecificPool.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/RouteSpecificPool.java new file mode 100644 index 000000000..73964c8d1 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/RouteSpecificPool.java @@ -0,0 +1,313 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.Queue; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; +import ch.boye.httpclientandroidlib.conn.params.ConnPerRoute; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; +import ch.boye.httpclientandroidlib.util.LangUtils; + + +/** + * A connection sub-pool for a specific route, used by {@link ConnPoolByRoute}. + * The methods in this class are unsynchronized. It is expected that the + * containing pool takes care of synchronization. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.pool.AbstractConnPool} + */ +@Deprecated +public class RouteSpecificPool { + + public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); + + /** The route this pool is for. */ + protected final HttpRoute route; //Immutable + + protected final int maxEntries; + + /** Connections per route */ + protected final ConnPerRoute connPerRoute; + + /** + * The list of free entries. + * This list is managed LIFO, to increase idle times and + * allow for closing connections that are not really needed. + */ + protected final LinkedList<BasicPoolEntry> freeEntries; + + /** The list of threads waiting for this pool. */ + protected final Queue<WaitingThread> waitingThreads; + + /** The number of created entries. */ + protected int numEntries; + + /** + * @deprecated (4.1) use {@link RouteSpecificPool#RouteSpecificPool(HttpRoute, ConnPerRoute)} + */ + @Deprecated + public RouteSpecificPool(final HttpRoute route, final int maxEntries) { + this.route = route; + this.maxEntries = maxEntries; + this.connPerRoute = new ConnPerRoute() { + public int getMaxForRoute(final HttpRoute route) { + return RouteSpecificPool.this.maxEntries; + } + }; + this.freeEntries = new LinkedList<BasicPoolEntry>(); + this.waitingThreads = new LinkedList<WaitingThread>(); + this.numEntries = 0; + } + + + /** + * Creates a new route-specific pool. + * + * @param route the route for which to pool + * @param connPerRoute the connections per route configuration + */ + public RouteSpecificPool(final HttpRoute route, final ConnPerRoute connPerRoute) { + this.route = route; + this.connPerRoute = connPerRoute; + this.maxEntries = connPerRoute.getMaxForRoute(route); + this.freeEntries = new LinkedList<BasicPoolEntry>(); + this.waitingThreads = new LinkedList<WaitingThread>(); + this.numEntries = 0; + } + + + /** + * Obtains the route for which this pool is specific. + * + * @return the route + */ + public final HttpRoute getRoute() { + return route; + } + + + /** + * Obtains the maximum number of entries allowed for this pool. + * + * @return the max entry number + */ + public final int getMaxEntries() { + return maxEntries; + } + + + /** + * Indicates whether this pool is unused. + * A pool is unused if there is neither an entry nor a waiting thread. + * All entries count, not only the free but also the allocated ones. + * + * @return <code>true</code> if this pool is unused, + * <code>false</code> otherwise + */ + public boolean isUnused() { + return (numEntries < 1) && waitingThreads.isEmpty(); + } + + + /** + * Return remaining capacity of this pool + * + * @return capacity + */ + public int getCapacity() { + return connPerRoute.getMaxForRoute(route) - numEntries; + } + + + /** + * Obtains the number of entries. + * This includes not only the free entries, but also those that + * have been created and are currently issued to an application. + * + * @return the number of entries for the route of this pool + */ + public final int getEntryCount() { + return numEntries; + } + + + /** + * Obtains a free entry from this pool, if one is available. + * + * @return an available pool entry, or <code>null</code> if there is none + */ + public BasicPoolEntry allocEntry(final Object state) { + if (!freeEntries.isEmpty()) { + final ListIterator<BasicPoolEntry> it = freeEntries.listIterator(freeEntries.size()); + while (it.hasPrevious()) { + final BasicPoolEntry entry = it.previous(); + if (entry.getState() == null || LangUtils.equals(state, entry.getState())) { + it.remove(); + return entry; + } + } + } + if (getCapacity() == 0 && !freeEntries.isEmpty()) { + final BasicPoolEntry entry = freeEntries.remove(); + entry.shutdownEntry(); + final OperatedClientConnection conn = entry.getConnection(); + try { + conn.close(); + } catch (final IOException ex) { + log.debug("I/O error closing connection", ex); + } + return entry; + } + return null; + } + + + /** + * Returns an allocated entry to this pool. + * + * @param entry the entry obtained from {@link #allocEntry allocEntry} + * or presented to {@link #createdEntry createdEntry} + */ + public void freeEntry(final BasicPoolEntry entry) { + if (numEntries < 1) { + throw new IllegalStateException + ("No entry created for this pool. " + route); + } + if (numEntries <= freeEntries.size()) { + throw new IllegalStateException + ("No entry allocated from this pool. " + route); + } + freeEntries.add(entry); + } + + + /** + * Indicates creation of an entry for this pool. + * The entry will <i>not</i> be added to the list of free entries, + * it is only recognized as belonging to this pool now. It can then + * be passed to {@link #freeEntry freeEntry}. + * + * @param entry the entry that was created for this pool + */ + public void createdEntry(final BasicPoolEntry entry) { + Args.check(route.equals(entry.getPlannedRoute()), "Entry not planned for this pool"); + numEntries++; + } + + + /** + * Deletes an entry from this pool. + * Only entries that are currently free in this pool can be deleted. + * Allocated entries can not be deleted. + * + * @param entry the entry to delete from this pool + * + * @return <code>true</code> if the entry was found and deleted, or + * <code>false</code> if the entry was not found + */ + public boolean deleteEntry(final BasicPoolEntry entry) { + + final boolean found = freeEntries.remove(entry); + if (found) { + numEntries--; + } + return found; + } + + + /** + * Forgets about an entry from this pool. + * This method is used to indicate that an entry + * {@link #allocEntry allocated} + * from this pool has been lost and will not be returned. + */ + public void dropEntry() { + Asserts.check(numEntries > 0, "There is no entry that could be dropped"); + numEntries--; + } + + + /** + * Adds a waiting thread. + * This pool makes no attempt to match waiting threads with pool entries. + * It is the caller's responsibility to check that there is no entry + * before adding a waiting thread. + * + * @param wt the waiting thread + */ + public void queueThread(final WaitingThread wt) { + Args.notNull(wt, "Waiting thread"); + this.waitingThreads.add(wt); + } + + + /** + * Checks whether there is a waiting thread in this pool. + * + * @return <code>true</code> if there is a waiting thread, + * <code>false</code> otherwise + */ + public boolean hasThread() { + return !this.waitingThreads.isEmpty(); + } + + + /** + * Returns the next thread in the queue. + * + * @return a waiting thread, or <code>null</code> if there is none + */ + public WaitingThread nextThread() { + return this.waitingThreads.peek(); + } + + + /** + * Removes a waiting thread, if it is queued. + * + * @param wt the waiting thread + */ + public void removeThread(final WaitingThread wt) { + if (wt == null) { + return; + } + + this.waitingThreads.remove(wt); + } + + +} // class RouteSpecificPool diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ThreadSafeClientConnManager.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ThreadSafeClientConnManager.java new file mode 100644 index 000000000..1a1ff5c37 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/ThreadSafeClientConnManager.java @@ -0,0 +1,377 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; +/* LogFactory removed by HttpClient for Android script. */ +import ch.boye.httpclientandroidlib.annotation.ThreadSafe; +import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; +import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; +import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; +import ch.boye.httpclientandroidlib.conn.ConnectionPoolTimeoutException; +import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; +import ch.boye.httpclientandroidlib.conn.params.ConnPerRouteBean; +import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; +import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; +import ch.boye.httpclientandroidlib.impl.conn.DefaultClientConnectionOperator; +import ch.boye.httpclientandroidlib.impl.conn.SchemeRegistryFactory; +import ch.boye.httpclientandroidlib.params.HttpParams; +import ch.boye.httpclientandroidlib.util.Args; +import ch.boye.httpclientandroidlib.util.Asserts; + +/** + * Manages a pool of {@link ch.boye.httpclientandroidlib.conn.OperatedClientConnection } + * and is able to service connection requests from multiple execution threads. + * Connections are pooled on a per route basis. A request for a route which + * already the manager has persistent connections for available in the pool + * will be services by leasing a connection from the pool rather than + * creating a brand new connection. + * <p> + * ThreadSafeClientConnManager maintains a maximum limit of connection on + * a per route basis and in total. Per default this implementation will + * create no more than than 2 concurrent connections per given route + * and no more 20 connections in total. For many real-world applications + * these limits may prove too constraining, especially if they use HTTP + * as a transport protocol for their services. Connection limits, however, + * can be adjusted using HTTP parameters. + * + * @since 4.0 + * + * @deprecated (4.2) use {@link ch.boye.httpclientandroidlib.impl.conn.PoolingHttpClientConnectionManager} + */ +@ThreadSafe +@Deprecated +public class ThreadSafeClientConnManager implements ClientConnectionManager { + + public HttpClientAndroidLog log; + + /** The schemes supported by this connection manager. */ + protected final SchemeRegistry schemeRegistry; // @ThreadSafe + + protected final AbstractConnPool connectionPool; + + /** The pool of connections being managed. */ + protected final ConnPoolByRoute pool; + + /** The operator for opening and updating connections. */ + protected final ClientConnectionOperator connOperator; // DefaultClientConnectionOperator is @ThreadSafe + + protected final ConnPerRouteBean connPerRoute; + + /** + * Creates a new thread safe connection manager. + * + * @param schreg the scheme registry. + */ + public ThreadSafeClientConnManager(final SchemeRegistry schreg) { + this(schreg, -1, TimeUnit.MILLISECONDS); + } + + /** + * @since 4.1 + */ + public ThreadSafeClientConnManager() { + this(SchemeRegistryFactory.createDefault()); + } + + /** + * Creates a new thread safe connection manager. + * + * @param schreg the scheme registry. + * @param connTTL max connection lifetime, <=0 implies "infinity" + * @param connTTLTimeUnit TimeUnit of connTTL + * + * @since 4.1 + */ + public ThreadSafeClientConnManager(final SchemeRegistry schreg, + final long connTTL, final TimeUnit connTTLTimeUnit) { + this(schreg, connTTL, connTTLTimeUnit, new ConnPerRouteBean()); + } + + /** + * Creates a new thread safe connection manager. + * + * @param schreg the scheme registry. + * @param connTTL max connection lifetime, <=0 implies "infinity" + * @param connTTLTimeUnit TimeUnit of connTTL + * @param connPerRoute mapping of maximum connections per route, + * provided as a dependency so it can be managed externally, e.g. + * for dynamic connection pool size management. + * + * @since 4.2 + */ + public ThreadSafeClientConnManager(final SchemeRegistry schreg, + final long connTTL, final TimeUnit connTTLTimeUnit, final ConnPerRouteBean connPerRoute) { + super(); + Args.notNull(schreg, "Scheme registry"); + this.log = new HttpClientAndroidLog(getClass()); + this.schemeRegistry = schreg; + this.connPerRoute = connPerRoute; + this.connOperator = createConnectionOperator(schreg); + this.pool = createConnectionPool(connTTL, connTTLTimeUnit) ; + this.connectionPool = this.pool; + } + + /** + * Creates a new thread safe connection manager. + * + * @param params the parameters for this manager. + * @param schreg the scheme registry. + * + * @deprecated (4.1) use {@link ThreadSafeClientConnManager#ThreadSafeClientConnManager(SchemeRegistry)} + */ + @Deprecated + public ThreadSafeClientConnManager(final HttpParams params, + final SchemeRegistry schreg) { + Args.notNull(schreg, "Scheme registry"); + this.log = new HttpClientAndroidLog(getClass()); + this.schemeRegistry = schreg; + this.connPerRoute = new ConnPerRouteBean(); + this.connOperator = createConnectionOperator(schreg); + this.pool = (ConnPoolByRoute) createConnectionPool(params) ; + this.connectionPool = this.pool; + } + + @Override + protected void finalize() throws Throwable { + try { + shutdown(); + } finally { + super.finalize(); + } + } + + /** + * Hook for creating the connection pool. + * + * @return the connection pool to use + * + * @deprecated (4.1) use #createConnectionPool(long, TimeUnit)) + */ + @Deprecated + protected AbstractConnPool createConnectionPool(final HttpParams params) { + return new ConnPoolByRoute(connOperator, params); + } + + /** + * Hook for creating the connection pool. + * + * @return the connection pool to use + * + * @since 4.1 + */ + protected ConnPoolByRoute createConnectionPool(final long connTTL, final TimeUnit connTTLTimeUnit) { + return new ConnPoolByRoute(connOperator, connPerRoute, 20, connTTL, connTTLTimeUnit); + } + + /** + * Hook for creating the connection operator. + * It is called by the constructor. + * Derived classes can override this method to change the + * instantiation of the operator. + * The default implementation here instantiates + * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. + * + * @param schreg the scheme registry. + * + * @return the connection operator to use + */ + protected ClientConnectionOperator + createConnectionOperator(final SchemeRegistry schreg) { + + return new DefaultClientConnectionOperator(schreg);// @ThreadSafe + } + + public SchemeRegistry getSchemeRegistry() { + return this.schemeRegistry; + } + + public ClientConnectionRequest requestConnection( + final HttpRoute route, + final Object state) { + + final PoolEntryRequest poolRequest = pool.requestPoolEntry( + route, state); + + return new ClientConnectionRequest() { + + public void abortRequest() { + poolRequest.abortRequest(); + } + + public ManagedClientConnection getConnection( + final long timeout, final TimeUnit tunit) throws InterruptedException, + ConnectionPoolTimeoutException { + Args.notNull(route, "Route"); + + if (log.isDebugEnabled()) { + log.debug("Get connection: " + route + ", timeout = " + timeout); + } + + final BasicPoolEntry entry = poolRequest.getPoolEntry(timeout, tunit); + return new BasicPooledConnAdapter(ThreadSafeClientConnManager.this, entry); + } + + }; + + } + + public void releaseConnection(final ManagedClientConnection conn, final long validDuration, final TimeUnit timeUnit) { + Args.check(conn instanceof BasicPooledConnAdapter, "Connection class mismatch, " + + "connection not obtained from this manager"); + final BasicPooledConnAdapter hca = (BasicPooledConnAdapter) conn; + if (hca.getPoolEntry() != null) { + Asserts.check(hca.getManager() == this, "Connection not obtained from this manager"); + } + synchronized (hca) { + final BasicPoolEntry entry = (BasicPoolEntry) hca.getPoolEntry(); + if (entry == null) { + return; + } + try { + // make sure that the response has been read completely + if (hca.isOpen() && !hca.isMarkedReusable()) { + // In MTHCM, there would be a call to + // SimpleHttpConnectionManager.finishLastResponse(conn); + // Consuming the response is handled outside in 4.0. + + // make sure this connection will not be re-used + // Shut down rather than close, we might have gotten here + // because of a shutdown trigger. + // Shutdown of the adapter also clears the tracked route. + hca.shutdown(); + } + } catch (final IOException iox) { + if (log.isDebugEnabled()) { + log.debug("Exception shutting down released connection.", + iox); + } + } finally { + final boolean reusable = hca.isMarkedReusable(); + if (log.isDebugEnabled()) { + if (reusable) { + log.debug("Released connection is reusable."); + } else { + log.debug("Released connection is not reusable."); + } + } + hca.detach(); + pool.freeEntry(entry, reusable, validDuration, timeUnit); + } + } + } + + public void shutdown() { + log.debug("Shutting down"); + pool.shutdown(); + } + + /** + * Gets the total number of pooled connections for the given route. + * This is the total number of connections that have been created and + * are still in use by this connection manager for the route. + * This value will not exceed the maximum number of connections per host. + * + * @param route the route in question + * + * @return the total number of pooled connections for that route + */ + public int getConnectionsInPool(final HttpRoute route) { + return pool.getConnectionsInPool(route); + } + + /** + * Gets the total number of pooled connections. This is the total number of + * connections that have been created and are still in use by this connection + * manager. This value will not exceed the maximum number of connections + * in total. + * + * @return the total number of pooled connections + */ + public int getConnectionsInPool() { + return pool.getConnectionsInPool(); + } + + public void closeIdleConnections(final long idleTimeout, final TimeUnit tunit) { + if (log.isDebugEnabled()) { + log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit); + } + pool.closeIdleConnections(idleTimeout, tunit); + } + + public void closeExpiredConnections() { + log.debug("Closing expired connections"); + pool.closeExpiredConnections(); + } + + /** + * since 4.1 + */ + public int getMaxTotal() { + return pool.getMaxTotalConnections(); + } + + /** + * since 4.1 + */ + public void setMaxTotal(final int max) { + pool.setMaxTotalConnections(max); + } + + /** + * @since 4.1 + */ + public int getDefaultMaxPerRoute() { + return connPerRoute.getDefaultMaxPerRoute(); + } + + /** + * @since 4.1 + */ + public void setDefaultMaxPerRoute(final int max) { + connPerRoute.setDefaultMaxPerRoute(max); + } + + /** + * @since 4.1 + */ + public int getMaxForRoute(final HttpRoute route) { + return connPerRoute.getMaxForRoute(route); + } + + /** + * @since 4.1 + */ + public void setMaxForRoute(final HttpRoute route, final int max) { + connPerRoute.setMaxForRoute(route, max); + } + +} + diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThread.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThread.java new file mode 100644 index 000000000..c3f46cacf --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThread.java @@ -0,0 +1,198 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + + +import java.util.Date; +import java.util.concurrent.locks.Condition; + +import ch.boye.httpclientandroidlib.util.Args; + +/** + * Represents a thread waiting for a connection. + * This class implements throwaway objects. It is instantiated whenever + * a thread needs to wait. Instances are not re-used, except if the + * waiting thread experiences a spurious wakeup and continues to wait. + * <br/> + * All methods assume external synchronization on the condition + * passed to the constructor. + * Instances of this class do <i>not</i> synchronize access! + * + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public class WaitingThread { + + /** The condition on which the thread is waiting. */ + private final Condition cond; + + /** The route specific pool on which the thread is waiting. */ + //@@@ replace with generic pool interface + private final RouteSpecificPool pool; + + /** The thread that is waiting for an entry. */ + private Thread waiter; + + /** True if this was interrupted. */ + private boolean aborted; + + + /** + * Creates a new entry for a waiting thread. + * + * @param cond the condition for which to wait + * @param pool the pool on which the thread will be waiting, + * or <code>null</code> + */ + public WaitingThread(final Condition cond, final RouteSpecificPool pool) { + + Args.notNull(cond, "Condition"); + + this.cond = cond; + this.pool = pool; + } + + + /** + * Obtains the condition. + * + * @return the condition on which to wait, never <code>null</code> + */ + public final Condition getCondition() { + // not synchronized + return this.cond; + } + + + /** + * Obtains the pool, if there is one. + * + * @return the pool on which a thread is or was waiting, + * or <code>null</code> + */ + public final RouteSpecificPool getPool() { + // not synchronized + return this.pool; + } + + + /** + * Obtains the thread, if there is one. + * + * @return the thread which is waiting, or <code>null</code> + */ + public final Thread getThread() { + // not synchronized + return this.waiter; + } + + + /** + * Blocks the calling thread. + * This method returns when the thread is notified or interrupted, + * if a timeout occurrs, or if there is a spurious wakeup. + * <br/> + * This method assumes external synchronization. + * + * @param deadline when to time out, or <code>null</code> for no timeout + * + * @return <code>true</code> if the condition was satisfied, + * <code>false</code> in case of a timeout. + * Typically, a call to {@link #wakeup} is used to indicate + * that the condition was satisfied. Since the condition is + * accessible outside, this cannot be guaranteed though. + * + * @throws InterruptedException if the waiting thread was interrupted + * + * @see #wakeup + */ + public boolean await(final Date deadline) + throws InterruptedException { + + // This is only a sanity check. We cannot synchronize here, + // the lock would not be released on calling cond.await() below. + if (this.waiter != null) { + throw new IllegalStateException + ("A thread is already waiting on this object." + + "\ncaller: " + Thread.currentThread() + + "\nwaiter: " + this.waiter); + } + + if (aborted) { + throw new InterruptedException("Operation interrupted"); + } + + this.waiter = Thread.currentThread(); + + boolean success = false; + try { + if (deadline != null) { + success = this.cond.awaitUntil(deadline); + } else { + this.cond.await(); + success = true; + } + if (aborted) { + throw new InterruptedException("Operation interrupted"); + } + } finally { + this.waiter = null; + } + return success; + + } // await + + + /** + * Wakes up the waiting thread. + * <br/> + * This method assumes external synchronization. + */ + public void wakeup() { + + // If external synchronization and pooling works properly, + // this cannot happen. Just a sanity check. + if (this.waiter == null) { + throw new IllegalStateException + ("Nobody waiting on this object."); + } + + // One condition might be shared by several WaitingThread instances. + // It probably isn't, but just in case: wake all, not just one. + this.cond.signalAll(); + } + + public void interrupt() { + aborted = true; + this.cond.signalAll(); + } + + +} // class WaitingThread diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThreadAborter.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThreadAborter.java new file mode 100644 index 000000000..7ad999996 --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/WaitingThreadAborter.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * 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.conn.tsccm; + +/** + * A simple class that can interrupt a {@link WaitingThread}. + * + * Must be called with the pool lock held. + * + * @since 4.0 + * + * @deprecated (4.2) do not use + */ +@Deprecated +public class WaitingThreadAborter { + + private WaitingThread waitingThread; + private boolean aborted; + + /** + * If a waiting thread has been set, interrupts it. + */ + public void abort() { + aborted = true; + + if (waitingThread != null) { + waitingThread.interrupt(); + } + + } + + /** + * Sets the waiting thread. If this has already been aborted, + * the waiting thread is immediately interrupted. + * + * @param waitingThread The thread to interrupt when aborting. + */ + public void setWaitingThread(final WaitingThread waitingThread) { + this.waitingThread = waitingThread; + if (aborted) { + waitingThread.interrupt(); + } + } + +} diff --git a/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/package-info.java b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/package-info.java new file mode 100644 index 000000000..5115d461f --- /dev/null +++ b/mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/conn/tsccm/package-info.java @@ -0,0 +1,33 @@ +/* + * ==================================================================== + * 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/>. + * + */ + +/** + * Deprecated. + * + * @deprecated (4.3) + */ +package ch.boye.httpclientandroidlib.impl.conn.tsccm; |