Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.net.http/share/classes/jdk/internal/net/http/Http2ClientImpl.java
41171 views
1
/*
2
* Copyright (c) 2015, 2018, 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 jdk.internal.net.http;
27
28
import java.io.EOFException;
29
import java.io.IOException;
30
import java.io.UncheckedIOException;
31
import java.net.ConnectException;
32
import java.net.InetSocketAddress;
33
import java.net.URI;
34
import java.util.Base64;
35
import java.util.Collections;
36
import java.util.HashSet;
37
import java.util.Map;
38
import java.util.Set;
39
import java.util.concurrent.ConcurrentHashMap;
40
import java.util.concurrent.CompletableFuture;
41
import jdk.internal.net.http.common.Log;
42
import jdk.internal.net.http.common.Logger;
43
import jdk.internal.net.http.common.MinimalFuture;
44
import jdk.internal.net.http.common.Utils;
45
import jdk.internal.net.http.frame.SettingsFrame;
46
import static jdk.internal.net.http.frame.SettingsFrame.INITIAL_WINDOW_SIZE;
47
import static jdk.internal.net.http.frame.SettingsFrame.ENABLE_PUSH;
48
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
49
import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS;
50
import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE;
51
52
/**
53
* Http2 specific aspects of HttpClientImpl
54
*/
55
class Http2ClientImpl {
56
57
final static Logger debug =
58
Utils.getDebugLogger("Http2ClientImpl"::toString, Utils.DEBUG);
59
60
private final HttpClientImpl client;
61
62
Http2ClientImpl(HttpClientImpl client) {
63
this.client = client;
64
}
65
66
/* Map key is "scheme:host:port" */
67
private final Map<String,Http2Connection> connections = new ConcurrentHashMap<>();
68
69
private final Set<String> failures = Collections.synchronizedSet(new HashSet<>());
70
71
/**
72
* When HTTP/2 requested only. The following describes the aggregate behavior including the
73
* calling code. In all cases, the HTTP2 connection cache
74
* is checked first for a suitable connection and that is returned if available.
75
* If not, a new connection is opened, except in https case when a previous negotiate failed.
76
* In that case, we want to continue using http/1.1. When a connection is to be opened and
77
* if multiple requests are sent in parallel then each will open a new connection.
78
*
79
* If negotiation/upgrade succeeds then
80
* one connection will be put in the cache and the others will be closed
81
* after the initial request completes (not strictly necessary for h2, only for h2c)
82
*
83
* If negotiate/upgrade fails, then any opened connections remain open (as http/1.1)
84
* and will be used and cached in the http/1 cache. Note, this method handles the
85
* https failure case only (by completing the CF with an ALPN exception, handled externally)
86
* The h2c upgrade is handled externally also.
87
*
88
* Specific CF behavior of this method.
89
* 1. completes with ALPN exception: h2 negotiate failed for first time. failure recorded.
90
* 2. completes with other exception: failure not recorded. Caller must handle
91
* 3. completes normally with null: no connection in cache for h2c or h2 failed previously
92
* 4. completes normally with connection: h2 or h2c connection in cache. Use it.
93
*/
94
CompletableFuture<Http2Connection> getConnectionFor(HttpRequestImpl req,
95
Exchange<?> exchange) {
96
URI uri = req.uri();
97
InetSocketAddress proxy = req.proxy();
98
String key = Http2Connection.keyFor(uri, proxy);
99
100
synchronized (this) {
101
Http2Connection connection = connections.get(key);
102
if (connection != null) {
103
try {
104
if (connection.closed || !connection.reserveStream(true)) {
105
if (debug.on())
106
debug.log("removing found closed or closing connection: %s", connection);
107
deleteConnection(connection);
108
} else {
109
// fast path if connection already exists
110
if (debug.on())
111
debug.log("found connection in the pool: %s", connection);
112
return MinimalFuture.completedFuture(connection);
113
}
114
} catch (IOException e) {
115
// thrown by connection.reserveStream()
116
return MinimalFuture.failedFuture(e);
117
}
118
}
119
120
if (!req.secure() || failures.contains(key)) {
121
// secure: negotiate failed before. Use http/1.1
122
// !secure: no connection available in cache. Attempt upgrade
123
if (debug.on()) debug.log("not found in connection pool");
124
return MinimalFuture.completedFuture(null);
125
}
126
}
127
return Http2Connection
128
.createAsync(req, this, exchange)
129
.whenComplete((conn, t) -> {
130
synchronized (Http2ClientImpl.this) {
131
if (conn != null) {
132
try {
133
conn.reserveStream(true);
134
} catch (IOException e) {
135
throw new UncheckedIOException(e); // shouldn't happen
136
}
137
offerConnection(conn);
138
} else {
139
Throwable cause = Utils.getCompletionCause(t);
140
if (cause instanceof Http2Connection.ALPNException)
141
failures.add(key);
142
}
143
}
144
});
145
}
146
147
/*
148
* Cache the given connection, if no connection to the same
149
* destination exists. If one exists, then we let the initial stream
150
* complete but allow it to close itself upon completion.
151
* This situation should not arise with https because the request
152
* has not been sent as part of the initial alpn negotiation
153
*/
154
boolean offerConnection(Http2Connection c) {
155
if (debug.on()) debug.log("offering to the connection pool: %s", c);
156
if (c.closed || c.finalStream()) {
157
if (debug.on())
158
debug.log("skipping offered closed or closing connection: %s", c);
159
return false;
160
}
161
162
String key = c.key();
163
synchronized(this) {
164
Http2Connection c1 = connections.putIfAbsent(key, c);
165
if (c1 != null) {
166
c.setFinalStream();
167
if (debug.on())
168
debug.log("existing entry in connection pool for %s", key);
169
return false;
170
}
171
if (debug.on())
172
debug.log("put in the connection pool: %s", c);
173
return true;
174
}
175
}
176
177
void deleteConnection(Http2Connection c) {
178
if (debug.on())
179
debug.log("removing from the connection pool: %s", c);
180
synchronized (this) {
181
Http2Connection c1 = connections.get(c.key());
182
if (c1 != null && c1.equals(c)) {
183
connections.remove(c.key());
184
if (debug.on())
185
debug.log("removed from the connection pool: %s", c);
186
}
187
}
188
}
189
190
private EOFException STOPPED;
191
void stop() {
192
if (debug.on()) debug.log("stopping");
193
STOPPED = new EOFException("HTTP/2 client stopped");
194
STOPPED.setStackTrace(new StackTraceElement[0]);
195
connections.values().forEach(this::close);
196
connections.clear();
197
}
198
199
private void close(Http2Connection h2c) {
200
try { h2c.close(); } catch (Throwable t) {}
201
try { h2c.shutdown(STOPPED); } catch (Throwable t) {}
202
}
203
204
HttpClientImpl client() {
205
return client;
206
}
207
208
/** Returns the client settings as a base64 (url) encoded string */
209
String getSettingsString() {
210
SettingsFrame sf = getClientSettings();
211
byte[] settings = sf.toByteArray(); // without the header
212
Base64.Encoder encoder = Base64.getUrlEncoder()
213
.withoutPadding();
214
return encoder.encodeToString(settings);
215
}
216
217
private static final int K = 1024;
218
219
private static int getParameter(String property, int min, int max, int defaultValue) {
220
int value = Utils.getIntegerNetProperty(property, defaultValue);
221
// use default value if misconfigured
222
if (value < min || value > max) {
223
Log.logError("Property value for {0}={1} not in [{2}..{3}]: " +
224
"using default={4}", property, value, min, max, defaultValue);
225
value = defaultValue;
226
}
227
return value;
228
}
229
230
// used for the connection window, to have a connection window size
231
// bigger than the initial stream window size.
232
int getConnectionWindowSize(SettingsFrame clientSettings) {
233
// Maximum size is 2^31-1. Don't allow window size to be less
234
// than the stream window size. HTTP/2 specify a default of 64 * K -1,
235
// but we use 2^26 by default for better performance.
236
int streamWindow = clientSettings.getParameter(INITIAL_WINDOW_SIZE);
237
238
// The default is the max between the stream window size
239
// and the connection window size.
240
int defaultValue = Math.min(Integer.MAX_VALUE,
241
Math.max(streamWindow, K*K*32));
242
243
return getParameter(
244
"jdk.httpclient.connectionWindowSize",
245
streamWindow, Integer.MAX_VALUE, defaultValue);
246
}
247
248
SettingsFrame getClientSettings() {
249
SettingsFrame frame = new SettingsFrame();
250
// default defined for HTTP/2 is 4 K, we use 16 K.
251
frame.setParameter(HEADER_TABLE_SIZE, getParameter(
252
"jdk.httpclient.hpack.maxheadertablesize",
253
0, Integer.MAX_VALUE, 16 * K));
254
// O: does not accept push streams. 1: accepts push streams.
255
frame.setParameter(ENABLE_PUSH, getParameter(
256
"jdk.httpclient.enablepush",
257
0, 1, 1));
258
// HTTP/2 recommends to set the number of concurrent streams
259
// no lower than 100. We use 100. 0 means no stream would be
260
// accepted. That would render the client to be non functional,
261
// so we won't let 0 be configured for our Http2ClientImpl.
262
frame.setParameter(MAX_CONCURRENT_STREAMS, getParameter(
263
"jdk.httpclient.maxstreams",
264
1, Integer.MAX_VALUE, 100));
265
// Maximum size is 2^31-1. Don't allow window size to be less
266
// than the minimum frame size as this is likely to be a
267
// configuration error. HTTP/2 specify a default of 64 * K -1,
268
// but we use 16 M for better performance.
269
frame.setParameter(INITIAL_WINDOW_SIZE, getParameter(
270
"jdk.httpclient.windowsize",
271
16 * K, Integer.MAX_VALUE, 16*K*K));
272
// HTTP/2 specify a minimum size of 16 K, a maximum size of 2^24-1,
273
// and a default of 16 K. We use 16 K as default.
274
frame.setParameter(MAX_FRAME_SIZE, getParameter(
275
"jdk.httpclient.maxframesize",
276
16 * K, 16 * K * K -1, 16 * K));
277
return frame;
278
}
279
}
280
281