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,108 @@
/*
* 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 javax.servlet.annotation;
import java.io.IOException;
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.buf.ByteChunk;
public class TestServletSecurity extends TomcatBaseTest {
@Test
public void testFooThenFooBar() throws Exception {
doTestFooAndFooBar(true);
}
@Test
public void testFooBarThenFoo() throws Exception {
doTestFooAndFooBar(false);
}
public void doTestFooAndFooBar(boolean fooFirst) throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "Foo", Foo.class.getName());
ctx.addServletMappingDecoded("/foo/*", "Foo");
Tomcat.addServlet(ctx, "FooBar", FooBar.class.getName());
ctx.addServletMappingDecoded("/foo/bar/*", "FooBar");
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc;
if (fooFirst) {
rc = getUrl("http://localhost:" + getPort() + "/foo", bc, null, null);
} else {
rc = getUrl("http://localhost:" + getPort() + "/foo/bar", bc, null, null);
}
bc.recycle();
Assert.assertEquals(403, rc);
if (fooFirst) {
rc = getUrl("http://localhost:" + getPort() + "/foo/bar", bc, null, null);
} else {
rc = getUrl("http://localhost:" + getPort() + "/foo", bc, null, null);
}
Assert.assertEquals(403, rc);
}
@ServletSecurity(@HttpConstraint(ServletSecurity.EmptyRoleSemantic.DENY))
public static class Foo extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().print("OK: Foo");
}
}
public static class FooBar extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().print("OK: FooBar");
}
}
}

View File

@@ -0,0 +1,247 @@
/*
* 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 javax.servlet.annotation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
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.junit.runners.Parameterized.Parameters;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
@RunWith(Parameterized.class)
public class TestServletSecurityMappings extends TomcatBaseTest {
@Parameters(name="{0}, {1}, {2}, {3}")
public static Collection<Object[]> inputs() {
List<Object[]> result = new ArrayList<>();
result.add(new Object[] { Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, Boolean.FALSE });
result.add(new Object[] { Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, Boolean.TRUE });
result.add(new Object[] { Boolean.FALSE, Boolean.FALSE, Boolean.TRUE , Boolean.FALSE });
result.add(new Object[] { Boolean.FALSE, Boolean.FALSE, Boolean.TRUE, Boolean.TRUE });
result.add(new Object[] { Boolean.FALSE, Boolean.TRUE, Boolean.FALSE, Boolean.FALSE });
result.add(new Object[] { Boolean.FALSE, Boolean.TRUE, Boolean.FALSE, Boolean.TRUE });
result.add(new Object[] { Boolean.FALSE, Boolean.TRUE, Boolean.TRUE , Boolean.FALSE });
result.add(new Object[] { Boolean.FALSE, Boolean.TRUE, Boolean.TRUE, Boolean.TRUE });
result.add(new Object[] { Boolean.TRUE, Boolean.FALSE, Boolean.FALSE, Boolean.FALSE });
result.add(new Object[] { Boolean.TRUE, Boolean.FALSE, Boolean.FALSE, Boolean.TRUE });
result.add(new Object[] { Boolean.TRUE, Boolean.FALSE, Boolean.TRUE , Boolean.FALSE });
result.add(new Object[] { Boolean.TRUE, Boolean.FALSE, Boolean.TRUE, Boolean.TRUE });
result.add(new Object[] { Boolean.TRUE, Boolean.TRUE, Boolean.FALSE, Boolean.FALSE });
result.add(new Object[] { Boolean.TRUE, Boolean.TRUE, Boolean.FALSE, Boolean.TRUE });
result.add(new Object[] { Boolean.TRUE, Boolean.TRUE, Boolean.TRUE , Boolean.FALSE });
result.add(new Object[] { Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, Boolean.TRUE });
return result;
}
@Parameter(0)
public boolean redirectContextRoot;
@Parameter(1)
public boolean secureRoot;
@Parameter(2)
public boolean secureDefault;
@Parameter(3)
public boolean secureFoo;
@Test
public void doTestSecurityAnnotationsAddServlet() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("/test", null);
ctx.setMapperContextRootRedirectEnabled(redirectContextRoot);
ServletContainerInitializer sci = new SCI(secureRoot, secureDefault, secureFoo);
ctx.addServletContainerInitializer(sci, null);
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc;
// Foo
rc = getUrl("http://localhost:" + getPort() + "/test/foo", bc, false);
if (secureFoo || secureDefault) {
Assert.assertEquals(403, rc);
} else {
Assert.assertEquals(200, rc);
}
bc.recycle();
// Default
rc = getUrl("http://localhost:" + getPort() + "/test/something", bc, false);
if (secureDefault) {
Assert.assertEquals(403, rc);
} else {
Assert.assertEquals(200, rc);
}
bc.recycle();
// Root
rc = getUrl("http://localhost:" + getPort() + "/test", bc, false);
if (redirectContextRoot) {
Assert.assertEquals(302, rc);
} else {
if (secureRoot || secureDefault) {
Assert.assertEquals(403, rc);
} else {
Assert.assertEquals(200, rc);
}
}
}
public static class SCI implements ServletContainerInitializer {
private final boolean secureRoot;
private final boolean secureDefault;
private final boolean secureFoo;
public SCI(boolean secureRoot, boolean secureDefault, boolean secureFoo) {
this.secureRoot = secureRoot;
this.secureDefault = secureDefault;
this.secureFoo = secureFoo;
}
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx)
throws ServletException {
ServletRegistration.Dynamic sr;
if (secureRoot) {
sr = ctx.addServlet("Root", SecureRoot.class.getName());
} else {
sr =ctx.addServlet("Root", Root.class.getName());
}
sr.addMapping("");
if (secureDefault) {
sr = ctx.addServlet("Default", SecureDefault.class.getName());
} else {
sr = ctx.addServlet("Default", Default.class.getName());
}
sr.addMapping("/");
if (secureFoo) {
sr = ctx.addServlet("Foo", SecureFoo.class.getName());
} else {
sr = ctx.addServlet("Foo", Foo.class.getName());
}
sr.addMapping("/foo");
}
}
@ServletSecurity(@HttpConstraint(ServletSecurity.EmptyRoleSemantic.DENY))
public static class SecureRoot extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().print("OK");
}
}
public static class Root extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().print("OK");
}
}
@ServletSecurity(@HttpConstraint(ServletSecurity.EmptyRoleSemantic.DENY))
public static class SecureDefault extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().print("OK");
}
}
public static class Default extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().print("OK");
}
}
@ServletSecurity(@HttpConstraint(ServletSecurity.EmptyRoleSemantic.DENY))
public static class SecureFoo extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().print("OK");
}
}
public static class Foo extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().print("OK");
}
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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 javax.servlet.http;
import java.util.BitSet;
import org.junit.Assert;
import org.junit.Test;
/**
* Basic tests for Cookie in default configuration.
*/
public class TestCookie {
public static final BitSet CHAR; // <any US-ASCII character (octets 0 - 127)>
public static final BitSet CTL; // <any US-ASCII control character (octets 0 - 31) and DEL (127)>
public static final BitSet SEPARATORS;
public static final BitSet TOKEN; // 1*<any CHAR except CTLs or separators>
static {
CHAR = new BitSet(256);
CHAR.set(0, 128);
CTL = new BitSet(256);
CTL.set(0, 32);
CTL.set(127);
SEPARATORS = new BitSet(256);
for (char ch : "()<>@,;:\\\"/[]?={} \t".toCharArray()) {
SEPARATORS.set(ch);
}
TOKEN = new BitSet(256);
TOKEN.or(CHAR); // any CHAR
TOKEN.andNot(CTL); // except CTLs
TOKEN.andNot(SEPARATORS); // or separators
}
@Test
public void testDefaults() {
Cookie cookie = new Cookie("foo", null);
Assert.assertEquals("foo", cookie.getName());
Assert.assertNull(cookie.getValue());
Assert.assertEquals(0, cookie.getVersion());
Assert.assertEquals(-1, cookie.getMaxAge());
}
@Test
public void testInitialValue() {
Cookie cookie = new Cookie("foo", "bar");
Assert.assertEquals("foo", cookie.getName());
Assert.assertEquals("bar", cookie.getValue());
Assert.assertEquals(0, cookie.getVersion());
}
@Test
public void defaultImpliesNetscape() {
// $Foo is allowed by Netscape but not by RFC2109
Cookie cookie = new Cookie("$Foo", null);
Assert.assertEquals("$Foo", cookie.getName());
}
@Test
public void tokenVersion() {
Cookie cookie = new Cookie("Version", null);
Assert.assertEquals("Version", cookie.getName());
}
@Test
public void attributeVersion() {
Cookie cookie = new Cookie("Comment", null);
Assert.assertEquals("Comment", cookie.getName());
}
@Test
public void attributeDiscard() {
Cookie cookie = new Cookie("Discard", null);
Assert.assertEquals("Discard", cookie.getName());
}
@Test
public void attributeExpires() {
Cookie cookie = new Cookie("Expires", null);
Assert.assertEquals("Expires", cookie.getName());
}
@Test
public void attributeMaxAge() {
Cookie cookie = new Cookie("Max-Age", null);
Assert.assertEquals("Max-Age", cookie.getName());
}
@Test
public void attributeDomain() {
Cookie cookie = new Cookie("Domain", null);
Assert.assertEquals("Domain", cookie.getName());
}
@Test
public void attributePath() {
Cookie cookie = new Cookie("Path", null);
Assert.assertEquals("Path", cookie.getName());
}
@Test
public void attributeSecure() {
Cookie cookie = new Cookie("Secure", null);
Assert.assertEquals("Secure", cookie.getName());
}
@Test
public void attributeHttpOnly() {
Cookie cookie = new Cookie("HttpOnly", null);
Assert.assertEquals("HttpOnly", cookie.getName());
}
@Test
public void strictNamingImpliesRFC2109() {
// Needs to be something RFC6265 allows, but strict naming does not.
@SuppressWarnings("unused")
Cookie cookie = new Cookie("$Foo", null);
}
public static void checkCharInName(CookieNameValidator validator, BitSet allowed) {
for (char ch = 0; ch < allowed.size(); ch++) {
boolean expected = allowed.get(ch);
String name = "X" + ch + "X";
try {
validator.validate(name);
if (!expected) {
Assert.fail(String.format("Char %d should not be allowed", Integer.valueOf(ch)));
}
} catch (IllegalArgumentException e) {
if (expected) {
Assert.fail(String.format("Char %d should be allowed", Integer.valueOf(ch)));
}
}
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 javax.servlet.http;
import org.junit.Test;
/**
* Basic tests for Cookie in default configuration.
*/
public class TestCookieRFC2109Validator {
static {
System.setProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR", "true");
}
private RFC2109Validator validator = new RFC2109Validator();
@Test
public void actualCharactersAllowedInName() {
TestCookie.checkCharInName(validator, TestCookie.TOKEN);
}
@Test(expected = IllegalArgumentException.class)
public void leadingDollar() {
validator.validate("$Version");
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 javax.servlet.http;
import org.junit.Test;
/**
* Basic tests for Cookie in default configuration.
*/
public class TestCookieRFC6265Validator {
static {
System.setProperty("org.apache.tomcat.util.http.ServerCookie.FWD_SLASH_IS_SEPARATOR", "true");
}
private RFC6265Validator validator = new RFC6265Validator();
@Test
public void actualCharactersAllowedInName() {
TestCookie.checkCharInName(validator, TestCookie.TOKEN);
}
@Test
public void leadingDollar() {
validator.validate("$Version");
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 javax.servlet.http;
import org.junit.Assert;
import org.junit.Test;
/**
* Basic tests for Cookie in STRICT_SERVLET_COMPLIANCE configuration.
*/
public class TestCookieStrict {
static {
System.setProperty("org.apache.tomcat.util.http.ServerCookie.STRICT_NAMING", "true");
}
@Test
public void testDefaults() {
Cookie cookie = new Cookie("strict", null);
Assert.assertEquals("strict", cookie.getName());
Assert.assertNull(cookie.getValue());
Assert.assertEquals(0, cookie.getVersion());
Assert.assertEquals(-1, cookie.getMaxAge());
}
@Test(expected = IllegalArgumentException.class)
public void strictNamingImpliesRFC2109() {
@SuppressWarnings("unused")
Cookie cookie = new Cookie("@Foo", null);
}
}

View File

@@ -0,0 +1,207 @@
/*
* 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 javax.servlet.http;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap;
public class TestHttpServlet extends TomcatBaseTest {
@Test
public void testBug53454() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
StandardContext ctx = (StandardContext) tomcat.addContext("", null);
// Map the test Servlet
LargeBodyServlet largeBodyServlet = new LargeBodyServlet();
Tomcat.addServlet(ctx, "largeBodyServlet", largeBodyServlet);
ctx.addServletMappingDecoded("/", "largeBodyServlet");
tomcat.start();
Map<String,List<String>> resHeaders= new HashMap<>();
int rc = headUrl("http://localhost:" + getPort() + "/", new ByteChunk(),
resHeaders);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertEquals(LargeBodyServlet.RESPONSE_LENGTH,
resHeaders.get("Content-Length").get(0));
}
private static class LargeBodyServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String RESPONSE_LENGTH = "12345678901";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setHeader("content-length", RESPONSE_LENGTH);
}
}
/*
* Verifies that the same Content-Length is returned for both GET and HEAD
* operations when a Servlet includes content from another Servlet
*/
@Test
public void testBug57602() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
StandardContext ctx = (StandardContext) tomcat.addContext("", null);
Bug57602ServletOuter outer = new Bug57602ServletOuter();
Tomcat.addServlet(ctx, "Bug57602ServletOuter", outer);
ctx.addServletMappingDecoded("/outer", "Bug57602ServletOuter");
Bug57602ServletInner inner = new Bug57602ServletInner();
Tomcat.addServlet(ctx, "Bug57602ServletInner", inner);
ctx.addServletMappingDecoded("/inner", "Bug57602ServletInner");
tomcat.start();
Map<String,List<String>> resHeaders= new CaseInsensitiveKeyMap<>();
String path = "http://localhost:" + getPort() + "/outer";
ByteChunk out = new ByteChunk();
int rc = getUrl(path, out, resHeaders);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
String length = getSingleHeader("Content-Length", resHeaders);
Assert.assertEquals(Long.parseLong(length), out.getLength());
out.recycle();
rc = headUrl(path, out, resHeaders);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertEquals(0, out.getLength());
Assert.assertEquals(length, resHeaders.get("Content-Length").get(0));
tomcat.stop();
}
@Test
public void testChunkingWithHead() throws Exception {
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
StandardContext ctx = (StandardContext) tomcat.addContext("", null);
ChunkingServlet s = new ChunkingServlet();
Tomcat.addServlet(ctx, "ChunkingServlet", s);
ctx.addServletMappingDecoded("/chunking", "ChunkingServlet");
tomcat.start();
Map<String,List<String>> getHeaders = new CaseInsensitiveKeyMap<>();
String path = "http://localhost:" + getPort() + "/chunking";
ByteChunk out = new ByteChunk();
int rc = getUrl(path, out, getHeaders);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
out.recycle();
Map<String,List<String>> headHeaders = new HashMap<>();
rc = headUrl(path, out, headHeaders);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
// Headers should be the same (apart from Date)
Assert.assertEquals(getHeaders.size(), headHeaders.size());
for (Map.Entry<String, List<String>> getHeader : getHeaders.entrySet()) {
String headerName = getHeader.getKey();
if ("date".equalsIgnoreCase(headerName)) {
continue;
}
Assert.assertTrue(headerName, headHeaders.containsKey(headerName));
List<String> getValues = getHeader.getValue();
List<String> headValues = headHeaders.get(headerName);
Assert.assertEquals(getValues.size(), headValues.size());
for (String value : getValues) {
Assert.assertTrue(headValues.contains(value));
}
}
tomcat.stop();
}
private static class Bug57602ServletOuter extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter pw = resp.getWriter();
pw.println("Header");
req.getRequestDispatcher("/inner").include(req, resp);
pw.println("Footer");
}
}
private static class Bug57602ServletInner extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter pw = resp.getWriter();
pw.println("Included");
}
}
private static class ChunkingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
PrintWriter pw = resp.getWriter();
// Trigger chunking
pw.write(new char[8192 * 16]);
pw.println("Data");
}
}
}

