Skip to content

Commit

Permalink
Merge master jdk-17.0.13+2 into openj9-staging
Browse files Browse the repository at this point in the history
Signed-off-by: J9 Build <[email protected]>
  • Loading branch information
j9build committed Aug 8, 2024
2 parents 68c5891 + f0d119a commit 5710390
Show file tree
Hide file tree
Showing 32 changed files with 1,341 additions and 388 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -71,9 +71,6 @@ public abstract class SSLContextImpl extends SSLContextSpi {
private volatile StatusResponseManager statusResponseManager;

private final ReentrantLock contextLock = new ReentrantLock();
final HashMap<Integer,
SessionTicketExtension.StatelessKey> keyHashMap = new HashMap<>();


SSLContextImpl() {
ephemeralKeyManager = new EphemeralKeyManager();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -28,7 +28,11 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;

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

// The current session ticket encryption key ID (only used in server context)
private int currentKeyID;
// Session ticket encryption keys and IDs map (only used in server context)
private final Map<Integer, SessionTicketExtension.StatelessKey> keyHashMap;

// Default setting for stateless session resumption support (RFC 5077)
private boolean statelessSession = true;

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

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

private void cleanupStatelessKeys() {
Iterator<Map.Entry<Integer, SessionTicketExtension.StatelessKey>> it =
keyHashMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, SessionTicketExtension.StatelessKey> entry = it.next();
SessionTicketExtension.StatelessKey k = entry.getValue();
if (k.isInvalid(this)) {
it.remove();
try {
k.key.destroy();
} catch (Exception e) {
// Suppress
}
}
}
}

// Package-private, used only from SessionTicketExtension.KeyState::getCurrentKey.
SessionTicketExtension.StatelessKey getKey(HandshakeContext hc) {
SessionTicketExtension.StatelessKey ssk = keyHashMap.get(currentKeyID);
if (ssk != null && !ssk.isExpired()) {
return ssk;
}
synchronized (this) {
// If the current key is no longer expired, it was already
// updated by a concurrent request, and we can return.
ssk = keyHashMap.get(currentKeyID);
if (ssk != null && !ssk.isExpired()) {
return ssk;
}
int newID = currentKeyID + 1;
ssk = new SessionTicketExtension.StatelessKey(hc, newID);
keyHashMap.put(Integer.valueOf(newID), ssk);
currentKeyID = newID;
}
// Check for and delete invalid keys every time we create a new stateless key.
cleanupStatelessKeys();
return ssk;
}

// Package-private, used only from SessionTicketExtension.KeyState::getKey.
SessionTicketExtension.StatelessKey getKey(int id) {
return keyHashMap.get(id);
}

// package-private method, used ONLY by ServerHandshaker
SSLSessionImpl get(byte[] id) {
return (SSLSessionImpl)getSession(id);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -37,6 +37,7 @@
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSessionContext;

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

static {
Expand Down Expand Up @@ -117,7 +117,8 @@ static final class StatelessKey {
final SecretKey key;
final int num;

StatelessKey(HandshakeContext hc, int newNum) {
// package-private, used only by SSLContextImpl
StatelessKey(HandshakeContext hc, int num) {
SecretKey k = null;
try {
KeyGenerator kg = KeyGenerator.getInstance("AES");
Expand All @@ -128,8 +129,7 @@ static final class StatelessKey {
}
key = k;
timeout = System.currentTimeMillis() + keyTimeout;
num = newNum;
hc.sslContext.keyHashMap.put(Integer.valueOf(num), this);
this.num = num;
}

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

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

// Get a key with a specific key number
static StatelessKey getKey(HandshakeContext hc, int num) {
StatelessKey ssk = hc.sslContext.keyHashMap.get(num);
SSLSessionContextImpl serverCache =
(SSLSessionContextImpl)hc.sslContext.engineGetServerSessionContext();
StatelessKey ssk = serverCache.getKey(num);

if (ssk == null || ssk.isInvalid(getSessionTimeout(hc))) {
if (ssk == null || ssk.isInvalid(serverCache)) {
return null;
}
return ssk;
}

// Get the current valid key, this will generate a new key if needed
static StatelessKey getCurrentKey(HandshakeContext hc) {
StatelessKey ssk = hc.sslContext.keyHashMap.get(currentKeyID);

if (ssk != null && !ssk.isExpired()) {
return ssk;
}
return nextKey(hc);
}

// This method locks when the first getCurrentKey() finds it to be too
// old and create a new key to replace the current key. After the new
// key established, the lock can be released so following
// operations will start using the new key.
// The first operation will take a longer code path by generating the
// next key and cleaning up old keys.
private static StatelessKey nextKey(HandshakeContext hc) {
StatelessKey ssk;

synchronized (hc.sslContext.keyHashMap) {
// If the current key is no longer expired, it was already
// updated by a previous operation and we can return.
ssk = hc.sslContext.keyHashMap.get(currentKeyID);
if (ssk != null && !ssk.isExpired()) {
return ssk;
}
int newNum;
if (currentKeyID == Integer.MAX_VALUE) {
newNum = 0;
} else {
newNum = currentKeyID + 1;
}
// Get new key
ssk = new StatelessKey(hc, newNum);
currentKeyID = newNum;
// Release lock since the new key is ready to be used.
}

// Clean up any old keys, then return the current key
cleanup(hc);
return ssk;
}

// Deletes any invalid SessionStateKeys.
static void cleanup(HandshakeContext hc) {
int sessionTimeout = getSessionTimeout(hc);

StatelessKey ks;
for (Object o : hc.sslContext.keyHashMap.keySet().toArray()) {
Integer i = (Integer)o;
ks = hc.sslContext.keyHashMap.get(i);
if (ks.isInvalid(sessionTimeout)) {
try {
ks.key.destroy();
} catch (Exception e) {
// Suppress
}
hc.sslContext.keyHashMap.remove(i);
}
}
}

static int getSessionTimeout(HandshakeContext hc) {
return hc.sslContext.engineGetServerSessionContext().
getSessionTimeout() * 1000;
SSLSessionContextImpl serverCache =
(SSLSessionContextImpl)hc.sslContext.engineGetServerSessionContext();
return serverCache.getKey(hc);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/java.desktop/share/classes/javax/swing/text/html/CSS.java
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,10 @@ Object getInternalCSSValue(CSS.Attribute key, String value) {
}

static Object mergeTextDecoration(String value) {
if (value.startsWith("none")) {
return null;
}

boolean underline = value.contains("underline");
boolean strikeThrough = value.contains("line-through");
if (!underline && !strikeThrough) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.io.IOException;
import java.lang.System.Logger.Level;
import java.net.InetSocketAddress;
import java.net.ProtocolException;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
Expand Down Expand Up @@ -447,10 +448,62 @@ private CompletableFuture<Response> sendRequestBody(ExchangeImpl<T> ex) {
CompletableFuture<Response> cf = ex.sendBodyAsync()
.thenCompose(exIm -> exIm.getResponseAsync(parentExecutor));
cf = wrapForUpgrade(cf);
// after 101 is handled we check for other 1xx responses
cf = cf.thenCompose(this::ignore1xxResponse);
cf = wrapForLog(cf);
return cf;
}

/**
* Checks whether the passed Response has a status code between 102 and 199 (both inclusive).
* If so, then that {@code Response} is considered intermediate informational response and is
* ignored by the client. This method then creates a new {@link CompletableFuture} which
* completes when a subsequent response is sent by the server. Such newly constructed
* {@link CompletableFuture} will not complete till a "final" response (one which doesn't have
* a response code between 102 and 199 inclusive) is sent by the server. The returned
* {@link CompletableFuture} is thus capable of handling multiple subsequent intermediate
* informational responses from the server.
* <p>
* If the passed Response doesn't have a status code between 102 and 199 (both inclusive) then
* this method immediately returns back a completed {@link CompletableFuture} with the passed
* {@code Response}.
* </p>
*
* @param rsp The response
* @return A {@code CompletableFuture} with the final response from the server
*/
private CompletableFuture<Response> ignore1xxResponse(final Response rsp) {
final int statusCode = rsp.statusCode();
// we ignore any response code which is 1xx.
// For 100 (with the request configured to expect-continue) and 101, we handle it
// specifically as defined in the RFC-9110, outside of this method.
// As noted in RFC-9110, section 15.2.1, if response code is 100 and if the request wasn't
// configured with expectContinue, then we ignore the 100 response and wait for the final
// response (just like any other 1xx response).
// Any other response code between 102 and 199 (both inclusive) aren't specified in the
// "HTTP semantics" RFC-9110. The spec states that these 1xx response codes are informational
// and interim and the client can choose to ignore them and continue to wait for the
// final response (headers)
if ((statusCode >= 102 && statusCode <= 199)
|| (statusCode == 100 && !request.expectContinue)) {
Log.logTrace("Ignoring (1xx informational) response code {0}", rsp.statusCode());
if (debug.on()) {
debug.log("Ignoring (1xx informational) response code "
+ rsp.statusCode());
}
assert exchImpl != null : "Illegal state - current exchange isn't set";
// ignore this Response and wait again for the subsequent response headers
final CompletableFuture<Response> cf = exchImpl.getResponseAsync(parentExecutor);
// we recompose the CF again into the ignore1xxResponse check/function because
// the 1xx response is allowed to be sent multiple times for a request, before
// a final response arrives
return cf.thenCompose(this::ignore1xxResponse);
} else {
// return the already completed future
return MinimalFuture.completedFuture(rsp);
}
}

CompletableFuture<Response> responseAsyncImpl0(HttpConnection connection) {
Function<ExchangeImpl<T>, CompletableFuture<Response>> after407Check;
bodyIgnored = null;
Expand Down Expand Up @@ -481,7 +534,30 @@ private CompletableFuture<Response> wrapForUpgrade(CompletableFuture<Response> c
if (upgrading) {
return cf.thenCompose(r -> checkForUpgradeAsync(r, exchImpl));
}
return cf;
// websocket requests use "Connection: Upgrade" and "Upgrade: websocket" headers.
// however, the "upgrading" flag we maintain in this class only tracks a h2 upgrade
// that we internally triggered. So it will be false in the case of websocket upgrade, hence
// this additional check. If it's a websocket request we allow 101 responses and we don't
// require any additional checks when a response arrives.
if (request.isWebSocket()) {
return cf;
}
// not expecting an upgrade, but if the server sends a 101 response then we fail the
// request and also let the ExchangeImpl deal with it as a protocol error
return cf.thenCompose(r -> {
if (r.statusCode == 101) {
final ProtocolException protoEx = new ProtocolException("Unexpected 101 " +
"response, when not upgrading");
assert exchImpl != null : "Illegal state - current exchange isn't set";
try {
exchImpl.onProtocolError(protoEx);
} catch (Throwable ignore){
// ignored
}
return MinimalFuture.failedFuture(protoEx);
}
return MinimalFuture.completedFuture(r);
});
}

private CompletableFuture<Response> wrapForLog(CompletableFuture<Response> cf) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,16 @@ abstract CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler,
*/
abstract void cancel(IOException cause);

/**
* Invoked whenever there is a (HTTP) protocol error when dealing with the response
* from the server. The implementations of {@code ExchangeImpl} are then expected to
* take necessary action that is expected by the corresponding specifications whenever
* a protocol error happens. For example, in HTTP/1.1, such protocol error would result
* in the connection being closed.
* @param cause The cause of the protocol violation
*/
abstract void onProtocolError(IOException cause);

/**
* Called when the exchange is released, so that cleanup actions may be
* performed - such as deregistering callbacks.
Expand Down
Loading

0 comments on commit 5710390

Please sign in to comment.