All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atlassian.asap.performance.Benchmark Maven / Gradle / Ivy

package com.atlassian.asap.performance;

import java.net.URI;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;

import com.atlassian.asap.api.Jwt;
import com.atlassian.asap.api.JwtBuilder;
import com.atlassian.asap.api.SigningAlgorithm;
import com.atlassian.asap.api.client.http.AuthorizationHeaderGenerator;
import com.atlassian.asap.api.exception.AuthenticationFailedException;
import com.atlassian.asap.api.exception.CannotRetrieveKeyException;
import com.atlassian.asap.api.exception.InvalidTokenException;
import com.atlassian.asap.api.server.http.RequestAuthenticator;
import com.atlassian.asap.core.client.http.AuthorizationHeaderGeneratorImpl;
import com.atlassian.asap.core.server.http.RequestAuthenticatorImpl;
import com.atlassian.asap.core.validator.JwtValidator;
import com.atlassian.asap.core.validator.JwtValidatorImpl;

import com.google.common.base.Stopwatch;

import org.apache.commons.math3.stat.descriptive.SummaryStatistics;

import static com.atlassian.asap.core.client.SimpleClientRunner.AUDIENCE_SYSPROP;
import static com.atlassian.asap.core.client.SimpleClientRunner.ISSUER_SYSPROP;
import static com.atlassian.asap.core.client.SimpleClientRunner.KEYID_SYSPROP;
import static com.atlassian.asap.core.client.SimpleClientRunner.PRIVATE_KEY_SYSPROP;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;

/**
 * A main program that runs basic performance checking of the ASAP library, both the client side (signing
 * and serialising tokens) and the server side (parsing and verifying signatures).
 */
public class Benchmark
{
    private static final String ALGORITHM_SYSPROP = "asap.client.algorithm";
    private static final String PUBLIC_KEY_SERVER_SYSPROP = "asap.public.key.repo.url";
    /**
     * A high value for repetitions is recommended, as cryptographic operations are highly influenced by runtime optimizations.
     * A high repetition value allows sufficient time for the effect of such optimizations to plateau
     */
    private static final String REPETITIONS_SYSPROP = "asap.benchmark.repetitions";
    private static final String DEFAULT_PUBLIC_KEY_SERVER_URL = "https://s3-ap-southeast-2.amazonaws.com/keymaker.syd.dev.atlassian.io/";

    /**
     * Main function to run the benchmark test.
     * @param args command line arguments
     */
    public static void main(String[] args)
    {
        long repetitions = Long.parseLong(defaultIfBlank(System.getProperty(REPETITIONS_SYSPROP), "200000"));
        SigningAlgorithm algorithm = SigningAlgorithm.valueOf(defaultIfBlank(System.getProperty(ALGORITHM_SYSPROP), "RS256"));
        String issuer = defaultIfBlank(System.getProperty(ISSUER_SYSPROP), "issuer1");
        String keyId = defaultIfBlank(System.getProperty(KEYID_SYSPROP), "issuer1/rsa-key-for-tests");
        String audience = defaultIfBlank(System.getProperty(AUDIENCE_SYSPROP), "audience");
        String publicKeyBaseUrl = defaultIfBlank(System.getProperty(PUBLIC_KEY_SERVER_SYSPROP), DEFAULT_PUBLIC_KEY_SERVER_URL);
        URI privateKeyBaseUrl = URI.create(defaultIfBlank(System.getProperty(PRIVATE_KEY_SYSPROP), "classpath:/privatekeys/"));

        Jwt jwt = createJwt(algorithm, issuer, keyId, audience);

        System.out.println("============== Benchmark Start ===============");
        System.out.printf("Client test using key %s and %s algorithm with %d repetitions...%n", keyId, algorithm, repetitions);
        clientTest(repetitions, jwt, privateKeyBaseUrl);
        System.out.printf("Server test using key %s and %s algorithm with %d repetitions...%n", keyId, algorithm, repetitions);
        serverValidTest(repetitions, jwt, publicKeyBaseUrl, privateKeyBaseUrl);
        System.out.println("============== Benchmark End ================");

    }

    private static void clientTest(long repetitions, Jwt jwt, URI privateKeyBaseUrl)
    {
        AuthorizationHeaderGenerator authorizationHeaderGenerator =
                AuthorizationHeaderGeneratorImpl.createDefault(privateKeyBaseUrl);
        Stopwatch stopwatch = Stopwatch.createUnstarted();
        try
        {
            stopwatch.start();
            for (long i = 0; i < repetitions; i++)
            {
                authorizationHeaderGenerator.generateAuthorizationHeader(jwt);
            }
            stopwatch.stop();
        }
        catch (InvalidTokenException | CannotRetrieveKeyException e)
        {
            e.printStackTrace();
            System.exit(1);
        }

        System.out.printf("Elapsed time: %d ms. Throughput: %f requests/sec%n",
                stopwatch.elapsed(TimeUnit.MILLISECONDS),
                1000.0 * repetitions / stopwatch.elapsed(TimeUnit.MILLISECONDS));
    }

    private static void serverValidTest(long repetitions, Jwt jwt, String publicKeyBaseUrl, URI privateKeyBaseUrl)
    {
        String firstAudience = jwt.getClaims().getAudience().iterator().next();
        JwtValidator jwtValidator = JwtValidatorImpl.createDefault(firstAudience, publicKeyBaseUrl);
        RequestAuthenticator requestAuthenticator = new RequestAuthenticatorImpl(jwtValidator);
        Stopwatch stopwatch = Stopwatch.createUnstarted();
        AuthorizationHeaderGenerator authorizationHeaderGenerator =
                AuthorizationHeaderGeneratorImpl.createDefault(privateKeyBaseUrl);
        SummaryStatistics stats = new SummaryStatistics();

        try
        {
            stopwatch.start();
            final String authorizationHeader = authorizationHeaderGenerator.generateAuthorizationHeader(jwt);
            for (long i = 0; i < repetitions; i++)
            {
                long startTime = System.nanoTime();
                requestAuthenticator.authenticateRequest(authorizationHeader);
                stats.addValue(TimeUnit.MILLISECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS));

            }
            stopwatch.stop();
        }
        catch (InvalidTokenException | CannotRetrieveKeyException | AuthenticationFailedException e)
        {
            e.printStackTrace();
            System.exit(1);
        }

        System.out.printf("Elapsed time: %d ms. Throughput: %f requests/sec%n",
                stopwatch.elapsed(TimeUnit.MILLISECONDS),
                1000.0 * repetitions / stopwatch.elapsed(TimeUnit.MILLISECONDS));
        // the latency between server receiving a verification request and then completing the verification with a public key retrieved from key server.
        System.out.printf("Server-side Token Verification Latency min/avg/max/stddev = %f/%f/%f/%f ms%n",
                stats.getMin(), stats.getMean(), stats.getMax(), stats.getStandardDeviation());
    }

    private static Jwt createJwt(SigningAlgorithm algorithm, String issuer, String keyId, String audience)
    {
        return JwtBuilder.newJwt()
                .algorithm(algorithm)
                .issuer(issuer)
                .audience(audience)
                .keyId(keyId)
                .expirationTime(Instant.now().plus(59, ChronoUnit.MINUTES)) // allow enough time for benchmark
                .build();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy