Merge "Remove test runner tool vogar from Froyo." into froyo
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java
index 55d762d..905d9ab 100644
--- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java
+++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java
@@ -30,7 +30,7 @@
  * and host name (<code>String</code>) or all three,  depending on whether or not a 
  * <code>Proxy</code> is used and the type of <code>Proxy</code> it is.
  * 
- * <code>HttpConfiguration</code> is used as a key by <code>HttpConnectionManager</code>
+ * <code>HttpConfiguration</code> is used as a key by <code>HttpConnectionPool</code>
  * to retrieve <code>HttpConnection</code>s from its connection pool.
  */
 public class HttpConfiguration {
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java
deleted file mode 100644
index 61d7cbe..0000000
--- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- *  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 org.apache.harmony.luni.internal.net.www.protocol.http;
-
-import java.io.IOException;
-import java.net.Proxy;
-import java.net.URI;
-import java.security.AccessController;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.harmony.luni.util.PriviAction;
-
-/**
- * <code>HttpConnectionManager</code> manages a pool of <code>HttpConnection</code>s
- * that are not currently in use and is used to get hold of persistent <code>HttpConnection</code>s.  
- * Clients should return an <code>HttpConnection</code> to the pool after use by calling 
- * <code>returnConnectionToPool</code>
- * 
- * Two system properties affect the behaviour of this class - <code>http.maxConnections</code>
- * and <code>http.keepAlive</code>.  <code>http.keepAlive</code> determines whether 
- * or not connections should be persisted and <code>http.maxConnections</code> 
- * determines the maximum number of connections to each individual host that 
- * should be kept in the pool.
- */
-public class HttpConnectionManager {
-
-    // The maximum number of connections to any location
-    private static int maxConnections = 5;
-
-    // Keeps connections alive if true
-    private static boolean keepAlive = true;
-
-    private static HttpConnectionManager defaultConnectionManager;
-    private ConnectionPool pool = new ConnectionPool();
-
-    /**
-     * Returns the default connection manager
-     */
-    public static HttpConnectionManager getDefault() {
-        if(defaultConnectionManager == null) {
-            defaultConnectionManager = new HttpConnectionManager();
-        }
-        return defaultConnectionManager;
-    }
-
-    public HttpConnection getConnection(URI uri, int connectTimeout) throws IOException {
-        checkSystemProperties();
-        HttpConfiguration config = new HttpConfiguration(uri);
-        return pool.getHttpConnection(config, connectTimeout);
-    }
-
-    public HttpConnection getConnection(URI uri, Proxy proxy, int connectTimeout) throws IOException {
-        checkSystemProperties();
-        HttpConfiguration config = new HttpConfiguration(uri, proxy);
-        return pool.getHttpConnection(config, connectTimeout);
-    }
-
-    public void returnConnectionToPool(HttpConnection connection) {
-        checkSystemProperties();
-        pool.returnConnection(connection);
-    }
-
-    public int numFreeConnections() {
-        return pool.numFreeConnections();
-    }
-
-    private void checkSystemProperties() {
-        String httpMaxConnections =  AccessController.doPrivileged(new PriviAction<String>("http.maxConnections"));
-        String httpKeepAlive = AccessController.doPrivileged(new PriviAction<String>("http.keepAlive"));
-        if(httpMaxConnections != null) {
-            maxConnections = Integer.parseInt(httpMaxConnections);
-        }
-        if(httpKeepAlive != null) {
-            keepAlive = Boolean.parseBoolean(httpKeepAlive);
-            if(!keepAlive) {
-                pool.clear();
-            }
-        }
-    }
-
-    private static class ConnectionPool {
-
-        private Map<HttpConfiguration, List<HttpConnection>> freeConnectionMap = new HashMap<HttpConfiguration, List<HttpConnection>>(); // Map of free Sockets
-
-        public synchronized void clear() {
-            for (Iterator<List<HttpConnection>> iter = freeConnectionMap.values().iterator(); iter.hasNext();) {
-                List<HttpConnection> connections = iter.next();
-                for (Iterator<HttpConnection> iterator = connections.iterator(); iterator.hasNext();) {
-                    HttpConnection connection = iterator.next();
-                    connection.closeSocketAndStreams();
-                }
-            }
-            freeConnectionMap.clear();
-        }
-
-        public synchronized void returnConnection(HttpConnection connection) {
-            // BEGIN android-note
-            // Simplified the following test.
-            // END android-note
-            if(keepAlive && connection.isEligibleForRecycling()) {
-                HttpConfiguration config = connection.getHttpConfiguration();
-                List<HttpConnection> connections = freeConnectionMap.get(config);
-                if(connections == null) {
-                    connections = new ArrayList<HttpConnection>();
-                    freeConnectionMap.put(config, connections);
-                }
-                if(connections.size() < HttpConnectionManager.maxConnections) {
-                    if(!connections.contains(connection)) {
-                        connections.add(connection);
-                    }
-                } else {
-                    connection.closeSocketAndStreams();
-                }
-            } else {
-                // Make sure all streams are closed etc.
-                connection.closeSocketAndStreams();
-            }
-        }
-
-        public synchronized HttpConnection getHttpConnection(HttpConfiguration config, int connectTimeout) throws IOException {
-            List<HttpConnection> connections = freeConnectionMap.get(config);
-            if(keepAlive && connections == null) {
-                connections = new ArrayList<HttpConnection>();
-                freeConnectionMap.put(config, connections);
-            }
-            if(!keepAlive || connections.isEmpty()) {
-                HttpConnection connection = new HttpConnection(config, connectTimeout);
-                return connection;
-            } else {
-                HttpConnection connection = connections.get(0);
-                connections.remove(0);
-                if(!connection.isStale()) {
-                    SecurityManager security = System.getSecurityManager();
-                    if (security != null) {
-                        security.checkConnect(connection.getSocket().getInetAddress().getHostName(), connection.getSocket().getPort());
-                    }
-                    return connection;                 
-                } else {
-                    return getHttpConnection(config, connectTimeout);
-                }
-            }
-        }
-
-        public int numFreeConnections() {
-            int numFree = 0;
-            for (Iterator<List<HttpConnection>> iter = freeConnectionMap.values().iterator(); iter.hasNext();) {
-                List<HttpConnection> connections = iter.next();
-                numFree += connections.size();
-            }
-            return numFree;
-        }
-    }
-
-    public void reset() {
-        pool.clear();
-    }
-
-}
\ No newline at end of file
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionPool.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionPool.java
new file mode 100644
index 0000000..c3a997b
--- /dev/null
+++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionPool.java
@@ -0,0 +1,110 @@
+/*
+ *  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 org.apache.harmony.luni.internal.net.www.protocol.http;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A pool of HTTP connections. This class exposes its tuning parameters as
+ * system properties:
+ * <ul>
+ *   <li>{@code http.keepAlive} true if HTTP connections should be pooled at
+ *       all. Default is true.
+ *   <li>{@code http.maxConnections} maximum number of connections to each URI.
+ *       Default is 5.
+ * </ul>
+ *
+ * <p>This class <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.
+ *
+ * <p>If a security manager is in place, HTTP connection pooling will be
+ * disabled and these system properties will be ignored.
+ */
+public final class HttpConnectionPool {
+
+    public static final HttpConnectionPool INSTANCE = new HttpConnectionPool();
+
+    private final int maxConnections;
+    private final HashMap<HttpConfiguration, List<HttpConnection>> connectionPool
+            = new HashMap<HttpConfiguration, List<HttpConnection>>();
+
+    private HttpConnectionPool() {
+        SecurityManager security = System.getSecurityManager();
+        if (security != null) {
+            maxConnections = 0;
+            return;
+        }
+
+        String keepAlive = System.getProperty("http.keepAlive");
+        if (keepAlive != null && !Boolean.parseBoolean(keepAlive)) {
+            maxConnections = 0;
+            return;
+        }
+
+        String maxConnectionsString = System.getProperty("http.maxConnections");
+        this.maxConnections = maxConnectionsString != null
+                ? Integer.parseInt(maxConnectionsString)
+                : 5;
+    }
+
+    public HttpConnection get(HttpConfiguration config, int connectTimeout) throws IOException {
+        // First try to reuse an existing HTTP connection.
+        synchronized (connectionPool) {
+            List<HttpConnection> connections = connectionPool.get(config);
+            if (connections != null) {
+                while (!connections.isEmpty()) {
+                    HttpConnection connection = connections.remove(connections.size() - 1);
+                    if (!connection.isStale()) {
+                        return connection;
+                    }
+                }
+                connectionPool.remove(config);
+            }
+        }
+
+        /*
+         * We couldn't find a reusable connection, so we need to create a new
+         * connection. We're careful not to do so while holding a lock!
+         */
+        return new HttpConnection(config, connectTimeout);
+    }
+
+    public void recycle(HttpConnection connection) {
+        if (maxConnections > 0 && connection.isEligibleForRecycling()) {
+            HttpConfiguration config = connection.getHttpConfiguration();
+            synchronized (connectionPool) {
+                List<HttpConnection> connections = connectionPool.get(config);
+                if (connections == null) {
+                    connections = new ArrayList<HttpConnection>();
+                    connectionPool.put(config, connections);
+                }
+                if (connections.size() < maxConnections) {
+                    connections.add(connection);
+                    return; // keep the connection open
+                }
+            }
+        }
+
+        // don't close streams while holding a lock!
+        connection.closeSocketAndStreams();
+    }
+}
\ No newline at end of file
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnectionImpl.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnectionImpl.java
index 687a4f3..c8c9600 100644
--- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnectionImpl.java
+++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnectionImpl.java
@@ -917,14 +917,14 @@
      * Returns connected socket to be used for this HTTP connection.
      */
     protected HttpConnection getHTTPConnection(Proxy proxy) throws IOException {
-        HttpConnection connection;
+        HttpConfiguration configuration;
         if (proxy == null || proxy.type() == Proxy.Type.DIRECT) {
-          this.proxy = null; // not using proxy
-          connection = HttpConnectionManager.getDefault().getConnection(uri, getConnectTimeout());
+            this.proxy = null; // not using proxy
+            configuration = new HttpConfiguration(uri);
         } else {
-            connection = HttpConnectionManager.getDefault().getConnection(uri, proxy, getConnectTimeout());
+            configuration = new HttpConfiguration(uri, proxy);
         }
-        return connection;
+        return HttpConnectionPool.INSTANCE.get(configuration, getConnectTimeout());
     }
 
     /**
@@ -1000,8 +1000,7 @@
                  */
                 connection.closeSocketAndStreams();
             } else {
-                HttpConnectionManager.getDefault().returnConnectionToPool(
-                        connection);
+                HttpConnectionPool.INSTANCE.recycle(connection);
             }
             connection = null;
         }
