org.asynchttpclient.test.TestUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.asynchttpclient.test.TestUtils.java

Source

/*
 * Copyright (c) 2015 AsyncHttpClient Project. All rights reserved.
 *
 * This program is licensed to you under the Apache License Version 2.0,
 * and you may not use this file except in compliance with the Apache License Version 2.0.
 * You may obtain a copy of the Apache License Version 2.0 at
 *     http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the Apache License Version 2.0 is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
 */
package org.asynchttpclient.test;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.testng.Assert.*;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.asynchttpclient.AsyncCompletionHandler;
import org.asynchttpclient.AsyncHandler;
import org.asynchttpclient.HttpResponseBodyPart;
import org.asynchttpclient.HttpResponseHeaders;
import org.asynchttpclient.HttpResponseStatus;
import org.asynchttpclient.Response;
import org.asynchttpclient.SslEngineFactory;
import org.asynchttpclient.netty.ssl.JsseSslEngineFactory;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.security.LoginService;
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.security.authentication.DigestAuthenticator;
import org.eclipse.jetty.security.authentication.LoginAuthenticator;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.reactivestreams.Publisher;

import rx.Observable;
import rx.RxReactiveStreams;

public class TestUtils {

    public final static int TIMEOUT = 30;
    public static final String USER = "user";
    public static final String ADMIN = "admin";
    public static final String TEXT_HTML_CONTENT_TYPE_WITH_UTF_8_CHARSET = "text/html;charset=UTF-8";
    public static final String TEXT_HTML_CONTENT_TYPE_WITH_ISO_8859_1_CHARSET = "text/html;charset=ISO-8859-1";
    public static final File TMP_DIR = new File(System.getProperty("java.io.tmpdir"),
            "ahc-tests-" + UUID.randomUUID().toString().substring(0, 8));
    public static final byte[] PATTERN_BYTES = "FooBarBazQixFooBarBazQixFooBarBazQixFooBarBazQixFooBarBazQixFooBarBazQix"
            .getBytes(Charset.forName("UTF-16"));
    public static final File LARGE_IMAGE_FILE;
    public static final byte[] LARGE_IMAGE_BYTES;
    public static final Publisher<ByteBuffer> LARGE_IMAGE_PUBLISHER;
    public static final File SIMPLE_TEXT_FILE;
    public static final String SIMPLE_TEXT_FILE_STRING;
    private static final LoginService LOGIN_SERVICE = new HashLoginService("MyRealm",
            "src/test/resources/realm.properties");