View File

@@ -0,0 +1,366 @@
/*
* 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 javax.servlet.http;
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 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;
import org.apache.tomcat.util.descriptor.web.ErrorPage;
/**
* These tests evolved out of a discussion in the Jakarta Servlet project
* regarding the intended behaviour in various error scenarios. Async requests
* and/or async error pages added additional complexity.
*/
@RunWith(Parameterized.class)
public class TestHttpServletResponseSendError extends TomcatBaseTest {
/*
* Implementation notes:
* Original Request
* - async
* - error in original thread / new thread
* - error before / after startAsync
* - error before / after complete / dispatch
* Error page
* - sync
* - async
* - complete
* - dispatch
*/
private enum AsyncErrorPoint {
/*
* Thread A is the container thread the processes the original request.
* Thread B is the async thread (may or may not be a container thread)
* that is started by the async processing.
*/
THREAD_A_BEFORE_START_ASYNC,
THREAD_A_AFTER_START_ASYNC,
THREAD_A_AFTER_START_RUNNABLE,
THREAD_B_BEFORE_COMPLETE
/*
* If the error is triggered after Thread B completes async processing
* there is essentially a race condition between thread B making the
* change and the container checking to see if the error flag has been
* set. We can't easily control the execution order here so we don't
* test it.
*/
}
@Parameterized.Parameters(name = "{index}: async[{0}], throw[{1}], dispatch[{2}], errorPoint[{3}], useStart[{4}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
for (Boolean async : booleans) {
for (Boolean throwException : booleans) {
if (async.booleanValue()) {
for (Boolean useDispatch : booleans) {
for (AsyncErrorPoint errorPoint : AsyncErrorPoint.values()) {
for (Boolean useStart : booleans) {
if (throwException.booleanValue() && !useStart.booleanValue() &&
errorPoint == AsyncErrorPoint.THREAD_B_BEFORE_COMPLETE) {
// Skip this combination as exceptions that occur on application
// managed threads are not visible to the container.
continue;
}
parameterSets.add(new Object[] { async, throwException, useDispatch,
errorPoint, useStart} );
}
}
}
} else {
// Ignore the async specific parameters
parameterSets.add(new Object[] { async, throwException, Boolean.FALSE,
AsyncErrorPoint.THREAD_A_AFTER_START_ASYNC, Boolean.FALSE} );
}
}
}
return parameterSets;
}
@Parameter(0)
public boolean async;
@Parameter(1)
public boolean throwException;
@Parameter(2)
public boolean useDispatch;
@Parameter(3)
public AsyncErrorPoint errorPoint;
@Parameter(4)
public boolean useStart;
@Test
public void testSendError() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
if (async) {
Wrapper w = Tomcat.addServlet(ctx, "target",
new TesterAsyncServlet(throwException, useDispatch, errorPoint, useStart));
w.setAsyncSupported(true);
} else {
Tomcat.addServlet(ctx, "target", new TesterServlet(throwException));
}
ctx.addServletMappingDecoded("/target", "target");
Tomcat.addServlet(ctx, "dispatch", new TesterDispatchServlet());
ctx.addServletMappingDecoded("/dispatch", "dispatch");
Tomcat.addServlet(ctx, "error599", new ErrorServletStatic599());
ctx.addServletMappingDecoded("/error599", "error599");
Tomcat.addServlet(ctx, "errorException", new ErrorServletStaticException());
ctx.addServletMappingDecoded("/errorException", "errorException");
ErrorPage ep1 = new ErrorPage();
ep1.setErrorCode(599);
ep1.setLocation("/error599");
ctx.addErrorPage(ep1);
ErrorPage ep2 = new ErrorPage();
ep2.setExceptionType(SendErrorException.class.getName());
ep2.setLocation("/errorException");
ctx.addErrorPage(ep2);
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc;
rc = getUrl("http://localhost:" + getPort() + "/target", bc, null, null);
String body = bc.toString();
if (throwException) {
Assert.assertEquals(500, rc);
Assert.assertEquals("FAIL-Exception", body);
} else {
Assert.assertEquals(599, rc);
Assert.assertEquals("FAIL-599", body);
}
}
public static class TesterServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final boolean throwException;
public TesterServlet(boolean throwException) {
this.throwException = throwException;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (throwException) {
throw new SendErrorException();
} else {
// Custom 5xx code so we can detect if the correct error is
// reported
resp.sendError(599);
}
}
}
public static class TesterAsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final boolean throwException;
private final boolean useDispatch;
private final AsyncErrorPoint errorPoint;
private final boolean useStart;
public TesterAsyncServlet(boolean throwException, boolean useDispatch, AsyncErrorPoint errorPoint,
boolean useStart) {
this.throwException = throwException;
this.useDispatch = useDispatch;
this.errorPoint = errorPoint;
this.useStart = useStart;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (errorPoint == AsyncErrorPoint.THREAD_A_BEFORE_START_ASYNC) {
doError(resp);
}
AsyncContext ac = req.startAsync();
ac.setTimeout(2000);
if (errorPoint == AsyncErrorPoint.THREAD_A_AFTER_START_ASYNC) {
doError(resp);
}
AsyncRunnable r = new AsyncRunnable(ac, throwException, useDispatch, errorPoint);
if (useStart) {
ac.start(r);
} else {
Thread t = new Thread(r);
t.start();
}
if (errorPoint == AsyncErrorPoint.THREAD_A_AFTER_START_RUNNABLE) {
doError(resp);
}
}
private void doError(HttpServletResponse resp) throws IOException {
if (throwException) {
throw new SendErrorException();
} else {
resp.sendError(599);
}
}
}
public static class AsyncRunnable implements Runnable {
private final AsyncContext ac;
private final boolean throwException;
private final boolean useDispatch;
private final AsyncErrorPoint errorPoint;
public AsyncRunnable(AsyncContext ac, boolean throwException, boolean useDispatch,
AsyncErrorPoint errorPoint) {
this.ac = ac;
this.throwException = throwException;
this.useDispatch = useDispatch;
this.errorPoint = errorPoint;
}
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
if (errorPoint == AsyncErrorPoint.THREAD_B_AFTER_COMPLETE) {
if (useDispatch) {
ac.complete();
} else {
ac.dispatch("/dispatch");
}
}
*/
if (throwException) {
throw new SendErrorException();
} else {
// Custom 5xx code so we can detect if the correct error is
// reported
try {
((HttpServletResponse) ac.getResponse()).sendError(599);
} catch (IOException e) {
e.printStackTrace();
}
}
if (errorPoint == AsyncErrorPoint.THREAD_B_BEFORE_COMPLETE) {
if (useDispatch) {
ac.dispatch("/dispatch");
} else {
ac.complete();
}
}
}
}
public static class TesterDispatchServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().write("DISPATCH");
}
}
public static class SendErrorException extends RuntimeException {
private static final long serialVersionUID = 1L;
}
public static class ErrorServletStatic599 extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().write("FAIL-599");
}
}
public static class ErrorServletStaticException extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().write("FAIL-Exception");
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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 javax.servlet.jsp;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestPageContext extends TomcatBaseTest {
@Test
public void testBug49196() throws Exception {
getTomcatInstanceTestWebapp(false, true);
ByteChunk res = getUrl("http://localhost:" + getPort() +
"/test/bug49nnn/bug49196.jsp");
String result = res.toString();
Assert.assertTrue(result.contains("OK"));
}
}

