Adding benchmarks for socket implementations. (#75)
diff --git a/build.gradle b/build.gradle
index d6e84ab..3ee0373 100644
--- a/build.gradle
+++ b/build.gradle
@@ -77,8 +77,8 @@
bouncycastle_apis: 'org.bouncycastle:bcpkix-jdk15on:1.55',
// Benchmark dependencies
- jmh_generator_annprocess : "org.openjdk.jmh:jmh-generator-annprocess:$jmhVersion",
- netty: 'io.netty:netty-codec-http2:4.1.8.Final',
+ jmh_core: "org.openjdk.jmh:jmh-core:${jmhVersion}",
+ netty_handler: 'io.netty:netty-handler:4.1.8.Final',
netty_tcnative: 'io.netty:netty-tcnative-boringssl-static:1.1.33.Fork26',
]
}
diff --git a/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java b/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
index ca5b11d..c2272d5 100644
--- a/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
+++ b/common/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java
@@ -16,8 +16,6 @@
package org.conscrypt;
-import static org.conscrypt.Platform.getFileDescriptor;
-
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
@@ -25,11 +23,12 @@
import java.security.KeyManagementException;
public class OpenSSLSocketFactoryImpl extends javax.net.ssl.SSLSocketFactory {
- private static boolean alwaysUseSocketEngine =
- Boolean.parseBoolean(System.getProperty("org.conscrypt.alwaysUseEngineSocket"));
+ private static boolean useEngineSocketByDefault =
+ Boolean.parseBoolean(System.getProperty("org.conscrypt.useEngineSocketByDefault"));
private final SSLParametersImpl sslParameters;
private final IOException instantiationException;
+ private boolean useEngineSocket = useEngineSocketByDefault;
public OpenSSLSocketFactoryImpl() {
SSLParametersImpl sslParametersLocal = null;
@@ -49,8 +48,19 @@
this.instantiationException = null;
}
- public static void setAlwaysUseEngineSocket(boolean enable) {
- alwaysUseSocketEngine = enable;
+ /**
+ * Configures the default socket to be created for all instances.
+ */
+ public static void setUseEngineSocketByDefault(boolean useEngineSocket) {
+ useEngineSocketByDefault = useEngineSocket;
+ }
+
+ /**
+ * Configures the socket to be created for this instance. If not called,
+ * {@link #useEngineSocketByDefault} will be used.
+ */
+ public void setUseEngineSocket(boolean useEngineSocket) {
+ this.useEngineSocket = useEngineSocket;
}
@Override
@@ -115,18 +125,12 @@
} catch (RuntimeException re) {
// Ignore
}
- if (socketHasFd && !alwaysUseSocketEngine) {
- return new OpenSSLSocketImplWrapper(s,
- hostname,
- port,
- autoClose,
- (SSLParametersImpl) sslParameters.clone());
+ if (socketHasFd && !useEngineSocket) {
+ return new OpenSSLSocketImplWrapper(
+ s, hostname, port, autoClose, (SSLParametersImpl) sslParameters.clone());
} else {
- return new OpenSSLEngineSocketImpl(s,
- hostname,
- port,
- autoClose,
- (SSLParametersImpl) sslParameters.clone());
+ return new OpenSSLEngineSocketImpl(
+ s, hostname, port, autoClose, (SSLParametersImpl) sslParameters.clone());
}
}
}
diff --git a/openjdk-benchmarks/build.gradle b/openjdk-benchmarks/build.gradle
index 2c4e1d1..7d82caa 100644
--- a/openjdk-benchmarks/build.gradle
+++ b/openjdk-benchmarks/build.gradle
@@ -22,14 +22,13 @@
}
dependencies {
- compile project(path: ':conscrypt-openjdk', configuration: "$openJdkConfiguration"), libraries.guava
- jmh libraries.junit,
- libraries.netty,
- libraries.netty_tcnative,
- // JMH plugin doesn't seem to include this dependency by default. This version of the JMH
- // plugin seems to require the use of v1.12 for all other JMH dependencies, so not overriding
- // them here.
- libraries.jmh_generator_annprocess
+ compile project(path: ':conscrypt-openjdk', configuration: "$openJdkConfiguration"),
+ libraries.guava,
+ libraries.junit,
+ libraries.netty_handler,
+ libraries.netty_tcnative
+
+ jmh libraries.jmh_core
}
// Running benchmarks in IntelliJ seems broken without this.
diff --git a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketBenchmark.java b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketBenchmark.java
new file mode 100644
index 0000000..ee7aee6
--- /dev/null
+++ b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ClientSocketBenchmark.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.benchmarks;
+
+import static org.conscrypt.benchmarks.Util.LOCALHOST;
+import static org.conscrypt.benchmarks.Util.getConscryptSocketFactory;
+import static org.conscrypt.benchmarks.Util.getJdkSocketFactory;
+import static org.conscrypt.benchmarks.Util.getProtocols;
+import static org.conscrypt.benchmarks.Util.newTextMessage;
+import static org.conscrypt.benchmarks.Util.pickUnusedPort;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+
+/**
+ * Benchmark for comparing performance of client socket implementations. All benchmarks use Netty
+ * with tcnative as the server.
+ */
+@State(Scope.Benchmark)
+public class ClientSocketBenchmark {
+ /**
+ * Various factories for SSL sockets.
+ */
+ public enum SslSocketType {
+ JDK {
+ private final SSLSocketFactory socketFactory = getJdkSocketFactory();
+ @Override
+ SSLSocket newSslSocket(String host, int port) {
+ try {
+ return (SSLSocket) socketFactory.createSocket(host, port);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ },
+ CONSCRYPT {
+ private final SSLSocketFactory socketFactory = getConscryptSocketFactory(false);
+ @Override
+ SSLSocket newSslSocket(String host, int port) {
+ try {
+ return (SSLSocket) socketFactory.createSocket(host, port);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ },
+ CONSCRYPT_ENGINE {
+ private final SSLSocketFactory socketFactory = getConscryptSocketFactory(true);
+ @Override
+ SSLSocket newSslSocket(String host, int port) {
+ try {
+ return (SSLSocket) socketFactory.createSocket(
+ SocketFactory.getDefault().createSocket(host, port), host, port, true);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ final SSLSocket newSslSocket(String host, int port, String cipher) {
+ try {
+ SSLSocket sslSocket = newSslSocket(host, port);
+ sslSocket.setEnabledProtocols(getProtocols());
+ sslSocket.setEnabledCipherSuites(new String[] {cipher});
+ return sslSocket;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ abstract SSLSocket newSslSocket(String host, int port);
+ }
+
+ @Param public SslSocketType sslSocketType;
+
+ @Param({"64", "128", "512", "1024", "4096"}) public int messageSize;
+
+ @Param({"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"}) public String cipher;
+
+ private TestClient client;
+ private NettyEchoServer server;
+ private byte[] message;
+ private byte[] response;
+
+ @Setup
+ public void setup() throws Exception {
+ message = newTextMessage(messageSize);
+ response = new byte[messageSize];
+
+ int port = pickUnusedPort();
+ server = new NettyEchoServer(port, messageSize, cipher);
+ server.start();
+
+ client = new TestClient(sslSocketType.newSslSocket(LOCALHOST, port, cipher));
+ client.start();
+ }
+
+ @TearDown
+ public void teardown() throws Exception {
+ client.stop();
+ server.stop();
+ }
+
+ @Benchmark
+ public void pingPong() throws IOException {
+ client.sendMessage(message);
+ int numBytes = client.readMessage(response);
+ assertEquals(messageSize, numBytes);
+ }
+
+ public static void main(String[] args) throws Exception {
+ ClientSocketBenchmark bm = new ClientSocketBenchmark();
+ bm.sslSocketType = SslSocketType.CONSCRYPT_ENGINE;
+ bm.messageSize = 1024;
+ bm.cipher = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
+ bm.setup();
+ try {
+ while (true) {
+ if (Thread.interrupted()) {
+ break;
+ }
+ bm.pingPong();
+ }
+ } finally {
+ bm.teardown();
+ }
+ }
+}
diff --git a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketBenchmark.java b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketBenchmark.java
new file mode 100644
index 0000000..c00a9c5
--- /dev/null
+++ b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/ServerSocketBenchmark.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.benchmarks;
+
+import static org.conscrypt.benchmarks.Util.getProtocols;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+
+/**
+ * Benchmark for comparing performance of server socket implementations. All benchmarks use the
+ * standard JDK TLS implementation.
+ */
+@State(Scope.Benchmark)
+public class ServerSocketBenchmark {
+ /**
+ * Various factories for SSL server sockets.
+ */
+ public enum SslSocketType {
+ JDK(Util.getJdkServerSocketFactory()),
+ CONSCRYPT(Util.getConscryptServerSocketFactory());
+
+ private final SSLServerSocketFactory serverSocketFactory;
+
+ SslSocketType(SSLServerSocketFactory serverSocketFactory) {
+ this.serverSocketFactory = serverSocketFactory;
+ }
+
+ final SSLServerSocket newServerSocket(String cipher) {
+ try {
+ int port = Util.pickUnusedPort();
+ SSLServerSocket sslSocket =
+ (SSLServerSocket) serverSocketFactory.createServerSocket(port);
+ sslSocket.setEnabledProtocols(Util.getProtocols());
+ sslSocket.setEnabledCipherSuites(new String[] {cipher});
+ return sslSocket;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Param public SslSocketType sslSocketType;
+
+ @Param({"64", "128", "512", "1024", "4096"}) public int messageSize;
+
+ @Param({"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"}) public String cipher;
+
+ private TestClient client;
+ private EchoServer server;
+ private byte[] message;
+ private byte[] response;
+
+ @Setup
+ public void setup() throws Exception {
+ message = Util.newTextMessage(messageSize);
+ response = new byte[messageSize];
+
+ server = new EchoServer(sslSocketType.newServerSocket(cipher), messageSize);
+
+ Future connectedFuture = server.start();
+
+ SSLSocket socket;
+ try {
+ SSLSocketFactory socketFactory = Util.getJdkSocketFactory();
+ socket = (SSLSocket) socketFactory.createSocket(Util.LOCALHOST, server.port());
+ socket.setEnabledProtocols(getProtocols());
+ socket.setEnabledCipherSuites(new String[] {cipher});
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ client = new TestClient(socket);
+ client.start();
+
+ // Wait for the initial connection to complete.
+ connectedFuture.get(5, TimeUnit.SECONDS);
+ }
+
+ @TearDown
+ public void teardown() throws Exception {
+ client.stop();
+ server.stop();
+ }
+
+ @Benchmark
+ public void pingPong() throws IOException {
+ client.sendMessage(message);
+ int numBytes = client.readMessage(response);
+ assertEquals(messageSize, numBytes);
+ }
+}
diff --git a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java
index 89cf457..cd0083e 100644
--- a/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java
+++ b/openjdk-benchmarks/src/jmh/java/org/conscrypt/benchmarks/SslEngineBenchmark.java
@@ -14,16 +14,37 @@
* limitations under the License.
*/
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
package org.conscrypt.benchmarks;
import static java.lang.Math.max;
+import static org.conscrypt.benchmarks.Util.PROTOCOL_TLS_V1_2;
+import static org.conscrypt.benchmarks.Util.initClientSslContext;
+import static org.conscrypt.benchmarks.Util.initEngine;
+import static org.conscrypt.benchmarks.Util.initServerContext;
+import static org.conscrypt.benchmarks.Util.newNettyClientContext;
+import static org.conscrypt.benchmarks.Util.newNettyServerContext;
+import static org.conscrypt.benchmarks.Util.newTextMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.handler.ssl.SslContext;
-import io.netty.handler.ssl.SslContextBuilder;
-import java.io.File;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.SSLContext;
@@ -42,14 +63,10 @@
*/
@State(Scope.Benchmark)
public class SslEngineBenchmark {
- private static final byte[] CHARS =
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".getBytes();
- private static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
-
public enum SslProvider {
JDK {
- private final SSLContext clientContext = initContext(newContext(), true);
- private final SSLContext serverContext = initContext(newContext(), false);
+ private final SSLContext clientContext = initClientSslContext(newContext());
+ private final SSLContext serverContext = initServerContext(newContext());
@Override
SSLEngine newClientEngine(String cipher) {
@@ -70,8 +87,8 @@
}
},
CONSCRYPT {
- private final SSLContext clientContext = initContext(newContext(), true);
- private final SSLContext serverContext = initContext(newContext(), false);
+ private final SSLContext clientContext = initClientSslContext(newContext());
+ private final SSLContext serverContext = initServerContext(newContext());
@Override
SSLEngine newClientEngine(String cipher) {
@@ -92,8 +109,8 @@
}
},
NETTY {
- private final SslContext clientContext = newClientContext();
- private final SslContext serverContext = newServerContext();
+ private final SslContext clientContext = newNettyClientContext(null);
+ private final SslContext serverContext = newNettyServerContext(null);
@Override
SSLEngine newClientEngine(String cipher) {
@@ -106,53 +123,10 @@
return initEngine(
serverContext.newEngine(UnpooledByteBufAllocator.DEFAULT), cipher, false);
}
-
- private SslContext newClientContext() {
- try {
- File clientCert = Util.loadCert("ca.pem");
- return SslContextBuilder.forClient()
- .sslProvider(io.netty.handler.ssl.SslProvider.OPENSSL)
- .trustManager(clientCert)
- .build();
- } catch (SSLException e) {
- throw new RuntimeException(e);
- }
- }
-
- private SslContext newServerContext() {
- try {
- File serverCert = Util.loadCert("server1.pem");
- File serverKey = Util.loadCert("server1.key");
- return SslContextBuilder.forServer(serverCert, serverKey)
- .sslProvider(io.netty.handler.ssl.SslProvider.OPENSSL)
- .build();
- } catch (SSLException e) {
- throw new RuntimeException(e);
- }
- }
};
abstract SSLEngine newClientEngine(String cipher);
abstract SSLEngine newServerEngine(String cipher);
-
- final SSLContext initContext(SSLContext context, boolean client) {
- if (client) {
- File cert = Util.loadCert("ca.pem");
- context = Util.initClientSslContext(context, cert);
- } else {
- File cert = Util.loadCert("server1.pem");
- File key = Util.loadCert("server1.key");
- context = Util.initServerContext(context, cert, key);
- }
- return context;
- }
-
- final SSLEngine initEngine(SSLEngine engine, String cipher, boolean client) {
- engine.setEnabledProtocols(new String[] {PROTOCOL_TLS_V1_2});
- engine.setEnabledCipherSuites(new String[] {cipher});
- engine.setUseClientMode(client);
- return engine;
- }
}
public enum BufferType {
@@ -195,12 +169,10 @@
encryptedBuffer = bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
// Generate the message to be sent from the client.
- clientCleartextBuffer = bufferType.newBuffer(messageSize);
serverCleartextBuffer = bufferType.newBuffer(
max(messageSize, serverEngine.getSession().getApplicationBufferSize()));
- for (int i = 0; clientCleartextBuffer.hasRemaining(); i = (i + 1) % CHARS.length) {
- clientCleartextBuffer.put(CHARS[i]);
- }
+ clientCleartextBuffer = bufferType.newBuffer(messageSize);
+ clientCleartextBuffer.put(newTextMessage(messageSize));
clientCleartextBuffer.flip();
// Complete the initial TLS handshake.
diff --git a/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/EchoServer.java b/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/EchoServer.java
new file mode 100644
index 0000000..25edaf6
--- /dev/null
+++ b/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/EchoServer.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.benchmarks;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * Simple echo server that responds with an identical message to the one received.
+ */
+final class EchoServer {
+ private final ExecutorService executor = Executors.newSingleThreadExecutor();
+ private final SSLServerSocket serverSocket;
+ private final int messageSize;
+ private final byte[] buffer;
+ private SSLSocket socket;
+ private volatile boolean stopping;
+
+ EchoServer(SSLServerSocket serverSocket, int messageSize) {
+ this.serverSocket = serverSocket;
+ this.messageSize = messageSize;
+ buffer = new byte[messageSize];
+ }
+
+ Future<?> start() {
+ return executor.submit(new AcceptTask());
+ }
+
+ void stop() {
+ try {
+ stopping = true;
+ if (socket != null) {
+ socket.close();
+ }
+ serverSocket.close();
+ executor.shutdown();
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ } catch (IOException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ int port() {
+ return serverSocket.getLocalPort();
+ }
+
+ private final class AcceptTask implements Runnable {
+ @Override
+ public void run() {
+ try {
+ if (stopping) {
+ return;
+ }
+ socket = (SSLSocket) serverSocket.accept();
+
+ if (stopping) {
+ return;
+ }
+ executor.execute(new ReadTask());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private final class ReadTask implements Runnable {
+ @Override
+ public void run() {
+ try {
+ if (stopping) {
+ return;
+ }
+ byte[] output = readMessage();
+ sendMessage(output);
+
+ if (stopping) {
+ return;
+ }
+ // Keep running the task until it's being shut down.
+ executor.execute(this);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private byte[] readMessage() {
+ try {
+ int totalBytesRead = 0;
+ while (totalBytesRead < messageSize) {
+ int remaining = messageSize - totalBytesRead;
+ int bytesRead = socket.getInputStream().read(buffer, totalBytesRead, remaining);
+ if (bytesRead == -1) {
+ break;
+ }
+ totalBytesRead += bytesRead;
+ }
+ return Arrays.copyOfRange(buffer, 0, totalBytesRead);
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void sendMessage(byte[] data) {
+ try {
+ socket.getOutputStream().write(data);
+ socket.getOutputStream().flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/NettyEchoServer.java b/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/NettyEchoServer.java
new file mode 100644
index 0000000..947d78f
--- /dev/null
+++ b/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/NettyEchoServer.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.benchmarks;
+
+import static io.netty.channel.ChannelOption.SO_BACKLOG;
+import static io.netty.channel.ChannelOption.SO_KEEPALIVE;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslHandler;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * A test server based on Netty and Netty-tcnative that auto-replies with every message
+ * it receives.
+ */
+final class NettyEchoServer {
+ private final EventLoopGroup group = new NioEventLoopGroup();
+ private final int port;
+ private final int messageSize;
+ private Channel channel;
+ private String cipher;
+
+ NettyEchoServer(int port, int messageSize, String cipher) {
+ this.port = port;
+ this.messageSize = messageSize;
+ this.cipher = cipher;
+ }
+
+ void start() {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(group);
+ b.channel(NioServerSocketChannel.class);
+ b.option(SO_BACKLOG, 128);
+ b.childOption(SO_KEEPALIVE, true);
+ b.childHandler(new ChannelInitializer<Channel>() {
+ @Override
+ public void initChannel(final Channel ch) throws Exception {
+ SslContext context = Util.newNettyServerContext(cipher);
+ SSLEngine sslEngine = context.newEngine(ch.alloc());
+ ch.pipeline().addFirst(new SslHandler(sslEngine));
+ ch.pipeline().addLast(new EchoService());
+ }
+ });
+ // Bind and start to accept incoming connections.
+ ChannelFuture future = b.bind(port);
+ try {
+ future.await();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException("Interrupted waiting for bind");
+ }
+ if (!future.isSuccess()) {
+ throw new RuntimeException("Failed to bind", future.cause());
+ }
+ channel = future.channel();
+ }
+
+ void stop() {
+ if (channel != null) {
+ channel.close().awaitUninterruptibly();
+ group.shutdownGracefully(1, 5, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Handler that automatically responds with ever message it receives.
+ */
+ private final class EchoService extends ByteToMessageDecoder {
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
+ throws Exception {
+ if (in.readableBytes() >= messageSize) {
+ // Copy the input to a new direct buffer.
+ ByteBuf response = ctx.alloc().directBuffer(messageSize);
+ response.writeBytes(in, in.readerIndex(), messageSize);
+ in.skipBytes(messageSize);
+
+ ctx.writeAndFlush(response);
+ }
+ }
+ }
+}
diff --git a/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/TestClient.java b/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/TestClient.java
new file mode 100644
index 0000000..bbeeae4
--- /dev/null
+++ b/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/TestClient.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.conscrypt.benchmarks;
+
+import java.io.IOException;
+import javax.net.ssl.SSLSocket;
+
+/**
+ * Client-side endpoint. Provides basic services for sending/receiving messages from the client
+ * socket.
+ */
+final class TestClient {
+ private final SSLSocket socket;
+
+ TestClient(SSLSocket socket) {
+ this.socket = socket;
+ }
+
+ void start() {
+ try {
+ socket.startHandshake();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void stop() {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ int readMessage(byte[] buffer) {
+ try {
+ int totalBytesRead = 0;
+ while (totalBytesRead < buffer.length) {
+ int remaining = buffer.length - totalBytesRead;
+ int bytesRead = socket.getInputStream().read(buffer, totalBytesRead, remaining);
+ if (bytesRead == -1) {
+ break;
+ }
+ totalBytesRead += bytesRead;
+ }
+ return totalBytesRead;
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ void sendMessage(byte[] data) {
+ try {
+ socket.getOutputStream().write(data);
+ socket.getOutputStream().flush();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/Util.java b/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/Util.java
index ba6c20a..eb5172b 100644
--- a/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/Util.java
+++ b/openjdk-benchmarks/src/main/java/org/conscrypt/benchmarks/Util.java
@@ -19,6 +19,8 @@
import com.google.common.base.Charsets;
import com.google.common.io.BaseEncoding;
import com.google.common.io.CharStreams;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
@@ -28,35 +30,169 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.net.ServerSocket;
import java.security.KeyException;
import java.security.KeyFactory;
import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.security.auth.x500.X500Principal;
+import org.conscrypt.OpenSSLProvider;
+import org.conscrypt.OpenSSLSocketFactoryImpl;
/**
* Utility methods to support testing.
*/
final class Util {
+ private static final Provider JDK_PROVIDER = getDefaultTlsProvider();
+ private static final Provider CONSCRYPT_PROVIDER = new OpenSSLProvider();
+ private static final byte[] CHARS =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".getBytes();
private static final Pattern KEY_PATTERN =
Pattern.compile("-+BEGIN\\s+.*PRIVATE\\s+KEY[^-]*-+(?:\\s|\\r|\\n)+" + // Header
"([a-z0-9+/=\\r\\n]+)" + // Base64 text
"-+END\\s+.*PRIVATE\\s+KEY[^-]*-+", // Footer
Pattern.CASE_INSENSITIVE);
+ static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
+ static final String PROVIDER_PROPERTY = "SSLContext.TLSv1.2";
+ static final String LOCALHOST = "localhost";
+
private Util() {}
/**
+ * Returns an array containing only {@link #PROTOCOL_TLS_V1_2}.
+ */
+ static String[] getProtocols() {
+ return new String[] {PROTOCOL_TLS_V1_2};
+ }
+
+ static SSLSocketFactory getJdkSocketFactory() {
+ return getSocketFactory(JDK_PROVIDER);
+ }
+
+ static SSLServerSocketFactory getJdkServerSocketFactory() {
+ return getServerSocketFactory(JDK_PROVIDER);
+ }
+
+ static SSLSocketFactory getConscryptSocketFactory(boolean useEngineSocket) {
+ OpenSSLSocketFactoryImpl socketFactory =
+ (OpenSSLSocketFactoryImpl) getSocketFactory(CONSCRYPT_PROVIDER);
+ socketFactory.setUseEngineSocket(useEngineSocket);
+ return socketFactory;
+ }
+
+ static SSLServerSocketFactory getConscryptServerSocketFactory() {
+ return getServerSocketFactory(CONSCRYPT_PROVIDER);
+ }
+
+ private static SSLSocketFactory getSocketFactory(Provider provider) {
+ SSLContext clientContext = initClientSslContext(newContext(provider));
+ return clientContext.getSocketFactory();
+ }
+
+ private static SSLServerSocketFactory getServerSocketFactory(Provider provider) {
+ SSLContext serverContext = initServerContext(newContext(provider));
+ return serverContext.getServerSocketFactory();
+ }
+
+ static SSLContext newContext(Provider provider) {
+ try {
+ return SSLContext.getInstance("TLS", provider);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static SslContext newNettyClientContext(String cipher) {
+ try {
+ File clientCert = loadCert("ca.pem");
+ SslContextBuilder ctx = SslContextBuilder.forClient()
+ .sslProvider(io.netty.handler.ssl.SslProvider.OPENSSL)
+ .trustManager(clientCert);
+ if (cipher != null) {
+ ctx.ciphers(Collections.singletonList(cipher));
+ }
+ return ctx.build();
+ } catch (SSLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static SslContext newNettyServerContext(String cipher) {
+ try {
+ File serverCert = loadCert("server1.pem");
+ File serverKey = loadCert("server1.key");
+ SslContextBuilder ctx = SslContextBuilder.forServer(serverCert, serverKey)
+ .sslProvider(io.netty.handler.ssl.SslProvider.OPENSSL);
+ if (cipher != null) {
+ ctx.ciphers(Collections.singletonList(cipher));
+ }
+ return ctx.build();
+ } catch (SSLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Picks a port that is not used right at this moment.
+ * Warning: Not thread safe. May see "BindException: Address already in use: bind" if using the
+ * returned port to create a new server socket when other threads/processes are concurrently
+ * creating new sockets without a specific port.
+ */
+ static int pickUnusedPort() {
+ try {
+ ServerSocket serverSocket = new ServerSocket(0);
+ int port = serverSocket.getLocalPort();
+ serverSocket.close();
+ return port;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Creates a text message of the given length.
+ */
+ static byte[] newTextMessage(int length) {
+ byte[] msg = new byte[length];
+ for (int msgIndex = 0; msgIndex < length; ) {
+ int remaining = length - msgIndex;
+ int numChars = Math.min(remaining, CHARS.length);
+ System.arraycopy(CHARS, 0, msg, msgIndex, numChars);
+ msgIndex += numChars;
+ }
+ return msg;
+ }
+
+ /**
+ * Initializes the given engine with the cipher and client mode.
+ */
+ static SSLEngine initEngine(SSLEngine engine, String cipher, boolean client) {
+ engine.setEnabledProtocols(getProtocols());
+ engine.setEnabledCipherSuites(new String[] {cipher});
+ engine.setUseClientMode(client);
+ return engine;
+ }
+
+ /**
* Load a file from the resources folder.
*
* @param name name of a file in src/main/resources/certs.
@@ -84,9 +220,16 @@
}
/**
+ * Initializes the given client-side {@code context} with a default cert.
+ */
+ static SSLContext initClientSslContext(SSLContext context) {
+ File cert = loadCert("ca.pem");
+ return initClientSslContext(context, cert);
+ }
+
+ /**
* Initializes the given client-side {@code context} with an appropriate trust manager based on
- * the
- * {@code certChainFile} as its only root certificate.
+ * the {@code certChainFile} as its only root certificate.
*/
static SSLContext initClientSslContext(SSLContext context, File certChainFile) {
try {
@@ -110,7 +253,16 @@
}
/**
- * Initializes the given server-side {@code context} with the
+ * Initializes the given server-side {@code context} with the default cert chain and key.
+ */
+ static SSLContext initServerContext(SSLContext context) {
+ File cert = loadCert("server1.pem");
+ File key = loadCert("server1.key");
+ return initServerContext(context, cert, key);
+ }
+
+ /**
+ * Initializes the given server-side {@code context} with the given cert chain and private key.
*/
static SSLContext initServerContext(SSLContext context, File certChainFile, File keyFile) {
try {
@@ -178,4 +330,13 @@
}
}
}
+
+ private static Provider getDefaultTlsProvider() {
+ for (Provider p : Security.getProviders()) {
+ if (p.get(PROVIDER_PROPERTY) != null) {
+ return p;
+ }
+ }
+ throw new RuntimeException("Unable to find a default provider for " + PROVIDER_PROPERTY);
+ }
}
diff --git a/openjdk/src/main/java/org/conscrypt/NativeCryptoJni.java b/openjdk/src/main/java/org/conscrypt/NativeCryptoJni.java
index 7e34f84..0076579 100644
--- a/openjdk/src/main/java/org/conscrypt/NativeCryptoJni.java
+++ b/openjdk/src/main/java/org/conscrypt/NativeCryptoJni.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The Android Open Source Project
+ * Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/openjdk/src/main/java/org/conscrypt/NativeLibraryLoader.java b/openjdk/src/main/java/org/conscrypt/NativeLibraryLoader.java
index 31a62eb..e1d46cc 100644
--- a/openjdk/src/main/java/org/conscrypt/NativeLibraryLoader.java
+++ b/openjdk/src/main/java/org/conscrypt/NativeLibraryLoader.java
@@ -1,4 +1,20 @@
/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
@@ -15,8 +31,8 @@
*/
package org.conscrypt;
-import java.io.Closeable;
import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
diff --git a/openjdk/src/main/java/org/conscrypt/NativeLibraryUtil.java b/openjdk/src/main/java/org/conscrypt/NativeLibraryUtil.java
index d69b0d6..547fc57 100644
--- a/openjdk/src/main/java/org/conscrypt/NativeLibraryUtil.java
+++ b/openjdk/src/main/java/org/conscrypt/NativeLibraryUtil.java
@@ -1,5 +1,21 @@
/*
- * Copyright 2016 The Netty Project
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance