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

View File

@@ -0,0 +1,331 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.descriptor.JspPropertyGroupDescriptor;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestApplicationContext extends TomcatBaseTest {
@Test
public void testBug53257() throws Exception {
getTomcatInstanceTestWebapp(false, true);
ByteChunk res = getUrl("http://localhost:" + getPort() +
"/test/bug53257/index.jsp");
String result = res.toString();
String[] lines = result.split("\n");
for (String line : lines) {
if (line.startsWith("FAIL")) {
Assert.fail(line);
}
}
}
@Test
public void testBug53467() throws Exception {
getTomcatInstanceTestWebapp(false, true);
ByteChunk res = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() +
"/test/bug5nnnn/bug53467%5D.jsp", res, null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertTrue(res.toString().contains("<p>OK</p>"));
}
@Test(expected = IllegalArgumentException.class)
public void testAddFilterWithFilterNameNull() throws LifecycleException {
getServletContext().addFilter(null, (Filter) null);
}
@Test(expected = IllegalArgumentException.class)
public void testAddFilterWithFilterNameEmptyString() throws LifecycleException {
getServletContext().addFilter("", (Filter) null);
}
@Test(expected = IllegalArgumentException.class)
public void testAddServletWithServletNameNull() throws LifecycleException {
getServletContext().addServlet(null, (Servlet) null);
}
@Test(expected = IllegalArgumentException.class)
public void testAddServletWithServletNameEmptyString() throws LifecycleException {
getServletContext().addServlet("", (Servlet) null);
}
@Test
public void testGetJspConfigDescriptor() throws Exception {
Tomcat tomcat = getTomcatInstanceTestWebapp(false, false);
StandardContext standardContext =
(StandardContext) tomcat.getHost().findChildren()[0];
ServletContext servletContext = standardContext.getServletContext();
Assert.assertNull(servletContext.getJspConfigDescriptor());
tomcat.start();
Assert.assertNotNull(servletContext.getJspConfigDescriptor());
}
@Test
public void testJspPropertyGroupsAreIsolated() throws Exception {
Tomcat tomcat = getTomcatInstanceTestWebapp(false, false);
StandardContext standardContext =
(StandardContext) tomcat.getHost().findChildren()[0];
ServletContext servletContext = standardContext.getServletContext();
Assert.assertNull(servletContext.getJspConfigDescriptor());
tomcat.start();
JspConfigDescriptor jspConfigDescriptor =
servletContext.getJspConfigDescriptor();
Collection<JspPropertyGroupDescriptor> propertyGroups =
jspConfigDescriptor.getJspPropertyGroups();
Assert.assertFalse(propertyGroups.isEmpty());
propertyGroups.clear();
jspConfigDescriptor = servletContext.getJspConfigDescriptor();
propertyGroups = jspConfigDescriptor.getJspPropertyGroups();
Assert.assertFalse(propertyGroups.isEmpty());
}
private ServletContext getServletContext() throws LifecycleException {
Tomcat tomcat = getTomcatInstanceTestWebapp(false, false);
StandardContext standardContext =
(StandardContext) tomcat.getHost().findChildren()[0];
return standardContext.getServletContext();
}
@Test(expected = IllegalStateException.class)
public void testSetInitParameter() throws Exception {
getTomcatInstance().start();
getServletContext().setInitParameter("name", "value");
}
/*
* Cross-context requests with parallel deployment
*/
@Test
public void testBug57190() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context foo1 = new StandardContext();
foo1.setName("/foo##1");
foo1.setPath("/foo");
foo1.setWebappVersion("1");
foo1.addLifecycleListener(new FixContextListener());
foo1.addLifecycleListener(new SetIdListener("foo1"));
tomcat.getHost().addChild(foo1);
Context foo2 = new StandardContext();
foo2.setName("/foo##2");
foo2.setPath("/foo");
foo2.setWebappVersion("2");
foo2.addLifecycleListener(new FixContextListener());
foo2.addLifecycleListener(new SetIdListener("foo2"));
tomcat.getHost().addChild(foo2);
Context bar = tomcat.addContext("/bar", null);
bar.addLifecycleListener(new SetIdListener("bar"));
Context ctx = tomcat.addContext("", null);
ctx.addLifecycleListener(new SetIdListener("ROOT"));
ctx.setCrossContext(true);
Tomcat.addServlet(ctx, "Bug57190Servlet", new Bug57190Servlet());
ctx.addServletMappingDecoded("/", "Bug57190Servlet");
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + "/");
String body = res.toString();
Assert.assertTrue(body, body.contains("01-bar"));
Assert.assertTrue(body, body.contains("02-foo2"));
Assert.assertTrue(body, body.contains("03-foo1"));
Assert.assertTrue(body, body.contains("04-foo2"));
Assert.assertTrue(body, body.contains("05-foo2"));
Assert.assertTrue(body, body.contains("06-ROOT"));
Assert.assertTrue(body, body.contains("07-ROOT"));
Assert.assertTrue(body, body.contains("08-foo2"));
Assert.assertTrue(body, body.contains("09-ROOT"));
}
private static class Bug57190Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
ServletContext sc = req.getServletContext();
pw.println("01-" + sc.getContext("/bar").getInitParameter("id"));
pw.println("02-" + sc.getContext("/foo").getInitParameter("id"));
pw.println("03-" + sc.getContext("/foo##1").getInitParameter("id"));
pw.println("04-" + sc.getContext("/foo##2").getInitParameter("id"));
pw.println("05-" + sc.getContext("/foo##3").getInitParameter("id"));
pw.println("06-" + sc.getContext("/unknown").getInitParameter("id"));
pw.println("07-" + sc.getContext("/").getInitParameter("id"));
pw.println("08-" + sc.getContext("/foo/bar").getInitParameter("id"));
pw.println("09-" + sc.getContext("/football").getInitParameter("id"));
}
}
private static class SetIdListener implements LifecycleListener {
private final String id;
public SetIdListener(String id) {
this.id = id;
}
@Override
public void lifecycleEvent(LifecycleEvent event) {
if (Lifecycle.CONFIGURE_START_EVENT.equals(event.getType())) {
((Context) event.getSource()).getServletContext().setInitParameter("id", id);
}
}
}
/*
* The expectation is that you can set a context attribute on
* ServletContextB from ServletContextA and then access that attribute via
* a cross-context dispatch to ServletContextB.
*/
@Test
public void testCrossContextSetAttribute() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx2 = tomcat.addContext("/second", null);
GetAttributeServlet getAttributeServlet = new GetAttributeServlet();
Tomcat.addServlet(ctx2, "getAttributeServlet", getAttributeServlet);
ctx2.addServletMappingDecoded("/test", "getAttributeServlet");
// No file system docBase required
Context ctx1 = tomcat.addContext("/first", null);
ctx1.setCrossContext(true);
SetAttributeServlet setAttributeServlet = new SetAttributeServlet("/test", "/second");
Tomcat.addServlet(ctx1, "setAttributeServlet", setAttributeServlet);
ctx1.addServletMappingDecoded("/test", "setAttributeServlet");
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + "/first/test", bc, null);
Assert.assertEquals(200, rc);
Assert.assertEquals("01-PASS", bc.toString());
}
private static class SetAttributeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String ATTRIBUTE_NAME = "setAttributeTest";
private static final String ATTRIBUTE_VALUE = "abcde";
private final String targetContextPath;
private final String targetPath;
public SetAttributeServlet(String targetPath, String targetContextPath) {
this.targetPath = targetPath;
this.targetContextPath = targetContextPath;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
ServletContext sc;
if (targetContextPath == null) {
sc = req.getServletContext();
} else {
sc = req.getServletContext().getContext(targetContextPath);
}
sc.setAttribute(ATTRIBUTE_NAME, ATTRIBUTE_VALUE);
sc.getRequestDispatcher(targetPath).forward(req, resp);
}
}
private static class GetAttributeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
PrintWriter pw = resp.getWriter();
String value = (String) req.getServletContext().getAttribute(
SetAttributeServlet.ATTRIBUTE_NAME);
if (SetAttributeServlet.ATTRIBUTE_VALUE.equals(value)) {
pw.print("01-PASS");
} else {
pw.print("01-FAIL");
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.apache.catalina.startup.TomcatBaseTest;
@RunWith(value = Parameterized.class)
public class TestApplicationContextStripPathParams extends TomcatBaseTest {
private final String input;
private final String expectedOutput;
public TestApplicationContextStripPathParams(String input, String expectedOutput) {
this.input = input;
this.expectedOutput = expectedOutput;
}
@Parameters(name = "{index}: input[{0}]")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{ "/foo", "/foo"},
{ "/foo/", "/foo/"},
{ "/foo/bar", "/foo/bar"},
{ "/foo;", "/foo"},
{ "/foo;/", "/foo/"},
{ "/foo;/bar", "/foo/bar"},
{ "/foo;a=1", "/foo"},
{ "/foo;a=1/", "/foo/"},
{ "/foo;a=1/bar", "/foo/bar"},
// Arguably not valid but does the right thing anyway
{ ";/foo", "/foo"},
{ ";a=1/foo", "/foo"},
{ ";/foo/bar", "/foo/bar"},
{ ";/foo;a=1/bar", "/foo/bar"},
});
}
@Test
public void testStringPathParams() {
String output = ApplicationContext.stripPathParams(input);
Assert.assertEquals(expectedOutput, output);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.filters.AddDefaultCharsetFilter;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.modeler.Registry;
public class TestApplicationFilterConfig extends TomcatBaseTest {
@Test
public void testBug54170() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "HelloWorld", new HelloWorldServlet());
ctx.addServletMappingDecoded("/", "HelloWorld");
// Add a filter with a name that should be escaped if used in a JMX
// object name
FilterDef filterDef = new FilterDef();
filterDef.setFilterClass(AddDefaultCharsetFilter.class.getName());
filterDef.setFilterName("bug54170*");
ctx.addFilterDef(filterDef);
tomcat.start();
final MBeanServer mbeanServer =
Registry.getRegistry(null, null).getMBeanServer();
// There should be one Servlet MBean registered
Set<ObjectName> servlets = mbeanServer.queryNames(
new ObjectName("Tomcat:j2eeType=Servlet,*"), null);
Assert.assertEquals(1, servlets.size());
// There should be one Filter MBean registered
Set<ObjectName> filters = mbeanServer.queryNames(
new ObjectName("Tomcat:j2eeType=Filter,*"), null);
Assert.assertEquals(1, filters.size());
}
}

