This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,331 @@
/*
* 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.core;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.descriptor.JspPropertyGroupDescriptor;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestApplicationContext extends TomcatBaseTest {
@Test
public void testBug53257() throws Exception {
getTomcatInstanceTestWebapp(false, true);
ByteChunk res = getUrl("http://localhost:" + getPort() +
"/test/bug53257/index.jsp");
String result = res.toString();
String[] lines = result.split("\n");
for (String line : lines) {
if (line.startsWith("FAIL")) {
Assert.fail(line);
}
}
}
@Test
public void testBug53467() throws Exception {
getTomcatInstanceTestWebapp(false, true);
ByteChunk res = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() +
"/test/bug5nnnn/bug53467%5D.jsp", res, null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertTrue(res.toString().contains("<p>OK</p>"));
}
@Test(expected = IllegalArgumentException.class)
public void testAddFilterWithFilterNameNull() throws LifecycleException {
getServletContext().addFilter(null, (Filter) null);
}
@Test(expected = IllegalArgumentException.class)
public void testAddFilterWithFilterNameEmptyString() throws LifecycleException {
getServletContext().addFilter("", (Filter) null);
}
@Test(expected = IllegalArgumentException.class)
public void testAddServletWithServletNameNull() throws LifecycleException {
getServletContext().addServlet(null, (Servlet) null);
}
@Test(expected = IllegalArgumentException.class)
public void testAddServletWithServletNameEmptyString() throws LifecycleException {
getServletContext().addServlet("", (Servlet) null);
}
@Test
public void testGetJspConfigDescriptor() throws Exception {
Tomcat tomcat = getTomcatInstanceTestWebapp(false, false);
StandardContext standardContext =
(StandardContext) tomcat.getHost().findChildren()[0];
ServletContext servletContext = standardContext.getServletContext();
Assert.assertNull(servletContext.getJspConfigDescriptor());
tomcat.start();
Assert.assertNotNull(servletContext.getJspConfigDescriptor());
}
@Test
public void testJspPropertyGroupsAreIsolated() throws Exception {
Tomcat tomcat = getTomcatInstanceTestWebapp(false, false);
StandardContext standardContext =
(StandardContext) tomcat.getHost().findChildren()[0];
ServletContext servletContext = standardContext.getServletContext();
Assert.assertNull(servletContext.getJspConfigDescriptor());
tomcat.start();
JspConfigDescriptor jspConfigDescriptor =
servletContext.getJspConfigDescriptor();
Collection<JspPropertyGroupDescriptor> propertyGroups =
jspConfigDescriptor.getJspPropertyGroups();
Assert.assertFalse(propertyGroups.isEmpty());
propertyGroups.clear();
jspConfigDescriptor = servletContext.getJspConfigDescriptor();
propertyGroups = jspConfigDescriptor.getJspPropertyGroups();
Assert.assertFalse(propertyGroups.isEmpty());
}
private ServletContext getServletContext() throws LifecycleException {
Tomcat tomcat = getTomcatInstanceTestWebapp(false, false);
StandardContext standardContext =
(StandardContext) tomcat.getHost().findChildren()[0];
return standardContext.getServletContext();
}
@Test(expected = IllegalStateException.class)
public void testSetInitParameter() throws Exception {
getTomcatInstance().start();
getServletContext().setInitParameter("name", "value");
}
/*
* Cross-context requests with parallel deployment
*/
@Test
public void testBug57190() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context foo1 = new StandardContext();
foo1.setName("/foo##1");
foo1.setPath("/foo");
foo1.setWebappVersion("1");
foo1.addLifecycleListener(new FixContextListener());
foo1.addLifecycleListener(new SetIdListener("foo1"));
tomcat.getHost().addChild(foo1);
Context foo2 = new StandardContext();
foo2.setName("/foo##2");
foo2.setPath("/foo");
foo2.setWebappVersion("2");
foo2.addLifecycleListener(new FixContextListener());
foo2.addLifecycleListener(new SetIdListener("foo2"));
tomcat.getHost().addChild(foo2);
Context bar = tomcat.addContext("/bar", null);
bar.addLifecycleListener(new SetIdListener("bar"));
Context ctx = tomcat.addContext("", null);
ctx.addLifecycleListener(new SetIdListener("ROOT"));
ctx.setCrossContext(true);
Tomcat.addServlet(ctx, "Bug57190Servlet", new Bug57190Servlet());
ctx.addServletMappingDecoded("/", "Bug57190Servlet");
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + "/");
String body = res.toString();
Assert.assertTrue(body, body.contains("01-bar"));
Assert.assertTrue(body, body.contains("02-foo2"));
Assert.assertTrue(body, body.contains("03-foo1"));
Assert.assertTrue(body, body.contains("04-foo2"));
Assert.assertTrue(body, body.contains("05-foo2"));
Assert.assertTrue(body, body.contains("06-ROOT"));
Assert.assertTrue(body, body.contains("07-ROOT"));
Assert.assertTrue(body, body.contains("08-foo2"));
Assert.assertTrue(body, body.contains("09-ROOT"));
}
private static class Bug57190Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
ServletContext sc = req.getServletContext();
pw.println("01-" + sc.getContext("/bar").getInitParameter("id"));
pw.println("02-" + sc.getContext("/foo").getInitParameter("id"));
pw.println("03-" + sc.getContext("/foo##1").getInitParameter("id"));
pw.println("04-" + sc.getContext("/foo##2").getInitParameter("id"));
pw.println("05-" + sc.getContext("/foo##3").getInitParameter("id"));
pw.println("06-" + sc.getContext("/unknown").getInitParameter("id"));
pw.println("07-" + sc.getContext("/").getInitParameter("id"));
pw.println("08-" + sc.getContext("/foo/bar").getInitParameter("id"));
pw.println("09-" + sc.getContext("/football").getInitParameter("id"));
}
}
private static class SetIdListener implements LifecycleListener {
private final String id;
public SetIdListener(String id) {
this.id = id;
}
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (Lifecycle.CONFIGURE_START_EVENT.equals(event.getType())) {
((Context) event.getSource()).getServletContext().setInitParameter("id", id);
}
}
}
/*
* The expectation is that you can set a context attribute on
* ServletContextB from ServletContextA and then access that attribute via
* a cross-context dispatch to ServletContextB.
*/
@Test
public void testCrossContextSetAttribute() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx2 = tomcat.addContext("/second", null);
GetAttributeServlet getAttributeServlet = new GetAttributeServlet();
Tomcat.addServlet(ctx2, "getAttributeServlet", getAttributeServlet);
ctx2.addServletMappingDecoded("/test", "getAttributeServlet");
// No file system docBase required
Context ctx1 = tomcat.addContext("/first", null);
ctx1.setCrossContext(true);
SetAttributeServlet setAttributeServlet = new SetAttributeServlet("/test", "/second");
Tomcat.addServlet(ctx1, "setAttributeServlet", setAttributeServlet);
ctx1.addServletMappingDecoded("/test", "setAttributeServlet");
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + "/first/test", bc, null);
Assert.assertEquals(200, rc);
Assert.assertEquals("01-PASS", bc.toString());
}
private static class SetAttributeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String ATTRIBUTE_NAME = "setAttributeTest";
private static final String ATTRIBUTE_VALUE = "abcde";
private final String targetContextPath;
private final String targetPath;
public SetAttributeServlet(String targetPath, String targetContextPath) {
this.targetPath = targetPath;
this.targetContextPath = targetContextPath;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
ServletContext sc;
if (targetContextPath == null) {
sc = req.getServletContext();
} else {
sc = req.getServletContext().getContext(targetContextPath);
}
sc.setAttribute(ATTRIBUTE_NAME, ATTRIBUTE_VALUE);
sc.getRequestDispatcher(targetPath).forward(req, resp);
}
}
private static class GetAttributeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
String value = (String) req.getServletContext().getAttribute(
SetAttributeServlet.ATTRIBUTE_NAME);
if (SetAttributeServlet.ATTRIBUTE_VALUE.equals(value)) {
pw.print("01-PASS");
} else {
pw.print("01-FAIL");
}
}
}
}

View File

