Skip to content

Commit 5710390

Browse files
committed
Merge master jdk-17.0.13+2 into openj9-staging
Signed-off-by: J9 Build <[email protected]>
2 parents 68c5891 + f0d119a commit 5710390

File tree

32 files changed

+1341
-388
lines changed

32 files changed

+1341
-388
lines changed

src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -71,9 +71,6 @@ public abstract class SSLContextImpl extends SSLContextSpi {
7171
private volatile StatusResponseManager statusResponseManager;
7272

7373
private final ReentrantLock contextLock = new ReentrantLock();
74-
final HashMap<Integer,
75-
SessionTicketExtension.StatelessKey> keyHashMap = new HashMap<>();
76-
7774

7875
SSLContextImpl() {
7976
ephemeralKeyManager = new EphemeralKeyManager();

src/java.base/share/classes/sun/security/ssl/SSLSessionContextImpl.java

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,11 @@
2828
import java.util.ArrayList;
2929
import java.util.Collections;
3030
import java.util.Enumeration;
31+
import java.util.Iterator;
3132
import java.util.Locale;
33+
import java.util.Map;
34+
import java.util.Random;
35+
import java.util.concurrent.ConcurrentHashMap;
3236
import javax.net.ssl.SSLSession;
3337
import javax.net.ssl.SSLSessionContext;
3438

@@ -69,6 +73,11 @@ final class SSLSessionContextImpl implements SSLSessionContext {
6973
private int cacheLimit; // the max cache size
7074
private int timeout; // timeout in seconds
7175

76+
// The current session ticket encryption key ID (only used in server context)
77+
private int currentKeyID;
78+
// Session ticket encryption keys and IDs map (only used in server context)
79+
private final Map<Integer, SessionTicketExtension.StatelessKey> keyHashMap;
80+
7281
// Default setting for stateless session resumption support (RFC 5077)
7382
private boolean statelessSession = true;
7483

@@ -80,6 +89,14 @@ final class SSLSessionContextImpl implements SSLSessionContext {
8089
// use soft reference
8190
sessionCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
8291
sessionHostPortCache = Cache.newSoftMemoryCache(cacheLimit, timeout);
92+
if (server) {
93+
keyHashMap = new ConcurrentHashMap<>();
94+
// Should be "randomly generated" according to RFC 5077,
95+
// but doesn't necessarily has to be a true random number.
96+
currentKeyID = new Random(System.nanoTime()).nextInt();
97+
} else {
98+
keyHashMap = Map.of();
99+
}
83100
}
84101

85102
// Stateless sessions when available, but there is a cache
@@ -170,6 +187,51 @@ public int getSessionCacheSize() {
170187
return cacheLimit;
171188
}
172189

190+
private void cleanupStatelessKeys() {
191+
Iterator<Map.Entry<Integer, SessionTicketExtension.StatelessKey>> it =
192+
keyHashMap.entrySet().iterator();
193+
while (it.hasNext()) {
194+
Map.Entry<Integer, SessionTicketExtension.StatelessKey> entry = it.next();
195+
SessionTicketExtension.StatelessKey k = entry.getValue();
196+
if (k.isInvalid(this)) {
197+
it.remove();
198+
try {
199+
k.key.destroy();
200+
} catch (Exception e) {
201+
// Suppress
202+
}
203+
}
204+
}
205+
}
206+
207+
// Package-private, used only from SessionTicketExtension.KeyState::getCurrentKey.
208+
SessionTicketExtension.StatelessKey getKey(HandshakeContext hc) {
209+
SessionTicketExtension.StatelessKey ssk = keyHashMap.get(currentKeyID);
210+
if (ssk != null && !ssk.isExpired()) {
211+
return ssk;
212+
}
213+
synchronized (this) {
214+
// If the current key is no longer expired, it was already
215+
// updated by a concurrent request, and we can return.
216+
ssk = keyHashMap.get(currentKeyID);
217+
if (ssk != null && !ssk.isExpired()) {
218+
return ssk;
219+
}
220+
int newID = currentKeyID + 1;
221+
ssk = new SessionTicketExtension.StatelessKey(hc, newID);
222+
keyHashMap.put(Integer.valueOf(newID), ssk);
223+
currentKeyID = newID;
224+
}
225+
// Check for and delete invalid keys every time we create a new stateless key.
226+
cleanupStatelessKeys();
227+
return ssk;
228+
}
229+
230+
// Package-private, used only from SessionTicketExtension.KeyState::getKey.
231+
SessionTicketExtension.StatelessKey getKey(int id) {
232+
return keyHashMap.get(id);
233+
}
234+
173235
// package-private method, used ONLY by ServerHandshaker
174236
SSLSessionImpl get(byte[] id) {
175237
return (SSLSessionImpl)getSession(id);

src/java.base/share/classes/sun/security/ssl/SessionTicketExtension.java

Lines changed: 14 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,7 @@
3737
import javax.crypto.SecretKey;
3838
import javax.crypto.spec.GCMParameterSpec;
3939
import javax.net.ssl.SSLProtocolException;
40+
import javax.net.ssl.SSLSessionContext;
4041

4142
import static sun.security.ssl.SSLExtension.CH_SESSION_TICKET;
4243
import static sun.security.ssl.SSLExtension.SH_SESSION_TICKET;
@@ -76,7 +77,6 @@ final class SessionTicketExtension {
7677
// Time in milliseconds until key is changed for encrypting session state
7778
private static final int TIMEOUT_DEFAULT = 3600 * 1000;
7879
private static final int keyTimeout;
79-
private static int currentKeyID = new SecureRandom().nextInt();
8080
private static final int KEYLEN = 256;
8181

8282
static {
@@ -117,7 +117,8 @@ static final class StatelessKey {
117117
final SecretKey key;
118118
final int num;
119119

120-
StatelessKey(HandshakeContext hc, int newNum) {
120+
// package-private, used only by SSLContextImpl
121+
StatelessKey(HandshakeContext hc, int num) {
121122
SecretKey k = null;
122123
try {
123124
KeyGenerator kg = KeyGenerator.getInstance("AES");
@@ -128,8 +129,7 @@ static final class StatelessKey {
128129
}
129130
key = k;
130131
timeout = System.currentTimeMillis() + keyTimeout;
131-
num = newNum;
132-
hc.sslContext.keyHashMap.put(Integer.valueOf(num), this);
132+
this.num = num;
133133
}
134134

135135
// Check if key needs to be changed
@@ -138,7 +138,8 @@ boolean isExpired() {
138138
}
139139

140140
// Check if this key is ready for deletion.
141-
boolean isInvalid(long sessionTimeout) {
141+
boolean isInvalid(SSLSessionContext sslSessionContext) {
142+
int sessionTimeout = sslSessionContext.getSessionTimeout() * 1000;
142143
return ((System.currentTimeMillis()) > (timeout + sessionTimeout));
143144
}
144145
}
@@ -147,79 +148,21 @@ private static final class KeyState {
147148

148149
// Get a key with a specific key number
149150
static StatelessKey getKey(HandshakeContext hc, int num) {
150-
StatelessKey ssk = hc.sslContext.keyHashMap.get(num);
151+
SSLSessionContextImpl serverCache =
152+
(SSLSessionContextImpl)hc.sslContext.engineGetServerSessionContext();
153+
StatelessKey ssk = serverCache.getKey(num);
151154

152-
if (ssk == null || ssk.isInvalid(getSessionTimeout(hc))) {
155+
if (ssk == null || ssk.isInvalid(serverCache)) {
153156
return null;
154157
}
155158
return ssk;
156159
}
157160

158161
// Get the current valid key, this will generate a new key if needed
159162
static StatelessKey getCurrentKey(HandshakeContext hc) {
160-
StatelessKey ssk = hc.sslContext.keyHashMap.get(currentKeyID);
161-
162-
if (ssk != null && !ssk.isExpired()) {
163-
return ssk;
164-
}
165-
return nextKey(hc);
166-
}
167-
168-
// This method locks when the first getCurrentKey() finds it to be too
169-
// old and create a new key to replace the current key. After the new
170-
// key established, the lock can be released so following
171-
// operations will start using the new key.
172-
// The first operation will take a longer code path by generating the
173-
// next key and cleaning up old keys.
174-
private static StatelessKey nextKey(HandshakeContext hc) {
175-
StatelessKey ssk;
176-
177-
synchronized (hc.sslContext.keyHashMap) {
178-
// If the current key is no longer expired, it was already
179-
// updated by a previous operation and we can return.
180-
ssk = hc.sslContext.keyHashMap.get(currentKeyID);
181-
if (ssk != null && !ssk.isExpired()) {
182-
return ssk;
183-
}
184-
int newNum;
185-
if (currentKeyID == Integer.MAX_VALUE) {
186-
newNum = 0;
187-
} else {
188-
newNum = currentKeyID + 1;
189-
}
190-
// Get new key
191-
ssk = new StatelessKey(hc, newNum);
192-
currentKeyID = newNum;
193-
// Release lock since the new key is ready to be used.
194-
}
195-
196-
// Clean up any old keys, then return the current key
197-
cleanup(hc);
198-
return ssk;
199-
}
200-
201-
// Deletes any invalid SessionStateKeys.
202-
static void cleanup(HandshakeContext hc) {
203-
int sessionTimeout = getSessionTimeout(hc);
204-
205-
StatelessKey ks;
206-
for (Object o : hc.sslContext.keyHashMap.keySet().toArray()) {
207-
Integer i = (Integer)o;
208-
ks = hc.sslContext.keyHashMap.get(i);
209-
if (ks.isInvalid(sessionTimeout)) {
210-
try {
211-
ks.key.destroy();
212-
} catch (Exception e) {
213-
// Suppress
214-
}
215-
hc.sslContext.keyHashMap.remove(i);
216-
}
217-
}
218-
}
219-
220-
static int getSessionTimeout(HandshakeContext hc) {
221-
return hc.sslContext.engineGetServerSessionContext().
222-
getSessionTimeout() * 1000;
163+
SSLSessionContextImpl serverCache =
164+
(SSLSessionContextImpl)hc.sslContext.engineGetServerSessionContext();
165+
return serverCache.getKey(hc);
223166
}
224167
}
225168

src/java.desktop/share/classes/javax/swing/text/html/CSS.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,10 @@ Object getInternalCSSValue(CSS.Attribute key, String value) {
842842
}
843843

844844
static Object mergeTextDecoration(String value) {
845+
if (value.startsWith("none")) {
846+
return null;
847+
}
848+
845849
boolean underline = value.contains("underline");
846850
boolean strikeThrough = value.contains("line-through");
847851
if (!underline && !strikeThrough) {

src/java.net.http/share/classes/jdk/internal/net/http/Exchange.java

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.io.IOException;
2929
import java.lang.System.Logger.Level;
3030
import java.net.InetSocketAddress;
31+
import java.net.ProtocolException;
3132
import java.net.ProxySelector;
3233
import java.net.URI;
3334
import java.net.URISyntaxException;
@@ -447,10 +448,62 @@ private CompletableFuture<Response> sendRequestBody(ExchangeImpl<T> ex) {
447448
CompletableFuture<Response> cf = ex.sendBodyAsync()
448449
.thenCompose(exIm -> exIm.getResponseAsync(parentExecutor));
449450
cf = wrapForUpgrade(cf);
451+
// after 101 is handled we check for other 1xx responses
452+
cf = cf.thenCompose(this::ignore1xxResponse);
450453
cf = wrapForLog(cf);
451454
return cf;
452455
}
453456

457+
/**
458+
* Checks whether the passed Response has a status code between 102 and 199 (both inclusive).
459+
* If so, then that {@code Response} is considered intermediate informational response and is
460+
* ignored by the client. This method then creates a new {@link CompletableFuture} which
461+
* completes when a subsequent response is sent by the server. Such newly constructed
462+
* {@link CompletableFuture} will not complete till a "final" response (one which doesn't have
463+
* a response code between 102 and 199 inclusive) is sent by the server. The returned
464+
* {@link CompletableFuture} is thus capable of handling multiple subsequent intermediate
465+
* informational responses from the server.
466+
* <p>
467+
* If the passed Response doesn't have a status code between 102 and 199 (both inclusive) then
468+
* this method immediately returns back a completed {@link CompletableFuture} with the passed
469+
* {@code Response}.
470+
* </p>
471+
*
472+
* @param rsp The response
473+
* @return A {@code CompletableFuture} with the final response from the server
474+
*/
475+
private CompletableFuture<Response> ignore1xxResponse(final Response rsp) {
476+
final int statusCode = rsp.statusCode();
477+
// we ignore any response code which is 1xx.
478+
// For 100 (with the request configured to expect-continue) and 101, we handle it
479+
// specifically as defined in the RFC-9110, outside of this method.
480+
// As noted in RFC-9110, section 15.2.1, if response code is 100 and if the request wasn't
481+
// configured with expectContinue, then we ignore the 100 response and wait for the final
482+
// response (just like any other 1xx response).
483+
// Any other response code between 102 and 199 (both inclusive) aren't specified in the
484+
// "HTTP semantics" RFC-9110. The spec states that these 1xx response codes are informational
485+
// and interim and the client can choose to ignore them and continue to wait for the
486+
// final response (headers)
487+
if ((statusCode >= 102 && statusCode <= 199)
488+
|| (statusCode == 100 && !request.expectContinue)) {
489+
Log.logTrace("Ignoring (1xx informational) response code {0}", rsp.statusCode());
490+
if (debug.on()) {
491+
debug.log("Ignoring (1xx informational) response code "
492+
+ rsp.statusCode());
493+
}
494+
assert exchImpl != null : "Illegal state - current exchange isn't set";
495+
// ignore this Response and wait again for the subsequent response headers
496+
final CompletableFuture<Response> cf = exchImpl.getResponseAsync(parentExecutor);
497+
// we recompose the CF again into the ignore1xxResponse check/function because
498+
// the 1xx response is allowed to be sent multiple times for a request, before
499+
// a final response arrives
500+
return cf.thenCompose(this::ignore1xxResponse);
501+
} else {
502+
// return the already completed future
503+
return MinimalFuture.completedFuture(rsp);
504+
}
505+
}
506+
454507
CompletableFuture<Response> responseAsyncImpl0(HttpConnection connection) {
455508
Function<ExchangeImpl<T>, CompletableFuture<Response>> after407Check;
456509
bodyIgnored = null;
@@ -481,7 +534,30 @@ private CompletableFuture<Response> wrapForUpgrade(CompletableFuture<Response> c
481534
if (upgrading) {
482535
return cf.thenCompose(r -> checkForUpgradeAsync(r, exchImpl));
483536
}
484-
return cf;
537+
// websocket requests use "Connection: Upgrade" and "Upgrade: websocket" headers.
538+
// however, the "upgrading" flag we maintain in this class only tracks a h2 upgrade
539+
// that we internally triggered. So it will be false in the case of websocket upgrade, hence
540+
// this additional check. If it's a websocket request we allow 101 responses and we don't
541+
// require any additional checks when a response arrives.
542+
if (request.isWebSocket()) {
543+
return cf;
544+
}
545+
// not expecting an upgrade, but if the server sends a 101 response then we fail the
546+
// request and also let the ExchangeImpl deal with it as a protocol error
547+
return cf.thenCompose(r -> {
548+
if (r.statusCode == 101) {
549+
final ProtocolException protoEx = new ProtocolException("Unexpected 101 " +
550+
"response, when not upgrading");
551+
assert exchImpl != null : "Illegal state - current exchange isn't set";
552+
try {
553+
exchImpl.onProtocolError(protoEx);
554+
} catch (Throwable ignore){
555+
// ignored
556+
}
557+
return MinimalFuture.failedFuture(protoEx);
558+
}
559+
return MinimalFuture.completedFuture(r);
560+
});
485561
}
486562

487563
private CompletableFuture<Response> wrapForLog(CompletableFuture<Response> cf) {

src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ abstract CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler,
199199
*/
200200
abstract void cancel(IOException cause);
201201

202+
/**
203+
* Invoked whenever there is a (HTTP) protocol error when dealing with the response
204+
* from the server. The implementations of {@code ExchangeImpl} are then expected to
205+
* take necessary action that is expected by the corresponding specifications whenever
206+
* a protocol error happens. For example, in HTTP/1.1, such protocol error would result
207+
* in the connection being closed.
208+
* @param cause The cause of the protocol violation
209+
*/
210+
abstract void onProtocolError(IOException cause);
211+
202212
/**
203213
* Called when the exchange is released, so that cleanup actions may be
204214
* performed - such as deregistering callbacks.

0 commit comments

Comments
 (0)