View File

@@ -0,0 +1,357 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestApplicationHttpRequest extends TomcatBaseTest {
/*
* https://bz.apache.org/bugzilla/show_bug.cgi?id=58836
*/
@Test
public void testForwardQueryString01() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
doQueryStringTest(null, "a=b", expected);
}
@Test
public void testForwardQueryString02() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "c" });
doQueryStringTest(null, "a=b&a=c", expected);
}
@Test
public void testForwardQueryString03() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
expected.put("c", new String[] { "d" });
doQueryStringTest(null, "a=b&c=d", expected);
}
@Test
public void testForwardQueryString04() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "d" });
doQueryStringTest(null, "a=b&c=d&a=e", expected);
}
@Test
public void testForwardQueryString05() throws Exception {
// Parameters with no value are assigned a value of the empty string
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "" });
doQueryStringTest(null, "a=b&c&a=e", expected);
}
@Test
public void testOriginalQueryString01() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
doQueryStringTest("a=b", null, expected);
}
@Test
public void testOriginalQueryString02() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "c" });
doQueryStringTest("a=b&a=c", null, expected);
}
@Test
public void testOriginalQueryString03() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
expected.put("c", new String[] { "d" });
doQueryStringTest("a=b&c=d", null, expected);
}
@Test
public void testOriginalQueryString04() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "d" });
doQueryStringTest("a=b&c=d&a=e", null, expected);
}
@Test
public void testOriginalQueryString05() throws Exception {
// Parameters with no value are assigned a value of the empty string
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "" });
doQueryStringTest("a=b&c&a=e", null, expected);
}
@Test
public void testMergeQueryString01() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "z", "b" });
doQueryStringTest("a=b", "a=z", expected);
}
@Test
public void testMergeQueryString02() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "z", "b", "e" });
expected.put("c", new String[] { "" });
doQueryStringTest("a=b&c&a=e", "a=z", expected);
}
@Test
public void testMergeQueryString03() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b", "e" });
expected.put("c", new String[] { "z", "" });
doQueryStringTest("a=b&c&a=e", "c=z", expected);
}
@Test
public void testMergeQueryString04() throws Exception {
Map<String,String[]> expected = new HashMap<>();
expected.put("a", new String[] { "", "b", "e" });
expected.put("c", new String[] { "" });
doQueryStringTest("a=b&c&a=e", "a", expected);
}
@Test
public void testMergeQueryString05() throws Exception {
// https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%81%D1%82
// "Test" = "Test"
String test = "\u0422\u0435\u0441\u0442";
String query = test + "=%D0%A2%D0%B5%D1%81%D1%82";
Map<String, String[]> expected = new HashMap<>();
expected.put("a", new String[] { "b" });
expected.put(test, new String[] { test });
doQueryStringTest("a=b", query, expected);
}
private void doQueryStringTest(String originalQueryString, String forwardQueryString,
Map<String,String[]> expected) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
if (forwardQueryString == null) {
Tomcat.addServlet(ctx, "forward", new ForwardServlet("/display"));
} else {
Tomcat.addServlet(ctx, "forward", new ForwardServlet("/display?" + forwardQueryString));
}
ctx.addServletMappingDecoded("/forward", "forward");
Tomcat.addServlet(ctx, "display", new DisplayParameterServlet(expected));
ctx.addServletMappingDecoded("/display", "display");
tomcat.start();
ByteChunk response = new ByteChunk();
StringBuilder target = new StringBuilder("http://localhost:");
target.append(getPort());
target.append("/forward");
if (originalQueryString != null) {
target.append('?');
target.append(originalQueryString);
}
int rc = getUrl(target.toString(), response, null);
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", response.toString());
}
@Test
public void testParameterImmutability() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "forward", new ForwardServlet("/modify"));
ctx.addServletMappingDecoded("/forward", "forward");
Tomcat.addServlet(ctx, "modify", new ModifyParameterServlet());
ctx.addServletMappingDecoded("/modify", "modify");
tomcat.start();
ByteChunk response = new ByteChunk();
StringBuilder target = new StringBuilder("http://localhost:");
target.append(getPort());
target.append("/forward");
int rc = getUrl(target.toString(), response, null);
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", response.toString());
}
private static class ForwardServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final String target;
public ForwardServlet(String target) {
this.target = target;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
req.getRequestDispatcher(target).forward(req, resp);
}
}
private static class DisplayParameterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private Map<String,String[]> expected;
public DisplayParameterServlet(Map<String,String[]> expected) {
this.expected = expected;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter w = resp.getWriter();
Map<String,String[]> actual = req.getParameterMap();
boolean ok = true;
for (Entry<String,String[]> entry : actual.entrySet()) {
String[] expectedValue = expected.get(entry.getKey());
if (expectedValue == null ||
expectedValue.length != entry.getValue().length) {
ok = false;
break;
}
for (int i = 0; i < expectedValue.length; i++) {
if (!expectedValue[i].equals(entry.getValue()[i])) {
ok = false;
break;
}
}
if (!ok) {
break;
}
}
if (ok) {
w.print("OK");
return;
}
boolean firstParam = true;
for (Entry<String,String[]> param : actual.entrySet()) {
if (firstParam) {
firstParam = false;
} else {
w.print(';');
}
w.print(param.getKey());
w.print(':');
boolean firstValue = true;
for (String value : param.getValue()) {
if (firstValue) {
firstValue = false;
} else {
w.print(',');
}
w.print('(');
w.print(value);
w.print(')');
}
}
}
}
private static class ModifyParameterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// Suppress warnings generated because the code is trying to put the
// wrong type of values into the Map
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Map map = req.getParameterMap();
boolean insertWorks;
try {
map.put("test", new Integer[] { Integer.valueOf(0) });
insertWorks = true;
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
insertWorks = false;
}
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter pw = resp.getWriter();
if (insertWorks) {
pw.print("FAIL");
} else {
pw.print("OK");
}
}
}
}