@@ -0,0 +1,524 @@
/*
* 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.core;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.util.URLEncoder;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.UDecoder;
@RunWith(value = Parameterized.class)
public class TestApplicationContextGetRequestDispatcher extends TomcatBaseTest {
private final boolean useAsync;
public TestApplicationContextGetRequestDispatcher(boolean useAsync) {
this.useAsync = useAsync;
}
@Parameters(name = "{index}: useAsync[{0}]")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{Boolean.TRUE},
{Boolean.FALSE}
});
}
@Test
public void testGetRequestDispatcherNullPath01() throws Exception {
doTestGetRequestDispatcher(true, "/start", null, null, "/target", DispatcherServlet.NULL);
}
@Test
public void testGetRequestDispatcherNullPath02() throws Exception {
doTestGetRequestDispatcher(false, "/start", null, null, "/target", DispatcherServlet.NULL);
}
@Test
public void testGetRequestDispatcherOutsideContextRoot01() throws Exception {
doTestGetRequestDispatcher(
true, "/start", null, "../outside", "/target", DispatcherServlet.NULL);
}
@Test
public void testGetRequestDispatcherOutsideContextRoot02() throws Exception {
doTestGetRequestDispatcher(
false, "/start", null, "../outside", "/target", DispatcherServlet.NULL);
}
@Test
public void testGetRequestDispatcherEncodedTraversal() throws Exception {
doTestGetRequestDispatcher(
true, "/prefix/start", null, "%2E%2E/target", "/target", DispatcherServlet.NULL);
}
@Test
public void testGetRequestDispatcherTraversal01() throws Exception {
doTestGetRequestDispatcher(
true, "/prefix/start", null, "../target", "/target", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcherTraversal02() throws Exception {
doTestGetRequestDispatcher(
false, "/prefix/start", null, "../target", "/target", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcherTraversal03() throws Exception {
doTestGetRequestDispatcher(
true, "/prefix/start", null, "../target?a=b", "/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcherTraversal04() throws Exception {
doTestGetRequestDispatcher(
false, "/prefix/start", null, "../target?a=b", "/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcherTraversal05() throws Exception {
doTestGetRequestDispatcher(
true, "/prefix/start", "a=b", "../target", "/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcherTraversal06() throws Exception {
doTestGetRequestDispatcher(
false, "/prefix/start", "a=b", "../target", "/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher01() throws Exception {
doTestGetRequestDispatcher(
true, "/prefix/start", null, "target", "/prefix/target", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcher02() throws Exception {
doTestGetRequestDispatcher(
false, "/prefix/start", null, "target", "/prefix/target", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcher03() throws Exception {
doTestGetRequestDispatcher(true, "/prefix/start", null, "target?a=b", "/prefix/target",
TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher04() throws Exception {
doTestGetRequestDispatcher(false, "/prefix/start", null, "target?a=b", "/prefix/target",
TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher05() throws Exception {
doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "target", "/prefix/target",
TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher06() throws Exception {
doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "target", "/prefix/target",
TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher11() throws Exception {
doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", null, "target",
"/aa%3Fbb%3Dcc/target", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcher12() throws Exception {
// Expected to fail because when the RD processes this as unencoded it
// sees /aa?bb=cc/target which it thinks is a query string. This is why
// Tomcat encodes by default.
doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", null, "target",
"/aa%3Fbb%3Dcc/target", Default404Servlet.DEFAULT_404);
}
@Test
public void testGetRequestDispatcher13() throws Exception {
doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", null, "target?a=b",
"/aa%3Fbb%3Dcc/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher14() throws Exception {
// Expected to fail because when the RD processes this as unencoded it
// sees /aa?bb=cc/target which it thinks is a query string. This is why
// Tomcat encodes by default.
doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", null, "target?a=b",
"/aa%3Fbb%3Dcc/target", Default404Servlet.DEFAULT_404);
}
@Test
public void testGetRequestDispatcher15() throws Exception {
doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", "a=b", "target",
"/aa%3Fbb%3Dcc/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher16() throws Exception {
// Expected to fail because when the RD processes this as unencoded it
// sees /aa?bb=cc/target which it thinks is a query string. This is why
// Tomcat encodes by default.
doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", "a=b", "target",
"/aa%3Fbb%3Dcc/target", Default404Servlet.DEFAULT_404);
}
@Test
public void testGetRequestDispatcher21() throws Exception {
doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", null, "target",
"/aa%3Dbb%3Dcc/target", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcher22() throws Exception {
doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", null, "target",
"/aa%3Dbb%3Dcc/target", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcher23() throws Exception {
doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", null, "target?a=b",
"/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher24() throws Exception {
doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", null, "target?a=b",
"/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher25() throws Exception {
doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", "a=b", "target",
"/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher26() throws Exception {
doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", "a=b", "target",
"/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher31() throws Exception {
doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc",
"/prefix/aa%3Fbb%3Dcc", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcher32() throws Exception {
doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc",
"/prefix/aa%3Fbb%3Dcc", Default404Servlet.DEFAULT_404);
}
@Test
public void testGetRequestDispatcher33() throws Exception {
doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b",
"/prefix/aa%3Fbb%3Dcc", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher34() throws Exception {
doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b",
"/prefix/aa%3Fbb%3Dcc", Default404Servlet.DEFAULT_404);
}
@Test
public void testGetRequestDispatcher35() throws Exception {
doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "aa%3Fbb%3Dcc",
"/prefix/aa%3Fbb%3Dcc", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher36() throws Exception {
doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "aa%3Fbb%3Dcc",
"/prefix/aa%3Fbb%3Dcc", Default404Servlet.DEFAULT_404);
}
@Test
public void testGetRequestDispatcher41() throws Exception {
doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc",
"/prefix/aa%253Fbb%253Dcc", Default404Servlet.DEFAULT_404);
}
@Test
public void testGetRequestDispatcher42() throws Exception {
doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc",
"/prefix/aa%253Fbb%253Dcc", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcher43() throws Exception {
doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b",
"/prefix/aa%253Fbb%253Dcc", Default404Servlet.DEFAULT_404);
}
@Test
public void testGetRequestDispatcher44() throws Exception {
doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b",
"/prefix/aa%253Fbb%253Dcc", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher45() throws Exception {
doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "aa%3Fbb%3Dcc",
"/prefix/aa%253Fbb%253Dcc", Default404Servlet.DEFAULT_404);
}
@Test
public void testGetRequestDispatcher46() throws Exception {
doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "aa%3Fbb%3Dcc",
"/prefix/aa%253Fbb%253Dcc", TargetServlet.OK + "a=b");
}
@Test
public void testGetRequestDispatcher47() throws Exception {
doTestGetRequestDispatcher(true, "/prefix/start", null, "aa+bb",
"/prefix/aa+bb", TargetServlet.OK);
}
@Test
public void testGetRequestDispatcher48() throws Exception {
doTestGetRequestDispatcher(false, "/prefix/start", null, "aa+bb",
"/prefix/aa+bb", TargetServlet.OK);
}
private void doTestGetRequestDispatcher(boolean useEncodedDispatchPaths, String startPath,
String startQueryString, String dispatchPath, String targetPath, String expectedBody)
throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("/test\u6771\u4eac", null);
ctx.setDispatchersUseEncodedPaths(useEncodedDispatchPaths);
// Add a default servlet to return 404 for not found resources
Tomcat.addServlet(ctx, "Default", new Default404Servlet());
ctx.addServletMappingDecoded("/*", "Default");
// Add a target servlet to dispatch to
Tomcat.addServlet(ctx, "target", new TargetServlet());
ctx.addServletMappingDecoded(
UDecoder.URLDecode(targetPath, StandardCharsets.UTF_8), "target");
if (useAsync) {
Wrapper w = Tomcat.addServlet(
ctx, "rd", new AsyncDispatcherServlet(dispatchPath, useEncodedDispatchPaths));
w.setAsyncSupported(true);
} else {
Tomcat.addServlet(ctx, "rd", new DispatcherServlet(dispatchPath));
}
ctx.addServletMappingDecoded(UDecoder.URLDecode(startPath, StandardCharsets.UTF_8), "rd");
tomcat.start();
StringBuilder url = new StringBuilder("http://localhost:");
url.append(getPort());
url.append("/test%E6%9D%B1%E4%BA%AC");
url.append(startPath);
if (startQueryString != null) {
url.append('?');
url.append(startQueryString);
}
ByteChunk bc = getUrl(url.toString());
String body = bc.toString();
Assert.assertEquals(expectedBody, body);
}
private static class Default404Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String DEFAULT_404 = "DEFAULT-404";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().print(DEFAULT_404);
resp.setStatus(404);
}
}
private static class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String NULL = "RD-NULL";
private final String dispatchPath;
public DispatcherServlet(String dispatchPath) {
this.dispatchPath = dispatchPath;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher rd = req.getRequestDispatcher(dispatchPath);
if (rd == null) {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().print(NULL);
} else {
rd.forward(req, resp);
}
}
}
private static class TargetServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String OK = "OK";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
String contextPath = req.getContextPath();
if ("/test%E6%9D%B1%E4%BA%AC".equals(contextPath)) {
resp.getWriter().print(OK);
} else {
resp.getWriter().print("FAIL - ContextPath");
}
String qs = req.getQueryString();
if (qs != null) {
resp.getWriter().print(qs);
}
}
}
private static class AsyncDispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String NULL = "RD-NULL";
private final String dispatchPath;
private final boolean encodePath;
public AsyncDispatcherServlet(String dispatchPath, boolean encodePath) {
this.dispatchPath = dispatchPath;
this.encodePath = encodePath;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
AsyncContext ac = req.startAsync();
// Quick and dirty. Sufficient for this test but ignores lots of
// edge cases.
String target = null;
if (dispatchPath != null) {
target = req.getServletPath();
int lastSlash = target.lastIndexOf('/');
target = target.substring(0, lastSlash + 1);
if (encodePath) {
target = URLEncoder.DEFAULT.encode(target, StandardCharsets.UTF_8);
}
target += dispatchPath;
}
try {
ac.dispatch(target);
} catch (UnsupportedOperationException uoe) {
ac.complete();
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().print(NULL);
}
}
}
}

View File

@@ -0,0 +1,586 @@
/*
* 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.core;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.servlet4preview.http.HttpServletMapping;
import org.apache.catalina.servlet4preview.http.MappingMatch;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
@RunWith(value = Parameterized.class)
public class TestApplicationContextGetRequestDispatcherB extends TomcatBaseTest {
@Parameters(name = "{index}: startMapping[{0}], startUri[{1}], dispatcherType[{2}], " +
"targetMapping[{3}], targetUri[{4}], useEncodedDispatchPaths[{5}], " +
"expectedRequestURI[{6}], expectedContextPath[{7}], expectedServletPath[{8}], " +
"expectedPathInfo[{9}], expectedQueryString[{10}], expectedMappingMatch[{11}, " +
"expectedMappingPattern[{12}], expectedMappingMatchValue[{13}], " +
"expectedMappingServletName[{14}], " +
"expectedDispatcherRequestURI[{15}], expectedDispatcherContextPath[{16}], " +
"expectedDispatcherServletPath[{17}], expectedDispatcherPathInfo[{18}], " +
"expectedDispatcherQueryString[{19}], expectedDispatcherMappingMatch[{20}]," +
"expectedDispatcherMappingPattern[{21}], expectedDispatcherMappingMatchValue[{22}]," +
"expectedDispatcherMappingServletName[{23}]," +
"expectedBody")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
// Simple dispatch for each type
{ "/start", "/start", DispatcherType.INCLUDE, "/target", "/target", Boolean.TRUE,
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"OK"},
{ "/start", "/start", DispatcherType.FORWARD, "/target", "/target", Boolean.TRUE,
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", "/start", DispatcherType.ASYNC, "/target", "/target", Boolean.TRUE,
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
// Simple dispatch with query strings
{ "/start", "/start?abcde=fghij", DispatcherType.INCLUDE, "/target", "/target?zyxwv=utsrq", Boolean.TRUE,
"/test/start", "/test", "/start", null, "abcde=fghij",
MappingMatch.EXACT, "/start", "start", "rd",
"/test/target", "/test", "/target", null, "zyxwv=utsrq",
MappingMatch.EXACT, "/target", "target", "target",
"OK"},
{ "/start", "/start?abcde=fghij", DispatcherType.FORWARD, "/target", "/target?zyxwv=utsrq", Boolean.TRUE,
"/test/target", "/test", "/target", null, "zyxwv=utsrq",
MappingMatch.EXACT, "/target", "target", "target",
"/test/start", "/test", "/start", null, "abcde=fghij",
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", "/start?abcde=fghij", DispatcherType.ASYNC, "/target", "/target?zyxwv=utsrq", Boolean.TRUE,
"/test/target", "/test", "/target", null, "zyxwv=utsrq",
MappingMatch.EXACT, "/target", "target", "target",
"/test/start", "/test", "/start", null, "abcde=fghij",
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
// Simple dispatch with trailing path parameters at start
{ "/start", "/start;abcde=fghij", DispatcherType.INCLUDE, "/target", "/target", Boolean.TRUE,
"/test/start;abcde=fghij", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"OK"},
{ "/start", "/start;abcde=fghij", DispatcherType.FORWARD, "/target", "/target", Boolean.TRUE,
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start;abcde=fghij", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", "/start;abcde=fghij", DispatcherType.ASYNC, "/target", "/target", Boolean.TRUE,
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start;abcde=fghij", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
// Simple dispatch with path parameters at start
{ "/start", ";abcde=fghij/start", DispatcherType.INCLUDE, "/target", "/target", Boolean.TRUE,
"/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"OK"},
{ "/start", ";abcde=fghij/start", DispatcherType.FORWARD, "/target", "/target", Boolean.TRUE,
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", ";abcde=fghij/start", DispatcherType.ASYNC, "/target", "/target", Boolean.TRUE,
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
// Simple dispatch with path parameters on dispatch
{ "/start", "/start", DispatcherType.INCLUDE, "/target", "/target;abcde=fghij", Boolean.TRUE,
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/target;abcde=fghij", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"OK"},
{ "/start", "/start", DispatcherType.FORWARD, "/target", "/target;abcde=fghij", Boolean.TRUE,
"/test/target;abcde=fghij", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", "/start", DispatcherType.ASYNC, "/target", "/target;abcde=fghij", Boolean.TRUE,
"/test/target;abcde=fghij", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
// Simple dispatch with multiple path parameters on start and dispatch
{ "/start", "/start;abcde=fghij", DispatcherType.INCLUDE, "/target", ";klmno=pqrst/target;uvwxy=z0123", Boolean.TRUE,
"/test/start;abcde=fghij", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/;klmno=pqrst/target;uvwxy=z0123", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"OK"},
{ "/start", "/start;abcde=fghij", DispatcherType.FORWARD, "/target", ";klmno=pqrst/target;uvwxy=z0123", Boolean.TRUE,
"/test/;klmno=pqrst/target;uvwxy=z0123", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start;abcde=fghij", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", "/start;abcde=fghij", DispatcherType.ASYNC, "/target", ";klmno=pqrst/target;uvwxy=z0123", Boolean.TRUE,
"/test/;klmno=pqrst/target;uvwxy=z0123", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start;abcde=fghij", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"ASYNC-IAE"},
// Simple dispatch with directory traversal
{ "/start/*", "/start/foo", DispatcherType.INCLUDE, "/target", "../target", Boolean.TRUE,
"/test/start/foo", "/test", "/start", "/foo", null,
MappingMatch.PATH, "/start/*", "foo", "rd",
"/test/start/../target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"OK"},
{ "/start/*", "/start/foo", DispatcherType.FORWARD, "/target", "../target", Boolean.TRUE,
"/test/start/../target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start/foo", "/test", "/start", "/foo", null,
MappingMatch.PATH, "/start/*", "foo", "rd",
"OK"},
{ "/start/*", "/start/foo", DispatcherType.ASYNC, "/target", "../target", Boolean.TRUE,
"/test/start/../target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start/foo", "/test", "/start", "/foo", null,
MappingMatch.PATH, "/start/*", "foo", "rd",
"ASYNC-IAE"},
// Simple dispatch with directory traversal and path parameters
// Note comments in Request.getRequestDispatcher(String) that
// explain why the path parameter abcde=fghij is not present on the
// dispatched requestURI
{ "/start/*", "/start;abcde=fghij/foo", DispatcherType.INCLUDE, "/target", "../target;klmno=pqrst", Boolean.TRUE,
"/test/start;abcde=fghij/foo", "/test", "/start", "/foo", null,
MappingMatch.PATH, "/start/*", "foo", "rd",
"/test/start/../target;klmno=pqrst", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"OK"},
{ "/start/*", "/start;abcde=fghij/foo", DispatcherType.FORWARD, "/target", "../target;klmno=pqrst", Boolean.TRUE,
"/test/start/../target;klmno=pqrst", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start;abcde=fghij/foo", "/test", "/start", "/foo", null,
MappingMatch.PATH, "/start/*", "foo", "rd",
"OK"},
{ "/start/*", "/start;abcde=fghij/foo", DispatcherType.ASYNC, "/target", "../target;klmno=pqrst", Boolean.TRUE,
"/test/start;abcde=fghij/../target;klmno=pqrst", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start;abcde=fghij/foo", "/test", "/start", "/foo", null,
MappingMatch.PATH, "/start/*", "foo", "rd",
"ASYNC-IAE"},
// Simple dispatch with invalid directory traversal
{ "/start/*", "/start/foo", DispatcherType.INCLUDE, "/target", "../../target", Boolean.TRUE,
"/test/start/foo", "/test", "/start", "/foo", null,
MappingMatch.PATH, "/start/*", "foo", "rd",
"/test/start/../target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"RD-NULL"},
{ "/start/*", "/start/foo", DispatcherType.FORWARD, "/target", "../../target", Boolean.TRUE,
"/test/start/../target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start/foo", "/test", "/start", "/foo", null,
MappingMatch.PATH, "/start/*", "foo", "rd",
"RD-NULL"},
{ "/start/*", "/start/foo", DispatcherType.ASYNC, "/target", "../../target", Boolean.TRUE,
"/test/start/../target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start/foo", "/test", "/start", "/foo", null,
MappingMatch.PATH, "/start/*", "foo", "rd",
"ASYNC-IAE"},
// Simple dispatch with invalid target
{ "/start", "/start", DispatcherType.INCLUDE, "/target", "/does-not-exist", Boolean.TRUE,
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"RD-NULL"},
{ "/start", "/start", DispatcherType.FORWARD, "/target", "/does-not-exist", Boolean.TRUE,
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"RD-NULL"},
{ "/start", "/start", DispatcherType.ASYNC, "/target", "/does-not-exist", Boolean.TRUE,
"/test/target", "/test", "/target", null, null,
MappingMatch.EXACT, "/target", "target", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"ASYNC-RD-NULL"},
// Welcome files
{ "/start", "/start", DispatcherType.INCLUDE, "*.html", "/", Boolean.TRUE,
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"OK"},
{ "/start", "/start", DispatcherType.FORWARD, "*.html", "/", Boolean.TRUE,
"/test/", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", "/start", DispatcherType.ASYNC, "*.html", "/", Boolean.TRUE,
"/test/", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
// Welcome files with query strings
{ "/start", "/start?abcde=fghij", DispatcherType.INCLUDE, "*.html", "/?zyxwv=utsrq", Boolean.TRUE,
"/test/start", "/test", "/start", null, "abcde=fghij",
MappingMatch.EXACT, "/start", "start", "rd",
"/test/", "/test", "/index.html", null, "zyxwv=utsrq",
MappingMatch.EXTENSION, "*.html", "index", "target",
"OK"},
{ "/start", "/start?abcde=fghij", DispatcherType.FORWARD, "*.html", "/?zyxwv=utsrq", Boolean.TRUE,
"/test/", "/test", "/index.html", null, "zyxwv=utsrq",
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test/start", "/test", "/start", null, "abcde=fghij",
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", "/start?abcde=fghij", DispatcherType.ASYNC, "*.html", "/?zyxwv=utsrq", Boolean.TRUE,
"/test/", "/test", "/index.html", null, "zyxwv=utsrq",
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test/start", "/test", "/start", null, "abcde=fghij",
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
// Welcome files with trailing path parameters at start
{ "/start", "/start;abcde=fghij", DispatcherType.INCLUDE, "*.html", "/", Boolean.TRUE,
"/test/start;abcde=fghij", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"OK"},
{ "/start", "/start;abcde=fghij", DispatcherType.FORWARD, "*.html", "/", Boolean.TRUE,
"/test/", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test/start;abcde=fghij", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", "/start;abcde=fghij", DispatcherType.ASYNC, "*.html", "/", Boolean.TRUE,
"/test/", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test/start;abcde=fghij", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
// Welcome files with path parameters at start
{ "/start", ";abcde=fghij/start", DispatcherType.INCLUDE, "*.html", "/", Boolean.TRUE,
"/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"OK"},
{ "/start", ";abcde=fghij/start", DispatcherType.FORWARD, "*.html", "/", Boolean.TRUE,
"/test/", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", ";abcde=fghij/start", DispatcherType.ASYNC, "*.html", "/", Boolean.TRUE,
"/test/", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
// Welcome files with trailing path parameters on dispatch
{ "/start", "/start", DispatcherType.INCLUDE, "*.html", "/;abcde=fghij", Boolean.TRUE,
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"/test/;abcde=fghij", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"OK"},
{ "/start", "/start", DispatcherType.FORWARD, "*.html", "/;abcde=fghij", Boolean.TRUE,
"/test/;abcde=fghij", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
{ "/start", "/start", DispatcherType.ASYNC, "*.html", "/;abcde=fghij", Boolean.TRUE,
"/test/;abcde=fghij", "/test", "/index.html", null, null,
MappingMatch.EXTENSION, "*.html", "index", "target",
"/test/start", "/test", "/start", null, null,
MappingMatch.EXACT, "/start", "start", "rd",
"OK"},
});
}
// Inputs
private final String startMapping;
private final String startUri;
private final DispatcherType dispatcherType;
private final String targetMapping;
private final String targetUri;
private final boolean useEncodedDispatchPaths;
// Outputs
private final String expectedRequestURI;
private final String expectedContextPath;
private final String expectedServletPath;
private final String expectedPathInfo;
private final String expectedQueryString;
private final MappingMatch expectedMappingMatch;
private final String expectedMappingPattern;
private final String expectedMappingMatchValue;
private final String expectedMappingServletName;
private final String expectedDispatcherRequestURI;
private final String expectedDispatcherContextPath;
private final String expectedDispatcherServletPath;
private final String expectedDispatcherPathInfo;
private final String expectedDispatcherQueryString;
private final MappingMatch expectedDispatcherMappingMatch;
private final String expectedDispatcherMappingPattern;
private final String expectedDispatcherMappingMatchValue;
private final String expectedDispatcherMappingServletName;
private final String expectedBody;
public TestApplicationContextGetRequestDispatcherB(String startMapping, String startUri,
DispatcherType dispatcherType, String targetMapping, String targetUri,
boolean useEncodedDispatchPaths,
String expectedRequestURI, String expectedContextPath, String expectedServletPath,
String expectedPathInfo, String expectedQueryString, MappingMatch expectedMappingMatch,
String expectedMappingPattern, String expectedMappingMatchValue,
String expectedMappingServletName,
String expectedDispatcherRequestURI, String expectedDispatcherContextPath,
String expectedDispatcherServletPath, String expectedDispatcherPathInfo,
String expectedDispatcherQueryString, MappingMatch expectedDispatcherMappingMatch,
String expectedDispatcherMappingPattern, String expectedDispatcherMappingMatchValue,
String expectedDispatcherMappingServletName,
String expectedBody) {
this.startMapping = startMapping;
this.startUri = startUri;
this.dispatcherType = dispatcherType;
this.targetMapping = targetMapping;
this.targetUri = targetUri;
this.useEncodedDispatchPaths = useEncodedDispatchPaths;
this.expectedRequestURI = expectedRequestURI;
this.expectedContextPath = expectedContextPath;
this.expectedServletPath = expectedServletPath;
this.expectedPathInfo = expectedPathInfo;
this.expectedQueryString = expectedQueryString;
this.expectedMappingMatch = expectedMappingMatch;
this.expectedMappingPattern = expectedMappingPattern;
this.expectedMappingMatchValue = expectedMappingMatchValue;
this.expectedMappingServletName = expectedMappingServletName;
this.expectedDispatcherRequestURI = expectedDispatcherRequestURI;
this.expectedDispatcherContextPath = expectedDispatcherContextPath;
this.expectedDispatcherServletPath = expectedDispatcherServletPath;
this.expectedDispatcherPathInfo = expectedDispatcherPathInfo;
this.expectedDispatcherQueryString = expectedDispatcherQueryString;
this.expectedDispatcherMappingMatch = expectedDispatcherMappingMatch;
this.expectedDispatcherMappingPattern = expectedDispatcherMappingPattern;
this.expectedDispatcherMappingMatchValue = expectedDispatcherMappingMatchValue;
this.expectedDispatcherMappingServletName = expectedDispatcherMappingServletName;
this.expectedBody = expectedBody;
}
@Test
public void doTest() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("/test", null);
ctx.setDispatchersUseEncodedPaths(useEncodedDispatchPaths);
ctx.addWelcomeFile("index.html");
// Add a target servlet to dispatch to
Tomcat.addServlet(ctx, "target", new Target());
ctx.addServletMappingDecoded(targetMapping, "target");
Wrapper w = Tomcat.addServlet(ctx, "rd", new Dispatch());
w.setAsyncSupported(true);
ctx.addServletMappingDecoded(startMapping, "rd");
tomcat.start();
StringBuilder url = new StringBuilder("http://localhost:");
url.append(getPort());
url.append("/test");
url.append(startUri);
ByteChunk bc = getUrl(url.toString());
String body = bc.toString();
Assert.assertEquals(expectedBody, body);
}
private class Dispatch extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (dispatcherType == DispatcherType.INCLUDE) {
RequestDispatcher rd = req.getRequestDispatcher(targetUri);
if (rd == null) {
writeResponse(resp, "RD-NULL");
} else {
rd.include(req, resp);
}
} else if (dispatcherType == DispatcherType.FORWARD) {
RequestDispatcher rd = req.getRequestDispatcher(targetUri);
if (rd == null) {
writeResponse(resp, "RD-NULL");
} else {
rd.forward(req, resp);
}
} else if (dispatcherType == DispatcherType.ASYNC) {
AsyncContext ac = req.startAsync();
try {
ac.dispatch(targetUri);
} catch (IllegalArgumentException iae) {
// targetUri is invalid?
if (!targetUri.startsWith("/")) {
// That'll do it.
ac.complete();
writeResponse(resp, "ASYNC-IAE");
} else {
// Not expected. Rethrow.
throw iae;
}
} catch (UnsupportedOperationException uoe) {
// While a custom context implementation could cause this,
// if this occurs during this unit test the cause will be an
// invalid (unmapped) target path which returned a null
// dispatcher
ac.complete();
writeResponse(resp, "ASYNC-RD-NULL");
}
} else {
// Unexpected dispatch type for this test
throw new ServletException("Unknown dispatch type: " + dispatcherType);
}
}
private void writeResponse(HttpServletResponse resp, String message) throws IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter pw = resp.getWriter();
pw.print(message);
}
}
private class Target extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Assert.assertEquals(expectedRequestURI, req.getRequestURI());
Assert.assertEquals(expectedContextPath, req.getContextPath());
Assert.assertEquals(expectedServletPath, req.getServletPath());
Assert.assertEquals(expectedPathInfo, req.getPathInfo());
Assert.assertEquals(expectedQueryString, req.getQueryString());
HttpServletMapping mapping =
((org.apache.catalina.servlet4preview.http.HttpServletRequest) req).getHttpServletMapping();
Assert.assertEquals(expectedMappingMatch, mapping.getMappingMatch());
Assert.assertEquals(expectedMappingPattern, mapping.getPattern());
Assert.assertEquals(expectedMappingMatchValue, mapping.getMatchValue());
Assert.assertEquals(expectedMappingServletName, mapping.getServletName());
for (DispatcherType type : DispatcherType.values()) {
if (type == dispatcherType) {
String name = dispatcherType.name().toLowerCase(Locale.ENGLISH);
Assert.assertEquals(expectedDispatcherRequestURI,
req.getAttribute("javax.servlet." + name + ".request_uri"));
Assert.assertEquals(expectedDispatcherContextPath,
req.getAttribute("javax.servlet." + name + ".context_path"));
Assert.assertEquals(expectedDispatcherServletPath,
req.getAttribute("javax.servlet." + name + ".servlet_path"));
Assert.assertEquals(expectedDispatcherPathInfo,
req.getAttribute("javax.servlet." + name + ".path_info"));
Assert.assertEquals(expectedDispatcherQueryString,
req.getAttribute("javax.servlet." + name + ".query_string"));
HttpServletMapping dispatcherMapping =
(HttpServletMapping) ((org.apache.catalina.servlet4preview.http.HttpServletRequest) req).getAttribute(
"javax.servlet." + name + ".mapping");
Assert.assertNotNull(dispatcherMapping);
Assert.assertEquals(expectedDispatcherMappingMatch,
dispatcherMapping.getMappingMatch());
Assert.assertEquals(expectedDispatcherMappingPattern,
dispatcherMapping.getPattern());
Assert.assertEquals(expectedDispatcherMappingMatchValue,
dispatcherMapping.getMatchValue());
Assert.assertEquals(expectedDispatcherMappingServletName,
dispatcherMapping.getServletName());
} else if (type == DispatcherType.ERROR || type == DispatcherType.REQUEST) {
// Skip - not tested
} else {
assertAllNull(req, type.name().toLowerCase(Locale.ENGLISH));
}
}
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter pw = resp.getWriter();
pw.print("OK");
}
}
private void assertAllNull(HttpServletRequest req, String type) {
Assert.assertNull(req.getAttribute("javax.servlet." + type + ".request_uri"));
Assert.assertNull(req.getAttribute("javax.servlet." + type + ".context_path"));
Assert.assertNull(req.getAttribute("javax.servlet." + type + ".servlet_path"));
Assert.assertNull(req.getAttribute("javax.servlet." + type + ".path_info"));
Assert.assertNull(req.getAttribute("javax.servlet." + type + ".query_string"));
Assert.assertNull(req.getAttribute("javax.servlet." + type + ".mapping"));
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.core;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.apache.catalina.startup.TomcatBaseTest;
@RunWith(value = Parameterized.class)
public class TestApplicationContextStripPathParams extends TomcatBaseTest {
private final String input;
private final String expectedOutput;
public TestApplicationContextStripPathParams(String input, String expectedOutput) {
this.input = input;
this.expectedOutput = expectedOutput;
}
@Parameters(name = "{index}: input[{0}]")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{ "/foo", "/foo"},
{ "/foo/", "/foo/"},
{ "/foo/bar", "/foo/bar"},
{ "/foo;", "/foo"},
{ "/foo;/", "/foo/"},
{ "/foo;/bar", "/foo/bar"},
{ "/foo;a=1", "/foo"},
{ "/foo;a=1/", "/foo/"},
{ "/foo;a=1/bar", "/foo/bar"},
// Arguably not valid but does the right thing anyway
{ ";/foo", "/foo"},
{ ";a=1/foo", "/foo"},
{ ";/foo/bar", "/foo/bar"},
{ ";/foo;a=1/bar", "/foo/bar"},
});
}
@Test
public void testStringPathParams() {
String output = ApplicationContext.stripPathParams(input);
Assert.assertEquals(expectedOutput, output);
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.core;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.filters.AddDefaultCharsetFilter;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.modeler.Registry;
public class TestApplicationFilterConfig extends TomcatBaseTest {
@Test
public void testBug54170() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "HelloWorld", new HelloWorldServlet());
ctx.addServletMappingDecoded("/", "HelloWorld");
// Add a filter with a name that should be escaped if used in a JMX
// object name
FilterDef filterDef = new FilterDef();
filterDef.setFilterClass(AddDefaultCharsetFilter.class.getName());
filterDef.setFilterName("bug54170*");
ctx.addFilterDef(filterDef);
tomcat.start();
final MBeanServer mbeanServer =
Registry.getRegistry(null, null).getMBeanServer();
// There should be one Servlet MBean registered
Set<ObjectName> servlets = mbeanServer.queryNames(
new ObjectName("Tomcat:j2eeType=Servlet,*"), null);
Assert.assertEquals(1, servlets.size());
// There should be one Filter MBean registered
Set<ObjectName> filters = mbeanServer.queryNames(
new ObjectName("Tomcat:j2eeType=Filter,*"), null);
Assert.assertEquals(1, filters.size());
}
}

View File

@@ -0,0 +1,357 @@
/*
* 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.core;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestApplicationHttpRequest extends TomcatBaseTest {
/*
* https://bz.apache.org/bugzilla/show_bug.cgi?id=58836
*/
@Test
public void testForwardQueryString01() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
doQueryStringTest(null, "a=b", expected);
}
@Test
public void testForwardQueryString02() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "c" });
doQueryStringTest(null, "a=b&a=c", expected);
}
@Test
public void testForwardQueryString03() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
expected.put("c", new String[] { "d" });
doQueryStringTest(null, "a=b&c=d", expected);
}
@Test
public void testForwardQueryString04() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "d" });
doQueryStringTest(null, "a=b&c=d&a=e", expected);
}
@Test
public void testForwardQueryString05() throws Exception {
// Parameters with no value are assigned a value of the empty string
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "" });
doQueryStringTest(null, "a=b&c&a=e", expected);
}
@Test
public void testOriginalQueryString01() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
doQueryStringTest("a=b", null, expected);
}
@Test
public void testOriginalQueryString02() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "c" });
doQueryStringTest("a=b&a=c", null, expected);
}
@Test
public void testOriginalQueryString03() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
expected.put("c", new String[] { "d" });
doQueryStringTest("a=b&c=d", null, expected);
}
@Test
public void testOriginalQueryString04() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "d" });
doQueryStringTest("a=b&c=d&a=e", null, expected);
}
@Test
public void testOriginalQueryString05() throws Exception {
// Parameters with no value are assigned a value of the empty string
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "" });
doQueryStringTest("a=b&c&a=e", null, expected);
}
@Test
public void testMergeQueryString01() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "z", "b" });
doQueryStringTest("a=b", "a=z", expected);
}
@Test
public void testMergeQueryString02() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "z", "b", "e" });
expected.put("c", new String[] { "" });
doQueryStringTest("a=b&c&a=e", "a=z", expected);
}
@Test
public void testMergeQueryString03() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "z", "" });
doQueryStringTest("a=b&c&a=e", "c=z", expected);
}
@Test
public void testMergeQueryString04() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "", "b", "e" });
expected.put("c", new String[] { "" });
doQueryStringTest("a=b&c&a=e", "a", expected);
}
@Test
public void testMergeQueryString05() throws Exception {
// https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%81%D1%82
// "Test" = "Test"
String test = "\u0422\u0435\u0441\u0442";
String query = test + "=%D0%A2%D0%B5%D1%81%D1%82";
Map<String, String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
expected.put(test, new String[] { test });
doQueryStringTest("a=b", query, expected);
}
private void doQueryStringTest(String originalQueryString, String forwardQueryString,
Map<String,String[]> expected) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
if (forwardQueryString == null) {
Tomcat.addServlet(ctx, "forward", new ForwardServlet("/display"));
} else {
Tomcat.addServlet(ctx, "forward", new ForwardServlet("/display?" + forwardQueryString));
}
ctx.addServletMappingDecoded("/forward", "forward");
Tomcat.addServlet(ctx, "display", new DisplayParameterServlet(expected));
ctx.addServletMappingDecoded("/display", "display");
tomcat.start();
ByteChunk response = new ByteChunk();
StringBuilder target = new StringBuilder("http://localhost:");
target.append(getPort());
target.append("/forward");
if (originalQueryString != null) {
target.append('?');
target.append(originalQueryString);
}
int rc = getUrl(target.toString(), response, null);
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", response.toString());
}
@Test
public void testParameterImmutability() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "forward", new ForwardServlet("/modify"));
ctx.addServletMappingDecoded("/forward", "forward");
Tomcat.addServlet(ctx, "modify", new ModifyParameterServlet());
ctx.addServletMappingDecoded("/modify", "modify");
tomcat.start();
ByteChunk response = new ByteChunk();
StringBuilder target = new StringBuilder("http://localhost:");
target.append(getPort());
target.append("/forward");
int rc = getUrl(target.toString(), response, null);
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", response.toString());
}
private static class ForwardServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final String target;
public ForwardServlet(String target) {
this.target = target;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
req.getRequestDispatcher(target).forward(req, resp);
}
}
private static class DisplayParameterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Map<String,String[]> expected;
public DisplayParameterServlet(Map<String,String[]> expected) {
this.expected = expected;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter w = resp.getWriter();
Map<String,String[]> actual = req.getParameterMap();
boolean ok = true;
for (Entry<String,String[]> entry : actual.entrySet()) {
String[] expectedValue = expected.get(entry.getKey());
if (expectedValue == null ||
expectedValue.length != entry.getValue().length) {
ok = false;
break;
}
for (int i = 0; i < expectedValue.length; i++) {
if (!expectedValue[i].equals(entry.getValue()[i])) {
ok = false;
break;
}
}
if (!ok) {
break;
}
}
if (ok) {
w.print("OK");
return;
}
boolean firstParam = true;
for (Entry<String,String[]> param : actual.entrySet()) {
if (firstParam) {
firstParam = false;
} else {
w.print(';');
}
w.print(param.getKey());
w.print(':');
boolean firstValue = true;
for (String value : param.getValue()) {
if (firstValue) {
firstValue = false;
} else {
w.print(',');
}
w.print('(');
w.print(value);
w.print(')');
}
}
}
}
private static class ModifyParameterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// Suppress warnings generated because the code is trying to put the
// wrong type of values into the Map
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Map map = req.getParameterMap();
boolean insertWorks;
try {
map.put("test", new Integer[] { Integer.valueOf(0) });
insertWorks = true;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
insertWorks = false;
}
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter pw = resp.getWriter();
if (insertWorks) {
pw.print("FAIL");
} else {
pw.print("OK");
}
}
}
}

View File

@@ -0,0 +1,376 @@
/*
* 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.core;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.servlet4preview.http.HttpServletMapping;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestApplicationMapping extends TomcatBaseTest {
@Test
public void testContextNonRootMappingContextRoot() throws Exception {
doTestMapping("/dummy", "", "", "", "CONTEXT_ROOT");
}
@Test
public void testContextNonRootMappingDefault() throws Exception {
doTestMapping("/dummy", "/", "/foo", "", "DEFAULT");
}
@Test
public void testContextNonRootMappingExtension() throws Exception {
doTestMapping("/dummy", "*.test", "/foo/bar.test", "foo/bar", "EXTENSION");
}
@Test
public void testContextNonRootMappingExact() throws Exception {
doTestMapping("/dummy", "/foo/bar", "/foo/bar", "foo/bar", "EXACT");
}
@Test
public void testContextNonRootMappingPathNone() throws Exception {
doTestMapping("/dummy", "/foo/bar/*", "/foo/bar", null, "PATH");
}
@Test
public void testContextNonRootMappingPathSeparatorOnly() throws Exception {
doTestMapping("/dummy", "/foo/bar/*", "/foo/bar/", "", "PATH");
}
@Test
public void testContextNonRootMappingPath() throws Exception {
doTestMapping("/dummy", "/foo/bar/*", "/foo/bar/foo2", "foo2", "PATH");
}
@Test
public void testContextRootMappingContextRoot() throws Exception {
doTestMapping("", "", "", "", "CONTEXT_ROOT");
}
@Test
public void testContextRootMappingDefault() throws Exception {
doTestMapping("", "/", "/foo", "", "DEFAULT");
}
@Test
public void testContextRootMappingExtension() throws Exception {
doTestMapping("", "*.test", "/foo/bar.test", "foo/bar", "EXTENSION");
}
@Test
public void testContextRootMappingExact() throws Exception {
doTestMapping("", "/foo/bar", "/foo/bar", "foo/bar", "EXACT");
}
@Test
public void testContextRootMappingPath() throws Exception {
doTestMapping("", "/foo/bar/*", "/foo/bar/foo2", "foo2", "PATH");
}
private void doTestMapping(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
doTestMappingDirect(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingInclude(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingNamedInclude(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingForward(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingNamedForward(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingAsync(contextPath, mapping, requestPath, matchValue, matchType);
}
private void doTestMappingDirect(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded(mapping, "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
}
private void doTestMappingInclude(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Include", new IncludeServlet());
ctx.addServletMappingDecoded(mapping, "Include");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ServletName=[Include]"));
Assert.assertTrue(body, body.contains("IncludeMatchValue=[mapping]"));
Assert.assertTrue(body, body.contains("IncludePattern=[/mapping]"));
Assert.assertTrue(body, body.contains("IncludeMatchType=[EXACT]"));
Assert.assertTrue(body, body.contains("IncludeServletName=[Mapping]"));
}
private void doTestMappingNamedInclude(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Include", new NamedIncludeServlet());
ctx.addServletMappingDecoded(mapping, "Include");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ServletName=[Include]"));
}
private void doTestMappingForward(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Forward", new ForwardServlet());
ctx.addServletMappingDecoded(mapping, "Forward");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[mapping]"));
Assert.assertTrue(body, body.contains("Pattern=[/mapping]"));
Assert.assertTrue(body, body.contains("MatchType=[EXACT]"));
Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
Assert.assertTrue(body, body.contains("ForwardMatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("ForwardPattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("ForwardMatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ForwardServletName=[Forward]"));
}
private void doTestMappingNamedForward(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Forward", new NamedForwardServlet());
ctx.addServletMappingDecoded(mapping, "Forward");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ServletName=[Forward]"));
}
private void doTestMappingAsync(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Wrapper w = Tomcat.addServlet(ctx, "Async", new AsyncServlet());
w.setAsyncSupported(true);
ctx.addServletMappingDecoded(mapping, "Async");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[mapping]"));
Assert.assertTrue(body, body.contains("Pattern=[/mapping]"));
Assert.assertTrue(body, body.contains("MatchType=[EXACT]"));
Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
Assert.assertTrue(body, body.contains("AsyncMatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("AsyncPattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("AsyncMatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("AsyncServletName=[Async]"));
}
private static class IncludeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher rd = req.getRequestDispatcher("/mapping");
rd.include(req, resp);
}
}
private static class NamedIncludeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher rd = req.getServletContext().getNamedDispatcher("Mapping");
rd.include(req, resp);
}
}
private static class NamedForwardServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher rd = req.getServletContext().getNamedDispatcher("Mapping");
rd.forward(req, resp);
}
}
private static class ForwardServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher rd = req.getRequestDispatcher("/mapping");
rd.forward(req, resp);
}
}
private static class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
AsyncContext ac = req.startAsync();
ac.dispatch("/mapping");
}
}
private static class MappingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
PrintWriter pw = resp.getWriter();
HttpServletMapping mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
req).getHttpServletMapping();
pw.println("MatchValue=[" + mapping.getMatchValue() + "]");
pw.println("Pattern=[" + mapping.getPattern() + "]");
pw.println("MatchType=[" + mapping.getMappingMatch() + "]");
pw.println("ServletName=[" + mapping.getServletName() + "]");
HttpServletMapping includeMapping = (HttpServletMapping) req.getAttribute(
ApplicationDispatcher.INCLUDE_MAPPING);
if (includeMapping != null) {
pw.println("IncludeMatchValue=[" + includeMapping.getMatchValue() + "]");
pw.println("IncludePattern=[" + includeMapping.getPattern() + "]");
pw.println("IncludeMatchType=[" + includeMapping.getMappingMatch() + "]");
pw.println("IncludeServletName=[" + includeMapping.getServletName() + "]");
}
HttpServletMapping forwardMapping = (HttpServletMapping) req.getAttribute(
ApplicationDispatcher.FORWARD_MAPPING);
if (forwardMapping != null) {
pw.println("ForwardMatchValue=[" + forwardMapping.getMatchValue() + "]");
pw.println("ForwardPattern=[" + forwardMapping.getPattern() + "]");
pw.println("ForwardMatchType=[" + forwardMapping.getMappingMatch() + "]");
pw.println("ForwardServletName=[" + forwardMapping.getServletName() + "]");
}
HttpServletMapping asyncMapping = (HttpServletMapping) req.getAttribute(
ApplicationDispatcher.ASYNC_MAPPING);
if (asyncMapping != null) {
pw.println("AsyncMatchValue=[" + asyncMapping.getMatchValue() + "]");
pw.println("AsyncPattern=[" + asyncMapping.getPattern() + "]");
pw.println("AsyncMatchType=[" + asyncMapping.getMappingMatch() + "]");
pw.println("AsyncServletName=[" + asyncMapping.getServletName() + "]");
}
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.core;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.junit.Assert;
import org.junit.Test;
public class TestApplicationPushBuilder {
@Test
public void test01() {
doTest("foo", StandardCharsets.UTF_8, "foo");
}
@Test
public void test02() {
doTest("/foo", StandardCharsets.UTF_8, "/foo");
}
@Test
public void test03() {
doTest("%20foo", StandardCharsets.UTF_8, " foo");
}
@Test
public void test04() {
doTest("fo%20o", StandardCharsets.UTF_8, "fo o");
}
@Test
public void test05() {
doTest("foo%20", StandardCharsets.UTF_8, "foo ");
}
@Test
public void test06() {
doTest("%21foo", StandardCharsets.UTF_8, "!foo");
}
@Test
public void test07() {
doTest("fo%21o", StandardCharsets.UTF_8, "fo!o");
}
@Test
public void test08() {
doTest("foo%21", StandardCharsets.UTF_8, "foo!");
}
private void doTest(String input, Charset charset, String expected) {
String result = ApplicationPushBuilder.decode(input, charset);
Assert.assertEquals(expected, result);
}
}

View File

@@ -0,0 +1,140 @@
/*
* 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.core;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.catalina.LifecycleState;
public class TestApplicationSessionCookieConfig {
private ApplicationSessionCookieConfig applicationSessionCookieConfig;
private final CustomContext context = new CustomContext();
@Before
public void setUp() throws Exception {
applicationSessionCookieConfig = new ApplicationSessionCookieConfig(
context);
}
@Test
public void testSetCommentInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setComment("test");
Assert.assertTrue(applicationSessionCookieConfig.getComment().equals("test"));
}
@Test(expected = IllegalStateException.class)
public void testSetCommentNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setComment("test");
}
@Test
public void testSetDomainInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setDomain("test");
Assert.assertTrue(applicationSessionCookieConfig.getDomain().equals("test"));
}
@Test(expected = IllegalStateException.class)
public void testSetDomainNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setDomain("test");
}
@Test
public void testSetHttpOnlyInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setHttpOnly(true);
Assert.assertTrue(applicationSessionCookieConfig.isHttpOnly());
}
@Test(expected = IllegalStateException.class)
public void testSetHttpOnlyNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setHttpOnly(true);
}
@Test
public void testSetMaxAgeInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setMaxAge(1);
Assert.assertTrue(applicationSessionCookieConfig.getMaxAge() == 1);
}
@Test(expected = IllegalStateException.class)
public void testSetMaxAgeNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setMaxAge(1);
}
@Test
public void testSetNameInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setName("test");
Assert.assertTrue(applicationSessionCookieConfig.getName().equals("test"));
}
@Test(expected = IllegalStateException.class)
public void testSetNameNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setName("test");
}
@Test
public void testSetPathInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setPath("test");
Assert.assertTrue(applicationSessionCookieConfig.getPath().equals("test"));
}
@Test(expected = IllegalStateException.class)
public void testSetPathNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setPath("test");
}
@Test
public void testSetSecureInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setSecure(true);
Assert.assertTrue(applicationSessionCookieConfig.isSecure());
}
@Test(expected = IllegalStateException.class)
public void testSetSecureNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setSecure(true);
}
private static class CustomContext extends StandardContext {
private volatile LifecycleState state;
@Override
public LifecycleState getState() {
return state;
}
@Override
public synchronized void setState(LifecycleState state) {
this.state = state;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,151 @@
/*
* 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.core;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Written for the specific test case of async Servlet, dispatches to sync
* Servlet that then tries to call startAsync() but covers all combinations
* for completeness.
*/
@RunWith(Parameterized.class)
public class TestAsyncContextImplDispatch extends TomcatBaseTest {
@Parameterized.Parameters(name = "{index}: tgt-sup [{0}], dis-sup [{1}], dis-st [{2}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
for (Boolean targetAsyncSupported : booleans) {
for (Boolean dispatchAsyncSupported : booleans) {
for (Boolean dispatchAsyncStart : booleans) {
Boolean allowed = Boolean.valueOf(!dispatchAsyncStart.booleanValue() ||
targetAsyncSupported.booleanValue() && dispatchAsyncSupported.booleanValue() &&
dispatchAsyncStart.booleanValue());
parameterSets.add(new Object[] { targetAsyncSupported, dispatchAsyncSupported, dispatchAsyncStart, allowed} );
}
}
}
return parameterSets;
}
@Parameter(0)
public boolean targetAsyncSupported;
@Parameter(1)
public boolean dispatchAsyncSupported;
@Parameter(2)
public boolean dispatchAsyncStart;
@Parameter(3)
public boolean allowed;
@Test
public void testSendError() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Wrapper w1 = Tomcat.addServlet(ctx, "target", new TesterServlet());
w1.setAsyncSupported(targetAsyncSupported);
ctx.addServletMappingDecoded("/target", "target");
Wrapper w2 = Tomcat.addServlet(ctx, "dispatch", new TesterDispatchServlet(dispatchAsyncStart));
w2.setAsyncSupported(dispatchAsyncSupported);
ctx.addServletMappingDecoded("/dispatch", "dispatch");
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc;
rc = getUrl("http://localhost:" + getPort() + "/target", bc, null, null);
String body = bc.toString();
if (allowed) {
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", body);
} else {
Assert.assertEquals(500, rc);
}
}
public static class TesterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.getRequestDispatcher("/dispatch").forward(req, resp);
}
}
public static class TesterDispatchServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final boolean start;
public TesterDispatchServlet(boolean start) {
this.start = start;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (start) {
AsyncContext ac = req.startAsync();
ac.complete();
}
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().write("OK");
}
}
}

