diff --git a/STATUS.adoc b/STATUS.adoc index 7467afaf..964d066f 100644 --- a/STATUS.adoc +++ b/STATUS.adoc @@ -525,6 +525,12 @@ h|nativeTest |image:https://ci.spring.io/api/v1/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-jetty-test/badge[link=https://ci.spring.io/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-jetty-test] |image:https://ci.spring.io/api/v1/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-jetty-native-test/badge[link=https://ci.spring.io/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-jetty-native-test] +|webmvc-jetty-tls +|image:https://ci.spring.io/api/v1/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-jetty-tls-app-test/badge[link=https://ci.spring.io/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-jetty-tls-app-test] +|image:https://ci.spring.io/api/v1/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-jetty-tls-native-app-test/badge[link=https://ci.spring.io/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-jetty-tls-native-app-test] +| +| + |webmvc-tomcat |image:https://ci.spring.io/api/v1/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-tomcat-app-test/badge[link=https://ci.spring.io/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-tomcat-app-test] |image:https://ci.spring.io/api/v1/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-tomcat-native-app-test/badge[link=https://ci.spring.io/teams/spring-aot-smoke-tests/pipelines/spring-aot-smoke-tests-1.0.x/jobs/webmvc-tomcat-native-app-test] diff --git a/ci/smoke-tests.yml b/ci/smoke-tests.yml index 5feadd07..fa42d450 100644 --- a/ci/smoke-tests.yml +++ b/ci/smoke-tests.yml @@ -255,6 +255,9 @@ groups: - name: webmvc-jetty app_test: true test: true + - name: webmvc-jetty-tls + app_test: true + test: false - name: webmvc-tomcat app_test: true test: true diff --git a/framework/webmvc-jetty-tls/README.adoc b/framework/webmvc-jetty-tls/README.adoc new file mode 100644 index 00000000..68a76b60 --- /dev/null +++ b/framework/webmvc-jetty-tls/README.adoc @@ -0,0 +1 @@ +Tests if WebMVC with Jetty with TLS is working diff --git a/framework/webmvc-jetty-tls/build.gradle b/framework/webmvc-jetty-tls/build.gradle new file mode 100644 index 00000000..c9b438c1 --- /dev/null +++ b/framework/webmvc-jetty-tls/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'java' + id 'org.springframework.boot' + id 'org.springframework.aot.smoke-test' + id 'org.graalvm.buildtools.native' +} + +dependencies { + implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES)) + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-jetty") + modules { + module("org.springframework.boot:spring-boot-starter-tomcat") { + replacedBy("org.springframework.boot:spring-boot-starter-jetty", "Use Jetty instead of Tomcat") + } + } + // Downgrade for Jetty, see https://github.com/spring-projects/spring-boot/issues/33044 + implementation('jakarta.servlet:jakarta.servlet-api') { + version { + strictly '5.0.0' + } + } + + testImplementation("org.springframework.boot:spring-boot-starter-test") + + appTestImplementation(project(":aot-smoke-test-support")) +} + +aotSmokeTest { + webApplication = true +} diff --git a/framework/webmvc-jetty-tls/src/appTest/java/com/example/webmvc/jetty/tls/WebMvcJettyTlsApplicationAotTests.java b/framework/webmvc-jetty-tls/src/appTest/java/com/example/webmvc/jetty/tls/WebMvcJettyTlsApplicationAotTests.java new file mode 100644 index 00000000..da1f979d --- /dev/null +++ b/framework/webmvc-jetty-tls/src/appTest/java/com/example/webmvc/jetty/tls/WebMvcJettyTlsApplicationAotTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2022 the original author or authors. + * + * Licensed 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 + * + * https://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 com.example.webmvc.jetty.tls; + +import java.io.InputStream; +import java.net.URI; +import java.net.http.HttpClient; +import java.security.KeyStore; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +import org.junit.jupiter.api.Test; + +import org.springframework.aot.smoketest.support.junit.ApplicationTest; +import org.springframework.aot.smoketest.support.junit.ApplicationUrl; +import org.springframework.aot.smoketest.support.junit.ApplicationUrl.Scheme; +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.http.client.reactive.JdkClientHttpConnector; +import org.springframework.test.web.reactive.server.WebTestClient; + +import static org.assertj.core.api.Assertions.assertThat; + +@ApplicationTest +class WebMvcJettyTlsApplicationAotTests { + + @Test + void stringResponseBody(@ApplicationUrl(scheme = Scheme.HTTPS) URI applicationUrl) throws Exception { + WebTestClient client = buildWebClient(applicationUrl); + + client.get().exchange().expectStatus().isOk().expectBody().consumeWith( + (result) -> assertThat(new String(result.getResponseBodyContent())).isEqualTo("Hello World TLS")); + } + + /** + * Builds a web client for the running application. This web client is configured to + * trust the TLS certificate of the server. + * @param applicationUrl the URL to the application + * @return web client + * @throws Exception if something went wrong + */ + private static WebTestClient buildWebClient(URI applicationUrl) throws Exception { + KeyStore trustStore = KeyStore.getInstance("jks"); + try (InputStream stream = WebMvcJettyTlsApplicationAotTests.class.getResourceAsStream("/truststore.jks")) { + assertThat(stream).as("Trust store on test classpath").isNotNull(); + trustStore.load(stream, null); + } + + TrustManagerFactory trustManagerFactory = TrustManagerFactory + .getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(trustStore); + + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustManagerFactory.getTrustManagers(), null); + + HttpClient httpClient = HttpClient.newBuilder().sslContext(sslContext).build(); + ClientHttpConnector connector = new JdkClientHttpConnector(httpClient); + return WebTestClient.bindToServer(connector).baseUrl(applicationUrl.toString()).build(); + } + +} diff --git a/framework/webmvc-jetty-tls/src/appTest/resources/truststore.jks b/framework/webmvc-jetty-tls/src/appTest/resources/truststore.jks new file mode 100644 index 00000000..06318674 Binary files /dev/null and b/framework/webmvc-jetty-tls/src/appTest/resources/truststore.jks differ diff --git a/framework/webmvc-jetty-tls/src/main/java/com/example/webmvc/jetty/tls/TestController.java b/framework/webmvc-jetty-tls/src/main/java/com/example/webmvc/jetty/tls/TestController.java new file mode 100644 index 00000000..55a74924 --- /dev/null +++ b/framework/webmvc-jetty-tls/src/main/java/com/example/webmvc/jetty/tls/TestController.java @@ -0,0 +1,15 @@ +package com.example.webmvc.jetty.tls; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class TestController { + + @GetMapping(value = "/", produces = MediaType.TEXT_PLAIN_VALUE) + public String index() { + return "Hello World TLS"; + } + +} diff --git a/framework/webmvc-jetty-tls/src/main/java/com/example/webmvc/jetty/tls/WebMvcJettyTlsApplication.java b/framework/webmvc-jetty-tls/src/main/java/com/example/webmvc/jetty/tls/WebMvcJettyTlsApplication.java new file mode 100644 index 00000000..f7e3d98a --- /dev/null +++ b/framework/webmvc-jetty-tls/src/main/java/com/example/webmvc/jetty/tls/WebMvcJettyTlsApplication.java @@ -0,0 +1,28 @@ +package com.example.webmvc.jetty.tls; + +import com.example.webmvc.jetty.tls.WebMvcJettyTlsApplication.KeyStoreRuntimeHints; + +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ImportRuntimeHints; + +@SpringBootApplication +@ImportRuntimeHints(KeyStoreRuntimeHints.class) +public class WebMvcJettyTlsApplication { + + public static void main(String[] args) { + SpringApplication.run(WebMvcJettyTlsApplication.class, args); + } + + static class KeyStoreRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + hints.resources().registerPattern("keystore.jks"); + } + + } + +} diff --git a/framework/webmvc-jetty-tls/src/main/resources/application.properties b/framework/webmvc-jetty-tls/src/main/resources/application.properties new file mode 100644 index 00000000..b2dbf04e --- /dev/null +++ b/framework/webmvc-jetty-tls/src/main/resources/application.properties @@ -0,0 +1,4 @@ +server.ssl.key-store=classpath:keystore.jks +server.ssl.key-store-password=secret +server.ssl.key-alias=localhost +server.ssl.key-password=secret diff --git a/framework/webmvc-jetty-tls/src/main/resources/keystore.jks b/framework/webmvc-jetty-tls/src/main/resources/keystore.jks new file mode 100644 index 00000000..cad0ed8d Binary files /dev/null and b/framework/webmvc-jetty-tls/src/main/resources/keystore.jks differ