View File

@@ -0,0 +1,376 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.servlet4preview.http.HttpServletMapping;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestApplicationMapping extends TomcatBaseTest {
@Test
public void testContextNonRootMappingContextRoot() throws Exception {
doTestMapping("/dummy", "", "", "", "CONTEXT_ROOT");
}
@Test
public void testContextNonRootMappingDefault() throws Exception {
doTestMapping("/dummy", "/", "/foo", "", "DEFAULT");
}
@Test
public void testContextNonRootMappingExtension() throws Exception {
doTestMapping("/dummy", "*.test", "/foo/bar.test", "foo/bar", "EXTENSION");
}
@Test
public void testContextNonRootMappingExact() throws Exception {
doTestMapping("/dummy", "/foo/bar", "/foo/bar", "foo/bar", "EXACT");
}
@Test
public void testContextNonRootMappingPathNone() throws Exception {
doTestMapping("/dummy", "/foo/bar/*", "/foo/bar", null, "PATH");
}
@Test
public void testContextNonRootMappingPathSeparatorOnly() throws Exception {
doTestMapping("/dummy", "/foo/bar/*", "/foo/bar/", "", "PATH");
}
@Test
public void testContextNonRootMappingPath() throws Exception {
doTestMapping("/dummy", "/foo/bar/*", "/foo/bar/foo2", "foo2", "PATH");
}
@Test
public void testContextRootMappingContextRoot() throws Exception {
doTestMapping("", "", "", "", "CONTEXT_ROOT");
}
@Test
public void testContextRootMappingDefault() throws Exception {
doTestMapping("", "/", "/foo", "", "DEFAULT");
}
@Test
public void testContextRootMappingExtension() throws Exception {
doTestMapping("", "*.test", "/foo/bar.test", "foo/bar", "EXTENSION");
}
@Test
public void testContextRootMappingExact() throws Exception {
doTestMapping("", "/foo/bar", "/foo/bar", "foo/bar", "EXACT");
}
@Test
public void testContextRootMappingPath() throws Exception {
doTestMapping("", "/foo/bar/*", "/foo/bar/foo2", "foo2", "PATH");
}
private void doTestMapping(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
doTestMappingDirect(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingInclude(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingNamedInclude(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingForward(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingNamedForward(contextPath, mapping, requestPath, matchValue, matchType);
tearDown();
setUp();
doTestMappingAsync(contextPath, mapping, requestPath, matchValue, matchType);
}
private void doTestMappingDirect(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded(mapping, "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
}
private void doTestMappingInclude(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Include", new IncludeServlet());
ctx.addServletMappingDecoded(mapping, "Include");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ServletName=[Include]"));
Assert.assertTrue(body, body.contains("IncludeMatchValue=[mapping]"));
Assert.assertTrue(body, body.contains("IncludePattern=[/mapping]"));
Assert.assertTrue(body, body.contains("IncludeMatchType=[EXACT]"));
Assert.assertTrue(body, body.contains("IncludeServletName=[Mapping]"));
}
private void doTestMappingNamedInclude(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Include", new NamedIncludeServlet());
ctx.addServletMappingDecoded(mapping, "Include");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ServletName=[Include]"));
}
private void doTestMappingForward(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Forward", new ForwardServlet());
ctx.addServletMappingDecoded(mapping, "Forward");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[mapping]"));
Assert.assertTrue(body, body.contains("Pattern=[/mapping]"));
Assert.assertTrue(body, body.contains("MatchType=[EXACT]"));
Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
Assert.assertTrue(body, body.contains("ForwardMatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("ForwardPattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("ForwardMatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ForwardServletName=[Forward]"));
}
private void doTestMappingNamedForward(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Tomcat.addServlet(ctx, "Forward", new NamedForwardServlet());
ctx.addServletMappingDecoded(mapping, "Forward");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("Pattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("ServletName=[Forward]"));
}
private void doTestMappingAsync(String contextPath, String mapping, String requestPath,
String matchValue, String matchType) throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext(contextPath, null);
Wrapper w = Tomcat.addServlet(ctx, "Async", new AsyncServlet());
w.setAsyncSupported(true);
ctx.addServletMappingDecoded(mapping, "Async");
Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
ctx.addServletMappingDecoded("/mapping", "Mapping");
tomcat.start();
ByteChunk bc = getUrl("http://localhost:" + getPort() + contextPath + requestPath);
String body = bc.toString();
Assert.assertTrue(body, body.contains("MatchValue=[mapping]"));
Assert.assertTrue(body, body.contains("Pattern=[/mapping]"));
Assert.assertTrue(body, body.contains("MatchType=[EXACT]"));
Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
Assert.assertTrue(body, body.contains("AsyncMatchValue=[" + matchValue + "]"));
Assert.assertTrue(body, body.contains("AsyncPattern=[" + mapping + "]"));
Assert.assertTrue(body, body.contains("AsyncMatchType=[" + matchType + "]"));
Assert.assertTrue(body, body.contains("AsyncServletName=[Async]"));
}
private static class IncludeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher rd = req.getRequestDispatcher("/mapping");
rd.include(req, resp);
}
}
private static class NamedIncludeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher rd = req.getServletContext().getNamedDispatcher("Mapping");
rd.include(req, resp);
}
}
private static class NamedForwardServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher rd = req.getServletContext().getNamedDispatcher("Mapping");
rd.forward(req, resp);
}
}
private static class ForwardServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher rd = req.getRequestDispatcher("/mapping");
rd.forward(req, resp);
}
}
private static class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
AsyncContext ac = req.startAsync();
ac.dispatch("/mapping");
}
}
private static class MappingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain;charset=UTF-8");
PrintWriter pw = resp.getWriter();
HttpServletMapping mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
req).getHttpServletMapping();
pw.println("MatchValue=[" + mapping.getMatchValue() + "]");
pw.println("Pattern=[" + mapping.getPattern() + "]");
pw.println("MatchType=[" + mapping.getMappingMatch() + "]");
pw.println("ServletName=[" + mapping.getServletName() + "]");
HttpServletMapping includeMapping = (HttpServletMapping) req.getAttribute(
ApplicationDispatcher.INCLUDE_MAPPING);
if (includeMapping != null) {
pw.println("IncludeMatchValue=[" + includeMapping.getMatchValue() + "]");
pw.println("IncludePattern=[" + includeMapping.getPattern() + "]");
pw.println("IncludeMatchType=[" + includeMapping.getMappingMatch() + "]");
pw.println("IncludeServletName=[" + includeMapping.getServletName() + "]");
}
HttpServletMapping forwardMapping = (HttpServletMapping) req.getAttribute(
ApplicationDispatcher.FORWARD_MAPPING);
if (forwardMapping != null) {
pw.println("ForwardMatchValue=[" + forwardMapping.getMatchValue() + "]");
pw.println("ForwardPattern=[" + forwardMapping.getPattern() + "]");
pw.println("ForwardMatchType=[" + forwardMapping.getMappingMatch() + "]");
pw.println("ForwardServletName=[" + forwardMapping.getServletName() + "]");
}
HttpServletMapping asyncMapping = (HttpServletMapping) req.getAttribute(
ApplicationDispatcher.ASYNC_MAPPING);
if (asyncMapping != null) {
pw.println("AsyncMatchValue=[" + asyncMapping.getMatchValue() + "]");
pw.println("AsyncPattern=[" + asyncMapping.getPattern() + "]");
pw.println("AsyncMatchType=[" + asyncMapping.getMappingMatch() + "]");
pw.println("AsyncServletName=[" + asyncMapping.getServletName() + "]");
}
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.junit.Assert;
import org.junit.Test;
public class TestApplicationPushBuilder {
@Test
public void test01() {
doTest("foo", StandardCharsets.UTF_8, "foo");
}
@Test
public void test02() {
doTest("/foo", StandardCharsets.UTF_8, "/foo");
}
@Test
public void test03() {
doTest("%20foo", StandardCharsets.UTF_8, " foo");
}
@Test
public void test04() {
doTest("fo%20o", StandardCharsets.UTF_8, "fo o");
}
@Test
public void test05() {
doTest("foo%20", StandardCharsets.UTF_8, "foo ");
}
@Test
public void test06() {
doTest("%21foo", StandardCharsets.UTF_8, "!foo");
}
@Test
public void test07() {
doTest("fo%21o", StandardCharsets.UTF_8, "fo!o");
}
@Test
public void test08() {
doTest("foo%21", StandardCharsets.UTF_8, "foo!");
}
private void doTest(String input, Charset charset, String expected) {
String result = ApplicationPushBuilder.decode(input, charset);
Assert.assertEquals(expected, result);
}
}

