/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF 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.apache.bookkeeper.http.vertx;

import java.io.FileInputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import org.apache.bookkeeper.http.HttpServerConfiguration;
import org.apache.bookkeeper.http.HttpServiceProvider;
import org.apache.bookkeeper.http.NullHttpServiceProvider;
import org.junit.Test;

/**
 * Unit test {@link VertxHttpServer}.
 */
public class TestVertxHttpsServer {

    private static final String CLIENT_KEYSTORE_PATH = "./src/test/resources/vertx_client_key.jks";

    private static final String CLIENT_TRUSTSTORE_PATH = "./src/test/resources/vertx_client_trust.jks";

    private static final String CLIENT_WRONG_TRUSTSTORE_PATH = "./src/test/resources/vertx_client_wrong_trust.jks";

    private static final String CLIENT_KEYSTORE_PASSWORD = "vertx_client_pwd";

    private static final String CLIENT_TRUSTSTORE_PASSWORD = "vertx_client_pwd";

    private static final String SERVER_KEYSTORE_PATH = "./src/test/resources/vertx_server_key.jks";

    private static final String SERVER_TRUSTSTORE_PATH = "./src/test/resources/vertx_server_trust.jks";

    private static final String SERVER_KEYSTORE_PASSWORD = "vertx_server_pwd";

    private static final String SERVER_TRUSTSTORE_PASSWORD = "vertx_server_pwd";

    @Test(timeout = 60_000)
    public void testVertxServerTls() throws Exception {
        VertxHttpServer httpServer = new VertxHttpServer();
        HttpServiceProvider httpServiceProvider = NullHttpServiceProvider.getInstance();
        httpServer.initialize(httpServiceProvider);
        HttpServerConfiguration httpServerConfiguration = new HttpServerConfiguration();
        httpServerConfiguration.setTlsEnable(true);
        httpServerConfiguration.setKeyStorePath(SERVER_KEYSTORE_PATH);
        httpServerConfiguration.setKeyStorePassword(SERVER_KEYSTORE_PASSWORD);
        httpServerConfiguration.setTrustStorePath(SERVER_TRUSTSTORE_PATH);
        httpServerConfiguration.setTrustStorePassword(SERVER_TRUSTSTORE_PASSWORD);
        httpServer.startServer(0, "localhost", httpServerConfiguration);
        int actualPort = httpServer.getListeningPort();
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        // key store
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        KeyStore keyStore = KeyStore.getInstance("JKS");
        try (FileInputStream inputStream = new FileInputStream(CLIENT_KEYSTORE_PATH)) {
            keyStore.load(inputStream, CLIENT_KEYSTORE_PASSWORD.toCharArray());
        }
        keyManagerFactory.init(keyStore, "vertx_client_pwd".toCharArray());
        KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
        // trust store
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm());
        KeyStore trustStore = KeyStore.getInstance("JKS");
        try (FileInputStream inputStream = new FileInputStream(CLIENT_TRUSTSTORE_PATH)) {
            trustStore.load(inputStream, CLIENT_TRUSTSTORE_PASSWORD.toCharArray());
        }
        trustManagerFactory.init(trustStore);
        sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), new SecureRandom());
        URL url = new URL("https://localhost:" + actualPort);
        HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
        urlConnection.setHostnameVerifier((s, sslSession) -> true);
        SSLSocketFactory socketFactory = sslContext.getSocketFactory();
        urlConnection.setSSLSocketFactory(socketFactory);
        urlConnection.setRequestMethod("GET");
        urlConnection.getResponseCode();
        httpServer.stopServer();
    }

    @Test(timeout = 60_000, expected = SSLHandshakeException.class)
    public void testVertxServerTlsFailByCertNotMatch() throws Exception {
        VertxHttpServer httpServer = new VertxHttpServer();
        HttpServerConfiguration httpServerConfiguration = new HttpServerConfiguration();
        httpServerConfiguration.setTlsEnable(true);
        httpServerConfiguration.setKeyStorePath(SERVER_KEYSTORE_PATH);
        httpServerConfiguration.setKeyStorePassword(SERVER_KEYSTORE_PASSWORD);
        httpServerConfiguration.setTrustStorePath(SERVER_TRUSTSTORE_PATH);
        httpServerConfiguration.setTrustStorePassword(SERVER_TRUSTSTORE_PASSWORD);
        httpServer.startServer(0, "localhost", httpServerConfiguration);
        int actualPort = httpServer.getListeningPort();
        SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
        // key store
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        KeyStore keyStore = KeyStore.getInstance("JKS");
        try (FileInputStream inputStream = new FileInputStream(CLIENT_KEYSTORE_PATH)) {
            keyStore.load(inputStream, CLIENT_KEYSTORE_PASSWORD.toCharArray());
        }
        keyManagerFactory.init(keyStore, "vertx_client_pwd".toCharArray());
        KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
        // trust store
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                TrustManagerFactory.getDefaultAlgorithm());
        KeyStore trustStore = KeyStore.getInstance("JKS");
        try (FileInputStream inputStream = new FileInputStream(CLIENT_WRONG_TRUSTSTORE_PATH)) {
            trustStore.load(inputStream, CLIENT_TRUSTSTORE_PASSWORD.toCharArray());
        }
        trustManagerFactory.init(trustStore);
        sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), new SecureRandom());
        URL url = new URL("https://localhost:" + actualPort);
        HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
        urlConnection.setHostnameVerifier((s, sslSession) -> true);
        SSLSocketFactory socketFactory = sslContext.getSocketFactory();
        urlConnection.setSSLSocketFactory(socketFactory);
        urlConnection.setRequestMethod("GET");
        urlConnection.getResponseCode();
    }

}