View File

@@ -0,0 +1,378 @@
/*
* 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.core;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.TestCoyoteAdapter;
import org.apache.catalina.startup.SimpleHttpClient;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
/*
* Derived from a test for https://bz.apache.org/bugzilla/show_bug.cgi?id=63816
* Expanded to cover https://bz.apache.org/bugzilla/show_bug.cgi?id=63817 and
* additional scenarios.
*/
@RunWith(Parameterized.class)
public class TestAsyncContextStateChanges extends TomcatBaseTest {
@Parameterized.Parameters(name = "{index}: end [{0}], timing [{1}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
for (AsyncEnd asyncEnd : AsyncEnd.values()) {
for (EndTiming endTiming : EndTiming.values()) {
parameterSets.add(new Object[] { asyncEnd, endTiming });
}
}
return parameterSets;
}
@Parameter(0)
public AsyncEnd asyncEnd;
@Parameter(1)
public EndTiming endTiming;
private ServletRequest servletRequest = null;
private AsyncContext asyncContext = null;
private AtomicBoolean failed = new AtomicBoolean();
private CountDownLatch servletLatch;
private CountDownLatch threadLatch;
private CountDownLatch closeLatch;
private CountDownLatch endLatch;
private boolean dispatch;
@Test
public void testAsync() throws Exception {
dispatch = false;
servletRequest = null;
asyncContext = null;
// Initialise tracking fields
failed.set(true);
servletLatch = new CountDownLatch(1);
threadLatch = new CountDownLatch(1);
closeLatch = new CountDownLatch(1);
endLatch = new CountDownLatch(1);
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
AsyncServlet bug63816Servlet = new AsyncServlet();
Wrapper wrapper = Tomcat.addServlet(ctx, "bug63816Servlet", bug63816Servlet);
wrapper.setAsyncSupported(true);
ctx.addServletMappingDecoded("/*", "bug63816Servlet");
tomcat.start();
Client client = new Client();
client.setPort(getPort());
client.setRequest(new String[] { "GET / HTTP/1.1" + SimpleHttpClient.CRLF +
"Host: localhost:" + SimpleHttpClient.CRLF +
SimpleHttpClient.CRLF});
client.connect();
client.sendRequest();
// Wait for Servlet to start processing request
servletLatch.await();
if (asyncEnd.isError()) {
client.disconnect();
closeLatch.countDown();
try {
endLatch.await();
} catch (InterruptedException e) {
// Ignore
}
} else {
client.setUseContentLength(true);
client.readResponse(true);
}
Assert.assertFalse(failed.get());
}
private static final class Client extends SimpleHttpClient {
@Override
public boolean isResponseBodyOK() {
return true;
}
}
private final class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
servletLatch.countDown();
if (dispatch) {
return;
}
if (!asyncEnd.isError()) {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.setContentLength(2);
resp.getWriter().print("OK");
}
servletRequest = req;
asyncContext = req.startAsync();
asyncContext.addListener(new Listener());
if (!asyncEnd.isError()) {
// Use a short timeout so the test does not pause for too long
// waiting for the timeout to be triggered.
asyncContext.setTimeout(1000);
}
Thread t = new NonContainerThread();
switch (endTiming) {
case INLINE: {
t.run();
break;
}
case THREAD_AFTER_EXIT: {
t.start();
threadLatch.countDown();
break;
}
case THREAD_BEFORE_EXIT: {
t.start();
try {
threadLatch.await();
} catch (InterruptedException e) {
// Ignore
}
endLatch.countDown();
break;
}
}
}
}
private final class NonContainerThread extends Thread {
@Override
public void run() {
if (endTiming == EndTiming.THREAD_AFTER_EXIT) {
try {
threadLatch.await();
/*
* As much as I dislike it, I don't see any easy way around
* this hack. The latch above is released as the Servlet
* exits but we need to wait for the post processing to
* complete for the test to work as intended. In real-world
* applications this does mean that there is a real chance
* of an ISE. We may need to increase this delay for some CI
* systems.
*/
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
// Trigger the error if necessary
if (asyncEnd.isError()) {
try {
closeLatch.await();
} catch (InterruptedException e) {
// Ignore
}
try {
ServletResponse resp = asyncContext.getResponse();
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
OutputStream os = resp.getOutputStream();
resp.setContentType("text/plain");
for (int i = 0; i < 16; i++) {
os.write(TestCoyoteAdapter.TEXT_8K.getBytes(StandardCharsets.UTF_8));
}
} catch (IOException e) {
// Expected
}
}
if (endTiming != EndTiming.THREAD_AFTER_EXIT) {
try {
switch (asyncEnd) {
case COMPLETE:
case ERROR_COMPLETE: {
asyncContext.complete();
break;
}
case DISPATCH:
case ERROR_DISPATCH: {
dispatch = true;
asyncContext.dispatch();
break;
}
case NONE:
case ERROR_NONE: {
break;
}
}
// The request must stay in async mode until doGet() exists
if (servletRequest.isAsyncStarted()) {
failed.set(false);
}
} finally {
if (endTiming == EndTiming.THREAD_BEFORE_EXIT) {
threadLatch.countDown();
}
}
}
}
}
private class Listener implements AsyncListener {
@Override
public void onComplete(AsyncEvent event) throws IOException {
if (endTiming == EndTiming.INLINE) {
endLatch.countDown();
}
}
@Override
public void onTimeout(AsyncEvent event) throws IOException {
// Need to handle timeouts for THREAD_AFTER_EXIT in the listener to
// avoid concurrency issues.
if (endTiming == EndTiming.THREAD_AFTER_EXIT) {
switch (asyncEnd) {
case COMPLETE: {
asyncContext.complete();
break;
}
case DISPATCH: {
dispatch = true;
asyncContext.dispatch();
break;
}
default:
// NO-OP
}
}
if (servletRequest.isAsyncStarted() == asyncEnd.isNone()) {
failed.set(false);
}
endLatch.countDown();
}
@Override
public void onError(AsyncEvent event) throws IOException {
// Need to handle errors for THREAD_AFTER_EXIT in the listener to
// avoid concurrency issues.
if (endTiming == EndTiming.THREAD_AFTER_EXIT) {
switch (asyncEnd) {
case ERROR_COMPLETE: {
asyncContext.complete();
break;
}
case ERROR_DISPATCH: {
dispatch = true;
asyncContext.dispatch();
break;
}
default:
// NO-OP
}
if (servletRequest.isAsyncStarted() == asyncEnd.isNone()) {
failed.set(false);
}
endLatch.countDown();
}
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
// NO-OP
}
}
public enum AsyncEnd {
NONE ( true, false),
COMPLETE (false, false),
DISPATCH (false, false),
ERROR_NONE ( true, true),
ERROR_COMPLETE(false, true),
ERROR_DISPATCH(false, true);
final boolean none;
final boolean error;
private AsyncEnd(boolean none, boolean error) {
this.none = none;
this.error = error;
}
public boolean isNone() {
return none;
}
public boolean isError() {
return error;
}
}
public enum EndTiming {
INLINE,
THREAD_BEFORE_EXIT,
THREAD_AFTER_EXIT
}
}

View File

@@ -0,0 +1,163 @@
/*
* 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.core;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import javax.naming.NamingException;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.servlets.DefaultServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.jasper.servlet.JasperInitializer;
import org.apache.tomcat.InstanceManager;
public class TestDefaultInstanceManager extends TomcatBaseTest {
@Test
public void testClassUnloading() throws Exception {
DefaultInstanceManager instanceManager = doClassUnloadingPrep();
// Request a JSP page (that doesn't load any tag libraries etc.)
// This page does use @PostConstruct to ensure that the cache does not
// retain strong references
getUrl("http://localhost:" + getPort() + "/test/annotations.jsp");
// Request a second JSP (again, no tag libraries etc.)
getUrl("http://localhost:" + getPort() + "/test/bug36923.jsp");
// Check the number of classes in the cache
int count = instanceManager.getAnnotationCacheSize();
// Request a third JSP (again, no tag libraries etc.)
getUrl("http://localhost:" + getPort() + "/test/bug5nnnn/bug51544.jsp");
// Force a GC to clear out unloaded class (first JSP)
System.gc();
// Spin a while until GC happens or we wait too long
int loop = 0;
while (loop < 10) {
instanceManager.backgroundProcess();
if (instanceManager.getAnnotationCacheSize() == count) {
break;
}
Thread.sleep(100);
loop++;
}
// First JSP should be unloaded and replaced by third (second left
// alone) so no change in overall count
Assert.assertEquals(count, instanceManager.getAnnotationCacheSize());
}
private DefaultInstanceManager doClassUnloadingPrep() throws Exception {
Tomcat tomcat = getTomcatInstance();
// Create the context (don't use addWebapp as we want to modify the
// JSP Servlet settings).
File appDir = new File("test/webapp");
StandardContext ctxt = (StandardContext) tomcat.addContext(
null, "/test", appDir.getAbsolutePath());
ctxt.addServletContainerInitializer(new JasperInitializer(), null);
// Configure the defaults and then tweak the JSP servlet settings
// Note: Min value for maxLoadedJsps is 2
Tomcat.initWebappDefaults(ctxt);
Wrapper w = (Wrapper) ctxt.findChild("jsp");
w.addInitParameter("maxLoadedJsps", "2");
tomcat.start();
return (DefaultInstanceManager) ctxt.getInstanceManager();
}
/*
* Performance test. Comment out @Ignore to run the test.
*/
@Ignore
@Test
public void testConcurrency() throws Exception {
// Create a populated InstanceManager
Tomcat tomcat = getTomcatInstance();
Context ctx = tomcat.addContext(null, "", null);
tomcat.start();
InstanceManager im = ctx.getInstanceManager();
for (int i = 1; i < 9; i++) {
doTestConcurrency(im, i);
}
}
private void doTestConcurrency(InstanceManager im, int threadCount) throws Exception {
long start = System.nanoTime();
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(new InstanceManagerRunnable(im));
}
for (int i = 0; i < threadCount; i++) {
threads[i].start();
}
for (int i = 0; i < threadCount; i++) {
threads[i].join();
}
long duration = System.nanoTime() - start;
System.out.println(threadCount + " threads completed in " + duration + "ns");
}
private class InstanceManagerRunnable implements Runnable {
private final InstanceManager im;
private InstanceManagerRunnable(InstanceManager im) {
this.im = im;
}
@Override
public void run() {
try {
Object test = new DefaultServlet();
for (int i = 0; i < 200000; i++) {
im.newInstance(test);
im.destroyInstance(test);
}
} catch (NamingException | IllegalAccessException | InvocationTargetException ne) {
ne.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,184 @@
/*
* 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.core;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.descriptor.web.ContextEnvironment;
public class TestNamingContextListener extends TomcatBaseTest {
private static final String BUG49132_NAME = "TestName";
private static final String BUG49132_VALUE = "Test Value";
private static final String BUG54096_NameA = "envA";
private static final String BUG54096_ValueA = "valueA";
private static final String BUG54096_NameB = "envB";
private static final String BUG54096_ValueB = "B";
/*
* Test JNDI is available to ServletContextListeners.
*/
@Test
public void testBug49132() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Enable JNDI - it is disabled by default
tomcat.enableNaming();
ContextEnvironment environment = new ContextEnvironment();
environment.setType(BUG49132_VALUE.getClass().getName());
environment.setName(BUG49132_NAME);
environment.setValue(BUG49132_VALUE);
ctx.getNamingResources().addEnvironment(environment);
ctx.addApplicationListener(Bug49132Listener.class.getName());
tomcat.start();
Assert.assertEquals(LifecycleState.STARTED, ctx.getState());
}
public static final class Bug49132Listener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent sce) {
// NOOP
}
@Override
public void contextInitialized(ServletContextEvent sce) {
javax.naming.Context initCtx;
try {
initCtx = new InitialContext();
javax.naming.Context envCtx =
(javax.naming.Context) initCtx.lookup("java:comp/env");
String value = (String) envCtx.lookup(BUG49132_NAME);
if (!BUG49132_VALUE.equals(value)) {
throw new RuntimeException();
}
} catch (NamingException e) {
throw new RuntimeException(e);
}
}
}
@Test
public void testBug54096() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Enable JNDI - it is disabled by default
tomcat.enableNaming();
ContextEnvironment environmentA = new ContextEnvironment();
environmentA.setType(Bug54096EnvA.class.getName());
environmentA.setName(BUG54096_NameA);
environmentA.setValue(BUG54096_ValueA);
ctx.getNamingResources().addEnvironment(environmentA);
ContextEnvironment environmentB = new ContextEnvironment();
environmentB.setType(Bug54096EnvB.class.getName());
environmentB.setName(BUG54096_NameB);
environmentB.setValue(BUG54096_ValueB);
ctx.getNamingResources().addEnvironment(environmentB);
ctx.addApplicationListener(Bug54096Listener.class.getName());
tomcat.start();
Assert.assertEquals(LifecycleState.STARTED, ctx.getState());
}
public static class Bug54096EnvA {
private final String value;
public Bug54096EnvA(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public static class Bug54096EnvB {
private final char value;
public Bug54096EnvB(char value) {
this.value = value;
}
public char getValue() {
return value;
}
}
public static final class Bug54096Listener implements
ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent sce) {
// NOOP
}
@Override
public void contextInitialized(ServletContextEvent sce) {
javax.naming.Context initCtx;
try {
initCtx = new InitialContext();
javax.naming.Context envCtx =
(javax.naming.Context) initCtx.lookup("java:comp/env");
// Validate entry A
Bug54096EnvA valueA =
(Bug54096EnvA) envCtx.lookup(BUG54096_NameA);
if (!BUG54096_ValueA.equals(valueA.getValue())) {
throw new RuntimeException();
}
// Validate entry B
Bug54096EnvB valueB =
(Bug54096EnvB) envCtx.lookup(BUG54096_NameB);
if (BUG54096_ValueB.charAt(0) != valueB.getValue()) {
throw new RuntimeException();
}
} catch (NamingException e) {
throw new RuntimeException(e);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
/*
* 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.core;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestStandardContextAliases extends TomcatBaseTest {
@Test
public void testDirContextAliases() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
File lib = new File("webapps/examples/WEB-INF/lib");
ctx.setResources(new StandardRoot(ctx));
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/lib",
lib.getAbsolutePath(), null, "/");
Tomcat.addServlet(ctx, "test", new TestServlet());
ctx.addServletMappingDecoded("/", "test");
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + "/");
String result = res.toString();
if (result == null) {
result = "";
}
Assert.assertTrue(result.indexOf("00-PASS") > -1);
Assert.assertTrue(result.indexOf("01-PASS") > -1);
Assert.assertTrue(result.indexOf("02-PASS") > -1);
}
/**
* Looks for the JSTL JARs in WEB-INF/lib.
*/
public static class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
ServletContext context = getServletContext();
// Check resources individually
URL url = context.getResource("/WEB-INF/lib/taglibs-standard-spec-1.2.5.jar");
if (url != null) {
resp.getWriter().write("00-PASS\n");
}
url = context.getResource("/WEB-INF/lib/taglibs-standard-impl-1.2.5.jar");
if (url != null) {
resp.getWriter().write("01-PASS\n");
}
// Check a directory listing
Set<String> libs = context.getResourcePaths("/WEB-INF/lib");
if (libs == null) {
return;
}
if (!libs.contains("/WEB-INF/lib/taglibs-standard-spec-1.2.5.jar")) {
return;
}
if (!libs.contains("/WEB-INF/lib/taglibs-standard-impl-1.2.5.jar")) {
return;
}
resp.getWriter().write("02-PASS\n");
}
}
}

