/* * 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.catalina.servlets; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TimeZone; import javax.servlet.http.HttpServletResponse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import org.junit.Assert; import org.junit.Test; import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.startup.SimpleHttpClient; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; public class TestDefaultServlet extends TomcatBaseTest { /** * Test attempting to access special paths (WEB-INF/META-INF) using * DefaultServlet. */ @Test public void testGetSpecials() throws Exception { Tomcat tomcat = getTomcatInstance(); String contextPath = "/examples"; File appDir = new File(getBuildDirectory(), "webapps" + contextPath); // app dir is relative to server home tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); tomcat.start(); final ByteChunk res = new ByteChunk(); int rc =getUrl("http://localhost:" + getPort() + contextPath + "/WEB-INF/web.xml", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); rc =getUrl("http://localhost:" + getPort() + contextPath + "/WEB-INF/doesntexistanywhere", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); rc =getUrl("http://localhost:" + getPort() + contextPath + "/WEB-INF/", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); rc =getUrl("http://localhost:" + getPort() + contextPath + "/META-INF/MANIFEST.MF", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); rc =getUrl("http://localhost:" + getPort() + contextPath + "/META-INF/doesntexistanywhere", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); } /** * Test https://bz.apache.org/bugzilla/show_bug.cgi?id=50026 * Verify serving of resources from context root with subpath mapping. */ @Test public void testGetWithSubpathmount() throws Exception { Tomcat tomcat = getTomcatInstance(); String contextPath = "/examples"; File appDir = new File(getBuildDirectory(), "webapps" + contextPath); // app dir is relative to server home org.apache.catalina.Context ctx = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); // Override the default servlet with our own mappings Tomcat.addServlet(ctx, "default2", new DefaultServlet()); ctx.addServletMapping("/", "default2"); ctx.addServletMapping("/servlets/*", "default2"); ctx.addServletMapping("/static/*", "default2"); tomcat.start(); final ByteChunk res = new ByteChunk(); // Make sure DefaultServlet isn't exposing special directories // by remounting the webapp under a sub-path int rc =getUrl("http://localhost:" + getPort() + contextPath + "/static/WEB-INF/web.xml", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); rc =getUrl("http://localhost:" + getPort() + contextPath + "/static/WEB-INF/doesntexistanywhere", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); rc =getUrl("http://localhost:" + getPort() + contextPath + "/static/WEB-INF/", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); rc =getUrl("http://localhost:" + getPort() + contextPath + "/static/META-INF/MANIFEST.MF", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); rc =getUrl("http://localhost:" + getPort() + contextPath + "/static/META-INF/doesntexistanywhere", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); // Make sure DefaultServlet is serving resources relative to the // context root regardless of where the it is mapped final ByteChunk rootResource = new ByteChunk(); rc =getUrl("http://localhost:" + getPort() + contextPath + "/index.html", rootResource, null); assertEquals(HttpServletResponse.SC_OK, rc); final ByteChunk subpathResource = new ByteChunk(); rc =getUrl("http://localhost:" + getPort() + contextPath + "/servlets/index.html", subpathResource, null); assertEquals(HttpServletResponse.SC_OK, rc); assertFalse(rootResource.toString().equals(subpathResource.toString())); rc =getUrl("http://localhost:" + getPort() + contextPath + "/static/index.html", res, null); assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); } /** * Test https://bz.apache.org/bugzilla/show_bug.cgi?id=50413 Serving a * custom error page */ @Test public void testCustomErrorPage() throws Exception { File appDir = new File(getTemporaryDirectory(), "MyApp"); File webInf = new File(appDir, "WEB-INF"); addDeleteOnTearDown(appDir); if (!webInf.mkdirs() && !webInf.isDirectory()) { fail("Unable to create directory [" + webInf + "]"); } Writer w = new OutputStreamWriter(new FileOutputStream(new File(appDir, "WEB-INF/web.xml")), "UTF-8"); try { w.write("<?xml version='1.0' encoding='UTF-8'?>\n" + "<web-app xmlns='http://java.sun.com/xml/ns/j2ee' " + " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'" + " xsi:schemaLocation='http://java.sun.com/xml/ns/j2ee " + " http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd'" + " version='2.4'>\n" + "<error-page>\n<error-code>404</error-code>\n" + "<location>/404.html</location>\n</error-page>\n" + "</web-app>\n"); w.flush(); } finally { w.close(); } w = new OutputStreamWriter(new FileOutputStream(new File(appDir, "404.html")), "ISO-8859-1"); try { w.write("It is 404.html"); w.flush(); } finally { w.close(); } Tomcat tomcat = getTomcatInstance(); String contextPath = "/MyApp"; tomcat.addWebapp(null, contextPath, appDir.getAbsolutePath()); tomcat.start(); TestCustomErrorClient client = new TestCustomErrorClient(tomcat.getConnector().getLocalPort()); client.reset(); client.setRequest(new String[] { "GET /MyApp/missing HTTP/1.0" +CRLF + CRLF }); client.connect(); client.processRequest(); assertTrue(client.isResponse404()); assertEquals("It is 404.html", client.getResponseBody()); SimpleDateFormat format = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US); format.setTimeZone(TimeZone.getTimeZone("GMT")); String tomorrow = format.format(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000)); // https://bz.apache.org/bugzilla/show_bug.cgi?id=50413 // client.reset(); client.setRequest(new String[] { "GET /MyApp/missing HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Connection: close" + CRLF + "If-Modified-Since: " + tomorrow + CRLF + CRLF }); client.connect(); client.processRequest(); assertTrue(client.isResponse404()); assertEquals("It is 404.html", client.getResponseBody()); // https://bz.apache.org/bugzilla/show_bug.cgi?id=50413#c6 // client.reset(); client.setRequest(new String[] { "GET /MyApp/missing HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Connection: close" + CRLF + "Range: bytes=0-100" + CRLF + CRLF }); client.connect(); client.processRequest(); assertTrue(client.isResponse404()); assertEquals("It is 404.html", client.getResponseBody()); } /** * Test what happens if a custom 404 page is configured, * but its file is actually missing. */ @Test public void testCustomErrorPageMissing() throws Exception { File appDir = new File(getTemporaryDirectory(), "MyApp"); File webInf = new File(appDir, "WEB-INF"); addDeleteOnTearDown(appDir); if (!webInf.mkdirs() && !webInf.isDirectory()) { fail("Unable to create directory [" + webInf + "]"); } Writer w = new OutputStreamWriter(new FileOutputStream(new File(appDir, "WEB-INF/web.xml")), "UTF-8"); try { w.write("<?xml version='1.0' encoding='UTF-8'?>\n" + "<web-app xmlns='http://java.sun.com/xml/ns/j2ee' " + " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'" + " xsi:schemaLocation='http://java.sun.com/xml/ns/j2ee " + " http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd'" + " version='2.4'>\n" + "<error-page>\n<error-code>404</error-code>\n" + "<location>/404-absent.html</location>\n</error-page>\n" + "</web-app>\n"); w.flush(); } finally { w.close(); } Tomcat tomcat = getTomcatInstance(); String contextPath = "/MyApp"; tomcat.addWebapp(null, contextPath, appDir.getAbsolutePath()); tomcat.start(); TestCustomErrorClient client = new TestCustomErrorClient(tomcat.getConnector().getLocalPort()); client.reset(); client.setRequest(new String[] { "GET /MyApp/missing HTTP/1.0" + CRLF + CRLF }); client.connect(); client.processRequest(); assertTrue(client.isResponse404()); } /** * Verifies that the same Content-Length is returned for both GET and HEAD * operations when a static resource served by the DefaultServlet is * included. */ @Test public void testBug57601() throws Exception { Tomcat tomcat = getTomcatInstance(); File appDir = new File("test/webapp-3.0"); tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); tomcat.start(); Map<String,List<String>> resHeaders= new HashMap<String,List<String>>(); String path = "http://localhost:" + getPort() + "/test/bug5nnnn/bug57601.jsp"; ByteChunk out = new ByteChunk(); int rc = getUrl(path, out, resHeaders); Assert.assertEquals(HttpServletResponse.SC_OK, rc); String length = resHeaders.get("Content-Length").get(0); Assert.assertEquals(Long.parseLong(length), out.getLength()); out.recycle(); rc = headUrl(path, out, resHeaders); Assert.assertEquals(HttpServletResponse.SC_OK, rc); Assert.assertEquals(0, out.getLength()); Assert.assertEquals(length, resHeaders.get("Content-Length").get(0)); tomcat.stop(); } public static int getUrl(String path, ByteChunk out, Map<String, List<String>> resHead) throws IOException { out.recycle(); return TomcatBaseTest.getUrl(path, out, resHead); } private static class TestCustomErrorClient extends SimpleHttpClient { public TestCustomErrorClient(int port) { setPort(port); } @Override public boolean isResponseBodyOK() { return true; } } }