View File

@@ -0,0 +1,140 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.catalina.LifecycleState;
public class TestApplicationSessionCookieConfig {
private ApplicationSessionCookieConfig applicationSessionCookieConfig;
private final CustomContext context = new CustomContext();
@Before
public void setUp() throws Exception {
applicationSessionCookieConfig = new ApplicationSessionCookieConfig(
context);
}
@Test
public void testSetCommentInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setComment("test");
Assert.assertTrue(applicationSessionCookieConfig.getComment().equals("test"));
}
@Test(expected = IllegalStateException.class)
public void testSetCommentNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setComment("test");
}
@Test
public void testSetDomainInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setDomain("test");
Assert.assertTrue(applicationSessionCookieConfig.getDomain().equals("test"));
}
@Test(expected = IllegalStateException.class)
public void testSetDomainNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setDomain("test");
}
@Test
public void testSetHttpOnlyInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setHttpOnly(true);
Assert.assertTrue(applicationSessionCookieConfig.isHttpOnly());
}
@Test(expected = IllegalStateException.class)
public void testSetHttpOnlyNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setHttpOnly(true);
}
@Test
public void testSetMaxAgeInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setMaxAge(1);
Assert.assertTrue(applicationSessionCookieConfig.getMaxAge() == 1);
}
@Test(expected = IllegalStateException.class)
public void testSetMaxAgeNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setMaxAge(1);
}
@Test
public void testSetNameInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setName("test");
Assert.assertTrue(applicationSessionCookieConfig.getName().equals("test"));
}
@Test(expected = IllegalStateException.class)
public void testSetNameNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setName("test");
}
@Test
public void testSetPathInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setPath("test");
Assert.assertTrue(applicationSessionCookieConfig.getPath().equals("test"));
}
@Test(expected = IllegalStateException.class)
public void testSetPathNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setPath("test");
}
@Test
public void testSetSecureInitPhase() {
context.setState(LifecycleState.STARTING_PREP);
applicationSessionCookieConfig.setSecure(true);
Assert.assertTrue(applicationSessionCookieConfig.isSecure());
}
@Test(expected = IllegalStateException.class)
public void testSetSecureNotInitPhase() {
context.setState(LifecycleState.STARTED);
applicationSessionCookieConfig.setSecure(true);
}
private static class CustomContext extends StandardContext {
private volatile LifecycleState state;
@Override
public LifecycleState getState() {
return state;
}
@Override
public synchronized void setState(LifecycleState state) {
this.state = state;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,151 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Written for the specific test case of async Servlet, dispatches to sync
* Servlet that then tries to call startAsync() but covers all combinations
* for completeness.
*/
@RunWith(Parameterized.class)
public class TestAsyncContextImplDispatch extends TomcatBaseTest {
@Parameterized.Parameters(name = "{index}: tgt-sup [{0}], dis-sup [{1}], dis-st [{2}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
for (Boolean targetAsyncSupported : booleans) {
for (Boolean dispatchAsyncSupported : booleans) {
for (Boolean dispatchAsyncStart : booleans) {
Boolean allowed = Boolean.valueOf(!dispatchAsyncStart.booleanValue() ||
targetAsyncSupported.booleanValue() && dispatchAsyncSupported.booleanValue() &&
dispatchAsyncStart.booleanValue());
parameterSets.add(new Object[] { targetAsyncSupported, dispatchAsyncSupported, dispatchAsyncStart, allowed} );
}
}
}
return parameterSets;
}
@Parameter(0)
public boolean targetAsyncSupported;
@Parameter(1)
public boolean dispatchAsyncSupported;
@Parameter(2)
public boolean dispatchAsyncStart;
@Parameter(3)
public boolean allowed;
@Test
public void testSendError() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Wrapper w1 = Tomcat.addServlet(ctx, "target", new TesterServlet());
w1.setAsyncSupported(targetAsyncSupported);
ctx.addServletMappingDecoded("/target", "target");
Wrapper w2 = Tomcat.addServlet(ctx, "dispatch", new TesterDispatchServlet(dispatchAsyncStart));
w2.setAsyncSupported(dispatchAsyncSupported);
ctx.addServletMappingDecoded("/dispatch", "dispatch");
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc;
rc = getUrl("http://localhost:" + getPort() + "/target", bc, null, null);
String body = bc.toString();
if (allowed) {
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", body);
} else {
Assert.assertEquals(500, rc);
}
}
public static class TesterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.getRequestDispatcher("/dispatch").forward(req, resp);
}
}
public static class TesterDispatchServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final boolean start;
public TesterDispatchServlet(boolean start) {
this.start = start;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (start) {
AsyncContext ac = req.startAsync();
ac.complete();
}
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().write("OK");
}
}
}

