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

io.quarkus.test.bootstrap.RestService Maven / Gradle / Ivy

package io.quarkus.test.bootstrap;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;

import io.quarkus.test.security.certificate.CertificateBuilder;
import io.quarkus.test.security.certificate.PemClientCertificate;
import io.quarkus.test.services.Certificate;
import io.quarkus.test.services.URILike;
import io.quarkus.test.utils.TestExecutionProperties;
import io.restassured.RestAssured;
import io.restassured.specification.RequestSpecification;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.KeyStoreOptions;
import io.vertx.core.net.PemKeyCertOptions;
import io.vertx.core.net.PemTrustOptions;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.ext.web.client.WebClient;

public class RestService extends BaseService {

    private static final int BUFFER_SIZE = 1024;
    private static final String BASE_PATH = "/";

    private final boolean setupRestAssured;
    private WebClient webClient;

    public RestService() {
        this(true);
    }

    /**
     * @param setupRestAssured whether RestAssured base URI, path and port should be configured;
     *        usually you don't mind as setting up RestAssured doesn't hurt you, but it can be useful
     *        when you want to avoid Groovy which RestAssured relies on (like when this framework
     *        is build with a different Quarkus version than it is tested in Maven invoker tests).
     */
    public RestService(boolean setupRestAssured) {
        this.setupRestAssured = setupRestAssured;
    }

    public RequestSpecification given() {
        var host = getURI(Protocol.HTTP);
        return RestAssured.given()
                .baseUri(host.getRestAssuredStyleUri())
                .basePath(BASE_PATH)
                .port(host.getPort());
    }

    public RequestSpecification https() {
        Protocol protocol = Protocol.HTTPS;
        var host = getURI(protocol);
        return RestAssured.given()
                .baseUri(host.getRestAssuredStyleUri())
                .basePath(BASE_PATH)
                .port(host.getPort());
    }

    public RequestSpecification management() {
        var host = getURI(Protocol.MANAGEMENT);
        if (host.getScheme().equals(Protocol.MANAGEMENT.getValue())) {
            throw new IllegalArgumentException("Can not find URL to the management interface");
        }
        return RestAssured.given()
                .baseUri(host.getRestAssuredStyleUri())
                .basePath(BASE_PATH)
                .port(host.getPort());
    }

    public RequestSpecification relaxedHttps() {
        return this.https().relaxedHTTPSValidation();
    }

    public WebClient mutiny() {
        return mutiny(new WebClientOptions());
    }

    public WebClient mutiny(WebClientOptions options) {
        if (webClient == null) {
            var uri = getURI(Protocol.HTTP);
            webClient = WebClient.create(Vertx.vertx(), options
                    .setDefaultHost(uri.getHost())
                    .setDefaultPort(uri.getPort()));
        }

        return webClient;
    }

    /**
     * @see this#mutinyHttps(boolean, String, boolean)
     */
    public WebClient mutinyHttps() {
        return mutinyHttps(null);
    }

    /**
     * @see this#mutinyHttps(boolean, String, boolean)
     */
    public WebClient mutinyHttps(String clientCertificateCn) {
        return mutinyHttps(true, clientCertificateCn, true);
    }

    /**
     * Returns {@link WebClient} configured from {@link CertificateBuilder}.
     * The SSL configuration is done on best effort basis and should only work for JKS and PKCS12 formats.
     * When specific configuration is required, manual client configuration is better option.
     * This HTTPS version of {@link WebClient} is not cached, however it is automatically closed for you.
     * If you need to reuse a client with same configuration within one test class, just keep it in a test class var.
     * Default port and host are always preconfigured for you.
     * If management interface SSL is enabled, the defaults are configured to the management interface.
     * You can always declare your own host and port during the request.
     */
    public WebClient mutinyHttps(boolean verifyHost, String clientCertificateCn, boolean withTruststore) {
        var certificateBuilder = this. getPropertyFromContext(CertificateBuilder.INSTANCE_KEY);
        if (certificateBuilder.certificates().size() != 1) {
            throw new IllegalArgumentException("Exactly one certificate must exist for the SSL configuration to work");
        }

        var options = new WebClientOptions();
        options.setVerifyHost(verifyHost);
        options.setSsl(true);

        final URILike uri;
        if (TestExecutionProperties.useManagementSsl(this)) {
            uri = getURI(Protocol.MANAGEMENT);
        } else {
            uri = getURI(Protocol.HTTPS);
        }
        options.setDefaultHost(uri.getHost());
        options.setDefaultPort(uri.getPort());

        var certificate = certificateBuilder.certificates().get(0);

        boolean isPemCertificate = Certificate.Format.PEM.toString().equals(certificate.format());
        if (isPemCertificate && clientCertificateCn != null) {
            var clientCert = (PemClientCertificate) certificate.getClientCertificateByCn(clientCertificateCn);
            options.setPemKeyCertOptions(
                    new PemKeyCertOptions().addCertPath(clientCert.certPath()).addKeyPath(clientCert.keyPath()));
            options.setPemTrustOptions(new PemTrustOptions().addCertPath(clientCert.truststorePath()));
        } else {
            final String keystorePath;
            final String truststorePath;

            if (clientCertificateCn != null) {
                var clientCert = certificate.getClientCertificateByCn(clientCertificateCn);
                Objects.requireNonNull(clientCert, "Client certificate with CN %s not found".formatted(clientCertificateCn));
                keystorePath = clientCert.keystorePath();
                truststorePath = clientCert.truststorePath();
            } else {
                keystorePath = certificate.keystorePath();
                truststorePath = certificate.truststorePath();
            }

            if (keystorePath != null) {
                options.setKeyCertOptions(
                        new KeyStoreOptions().setValue(Buffer.buffer(getFileContent(keystorePath)))
                                .setPassword(certificate.password()).setType(certificate.format()));
            }

            if (withTruststore && truststorePath != null) {
                options.setTrustOptions(
                        new KeyStoreOptions().setValue(Buffer.buffer(getFileContent(truststorePath)))
                                .setPassword(certificate.password()).setType(certificate.format()));
            }
        }

        var vertx = Vertx.vertx();
        var webClient = WebClient.create(vertx, options);
        onPreStop(service -> {
            webClient.close();
            vertx.close().await().indefinitely();
        });
        return webClient;
    }

    @Override
    public void start() {
        super.start();
        var host = getURI(Protocol.HTTP);
        if (setupRestAssured) {
            RestAssured.baseURI = host.getRestAssuredStyleUri();
            RestAssured.basePath = BASE_PATH;
            RestAssured.port = host.getPort();
        }
    }

    private static byte[] getFileContent(String path) {
        byte[] data;
        try (InputStream is = Files.newInputStream(Path.of(path))) {
            data = doRead(is);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return data;
    }

    private static byte[] doRead(InputStream is) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buf = new byte[BUFFER_SIZE];
        int r;
        while ((r = is.read(buf)) > 0) {
            out.write(buf, 0, r);
        }
        return out.toByteArray();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy