/** * Copyright (C) 2012-2017 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 * * 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 ninja.standalone; import com.github.kevinsawicki.http.HttpRequest; import com.google.inject.CreationException; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.URI; import java.net.URL; import java.net.URLConnection; import ninja.utils.NinjaMode; import org.apache.commons.io.IOUtils; import org.eclipse.jetty.server.handler.ErrorHandler; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.core.IsInstanceOf.instanceOf; import org.junit.Test; import static org.junit.Assert.*; public class NinjaJettyTest { static int RANDOM_PORT = StandaloneHelper.findAvailablePort(8081, 9000); @Test public void minimal() throws Exception { NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.minimal.conf") .port(RANDOM_PORT); try { assertThat(standalone.getPort(), is(RANDOM_PORT)); assertThat(standalone.getHost(), is(nullValue())); assertThat(standalone.getContextPath(), is(nullValue())); assertThat(standalone.getNinjaMode(), is(NinjaMode.prod)); standalone.start(); assertThat(standalone.getServerUrls().get(0), is("http://localhost:" + RANDOM_PORT)); assertThat(standalone.contextHandler, is(not(nullValue()))); assertNotNull(standalone.ninjaServletListener); assertThat(standalone.contextHandler.isAvailable(), is(true)); assertThat(standalone.contextHandler.isStarted(), is(true)); assertThat(standalone.jetty.isStarted(), is(true)); standalone.shutdown(); assertThat(standalone.contextHandler.isStopped(), is(true)); assertThat(standalone.jetty.isStopped(), is(true)); } finally { standalone.shutdown(); } } @Test public void minimalWithContext() throws Exception { NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.minimal.conf") .ninjaMode(NinjaMode.test) .port(RANDOM_PORT) .host("localhost") .contextPath("/mycontext"); try { standalone.start(); assertThat(standalone.getPort(), is(RANDOM_PORT)); assertThat(standalone.getHost(), is("localhost")); assertThat(standalone.getContextPath(), is("/mycontext")); assertThat(standalone.getNinjaMode(), is(NinjaMode.test)); assertEquals("/mycontext", standalone.contextHandler.getContextPath()); assertThat(standalone.getContextPath(), is(not(nullValue()))); assertThat(standalone.ninjaServletListener, is(not(nullValue()))); assertThat(standalone.contextHandler.isAvailable(), is(true)); assertThat(standalone.contextHandler.isStarted(), is(true)); assertThat(standalone.jetty.isStarted(), is(true)); standalone.shutdown(); assertThat(standalone.contextHandler.isStopped(), is(true)); assertThat(standalone.jetty.isStopped(), is(true)); } finally { standalone.shutdown(); } } @Test public void missingConfigurationThrowsException() throws Exception { NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.empty.conf") .port(RANDOM_PORT); try { standalone.start(); fail("start() should have thrown exception"); } catch (Exception e) { assertThat(e.getMessage(), containsString("application.secret not set")); } finally { standalone.shutdown(); } } @Test public void missingLanguageThrowsInjectorException() throws Exception { // bad configuration file will throw exception when creating NinjaPropertiesImpl // that exception occurs in NinjaBootstrap during injector creation NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.missinglang.conf") .port(RANDOM_PORT); try { standalone.start(); fail("start() should have thrown exception"); } catch (CreationException e) { assertThat(e.getMessage(), containsString("not retrieve application languages from ninjaProperties")); } finally { standalone.shutdown(); } } @Test public void jettyConfiguration() throws Exception { // use test resource of "jetty.xml" but we need to swap into a new // random port and then write the file back out String jettyConfiguration = createJettyConfiguration("jetty.xml", RANDOM_PORT); NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.com.example.conf") .jettyConfiguration(jettyConfiguration); try { standalone.start(); // port won't be correct b/c actually configured via jetty file assertThat(standalone.ninjaServletListener, is(not(nullValue()))); assertThat(standalone.contextHandler.isAvailable(), is(true)); assertThat(standalone.contextHandler.isStarted(), is(true)); assertThat(standalone.jetty.isStarted(), is(true)); String page = get("http://localhost:" + RANDOM_PORT + "/home"); assertThat(page, containsString("Hello World!")); } finally { standalone.shutdown(); } } @Test public void jettyConfigurationWithContext() throws Exception { // use test resource of "jetty.xml" but we need to swap into a new // random port and then write the file back out String jettyConfiguration = createJettyConfiguration("jetty.xml", RANDOM_PORT); NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.com.example.conf") .contextPath("/mycontext") .jettyConfiguration(jettyConfiguration); try { standalone.start(); // port won't be correct b/c actually configured via jetty file assertThat(standalone.ninjaServletListener, is(not(nullValue()))); assertThat(standalone.contextHandler.isAvailable(), is(true)); assertThat(standalone.contextHandler.isStarted(), is(true)); assertThat(standalone.jetty.isStarted(), is(true)); String page; page = get("http://localhost:" + RANDOM_PORT + "/mycontext/home"); assertThat(page, containsString("Hello World!")); page = get("http://localhost:" + RANDOM_PORT + "/mycontext/context_path"); // requestPath removes contextPath assertThat(page, containsString("/mycontext")); page = get("http://localhost:" + RANDOM_PORT + "/mycontext/request_path"); // requestPath removes contextPath assertThat(page, containsString("/request_path")); // is the port correct (otherwise logging will be wrong) assertThat(standalone.getPort(), is(RANDOM_PORT)); } finally { standalone.shutdown(); } } static public String createJettyConfiguration(String confName, int port) throws Exception { URL jettyConfig = NinjaJettyTest.class.getResource("/conf/" + confName); String jettyConfigString = IOUtils.toString(jettyConfig, "UTF-8"); // replace port w/ random String jettyConfigStringReplaced = jettyConfigString.replace("\"8080\"", "\"" + port + "\""); File jettyConfigFile = new File(jettyConfig.toURI()); File resourceDir = jettyConfigFile.getParentFile(); File newJettyConfigFile = new File(resourceDir, jettyConfigFile.getName() + "-" + port + ".xml"); IOUtils.write(jettyConfigStringReplaced, new FileOutputStream(newJettyConfigFile)); return "conf/" + newJettyConfigFile.getName(); } static public String get(String url) throws Exception { URL u = new URL(url); URLConnection conn = u.openConnection(); conn.setAllowUserInteraction(false); conn.setConnectTimeout(3000); conn.setReadTimeout(3000); try (InputStream is = conn.getInputStream()) { return IOUtils.toString(conn.getInputStream()); } } @Test public void ssl() throws Exception { NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.minimal.conf") .ninjaMode(NinjaMode.test) .port(-1) .sslPort(RANDOM_PORT); try { standalone.start(); assertThat(standalone.getPort(), is(-1)); assertThat(standalone.getHost(), is(nullValue())); assertThat(standalone.getContextPath(), is("")); assertThat(standalone.getNinjaMode(), is(NinjaMode.test)); assertThat(standalone.getSslPort(), is(RANDOM_PORT)); assertThat(standalone.getSslKeystoreUri(), is(new URI(Standalone.DEFAULT_DEV_NINJA_SSL_KEYSTORE_URI))); assertThat(standalone.getSslKeystorePassword(), is(Standalone.DEFAULT_DEV_NINJA_SSL_KEYSTORE_PASSWORD)); assertThat(standalone.getSslTruststoreUri(), is(new URI(Standalone.DEFAULT_DEV_NINJA_SSL_TRUSTSTORE_URI))); assertThat(standalone.getSslTruststorePassword(), is(Standalone.DEFAULT_DEV_NINJA_SSL_TRUSTSTORE_PASSWORD)); assertThat(standalone.getServerUrls().get(0), is("https://localhost:" + RANDOM_PORT)); assertThat(standalone.contextHandler, is(not(nullValue()))); assertNotNull(standalone.ninjaServletListener); assertThat(standalone.contextHandler.isAvailable(), is(true)); assertThat(standalone.contextHandler.isStarted(), is(true)); assertThat(standalone.jetty.isStarted(), is(true)); standalone.shutdown(); assertThat(standalone.contextHandler.isStopped(), is(true)); assertThat(standalone.jetty.isStopped(), is(true)); } finally { standalone.shutdown(); } } @Test public void sessionsAreNotSharedOnSingleResultInstances() throws Exception { // this test is not really specific to jetty, but its easier to test here NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.com.session.conf") .port(RANDOM_PORT); try { standalone.start(); // establish session with a set-cookie header HttpRequest client0 = HttpRequest.get(standalone.getBaseUrls().get(0) + "/getOrCreateSession"); assertThat(client0.code(), is(200)); assertThat(client0.header("Set-Cookie"), is(not(nullValue()))); // call redirect so its session is processed first time (triggers bug for issue #450) HttpRequest client1 = HttpRequest.get(standalone.getBaseUrls().get(0) + "/badRoute") .header("Cookie", client0.header("Set-Cookie")) .followRedirects(false); assertThat(client1.code(), is(303)); assertThat(client1.header("Set-Cookie"), is(not(nullValue()))); // call redirect with a mock new browser (no cookie header) -- we // should not get a session value back HttpRequest client2 = HttpRequest.get(standalone.getBaseUrls().get(0) + "/badRoute") .followRedirects(false); assertThat(client2.code(), is(303)); // if cookie is NOT null then someone elses cookie (from the previous // request above) got assigned to us! assertThat(client2.header("Set-Cookie"), is(nullValue())); } finally { standalone.shutdown(); } } @Test public void directoryListingIsForbidden() throws Exception { NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.minimal.conf") .port(RANDOM_PORT); try { standalone.start(); String directoryLisingAllowed = standalone.contextHandler.getInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed"); assertThat(directoryLisingAllowed, is("false")); } finally { standalone.shutdown(); } } @Test public void checkThatSilentErrorHandlerIsRegistered() throws Exception { NinjaJetty standalone = new NinjaJetty() .externalConfigurationPath("conf/jetty.minimal.conf") .port(RANDOM_PORT); try { standalone.start(); ErrorHandler errorHandler = standalone.contextHandler.getErrorHandler(); assertThat(errorHandler, instanceOf(NinjaJetty.SilentErrorHandler.class)); } finally { standalone.shutdown(); } } }