View File

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

View File

@@ -0,0 +1,163 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import javax.naming.NamingException;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.servlets.DefaultServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.jasper.servlet.JasperInitializer;
import org.apache.tomcat.InstanceManager;
public class TestDefaultInstanceManager extends TomcatBaseTest {
@Test
public void testClassUnloading() throws Exception {
DefaultInstanceManager instanceManager = doClassUnloadingPrep();
// Request a JSP page (that doesn't load any tag libraries etc.)
// This page does use @PostConstruct to ensure that the cache does not
// retain strong references
getUrl("http://localhost:" + getPort() + "/test/annotations.jsp");
// Request a second JSP (again, no tag libraries etc.)
getUrl("http://localhost:" + getPort() + "/test/bug36923.jsp");
// Check the number of classes in the cache
int count = instanceManager.getAnnotationCacheSize();
// Request a third JSP (again, no tag libraries etc.)
getUrl("http://localhost:" + getPort() + "/test/bug5nnnn/bug51544.jsp");
// Force a GC to clear out unloaded class (first JSP)
System.gc();
// Spin a while until GC happens or we wait too long
int loop = 0;
while (loop < 10) {
instanceManager.backgroundProcess();
if (instanceManager.getAnnotationCacheSize() == count) {
break;
}
Thread.sleep(100);
loop++;
}
// First JSP should be unloaded and replaced by third (second left
// alone) so no change in overall count
Assert.assertEquals(count, instanceManager.getAnnotationCacheSize());
}
private DefaultInstanceManager doClassUnloadingPrep() throws Exception {
Tomcat tomcat = getTomcatInstance();
// Create the context (don't use addWebapp as we want to modify the
// JSP Servlet settings).
File appDir = new File("test/webapp");
StandardContext ctxt = (StandardContext) tomcat.addContext(
null, "/test", appDir.getAbsolutePath());
ctxt.addServletContainerInitializer(new JasperInitializer(), null);
// Configure the defaults and then tweak the JSP servlet settings
// Note: Min value for maxLoadedJsps is 2
Tomcat.initWebappDefaults(ctxt);
Wrapper w = (Wrapper) ctxt.findChild("jsp");
w.addInitParameter("maxLoadedJsps", "2");
tomcat.start();
return (DefaultInstanceManager) ctxt.getInstanceManager();
}
/*
* Performance test. Comment out @Ignore to run the test.
*/
@Ignore
@Test
public void testConcurrency() throws Exception {
// Create a populated InstanceManager
Tomcat tomcat = getTomcatInstance();
Context ctx = tomcat.addContext(null, "", null);
tomcat.start();
InstanceManager im = ctx.getInstanceManager();
for (int i = 1; i < 9; i++) {
doTestConcurrency(im, i);
}
}
private void doTestConcurrency(InstanceManager im, int threadCount) throws Exception {
long start = System.nanoTime();
Thread[] threads = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
threads[i] = new Thread(new InstanceManagerRunnable(im));
}
for (int i = 0; i < threadCount; i++) {
threads[i].start();
}
for (int i = 0; i < threadCount; i++) {
threads[i].join();
}
long duration = System.nanoTime() - start;
System.out.println(threadCount + " threads completed in " + duration + "ns");
}
private class InstanceManagerRunnable implements Runnable {
private final InstanceManager im;
private InstanceManagerRunnable(InstanceManager im) {
this.im = im;
}
@Override
public void run() {
try {
Object test = new DefaultServlet();
for (int i = 0; i < 200000; i++) {
im.newInstance(test);
im.destroyInstance(test);
}
} catch (NamingException | IllegalAccessException | InvocationTargetException ne) {
ne.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,184 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.descriptor.web.ContextEnvironment;
public class TestNamingContextListener extends TomcatBaseTest {
private static final String BUG49132_NAME = "TestName";
private static final String BUG49132_VALUE = "Test Value";
private static final String BUG54096_NameA = "envA";
private static final String BUG54096_ValueA = "valueA";
private static final String BUG54096_NameB = "envB";
private static final String BUG54096_ValueB = "B";
/*
* Test JNDI is available to ServletContextListeners.
*/
@Test
public void testBug49132() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Enable JNDI - it is disabled by default
tomcat.enableNaming();
ContextEnvironment environment = new ContextEnvironment();
environment.setType(BUG49132_VALUE.getClass().getName());
environment.setName(BUG49132_NAME);
environment.setValue(BUG49132_VALUE);
ctx.getNamingResources().addEnvironment(environment);
ctx.addApplicationListener(Bug49132Listener.class.getName());
tomcat.start();
Assert.assertEquals(LifecycleState.STARTED, ctx.getState());
}
public static final class Bug49132Listener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent sce) {
// NOOP
}
@Override
public void contextInitialized(ServletContextEvent sce) {
javax.naming.Context initCtx;
try {
initCtx = new InitialContext();
javax.naming.Context envCtx =
(javax.naming.Context) initCtx.lookup("java:comp/env");
String value = (String) envCtx.lookup(BUG49132_NAME);
if (!BUG49132_VALUE.equals(value)) {
throw new RuntimeException();
}
} catch (NamingException e) {
throw new RuntimeException(e);
}
}
}
@Test
public void testBug54096() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Enable JNDI - it is disabled by default
tomcat.enableNaming();
ContextEnvironment environmentA = new ContextEnvironment();
environmentA.setType(Bug54096EnvA.class.getName());
environmentA.setName(BUG54096_NameA);
environmentA.setValue(BUG54096_ValueA);
ctx.getNamingResources().addEnvironment(environmentA);
ContextEnvironment environmentB = new ContextEnvironment();
environmentB.setType(Bug54096EnvB.class.getName());
environmentB.setName(BUG54096_NameB);
environmentB.setValue(BUG54096_ValueB);
ctx.getNamingResources().addEnvironment(environmentB);
ctx.addApplicationListener(Bug54096Listener.class.getName());
tomcat.start();
Assert.assertEquals(LifecycleState.STARTED, ctx.getState());
}
public static class Bug54096EnvA {
private final String value;
public Bug54096EnvA(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public static class Bug54096EnvB {
private final char value;
public Bug54096EnvB(char value) {
this.value = value;
}
public char getValue() {
return value;
}
}
public static final class Bug54096Listener implements
ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent sce) {
// NOOP
}
@Override
public void contextInitialized(ServletContextEvent sce) {
javax.naming.Context initCtx;
try {
initCtx = new InitialContext();
javax.naming.Context envCtx =
(javax.naming.Context) initCtx.lookup("java:comp/env");
// Validate entry A
Bug54096EnvA valueA =
(Bug54096EnvA) envCtx.lookup(BUG54096_NameA);
if (!BUG54096_ValueA.equals(valueA.getValue())) {
throw new RuntimeException();
}
// Validate entry B
Bug54096EnvB valueB =
(Bug54096EnvB) envCtx.lookup(BUG54096_NameB);
if (BUG54096_ValueB.charAt(0) != valueB.getValue()) {
throw new RuntimeException();
}
} catch (NamingException e) {
throw new RuntimeException(e);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestStandardContextAliases extends TomcatBaseTest {
@Test
public void testDirContextAliases() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
File lib = new File("webapps/examples/WEB-INF/lib");
ctx.setResources(new StandardRoot(ctx));
ctx.getResources().createWebResourceSet(
WebResourceRoot.ResourceSetType.POST, "/WEB-INF/lib",
lib.getAbsolutePath(), null, "/");
Tomcat.addServlet(ctx, "test", new TestServlet());
ctx.addServletMappingDecoded("/", "test");
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + "/");
String result = res.toString();
if (result == null) {
result = "";
}
Assert.assertTrue(result.indexOf("00-PASS") > -1);
Assert.assertTrue(result.indexOf("01-PASS") > -1);
Assert.assertTrue(result.indexOf("02-PASS") > -1);
}
/**
* Looks for the JSTL JARs in WEB-INF/lib.
*/
public static class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
ServletContext context = getServletContext();
// Check resources individually
URL url = context.getResource("/WEB-INF/lib/taglibs-standard-spec-1.2.5.jar");
if (url != null) {
resp.getWriter().write("00-PASS\n");
}
url = context.getResource("/WEB-INF/lib/taglibs-standard-impl-1.2.5.jar");
if (url != null) {
resp.getWriter().write("01-PASS\n");
}
// Check a directory listing
Set<String> libs = context.getResourcePaths("/WEB-INF/lib");
if (libs == null) {
return;
}
if (!libs.contains("/WEB-INF/lib/taglibs-standard-spec-1.2.5.jar")) {
return;
}
if (!libs.contains("/WEB-INF/lib/taglibs-standard-impl-1.2.5.jar")) {
return;
}
resp.getWriter().write("02-PASS\n");
}
}
}