View File

@@ -0,0 +1,282 @@
/*
* 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.core;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.startup.Constants;
import org.apache.catalina.startup.ContextConfig;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.util.IOTools;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.WebXml;
public class TestStandardContextResources extends TomcatBaseTest {
@Override
public void setUp() throws Exception {
super.setUp();
Tomcat tomcat = getTomcatInstance();
// BZ 49218: The test fails if JreMemoryLeakPreventionListener is not
// present. The listener affects the JVM, and thus not only the current,
// but also the subsequent tests that are run in the same JVM. So it is
// fair to add it in every test.
tomcat.getServer().addLifecycleListener(
new JreMemoryLeakPreventionListener());
}
@Test
public void testResources() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-fragments");
// app dir is relative to server home
Context ctx = tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
tomcat.start();
assertPageContains("/test/resourceA.jsp",
"<p>resourceA.jsp in the web application</p>");
assertPageContains("/test/resourceB.jsp",
"<p>resourceB.jsp in resources.jar</p>");
assertPageContains("/test/folder/resourceC.jsp",
"<p>resourceC.jsp in the web application</p>");
assertPageContains("/test/folder/resourceD.jsp",
"<p>resourceD.jsp in resources.jar</p>");
assertPageContains("/test/folder/resourceE.jsp",
"<p>resourceE.jsp in the web application</p>");
assertPageContains("/test/resourceG.jsp",
"<p>resourceG.jsp in WEB-INF/classes</p>", 404);
// For BZ 54391. Relative ordering is specified in resources2.jar.
// It is not absolute-ordering, so there may be other jars in the list
@SuppressWarnings("unchecked")
List<String> orderedLibs = (List<String>) ctx.getServletContext()
.getAttribute(ServletContext.ORDERED_LIBS);
if (orderedLibs.size() > 2) {
log.warn("testResources(): orderedLibs: " + orderedLibs);
}
int index = orderedLibs.indexOf("resources.jar");
int index2 = orderedLibs.indexOf("resources2.jar");
Assert.assertTrue(orderedLibs.toString(), index >= 0 && index2 >= 0
&& index < index2);
}
@Test
public void testResourcesWebInfClasses() throws Exception {
Tomcat tomcat = getTomcatInstance();
// app dir is relative to server home
File appDir = new File("test/webapp-fragments");
// Need to cast to be able to set StandardContext specific attribute
StandardContext ctxt = (StandardContext)
tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
ctxt.setAddWebinfClassesResources(true);
tomcat.start();
assertPageContains("/test/resourceA.jsp",
"<p>resourceA.jsp in the web application</p>");
assertPageContains("/test/resourceB.jsp",
"<p>resourceB.jsp in resources.jar</p>");
assertPageContains("/test/folder/resourceC.jsp",
"<p>resourceC.jsp in the web application</p>");
assertPageContains("/test/folder/resourceD.jsp",
"<p>resourceD.jsp in resources.jar</p>");
assertPageContains("/test/folder/resourceE.jsp",
"<p>resourceE.jsp in the web application</p>");
assertPageContains("/test/resourceG.jsp",
"<p>resourceG.jsp in WEB-INF/classes</p>");
}
@Test
public void testResourcesAbsoluteOrdering() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-fragments");
AbsoluteOrderContextConfig absoluteOrderConfig = new AbsoluteOrderContextConfig();
// app dir is relative to server home
StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test",
appDir.getAbsolutePath(), (LifecycleListener) absoluteOrderConfig);
Tomcat.addServlet(ctx, "getresource", new GetResourceServlet());
ctx.addServletMappingDecoded("/getresource", "getresource");
tomcat.start();
assertPageContains("/test/getresource?path=/resourceF.jsp",
"<p>resourceF.jsp in resources2.jar</p>");
assertPageContains("/test/getresource?path=/resourceB.jsp",
"<p>resourceB.jsp in resources.jar</p>");
// Check ordering, for BZ 54391
Assert.assertEquals(Arrays.asList("resources.jar", "resources2.jar"), ctx
.getServletContext().getAttribute(ServletContext.ORDERED_LIBS));
tomcat.getHost().removeChild(ctx);
tomcat.getHost().stop();
// change ordering
absoluteOrderConfig.swap();
ctx = (StandardContext) tomcat.addWebapp(null, "/test",
appDir.getAbsolutePath(), (LifecycleListener) absoluteOrderConfig);
Tomcat.addServlet(ctx, "getresource", new GetResourceServlet());
ctx.addServletMappingDecoded("/getresource", "getresource");
tomcat.getHost().start();
assertPageContains("/test/getresource?path=/resourceF.jsp",
"<p>resourceF.jsp in resources2.jar</p>");
assertPageContains("/test/getresource?path=/resourceB.jsp",
"<p>resourceB.jsp in resources2.jar</p>");
// Check ordering, for BZ 54391
Assert.assertEquals(Arrays.asList("resources2.jar", "resources.jar"), ctx
.getServletContext().getAttribute(ServletContext.ORDERED_LIBS));
}
public static class AbsoluteOrderContextConfig extends ContextConfig {
private boolean swap = false;
public AbsoluteOrderContextConfig() {
super();
// Prevent it from looking (if it finds one - it'll have dup error)
setDefaultWebXml(Constants.NoDefaultWebXml);
}
@Override
protected WebXml createWebXml() {
WebXml wxml = new WebXml();
if (swap) {
wxml.addAbsoluteOrdering("resources2");
wxml.addAbsoluteOrdering("resources");
} else {
wxml.addAbsoluteOrdering("resources");
wxml.addAbsoluteOrdering("resources2");
}
return wxml;
}
protected void swap() {
swap = !swap;
}
}
@Test
public void testResources2() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-fragments");
// app dir is relative to server home
StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test",
appDir.getAbsolutePath());
skipTldsForResourceJars(ctx);
Tomcat.addServlet(ctx, "getresource", new GetResourceServlet());
ctx.addServletMappingDecoded("/getresource", "getresource");
tomcat.start();
assertPageContains("/test/getresource?path=/resourceF.jsp",
"<p>resourceF.jsp in resources2.jar</p>");
assertPageContains("/test/getresource?path=/resourceA.jsp",
"<p>resourceA.jsp in the web application</p>");
assertPageContains("/test/getresource?path=/resourceB.jsp",
"<p>resourceB.jsp in resources.jar</p>");
assertPageContains("/test/getresource?path=/folder/resourceC.jsp",
"<p>resourceC.jsp in the web application</p>");
assertPageContains("/test/getresource?path=/folder/resourceD.jsp",
"<p>resourceD.jsp in resources.jar</p>");
assertPageContains("/test/getresource?path=/folder/resourceE.jsp",
"<p>resourceE.jsp in the web application</p>");
}
/**
* A servlet that prints the requested resource. The path to the requested
* resource is passed as a parameter, <code>path</code>.
*/
public static class GetResourceServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
ServletContext context = getServletContext();
// Check resources individually
URL url = context.getResource(req.getParameter("path"));
if (url == null) {
resp.getWriter().println("Not found");
return;
}
try (InputStream input = url.openStream();
OutputStream output = resp.getOutputStream()) {
IOTools.flow(input, output);
}
}
}
private void assertPageContains(String pageUrl, String expectedBody)
throws IOException {
assertPageContains(pageUrl, expectedBody, 200);
}
private void assertPageContains(String pageUrl, String expectedBody,
int expectedStatus) throws IOException {
ByteChunk res = new ByteChunk();
int sc = getUrl("http://localhost:" + getPort() + pageUrl, res, null);
Assert.assertEquals(expectedStatus, sc);
if (expectedStatus == 200) {
String result = res.toString();
Assert.assertTrue(result, result.indexOf(expectedBody) > 0);
}
}
}

