init
This commit is contained in:
153
test/javax/servlet/http/TestCookie.java
Normal file
153
test/javax/servlet/http/TestCookie.java
Normal 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
test/javax/servlet/http/TestCookieRFC2109Validator.java
Normal file
40
test/javax/servlet/http/TestCookieRFC2109Validator.java
Normal 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");
|
||||
}
|
||||
}
|
||||
40
test/javax/servlet/http/TestCookieRFC6265Validator.java
Normal file
40
test/javax/servlet/http/TestCookieRFC6265Validator.java
Normal 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");
|
||||
}
|
||||
}
|
||||
44
test/javax/servlet/http/TestCookieStrict.java
Normal file
44
test/javax/servlet/http/TestCookieStrict.java
Normal 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);
|
||||
}
|
||||
}
|
||||
207
test/javax/servlet/http/TestHttpServlet.java
Normal file
207
test/javax/servlet/http/TestHttpServlet.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
366
test/javax/servlet/http/TestHttpServletResponseSendError.java
Normal file
366
test/javax/servlet/http/TestHttpServletResponseSendError.java
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user