View File

@@ -0,0 +1,282 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.startup.Constants;
import org.apache.catalina.startup.ContextConfig;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.util.IOTools;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.WebXml;
public class TestStandardContextResources extends TomcatBaseTest {
@Override
public void setUp() throws Exception {
super.setUp();
Tomcat tomcat = getTomcatInstance();
// BZ 49218: The test fails if JreMemoryLeakPreventionListener is not
// present. The listener affects the JVM, and thus not only the current,
// but also the subsequent tests that are run in the same JVM. So it is
// fair to add it in every test.
tomcat.getServer().addLifecycleListener(
new JreMemoryLeakPreventionListener());
}
@Test
public void testResources() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-fragments");
// app dir is relative to server home
Context ctx = tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
tomcat.start();
assertPageContains("/test/resourceA.jsp",
"<p>resourceA.jsp in the web application</p>");
assertPageContains("/test/resourceB.jsp",
"<p>resourceB.jsp in resources.jar</p>");
assertPageContains("/test/folder/resourceC.jsp",
"<p>resourceC.jsp in the web application</p>");
assertPageContains("/test/folder/resourceD.jsp",
"<p>resourceD.jsp in resources.jar</p>");
assertPageContains("/test/folder/resourceE.jsp",
"<p>resourceE.jsp in the web application</p>");
assertPageContains("/test/resourceG.jsp",
"<p>resourceG.jsp in WEB-INF/classes</p>", 404);
// For BZ 54391. Relative ordering is specified in resources2.jar.
// It is not absolute-ordering, so there may be other jars in the list
@SuppressWarnings("unchecked")
List<String> orderedLibs = (List<String>) ctx.getServletContext()
.getAttribute(ServletContext.ORDERED_LIBS);
if (orderedLibs.size() > 2) {
log.warn("testResources(): orderedLibs: " + orderedLibs);
}
int index = orderedLibs.indexOf("resources.jar");
int index2 = orderedLibs.indexOf("resources2.jar");
Assert.assertTrue(orderedLibs.toString(), index >= 0 && index2 >= 0
&& index < index2);
}
@Test
public void testResourcesWebInfClasses() throws Exception {
Tomcat tomcat = getTomcatInstance();
// app dir is relative to server home
File appDir = new File("test/webapp-fragments");
// Need to cast to be able to set StandardContext specific attribute
StandardContext ctxt = (StandardContext)
tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
ctxt.setAddWebinfClassesResources(true);
tomcat.start();
assertPageContains("/test/resourceA.jsp",
"<p>resourceA.jsp in the web application</p>");
assertPageContains("/test/resourceB.jsp",
"<p>resourceB.jsp in resources.jar</p>");
assertPageContains("/test/folder/resourceC.jsp",
"<p>resourceC.jsp in the web application</p>");
assertPageContains("/test/folder/resourceD.jsp",
"<p>resourceD.jsp in resources.jar</p>");
assertPageContains("/test/folder/resourceE.jsp",
"<p>resourceE.jsp in the web application</p>");
assertPageContains("/test/resourceG.jsp",
"<p>resourceG.jsp in WEB-INF/classes</p>");
}
@Test
public void testResourcesAbsoluteOrdering() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-fragments");
AbsoluteOrderContextConfig absoluteOrderConfig = new AbsoluteOrderContextConfig();
// app dir is relative to server home
StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test",
appDir.getAbsolutePath(), (LifecycleListener) absoluteOrderConfig);
Tomcat.addServlet(ctx, "getresource", new GetResourceServlet());
ctx.addServletMappingDecoded("/getresource", "getresource");
tomcat.start();
assertPageContains("/test/getresource?path=/resourceF.jsp",
"<p>resourceF.jsp in resources2.jar</p>");
assertPageContains("/test/getresource?path=/resourceB.jsp",
"<p>resourceB.jsp in resources.jar</p>");
// Check ordering, for BZ 54391
Assert.assertEquals(Arrays.asList("resources.jar", "resources2.jar"), ctx
.getServletContext().getAttribute(ServletContext.ORDERED_LIBS));
tomcat.getHost().removeChild(ctx);
tomcat.getHost().stop();
// change ordering
absoluteOrderConfig.swap();
ctx = (StandardContext) tomcat.addWebapp(null, "/test",
appDir.getAbsolutePath(), (LifecycleListener) absoluteOrderConfig);
Tomcat.addServlet(ctx, "getresource", new GetResourceServlet());
ctx.addServletMappingDecoded("/getresource", "getresource");
tomcat.getHost().start();
assertPageContains("/test/getresource?path=/resourceF.jsp",
"<p>resourceF.jsp in resources2.jar</p>");
assertPageContains("/test/getresource?path=/resourceB.jsp",
"<p>resourceB.jsp in resources2.jar</p>");
// Check ordering, for BZ 54391
Assert.assertEquals(Arrays.asList("resources2.jar", "resources.jar"), ctx
.getServletContext().getAttribute(ServletContext.ORDERED_LIBS));
}
public static class AbsoluteOrderContextConfig extends ContextConfig {
private boolean swap = false;
public AbsoluteOrderContextConfig() {
super();
// Prevent it from looking (if it finds one - it'll have dup error)
setDefaultWebXml(Constants.NoDefaultWebXml);
}
@Override
protected WebXml createWebXml() {
WebXml wxml = new WebXml();
if (swap) {
wxml.addAbsoluteOrdering("resources2");
wxml.addAbsoluteOrdering("resources");
} else {
wxml.addAbsoluteOrdering("resources");
wxml.addAbsoluteOrdering("resources2");
}
return wxml;
}
protected void swap() {
swap = !swap;
}
}
@Test
public void testResources2() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp-fragments");
// app dir is relative to server home
StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test",
appDir.getAbsolutePath());
skipTldsForResourceJars(ctx);
Tomcat.addServlet(ctx, "getresource", new GetResourceServlet());
ctx.addServletMappingDecoded("/getresource", "getresource");
tomcat.start();
assertPageContains("/test/getresource?path=/resourceF.jsp",
"<p>resourceF.jsp in resources2.jar</p>");
assertPageContains("/test/getresource?path=/resourceA.jsp",
"<p>resourceA.jsp in the web application</p>");
assertPageContains("/test/getresource?path=/resourceB.jsp",
"<p>resourceB.jsp in resources.jar</p>");
assertPageContains("/test/getresource?path=/folder/resourceC.jsp",
"<p>resourceC.jsp in the web application</p>");
assertPageContains("/test/getresource?path=/folder/resourceD.jsp",
"<p>resourceD.jsp in resources.jar</p>");
assertPageContains("/test/getresource?path=/folder/resourceE.jsp",
"<p>resourceE.jsp in the web application</p>");
}
/**
* A servlet that prints the requested resource. The path to the requested
* resource is passed as a parameter, <code>path</code>.
*/
public static class GetResourceServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
ServletContext context = getServletContext();
// Check resources individually
URL url = context.getResource(req.getParameter("path"));
if (url == null) {
resp.getWriter().println("Not found");
return;
}
try (InputStream input = url.openStream();
OutputStream output = resp.getOutputStream()) {
IOTools.flow(input, output);
}
}
}
private void assertPageContains(String pageUrl, String expectedBody)
throws IOException {
assertPageContains(pageUrl, expectedBody, 200);
}
private void assertPageContains(String pageUrl, String expectedBody,
int expectedStatus) throws IOException {
ByteChunk res = new ByteChunk();
int sc = getUrl("http://localhost:" + getPort() + pageUrl, res, null);
Assert.assertEquals(expectedStatus, sc);
if (expectedStatus == 200) {
String result = res.toString();
Assert.assertTrue(result, result.indexOf(expectedBody) > 0);
}
}
}

View File

@@ -0,0 +1,185 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Response;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.ErrorPage;
public class TestStandardContextValve extends TomcatBaseTest {
@Test
public void testBug51653a() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Traces order of events across multiple components
StringBuilder trace = new StringBuilder();
//Add the error page
Tomcat.addServlet(ctx, "errorPage", new Bug51653ErrorPage(trace));
ctx.addServletMappingDecoded("/error", "errorPage");
// And the handling for 404 responses
ErrorPage errorPage = new ErrorPage();
errorPage.setErrorCode(Response.SC_NOT_FOUND);
errorPage.setLocation("/error");
ctx.addErrorPage(errorPage);
// Add the request listener
Bug51653RequestListener reqListener =
new Bug51653RequestListener(trace);
((StandardContext) ctx).addApplicationEventListener(reqListener);
tomcat.start();
// Request a page that does not exist
int rc = getUrl("http://localhost:" + getPort() + "/invalid",
new ByteChunk(), null);
// Need to allow time (but not too long in case the test fails) for
// ServletRequestListener to complete
int i = 20;
while (i > 0) {
if (trace.toString().endsWith("Destroy")) {
break;
}
Thread.sleep(250);
i--;
}
Assert.assertEquals(Response.SC_NOT_FOUND, rc);
Assert.assertEquals("InitErrorDestroy", trace.toString());
}
@Test
public void testBug51653b() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Traces order of events across multiple components
StringBuilder trace = new StringBuilder();
// Add the page that generates the error
Tomcat.addServlet(ctx, "test", new Bug51653ErrorTrigger());
ctx.addServletMappingDecoded("/test", "test");
// Add the error page
Tomcat.addServlet(ctx, "errorPage", new Bug51653ErrorPage(trace));
ctx.addServletMappingDecoded("/error", "errorPage");
// And the handling for 404 responses
ErrorPage errorPage = new ErrorPage();
errorPage.setErrorCode(Response.SC_NOT_FOUND);
errorPage.setLocation("/error");
ctx.addErrorPage(errorPage);
// Add the request listener
Bug51653RequestListener reqListener =
new Bug51653RequestListener(trace);
((StandardContext) ctx).addApplicationEventListener(reqListener);
tomcat.start();
// Request a page that does not exist
int rc = getUrl("http://localhost:" + getPort() + "/test",
new ByteChunk(), null);
// Need to allow time (but not too long in case the test fails) for
// ServletRequestListener to complete
int i = 20;
while (i > 0) {
if (trace.toString().endsWith("Destroy")) {
break;
}
Thread.sleep(250);
i--;
}
Assert.assertEquals(Response.SC_NOT_FOUND, rc);
Assert.assertEquals("InitErrorDestroy", trace.toString());
}
private static class Bug51653ErrorTrigger extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.sendError(Response.SC_NOT_FOUND);
}
}
private static class Bug51653ErrorPage extends HttpServlet {
private static final long serialVersionUID = 1L;
private StringBuilder sb;
public Bug51653ErrorPage(StringBuilder sb) {
this.sb = sb;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
sb.append("Error");
resp.setContentType("text/plain");
resp.getWriter().write("Error");
}
}
private static class Bug51653RequestListener
implements ServletRequestListener {
private StringBuilder sb;
public Bug51653RequestListener(StringBuilder sb) {
this.sb = sb;
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
sb.append("Init");
}
@Override
public void requestDestroyed(ServletRequestEvent sre) {
sb.append("Destroy");
}
}
}

