diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java index d25f3f44d65..692be858f1c 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java @@ -400,8 +400,7 @@ public class CrossOriginFilter implements Filter { response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, origin); //W3C CORS spec http://www.w3.org/TR/cors/#resource-implementation - if (!anyOriginAllowed) - response.addHeader("Vary", ORIGIN_HEADER); + response.addHeader("Vary", ORIGIN_HEADER); if (allowCredentials) response.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true"); if (!exposedHeaders.isEmpty()) diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java index 01a0bca240d..cc529bf176b 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java @@ -135,7 +135,7 @@ public class CrossOriginFilterTest Set fieldNames = response.getFieldNamesCollection(); assertThat(response.toString(), CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, isIn(fieldNames)); assertThat(response.toString(), CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, isIn(fieldNames)); - assertThat(response.toString(), "Vary", not(isIn(fieldNames))); + assertThat(response.toString(), "Vary", isIn(fieldNames)); assertTrue(latch.await(1, TimeUnit.SECONDS)); } diff --git a/pom.xml b/pom.xml index fb30a10e6f2..772384bfa5a 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,8 @@ 1.2.0 1.1.5 5.3.1 - 3.5.0 + 3.6.0 + 1.3.1 2.4.5.Final 1.0.5 diff --git a/tests/pom.xml b/tests/pom.xml index 75c7c09c84c..75cd4504512 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -68,5 +68,6 @@ test-quickstart test-jmx test-http-client-transport + test-distribution diff --git a/tests/test-distribution/pom.xml b/tests/test-distribution/pom.xml new file mode 100644 index 00000000000..091418d18cb --- /dev/null +++ b/tests/test-distribution/pom.xml @@ -0,0 +1,100 @@ + + + org.eclipse.jetty.tests + tests-parent + 10.0.0-SNAPSHOT + + + 4.0.0 + test-distribution + jar + + + ${project.groupId}.tests.distribution + + + + + org.slf4j + slf4j-simple + + + org.eclipse.jetty + jetty-util + ${project.version} + + + org.apache.maven + maven-artifact + + + org.apache.maven + maven-resolver-provider + ${maven.version} + + + org.apache.maven.resolver + maven-resolver-connector-basic + ${maven.resolver.version} + + + org.apache.maven.resolver + maven-resolver-transport-file + ${maven.resolver.version} + + + org.apache.maven.resolver + maven-resolver-transport-http + ${maven.resolver.version} + + + + org.eclipse.jetty + jetty-distribution + ${project.version} + zip + test + + + org.eclipse.jetty + jetty-client + ${project.version} + test + + + org.eclipse.jetty.http2 + http2-http-client-transport + ${project.version} + test + + + org.eclipse.jetty.tests + test-simple-webapp + ${project.version} + war + test + + + org.eclipse.jetty.toolchain + jetty-test-helper + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + ${settings.localRepository} + ${project.version} + + + + + + + diff --git a/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java new file mode 100644 index 00000000000..57571c4d0c0 --- /dev/null +++ b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java @@ -0,0 +1,601 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.maven.repository.internal.MavenRepositorySystemUtils; +import org.codehaus.plexus.util.IOUtil; +import org.eclipse.aether.AbstractRepositoryListener; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositoryEvent; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; +import org.eclipse.aether.impl.DefaultServiceLocator; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.ArtifactResolutionException; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; +import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.transfer.AbstractTransferListener; +import org.eclipse.aether.transport.file.FileTransporterFactory; +import org.eclipse.aether.transport.http.HttpTransporterFactory; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +/** + *

Helper class to test the Jetty Distribution

. + *

API can change without any further notice.

+ *

Usage:

