init
This commit is contained in:
331
test/org/apache/catalina/core/TestApplicationContext.java
Normal file
331
test/org/apache/catalina/core/TestApplicationContext.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
357
test/org/apache/catalina/core/TestApplicationHttpRequest.java
Normal file
357
test/org/apache/catalina/core/TestApplicationHttpRequest.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
376
test/org/apache/catalina/core/TestApplicationMapping.java
Normal file
376
test/org/apache/catalina/core/TestApplicationMapping.java
Normal 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() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
3005
test/org/apache/catalina/core/TestAsyncContextImpl.java
Normal file
3005
test/org/apache/catalina/core/TestAsyncContextImpl.java
Normal file
File diff suppressed because it is too large
Load Diff
151
test/org/apache/catalina/core/TestAsyncContextImplDispatch.java
Normal file
151
test/org/apache/catalina/core/TestAsyncContextImplDispatch.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
378
test/org/apache/catalina/core/TestAsyncContextStateChanges.java
Normal file
378
test/org/apache/catalina/core/TestAsyncContextStateChanges.java
Normal 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
|
||||
}
|
||||
}
|
||||
163
test/org/apache/catalina/core/TestDefaultInstanceManager.java
Normal file
163
test/org/apache/catalina/core/TestDefaultInstanceManager.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
184
test/org/apache/catalina/core/TestNamingContextListener.java
Normal file
184
test/org/apache/catalina/core/TestNamingContextListener.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1056
test/org/apache/catalina/core/TestStandardContext.java
Normal file
1056
test/org/apache/catalina/core/TestStandardContext.java
Normal file
File diff suppressed because it is too large
Load Diff
118
test/org/apache/catalina/core/TestStandardContextAliases.java
Normal file
118
test/org/apache/catalina/core/TestStandardContextAliases.java
Normal 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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
282
test/org/apache/catalina/core/TestStandardContextResources.java
Normal file
282
test/org/apache/catalina/core/TestStandardContextResources.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
185
test/org/apache/catalina/core/TestStandardContextValve.java
Normal file
185
test/org/apache/catalina/core/TestStandardContextValve.java
Normal 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");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
169
test/org/apache/catalina/core/TestStandardHostValve.java
Normal file
169
test/org/apache/catalina/core/TestStandardHostValve.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
663
test/org/apache/catalina/core/TestStandardWrapper.java
Normal file
663
test/org/apache/catalina/core/TestStandardWrapper.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
482
test/org/apache/catalina/core/TestSwallowAbortedUploads.java
Normal file
482
test/org/apache/catalina/core/TestSwallowAbortedUploads.java
Normal 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
59
test/org/apache/catalina/core/TesterTldListener.java
Normal file
59
test/org/apache/catalina/core/TesterTldListener.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user