View File

@@ -0,0 +1,185 @@
/*
* 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.core;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Response;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.ErrorPage;
public class TestStandardContextValve extends TomcatBaseTest {
@Test
public void testBug51653a() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Traces order of events across multiple components
StringBuilder trace = new StringBuilder();
//Add the error page
Tomcat.addServlet(ctx, "errorPage", new Bug51653ErrorPage(trace));
ctx.addServletMappingDecoded("/error", "errorPage");
// And the handling for 404 responses
ErrorPage errorPage = new ErrorPage();
errorPage.setErrorCode(Response.SC_NOT_FOUND);
errorPage.setLocation("/error");
ctx.addErrorPage(errorPage);
// Add the request listener
Bug51653RequestListener reqListener =
new Bug51653RequestListener(trace);
((StandardContext) ctx).addApplicationEventListener(reqListener);
tomcat.start();
// Request a page that does not exist
int rc = getUrl("http://localhost:" + getPort() + "/invalid",
new ByteChunk(), null);
// Need to allow time (but not too long in case the test fails) for
// ServletRequestListener to complete
int i = 20;
while (i > 0) {
if (trace.toString().endsWith("Destroy")) {
break;
}
Thread.sleep(250);
i--;
}
Assert.assertEquals(Response.SC_NOT_FOUND, rc);
Assert.assertEquals("InitErrorDestroy", trace.toString());
}
@Test
public void testBug51653b() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Traces order of events across multiple components
StringBuilder trace = new StringBuilder();
// Add the page that generates the error
Tomcat.addServlet(ctx, "test", new Bug51653ErrorTrigger());
ctx.addServletMappingDecoded("/test", "test");
// Add the error page
Tomcat.addServlet(ctx, "errorPage", new Bug51653ErrorPage(trace));
ctx.addServletMappingDecoded("/error", "errorPage");
// And the handling for 404 responses
ErrorPage errorPage = new ErrorPage();
errorPage.setErrorCode(Response.SC_NOT_FOUND);
errorPage.setLocation("/error");
ctx.addErrorPage(errorPage);
// Add the request listener
Bug51653RequestListener reqListener =
new Bug51653RequestListener(trace);
((StandardContext) ctx).addApplicationEventListener(reqListener);
tomcat.start();
// Request a page that does not exist
int rc = getUrl("http://localhost:" + getPort() + "/test",
new ByteChunk(), null);
// Need to allow time (but not too long in case the test fails) for
// ServletRequestListener to complete
int i = 20;
while (i > 0) {
if (trace.toString().endsWith("Destroy")) {
break;
}
Thread.sleep(250);
i--;
}
Assert.assertEquals(Response.SC_NOT_FOUND, rc);
Assert.assertEquals("InitErrorDestroy", trace.toString());
}
private static class Bug51653ErrorTrigger extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.sendError(Response.SC_NOT_FOUND);
}
}
private static class Bug51653ErrorPage extends HttpServlet {
private static final long serialVersionUID = 1L;
private StringBuilder sb;
public Bug51653ErrorPage(StringBuilder sb) {
this.sb = sb;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
sb.append("Error");
resp.setContentType("text/plain");
resp.getWriter().write("Error");
}
}
private static class Bug51653RequestListener
implements ServletRequestListener {
private StringBuilder sb;
public Bug51653RequestListener(StringBuilder sb) {
this.sb = sb;
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
sb.append("Init");
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
sb.append("Destroy");
}
}
}

View File

@@ -0,0 +1,169 @@
/*
* 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.core;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Response;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.ErrorPage;
public class TestStandardHostValve extends TomcatBaseTest {
@Test
public void testErrorPageHandling() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Add the error page
Tomcat.addServlet(ctx, "error", new ErrorServlet());
ctx.addServletMappingDecoded("/error", "error");
// Add the error handling page
Tomcat.addServlet(ctx, "report", new ReportServlet());
ctx.addServletMappingDecoded("/report/*", "report");
// And the handling for 500 responses
ErrorPage errorPage500 = new ErrorPage();
errorPage500.setErrorCode(Response.SC_INTERNAL_SERVER_ERROR);
errorPage500.setLocation("/report/500");
ctx.addErrorPage(errorPage500);
// And the default error handling
ErrorPage errorPageDefault = new ErrorPage();
errorPageDefault.setLocation("/report/default");
ctx.addErrorPage(errorPageDefault);
tomcat.start();
doTestErrorPageHandling(500, "/500");
doTestErrorPageHandling(501, "/default");
}
@Test(expected=IllegalArgumentException.class)
public void testInvalidErrorPage() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Add a broken error page configuration
ErrorPage errorPage500 = new ErrorPage();
errorPage500.setErrorCode("java.lang.Exception");
errorPage500.setLocation("/report/500");
ctx.addErrorPage(errorPage500);
}
@Test
public void testSRLAfterError() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Add the error page
Tomcat.addServlet(ctx, "error", new ErrorServlet());
ctx.addServletMappingDecoded("/error", "error");
final List<String> result = new ArrayList<>();
// Add the request listener
ServletRequestListener servletRequestListener = new ServletRequestListener() {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
result.add("Visit requestDestroyed");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
result.add("Visit requestInitialized");
}
};
((StandardContext) ctx).addApplicationEventListener(servletRequestListener);
tomcat.start();
// Request a page that triggers an error
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + "/error?errorCode=400", bc, null);
Assert.assertEquals(400, rc);
Assert.assertTrue(result.contains("Visit requestInitialized"));
Assert.assertTrue(result.contains("Visit requestDestroyed"));
}
private void doTestErrorPageHandling(int error, String report)
throws Exception {
// Request a page that triggers an error
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() +
"/error?errorCode=" + error, bc, null);
Assert.assertEquals(error, rc);
Assert.assertEquals(report, bc.toString());
}
private static class ErrorServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
int error = Integer.parseInt(req.getParameter("errorCode"));
resp.sendError(error);
}
}
private static class ReportServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String pathInfo = req.getPathInfo();
resp.setContentType("text/plain");
resp.getWriter().print(pathInfo);
}
}
}

View File

@@ -0,0 +1,663 @@
/*
* 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.core;
import java.io.File;
import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.HttpConstraint;
import javax.servlet.annotation.HttpMethodConstraint;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.authenticator.BasicAuthenticator;
import org.apache.catalina.realm.MessageDigestCredentialHandler;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
public class TestStandardWrapper extends TomcatBaseTest {
@Test
public void testSecurityAnnotationsSimple() throws Exception {
doTest(DenyAllServlet.class.getName(), false, false, false, false);
}
@Test
public void testSecurityAnnotationsSubclass1() throws Exception {
doTest(SubclassDenyAllServlet.class.getName(),
false, false, false,false);
}
@Test
public void testSecurityAnnotationsSubclass2() throws Exception {
doTest(SubclassAllowAllServlet.class.getName(),
false, false, true, false);
}
@Test
public void testSecurityAnnotationsMethods1() throws Exception {
doTest(MethodConstraintServlet.class.getName(),
false, false, false, false);
}
@Test
public void testSecurityAnnotationsMethods2() throws Exception {
doTest(MethodConstraintServlet.class.getName(),
true, false, true, false);
}
@Test
public void testSecurityAnnotationsRole1() throws Exception {
doTest(RoleAllowServlet.class.getName(), false, true, true, false);
}
@Test
public void testSecurityAnnotationsRole2() throws Exception {
doTest(RoleDenyServlet.class.getName(), false, true, false, false);
}
@Test
public void testSecurityAnnotationsUncoveredGet01() throws Exception {
// Use a POST with role - should be allowed
doTest(UncoveredGetServlet.class.getName(), true, true, true, false);
}
@Test
public void testSecurityAnnotationsUncoveredGet02() throws Exception {
// Use a POST with role - should be allowed
doTest(UncoveredGetServlet.class.getName(), true, true, true, true);
}
@Test
public void testSecurityAnnotationsUncoveredGet03() throws Exception {
// Use a POST no role - should be blocked
doTest(UncoveredGetServlet.class.getName(), true, false, false, false);
}
@Test
public void testSecurityAnnotationsUncoveredGet04() throws Exception {
// Use a POST no role - should be blocked
doTest(UncoveredGetServlet.class.getName(), true, false, false, true);
}
@Test
public void testSecurityAnnotationsUncoveredGet05() throws Exception {
// Use a GET with role - should be allowed as denyUncovered is false
doTest(UncoveredGetServlet.class.getName(), false, true, true, false);
}
@Test
public void testSecurityAnnotationsUncoveredGet06() throws Exception {
// Use a GET with role - should be blocked as denyUncovered is true
doTest(UncoveredGetServlet.class.getName(), false, true, false, true);
}
@Test
public void testSecurityAnnotationsUncoveredGet07() throws Exception {
// Use a GET no role - should be allowed as denyUncovered is false
doTest(UncoveredGetServlet.class.getName(), false, false, true, false);
}
@Test
public void testSecurityAnnotationsUncoveredGet08() throws Exception {
// Use a GET no role - should be blocked as denyUncovered is true
doTest(UncoveredGetServlet.class.getName(), true, false, false, true);
}
@Test
public void testSecurityAnnotationsWebXmlPriority() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-fragments");
Context ctx = tomcat.addWebapp(null, "", appDir.getAbsolutePath());
skipTldsForResourceJars(ctx);
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc;
rc = getUrl("http://localhost:" + getPort() +
"/testStandardWrapper/securityAnnotationsWebXmlPriority",
bc, null, null);
Assert.assertTrue(bc.getLength() > 0);
Assert.assertEquals(403, rc);
}
@Test
public void testSecurityAnnotationsMetaDataPriority() throws Exception {
getTomcatInstanceTestWebapp(false, true);
ByteChunk bc = new ByteChunk();
int rc;
rc = getUrl("http://localhost:" + getPort() +
"/test/testStandardWrapper/securityAnnotationsMetaDataPriority",
bc, null, null);
Assert.assertEquals("OK", bc.toString());
Assert.assertEquals(200, rc);
}
@Test
public void testSecurityAnnotationsAddServlet1() throws Exception {
doTestSecurityAnnotationsAddServlet(false);
}
@Test
public void testSecurityAnnotationsAddServlet2() throws Exception {
doTestSecurityAnnotationsAddServlet(true);
}
@Test
public void testSecurityAnnotationsNoWebXmlConstraints() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-servletsecurity");
tomcat.addWebapp(null, "", appDir.getAbsolutePath());
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc;
rc = getUrl("http://localhost:" + getPort() + "/",
bc, null, null);
Assert.assertTrue(bc.getLength() > 0);
Assert.assertEquals(403, rc);
}
@Test
public void testSecurityAnnotationsNoWebXmlLoginConfig() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-servletsecurity2");
tomcat.addWebapp(null, "", appDir.getAbsolutePath());
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc;
rc = getUrl("http://localhost:" + getPort() + "/protected.jsp",
bc, null, null);
Assert.assertTrue(bc.getLength() > 0);
Assert.assertEquals(403, rc);
bc.recycle();
rc = getUrl("http://localhost:" + getPort() + "/unprotected.jsp",
bc, null, null);
Assert.assertEquals(200, rc);
Assert.assertTrue(bc.toString().contains("00-OK"));
}
@Test
public void testRoleMappingInEngine() throws Exception {
doTestRoleMapping("engine");
}
@Test
public void testRoleMappingInHost() throws Exception {
doTestRoleMapping("host");
}
@Test
public void testRoleMappingInContext() throws Exception {
doTestRoleMapping("context");
}
private void doTestRoleMapping(String realmContainer)
throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
ctx.addRoleMapping("testRole", "very-complex-role-name");
Wrapper wrapper = Tomcat.addServlet(ctx, "servlet", RoleAllowServlet.class.getName());
ctx.addServletMappingDecoded("/", "servlet");
ctx.setLoginConfig(new LoginConfig("BASIC", null, null, null));
ctx.getPipeline().addValve(new BasicAuthenticator());
TesterMapRealm realm = new TesterMapRealm();
MessageDigestCredentialHandler ch = new MessageDigestCredentialHandler();
ch.setAlgorithm("SHA");
realm.setCredentialHandler(ch);
/* Attach the realm to the appropriate container, but role mapping must
* always succeed because it is evaluated at context level.
*/
if (realmContainer.equals("engine")) {
tomcat.getEngine().setRealm(realm);
} else if (realmContainer.equals("host")) {
tomcat.getHost().setRealm(realm);
} else if (realmContainer.equals("context")) {
ctx.setRealm(realm);
} else {
throw new IllegalArgumentException("realmContainer is invalid");
}
realm.addUser("testUser", ch.mutate("testPwd"));
realm.addUserRole("testUser", "testRole1");
realm.addUserRole("testUser", "very-complex-role-name");
realm.addUserRole("testUser", "another-very-complex-role-name");
tomcat.start();
Principal p = realm.authenticate("testUser", "testPwd");
Assert.assertNotNull(p);
Assert.assertEquals("testUser", p.getName());
// This one is mapped
Assert.assertTrue(realm.hasRole(wrapper, p, "testRole"));
Assert.assertTrue(realm.hasRole(wrapper, p, "testRole1"));
Assert.assertFalse(realm.hasRole(wrapper, p, "testRole2"));
Assert.assertTrue(realm.hasRole(wrapper, p, "very-complex-role-name"));
Assert.assertTrue(realm.hasRole(wrapper, p, "another-very-complex-role-name"));
// This now tests RealmBase#hasResourcePermission() because we need a wrapper
// to be passed from an authenticator
ByteChunk bc = new ByteChunk();
Map<String,List<String>> reqHeaders = new HashMap<>();
List<String> authHeaders = new ArrayList<>();
// testUser, testPwd
authHeaders.add("Basic dGVzdFVzZXI6dGVzdFB3ZA==");
reqHeaders.put("Authorization", authHeaders);
int rc = getUrl("http://localhost:" + getPort() + "/", bc, reqHeaders,
null);
Assert.assertEquals("OK", bc.toString());
Assert.assertEquals(200, rc);
}
private void doTestSecurityAnnotationsAddServlet(boolean useCreateServlet)
throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Servlet s = new DenyAllServlet();
ServletContainerInitializer sci = new SCI(s, useCreateServlet);
ctx.addServletContainerInitializer(sci, null);
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc;
rc = getUrl("http://localhost:" + getPort() + "/", bc, null, null);
if (useCreateServlet) {
Assert.assertTrue(bc.getLength() > 0);
Assert.assertEquals(403, rc);
} else {
Assert.assertEquals("OK", bc.toString());
Assert.assertEquals(200, rc);
}
}
private void doTest(String servletClassName, boolean usePost,
boolean useRole, boolean expect200, boolean denyUncovered)
throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
ctx.setDenyUncoveredHttpMethods(denyUncovered);
Wrapper wrapper = Tomcat.addServlet(ctx, "servlet", servletClassName);
wrapper.setAsyncSupported(true);
ctx.addServletMappingDecoded("/", "servlet");
if (useRole) {
TesterMapRealm realm = new TesterMapRealm();
realm.addUser("testUser", "testPwd");
realm.addUserRole("testUser", "testRole");
ctx.setRealm(realm);
ctx.setLoginConfig(new LoginConfig("BASIC", null, null, null));
ctx.getPipeline().addValve(new BasicAuthenticator());
}
tomcat.start();
ByteChunk bc = new ByteChunk();
Map<String,List<String>> reqHeaders = null;
if (useRole) {
reqHeaders = new HashMap<>();
List<String> authHeaders = new ArrayList<>();
// testUser, testPwd
authHeaders.add("Basic dGVzdFVzZXI6dGVzdFB3ZA==");
reqHeaders.put("Authorization", authHeaders);
}
int rc;
if (usePost) {
rc = postUrl(null, "http://localhost:" + getPort() + "/", bc,
reqHeaders, null);
} else {
rc = getUrl("http://localhost:" + getPort() + "/", bc, reqHeaders,
null);
}
if (expect200) {
Assert.assertEquals("OK", bc.toString());
Assert.assertEquals(200, rc);
} else {
Assert.assertTrue(bc.getLength() > 0);
Assert.assertEquals(403, rc);
}
}
public static class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.getWriter().print("OK");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
}
@ServletSecurity(@HttpConstraint(EmptyRoleSemantic.DENY))
public static class DenyAllServlet extends TestServlet {
private static final long serialVersionUID = 1L;
}
public static class SubclassDenyAllServlet extends DenyAllServlet {
private static final long serialVersionUID = 1L;
}
@ServletSecurity(@HttpConstraint(EmptyRoleSemantic.PERMIT))
public static class SubclassAllowAllServlet extends DenyAllServlet {
private static final long serialVersionUID = 1L;
}
@ServletSecurity(value= @HttpConstraint(EmptyRoleSemantic.PERMIT),
httpMethodConstraints = {
@HttpMethodConstraint(value="GET",
emptyRoleSemantic = EmptyRoleSemantic.DENY)
}
)
public static class MethodConstraintServlet extends TestServlet {
private static final long serialVersionUID = 1L;
}
@ServletSecurity(httpMethodConstraints = {
@HttpMethodConstraint(value="POST",rolesAllowed = "testRole")
}
)
public static class UncoveredGetServlet extends TestServlet {
private static final long serialVersionUID = 1L;
}
@ServletSecurity(@HttpConstraint(rolesAllowed = "testRole"))
public static class RoleAllowServlet extends TestServlet {
private static final long serialVersionUID = 1L;
}
@ServletSecurity(@HttpConstraint(rolesAllowed = "otherRole"))
public static class RoleDenyServlet extends TestServlet {
private static final long serialVersionUID = 1L;
}
public static class SCI implements ServletContainerInitializer {
private Servlet servlet;
private boolean createServlet;
public SCI(Servlet servlet, boolean createServlet) {
this.servlet = servlet;
this.createServlet = createServlet;
}
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException {
Servlet s;
if (createServlet) {
s = ctx.createServlet(servlet.getClass());
} else {
s = servlet;
}
ServletRegistration.Dynamic r = ctx.addServlet("servlet", s);
r.addMapping("/");
}
}
public static final int BUG51445_THREAD_COUNT = 5;
public static CountDownLatch latch = null;
public static final AtomicInteger counter = new AtomicInteger(0);
public static void initLatch() {
latch = new CountDownLatch(BUG51445_THREAD_COUNT);
}
@Test
public void testBug51445AddServlet() throws Exception {
initLatch();
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "Bug51445", new Bug51445Servlet());
ctx.addServletMappingDecoded("/", "Bug51445");
tomcat.start();
// Start the threads
Bug51445Thread[] threads = new Bug51445Thread[5];
for (int i = 0; i < BUG51445_THREAD_COUNT; i ++) {
threads[i] = new Bug51445Thread(getPort());
threads[i].start();
}
// Wait for threads to finish
for (int i = 0; i < BUG51445_THREAD_COUNT; i ++) {
threads[i].join();
}
Set<String> servlets = new HashSet<>();
// Output the result
for (int i = 0; i < BUG51445_THREAD_COUNT; i ++) {
System.out.println(threads[i].getResult());
}
// Check the result
for (int i = 0; i < BUG51445_THREAD_COUNT; i ++) {
String[] results = threads[i].getResult().split(",");
Assert.assertEquals(2, results.length);
Assert.assertEquals("10", results[0]);
Assert.assertFalse(servlets.contains(results[1]));
servlets.add(results[1]);
}
}
@Test
public void testBug51445AddChild() throws Exception {
initLatch();
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
StandardWrapper wrapper = new StandardWrapper();
wrapper.setServletName("Bug51445");
wrapper.setServletClass(Bug51445Servlet.class.getName());
ctx.addChild(wrapper);
ctx.addServletMappingDecoded("/", "Bug51445");
tomcat.start();
// Start the threads
Bug51445Thread[] threads = new Bug51445Thread[5];
for (int i = 0; i < BUG51445_THREAD_COUNT; i ++) {
threads[i] = new Bug51445Thread(getPort());
threads[i].start();
}
// Wait for threads to finish
for (int i = 0; i < BUG51445_THREAD_COUNT; i ++) {
threads[i].join();
}
Set<String> servlets = new HashSet<>();
// Output the result
for (int i = 0; i < BUG51445_THREAD_COUNT; i ++) {
System.out.println(threads[i].getResult());
}
// Check the result
for (int i = 0; i < BUG51445_THREAD_COUNT; i ++) {
String[] results = threads[i].getResult().split(",");
Assert.assertEquals(2, results.length);
Assert.assertEquals("10", results[0]);
Assert.assertFalse(servlets.contains(results[1]));
servlets.add(results[1]);
}
}
private static class Bug51445Thread extends Thread {
private int port;
private String result;
public Bug51445Thread(int port) {
this.port = port;
}
@Override
public void run() {
try {
ByteChunk res = getUrl("http://localhost:" + port + "/");
result = res.toString();
} catch (IOException ioe) {
result = ioe.getMessage();
}
}
public String getResult() {
return result;
}
}
/**
* SingleThreadModel servlet that sets a value in the init() method.
*/
@SuppressWarnings("deprecation")
public static class Bug51445Servlet extends HttpServlet
implements javax.servlet.SingleThreadModel {
private static final long serialVersionUID = 1L;
private static final long LATCH_TIMEOUT = 60;
private int data = 0;
private int counter;
public Bug51445Servlet() {
// Use this rather than hashCode since in some environments,
// multiple instances of this object were created with the same
// hashCode
counter = TestStandardWrapper.counter.incrementAndGet();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
boolean latchAwaitResult = false;
// Ensure all threads have their own instance of the servlet
latch.countDown();
try {
latchAwaitResult = latch.await(LATCH_TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// Ignore
}
resp.setContentType("text/plain");
if (latchAwaitResult) {
resp.getWriter().print(data);
} else {
resp.getWriter().print("Latch await failed");
}
resp.getWriter().print(",");
resp.getWriter().print(counter);
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
data = 10;
}
}
}

