Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* |
michael@0 | 2 | * ==================================================================== |
michael@0 | 3 | * Licensed to the Apache Software Foundation (ASF) under one |
michael@0 | 4 | * or more contributor license agreements. See the NOTICE file |
michael@0 | 5 | * distributed with this work for additional information |
michael@0 | 6 | * regarding copyright ownership. The ASF licenses this file |
michael@0 | 7 | * to you under the Apache License, Version 2.0 (the |
michael@0 | 8 | * "License"); you may not use this file except in compliance |
michael@0 | 9 | * with the License. You may obtain a copy of the License at |
michael@0 | 10 | * |
michael@0 | 11 | * http://www.apache.org/licenses/LICENSE-2.0 |
michael@0 | 12 | * |
michael@0 | 13 | * Unless required by applicable law or agreed to in writing, |
michael@0 | 14 | * software distributed under the License is distributed on an |
michael@0 | 15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
michael@0 | 16 | * KIND, either express or implied. See the License for the |
michael@0 | 17 | * specific language governing permissions and limitations |
michael@0 | 18 | * under the License. |
michael@0 | 19 | * ==================================================================== |
michael@0 | 20 | * |
michael@0 | 21 | * This software consists of voluntary contributions made by many |
michael@0 | 22 | * individuals on behalf of the Apache Software Foundation. For more |
michael@0 | 23 | * information on the Apache Software Foundation, please see |
michael@0 | 24 | * <http://www.apache.org/>. |
michael@0 | 25 | * |
michael@0 | 26 | */ |
michael@0 | 27 | |
michael@0 | 28 | package ch.boye.httpclientandroidlib.impl.conn; |
michael@0 | 29 | |
michael@0 | 30 | import java.io.IOException; |
michael@0 | 31 | import java.util.concurrent.TimeUnit; |
michael@0 | 32 | |
michael@0 | 33 | import ch.boye.httpclientandroidlib.annotation.GuardedBy; |
michael@0 | 34 | import ch.boye.httpclientandroidlib.annotation.ThreadSafe; |
michael@0 | 35 | |
michael@0 | 36 | import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; |
michael@0 | 37 | /* LogFactory removed by HttpClient for Android script. */ |
michael@0 | 38 | import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; |
michael@0 | 39 | import ch.boye.httpclientandroidlib.conn.ClientConnectionOperator; |
michael@0 | 40 | import ch.boye.httpclientandroidlib.conn.ClientConnectionRequest; |
michael@0 | 41 | import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; |
michael@0 | 42 | import ch.boye.httpclientandroidlib.conn.routing.HttpRoute; |
michael@0 | 43 | import ch.boye.httpclientandroidlib.conn.routing.RouteTracker; |
michael@0 | 44 | import ch.boye.httpclientandroidlib.conn.scheme.SchemeRegistry; |
michael@0 | 45 | import ch.boye.httpclientandroidlib.params.HttpParams; |
michael@0 | 46 | |
michael@0 | 47 | /** |
michael@0 | 48 | * A connection manager for a single connection. This connection manager |
michael@0 | 49 | * maintains only one active connection at a time. Even though this class |
michael@0 | 50 | * is thread-safe it ought to be used by one execution thread only. |
michael@0 | 51 | * <p> |
michael@0 | 52 | * SingleClientConnManager will make an effort to reuse the connection |
michael@0 | 53 | * for subsequent requests with the same {@link HttpRoute route}. |
michael@0 | 54 | * It will, however, close the existing connection and open it |
michael@0 | 55 | * for the given route, if the route of the persistent connection does |
michael@0 | 56 | * not match that of the connection request. If the connection has been |
michael@0 | 57 | * already been allocated {@link IllegalStateException} is thrown. |
michael@0 | 58 | * |
michael@0 | 59 | * @since 4.0 |
michael@0 | 60 | */ |
michael@0 | 61 | @ThreadSafe |
michael@0 | 62 | public class SingleClientConnManager implements ClientConnectionManager { |
michael@0 | 63 | |
michael@0 | 64 | public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); |
michael@0 | 65 | |
michael@0 | 66 | /** The message to be logged on multiple allocation. */ |
michael@0 | 67 | public final static String MISUSE_MESSAGE = |
michael@0 | 68 | "Invalid use of SingleClientConnManager: connection still allocated.\n" + |
michael@0 | 69 | "Make sure to release the connection before allocating another one."; |
michael@0 | 70 | |
michael@0 | 71 | /** The schemes supported by this connection manager. */ |
michael@0 | 72 | protected final SchemeRegistry schemeRegistry; |
michael@0 | 73 | |
michael@0 | 74 | /** The operator for opening and updating connections. */ |
michael@0 | 75 | protected final ClientConnectionOperator connOperator; |
michael@0 | 76 | |
michael@0 | 77 | /** Whether the connection should be shut down on release. */ |
michael@0 | 78 | protected final boolean alwaysShutDown; |
michael@0 | 79 | |
michael@0 | 80 | /** The one and only entry in this pool. */ |
michael@0 | 81 | @GuardedBy("this") |
michael@0 | 82 | protected PoolEntry uniquePoolEntry; |
michael@0 | 83 | |
michael@0 | 84 | /** The currently issued managed connection, if any. */ |
michael@0 | 85 | @GuardedBy("this") |
michael@0 | 86 | protected ConnAdapter managedConn; |
michael@0 | 87 | |
michael@0 | 88 | /** The time of the last connection release, or -1. */ |
michael@0 | 89 | @GuardedBy("this") |
michael@0 | 90 | protected long lastReleaseTime; |
michael@0 | 91 | |
michael@0 | 92 | /** The time the last released connection expires and shouldn't be reused. */ |
michael@0 | 93 | @GuardedBy("this") |
michael@0 | 94 | protected long connectionExpiresTime; |
michael@0 | 95 | |
michael@0 | 96 | /** Indicates whether this connection manager is shut down. */ |
michael@0 | 97 | protected volatile boolean isShutDown; |
michael@0 | 98 | |
michael@0 | 99 | /** |
michael@0 | 100 | * Creates a new simple connection manager. |
michael@0 | 101 | * |
michael@0 | 102 | * @param params the parameters for this manager |
michael@0 | 103 | * @param schreg the scheme registry |
michael@0 | 104 | * |
michael@0 | 105 | * @deprecated use {@link SingleClientConnManager#SingleClientConnManager(SchemeRegistry)} |
michael@0 | 106 | */ |
michael@0 | 107 | @Deprecated |
michael@0 | 108 | public SingleClientConnManager(HttpParams params, |
michael@0 | 109 | SchemeRegistry schreg) { |
michael@0 | 110 | this(schreg); |
michael@0 | 111 | } |
michael@0 | 112 | /** |
michael@0 | 113 | * Creates a new simple connection manager. |
michael@0 | 114 | * |
michael@0 | 115 | * @param schreg the scheme registry |
michael@0 | 116 | */ |
michael@0 | 117 | public SingleClientConnManager(final SchemeRegistry schreg) { |
michael@0 | 118 | if (schreg == null) { |
michael@0 | 119 | throw new IllegalArgumentException |
michael@0 | 120 | ("Scheme registry must not be null."); |
michael@0 | 121 | } |
michael@0 | 122 | this.schemeRegistry = schreg; |
michael@0 | 123 | this.connOperator = createConnectionOperator(schreg); |
michael@0 | 124 | this.uniquePoolEntry = new PoolEntry(); |
michael@0 | 125 | this.managedConn = null; |
michael@0 | 126 | this.lastReleaseTime = -1L; |
michael@0 | 127 | this.alwaysShutDown = false; //@@@ from params? as argument? |
michael@0 | 128 | this.isShutDown = false; |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | /** |
michael@0 | 132 | * @since 4.1 |
michael@0 | 133 | */ |
michael@0 | 134 | public SingleClientConnManager() { |
michael@0 | 135 | this(SchemeRegistryFactory.createDefault()); |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | @Override |
michael@0 | 139 | protected void finalize() throws Throwable { |
michael@0 | 140 | try { |
michael@0 | 141 | shutdown(); |
michael@0 | 142 | } finally { // Make sure we call overridden method even if shutdown barfs |
michael@0 | 143 | super.finalize(); |
michael@0 | 144 | } |
michael@0 | 145 | } |
michael@0 | 146 | |
michael@0 | 147 | public SchemeRegistry getSchemeRegistry() { |
michael@0 | 148 | return this.schemeRegistry; |
michael@0 | 149 | } |
michael@0 | 150 | |
michael@0 | 151 | /** |
michael@0 | 152 | * Hook for creating the connection operator. |
michael@0 | 153 | * It is called by the constructor. |
michael@0 | 154 | * Derived classes can override this method to change the |
michael@0 | 155 | * instantiation of the operator. |
michael@0 | 156 | * The default implementation here instantiates |
michael@0 | 157 | * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}. |
michael@0 | 158 | * |
michael@0 | 159 | * @param schreg the scheme registry to use, or <code>null</code> |
michael@0 | 160 | * |
michael@0 | 161 | * @return the connection operator to use |
michael@0 | 162 | */ |
michael@0 | 163 | protected ClientConnectionOperator |
michael@0 | 164 | createConnectionOperator(SchemeRegistry schreg) { |
michael@0 | 165 | return new DefaultClientConnectionOperator(schreg); |
michael@0 | 166 | } |
michael@0 | 167 | |
michael@0 | 168 | /** |
michael@0 | 169 | * Asserts that this manager is not shut down. |
michael@0 | 170 | * |
michael@0 | 171 | * @throws IllegalStateException if this manager is shut down |
michael@0 | 172 | */ |
michael@0 | 173 | protected final void assertStillUp() throws IllegalStateException { |
michael@0 | 174 | if (this.isShutDown) |
michael@0 | 175 | throw new IllegalStateException("Manager is shut down."); |
michael@0 | 176 | } |
michael@0 | 177 | |
michael@0 | 178 | public final ClientConnectionRequest requestConnection( |
michael@0 | 179 | final HttpRoute route, |
michael@0 | 180 | final Object state) { |
michael@0 | 181 | |
michael@0 | 182 | return new ClientConnectionRequest() { |
michael@0 | 183 | |
michael@0 | 184 | public void abortRequest() { |
michael@0 | 185 | // Nothing to abort, since requests are immediate. |
michael@0 | 186 | } |
michael@0 | 187 | |
michael@0 | 188 | public ManagedClientConnection getConnection( |
michael@0 | 189 | long timeout, TimeUnit tunit) { |
michael@0 | 190 | return SingleClientConnManager.this.getConnection( |
michael@0 | 191 | route, state); |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | }; |
michael@0 | 195 | } |
michael@0 | 196 | |
michael@0 | 197 | /** |
michael@0 | 198 | * Obtains a connection. |
michael@0 | 199 | * |
michael@0 | 200 | * @param route where the connection should point to |
michael@0 | 201 | * |
michael@0 | 202 | * @return a connection that can be used to communicate |
michael@0 | 203 | * along the given route |
michael@0 | 204 | */ |
michael@0 | 205 | public synchronized ManagedClientConnection getConnection(HttpRoute route, Object state) { |
michael@0 | 206 | if (route == null) { |
michael@0 | 207 | throw new IllegalArgumentException("Route may not be null."); |
michael@0 | 208 | } |
michael@0 | 209 | assertStillUp(); |
michael@0 | 210 | |
michael@0 | 211 | if (log.isDebugEnabled()) { |
michael@0 | 212 | log.debug("Get connection for route " + route); |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | if (managedConn != null) |
michael@0 | 216 | throw new IllegalStateException(MISUSE_MESSAGE); |
michael@0 | 217 | |
michael@0 | 218 | // check re-usability of the connection |
michael@0 | 219 | boolean recreate = false; |
michael@0 | 220 | boolean shutdown = false; |
michael@0 | 221 | |
michael@0 | 222 | // Kill the connection if it expired. |
michael@0 | 223 | closeExpiredConnections(); |
michael@0 | 224 | |
michael@0 | 225 | if (uniquePoolEntry.connection.isOpen()) { |
michael@0 | 226 | RouteTracker tracker = uniquePoolEntry.tracker; |
michael@0 | 227 | shutdown = (tracker == null || // can happen if method is aborted |
michael@0 | 228 | !tracker.toRoute().equals(route)); |
michael@0 | 229 | } else { |
michael@0 | 230 | // If the connection is not open, create a new PoolEntry, |
michael@0 | 231 | // as the connection may have been marked not reusable, |
michael@0 | 232 | // due to aborts -- and the PoolEntry should not be reused |
michael@0 | 233 | // either. There's no harm in recreating an entry if |
michael@0 | 234 | // the connection is closed. |
michael@0 | 235 | recreate = true; |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | if (shutdown) { |
michael@0 | 239 | recreate = true; |
michael@0 | 240 | try { |
michael@0 | 241 | uniquePoolEntry.shutdown(); |
michael@0 | 242 | } catch (IOException iox) { |
michael@0 | 243 | log.debug("Problem shutting down connection.", iox); |
michael@0 | 244 | } |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | if (recreate) |
michael@0 | 248 | uniquePoolEntry = new PoolEntry(); |
michael@0 | 249 | |
michael@0 | 250 | managedConn = new ConnAdapter(uniquePoolEntry, route); |
michael@0 | 251 | |
michael@0 | 252 | return managedConn; |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | public synchronized void releaseConnection( |
michael@0 | 256 | ManagedClientConnection conn, |
michael@0 | 257 | long validDuration, TimeUnit timeUnit) { |
michael@0 | 258 | assertStillUp(); |
michael@0 | 259 | |
michael@0 | 260 | if (!(conn instanceof ConnAdapter)) { |
michael@0 | 261 | throw new IllegalArgumentException |
michael@0 | 262 | ("Connection class mismatch, " + |
michael@0 | 263 | "connection not obtained from this manager."); |
michael@0 | 264 | } |
michael@0 | 265 | |
michael@0 | 266 | if (log.isDebugEnabled()) { |
michael@0 | 267 | log.debug("Releasing connection " + conn); |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | ConnAdapter sca = (ConnAdapter) conn; |
michael@0 | 271 | if (sca.poolEntry == null) |
michael@0 | 272 | return; // already released |
michael@0 | 273 | ClientConnectionManager manager = sca.getManager(); |
michael@0 | 274 | if (manager != null && manager != this) { |
michael@0 | 275 | throw new IllegalArgumentException |
michael@0 | 276 | ("Connection not obtained from this manager."); |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | try { |
michael@0 | 280 | // make sure that the response has been read completely |
michael@0 | 281 | if (sca.isOpen() && (this.alwaysShutDown || |
michael@0 | 282 | !sca.isMarkedReusable()) |
michael@0 | 283 | ) { |
michael@0 | 284 | if (log.isDebugEnabled()) { |
michael@0 | 285 | log.debug |
michael@0 | 286 | ("Released connection open but not reusable."); |
michael@0 | 287 | } |
michael@0 | 288 | |
michael@0 | 289 | // make sure this connection will not be re-used |
michael@0 | 290 | // we might have gotten here because of a shutdown trigger |
michael@0 | 291 | // shutdown of the adapter also clears the tracked route |
michael@0 | 292 | sca.shutdown(); |
michael@0 | 293 | } |
michael@0 | 294 | } catch (IOException iox) { |
michael@0 | 295 | if (log.isDebugEnabled()) |
michael@0 | 296 | log.debug("Exception shutting down released connection.", |
michael@0 | 297 | iox); |
michael@0 | 298 | } finally { |
michael@0 | 299 | sca.detach(); |
michael@0 | 300 | managedConn = null; |
michael@0 | 301 | lastReleaseTime = System.currentTimeMillis(); |
michael@0 | 302 | if(validDuration > 0) |
michael@0 | 303 | connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime; |
michael@0 | 304 | else |
michael@0 | 305 | connectionExpiresTime = Long.MAX_VALUE; |
michael@0 | 306 | } |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | public synchronized void closeExpiredConnections() { |
michael@0 | 310 | if(System.currentTimeMillis() >= connectionExpiresTime) { |
michael@0 | 311 | closeIdleConnections(0, TimeUnit.MILLISECONDS); |
michael@0 | 312 | } |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | public synchronized void closeIdleConnections(long idletime, TimeUnit tunit) { |
michael@0 | 316 | assertStillUp(); |
michael@0 | 317 | |
michael@0 | 318 | // idletime can be 0 or negative, no problem there |
michael@0 | 319 | if (tunit == null) { |
michael@0 | 320 | throw new IllegalArgumentException("Time unit must not be null."); |
michael@0 | 321 | } |
michael@0 | 322 | |
michael@0 | 323 | if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) { |
michael@0 | 324 | final long cutoff = |
michael@0 | 325 | System.currentTimeMillis() - tunit.toMillis(idletime); |
michael@0 | 326 | if (lastReleaseTime <= cutoff) { |
michael@0 | 327 | try { |
michael@0 | 328 | uniquePoolEntry.close(); |
michael@0 | 329 | } catch (IOException iox) { |
michael@0 | 330 | // ignore |
michael@0 | 331 | log.debug("Problem closing idle connection.", iox); |
michael@0 | 332 | } |
michael@0 | 333 | } |
michael@0 | 334 | } |
michael@0 | 335 | } |
michael@0 | 336 | |
michael@0 | 337 | public synchronized void shutdown() { |
michael@0 | 338 | |
michael@0 | 339 | this.isShutDown = true; |
michael@0 | 340 | |
michael@0 | 341 | if (managedConn != null) |
michael@0 | 342 | managedConn.detach(); |
michael@0 | 343 | |
michael@0 | 344 | try { |
michael@0 | 345 | if (uniquePoolEntry != null) // and connection open? |
michael@0 | 346 | uniquePoolEntry.shutdown(); |
michael@0 | 347 | } catch (IOException iox) { |
michael@0 | 348 | // ignore |
michael@0 | 349 | log.debug("Problem while shutting down manager.", iox); |
michael@0 | 350 | } finally { |
michael@0 | 351 | uniquePoolEntry = null; |
michael@0 | 352 | } |
michael@0 | 353 | } |
michael@0 | 354 | |
michael@0 | 355 | /** |
michael@0 | 356 | * @deprecated no longer used |
michael@0 | 357 | */ |
michael@0 | 358 | @Deprecated |
michael@0 | 359 | protected synchronized void revokeConnection() { |
michael@0 | 360 | if (managedConn == null) |
michael@0 | 361 | return; |
michael@0 | 362 | managedConn.detach(); |
michael@0 | 363 | try { |
michael@0 | 364 | uniquePoolEntry.shutdown(); |
michael@0 | 365 | } catch (IOException iox) { |
michael@0 | 366 | // ignore |
michael@0 | 367 | log.debug("Problem while shutting down connection.", iox); |
michael@0 | 368 | } |
michael@0 | 369 | } |
michael@0 | 370 | |
michael@0 | 371 | /** |
michael@0 | 372 | * The pool entry for this connection manager. |
michael@0 | 373 | */ |
michael@0 | 374 | protected class PoolEntry extends AbstractPoolEntry { |
michael@0 | 375 | |
michael@0 | 376 | /** |
michael@0 | 377 | * Creates a new pool entry. |
michael@0 | 378 | * |
michael@0 | 379 | */ |
michael@0 | 380 | protected PoolEntry() { |
michael@0 | 381 | super(SingleClientConnManager.this.connOperator, null); |
michael@0 | 382 | } |
michael@0 | 383 | |
michael@0 | 384 | /** |
michael@0 | 385 | * Closes the connection in this pool entry. |
michael@0 | 386 | */ |
michael@0 | 387 | protected void close() throws IOException { |
michael@0 | 388 | shutdownEntry(); |
michael@0 | 389 | if (connection.isOpen()) |
michael@0 | 390 | connection.close(); |
michael@0 | 391 | } |
michael@0 | 392 | |
michael@0 | 393 | /** |
michael@0 | 394 | * Shuts down the connection in this pool entry. |
michael@0 | 395 | */ |
michael@0 | 396 | protected void shutdown() throws IOException { |
michael@0 | 397 | shutdownEntry(); |
michael@0 | 398 | if (connection.isOpen()) |
michael@0 | 399 | connection.shutdown(); |
michael@0 | 400 | } |
michael@0 | 401 | |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | /** |
michael@0 | 405 | * The connection adapter used by this manager. |
michael@0 | 406 | */ |
michael@0 | 407 | protected class ConnAdapter extends AbstractPooledConnAdapter { |
michael@0 | 408 | |
michael@0 | 409 | /** |
michael@0 | 410 | * Creates a new connection adapter. |
michael@0 | 411 | * |
michael@0 | 412 | * @param entry the pool entry for the connection being wrapped |
michael@0 | 413 | * @param route the planned route for this connection |
michael@0 | 414 | */ |
michael@0 | 415 | protected ConnAdapter(PoolEntry entry, HttpRoute route) { |
michael@0 | 416 | super(SingleClientConnManager.this, entry); |
michael@0 | 417 | markReusable(); |
michael@0 | 418 | entry.route = route; |
michael@0 | 419 | } |
michael@0 | 420 | |
michael@0 | 421 | } |
michael@0 | 422 | |
michael@0 | 423 | } |