blob: 60d6b960460f0ac85001b8e93e71c6be9a50ab77 [file] [log] [blame]
/* GENERATED SOURCE. DO NOT MODIFY. */
/*
* 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.
*/
package com.android.okhttp;
import com.android.okhttp.internal.Internal;
import com.android.okhttp.internal.RouteDatabase;
import com.android.okhttp.internal.Util;
import com.android.okhttp.internal.http.StreamAllocation;
import com.android.okhttp.internal.io.RealConnection;
import java.lang.ref.Reference;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Manages reuse of HTTP and SPDY connections for reduced network latency. HTTP
* requests that share the same {@link com.android.okhttp.Address} may share a
* {@link Connection}. This class implements the policy of which connections to
* keep open for future use.
*
* <p>The {@link #getDefault() system-wide default} uses system properties for
* tuning parameters:
* <ul>
* <li>{@code http.keepAlive} true if HTTP and SPDY connections should be
* pooled at all. Default is true.
* <li>{@code http.maxConnections} maximum number of idle connections to
* each to keep in the pool. Default is 5.
* <li>{@code http.keepAliveDuration} Time in milliseconds to keep the
* connection alive in the pool before closing it. Default is 5 minutes.
* This property isn't used by {@code HttpURLConnection}.
* </ul>
*
* <p>The default instance <i>doesn't</i> adjust its configuration as system
* properties are changed. This assumes that the applications that set these
* parameters do so before making HTTP connections, and that this class is
* initialized lazily.
* @hide This class is not part of the Android public SDK API
*/
public final class ConnectionPool {
private static final long DEFAULT_KEEP_ALIVE_DURATION_MS = 5 * 60 * 1000; // 5 min
@android.compat.annotation.UnsupportedAppUsage
private static final ConnectionPool systemDefault;
static {
String keepAlive = System.getProperty("http.keepAlive");
String keepAliveDuration = System.getProperty("http.keepAliveDuration");
String maxIdleConnections = System.getProperty("http.maxConnections");
long keepAliveDurationMs = keepAliveDuration != null
? Long.parseLong(keepAliveDuration)
: DEFAULT_KEEP_ALIVE_DURATION_MS;
if (keepAlive != null && !Boolean.parseBoolean(keepAlive)) {
systemDefault = new ConnectionPool(0, keepAliveDurationMs);
} else if (maxIdleConnections != null) {
systemDefault = new ConnectionPool(Integer.parseInt(maxIdleConnections), keepAliveDurationMs);
} else {
systemDefault = new ConnectionPool(5, keepAliveDurationMs);
}
}
/**
* A background thread is used to cleanup expired connections. There will be, at most, a single
* thread running per connection pool. We use a thread pool executor because it can shrink to
* zero threads, permitting this pool to be garbage collected.
*/
private final Executor executor = new ThreadPoolExecutor(
0 /* corePoolSize */, 1 /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));
/** The maximum number of idle connections for each address. */
@android.compat.annotation.UnsupportedAppUsage
private final int maxIdleConnections;
@android.compat.annotation.UnsupportedAppUsage
private final long keepAliveDurationNs;
private Runnable cleanupRunnable = new Runnable() {
@Override public void run() {
while (true) {
long waitNanos = cleanup(System.nanoTime());
if (waitNanos == -1) return;
if (waitNanos > 0) {
long waitMillis = waitNanos / 1000000L;
waitNanos -= (waitMillis * 1000000L);
synchronized (ConnectionPool.this) {
try {
ConnectionPool.this.wait(waitMillis, (int) waitNanos);
} catch (InterruptedException ignored) {
}
}
}
}
}
};
@android.compat.annotation.UnsupportedAppUsage
private final Deque<RealConnection> connections = new ArrayDeque<>();
final RouteDatabase routeDatabase = new RouteDatabase();
public ConnectionPool(int maxIdleConnections, long keepAliveDurationMs) {
this(maxIdleConnections, keepAliveDurationMs, TimeUnit.MILLISECONDS);
}
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
this.maxIdleConnections = maxIdleConnections;
this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);
// Put a floor on the keep alive duration, otherwise cleanup will spin loop.
if (keepAliveDuration <= 0) {
throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
}
}
public static ConnectionPool getDefault() {
return systemDefault;
}
/** Returns the number of idle connections in the pool. */
public synchronized int getIdleConnectionCount() {
int total = 0;
for (RealConnection connection : connections) {
if (connection.allocations.isEmpty()) total++;
}
return total;
}
/**
* Returns total number of connections in the pool. Note that prior to OkHttp 2.7 this included
* only idle connections and SPDY connections. In OkHttp 2.7 this includes all connections, both
* active and inactive. Use {@link #getIdleConnectionCount()} to count connections not currently
* in use.
*/
public synchronized int getConnectionCount() {
return connections.size();
}
/** @deprecated Use {@link #getMultiplexedConnectionCount()}. */
@Deprecated
public synchronized int getSpdyConnectionCount() {
return getMultiplexedConnectionCount();
}
/** Returns total number of multiplexed connections in the pool. */
public synchronized int getMultiplexedConnectionCount() {
int total = 0;
for (RealConnection connection : connections) {
if (connection.isMultiplexed()) total++;
}
return total;
}
/** Returns total number of http connections in the pool. */
public synchronized int getHttpConnectionCount() {
return connections.size() - getMultiplexedConnectionCount();
}
/** Returns a recycled connection to {@code address}, or null if no such connection exists. */
RealConnection get(Address address, StreamAllocation streamAllocation) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
// TODO(jwilson): this is awkward. We're already holding a lock on 'this', and
// connection.allocationLimit() may also lock the FramedConnection.
if (connection.allocations.size() < connection.allocationLimit()
&& address.equals(connection.getRoute().address)
&& !connection.noNewStreams) {
streamAllocation.acquire(connection);
return connection;
}
}
return null;
}
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
if (connections.isEmpty()) {
executor.execute(cleanupRunnable);
}
connections.add(connection);
}
/**
* Notify this pool that {@code connection} has become idle. Returns true if the connection
* has been removed from the pool and should be closed.
*/
boolean connectionBecameIdle(RealConnection connection) {
assert (Thread.holdsLock(this));
if (connection.noNewStreams || maxIdleConnections == 0) {
connections.remove(connection);
return true;
} else {
notifyAll(); // Awake the cleanup thread: we may have exceeded the idle connection limit.
return false;
}
}
/** Close and remove all idle connections in the pool. */
public void evictAll() {
List<RealConnection> evictedConnections = new ArrayList<>();
synchronized (this) {
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();
if (connection.allocations.isEmpty()) {
connection.noNewStreams = true;
evictedConnections.add(connection);
i.remove();
}
}
}
for (RealConnection connection : evictedConnections) {
Util.closeQuietly(connection.getSocket());
}
}
/**
* Performs maintenance on this pool, evicting the connection that has been idle the longest if
* either it has exceeded the keep alive limit or the idle connections limit.
*
* <p>Returns the duration in nanos to sleep until the next scheduled call to this method.
* Returns -1 if no further cleanups are required.
*/
long cleanup(long now) {
int inUseConnectionCount = 0;
int idleConnectionCount = 0;
RealConnection longestIdleConnection = null;
long longestIdleDurationNs = Long.MIN_VALUE;
// Find either a connection to evict, or the time that the next eviction is due.
synchronized (this) {
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();
// If the connection is in use, keep searching.
if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++;
continue;
}
idleConnectionCount++;
// If the connection is ready to be evicted, we're done.
long idleDurationNs = now - connection.idleAtNanos;
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
}
}
if (longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections) {
// We've found a connection to evict. Remove it from the list, then close it below (outside
// of the synchronized block).
connections.remove(longestIdleConnection);
} else if (idleConnectionCount > 0) {
// A connection will be ready to evict soon.
return keepAliveDurationNs - longestIdleDurationNs;
} else if (inUseConnectionCount > 0) {
// All connections are in use. It'll be at least the keep alive duration 'til we run again.
return keepAliveDurationNs;
} else {
// No connections, idle or in use.
return -1;
}
}
Util.closeQuietly(longestIdleConnection.getSocket());
// Cleanup again immediately.
return 0;
}
/**
* Prunes any leaked allocations and then returns the number of remaining live allocations on
* {@code connection}. Allocations are leaked if the connection is tracking them but the
* application code has abandoned them. Leak detection is imprecise and relies on garbage
* collection.
*/
private int pruneAndGetAllocationCount(RealConnection connection, long now) {
List<Reference<StreamAllocation>> references = connection.allocations;
for (int i = 0; i < references.size(); ) {
Reference<StreamAllocation> reference = references.get(i);
if (reference.get() != null) {
i++;
continue;
}
// Android-removed: Drop warning about a leak that may not be the app's fault.
// We can't tell here whether the app accessed the response body (InputStream) or
// only the header fields; at least in the latter case, the app has done nothing
// wrong so we shouldn't warn. http://b/64789755
// // We've discovered a leaked allocation. This is an application bug.
// Internal.logger.warning("A connection to " + connection.getRoute().getAddress().url()
// + " was leaked. Did you forget to close a response body?");
references.remove(i);
connection.noNewStreams = true;
// If this was the last allocation, the connection is eligible for immediate eviction.
if (references.isEmpty()) {
connection.idleAtNanos = now - keepAliveDurationNs;
return 0;
}
}
return references.size();
}
void setCleanupRunnableForTest(Runnable cleanupRunnable) {
this.cleanupRunnable = cleanupRunnable;
}
}