10c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler/*
20c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * Copyright 2017 The Android Open Source Project
30c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler *
40c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * Licensed under the Apache License, Version 2.0 (the "License");
50c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * you may not use this file except in compliance with the License.
60c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * You may obtain a copy of the License at
70c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler *
80c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler *      http://www.apache.org/licenses/LICENSE-2.0
90c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler *
100c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * Unless required by applicable law or agreed to in writing, software
110c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * distributed under the License is distributed on an "AS IS" BASIS,
120c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * See the License for the specific language governing permissions and
140c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * limitations under the License.
150c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler */
160c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
170c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerpackage org.conscrypt.benchmarks;
180c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
1938ff07c8d63362d70a32938edb1bae9dea218f5bNathan Mittlerimport static org.conscrypt.testing.TestUtil.LOCALHOST;
20171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Rootimport static org.conscrypt.testing.TestUtil.getConscryptServerSocketFactory;
2138ff07c8d63362d70a32938edb1bae9dea218f5bNathan Mittlerimport static org.conscrypt.testing.TestUtil.getConscryptSocketFactory;
22171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Rootimport static org.conscrypt.testing.TestUtil.getJdkServerSocketFactory;
2338ff07c8d63362d70a32938edb1bae9dea218f5bNathan Mittlerimport static org.conscrypt.testing.TestUtil.getJdkSocketFactory;
2438ff07c8d63362d70a32938edb1bae9dea218f5bNathan Mittlerimport static org.conscrypt.testing.TestUtil.getProtocols;
2538ff07c8d63362d70a32938edb1bae9dea218f5bNathan Mittlerimport static org.conscrypt.testing.TestUtil.newTextMessage;
2638ff07c8d63362d70a32938edb1bae9dea218f5bNathan Mittlerimport static org.conscrypt.testing.TestUtil.pickUnusedPort;
270c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
280c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport java.io.IOException;
29171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Rootimport java.io.OutputStream;
3035b401854558f39cf694facb87ea28e250548dd2Nathan Mittlerimport java.util.concurrent.ExecutorService;
3135b401854558f39cf694facb87ea28e250548dd2Nathan Mittlerimport java.util.concurrent.Executors;
32171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Rootimport java.util.concurrent.Future;
3335b401854558f39cf694facb87ea28e250548dd2Nathan Mittlerimport java.util.concurrent.TimeUnit;
3435b401854558f39cf694facb87ea28e250548dd2Nathan Mittlerimport java.util.concurrent.atomic.AtomicBoolean;
3535b401854558f39cf694facb87ea28e250548dd2Nathan Mittlerimport java.util.concurrent.atomic.AtomicLong;
360c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport javax.net.SocketFactory;
37171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Rootimport javax.net.ssl.SSLServerSocket;
38171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Rootimport javax.net.ssl.SSLServerSocketFactory;
390c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport javax.net.ssl.SSLSocket;
400c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport javax.net.ssl.SSLSocketFactory;
41c58d52620b66bfee6298d0ca58540fcdaa469c8cNathan Mittlerimport org.conscrypt.testing.TestClient;
42171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Rootimport org.conscrypt.testing.TestServer;
4335b401854558f39cf694facb87ea28e250548dd2Nathan Mittlerimport org.openjdk.jmh.annotations.AuxCounters;
440c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport org.openjdk.jmh.annotations.Benchmark;
4535b401854558f39cf694facb87ea28e250548dd2Nathan Mittlerimport org.openjdk.jmh.annotations.Fork;
4635b401854558f39cf694facb87ea28e250548dd2Nathan Mittlerimport org.openjdk.jmh.annotations.Level;
470c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport org.openjdk.jmh.annotations.Param;
480c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport org.openjdk.jmh.annotations.Scope;
490c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport org.openjdk.jmh.annotations.Setup;
500c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport org.openjdk.jmh.annotations.State;
510c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittlerimport org.openjdk.jmh.annotations.TearDown;
520c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
530c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler/**
540c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * Benchmark for comparing performance of client socket implementations. All benchmarks use Netty
550c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler * with tcnative as the server.
560c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler */
570c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler@State(Scope.Benchmark)
5835b401854558f39cf694facb87ea28e250548dd2Nathan Mittler@Fork(1)
5935b401854558f39cf694facb87ea28e250548dd2Nathan Mittlerpublic class ClientSocketThroughputBenchmark {
6035b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    /**
6178a2c40bd741f3e4572941756baae8cd3acbbafaKenny Root     * Use an AuxCounter so we can measure that bytes per second as they accumulate without
6278a2c40bd741f3e4572941756baae8cd3acbbafaKenny Root     * consuming CPU in the benchmark method.
6335b401854558f39cf694facb87ea28e250548dd2Nathan Mittler     */
6435b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    @AuxCounters
6535b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    @State(Scope.Thread)
6678a2c40bd741f3e4572941756baae8cd3acbbafaKenny Root    public static class BytesPerSecondCounter {
6735b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        @Setup(Level.Iteration)
6835b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        public void clean() {
6978a2c40bd741f3e4572941756baae8cd3acbbafaKenny Root            bytesCounter.set(0);
7035b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        }
7135b401854558f39cf694facb87ea28e250548dd2Nathan Mittler
7278a2c40bd741f3e4572941756baae8cd3acbbafaKenny Root        public long bytesPerSecond() {
7378a2c40bd741f3e4572941756baae8cd3acbbafaKenny Root            return bytesCounter.get();
7435b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        }
7535b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    }
7635b401854558f39cf694facb87ea28e250548dd2Nathan Mittler
770c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    /**
780c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler     * Various factories for SSL sockets.
790c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler     */
80c58d52620b66bfee6298d0ca58540fcdaa469c8cNathan Mittler    public enum SslProvider {
81171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        JDK(getJdkSocketFactory(), getJdkServerSocketFactory()),
82171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        CONSCRYPT(getConscryptSocketFactory(false), getConscryptServerSocketFactory(false)),
83171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        CONSCRYPT_ENGINE(getConscryptSocketFactory(true), getConscryptServerSocketFactory(true)) {
840c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler            @Override
85171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root            SSLSocket newClientSocket(String host, int port, SSLSocketFactory socketFactory)  throws IOException {
86171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                return (SSLSocket) socketFactory.createSocket(
87171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                    SocketFactory.getDefault().createSocket(host, port), host, port, true);
880c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler            }
890c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler        };
900c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
91171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        private final SSLSocketFactory clientSocketFactory;
92171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        private final SSLServerSocketFactory serverSocketFactory;
93171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root
94171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        SslProvider(SSLSocketFactory clientSocketFactory, SSLServerSocketFactory serverSocketFactory) {
95171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root            this.clientSocketFactory = clientSocketFactory;
96171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root            this.serverSocketFactory = serverSocketFactory;
97171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        }
98171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root
99171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        final SSLSocket newClientSocket(String host, int port, String cipher) {
1000c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler            try {
101171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                SSLSocket sslSocket = newClientSocket(host, port, clientSocketFactory);
1020c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler                sslSocket.setEnabledProtocols(getProtocols());
1030c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler                sslSocket.setEnabledCipherSuites(new String[] {cipher});
1040c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler                return sslSocket;
1050c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler            } catch (Exception e) {
1060c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler                throw new RuntimeException(e);
1070c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler            }
1080c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler        }
1090c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
110171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        SSLSocket newClientSocket(String host, int port, SSLSocketFactory socketFactory)  throws IOException {
111171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root            return (SSLSocket) socketFactory.createSocket(host, port);
112171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        }
113171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root
114171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        final SSLServerSocket newServerSocket(String cipher) {
115171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root            try {
116171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                int port = pickUnusedPort();
117171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                SSLServerSocket sslSocket =
118171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                    (SSLServerSocket) serverSocketFactory.createServerSocket(port);
119171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                sslSocket.setEnabledProtocols(getProtocols());
120171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                sslSocket.setEnabledCipherSuites(new String[] {cipher});
121171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                return sslSocket;
122171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root            } catch (IOException e) {
123171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root                throw new RuntimeException(e);
124171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root            }
125171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        }
1260c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    }
1270c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
128c58d52620b66bfee6298d0ca58540fcdaa469c8cNathan Mittler    @Param public SslProvider sslProvider;
1290c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
13035b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    @Param({"64", "1024"}) public int messageSize;
1310c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
1329390a99b27d7c4234d060f284184a3ee87be1618Kenny Root    @Param({"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}) public String cipher;
1330c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
1340c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    private TestClient client;
135171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root    private TestServer server;
1360c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    private byte[] message;
13735b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    private ExecutorService executor;
13835b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    private volatile boolean stopping;
13935b401854558f39cf694facb87ea28e250548dd2Nathan Mittler
14078a2c40bd741f3e4572941756baae8cd3acbbafaKenny Root    private static final AtomicLong bytesCounter = new AtomicLong();
14135b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    private AtomicBoolean recording = new AtomicBoolean();
1420c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
14335b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    @Setup(Level.Trial)
1440c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    public void setup() throws Exception {
14535b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        recording.set(false);
14635b401854558f39cf694facb87ea28e250548dd2Nathan Mittler
1470c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler        message = newTextMessage(messageSize);
1480c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
149171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        server = new TestServer(sslProvider.newServerSocket(cipher), messageSize);
150171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        server.setMessageProcessor(new TestServer.MessageProcessor() {
15135b401854558f39cf694facb87ea28e250548dd2Nathan Mittler            @Override
152171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root            public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
15335b401854558f39cf694facb87ea28e250548dd2Nathan Mittler                if (recording.get()) {
15435b401854558f39cf694facb87ea28e250548dd2Nathan Mittler                    // Server received a message, increment the count.
15578a2c40bd741f3e4572941756baae8cd3acbbafaKenny Root                    bytesCounter.addAndGet(numBytes);
15635b401854558f39cf694facb87ea28e250548dd2Nathan Mittler                }
15735b401854558f39cf694facb87ea28e250548dd2Nathan Mittler            }
15835b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        });
159171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        Future<?> connectedFuture = server.start();
1600c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
161171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        client = new TestClient(sslProvider.newClientSocket(LOCALHOST, server.port(), cipher));
1620c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler        client.start();
16335b401854558f39cf694facb87ea28e250548dd2Nathan Mittler
164171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        // Wait for the initial connection to complete.
165171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root        connectedFuture.get(5, TimeUnit.SECONDS);
166171e29e7efffd0ec2b64d4410c1c709d63beee0aKenny Root
16735b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        executor = Executors.newSingleThreadExecutor();
16835b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        executor.submit(new Runnable() {
16935b401854558f39cf694facb87ea28e250548dd2Nathan Mittler            @Override
17035b401854558f39cf694facb87ea28e250548dd2Nathan Mittler            public void run() {
17135b401854558f39cf694facb87ea28e250548dd2Nathan Mittler                Thread thread = Thread.currentThread();
17235b401854558f39cf694facb87ea28e250548dd2Nathan Mittler                while (!stopping && !thread.isInterrupted()) {
17335b401854558f39cf694facb87ea28e250548dd2Nathan Mittler                    client.sendMessage(message);
17435b401854558f39cf694facb87ea28e250548dd2Nathan Mittler                }
17535b401854558f39cf694facb87ea28e250548dd2Nathan Mittler            }
17635b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        });
1770c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    }
1780c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
17935b401854558f39cf694facb87ea28e250548dd2Nathan Mittler    @TearDown(Level.Trial)
1800c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    public void teardown() throws Exception {
18135b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        stopping = true;
1820c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler        client.stop();
1830c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler        server.stop();
18435b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        executor.shutdown();
18535b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        executor.awaitTermination(5, TimeUnit.SECONDS);
1860c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    }
1870c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler
1880c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    @Benchmark
18978a2c40bd741f3e4572941756baae8cd3acbbafaKenny Root    public final void throughput(BytesPerSecondCounter counter) throws Exception {
19035b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        recording.set(true);
19135b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        // No need to do anything, just sleep here.
19235b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        Thread.sleep(1001);
19335b401854558f39cf694facb87ea28e250548dd2Nathan Mittler        recording.set(false);
1940c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler    }
1950c6f7671f35af188bcfd7cf84f9c46f72a25ccffNathan Mittler}
196