    static {
        try {
            TMP_DIR.mkdirs();
            TMP_DIR.deleteOnExit();
            LARGE_IMAGE_FILE = resourceAsFile("300k.png");
            LARGE_IMAGE_BYTES = FileUtils.readFileToByteArray(LARGE_IMAGE_FILE);
            LARGE_IMAGE_PUBLISHER = createPublisher(LARGE_IMAGE_BYTES, /* chunkSize */1000);
            SIMPLE_TEXT_FILE = resourceAsFile("SimpleTextFile.txt");
            SIMPLE_TEXT_FILE_STRING = FileUtils.readFileToString(SIMPLE_TEXT_FILE, UTF_8);
        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public static synchronized int findFreePort() throws IOException {
        try (ServerSocket socket = ServerSocketFactory.getDefault().createServerSocket(0)) {
            return socket.getLocalPort();
        }
    }

    public static File resourceAsFile(String path) throws URISyntaxException, IOException {
        ClassLoader cl = TestUtils.class.getClassLoader();
        URI uri = cl.getResource(path).toURI();
        if (uri.isAbsolute() && !uri.isOpaque()) {
            return new File(uri);
        } else {
            File tmpFile = File.createTempFile("tmpfile-", ".data", TMP_DIR);
            tmpFile.deleteOnExit();
            try (InputStream is = cl.getResourceAsStream(path)) {
                FileUtils.copyInputStreamToFile(is, tmpFile);
                return tmpFile;
            }
        }
    }

    public static File createTempFile(int approxSize) throws IOException {
        long repeats = approxSize / TestUtils.PATTERN_BYTES.length + 1;
        File tmpFile = File.createTempFile("tmpfile-", ".data", TMP_DIR);
        tmpFile.deleteOnExit();
        try (FileOutputStream out = new FileOutputStream(tmpFile)) {
            for (int i = 0; i < repeats; i++) {
                out.write(PATTERN_BYTES);
            }

            long expectedFileSize = PATTERN_BYTES.length * repeats;
            assertEquals(tmpFile.length(), expectedFileSize, "Invalid file length");

            return tmpFile;
        }
    }

    public static Publisher<ByteBuffer> createPublisher(final byte[] bytes, final int chunkSize) {
        Observable<ByteBuffer> observable = Observable.from(new ByteBufferIterable(bytes, chunkSize));
        return RxReactiveStreams.toPublisher(observable);
    }

    public static class ByteBufferIterable implements Iterable<ByteBuffer> {
        private final byte[] payload;
        private final int chunkSize;

        public ByteBufferIterable(byte[] payload, int chunkSize) {
            this.payload = payload;
            this.chunkSize = chunkSize;
        }

        @Override
        public Iterator<ByteBuffer> iterator() {
            return new Iterator<ByteBuffer>() {
                private int currentIndex = 0;

                @Override
                public boolean hasNext() {
                    return currentIndex != payload.length;
                }

                @Override
                public ByteBuffer next() {
                    int newIndex = Math.min(currentIndex + chunkSize, payload.length);
                    byte[] bytesInElement = Arrays.copyOfRange(payload, currentIndex, newIndex);
                    currentIndex = newIndex;
                    return ByteBuffer.wrap(bytesInElement);
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException(
                            "ByteBufferIterable's iterator does not support remove.");
                }
            };
        }
    }

    public static ServerConnector addHttpConnector(Server server) {
        ServerConnector connector = new ServerConnector(server);
        server.addConnector(connector);
        return connector;
    }

    public static ServerConnector addHttpsConnector(Server server) throws IOException, URISyntaxException {

        String keyStoreFile = resourceAsFile("ssltest-keystore.jks").getAbsolutePath();
        SslContextFactory sslContextFactory = new SslContextFactory(keyStoreFile);
        sslContextFactory.setKeyStorePassword("changeit");

        String trustStoreFile = resourceAsFile("ssltest-cacerts.jks").getAbsolutePath();
        sslContextFactory.setTrustStorePath(trustStoreFile);
        sslContextFactory.setTrustStorePassword("changeit");

        HttpConfiguration httpsConfig = new HttpConfiguration();
        httpsConfig.setSecureScheme("https");
        httpsConfig.addCustomizer(new SecureRequestCustomizer());

        ServerConnector connector = new ServerConnector(server,
                new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(httpsConfig));

        server.addConnector(connector);

        return connector;
    }

    public static void addBasicAuthHandler(Server server, Handler handler) {
        addAuthHandler(server, Constraint.__BASIC_AUTH, new BasicAuthenticator(), handler);
    }

    public static void addDigestAuthHandler(Server server, Handler handler) {
        addAuthHandler(server, Constraint.__DIGEST_AUTH, new DigestAuthenticator(), handler);
    }

    private static void addAuthHandler(Server server, String auth, LoginAuthenticator authenticator,
            Handler handler) {

        server.addBean(LOGIN_SERVICE);

        Constraint constraint = new Constraint();
        constraint.setName(auth);
        constraint.setRoles(new String[] { USER, ADMIN });
        constraint.setAuthenticate(true);

        ConstraintMapping mapping = new ConstraintMapping();
        mapping.setConstraint(constraint);
        mapping.setPathSpec("/*");

        Set<String> knownRoles = new HashSet<>();
        knownRoles.add(USER);
        knownRoles.add(ADMIN);

        List<ConstraintMapping> cm = new ArrayList<>();
        cm.add(mapping);

        ConstraintSecurityHandler security = new ConstraintSecurityHandler();
        security.setConstraintMappings(cm, knownRoles);
        security.setAuthenticator(authenticator);
        security.setLoginService(LOGIN_SERVICE);
        security.setHandler(handler);
        server.setHandler(security);
    }

    private static KeyManager[] createKeyManagers() throws GeneralSecurityException, IOException {
        KeyStore ks = KeyStore.getInstance("JKS");
        try (InputStream keyStoreStream = TestUtils.class.getClassLoader()
                .getResourceAsStream("ssltest-cacerts.jks")) {
            char[] keyStorePassword = "changeit".toCharArray();
            ks.load(keyStoreStream, keyStorePassword);
        }
        assert (ks.size() > 0);

        // Set up key manager factory to use our key store
        char[] certificatePassword = "changeit".toCharArray();
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
        kmf.init(ks, certificatePassword);

        // Initialize the SSLContext to work with our key managers.
        return kmf.getKeyManagers();
    }

    private static TrustManager[] createTrustManagers() throws GeneralSecurityException, IOException {
        KeyStore ks = KeyStore.getInstance("JKS");
        try (InputStream keyStoreStream = TestUtils.class.getClassLoader()
                .getResourceAsStream("ssltest-keystore.jks")) {
            char[] keyStorePassword = "changeit".toCharArray();
            ks.load(keyStoreStream, keyStorePassword);
        }
        assert (ks.size() > 0);

        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        return tmf.getTrustManagers();
    }

    public static SslEngineFactory createSslEngineFactory() throws SSLException {
        return createSslEngineFactory(new AtomicBoolean(true));
    }

    public static SslEngineFactory createSslEngineFactory(AtomicBoolean trust) throws SSLException {

        try {
            KeyManager[] keyManagers = createKeyManagers();
            TrustManager[] trustManagers = new TrustManager[] {
                    dummyTrustManager(trust, (X509TrustManager) createTrustManagers()[0]) };
            SecureRandom secureRandom = new SecureRandom();

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagers, trustManagers, secureRandom);

            return new JsseSslEngineFactory(sslContext);

        } catch (Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public static class DummyTrustManager implements X509TrustManager {

        private final X509TrustManager tm;
        private final AtomicBoolean trust;

        public DummyTrustManager(final AtomicBoolean trust, final X509TrustManager tm) {
            this.trust = trust;
            this.tm = tm;
        }

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            tm.checkClientTrusted(chain, authType);
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            if (!trust.get()) {
                throw new CertificateException("Server certificate not trusted.");
            }
            tm.checkServerTrusted(chain, authType);
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return tm.getAcceptedIssuers();
        }
    }

    private static TrustManager dummyTrustManager(final AtomicBoolean trust, final X509TrustManager tm) {
        return new DummyTrustManager(trust, tm);

    }

    public static File getClasspathFile(String file) throws FileNotFoundException {
        ClassLoader cl = null;
        try {
            cl = Thread.currentThread().getContextClassLoader();
        } catch (Throwable ex) {
        }
        if (cl == null) {
            cl = TestUtils.class.getClassLoader();
        }
        URL resourceUrl = cl.getResource(file);

        try {
            return new File(new URI(resourceUrl.toString()).getSchemeSpecificPart());
        } catch (URISyntaxException e) {
            throw new FileNotFoundException(file);
        }
    }

    public static void assertContentTypesEquals(String actual, String expected) {
        assertEquals(actual.replace("; ", "").toLowerCase(Locale.ENGLISH),
                expected.replace("; ", "").toLowerCase(Locale.ENGLISH), "Unexpected content-type");
    }

    public static String getLocalhostIp() {
        return "127.0.0.1";
    }

    public static class AsyncCompletionHandlerAdapter extends AsyncCompletionHandler<Response> {

        @Override
        public Response onCompleted(Response response) throws Exception {
            return response;
        }

        @Override
        public void onThrowable(Throwable t) {
            fail("Unexpected exception: " + t.getMessage(), t);
        }
    }

    public static class AsyncHandlerAdapter implements AsyncHandler<String> {

        @Override
        public void onThrowable(Throwable t) {
            fail("Unexpected exception", t);
        }

        @Override
        public State onBodyPartReceived(final HttpResponseBodyPart content) throws Exception {
            return State.CONTINUE;
        }

        @Override
        public State onStatusReceived(final HttpResponseStatus responseStatus) throws Exception {
            return State.CONTINUE;
        }

        @Override
        public State onHeadersReceived(final HttpResponseHeaders headers) throws Exception {
            return State.CONTINUE;
        }

        @Override
        public String onCompleted() throws Exception {
            return "";
        }
    }

    public static void writeResponseBody(HttpServletResponse response, String body) {
        response.setContentLength(body.length());
        try {
            response.getOutputStream().print(body);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}