View File

@@ -0,0 +1,169 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Response;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.ErrorPage;
public class TestStandardHostValve extends TomcatBaseTest {
@Test
public void testErrorPageHandling() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Add the error page
Tomcat.addServlet(ctx, "error", new ErrorServlet());
ctx.addServletMappingDecoded("/error", "error");
// Add the error handling page
Tomcat.addServlet(ctx, "report", new ReportServlet());
ctx.addServletMappingDecoded("/report/*", "report");
// And the handling for 500 responses
ErrorPage errorPage500 = new ErrorPage();
errorPage500.setErrorCode(Response.SC_INTERNAL_SERVER_ERROR);
errorPage500.setLocation("/report/500");
ctx.addErrorPage(errorPage500);
// And the default error handling
ErrorPage errorPageDefault = new ErrorPage();
errorPageDefault.setLocation("/report/default");
ctx.addErrorPage(errorPageDefault);
tomcat.start();
doTestErrorPageHandling(500, "/500");
doTestErrorPageHandling(501, "/default");
}
@Test(expected=IllegalArgumentException.class)
public void testInvalidErrorPage() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Add a broken error page configuration
ErrorPage errorPage500 = new ErrorPage();
errorPage500.setErrorCode("java.lang.Exception");
errorPage500.setLocation("/report/500");
ctx.addErrorPage(errorPage500);
}
@Test
public void testSRLAfterError() throws Exception {
// Set up a container
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
// Add the error page
Tomcat.addServlet(ctx, "error", new ErrorServlet());
ctx.addServletMappingDecoded("/error", "error");
final List<String> result = new ArrayList<>();
// Add the request listener
ServletRequestListener servletRequestListener = new ServletRequestListener() {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
result.add("Visit requestDestroyed");
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
result.add("Visit requestInitialized");
}
};
((StandardContext) ctx).addApplicationEventListener(servletRequestListener);
tomcat.start();
// Request a page that triggers an error
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + "/error?errorCode=400", bc, null);
Assert.assertEquals(400, rc);
Assert.assertTrue(result.contains("Visit requestInitialized"));
Assert.assertTrue(result.contains("Visit requestDestroyed"));
}
private void doTestErrorPageHandling(int error, String report)
throws Exception {
// Request a page that triggers an error
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() +
"/error?errorCode=" + error, bc, null);
Assert.assertEquals(error, rc);
Assert.assertEquals(report, bc.toString());
}
private static class ErrorServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
int error = Integer.parseInt(req.getParameter("errorCode"));
resp.sendError(error);
}
}
private static class ReportServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String pathInfo = req.getPathInfo();
resp.setContentType("text/plain");
resp.getWriter().print(pathInfo);
}
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class TesterTldListener implements ServletContextListener {
private static StringBuilder log = new StringBuilder();
public static String getLog() {
return log.toString();
}
private ServletContext servletContext;
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext sc = sce.getServletContext();
servletContext = sc;
// Try and use one of the Servlet 3.0 methods that should be blocked
try {
sc.getEffectiveMajorVersion();
log.append("FAIL-01");
} catch (UnsupportedOperationException uoe) {
log.append("PASS-01");
} catch (Exception e) {
log.append("FAIL-02");
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Bug 57446. Same ServletContext should be presented as at init
if (servletContext == sce.getServletContext()) {
log.append("PASS-02");
} else {
//log.append("FAIL-03");
}
}
}