+ *
+ * // Create the distribution.
+ * String jettyVersion = "9.4.14.v20181114";
+ * DistributionTester distribution = DistributionTester.Builder.newInstance()
+ *         .jettyVersion(jettyVersion)
+ *         .jettyBase(Paths.get("demo-base"))
+ *         .build();
+ *
+ * // The first run initializes the Jetty Base.
+ * try (DistributionTester.Run run1 = distribution.start("--create-startd", "--add-to-start=http2c,jsp,deploy"))
+ * {
+ *     assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
+ *     assertEquals(0, run1.getExitValue());
+ *
+ *     // Install a web application.
+ *     File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
+ *     distribution.installWarFile(war, "test");
+ *
+ *     // The second run starts the distribution.
+ *     int port = 9090;
+ *     try (DistributionTester.Run run = distribution.start("jetty.http.port=" + port))
+ *     {
+ *         // Wait for Jetty to be fully started.
+ *         assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS));
+ *
+ *         // Make a HTTP request to the web application.
+ *         HttpClient client = new HttpClient();
+ *         client.start();
+ *         ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
+ *         assertEquals(HttpStatus.OK_200, response.getStatus());
+ *     }
+ * }
+ * 
+ */ +public class DistributionTester +{ + private static final Logger LOGGER = Log.getLogger(DistributionTester.class); + + private Config config; + + private DistributionTester(Config config) + { + this.config = config; + } + + /** + * Starts the distribution with the given arguments + * + * @param args arguments to use to start the distribution + */ + public DistributionTester.Run start(String... args) throws Exception + { + return start(Arrays.asList(args)); + } + + /** + * Start the distribution with the arguments + * + * @param args arguments to use to start the distribution + */ + public DistributionTester.Run start(List args) throws Exception + { + List commands = new ArrayList<>(); + commands.add(getJavaExecutable()); + commands.add("-jar"); + commands.add(config.jettyHome.toAbsolutePath() + "/start.jar"); + commands.addAll(args); + + File workingDir = config.jettyBase.toFile(); + LOGGER.info("Executing: {}", commands); + LOGGER.info("Working Dir: {}", workingDir.getAbsolutePath()); + + ProcessBuilder pbCmd = new ProcessBuilder(commands); + pbCmd.directory(workingDir); + Process process = pbCmd.start(); + + return new Run(process); + } + + /** + * @return a free port chosen by the OS that can be used to listen to + * @throws IOException if a free port is not available + */ + public int freePort() throws IOException + { + try (ServerSocket server = new ServerSocket()) + { + server.setReuseAddress(true); + server.bind(new InetSocketAddress("localhost", 0)); + return server.getLocalPort(); + } + } + + /** + * Installs in {@code ${jetty.base}/webapps} the given war file under the given context path. + * + * @param warFile the war file to install + * @param context the context path + * @throws IOException if the installation fails + */ + public void installWarFile(File warFile, String context) throws IOException + { + //webapps + Path webapps = Paths.get(config.jettyBase.toString(), "webapps", context); + if (!Files.exists(webapps)) + Files.createDirectories(webapps); + unzip(warFile, webapps.toFile()); + } + + /** + * Resolves an artifact given its Maven coordinates. + * + * @param coordinates :[:[:]]: + * @return the artifact + * @see #installWarFile(File, String) + */ + public File resolveArtifact(String coordinates) throws ArtifactResolutionException + { + RepositorySystem repositorySystem = newRepositorySystem(); + + Artifact artifact = new DefaultArtifact(coordinates); + + RepositorySystemSession session = newRepositorySystemSession(repositorySystem); + + ArtifactRequest artifactRequest = new ArtifactRequest(); + artifactRequest.setArtifact(artifact); + artifactRequest.setRepositories(newRepositories()); + ArtifactResult artifactResult = repositorySystem.resolveArtifact(session, artifactRequest); + + artifact = artifactResult.getArtifact(); + return artifact.getFile(); + } + + private void init() throws Exception + { + if (config.jettyHome == null) + config.jettyHome = resolveDistribution(config.jettyVersion); + + if (config.jettyBase == null) + { + config.jettyBase = Files.createTempDirectory("jetty_base_"); + } + else + { + if (!config.jettyBase.isAbsolute()) + config.jettyBase = config.jettyHome.resolve(config.jettyBase); + } + } + + private String getJavaExecutable() + { + String[] javaExecutables = new String[]{"java", "java.exe"}; + File javaHomeDir = new File(System.getProperty("java.home")); + for (String javaExecutable : javaExecutables) + { + File javaFile = new File(javaHomeDir, "bin" + File.separator + javaExecutable); + if (javaFile.exists() && javaFile.isFile()) + return javaFile.getAbsolutePath(); + } + return "java"; + } + + private void unzip(File zipFile, File output) throws IOException + { + try (InputStream fileInputStream = Files.newInputStream(zipFile.toPath()); + ZipInputStream zipInputStream = new ZipInputStream(fileInputStream)) + { + ZipEntry entry = zipInputStream.getNextEntry(); + while (entry != null) + { + if (entry.isDirectory()) + { + File dir = new File(output, entry.getName()); + if (!Files.exists(dir.toPath())) + { + Files.createDirectories(dir.toPath()); + } + } + else + { + // Read zipEntry and write to a file. + File file = new File(output, entry.getName()); + if (!Files.exists(file.getParentFile().toPath())) + { + Files.createDirectories(file.getParentFile().toPath()); + } + try (OutputStream outputStream = Files.newOutputStream(file.toPath())) + { + IOUtil.copy(zipInputStream, outputStream); + } + } + // Get next entry + entry = zipInputStream.getNextEntry(); + } + } + } + + private Path resolveDistribution(String version) throws Exception + { + File artifactFile = resolveArtifact("org.eclipse.jetty:jetty-distribution:zip:" + version); + + // create tmp directory to unzip distribution + Path tmp = Files.createTempDirectory("jetty_home_"); + File tmpFile = tmp.toFile(); + + unzip(artifactFile, tmpFile); + + return tmp.resolve("jetty-distribution-" + version); + } + + private RepositorySystem newRepositorySystem() + { + DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); + locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class); + locator.addService(TransporterFactory.class, FileTransporterFactory.class); + locator.addService(TransporterFactory.class, HttpTransporterFactory.class); + + locator.setErrorHandler(new DefaultServiceLocator.ErrorHandler() + { + @Override + public void serviceCreationFailed(Class type, Class impl, Throwable exception) + { + LOGGER.warn("Service creation failed for {} implementation {}: {}", + type, impl, exception.getMessage(), exception); + } + }); + + return locator.getService(RepositorySystem.class); + } + + private List newRepositories() + { + List remoteRepositories = new ArrayList<>(config.mavenRemoteRepositories.size() + 1); + config.mavenRemoteRepositories.forEach((key, value) -> remoteRepositories.add(new RemoteRepository.Builder(key, "default", value).build())); + remoteRepositories.add(newCentralRepository()); + return remoteRepositories; + } + + private static RemoteRepository newCentralRepository() + { + return new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2/").build(); + } + + private DefaultRepositorySystemSession newRepositorySystemSession(RepositorySystem system) + { + DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); + + LocalRepository localRepo = new LocalRepository(config.mavenLocalRepository); + session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo)); + + session.setTransferListener(new LogTransferListener()); + session.setRepositoryListener(new LogRepositoryListener()); + + return session; + } + + private static class Config + { + private Path jettyBase; + private Path jettyHome; + private String jettyVersion; + private String mavenLocalRepository = System.getProperty("user.home") + "/.m2/repository"; + private Map mavenRemoteRepositories = new HashMap<>(); + + @Override + public String toString() + { + return String.format("%s@%x{jettyBase=%s, jettyHome=%s, jettyVersion=%s, mavenLocalRepository=%s, mavenRemoteRepositories=%s}", + getClass().getSimpleName(), + hashCode(), + jettyBase, + jettyHome, + jettyVersion, + mavenLocalRepository, + mavenRemoteRepositories); + } + } + + private static class LogTransferListener extends AbstractTransferListener + { + // no op + } + + private static class LogRepositoryListener extends AbstractRepositoryListener + { + @Override + public void artifactDownloaded(RepositoryEvent event) + { + LOGGER.debug("distribution downloaded to {}", event.getFile()); + } + + @Override + public void artifactResolved(RepositoryEvent event) + { + LOGGER.debug("distribution resolved to {}", event.getFile()); + } + } + + /** + * A distribution run wraps the process that started the Jetty distribution. + */ + public static class Run implements Closeable + { + private final Process process; + private final List consoleStreamers = new ArrayList<>(); + private final List logs = new ArrayList<>(); + + private Run(Process process) + { + this.process = process; + consoleStreamers.add(startPump("STDOUT", process.getInputStream())); + consoleStreamers.add(startPump("STDERR", process.getErrorStream())); + } + + private ConsoleStreamer startPump(String mode, InputStream stream) + { + ConsoleStreamer pump = new ConsoleStreamer(stream); + Thread thread = new Thread(pump, "ConsoleStreamer/" + mode); + thread.start(); + return pump; + } + + /** + * Waits for the given time for the distribution process to stop. + * + * @param time the time to wait + * @param unit the unit of time + * @return true if the distribution process is terminated, false if the timeout elapsed + * @throws InterruptedException if the wait is interrupted + */ + public boolean awaitFor(long time, TimeUnit unit) throws InterruptedException + { + boolean result = process.waitFor(time, unit); + if (result) + stopConsoleStreamers(); + return result; + } + + /** + * @return the distribution process exit value + * @throws IllegalThreadStateException if the distribution process is not terminated yet + */ + public int getExitValue() throws IllegalThreadStateException + { + return process.exitValue(); + } + + /** + * Stops the distribution process. + * + * @see #awaitFor(long, TimeUnit) + */ + public void stop() + { + process.destroy(); + stopConsoleStreamers(); + } + + /** + * Forcibly destroys the distribution process. + */ + public void destroy() + { + process.destroyForcibly(); + stopConsoleStreamers(); + } + + private void stopConsoleStreamers() + { + consoleStreamers.forEach(ConsoleStreamer::stop); + } + + /** + * @see #destroy() + */ + @Override + public void close() + { + destroy(); + } + + /** + * Awaits the console logs to contain the given text, for the given amount of time. + * + * @param txt the text that must be present in the console logs + * @param time the time to wait + * @param unit the unit of time + * @return true if the text was found, false if the timeout elapsed + * @throws InterruptedException if the wait is interrupted + */ + public boolean awaitConsoleLogsFor(String txt, long time, TimeUnit unit) throws InterruptedException + { + long end = System.nanoTime() + unit.toNanos(time); + while (System.nanoTime() < end) + { + boolean result = logs.stream().anyMatch(s -> s.contains(txt)); + if (result) + return true; + Thread.sleep(250); + } + return false; + } + + /** + * Simple streamer for the console output from a Process + */ + private class ConsoleStreamer implements Runnable + { + private final BufferedReader reader; + private volatile boolean stop; + + public ConsoleStreamer(InputStream stream) + { + this.reader = new BufferedReader(new InputStreamReader(stream)); + } + + @Override + public void run() + { + try + { + String line; + while ((line = reader.readLine()) != null && !stop) + { + LOGGER.info("{}", line); + logs.add(line); + } + } + catch (IOException ignore) + { + // ignore + } + finally + { + IO.close(reader); + } + } + + public void stop() + { + stop = true; + IO.close(reader); + } + } + } + + public static class Builder + { + private Config config = new Config(); + + private Builder() + { + } + + /** + * @param jettyVersion the version to use (format: 9.4.14.v20181114 9.4.15-SNAPSHOT). + * The distribution will be downloaded from local repository or remote + * @return the {@link Builder} + */ + public Builder jettyVersion(String jettyVersion) + { + config.jettyVersion = jettyVersion; + return this; + } + + /** + * @param jettyHome Path to the local exploded jetty distribution + * if configured the jettyVersion parameter will not be used + * @return the {@link Builder} + */ + public Builder jettyHome(Path jettyHome) + { + config.jettyHome = jettyHome; + return this; + } + + /** + *

Sets the path for the Jetty Base directory.

+ *

If the path is relative, it will be resolved against the Jetty Home directory.

+ * + * @param jettyBase Path to the local Jetty Base directory + * @return the {@link Builder} + */ + public Builder jettyBase(Path jettyBase) + { + config.jettyBase = jettyBase; + return this; + } + + /** + * @param mavenLocalRepository Path to the local maven repository + * @return the {@link Builder} + */ + public Builder mavenLocalRepository(String mavenLocalRepository) + { + config.mavenLocalRepository = mavenLocalRepository; + return this; + } + + /** + * If needed to resolve the Jetty distribution from another Maven remote repositories + * + * @param id the id + * @param url the Maven remote repository url + * @return the {@link Builder} + */ + public Builder addRemoteRepository(String id, String url) + { + config.mavenRemoteRepositories.put(id, url); + return this; + } + + /** + * @return an empty instance of {@link Builder} + */ + public static Builder newInstance() + { + return new Builder(); + } + + /** + * @return a new configured instance of {@link DistributionTester} + */ + public DistributionTester build() throws Exception + { + DistributionTester tester = new DistributionTester(config); + tester.init(); + return tester; + } + } +} diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/AbstractDistributionTest.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/AbstractDistributionTest.java new file mode 100644 index 00000000000..1050918ed13 --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/AbstractDistributionTest.java @@ -0,0 +1,47 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution; + +import java.util.function.Supplier; + +import org.eclipse.jetty.client.HttpClient; +import org.junit.jupiter.api.AfterEach; + +public class AbstractDistributionTest +{ + protected HttpClient client; + + protected void startHttpClient() throws Exception + { + startHttpClient(HttpClient::new); + } + + protected void startHttpClient(Supplier supplier) throws Exception + { + client = supplier.get(); + client.start(); + } + + @AfterEach + public void dispose() throws Exception + { + if (client != null) + client.stop(); + } +} diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java new file mode 100644 index 00000000000..11718fed86f --- /dev/null +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java @@ -0,0 +1,207 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.tests.distribution; + +import java.io.File; +import java.nio.file.Paths; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http2.client.HTTP2Client; +import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnJre; +import org.junit.jupiter.api.condition.JRE; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class DistributionTests extends AbstractDistributionTest +{ + @Test + public void testStartStop() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + DistributionTester distribution = DistributionTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + try (DistributionTester.Run run1 = distribution.start("--add-to-start=http")) + { + assertTrue(run1.awaitFor(5, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + int port = distribution.freePort(); + try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) + { + assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + + startHttpClient(); + ContentResponse response = client.GET("http://localhost:" + port); + assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus()); + + run2.stop(); + assertTrue(run2.awaitFor(5, TimeUnit.SECONDS)); + } + } + } + + @Test + public void testSimpleWebAppWithJSP() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + DistributionTester distribution = DistributionTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + String[] args1 = { + "--create-startd", + "--approve-all-licenses", + "--add-to-start=resources,server,http,webapp,deploy,jsp,jmx,servlet,servlets" + }; + try (DistributionTester.Run run1 = distribution.start(args1)) + { + assertTrue(run1.awaitFor(5, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion); + distribution.installWarFile(war, "test"); + + int port = distribution.freePort(); + try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) + { + assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + + startHttpClient(); + ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); + assertEquals(HttpStatus.OK_200, response.getStatus()); + assertThat(response.getContentAsString(), containsString("Hello")); + assertThat(response.getContentAsString(), not(containsString("<%"))); + } + } + } + + @Test + @DisabledOnJre(JRE.JAVA_8) + public void testSimpleWebAppWithJSPOnModulePath() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + DistributionTester distribution = DistributionTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + String[] args1 = { + "--create-startd", + "--approve-all-licenses", + "--add-to-start=resources,server,http,webapp,deploy,jsp,jmx,servlet,servlets" + }; + try (DistributionTester.Run run1 = distribution.start(args1)) + { + assertTrue(run1.awaitFor(5, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion); + distribution.installWarFile(war, "test"); + + int port = distribution.freePort(); + String[] args2 = { + "--jpms", + "jetty.http.port=" + port + }; + try (DistributionTester.Run run2 = distribution.start(args2)) + { + assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + + startHttpClient(); + ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); + assertEquals(HttpStatus.OK_200, response.getStatus()); + assertThat(response.getContentAsString(), containsString("Hello")); + assertThat(response.getContentAsString(), not(containsString("<%"))); + } + } + } + + @Test + @DisabledOnJre(JRE.JAVA_8) + public void testSimpleWebAppWithJSPOverH2C() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + DistributionTester distribution = DistributionTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + String[] args1 = { + "--create-startd", + "--add-to-start=http2c,jsp,deploy" + }; + try (DistributionTester.Run run1 = distribution.start(args1)) + { + assertTrue(run1.awaitFor(5, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion); + distribution.installWarFile(war, "test"); + + int port = distribution.freePort(); + try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) + { + assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + + HTTP2Client h2Client = new HTTP2Client(); + startHttpClient(() -> new HttpClient(new HttpClientTransportOverHTTP2(h2Client), null)); + ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); + assertEquals(HttpStatus.OK_200, response.getStatus()); + assertThat(response.getContentAsString(), containsString("Hello")); + assertThat(response.getContentAsString(), not(containsString("<%"))); + } + } + } + + @Test + public void testDemoBase() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + DistributionTester distribution = DistributionTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .jettyBase(Paths.get("demo-base")) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + int port = distribution.freePort(); + try (DistributionTester.Run run1 = distribution.start("jetty.http.port=" + port)) + { + assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS)); + + startHttpClient(); + ContentResponse response = client.GET("http://localhost:" + port + "/test/jsp/dump.jsp"); + assertEquals(HttpStatus.OK_200, response.getStatus()); + assertThat(response.getContentAsString(), containsString("PathInfo")); + assertThat(response.getContentAsString(), not(containsString("<%"))); + } + } +} diff --git a/tests/test-distribution/src/test/resources/jetty-logging.properties b/tests/test-distribution/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..d96a696f82e --- /dev/null +++ b/tests/test-distribution/src/test/resources/jetty-logging.properties @@ -0,0 +1,2 @@ +org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +#org.eclipse.jetty.LEVEL=DEBUG diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java index b523e9bf398..299be625e9c 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java @@ -56,6 +56,7 @@ import org.eclipse.jetty.toolchain.test.JAR; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.PathAssert; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.junit.jupiter.api.Assertions; /** @@ -315,19 +316,15 @@ public class JettyDistro // The actual jetty-distribution-${version} directory is under this directory. // Lets find it. - File subdirs[] = distroUnpackDir.listFiles(new FileFilter() - { - @Override - public boolean accept(File path) + File subdirs[] = distroUnpackDir.listFiles( path -> { if (!path.isDirectory()) { return false; } - return path.getName().startsWith(artifactName + "-"); } - }); + ); if (subdirs.length == 0) { @@ -550,7 +547,7 @@ public class JettyDistro if (cmdLine == null || !cmdLine.contains("XmlConfiguration")) { - fail("Unable to get Jetty command line"); + Assertions.fail( "Unable to get Jetty command line"); } // Need to breakdown commandline into parts, as spaces in command line will cause failures. @@ -595,7 +592,7 @@ public class JettyDistro catch (InterruptedException e) { pid.destroy(); - fail("Unable to get required information within time limit"); + Assertions.fail( "Unable to get required information within time limit"); } } @@ -804,7 +801,7 @@ public class JettyDistro } } - fail("Unable to find java bin"); + Assertions.fail( "Unable to find java bin"); return "java"; } diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java index d47cc4380db..a612e6592e7 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java @@ -49,7 +49,7 @@ import org.junit.jupiter.api.Disabled; public class TestableJettyServer { private List _xmlConfigurations; - private final Map _properties = new HashMap(); + private final Map _properties = new HashMap<>(); private Server _server; private int _serverPort; private String _scheme = HttpScheme.HTTP.asString(); @@ -60,7 +60,7 @@ public class TestableJettyServer public TestableJettyServer() throws IOException { - _xmlConfigurations = new ArrayList(); + _xmlConfigurations = new ArrayList<>(); Properties properties = new Properties(); /* Establish Popular Directories */ diff --git a/tests/test-webapps/pom.xml b/tests/test-webapps/pom.xml index c71ef3ec853..3b0f030f246 100644 --- a/tests/test-webapps/pom.xml +++ b/tests/test-webapps/pom.xml @@ -39,5 +39,6 @@ test-jaas-webapp test-jndi-webapp test-http2-webapp + test-simple-webapp - + \ No newline at end of file diff --git a/tests/test-webapps/test-simple-webapp/pom.xml b/tests/test-webapps/test-simple-webapp/pom.xml new file mode 100644 index 00000000000..9c31f9dcde5 --- /dev/null +++ b/tests/test-webapps/test-simple-webapp/pom.xml @@ -0,0 +1,15 @@ + + + + org.eclipse.jetty.tests + test-webapps-parent + 10.0.0-SNAPSHOT + + + 4.0.0 + test-simple-webapp + war + + Test :: Jetty Test Simple Webapp + + diff --git a/tests/test-webapps/test-simple-webapp/src/main/webapp/WEB-INF/web.xml b/tests/test-webapps/test-simple-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..4905321a949 --- /dev/null +++ b/tests/test-webapps/test-simple-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,10 @@ + + + + Very Simple Web Application + diff --git a/tests/test-webapps/test-simple-webapp/src/main/webapp/index.jsp b/tests/test-webapps/test-simple-webapp/src/main/webapp/index.jsp new file mode 100644 index 00000000000..c38169bb958 --- /dev/null +++ b/tests/test-webapps/test-simple-webapp/src/main/webapp/index.jsp @@ -0,0 +1,5 @@ + + +

Hello World!

+ +