JSSE: optimize locking of storeLock inside WolfSSLAuthStore to reduce thread contention

pull/274/head
Chris Conlon 2025-06-10 10:50:20 -06:00
parent 9ee4cadde7
commit 5be4a99e0d
1 changed files with 115 additions and 110 deletions

View File

@ -314,13 +314,14 @@ public class WolfSSLAuthStore {
* object if not in cache, called on server side, or host * object if not in cache, called on server side, or host
* is null * is null
*/ */
protected synchronized WolfSSLImplementSSLSession getSession( protected WolfSSLImplementSSLSession getSession(
WolfSSLSession ssl, int port, String host, boolean clientMode, WolfSSLSession ssl, int port, String host, boolean clientMode,
String[] enabledCipherSuites, String[] enabledProtocols) { String[] enabledCipherSuites, String[] enabledProtocols) {
boolean needNewSession = false; boolean needNewSession = false;
WolfSSLImplementSSLSession ses = null; WolfSSLImplementSSLSession ses = null;
String toHash = null; String toHash = null;
int hashCode = 0;
if (ssl == null) { if (ssl == null) {
return null; return null;
@ -339,104 +340,106 @@ public class WolfSSLAuthStore {
* Synchronizes on storeLock internally. */ * Synchronizes on storeLock internally. */
printSessionStoreStatus(); printSessionStoreStatus();
/* Lock on static/global storeLock, since Java session cache table /* Generate cache key hash (host:port), outside lock */
* is shared between all threads */ toHash = host.concat(Integer.toString(port));
hashCode = toHash.hashCode();
/* Lock on static/global storeLock while getting session out of
* store, since Java session cache table is shared between all
* threads */
synchronized (storeLock) { synchronized (storeLock) {
/* Generate cache key hash (host:port) */
toHash = host.concat(Integer.toString(port));
/* Try getting session out of Java store */ /* Try getting session out of Java store */
ses = store.get(toHash.hashCode()); ses = store.get(hashCode);
/* Remove old entry from table. TLS 1.3 binder changes between /* Remove old entry from table. TLS 1.3 binder changes between
* resumptions and stored session should only be used to * resumptions and stored session should only be used to
* resume once. New session structure/object will be cached * resume once. New session structure/object will be cached
* after the resumed session completes the handshake, for * after the resumed session completes the handshake, for
* subsequent resumption attempts to use. */ * subsequent resumption attempts to use. */
store.remove(toHash.hashCode()); store.remove(hashCode);
}
/* Check conditions where we need to create a new new session: /* Check conditions where we need to create a new new session:
* 1. Session not found in cache * 1. Session not found in cache
* 2. Session marked as not resumable * 2. Session marked as not resumable
* 3. Original session cipher suite not available * 3. Original session cipher suite not available
* 4. Original session protocol version not available * 4. Original session protocol version not available
*/ */
if (ses == null || if (ses == null ||
!ses.isResumable() || !ses.isResumable() ||
!sessionCipherSuiteAvailable(ses, enabledCipherSuites) || !sessionCipherSuiteAvailable(ses, enabledCipherSuites) ||
!sessionProtocolAvailable(ses, enabledProtocols)) { !sessionProtocolAvailable(ses, enabledProtocols)) {
needNewSession = true; needNewSession = true;
}
if (needNewSession) {
if (ses == null) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "session not found in cache table, " +
"creating new session");
}
else if (!ses.isResumable()) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "native WOLFSSL_SESSION not resumable, " +
"creating new session");
}
else if (!sessionCipherSuiteAvailable(
ses, enabledCipherSuites)) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "cipher suite used in original WOLFSSL_SESSION " +
"not available, creating new session");
} }
if (needNewSession) { /* Not found in stored sessions create a new one */
if (ses == null) { ses = new WolfSSLImplementSSLSession(ssl, port, host, this);
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, ses.setValid(true); /* new sessions marked as valid */
() -> "session not found in cache table, " +
"creating new session"); ses.isFromTable = false;
} ses.setPseudoSessionId(
else if (!ses.isResumable()) { Integer.toString(ssl.hashCode()).getBytes());
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO, }
() -> "native WOLFSSL_SESSION not resumable, " + else {
"creating new session"); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
} () -> "session found in cache, trying to resume");
else if (!sessionCipherSuiteAvailable(
ses, enabledCipherSuites)) { ses.isFromTable = true;
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "cipher suite used in original WOLFSSL_SESSION " + /* Check if the session has stored SNI server names */
"not available, creating new session"); List<SNIServerName> sniNames = ses.getSNIServerNames();
} if (sniNames != null && !sniNames.isEmpty()) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Found SNI server names in cached session");
/* Apply SNI settings to the SSL connection */
for (SNIServerName name : sniNames) {
if (name instanceof SNIHostName) {
String hostName = ((SNIHostName)name).getAsciiName();
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Applying SNI hostname for resumption: " +
hostName);
/* Set the SNI directly on the SSL object */
ssl.useSNI((byte)WolfSSL.WOLFSSL_SNI_HOST_NAME,
hostName.getBytes());
}
}
}
if (ses.resume(ssl) != WolfSSL.SSL_SUCCESS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "native wolfSSL_set_session() failed, " +
"creating new session");
/* Not found in stored sessions create a new one */
ses = new WolfSSLImplementSSLSession(ssl, port, host, this); ses = new WolfSSLImplementSSLSession(ssl, port, host, this);
ses.setValid(true); /* new sessions marked as valid */ ses.setValid(true);
ses.isFromTable = false; ses.isFromTable = false;
ses.setPseudoSessionId( ses.setPseudoSessionId(
Integer.toString(ssl.hashCode()).getBytes()); Integer.toString(ssl.hashCode()).getBytes());
} }
else {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "session found in cache, trying to resume");
ses.isFromTable = true;
/* Check if the session has stored SNI server names */
List<SNIServerName> sniNames = ses.getSNIServerNames();
if (sniNames != null && !sniNames.isEmpty()) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Found SNI server names in cached session");
/* Apply SNI settings to the SSL connection */
for (SNIServerName name : sniNames) {
if (name instanceof SNIHostName) {
String hostName = ((SNIHostName)name).getAsciiName();
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "Applying SNI hostname for resumption: " +
hostName);
/* Set the SNI directly on the SSL object */
ssl.useSNI((byte)WolfSSL.WOLFSSL_SNI_HOST_NAME,
hostName.getBytes());
}
}
}
if (ses.resume(ssl) != WolfSSL.SSL_SUCCESS) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "native wolfSSL_set_session() failed, " +
"creating new session");
ses = new WolfSSLImplementSSLSession(ssl, port, host, this);
ses.setValid(true);
ses.isFromTable = false;
ses.setPseudoSessionId(
Integer.toString(ssl.hashCode()).getBytes());
}
}
return ses;
} }
return ses;
} }
/** /**
@ -640,39 +643,41 @@ public class WolfSSLAuthStore {
return WolfSSL.SSL_FAILURE; return WolfSSL.SSL_FAILURE;
} }
/* Lock access to store while adding new session, store is global */ if (session.getPeerHost() != null) {
synchronized (storeLock) { /* Generate key for storing into session table (host:port) */
if (session.getPeerHost() != null) { toHash = session.getPeerHost().concat(Integer.toString(
/* Generate key for storing into session table (host:port) */ session.getPeerPort()));
toHash = session.getPeerHost().concat(Integer.toString( hashCode = toHash.hashCode();
session.getPeerPort())); }
hashCode = toHash.hashCode(); else {
/* If no peer host is available then create hash key from
* session ID if not null, not zero length, and not all zeros */
byte[] sessionId = session.getId();
if (sessionId != null && sessionId.length > 0 &&
(idAllZeros(sessionId) == false)) {
hashCode = Arrays.toString(session.getId()).hashCode();
} else {
hashCode = 0;
} }
else { }
/* If no peer host is available then create hash key from
* session ID if not null, not zero length, and not all zeros */ /* Always try to store session into cache table, as long as we
byte[] sessionId = session.getId(); * have a hashCode. If session already exists for hashCode, it
if (sessionId != null && sessionId.length > 0 && * will be overwritten with new/refreshed version */
(idAllZeros(sessionId) == false)) { if (hashCode != 0) {
hashCode = Arrays.toString(session.getId()).hashCode(); WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
} else { () -> "stored session in cache table (host: " +
hashCode = 0; session.getPeerHost() + ", port: " +
} session.getPeerPort() + ") " + "hashCode = " + hashCode +
" side = " + session.getSideString());
session.isInTable = true;
/* Lock access to store while adding new session, store is global */
synchronized (storeLock) {
store.put(hashCode, session);
} }
/* Always try to store session into cache table, as long as we printSessionStoreStatus();
* have a hashCode. If session already exists for hashCode, it
* will be overwritten with new/refreshed version */
if (hashCode != 0) {
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
() -> "stored session in cache table (host: " +
session.getPeerHost() + ", port: " +
session.getPeerPort() + ") " + "hashCode = " + hashCode +
" side = " + session.getSideString());
store.put(hashCode, session);
session.isInTable = true;
printSessionStoreStatus();
}
} }
return WolfSSL.SSL_SUCCESS; return WolfSSL.SSL_SUCCESS;