View File

@@ -0,0 +1,482 @@
/*
* 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.core;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.SimpleHttpClient;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
public class TestSwallowAbortedUploads extends TomcatBaseTest {
private static Log log = LogFactory.getLog(TestSwallowAbortedUploads.class);
/*
* Test whether size limited uploads correctly handle connection draining.
*/
public Exception doAbortedUploadTest(AbortedUploadClient client, boolean limited,
boolean swallow) {
Exception ex = client.doRequest(limited, swallow);
if (log.isDebugEnabled()) {
log.debug("Response line: " + client.getResponseLine());
log.debug("Response headers: " + client.getResponseHeaders());
log.debug("Response body: " + client.getResponseBody());
if (ex != null) {
log.debug("Exception in client: ", ex);
}
}
return ex;
}
/*
* Test whether aborted POST correctly handle connection draining.
*/
public Exception doAbortedPOSTTest(AbortedPOSTClient client, int status,
boolean swallow) {
Exception ex = client.doRequest(status, swallow);
if (log.isDebugEnabled()) {
log.debug("Response line: " + client.getResponseLine());
log.debug("Response headers: " + client.getResponseHeaders());
log.debug("Response body: " + client.getResponseBody());
if (ex != null) {
log.info("Exception in client: ", ex);
}
}
return ex;
}
@Test
public void testAbortedUploadUnlimitedSwallow() {
log.info("Unlimited, swallow enabled");
AbortedUploadClient client = new AbortedUploadClient();
Exception ex = doAbortedUploadTest(client, false, true);
Assert.assertNull("Unlimited upload with swallow enabled generates client exception",
ex);
Assert.assertTrue("Unlimited upload with swallow enabled returns error status code",
client.isResponse200());
client.reset();
}
@Test
public void testAbortedUploadUnlimitedNoSwallow() {
log.info("Unlimited, swallow disabled");
AbortedUploadClient client = new AbortedUploadClient();
Exception ex = doAbortedUploadTest(client, false, false);
Assert.assertNull("Unlimited upload with swallow disabled generates client exception",
ex);
Assert.assertTrue("Unlimited upload with swallow disabled returns error status code",
client.isResponse200());
client.reset();
}
@Test
public void testAbortedUploadLimitedSwallow() {
log.info("Limited, swallow enabled");
AbortedUploadClient client = new AbortedUploadClient();
Exception ex = doAbortedUploadTest(client, true, true);
Assert.assertNull("Limited upload with swallow enabled generates client exception",
ex);
Assert.assertTrue("Limited upload with swallow enabled returns non-500 status code",
client.isResponse500());
client.reset();
}
@Test
public void testAbortedUploadLimitedNoSwallow() {
log.info("Limited, swallow disabled");
AbortedUploadClient client = new AbortedUploadClient();
Exception ex = doAbortedUploadTest(client, true, false);
Assert.assertTrue("Limited upload with swallow disabled does not generate client exception",
ex instanceof java.net.SocketException);
client.reset();
}
@Test
public void testAbortedPOSTOKSwallow() {
log.info("Aborted (OK), swallow enabled");
AbortedPOSTClient client = new AbortedPOSTClient();
Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_OK, true);
Assert.assertNull("Unlimited upload with swallow enabled generates client exception",
ex);
Assert.assertTrue("Unlimited upload with swallow enabled returns error status code",
client.isResponse200());
client.reset();
}
@Test
public void testAbortedPOSTOKNoSwallow() {
log.info("Aborted (OK), swallow disabled");
AbortedPOSTClient client = new AbortedPOSTClient();
Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_OK, false);
Assert.assertNull("Unlimited upload with swallow disabled generates client exception",
ex);
Assert.assertTrue("Unlimited upload with swallow disabled returns error status code",
client.isResponse200());
client.reset();
}
@Test
public void testAbortedPOST413Swallow() {
log.info("Aborted (413), swallow enabled");
AbortedPOSTClient client = new AbortedPOSTClient();
Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, true);
Assert.assertNull("Limited upload with swallow enabled generates client exception",
ex);
Assert.assertTrue("Limited upload with swallow enabled returns error status code",
client.isResponse413());
client.reset();
}
@Test
public void testAbortedPOST413NoSwallow() {
log.info("Aborted (413), swallow disabled");
AbortedPOSTClient client = new AbortedPOSTClient();
Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, false);
Assert.assertTrue("Limited upload with swallow disabled does not generate client exception",
ex instanceof java.net.SocketException);
client.reset();
}
@MultipartConfig
private static class AbortedUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
PrintWriter out = resp.getWriter();
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
StringBuilder sb = new StringBuilder();
try {
Collection<Part> c = req.getParts();
if (c == null) {
log.debug("Count: -1");
sb.append("Count: -1\n");
} else {
log.debug("Count: " + c.size());
sb.append("Count: " + c.size() + "\n");
for (Part p : c) {
log.debug("Name: " + p.getName() + ", Size: "
+ p.getSize());
sb.append("Name: " + p.getName() + ", Size: "
+ p.getSize() + "\n");
}
}
} catch (IllegalStateException ex) {
log.debug("IllegalStateException during getParts()");
sb.append("IllegalStateException during getParts()\n");
resp.setStatus(500);
} catch (Throwable ex) {
log.error("Exception during getParts()", ex);
sb.append(ex);
resp.setStatus(500);
}
out.print(sb.toString());
resp.flushBuffer();
}
}
/**
* Test no connection draining when upload too large
*/
private class AbortedUploadClient extends SimpleHttpClient {
private static final String URI = "/uploadAborted";
private static final String servletName = "uploadAborted";
private static final int limitSize = 100;
private static final int hugeSize = 10000000;
private Context context;
private synchronized void init(boolean limited, boolean swallow)
throws Exception {
Tomcat tomcat = getTomcatInstance();
context = tomcat.addContext("", TEMP_DIR);
Wrapper w;
w = Tomcat.addServlet(context, servletName,
new AbortedUploadServlet());
// Tomcat.addServlet does not respect annotations, so we have
// to set our own MultipartConfigElement.
// Choose upload file size limit.
if (limited) {
w.setMultipartConfigElement(new MultipartConfigElement("",
limitSize, -1, -1));
} else {
w.setMultipartConfigElement(new MultipartConfigElement(""));
}
context.addServletMappingDecoded(URI, servletName);
context.setSwallowAbortedUploads(swallow);
Connector c = tomcat.getConnector();
c.setMaxPostSize(2 * hugeSize);
Assert.assertTrue(c.setProperty("maxSwallowSize", Integer.toString(hugeSize)));
tomcat.start();
setPort(c.getLocalPort());
}
private Exception doRequest(boolean limited, boolean swallow) {
char body[] = new char[hugeSize];
Arrays.fill(body, 'X');
try {
init(limited, swallow);
// Open connection
connect();
// Send specified request body using method
String[] request;
String boundary = "--simpleboundary";
StringBuilder sb = new StringBuilder();
sb.append("--");
sb.append(boundary);
sb.append(CRLF);
sb.append("Content-Disposition: form-data; name=\"part\"");
sb.append(CRLF);
sb.append(CRLF);
sb.append(body);
sb.append(CRLF);
sb.append("--");
sb.append(boundary);
sb.append("--");
sb.append(CRLF);
// Re-encode the content so that bytes = characters
String content = new String(sb.toString().getBytes("UTF-8"),
"ASCII");
request = new String[] { "POST http://localhost:" + getPort() + URI + " HTTP/1.1" + CRLF
+ "Host: localhost:" + getPort() + CRLF
+ "Connection: close" + CRLF
+ "Content-Type: multipart/form-data; boundary=" + boundary + CRLF
+ "Content-Length: " + content.length() + CRLF
+ CRLF
+ content + CRLF };
setRequest(request);
processRequest(); // blocks until response has been read
// Close the connection
disconnect();
} catch (Exception e) {
return e;
}
return null;
}
@Override
public boolean isResponseBodyOK() {
return false; // Don't care
}
}
private static class AbortedPOSTServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final int status;
public AbortedPOSTServlet(int status) {
this.status = status;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.setStatus(status);
PrintWriter out = resp.getWriter();
out.print("OK");
resp.flushBuffer();
}
}
/**
* Test no connection draining when upload too large
*/
private class AbortedPOSTClient extends SimpleHttpClient {
private static final String URI = "/uploadAborted";
private static final String servletName = "uploadAborted";
private static final int hugeSize = 10000000;
private Context context;
private synchronized void init(int status, boolean swallow)
throws Exception {
Tomcat tomcat = getTomcatInstance();
context = tomcat.addContext("", TEMP_DIR);
AbortedPOSTServlet servlet = new AbortedPOSTServlet(status);
Tomcat.addServlet(context, servletName, servlet);
context.addServletMappingDecoded(URI, servletName);
context.setSwallowAbortedUploads(swallow);
tomcat.start();
Connector c = tomcat.getConnector();
c.setMaxPostSize(2 * hugeSize);
Assert.assertTrue(c.setProperty("maxSwallowSize", Integer.toString(hugeSize)));
setPort(c.getLocalPort());
}
private Exception doRequest(int status, boolean swallow) {
char body[] = new char[hugeSize];
Arrays.fill(body, 'X');
try {
init(status, swallow);
// Open connection
connect();
// Send specified request body using method
String[] request;
String content = new String(body);
request = new String[] { "POST http://localhost:" + getPort() + URI + " HTTP/1.1" + CRLF
+ "Host: localhost:" + getPort() + CRLF
+ "Connection: close" + CRLF
+ "Content-Length: " + content.length() + CRLF
+ CRLF
+ content + CRLF };
setRequest(request);
processRequest(); // blocks until response has been read
// Close the connection
disconnect();
} catch (Exception e) {
return e;
}
return null;
}
@Override
public boolean isResponseBodyOK() {
return false; // Don't care
}
}
@Test
public void testChunkedPUTLimit() throws Exception {
doTestChunkedPUT(true);
}
@Test
public void testChunkedPUTNoLimit() throws Exception {
doTestChunkedPUT(false);
}
public void doTestChunkedPUT(boolean limit) throws Exception {
Tomcat tomcat = getTomcatInstance();
tomcat.addContext("", TEMP_DIR);
// No need for target to exist.
if (!limit) {
tomcat.getConnector().setAttribute("maxSwallowSize", "-1");
}
tomcat.start();
Exception writeEx = null;
Exception readEx = null;
String responseLine = null;
try (Socket conn = new Socket("localhost", getPort())) {
Writer writer = new OutputStreamWriter(
conn.getOutputStream(), StandardCharsets.US_ASCII);
writer.write("PUT /does-not-exist HTTP/1.1\r\n");
writer.write("Host: any\r\n");
writer.write("Transfer-encoding: chunked\r\n");
writer.write("\r\n");
// Smarter than the typical client. Attempts to read the response
// even if the request is not fully written.
try {
// Write (or try to write) 16MB
for (int i = 0; i < 1024 * 1024; i++) {
writer.write("10\r\n");
writer.write("0123456789ABCDEF\r\n");
}
} catch (Exception e) {
writeEx = e;
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
conn.getInputStream(), StandardCharsets.US_ASCII));
responseLine = reader.readLine();
} catch (IOException e) {
readEx = e;
}
}
if (limit) {
Assert.assertNotNull(writeEx);
} else {
Assert.assertNull(writeEx);
Assert.assertNull(readEx);
Assert.assertNotNull(responseLine);
Assert.assertTrue(responseLine.contains("404"));
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.core;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class TesterTldListener implements ServletContextListener {
private static StringBuilder log = new StringBuilder();
public static String getLog() {
return log.toString();
}
private ServletContext servletContext;
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext sc = sce.getServletContext();
servletContext = sc;
// Try and use one of the Servlet 3.0 methods that should be blocked
try {
sc.getEffectiveMajorVersion();
log.append("FAIL-01");
} catch (UnsupportedOperationException uoe) {
log.append("PASS-01");
} catch (Exception e) {
log.append("FAIL-02");
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Bug 57446. Same ServletContext should be presented as at init
if (servletContext == sce.getServletContext()) {
log.append("PASS-02");
} else {
//log.append("FAIL-03");
}
}
}