diff --git a/libcore/luni/src/test/java/java/net/URLConnectionTest.java b/libcore/luni/src/test/java/java/net/URLConnectionTest.java
index bb517cc..263c961 100644
--- a/libcore/luni/src/test/java/java/net/URLConnectionTest.java
+++ b/libcore/luni/src/test/java/java/net/URLConnectionTest.java
@@ -16,9 +16,7 @@
 
 package java.net;
 
-import java.net.*;
 import java.io.BufferedReader;
-import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.util.Arrays;
@@ -60,6 +58,14 @@
         mServer.setMaxChunkSize(8);
         assertTrue(readFirstLine().equals("<html>"));
         assertTrue(readFirstLine().equals("<html>"));
+        assertEquals(1, mServer.getNumAcceptedConnections());
+    }
+
+    public void testConnectionsArePooled() throws Exception {
+        readFirstLine();
+        readFirstLine();
+        readFirstLine();
+        assertEquals(1, mServer.getNumAcceptedConnections());
     }
 
     enum UploadKind { CHUNKED, FIXED_LENGTH };
diff --git a/libcore/support/src/test/java/tests/support/Support_TestWebServer.java b/libcore/support/src/test/java/tests/support/Support_TestWebServer.java
index 7d20237..3d6b2b1 100644
--- a/libcore/support/src/test/java/tests/support/Support_TestWebServer.java
+++ b/libcore/support/src/test/java/tests/support/Support_TestWebServer.java
@@ -226,6 +226,10 @@
         return pathToRequest;
     }
 
