src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java
changeset 47361 74e1913a98c0
parent 47216 71c04702a3d5
child 57956 e0b8b019d2f5
equal deleted inserted replaced
47360:31c9cf5eca62 47361:74e1913a98c0
     1 /*
     1 /*
     2  * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    25 
    25 
    26 package sun.net.www.http;
    26 package sun.net.www.http;
    27 
    27 
    28 import java.io.IOException;
    28 import java.io.IOException;
    29 import java.io.NotSerializableException;
    29 import java.io.NotSerializableException;
       
    30 import java.io.ObjectInputStream;
       
    31 import java.io.ObjectOutputStream;
       
    32 import java.net.URL;
       
    33 import java.security.AccessController;
       
    34 import java.security.PrivilegedAction;
       
    35 import java.util.ArrayDeque;
    30 import java.util.ArrayList;
    36 import java.util.ArrayList;
    31 import java.util.HashMap;
    37 import java.util.HashMap;
    32 import java.net.URL;
    38 import java.util.List;
       
    39 
    33 import jdk.internal.misc.InnocuousThread;
    40 import jdk.internal.misc.InnocuousThread;
       
    41 import sun.security.action.GetIntegerAction;
    34 
    42 
    35 /**
    43 /**
    36  * A class that implements a cache of idle Http connections for keep-alive
    44  * A class that implements a cache of idle Http connections for keep-alive
    37  *
    45  *
    38  * @author Stephen R. Pietrowicz (NCSA)
    46  * @author Stephen R. Pietrowicz (NCSA)
    51      */
    59      */
    52     static final int MAX_CONNECTIONS = 5;
    60     static final int MAX_CONNECTIONS = 5;
    53     static int result = -1;
    61     static int result = -1;
    54     static int getMaxConnections() {
    62     static int getMaxConnections() {
    55         if (result == -1) {
    63         if (result == -1) {
    56             result = java.security.AccessController.doPrivileged(
    64             result = AccessController.doPrivileged(
    57                 new sun.security.action.GetIntegerAction("http.maxConnections",
    65                 new GetIntegerAction("http.maxConnections", MAX_CONNECTIONS))
    58                                                          MAX_CONNECTIONS))
       
    59                 .intValue();
    66                 .intValue();
    60             if (result <= 0)
    67             if (result <= 0) {
    61                 result = MAX_CONNECTIONS;
    68                 result = MAX_CONNECTIONS;
    62         }
    69             }
    63             return result;
    70         }
       
    71         return result;
    64     }
    72     }
    65 
    73 
    66     static final int LIFETIME = 5000;
    74     static final int LIFETIME = 5000;
    67 
    75 
    68     private Thread keepAliveTimer = null;
    76     private Thread keepAliveTimer = null;
    91              * to a server that sent me a keep-alive
    99              * to a server that sent me a keep-alive
    92              * time of 15 sec, the proxy unilaterally terminates my connection
   100              * time of 15 sec, the proxy unilaterally terminates my connection
    93              * The robustness to get around this is in HttpClient.parseHTTP()
   101              * The robustness to get around this is in HttpClient.parseHTTP()
    94              */
   102              */
    95             final KeepAliveCache cache = this;
   103             final KeepAliveCache cache = this;
    96             java.security.AccessController.doPrivileged(
   104             AccessController.doPrivileged(new PrivilegedAction<>() {
    97                 new java.security.PrivilegedAction<>() {
       
    98                 public Void run() {
   105                 public Void run() {
    99                     keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
   106                     keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
   100                     keepAliveTimer.setDaemon(true);
   107                     keepAliveTimer.setDaemon(true);
   101                     keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
   108                     keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
   102                     keepAliveTimer.start();
   109                     keepAliveTimer.start();
   108         KeepAliveKey key = new KeepAliveKey(url, obj);
   115         KeepAliveKey key = new KeepAliveKey(url, obj);
   109         ClientVector v = super.get(key);
   116         ClientVector v = super.get(key);
   110 
   117 
   111         if (v == null) {
   118         if (v == null) {
   112             int keepAliveTimeout = http.getKeepAliveTimeout();
   119             int keepAliveTimeout = http.getKeepAliveTimeout();
   113             v = new ClientVector(keepAliveTimeout > 0?
   120             v = new ClientVector(keepAliveTimeout > 0 ?
   114                                  keepAliveTimeout*1000 : LIFETIME);
   121                                  keepAliveTimeout * 1000 : LIFETIME);
   115             v.put(http);
   122             v.put(http);
   116             super.put(key, v);
   123             super.put(key, v);
   117         } else {
   124         } else {
   118             v.put(http);
   125             v.put(http);
   119         }
   126         }
   120     }
   127     }
   121 
   128 
   122     /* remove an obsolete HttpClient from its VectorCache */
   129     /* remove an obsolete HttpClient from its VectorCache */
   123     public synchronized void remove (HttpClient h, Object obj) {
   130     public synchronized void remove(HttpClient h, Object obj) {
   124         KeepAliveKey key = new KeepAliveKey(h.url, obj);
   131         KeepAliveKey key = new KeepAliveKey(h.url, obj);
   125         ClientVector v = super.get(key);
   132         ClientVector v = super.get(key);
   126         if (v != null) {
   133         if (v != null) {
   127             v.remove(h);
   134             v.remove(h);
   128             if (v.empty()) {
   135             if (v.isEmpty()) {
   129                 removeVector(key);
   136                 removeVector(key);
   130             }
   137             }
   131         }
   138         }
   132     }
   139     }
   133 
   140 
   140 
   147 
   141     /**
   148     /**
   142      * Check to see if this URL has a cached HttpClient
   149      * Check to see if this URL has a cached HttpClient
   143      */
   150      */
   144     public synchronized HttpClient get(URL url, Object obj) {
   151     public synchronized HttpClient get(URL url, Object obj) {
   145 
       
   146         KeepAliveKey key = new KeepAliveKey(url, obj);
   152         KeepAliveKey key = new KeepAliveKey(url, obj);
   147         ClientVector v = super.get(key);
   153         ClientVector v = super.get(key);
   148         if (v == null) { // nothing in cache yet
   154         if (v == null) { // nothing in cache yet
   149             return null;
   155             return null;
   150         }
   156         }
   159     public void run() {
   165     public void run() {
   160         do {
   166         do {
   161             try {
   167             try {
   162                 Thread.sleep(LIFETIME);
   168                 Thread.sleep(LIFETIME);
   163             } catch (InterruptedException e) {}
   169             } catch (InterruptedException e) {}
       
   170 
       
   171             // Remove all outdated HttpClients.
   164             synchronized (this) {
   172             synchronized (this) {
   165                 /* Remove all unused HttpClients.  Starting from the
       
   166                  * bottom of the stack (the least-recently used first).
       
   167                  * REMIND: It'd be nice to not remove *all* connections
       
   168                  * that aren't presently in use.  One could have been added
       
   169                  * a second ago that's still perfectly valid, and we're
       
   170                  * needlessly axing it.  But it's not clear how to do this
       
   171                  * cleanly, and doing it right may be more trouble than it's
       
   172                  * worth.
       
   173                  */
       
   174 
       
   175                 long currentTime = System.currentTimeMillis();
   173                 long currentTime = System.currentTimeMillis();
   176 
   174                 List<KeepAliveKey> keysToRemove = new ArrayList<>();
   177                 ArrayList<KeepAliveKey> keysToRemove
       
   178                     = new ArrayList<>();
       
   179 
   175 
   180                 for (KeepAliveKey key : keySet()) {
   176                 for (KeepAliveKey key : keySet()) {
   181                     ClientVector v = get(key);
   177                     ClientVector v = get(key);
   182                     synchronized (v) {
   178                     synchronized (v) {
   183                         int i;
   179                         KeepAliveEntry e = v.peek();
   184 
   180                         while (e != null) {
   185                         for (i = 0; i < v.size(); i++) {
       
   186                             KeepAliveEntry e = v.elementAt(i);
       
   187                             if ((currentTime - e.idleStartTime) > v.nap) {
   181                             if ((currentTime - e.idleStartTime) > v.nap) {
   188                                 HttpClient h = e.hc;
   182                                 v.poll();
   189                                 h.closeServer();
   183                                 e.hc.closeServer();
   190                             } else {
   184                             } else {
   191                                 break;
   185                                 break;
   192                             }
   186                             }
       
   187                             e = v.peek();
   193                         }
   188                         }
   194                         v.subList(0, i).clear();
   189 
   195 
   190                         if (v.isEmpty()) {
   196                         if (v.size() == 0) {
       
   197                             keysToRemove.add(key);
   191                             keysToRemove.add(key);
   198                         }
   192                         }
   199                     }
   193                     }
   200                 }
   194                 }
   201 
   195 
   202                 for (KeepAliveKey key : keysToRemove) {
   196                 for (KeepAliveKey key : keysToRemove) {
   203                     removeVector(key);
   197                     removeVector(key);
   204                 }
   198                 }
   205             }
   199             }
   206         } while (size() > 0);
   200         } while (!isEmpty());
   207 
       
   208         return;
       
   209     }
   201     }
   210 
   202 
   211     /*
   203     /*
   212      * Do not serialize this class!
   204      * Do not serialize this class!
   213      */
   205      */
   214     private void writeObject(java.io.ObjectOutputStream stream)
   206     private void writeObject(ObjectOutputStream stream) throws IOException {
   215     throws IOException {
       
   216         throw new NotSerializableException();
   207         throw new NotSerializableException();
   217     }
   208     }
   218 
   209 
   219     private void readObject(java.io.ObjectInputStream stream)
   210     private void readObject(ObjectInputStream stream)
   220     throws IOException, ClassNotFoundException {
   211         throws IOException, ClassNotFoundException
       
   212     {
   221         throw new NotSerializableException();
   213         throw new NotSerializableException();
   222     }
   214     }
   223 }
   215 }
   224 
   216 
   225 /* FILO order for recycling HttpClients, should run in a thread
   217 /* FILO order for recycling HttpClients, should run in a thread
   226  * to time them out.  If > maxConns are in use, block.
   218  * to time them out.  If > maxConns are in use, block.
   227  */
   219  */
   228 
   220 class ClientVector extends ArrayDeque<KeepAliveEntry> {
   229 
       
   230 class ClientVector extends java.util.Stack<KeepAliveEntry> {
       
   231     private static final long serialVersionUID = -8680532108106489459L;
   221     private static final long serialVersionUID = -8680532108106489459L;
   232 
   222 
   233     // sleep time in milliseconds, before cache clear
   223     // sleep time in milliseconds, before cache clear
   234     int nap;
   224     int nap;
   235 
   225 
   236 
   226     ClientVector(int nap) {
   237 
       
   238     ClientVector (int nap) {
       
   239         this.nap = nap;
   227         this.nap = nap;
   240     }
   228     }
   241 
   229 
   242     synchronized HttpClient get() {
   230     synchronized HttpClient get() {
   243         if (empty()) {
   231         if (isEmpty()) {
   244             return null;
   232             return null;
   245         } else {
   233         }
   246             // Loop until we find a connection that has not timed out
   234 
   247             HttpClient hc = null;
   235         // Loop until we find a connection that has not timed out
   248             long currentTime = System.currentTimeMillis();
   236         HttpClient hc = null;
   249             do {
   237         long currentTime = System.currentTimeMillis();
   250                 KeepAliveEntry e = pop();
   238         do {
   251                 if ((currentTime - e.idleStartTime) > nap) {
   239             KeepAliveEntry e = pop();
   252                     e.hc.closeServer();
   240             if ((currentTime - e.idleStartTime) > nap) {
   253                 } else {
   241                 e.hc.closeServer();
   254                     hc = e.hc;
   242             } else {
   255                 }
   243                 hc = e.hc;
   256             } while ((hc== null) && (!empty()));
   244             }
   257             return hc;
   245         } while ((hc == null) && (!isEmpty()));
   258         }
   246         return hc;
   259     }
   247     }
   260 
   248 
   261     /* return a still valid, unused HttpClient */
   249     /* return a still valid, unused HttpClient */
   262     synchronized void put(HttpClient h) {
   250     synchronized void put(HttpClient h) {
   263         if (size() >= KeepAliveCache.getMaxConnections()) {
   251         if (size() >= KeepAliveCache.getMaxConnections()) {
   265         } else {
   253         } else {
   266             push(new KeepAliveEntry(h, System.currentTimeMillis()));
   254             push(new KeepAliveEntry(h, System.currentTimeMillis()));
   267         }
   255         }
   268     }
   256     }
   269 
   257 
       
   258     /* remove an HttpClient */
       
   259     synchronized boolean remove(HttpClient h) {
       
   260         for (KeepAliveEntry curr : this) {
       
   261             if (curr.hc == h) {
       
   262                 return super.remove(curr);
       
   263             }
       
   264         }
       
   265         return false;
       
   266     }
       
   267 
   270     /*
   268     /*
   271      * Do not serialize this class!
   269      * Do not serialize this class!
   272      */
   270      */
   273     private void writeObject(java.io.ObjectOutputStream stream)
   271     private void writeObject(ObjectOutputStream stream) throws IOException {
   274     throws IOException {
       
   275         throw new NotSerializableException();
   272         throw new NotSerializableException();
   276     }
   273     }
   277 
   274 
   278     private void readObject(java.io.ObjectInputStream stream)
   275     private void readObject(ObjectInputStream stream)
   279     throws IOException, ClassNotFoundException {
   276         throws IOException, ClassNotFoundException
       
   277     {
   280         throw new NotSerializableException();
   278         throw new NotSerializableException();
   281     }
   279     }
   282 }
   280 }
   283 
       
   284 
   281 
   285 class KeepAliveKey {
   282 class KeepAliveKey {
   286     private String      protocol = null;
   283     private String      protocol = null;
   287     private String      host = null;
   284     private String      host = null;
   288     private int         port = 0;
   285     private int         port = 0;