package jdk.incubator.http;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Objects;
import jdk.incubator.http.internal.common.Utils;
final class ConnectionPool {
static final long KEEP_ALIVE = Utils.getIntegerNetProperty(
"jdk.httpclient.keepalive.timeout", 1200);
final HashMap<CacheKey,LinkedList<HttpConnection>> plainPool;
final HashMap<CacheKey,LinkedList<HttpConnection>> sslPool;
CacheCleaner cleaner;
static class CacheKey {
final InetSocketAddress proxy;
final InetSocketAddress destination;
CacheKey(InetSocketAddress destination, InetSocketAddress proxy) {
this.proxy = proxy;
this.destination = destination;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CacheKey other = (CacheKey) obj;
if (!Objects.equals(this.proxy, other.proxy)) {
return false;
}
if (!Objects.equals(this.destination, other.destination)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return Objects.hash(proxy, destination);
}
}
static class ExpiryEntry {
final HttpConnection connection;
final long expiry;
ExpiryEntry(HttpConnection connection, long expiry) {
this.connection = connection;
this.expiry = expiry;
}
}
final LinkedList<ExpiryEntry> expiryList;
ConnectionPool() {
plainPool = new HashMap<>();
sslPool = new HashMap<>();
expiryList = new LinkedList<>();
}
void start() {
}
static CacheKey cacheKey(InetSocketAddress destination,
InetSocketAddress proxy)
{
return new CacheKey(destination, proxy);
}
synchronized HttpConnection getConnection(boolean secure,
InetSocketAddress addr,
InetSocketAddress proxy) {
CacheKey key = new CacheKey(addr, proxy);
HttpConnection c = secure ? findConnection(key, sslPool)
: findConnection(key, plainPool);
return c;
}
synchronized void returnToPool(HttpConnection conn) {
if (conn instanceof PlainHttpConnection) {
putConnection(conn, plainPool);
} else {
putConnection(conn, sslPool);
}
addToExpiryList(conn);
}
private HttpConnection
findConnection(CacheKey key,
HashMap<CacheKey,LinkedList<HttpConnection>> pool) {
LinkedList<HttpConnection> l = pool.get(key);
if (l == null || l.size() == 0) {
return null;
} else {
HttpConnection c = l.removeFirst();
removeFromExpiryList(c);
return c;
}
}
private void
removeFromPool(HttpConnection c,
HashMap<CacheKey,LinkedList<HttpConnection>> pool) {
LinkedList<HttpConnection> l = pool.get(c.cacheKey());
assert l != null;
boolean wasPresent = l.remove(c);
assert wasPresent;
}
private void
putConnection(HttpConnection c,
HashMap<CacheKey,LinkedList<HttpConnection>> pool) {
CacheKey key = c.cacheKey();
LinkedList<HttpConnection> l = pool.get(key);
if (l == null) {
l = new LinkedList<>();
pool.put(key, l);
}
l.add(c);
}
final class CacheCleaner extends Thread {
volatile boolean stopping;
CacheCleaner() {
super(null, null, "HTTP-Cache-cleaner", 0, false);
setDaemon(true);
}
synchronized boolean stopping() {
return stopping;
}
synchronized void stopCleaner() {
stopping = true;
}
@Override
public void run() {
while (!stopping()) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {}
cleanCache();
}
}
}
synchronized void removeFromExpiryList(HttpConnection c) {
if (c == null) {
return;
}
ListIterator<ExpiryEntry> li = expiryList.listIterator();
while (li.hasNext()) {
ExpiryEntry e = li.next();
if (e.connection.equals(c)) {
li.remove();
return;
}
}
if (expiryList.isEmpty()) {
cleaner.stopCleaner();
cleaner = null;
}
}
private void cleanCache() {
long now = System.currentTimeMillis() / 1000;
LinkedList<HttpConnection> closelist = new LinkedList<>();
synchronized (this) {
ListIterator<ExpiryEntry> li = expiryList.listIterator();
while (li.hasNext()) {
ExpiryEntry entry = li.next();
if (entry.expiry <= now) {
li.remove();
HttpConnection c = entry.connection;
closelist.add(c);
if (c instanceof PlainHttpConnection) {
removeFromPool(c, plainPool);
} else {
removeFromPool(c, sslPool);
}
}
}
}
for (HttpConnection c : closelist) {
c.close();
}
}
private synchronized void addToExpiryList(HttpConnection conn) {
long now = System.currentTimeMillis() / 1000;
long then = now + KEEP_ALIVE;
if (expiryList.isEmpty()) {
cleaner = new CacheCleaner();
cleaner.start();
}
ListIterator<ExpiryEntry> li = expiryList.listIterator();
while (li.hasNext()) {
ExpiryEntry entry = li.next();
if (then > entry.expiry) {
li.previous();
li.add(new ExpiryEntry(conn, then));
return;
}
}
expiryList.add(new ExpiryEntry(conn, then));
}
}