+    public int getNumAcceptedConnections() {
+        return acceptedConnections;
+    }
+
     /**
      * Cause the thread accepting connections on the server socket to close
      */
@@ -274,23 +278,19 @@
          */
         public synchronized void run() {
             running = true;
-            try {
-                while (running) {
-                    // Log.d(LOGTAG, "TestWebServer run() calling accept()");
+            while (running) {
+                try {
                     Socket s = ss.accept();
                     acceptedConnections++;
                     if (acceptedConnections >= acceptLimit) {
                         running = false;
                     }
-
                     new Thread(new Worker(s), "additional worker").start();
+                } catch (SocketException e) {
+                    log(e.getMessage());
+                } catch (IOException e) {
+                    log(e.getMessage());
                 }
-            } catch (SocketException e) {
-                log("SocketException in AcceptThread: probably closed during accept");
-                running = false;
-            } catch (IOException e) {
-                log("IOException in AcceptThread");
-                running = false;
             }
             log("AcceptThread terminated" + this);
         }
diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
index 8006757..83e0b63 100644
--- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
+++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
@@ -991,75 +991,27 @@
     private native void nativefree();
 
     protected void finalize() throws IOException {
+        /*
+         * Just worry about our own state. Notably we do not try and
+         * close anything. The SocketImpl, either our own
+         * PlainSocketImpl, or the Socket we are wrapping, will do
+         * that. This might mean we do not properly SSL_shutdown, but
+         * if you want to do that, properly close the socket yourself.
+         *
+         * The reason why we don't try to SSL_shutdown is that there
+         * can be a race between finalizers where the PlainSocketImpl
+         * finalizer runs first and closes the socket. However, in the
+         * meanwhile, the underlying file descriptor could be reused
+         * for another purpose. If we call SSL_shutdown after that, the
+         * underlying socket BIOs still have the older file descriptor
+         * and will write the close notify to some unsuspecting
+         * reader.
+         */
         updateInstanceCount(-1);
-
         if (ssl == 0) {
-            /*
-             * It's already been closed, so there's no need to do anything
-             * more at this point.
-             */
             return;
         }
-
-        // Note the underlying socket up-front, for possible later use.
-        Socket underlyingSocket = socket;
-
-        // Fire up a thread to (hopefully) do all the real work.
-        Finalizer f = new Finalizer();
-        f.setDaemon(true);
-        f.start();
-
-        /*
-         * Give the finalizer thread one second to run. If it fails to
-         * terminate in that time, interrupt it (which may help if it
-         * is blocked on an interruptible I/O operation), make a note
-         * in the log, and go ahead and close the underlying socket if
-         * possible.
-         */
-        try {
-            f.join(1000);
-        } catch (InterruptedException ex) {
-            // Reassert interrupted status.
-            Thread.currentThread().interrupt();
-        }
-
-        if (f.isAlive()) {
-            f.interrupt();
-            Logger.global.log(Level.SEVERE,
-                    "Slow finalization of SSL socket (" + this + ", for " +
-                    underlyingSocket + ")");
-            if ((underlyingSocket != null) && !underlyingSocket.isClosed()) {
-                underlyingSocket.close();
-            }
-        }
-    }
-
-    /**
-     * Helper class for a thread that knows how to call
-     * {@link OpenSSLSocketImpl#close} on behalf of instances being finalized,
-     * since that call can take arbitrarily long (e.g., due to a slow network),
-     * and an overly long-running finalizer will cause the process to be
-     * totally aborted.
-     */
-    private class Finalizer extends Thread {
-        public void run() {
-            Socket underlyingSocket = socket; // for error reporting
-            try {
-                close();
-            } catch (IOException ex) {
-                /*
-                 * Clear interrupted status, so that the Logger call
-                 * immediately below won't get spuriously interrupted.
-                 */
-                Thread.interrupted();
-
-                Logger.global.log(Level.SEVERE,
-                        "Trouble finalizing SSL socket (" +
-                        OpenSSLSocketImpl.this + ", for " + underlyingSocket +
-                        ")",
-                        ex);
-            }
-        }
+        nativefree();
     }
 
     /**