Author: remm Date: Tue Oct 27 15:05:45 2015 New Revision: 1710834 URL: http://svn.apache.org/viewvc?rev=1710834&view=rev Log: Remove "optimization" to avoid creating the SSLSession, since it is always used in Tomcat and looks cheap to create.
Modified: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java Modified: tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java?rev=1710834&r1=1710833&r2=1710834&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java Tue Oct 27 15:05:45 2015 @@ -30,7 +30,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -107,7 +106,6 @@ public final class OpenSSLEngine extends RENEGOTIATION_UNSUPPORTED.setStackTrace(new StackTraceElement[0]); ENCRYPTED_PACKET_OVERSIZED.setStackTrace(new StackTraceElement[0]); DESTROYED_UPDATER = AtomicIntegerFieldUpdater.newUpdater(OpenSSLEngine.class, "destroyed"); - SESSION_UPDATER = AtomicReferenceFieldUpdater.newUpdater(OpenSSLEngine.class, SSLSession.class, "session"); } private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14 @@ -140,7 +138,6 @@ public final class OpenSSLEngine extends } private static final AtomicIntegerFieldUpdater<OpenSSLEngine> DESTROYED_UPDATER; - private static final AtomicReferenceFieldUpdater<OpenSSLEngine, SSLSession> SESSION_UPDATER; private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL"; @@ -180,7 +177,7 @@ public final class OpenSSLEngine extends private String selectedProtocol = null; - private volatile SSLSession session; + private final OpenSSLSession session; /** * Creates a new instance @@ -198,6 +195,7 @@ public final class OpenSSLEngine extends if (sslCtx == 0) { throw new IllegalArgumentException(sm.getString("engine.noSSLContext")); } + session = new OpenSSLSession(); ssl = SSL.newSSL(sslCtx, !clientMode); networkBIO = SSL.makeNetworkBIO(ssl); this.fallbackApplicationProtocol = fallbackApplicationProtocol; @@ -427,12 +425,12 @@ public final class OpenSSLEngine extends // Do we have enough room in dst to write encrypted data? int capacity = dst.remaining(); if (capacity < pendingNet) { - return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, handshakeStatus, 0, bytesProduced); + return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, handshakeStatus, 0, 0); } // Write the pending data from the network BIO into the dst buffer try { - bytesProduced += readEncryptedData(dst, pendingNet); + bytesProduced = readEncryptedData(dst, pendingNet); } catch (Exception e) { throw new SSLException(e); } @@ -879,242 +877,6 @@ public final class OpenSSLEngine extends @Override public SSLSession getSession() { - // A other methods on SSLEngine are thread-safe we also need to make this thread-safe... - SSLSession session = this.session; - if (session == null) { - session = new SSLSession() { - // SSLSession implementation seems to not need to be thread-safe so no need for volatile etc. - private X509Certificate[] x509PeerCerts; - - // lazy init for memory reasons - private Map<String, Object> values; - - @Override - public byte[] getId() { - // We don't cache that to keep memory usage to a minimum. - byte[] id = SSL.getSessionId(ssl); - if (id == null) { - // The id should never be null, if it was null then the SESSION itself was not valid. - throw new IllegalStateException(sm.getString("engine.noSession")); - } - return id; - } - - @Override - public SSLSessionContext getSessionContext() { - return sessionContext; - } - - @Override - public long getCreationTime() { - // We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds. - return SSL.getTime(ssl) * 1000L; - } - - @Override - public long getLastAccessedTime() { - // TODO: Add proper implementation - return getCreationTime(); - } - - @Override - public void invalidate() { - // NOOP - } - - @Override - public boolean isValid() { - return false; - } - - @Override - public void putValue(String name, Object value) { - if (name == null) { - throw new IllegalArgumentException(sm.getString("engine.nullName")); - } - if (value == null) { - throw new IllegalArgumentException(sm.getString("engine.nullValue")); - } - Map<String, Object> values = this.values; - if (values == null) { - // Use size of 2 to keep the memory overhead small - values = this.values = new HashMap<>(2); - } - Object old = values.put(name, value); - if (value instanceof SSLSessionBindingListener) { - ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name)); - } - notifyUnbound(old, name); - } - - @Override - public Object getValue(String name) { - if (name == null) { - throw new IllegalArgumentException(sm.getString("engine.nullName")); - } - if (values == null) { - return null; - } - return values.get(name); - } - - @Override - public void removeValue(String name) { - if (name == null) { - throw new IllegalArgumentException(sm.getString("engine.nullName")); - } - Map<String, Object> values = this.values; - if (values == null) { - return; - } - Object old = values.remove(name); - notifyUnbound(old, name); - } - - @Override - public String[] getValueNames() { - Map<String, Object> values = this.values; - if (values == null || values.isEmpty()) { - return new String[0]; - } - return values.keySet().toArray(new String[values.size()]); - } - - private void notifyUnbound(Object value, String name) { - if (value instanceof SSLSessionBindingListener) { - ((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name)); - } - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - // these are lazy created to reduce memory overhead - Certificate[] c = peerCerts; - if (c == null) { - if (SSL.isInInit(ssl) != 0) { - throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer")); - } - c = peerCerts = initPeerCertChain(); - } - return c; - } - - @Override - public Certificate[] getLocalCertificates() { - // TODO: Find out how to get these - return EMPTY_CERTIFICATES; - } - - @Override - public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { - // these are lazy created to reduce memory overhead - X509Certificate[] c = x509PeerCerts; - if (c == null) { - if (SSL.isInInit(ssl) != 0) { - throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer")); - } - byte[][] chain = SSL.getPeerCertChain(ssl); - if (chain == null) { - throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer")); - } - X509Certificate[] peerCerts = new X509Certificate[chain.length]; - for (int i = 0; i < peerCerts.length; i++) { - try { - peerCerts[i] = X509Certificate.getInstance(chain[i]); - } catch (CertificateException e) { - throw new IllegalStateException(e); - } - } - c = x509PeerCerts = peerCerts; - } - return c; - } - - @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - Certificate[] peer = getPeerCertificates(); - if (peer == null || peer.length == 0) { - return null; - } - return principal(peer); - } - - @Override - public Principal getLocalPrincipal() { - Certificate[] local = getLocalCertificates(); - if (local == null || local.length == 0) { - return null; - } - return principal(local); - } - - private Principal principal(Certificate[] certs) { - return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal(); - } - - @Override - public String getCipherSuite() { - if (!handshakeFinished) { - return INVALID_CIPHER; - } - if (cipher == null) { - String c = toJavaCipherSuite(SSL.getCipherForSSL(ssl)); - if (c != null) { - cipher = c; - } - } - return cipher; - } - - @Override - public String getProtocol() { - String applicationProtocol = OpenSSLEngine.this.applicationProtocol; - if (applicationProtocol == null) { - applicationProtocol = SSL.getNextProtoNegotiated(ssl); - if (applicationProtocol == null) { - applicationProtocol = fallbackApplicationProtocol; - } - if (applicationProtocol != null) { - OpenSSLEngine.this.applicationProtocol = applicationProtocol.replace(':', '_'); - } else { - OpenSSLEngine.this.applicationProtocol = applicationProtocol = ""; - } - } - String version = SSL.getVersion(ssl); - if (applicationProtocol.isEmpty()) { - return version; - } else { - return version + ':' + applicationProtocol; - } - } - - @Override - public String getPeerHost() { - return null; - } - - @Override - public int getPeerPort() { - return 0; - } - - @Override - public int getPacketBufferSize() { - return MAX_ENCRYPTED_PACKET_LENGTH; - } - - @Override - public int getApplicationBufferSize() { - return MAX_PLAINTEXT_LENGTH; - } - }; - - if (!SESSION_UPDATER.compareAndSet(this, null, session)) { - // Was lazy created in the meantime so get the current reference. - session = this.session; - } - } - return session; } @@ -1176,6 +938,7 @@ public final class OpenSSLEngine extends selectedProtocol = SSL.getNextProtoNegotiated(ssl); } } + session.lastAccessedTime = System.currentTimeMillis(); // if SSL_do_handshake returns > 0 it means the handshake was finished. This means we can update // handshakeFinished directly and so eliminate unnecessary calls to SSL.isInInit(...) handshakeFinished = true; @@ -1212,6 +975,7 @@ public final class OpenSSLEngine extends selectedProtocol = SSL.getNextProtoNegotiated(ssl); } } + session.lastAccessedTime = System.currentTimeMillis(); handshakeFinished = true; return SSLEngineResult.HandshakeStatus.FINISHED; } @@ -1343,4 +1107,238 @@ public final class OpenSSLEngine extends // Call shutdown as the user may have created the OpenSslEngine and not used it at all. shutdown(); } + + private class OpenSSLSession implements SSLSession { + + // SSLSession implementation seems to not need to be thread-safe so no need for volatile etc. + private X509Certificate[] x509PeerCerts; + + // lazy init for memory reasons + private Map<String, Object> values; + + // Last accessed time + private long lastAccessedTime = -1; + + @Override + public byte[] getId() { + // We don't cache that to keep memory usage to a minimum. + byte[] id = SSL.getSessionId(ssl); + if (id == null) { + // The id should never be null, if it was null then the SESSION itself was not valid. + throw new IllegalStateException(sm.getString("engine.noSession")); + } + return id; + } + + @Override + public SSLSessionContext getSessionContext() { + return sessionContext; + } + + @Override + public long getCreationTime() { + // We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds. + return SSL.getTime(ssl) * 1000L; + } + + @Override + public long getLastAccessedTime() { + return (lastAccessedTime > 0) ? lastAccessedTime : getCreationTime(); + } + + @Override + public void invalidate() { + // NOOP + } + + @Override + public boolean isValid() { + return false; + } + + @Override + public void putValue(String name, Object value) { + if (name == null) { + throw new IllegalArgumentException(sm.getString("engine.nullName")); + } + if (value == null) { + throw new IllegalArgumentException(sm.getString("engine.nullValue")); + } + Map<String, Object> values = this.values; + if (values == null) { + // Use size of 2 to keep the memory overhead small + values = this.values = new HashMap<>(2); + } + Object old = values.put(name, value); + if (value instanceof SSLSessionBindingListener) { + ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name)); + } + notifyUnbound(old, name); + } + + @Override + public Object getValue(String name) { + if (name == null) { + throw new IllegalArgumentException(sm.getString("engine.nullName")); + } + if (values == null) { + return null; + } + return values.get(name); + } + + @Override + public void removeValue(String name) { + if (name == null) { + throw new IllegalArgumentException(sm.getString("engine.nullName")); + } + Map<String, Object> values = this.values; + if (values == null) { + return; + } + Object old = values.remove(name); + notifyUnbound(old, name); + } + + @Override + public String[] getValueNames() { + Map<String, Object> values = this.values; + if (values == null || values.isEmpty()) { + return new String[0]; + } + return values.keySet().toArray(new String[values.size()]); + } + + private void notifyUnbound(Object value, String name) { + if (value instanceof SSLSessionBindingListener) { + ((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name)); + } + } + + @Override + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + // these are lazy created to reduce memory overhead + Certificate[] c = peerCerts; + if (c == null) { + if (SSL.isInInit(ssl) != 0) { + throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer")); + } + c = peerCerts = initPeerCertChain(); + } + return c; + } + + @Override + public Certificate[] getLocalCertificates() { + // FIXME: Not currently exposed by the native API + return EMPTY_CERTIFICATES; + } + + @Override + public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { + // these are lazy created to reduce memory overhead + X509Certificate[] c = x509PeerCerts; + if (c == null) { + if (SSL.isInInit(ssl) != 0) { + throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer")); + } + byte[][] chain = SSL.getPeerCertChain(ssl); + if (chain == null) { + throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer")); + } + X509Certificate[] peerCerts = new X509Certificate[chain.length]; + for (int i = 0; i < peerCerts.length; i++) { + try { + peerCerts[i] = X509Certificate.getInstance(chain[i]); + } catch (CertificateException e) { + throw new IllegalStateException(e); + } + } + c = x509PeerCerts = peerCerts; + } + return c; + } + + @Override + public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { + Certificate[] peer = getPeerCertificates(); + if (peer == null || peer.length == 0) { + return null; + } + return principal(peer); + } + + @Override + public Principal getLocalPrincipal() { + Certificate[] local = getLocalCertificates(); + if (local == null || local.length == 0) { + return null; + } + return principal(local); + } + + private Principal principal(Certificate[] certs) { + return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal(); + } + + @Override + public String getCipherSuite() { + if (!handshakeFinished) { + return INVALID_CIPHER; + } + if (cipher == null) { + String c = toJavaCipherSuite(SSL.getCipherForSSL(ssl)); + if (c != null) { + cipher = c; + } + } + return cipher; + } + + @Override + public String getProtocol() { + String applicationProtocol = OpenSSLEngine.this.applicationProtocol; + if (applicationProtocol == null) { + applicationProtocol = SSL.getNextProtoNegotiated(ssl); + if (applicationProtocol == null) { + applicationProtocol = fallbackApplicationProtocol; + } + if (applicationProtocol != null) { + OpenSSLEngine.this.applicationProtocol = applicationProtocol.replace(':', '_'); + } else { + OpenSSLEngine.this.applicationProtocol = applicationProtocol = ""; + } + } + String version = SSL.getVersion(ssl); + if (applicationProtocol.isEmpty()) { + return version; + } else { + return version + ':' + applicationProtocol; + } + } + + @Override + public String getPeerHost() { + // Not available for now in Tomcat (needs to be passed during engine creation) + return null; + } + + @Override + public int getPeerPort() { + // Not available for now in Tomcat (needs to be passed during engine creation) + return 0; + } + + @Override + public int getPacketBufferSize() { + return MAX_ENCRYPTED_PACKET_LENGTH; + } + + @Override + public int getApplicationBufferSize() { + return MAX_PLAINTEXT_LENGTH; + } + + } + } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org