import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}
}
}

View file

@ -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();
}
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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");
}
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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());
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}

View file

@ -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;

View file

@ -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

View file

@ -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;
}
}

View file

@ -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

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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();
}
}
}

View file

@ -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;