Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java
67766 views
1
/*
2
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
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
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.net.www.http;
27
28
import java.io.IOException;
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;
36
import java.util.ArrayList;
37
import java.util.HashMap;
38
import java.util.List;
39
import java.util.concurrent.locks.Lock;
40
import java.util.concurrent.locks.ReentrantLock;
41
42
import jdk.internal.misc.InnocuousThread;
43
import sun.security.action.GetIntegerAction;
44
import sun.net.www.protocol.http.HttpURLConnection;
45
import sun.util.logging.PlatformLogger;
46
47
/**
48
* A class that implements a cache of idle Http connections for keep-alive
49
*
50
* @author Stephen R. Pietrowicz (NCSA)
51
* @author Dave Brown
52
*/
53
public class KeepAliveCache
54
extends HashMap<KeepAliveKey, ClientVector>
55
implements Runnable {
56
@java.io.Serial
57
private static final long serialVersionUID = -2937172892064557949L;
58
59
// Keep alive time set according to priority specified here:
60
// 1. If server specifies a time with a Keep-Alive header
61
// 2. If user specifies a time with system property below
62
// 3. Default values which depend on proxy vs server and whether
63
// a Connection: keep-alive header was sent by server
64
65
// name suffixed with "server" or "proxy"
66
private static final String keepAliveProp = "http.keepAlive.time.";
67
68
private static final int userKeepAliveServer;
69
private static final int userKeepAliveProxy;
70
71
static final PlatformLogger logger = HttpURLConnection.getHttpLogger();
72
73
@SuppressWarnings("removal")
74
static int getUserKeepAliveSeconds(String type) {
75
int v = AccessController.doPrivileged(
76
new GetIntegerAction(keepAliveProp+type, -1)).intValue();
77
return v < -1 ? -1 : v;
78
}
79
80
static {
81
userKeepAliveServer = getUserKeepAliveSeconds("server");
82
userKeepAliveProxy = getUserKeepAliveSeconds("proxy");
83
}
84
85
/* maximum # keep-alive connections to maintain at once
86
* This should be 2 by the HTTP spec, but because we don't support pipe-lining
87
* a larger value is more appropriate. So we now set a default of 5, and the value
88
* refers to the number of idle connections per destination (in the cache) only.
89
* It can be reset by setting system property "http.maxConnections".
90
*/
91
static final int MAX_CONNECTIONS = 5;
92
static int result = -1;
93
@SuppressWarnings("removal")
94
static int getMaxConnections() {
95
if (result == -1) {
96
result = AccessController.doPrivileged(
97
new GetIntegerAction("http.maxConnections", MAX_CONNECTIONS))
98
.intValue();
99
if (result <= 0) {
100
result = MAX_CONNECTIONS;
101
}
102
}
103
return result;
104
}
105
106
static final int LIFETIME = 5000;
107
108
// This class is never serialized (see writeObject/readObject).
109
private final ReentrantLock cacheLock = new ReentrantLock();
110
private Thread keepAliveTimer = null;
111
112
/**
113
* Constructor
114
*/
115
public KeepAliveCache() {}
116
117
/**
118
* Register this URL and HttpClient (that supports keep-alive) with the cache
119
* @param url The URL contains info about the host and port
120
* @param http The HttpClient to be cached
121
*/
122
@SuppressWarnings("removal")
123
public void put(final URL url, Object obj, HttpClient http) {
124
cacheLock.lock();
125
try {
126
boolean startThread = (keepAliveTimer == null);
127
if (!startThread) {
128
if (!keepAliveTimer.isAlive()) {
129
startThread = true;
130
}
131
}
132
if (startThread) {
133
clear();
134
/* Unfortunately, we can't always believe the keep-alive timeout we got
135
* back from the server. If I'm connected through a Netscape proxy
136
* to a server that sent me a keep-alive
137
* time of 15 sec, the proxy unilaterally terminates my connection
138
* The robustness to get around this is in HttpClient.parseHTTP()
139
*/
140
final KeepAliveCache cache = this;
141
AccessController.doPrivileged(new PrivilegedAction<>() {
142
public Void run() {
143
keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
144
keepAliveTimer.setDaemon(true);
145
keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
146
keepAliveTimer.start();
147
return null;
148
}
149
});
150
}
151
152
KeepAliveKey key = new KeepAliveKey(url, obj);
153
ClientVector v = super.get(key);
154
155
if (v == null) {
156
int keepAliveTimeout = http.getKeepAliveTimeout();
157
if (keepAliveTimeout == 0) {
158
keepAliveTimeout = getUserKeepAlive(http.getUsingProxy());
159
if (keepAliveTimeout == -1) {
160
// same default for server and proxy
161
keepAliveTimeout = 5;
162
}
163
} else if (keepAliveTimeout == -1) {
164
keepAliveTimeout = getUserKeepAlive(http.getUsingProxy());
165
if (keepAliveTimeout == -1) {
166
// different default for server and proxy
167
keepAliveTimeout = http.getUsingProxy() ? 60 : 5;
168
}
169
}
170
// at this point keepAliveTimeout is the number of seconds to keep
171
// alive, which could be 0, if the user specified 0 for the property
172
assert keepAliveTimeout >= 0;
173
if (keepAliveTimeout == 0) {
174
http.closeServer();
175
} else {
176
v = new ClientVector(keepAliveTimeout * 1000);
177
v.put(http);
178
super.put(key, v);
179
}
180
} else {
181
v.put(http);
182
}
183
} finally {
184
cacheLock.unlock();
185
}
186
}
187
188
// returns the keep alive set by user in system property or -1 if not set
189
private static int getUserKeepAlive(boolean isProxy) {
190
return isProxy ? userKeepAliveProxy : userKeepAliveServer;
191
}
192
193
/* remove an obsolete HttpClient from its VectorCache */
194
public void remove(HttpClient h, Object obj) {
195
cacheLock.lock();
196
try {
197
KeepAliveKey key = new KeepAliveKey(h.url, obj);
198
ClientVector v = super.get(key);
199
if (v != null) {
200
v.remove(h);
201
if (v.isEmpty()) {
202
removeVector(key);
203
}
204
}
205
} finally {
206
cacheLock.unlock();
207
}
208
}
209
210
/* called by a clientVector thread when all its connections have timed out
211
* and that vector of connections should be removed.
212
*/
213
private void removeVector(KeepAliveKey k) {
214
assert cacheLock.isHeldByCurrentThread();
215
super.remove(k);
216
}
217
218
/**
219
* Check to see if this URL has a cached HttpClient
220
*/
221
public HttpClient get(URL url, Object obj) {
222
cacheLock.lock();
223
try {
224
KeepAliveKey key = new KeepAliveKey(url, obj);
225
ClientVector v = super.get(key);
226
if (v == null) { // nothing in cache yet
227
return null;
228
}
229
return v.get();
230
} finally {
231
cacheLock.unlock();
232
}
233
}
234
235
/* Sleeps for an alloted timeout, then checks for timed out connections.
236
* Errs on the side of caution (leave connections idle for a relatively
237
* short time).
238
*/
239
@Override
240
public void run() {
241
do {
242
try {
243
Thread.sleep(LIFETIME);
244
} catch (InterruptedException e) {}
245
246
// Remove all outdated HttpClients.
247
cacheLock.lock();
248
try {
249
long currentTime = System.currentTimeMillis();
250
List<KeepAliveKey> keysToRemove = new ArrayList<>();
251
252
for (KeepAliveKey key : keySet()) {
253
ClientVector v = get(key);
254
v.lock();
255
try {
256
KeepAliveEntry e = v.peek();
257
while (e != null) {
258
if ((currentTime - e.idleStartTime) > v.nap) {
259
v.poll();
260
e.hc.closeServer();
261
} else {
262
break;
263
}
264
e = v.peek();
265
}
266
267
if (v.isEmpty()) {
268
keysToRemove.add(key);
269
}
270
} finally {
271
v.unlock();
272
}
273
}
274
275
for (KeepAliveKey key : keysToRemove) {
276
removeVector(key);
277
}
278
} finally {
279
cacheLock.unlock();
280
}
281
} while (!isEmpty());
282
}
283
284
/*
285
* Do not serialize this class!
286
*/
287
@java.io.Serial
288
private void writeObject(ObjectOutputStream stream) throws IOException {
289
throw new NotSerializableException();
290
}
291
292
@java.io.Serial
293
private void readObject(ObjectInputStream stream)
294
throws IOException, ClassNotFoundException
295
{
296
throw new NotSerializableException();
297
}
298
}
299
300
/* FILO order for recycling HttpClients, should run in a thread
301
* to time them out. If > maxConns are in use, block.
302
*/
303
class ClientVector extends ArrayDeque<KeepAliveEntry> {
304
@java.io.Serial
305
private static final long serialVersionUID = -8680532108106489459L;
306
private final ReentrantLock lock = new ReentrantLock();
307
308
// sleep time in milliseconds, before cache clear
309
int nap;
310
311
ClientVector(int nap) {
312
this.nap = nap;
313
}
314
315
HttpClient get() {
316
lock();
317
try {
318
if (isEmpty()) {
319
return null;
320
}
321
322
// Loop until we find a connection that has not timed out
323
HttpClient hc = null;
324
long currentTime = System.currentTimeMillis();
325
do {
326
KeepAliveEntry e = pop();
327
if ((currentTime - e.idleStartTime) > nap) {
328
e.hc.closeServer();
329
} else {
330
hc = e.hc;
331
if (KeepAliveCache.logger.isLoggable(PlatformLogger.Level.FINEST)) {
332
String msg = "cached HttpClient was idle for "
333
+ Long.toString(currentTime - e.idleStartTime);
334
KeepAliveCache.logger.finest(msg);
335
}
336
}
337
} while ((hc == null) && (!isEmpty()));
338
return hc;
339
} finally {
340
unlock();
341
}
342
}
343
344
/* return a still valid, unused HttpClient */
345
void put(HttpClient h) {
346
lock();
347
try {
348
if (size() >= KeepAliveCache.getMaxConnections()) {
349
h.closeServer(); // otherwise the connection remains in limbo
350
} else {
351
push(new KeepAliveEntry(h, System.currentTimeMillis()));
352
}
353
} finally {
354
unlock();
355
}
356
}
357
358
/* remove an HttpClient */
359
boolean remove(HttpClient h) {
360
lock();
361
try {
362
for (KeepAliveEntry curr : this) {
363
if (curr.hc == h) {
364
return super.remove(curr);
365
}
366
}
367
return false;
368
} finally {
369
unlock();
370
}
371
}
372
373
final void lock() {
374
lock.lock();
375
}
376
377
final void unlock() {
378
lock.unlock();
379
}
380
381
/*
382
* Do not serialize this class!
383
*/
384
@java.io.Serial
385
private void writeObject(ObjectOutputStream stream) throws IOException {
386
throw new NotSerializableException();
387
}
388
389
@java.io.Serial
390
private void readObject(ObjectInputStream stream)
391
throws IOException, ClassNotFoundException
392
{
393
throw new NotSerializableException();
394
}
395
}
396
397
class KeepAliveKey {
398
private String protocol = null;
399
private String host = null;
400
private int port = 0;
401
private Object obj = null; // additional key, such as socketfactory
402
403
/**
404
* Constructor
405
*
406
* @param url the URL containing the protocol, host and port information
407
*/
408
public KeepAliveKey(URL url, Object obj) {
409
this.protocol = url.getProtocol();
410
this.host = url.getHost();
411
this.port = url.getPort();
412
this.obj = obj;
413
}
414
415
/**
416
* Determine whether or not two objects of this type are equal
417
*/
418
@Override
419
public boolean equals(Object obj) {
420
if ((obj instanceof KeepAliveKey) == false)
421
return false;
422
KeepAliveKey kae = (KeepAliveKey)obj;
423
return host.equals(kae.host)
424
&& (port == kae.port)
425
&& protocol.equals(kae.protocol)
426
&& this.obj == kae.obj;
427
}
428
429
/**
430
* The hashCode() for this object is the string hashCode() of
431
* concatenation of the protocol, host name and port.
432
*/
433
@Override
434
public int hashCode() {
435
String str = protocol+host+port;
436
return this.obj == null? str.hashCode() :
437
str.hashCode() + this.obj.hashCode();
438
}
439
}
440
441
class KeepAliveEntry {
442
HttpClient hc;
443
long idleStartTime;
444
445
KeepAliveEntry(HttpClient hc, long idleStartTime) {
446
this.hc = hc;
447
this.idleStartTime = idleStartTime;
448
}
449
}
450
451