1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18// BEGIN android-note 19// This class was copied from a newer version of harmony 20// to improve reusability of URLConnections 21// END android-note 22 23package org.apache.harmony.luni.internal.net.www.protocol.http; 24 25import java.io.IOException; 26import java.net.Proxy; 27import java.net.URI; 28import java.security.AccessController; 29import java.util.ArrayList; 30import java.util.HashMap; 31import java.util.Iterator; 32import java.util.List; 33import java.util.Map; 34 35import org.apache.harmony.luni.util.PriviAction; 36 37/** 38 * <code>HttpConnectionManager</code> manages a pool of <code>HttpConnection</code>s 39 * that are not currently in use and is used to get hold of persistent <code>HttpConnection</code>s. 40 * Clients should return an <code>HttpConnection</code> to the pool after use by calling 41 * <code>returnConnectionToPool</code> 42 * 43 * Two system properties affect the behaviour of this class - <code>http.maxConnections</code> 44 * and <code>http.keepAlive</code>. <code>http.keepAlive</code> determines whether 45 * or not connections should be persisted and <code>http.maxConnections</code> 46 * determines the maximum number of connections to each individual host that 47 * should be kept in the pool. 48 */ 49public class HttpConnectionManager { 50 51 // The maximum number of connections to any location 52 private static int maxConnections = 5; 53 54 // Keeps connections alive if true 55 private static boolean keepAlive = true; 56 57 private static HttpConnectionManager defaultConnectionManager; 58 private ConnectionPool pool = new ConnectionPool(); 59 60 /** 61 * Returns the default connection manager 62 */ 63 public static HttpConnectionManager getDefault() { 64 if(defaultConnectionManager == null) { 65 defaultConnectionManager = new HttpConnectionManager(); 66 } 67 return defaultConnectionManager; 68 } 69 70 public HttpConnection getConnection(URI uri, int connectTimeout) throws IOException { 71 checkSystemProperties(); 72 HttpConfiguration config = new HttpConfiguration(uri); 73 return pool.getHttpConnection(config, connectTimeout); 74 } 75 76 public HttpConnection getConnection(URI uri, Proxy proxy, int connectTimeout) throws IOException { 77 checkSystemProperties(); 78 HttpConfiguration config = new HttpConfiguration(uri, proxy); 79 return pool.getHttpConnection(config, connectTimeout); 80 } 81 82 public void returnConnectionToPool(HttpConnection connection) { 83 checkSystemProperties(); 84 pool.returnConnection(connection); 85 } 86 87 public int numFreeConnections() { 88 return pool.numFreeConnections(); 89 } 90 91 private void checkSystemProperties() { 92 String httpMaxConnections = AccessController.doPrivileged(new PriviAction<String>("http.maxConnections")); 93 String httpKeepAlive = AccessController.doPrivileged(new PriviAction<String>("http.keepAlive")); 94 if(httpMaxConnections != null) { 95 maxConnections = Integer.parseInt(httpMaxConnections); 96 } 97 if(httpKeepAlive != null) { 98 keepAlive = Boolean.parseBoolean(httpKeepAlive); 99 if(!keepAlive) { 100 pool.clear(); 101 } 102 } 103 } 104 105 private static class ConnectionPool { 106 107 private Map<HttpConfiguration, List<HttpConnection>> freeConnectionMap = new HashMap<HttpConfiguration, List<HttpConnection>>(); // Map of free Sockets 108 109 public synchronized void clear() { 110 for (Iterator<List<HttpConnection>> iter = freeConnectionMap.values().iterator(); iter.hasNext();) { 111 List<HttpConnection> connections = iter.next(); 112 for (Iterator<HttpConnection> iterator = connections.iterator(); iterator.hasNext();) { 113 HttpConnection connection = iterator.next(); 114 connection.closeSocketAndStreams(); 115 } 116 } 117 freeConnectionMap.clear(); 118 } 119 120 public synchronized void returnConnection(HttpConnection connection) { 121 // BEGIN android-note 122 // Simplified the following test. 123 // END android-note 124 if(keepAlive && connection.isEligibleForRecycling()) { 125 HttpConfiguration config = connection.getHttpConfiguration(); 126 List<HttpConnection> connections = freeConnectionMap.get(config); 127 if(connections == null) { 128 connections = new ArrayList<HttpConnection>(); 129 freeConnectionMap.put(config, connections); 130 } 131 if(connections.size() < HttpConnectionManager.maxConnections) { 132 if(!connections.contains(connection)) { 133 connections.add(connection); 134 } 135 } else { 136 connection.closeSocketAndStreams(); 137 } 138 } else { 139 // Make sure all streams are closed etc. 140 connection.closeSocketAndStreams(); 141 } 142 } 143 144 public synchronized HttpConnection getHttpConnection(HttpConfiguration config, int connectTimeout) throws IOException { 145 List<HttpConnection> connections = freeConnectionMap.get(config); 146 if(keepAlive && connections == null) { 147 connections = new ArrayList<HttpConnection>(); 148 freeConnectionMap.put(config, connections); 149 } 150 if(!keepAlive || connections.isEmpty()) { 151 HttpConnection connection = new HttpConnection(config, connectTimeout); 152 return connection; 153 } else { 154 HttpConnection connection = connections.get(0); 155 connections.remove(0); 156 if(!connection.isStale()) { 157 SecurityManager security = System.getSecurityManager(); 158 if (security != null) { 159 security.checkConnect(connection.getSocket().getInetAddress().getHostName(), connection.getSocket().getPort()); 160 } 161 return connection; 162 } else { 163 return getHttpConnection(config, connectTimeout); 164 } 165 } 166 } 167 168 public int numFreeConnections() { 169 int numFree = 0; 170 for (Iterator<List<HttpConnection>> iter = freeConnectionMap.values().iterator(); iter.hasNext();) { 171 List<HttpConnection> connections = iter.next(); 172 numFree += connections.size(); 173 } 174 return numFree; 175 } 176 } 177 178 public void reset() { 179 pool.clear(); 180 } 181 182} 183