View File

@@ -0,0 +1,195 @@
/*
* 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 javax.servlet.jsp;
import java.io.IOException;
import java.util.Enumeration;
import javax.el.ELContext;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpSession;
public class TesterPageContext extends PageContext {
@Override
public void initialize(Servlet servlet, ServletRequest request,
ServletResponse response, String errorPageURL,
boolean needsSession, int bufferSize, boolean autoFlush)
throws IOException, IllegalStateException, IllegalArgumentException {
// NO-OP
}
@Override
public void release() {
// NO-OP
}
@Override
public HttpSession getSession() {
// NO-OP
return null;
}
@Override
public Object getPage() {
// NO-OP
return null;
}
@Override
public ServletRequest getRequest() {
// NO-OP
return null;
}
@Override
public ServletResponse getResponse() {
// NO-OP
return null;
}
@Override
public Exception getException() {
// NO-OP
return null;
}
@Override
public ServletConfig getServletConfig() {
// NO-OP
return null;
}
@Override
public ServletContext getServletContext() {
// NO-OP
return null;
}
@Override
public void forward(String relativeUrlPath) throws ServletException,
IOException {
// NO-OP
}
@Override
public void include(String relativeUrlPath) throws ServletException,
IOException {
// NO-OP
}
@Override
public void include(String relativeUrlPath, boolean flush)
throws ServletException, IOException {
// NO-OP
}
@Override
public void handlePageException(Exception e) throws ServletException,
IOException {
// NO-OP
}
@Override
public void handlePageException(Throwable t) throws ServletException,
IOException {
// NO-OP
}
@Override
public void setAttribute(String name, Object value) {
// NO-OP
}
@Override
public void setAttribute(String name, Object value, int scope) {
// NO-OP
}
@Override
public Object getAttribute(String name) {
// NO-OP
return null;
}
@Override
public Object getAttribute(String name, int scope) {
// NO-OP
return null;
}
@Override
public Object findAttribute(String name) {
// NO-OP
return null;
}
@Override
public void removeAttribute(String name) {
// NO-OP
}
@Override
public void removeAttribute(String name, int scope) {
// NO-OP
}
@Override
public int getAttributesScope(String name) {
// NO-OP
return 0;
}
@Override
public Enumeration<String> getAttributeNamesInScope(int scope) {
// NO-OP
return null;
}
@Override
public JspWriter getOut() {
// NO-OP
return null;
}
@Override
@Deprecated
public javax.servlet.jsp.el.ExpressionEvaluator getExpressionEvaluator() {
// NO-OP
return null;
}
@Override
public ELContext getELContext() {
// NO-OP
return null;
}
@Override
@Deprecated
public javax.servlet.jsp.el.VariableResolver getVariableResolver() {
// NO-OP
return null;
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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 javax.servlet.jsp.el;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestScopedAttributeELResolver extends TomcatBaseTest {
@Test
public void testBug49196() throws Exception {
getTomcatInstanceTestWebapp(true, true);
ByteChunk res = getUrl("http://localhost:" + getPort() +
"/test/bug6nnnn/bug62453.jsp");
String result = res.toString();
Assert.assertTrue(result, result.contains("<div>foo: OK</div>"));
Assert.assertTrue(result, result.contains("<div>bar: </div>"));
Assert.assertTrue(result, result.contains("<div>baz: </div>"));
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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 javax.servlet.jsp.el;
import javax.el.ELContext;
import javax.el.ELManager;
import javax.el.ELResolver;
import javax.el.StandardELContext;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.TesterPageContext;
import org.junit.Test;
public class TestScopedAttributeELResolverPerformance {
/*
* With the caching of NotFound responses this test takes ~20ms. Without the
* caching it takes ~6s.
*/
@Test
public void testGetValuePerformance() throws Exception {
ELContext context = new StandardELContext(ELManager.getExpressionFactory());
context.putContext(JspContext.class, new TesterPageContext());
ELResolver resolver = new ScopedAttributeELResolver();
for (int i = 0; i < 100000; i++) {
resolver.getValue(context, null, "unknown");
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* 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 javax.servlet.resources;
import java.io.File;
import org.junit.Assert;
import org.junit.Test;
import org.apache.tomcat.util.descriptor.DigesterFactory;
import org.apache.tomcat.util.descriptor.XmlErrorHandler;
import org.apache.tomcat.util.descriptor.XmlIdentifiers;
import org.apache.tomcat.util.descriptor.web.WebRuleSet;
import org.apache.tomcat.util.descriptor.web.WebXml;
import org.apache.tomcat.util.digester.Digester;
public class TestSchemaValidation {
@Test
public void testWebapp() throws Exception {
XmlErrorHandler handler = new XmlErrorHandler();
Digester digester = DigesterFactory.newDigester(
true, true, new WebRuleSet(false), true);
digester.setErrorHandler(handler);
digester.push(new WebXml());
WebXml desc = (WebXml) digester.parse(
new File("test/webapp/WEB-INF/web.xml"));
Assert.assertEquals("3.1", desc.getVersion());
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
}
@Test
public void testWebapp_2_2() throws Exception {
XmlErrorHandler handler = new XmlErrorHandler();
Digester digester = DigesterFactory.newDigester(
true, true, new WebRuleSet(false), true);
digester.setErrorHandler(handler);
digester.push(new WebXml());
WebXml desc = (WebXml) digester.parse(
new File("test/webapp-2.2/WEB-INF/web.xml"));
Assert.assertEquals("2.2", desc.getVersion());
Assert.assertEquals(XmlIdentifiers.WEB_22_PUBLIC, desc.getPublicId());
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
}
@Test
public void testWebapp_2_3() throws Exception {
XmlErrorHandler handler = new XmlErrorHandler();
Digester digester = DigesterFactory.newDigester(
true, true, new WebRuleSet(false), true);
digester.setErrorHandler(handler);
digester.push(new WebXml());
WebXml desc = (WebXml) digester.parse(
new File("test/webapp-2.3/WEB-INF/web.xml"));
Assert.assertEquals("2.3", desc.getVersion());
Assert.assertEquals(XmlIdentifiers.WEB_23_PUBLIC, desc.getPublicId());
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
}
@Test
public void testWebapp_2_4() throws Exception {
XmlErrorHandler handler = new XmlErrorHandler();
Digester digester = DigesterFactory.newDigester(
true, true, new WebRuleSet(false), true);
digester.setErrorHandler(handler);
digester.push(new WebXml());
WebXml desc = (WebXml) digester.parse(
new File("test/webapp-2.4/WEB-INF/web.xml"));
Assert.assertEquals("2.4", desc.getVersion());
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
}
@Test
public void testWebapp_2_5() throws Exception {
XmlErrorHandler handler = new XmlErrorHandler();
Digester digester = DigesterFactory.newDigester(
true, true, new WebRuleSet(false), true);
digester.setErrorHandler(handler);
digester.push(new WebXml());
WebXml desc = (WebXml) digester.parse(
new File("test/webapp-2.5/WEB-INF/web.xml"));
Assert.assertEquals("2.5", desc.getVersion());
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
}
@Test
public void testWebapp_3_0() throws Exception {
XmlErrorHandler handler = new XmlErrorHandler();
Digester digester = DigesterFactory.newDigester(
true, true, new WebRuleSet(false), true);
digester.setErrorHandler(handler);
digester.push(new WebXml());
WebXml desc = (WebXml) digester.parse(
new File("test/webapp-3.0/WEB-INF/web.xml"));
Assert.assertEquals("3.0", desc.getVersion());
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
}
@Test
public void testWebapp_3_1() throws Exception {
XmlErrorHandler handler = new XmlErrorHandler();
Digester digester = DigesterFactory.newDigester(
true, true, new WebRuleSet(false), true);
digester.setErrorHandler(handler);
digester.push(new WebXml());
WebXml desc = (WebXml) digester.parse(
new File("test/webapp-3.1/WEB-INF/web.xml"));
Assert.assertEquals("3.1", desc.getVersion());
Assert.assertEquals(0, handler.getErrors().size());
Assert.assertEquals(0, handler.getWarnings().size());
}
}