< prev index next >

src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java

Print this page
rev 47358 : 8155590: Dubious collection management in sun.net.www.http.KeepAliveCache

*** 1,7 **** /* ! * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this
*** 25,38 **** package sun.net.www.http; import java.io.IOException; import java.io.NotSerializableException; import java.util.ArrayList; import java.util.HashMap; ! import java.net.URL; import jdk.internal.misc.InnocuousThread; /** * A class that implements a cache of idle Http connections for keep-alive * * @author Stephen R. Pietrowicz (NCSA) --- 25,46 ---- package sun.net.www.http; import java.io.IOException; import java.io.NotSerializableException; + import java.io.ObjectInputStream; + import java.io.ObjectOutputStream; + import java.net.URL; + import java.security.AccessController; + import java.security.PrivilegedAction; + import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; ! import java.util.List; ! import jdk.internal.misc.InnocuousThread; + import sun.security.action.GetIntegerAction; /** * A class that implements a cache of idle Http connections for keep-alive * * @author Stephen R. Pietrowicz (NCSA)
*** 51,68 **** */ static final int MAX_CONNECTIONS = 5; static int result = -1; static int getMaxConnections() { if (result == -1) { ! result = java.security.AccessController.doPrivileged( ! new sun.security.action.GetIntegerAction("http.maxConnections", ! MAX_CONNECTIONS)) .intValue(); ! if (result <= 0) result = MAX_CONNECTIONS; } ! return result; } static final int LIFETIME = 5000; private Thread keepAliveTimer = null; --- 59,76 ---- */ static final int MAX_CONNECTIONS = 5; static int result = -1; static int getMaxConnections() { if (result == -1) { ! result = AccessController.doPrivileged( ! new GetIntegerAction("http.maxConnections", MAX_CONNECTIONS)) .intValue(); ! if (result <= 0) { result = MAX_CONNECTIONS; + } } ! return result; } static final int LIFETIME = 5000; private Thread keepAliveTimer = null;
*** 91,102 **** * to a server that sent me a keep-alive * time of 15 sec, the proxy unilaterally terminates my connection * The robustness to get around this is in HttpClient.parseHTTP() */ final KeepAliveCache cache = this; ! java.security.AccessController.doPrivileged( ! new java.security.PrivilegedAction<>() { public Void run() { keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache); keepAliveTimer.setDaemon(true); keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2); keepAliveTimer.start(); --- 99,109 ---- * to a server that sent me a keep-alive * time of 15 sec, the proxy unilaterally terminates my connection * The robustness to get around this is in HttpClient.parseHTTP() */ final KeepAliveCache cache = this; ! AccessController.doPrivileged(new PrivilegedAction<>() { public Void run() { keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache); keepAliveTimer.setDaemon(true); keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2); keepAliveTimer.start();
*** 108,133 **** KeepAliveKey key = new KeepAliveKey(url, obj); ClientVector v = super.get(key); if (v == null) { int keepAliveTimeout = http.getKeepAliveTimeout(); ! v = new ClientVector(keepAliveTimeout > 0? ! keepAliveTimeout*1000 : LIFETIME); v.put(http); super.put(key, v); } else { v.put(http); } } /* remove an obsolete HttpClient from its VectorCache */ ! public synchronized void remove (HttpClient h, Object obj) { KeepAliveKey key = new KeepAliveKey(h.url, obj); ClientVector v = super.get(key); if (v != null) { v.remove(h); ! if (v.empty()) { removeVector(key); } } } --- 115,140 ---- KeepAliveKey key = new KeepAliveKey(url, obj); ClientVector v = super.get(key); if (v == null) { int keepAliveTimeout = http.getKeepAliveTimeout(); ! v = new ClientVector(keepAliveTimeout > 0 ? ! keepAliveTimeout * 1000 : LIFETIME); v.put(http); super.put(key, v); } else { v.put(http); } } /* remove an obsolete HttpClient from its VectorCache */ ! public synchronized void remove(HttpClient h, Object obj) { KeepAliveKey key = new KeepAliveKey(h.url, obj); ClientVector v = super.get(key); if (v != null) { v.remove(h); ! if (v.isEmpty()) { removeVector(key); } } }
*** 140,150 **** /** * Check to see if this URL has a cached HttpClient */ public synchronized HttpClient get(URL url, Object obj) { - KeepAliveKey key = new KeepAliveKey(url, obj); ClientVector v = super.get(key); if (v == null) { // nothing in cache yet return null; } --- 147,156 ----
*** 159,263 **** public void run() { do { try { Thread.sleep(LIFETIME); } catch (InterruptedException e) {} - synchronized (this) { - /* Remove all unused HttpClients. Starting from the - * bottom of the stack (the least-recently used first). - * REMIND: It'd be nice to not remove *all* connections - * that aren't presently in use. One could have been added - * a second ago that's still perfectly valid, and we're - * needlessly axing it. But it's not clear how to do this - * cleanly, and doing it right may be more trouble than it's - * worth. - */ long currentTime = System.currentTimeMillis(); ! ! ArrayList<KeepAliveKey> keysToRemove ! = new ArrayList<>(); for (KeepAliveKey key : keySet()) { ClientVector v = get(key); synchronized (v) { ! int i; ! ! for (i = 0; i < v.size(); i++) { ! KeepAliveEntry e = v.elementAt(i); if ((currentTime - e.idleStartTime) > v.nap) { ! HttpClient h = e.hc; ! h.closeServer(); } else { break; } } - v.subList(0, i).clear(); ! if (v.size() == 0) { keysToRemove.add(key); } } } for (KeepAliveKey key : keysToRemove) { removeVector(key); } } ! } while (size() > 0); ! ! return; } /* * Do not serialize this class! */ ! private void writeObject(java.io.ObjectOutputStream stream) ! throws IOException { throw new NotSerializableException(); } ! private void readObject(java.io.ObjectInputStream stream) ! throws IOException, ClassNotFoundException { throw new NotSerializableException(); } } /* FILO order for recycling HttpClients, should run in a thread * to time them out. If > maxConns are in use, block. */ ! ! ! class ClientVector extends java.util.Stack<KeepAliveEntry> { private static final long serialVersionUID = -8680532108106489459L; // sleep time in milliseconds, before cache clear int nap; ! ! ! ClientVector (int nap) { this.nap = nap; } synchronized HttpClient get() { ! if (empty()) { return null; - } else { - // Loop until we find a connection that has not timed out - HttpClient hc = null; - long currentTime = System.currentTimeMillis(); - do { - KeepAliveEntry e = pop(); - if ((currentTime - e.idleStartTime) > nap) { - e.hc.closeServer(); - } else { - hc = e.hc; - } - } while ((hc== null) && (!empty())); - return hc; } } /* return a still valid, unused HttpClient */ synchronized void put(HttpClient h) { if (size() >= KeepAliveCache.getMaxConnections()) { --- 165,251 ---- public void run() { do { try { Thread.sleep(LIFETIME); } catch (InterruptedException e) {} + // Remove all outdated HttpClients. + synchronized (this) { long currentTime = System.currentTimeMillis(); ! List<KeepAliveKey> keysToRemove = new ArrayList<>(); for (KeepAliveKey key : keySet()) { ClientVector v = get(key); synchronized (v) { ! KeepAliveEntry e = v.peek(); ! while (e != null) { if ((currentTime - e.idleStartTime) > v.nap) { ! v.poll(); ! e.hc.closeServer(); } else { break; } + e = v.peek(); } ! if (v.isEmpty()) { keysToRemove.add(key); } } } for (KeepAliveKey key : keysToRemove) { removeVector(key); } } ! } while (!isEmpty()); } /* * Do not serialize this class! */ ! private void writeObject(ObjectOutputStream stream) throws IOException { throw new NotSerializableException(); } ! private void readObject(ObjectInputStream stream) ! throws IOException, ClassNotFoundException ! { throw new NotSerializableException(); } } /* FILO order for recycling HttpClients, should run in a thread * to time them out. If > maxConns are in use, block. */ ! class ClientVector extends ArrayDeque<KeepAliveEntry> { private static final long serialVersionUID = -8680532108106489459L; // sleep time in milliseconds, before cache clear int nap; ! ClientVector(int nap) { this.nap = nap; } synchronized HttpClient get() { ! if (isEmpty()) { return null; } + + // Loop until we find a connection that has not timed out + HttpClient hc = null; + long currentTime = System.currentTimeMillis(); + do { + KeepAliveEntry e = pop(); + if ((currentTime - e.idleStartTime) > nap) { + e.hc.closeServer(); + } else { + hc = e.hc; + } + } while ((hc == null) && (!isEmpty())); + return hc; } /* return a still valid, unused HttpClient */ synchronized void put(HttpClient h) { if (size() >= KeepAliveCache.getMaxConnections()) {
*** 265,289 **** } else { push(new KeepAliveEntry(h, System.currentTimeMillis())); } } /* * Do not serialize this class! */ ! private void writeObject(java.io.ObjectOutputStream stream) ! throws IOException { throw new NotSerializableException(); } ! private void readObject(java.io.ObjectInputStream stream) ! throws IOException, ClassNotFoundException { throw new NotSerializableException(); } } - class KeepAliveKey { private String protocol = null; private String host = null; private int port = 0; private Object obj = null; // additional key, such as socketfactory --- 253,288 ---- } else { push(new KeepAliveEntry(h, System.currentTimeMillis())); } } + /* remove an HttpClient */ + synchronized boolean remove(HttpClient h) { + for (KeepAliveEntry curr : this) { + if (curr.hc == h) { + // as we return here, we should not see a + // ConcurrentModificationException + return super.remove(curr); + } + } + return false; + } + /* * Do not serialize this class! */ ! private void writeObject(ObjectOutputStream stream) throws IOException { throw new NotSerializableException(); } ! private void readObject(ObjectInputStream stream) ! throws IOException, ClassNotFoundException ! { throw new NotSerializableException(); } } class KeepAliveKey { private String protocol = null; private String host = null; private int port = 0; private Object obj = null; // additional key, such as socketfactory
< prev index next >