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 |
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; |