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,117 @@
/*
* 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.ant;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tools.ant.BuildException;
public class TestDeployTask extends TomcatBaseTest {
@Test
public void bug58086a() {
DeployTask deployTask = new DeployTask() {
@Override
public void execute(String command, InputStream istream, String contentType, long contentLength)
throws BuildException {
Assert.assertEquals("/deploy?path=somepath", command);
Assert.assertEquals("application/octet-stream", contentType);
try {
istream.close();
} catch (IOException e) {
}
}
};
setDefaults(deployTask);
testExecute(deployTask, "file:./test/deployment/context.war");
testExecute(deployTask, new File("test/deployment/context.war").toURI().toString());
testExecute(deployTask, new File("test/deployment/context.war").getAbsolutePath());
testExecute(deployTask, "jar:" + new File("test/deployment/context.jar").toURI().toString() + "!/context.war");
testExecute(deployTask, "file:./test/deployment/dir with spaces/context.war");
testExecute(deployTask, new File("test/deployment/dir with spaces/context.war").toURI().toString());
testExecute(deployTask, new File("test/deployment/dir with spaces/context.war").getAbsolutePath());
testExecute(deployTask, "jar:" + new File("test/deployment/dir with spaces/context.jar").toURI().toString()
+ "!/context.war");
testExecute(deployTask, "file:./test/deployment/dir%20with%20spaces/context.war");
}
@Test(expected = BuildException.class)
public void bug58086b() {
DeployTask deployTask = new DeployTask();
setDefaults(deployTask);
testExecute(deployTask, "scheme:./test/deployment/context.war");
}
@Test(expected = BuildException.class)
public void bug58086c() {
DeployTask deployTask = new DeployTask();
setDefaults(deployTask);
testExecute(deployTask, "sc:./test/deployment/context.war");
}
@Test
public void bug58086d() throws Exception {
Tomcat tomcat = getTomcatInstance();
File root = new File("test/deployment");
tomcat.addWebapp("", root.getAbsolutePath());
tomcat.start();
DeployTask deployTask = new DeployTask() {
@Override
public void execute(String command, InputStream istream, String contentType, long contentLength)
throws BuildException {
Assert.assertEquals("/deploy?path=somepath", command);
Assert.assertEquals("application/octet-stream", contentType);
try {
istream.close();
} catch (IOException e) {
}
}
};
setDefaults(deployTask);
testExecute(deployTask, "http://localhost:" + getPort() + "/context.war");
}
private void setDefaults(DeployTask deployTask) {
deployTask.setUrl("someurl");
deployTask.setUsername("someuser");
deployTask.setPassword("somepassword");
deployTask.setPath("somepath");
}
private void testExecute(DeployTask deployTask, String war) {
deployTask.setWar(war);
deployTask.execute();
}
}

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.authenticator;
import java.util.List;
import java.util.Map;
/**
* This class incorporates test response data
*/
class ResponseDescriptor {
private Map<String, List<String>> headers;
private String body;
private int responseCode;
public Map<String, List<String>> getHeaders() {
return headers;
}
public void setHeaders(Map<String, List<String>> headers) {
this.headers = headers;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public int getResponseCode() {
return responseCode;
}
public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
}

View File

@@ -0,0 +1,170 @@
/*
* 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.authenticator;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.catalina.valves.RemoteIpValve;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
public class TestAuthInfoResponseHeaders extends TomcatBaseTest {
private static String USER = "user";
private static String PWD = "pwd";
private static String ROLE = "role";
private static String URI = "/protected";
private static String CONTEXT_PATH = "/foo";
private static String CLIENT_AUTH_HEADER = "authorization";
/*
* Encapsulate the logic to generate an HTTP header
* for BASIC Authentication.
* Note: only used internally, so no need to validate arguments.
*/
private static final class BasicCredentials {
private final String method;
private final String username;
private final String password;
private final String credentials;
private BasicCredentials(String aMethod,
String aUsername, String aPassword) {
method = aMethod;
username = aUsername;
password = aPassword;
String userCredentials = username + ":" + password;
byte[] credentialsBytes =
userCredentials.getBytes(StandardCharsets.ISO_8859_1);
String base64auth = Base64.encodeBase64String(credentialsBytes);
credentials= method + " " + base64auth;
}
private String getCredentials() {
return credentials;
}
}
@Test
public void testNoHeaders() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false);
}
@Test
public void testWithHeaders() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, true);
}
public void doTest(String user, String pwd, String uri, boolean expectResponseAuthHeaders)
throws Exception {
if (expectResponseAuthHeaders) {
BasicAuthenticator auth =
(BasicAuthenticator) getTomcatInstance().getHost().findChild(
CONTEXT_PATH).getPipeline().getFirst();
auth.setSendAuthInfoResponseHeaders(true);
}
getTomcatInstance().start();
Map<String,List<String>> reqHeaders = new HashMap<>();
List<String> auth = new ArrayList<>();
auth.add(new BasicCredentials("Basic", user, pwd).getCredentials());
reqHeaders.put(CLIENT_AUTH_HEADER, auth);
List<String> forwardedFor = new ArrayList<>();
forwardedFor.add("192.168.0.10");
List<String> forwardedHost = new ArrayList<>();
forwardedHost.add("localhost");
reqHeaders.put("X-Forwarded-For", forwardedFor);
reqHeaders.put("X-Forwarded-Host", forwardedHost);
Map<String,List<String>> respHeaders = new HashMap<>();
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders,
respHeaders);
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", bc.toString());
if (expectResponseAuthHeaders) {
List<String> remoteUsers = respHeaders.get("remote-user");
Assert.assertNotNull(remoteUsers);
Assert.assertEquals(USER, remoteUsers.get(0));
List<String> authTypes = respHeaders.get("auth-type");
Assert.assertNotNull(authTypes);
Assert.assertEquals(HttpServletRequest.BASIC_AUTH, authTypes.get(0));
} else {
Assert.assertFalse(respHeaders.containsKey("remote-user"));
Assert.assertFalse(respHeaders.containsKey("auth-type"));
}
bc.recycle();
}
@Override
public void setUp() throws Exception {
super.setUp();
// Configure a context with digest auth and a single protected resource
Tomcat tomcat = getTomcatInstance();
tomcat.getHost().getPipeline().addValve(new RemoteIpValve());
// No file system docBase required
Context ctxt = tomcat.addContext(CONTEXT_PATH, null);
// Add protected servlet
Tomcat.addServlet(ctxt, "TesterServlet", new TesterServlet());
ctxt.addServletMappingDecoded(URI, "TesterServlet");
SecurityCollection collection = new SecurityCollection();
collection.addPatternDecoded(URI);
SecurityConstraint sc = new SecurityConstraint();
sc.addAuthRole(ROLE);
sc.addCollection(collection);
ctxt.addConstraint(sc);
// Configure the Realm
TesterMapRealm realm = new TesterMapRealm();
realm.addUser(USER, PWD);
realm.addUserRole(USER, ROLE);
ctxt.setRealm(realm);
// Configure the authenticator
LoginConfig lc = new LoginConfig();
lc.setAuthMethod(HttpServletRequest.BASIC_AUTH);
ctxt.setLoginConfig(lc);
ctxt.getPipeline().addValve(new BasicAuthenticator());
}
}

View File

@@ -0,0 +1,177 @@
/*
* 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.authenticator;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.BeforeClass;
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.Realm;
import org.apache.catalina.authenticator.AuthenticatorBase.AllowCorsPreflight;
import org.apache.catalina.filters.AddDefaultCharsetFilter;
import org.apache.catalina.filters.CorsFilter;
import org.apache.catalina.realm.NullRealm;
import org.apache.catalina.servlets.DefaultServlet;
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.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
@RunWith(Parameterized.class)
public class TestAuthenticatorBaseCorsPreflight extends TomcatBaseTest {
private static final String ALLOWED_ORIGIN = "http://example.com";
private static final String EMPTY_ORIGIN = "";
private static final String INVALID_ORIGIN = "http://%20";
private static final String SAME_ORIGIN = "http://localhost";
private static final String ALLOWED_METHOD = "GET";
private static final String BLOCKED_METHOD = "POST";
private static final String EMPTY_METHOD = "";
@Parameterized.Parameters(name = "{index}: input[{0}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
parameterSets.add(new Object[] { AllowCorsPreflight.NEVER, "/*", "OPTIONS", null, null, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", null, null, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.TRUE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", EMPTY_ORIGIN, ALLOWED_METHOD, Boolean.FALSE});
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", INVALID_ORIGIN, ALLOWED_METHOD, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", SAME_ORIGIN, ALLOWED_METHOD, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "GET", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, BLOCKED_METHOD, Boolean.FALSE });
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, EMPTY_METHOD, Boolean.FALSE});
parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, null, Boolean.FALSE});
parameterSets.add(new Object[] { AllowCorsPreflight.FILTER, "/*", "OPTIONS", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.TRUE });
parameterSets.add(new Object[] { AllowCorsPreflight.FILTER, "/x", "OPTIONS", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.FALSE });
return parameterSets;
}
@Parameter(0)
public AllowCorsPreflight allowCorsPreflight;
@Parameter(1)
public String filterMapping;
@Parameter(2)
public String method;
@Parameter(3)
public String origin;
@Parameter(4)
public String accessControl;
@Parameter(5)
public boolean allow;
@BeforeClass
public static void init() {
// So the test can set the origin header
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
}
@Test
public void test() throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp");
Context ctx = tomcat.addContext("", appDir.getAbsolutePath());
Tomcat.addServlet(ctx, "default", new DefaultServlet());
ctx.addServletMappingDecoded("/", "default");
LoginConfig loginConfig = new LoginConfig();
loginConfig.setAuthMethod("BASIC");
ctx.setLoginConfig(loginConfig);
BasicAuthenticator basicAuth = new BasicAuthenticator();
basicAuth.setAllowCorsPreflight(allowCorsPreflight.toString());
ctx.getPipeline().addValve(basicAuth);
Realm realm = new NullRealm();
ctx.setRealm(realm);
SecurityCollection securityCollection = new SecurityCollection();
securityCollection.addPattern("/*");
SecurityConstraint constraint = new SecurityConstraint();
constraint.setAuthConstraint(true);
constraint.addCollection(securityCollection);
ctx.addConstraint(constraint);
// For code coverage
FilterDef otherFilter = new FilterDef();
otherFilter.setFilterName("other");
otherFilter.setFilterClass(AddDefaultCharsetFilter.class.getName());
FilterMap otherMap = new FilterMap();
otherMap.setFilterName("other");
otherMap.addURLPatternDecoded("/other");
ctx.addFilterDef(otherFilter);
ctx.addFilterMap(otherMap);
FilterDef corsFilter = new FilterDef();
corsFilter.setFilterName("cors");
corsFilter.setFilterClass(CorsFilter.class.getName());
corsFilter.addInitParameter(CorsFilter.PARAM_CORS_ALLOWED_ORIGINS, ALLOWED_ORIGIN);
corsFilter.addInitParameter(CorsFilter.PARAM_CORS_ALLOWED_METHODS, ALLOWED_METHOD);
FilterMap corsFilterMap = new FilterMap();
corsFilterMap.setFilterName("cors");
corsFilterMap.addURLPatternDecoded(filterMapping);
ctx.addFilterDef(corsFilter);
ctx.addFilterMap(corsFilterMap);
tomcat.start();
Map<String,List<String>> reqHead = new HashMap<>();
if (origin != null) {
List<String> values = new ArrayList<>();
if (SAME_ORIGIN.equals(origin)) {
values.add(origin + ":" + getPort());
} else {
values.add(origin);
}
reqHead.put(CorsFilter.REQUEST_HEADER_ORIGIN, values);
}
if (accessControl != null) {
List<String> values = new ArrayList<>();
values.add(accessControl);
reqHead.put(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, values);
}
ByteChunk out = new ByteChunk();
int rc = methodUrl("http://localhost:" + getPort() + "/target", out, 300000, reqHead, null, method, false);
if (allow) {
Assert.assertEquals(200, rc);
} else {
Assert.assertEquals(403, rc);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,397 @@
/*
* 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.authenticator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.unittest.TesterContext;
import org.apache.tomcat.unittest.TesterServletContext;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomcat.util.security.ConcurrentMessageDigest;
import org.apache.tomcat.util.security.MD5Encoder;
public class TestDigestAuthenticator extends TomcatBaseTest {
private static String USER = "user";
private static String PWD = "pwd";
private static String ROLE = "role";
private static String URI = "/protected";
private static String QUERY = "?foo=bar";
private static String CONTEXT_PATH = "/foo";
private static String CLIENT_AUTH_HEADER = "authorization";
private static String REALM = "TestRealm";
private static String CNONCE = "cnonce";
private static String NC1 = "00000001";
private static String NC2 = "00000002";
private static String QOP = "auth";
@Test
public void bug54521() throws LifecycleException {
DigestAuthenticator digestAuthenticator = new DigestAuthenticator();
TesterContext context = new TesterContext();
context.setServletContext(new TesterServletContext());
digestAuthenticator.setContainer(context);
digestAuthenticator.start();
Request request = new TesterRequest();
final int count = 1000;
Set<String> nonces = new HashSet<>();
for (int i = 0; i < count; i++) {
nonces.add(digestAuthenticator.generateNonce(request));
}
Assert.assertEquals(count, nonces.size());
}
@Test
public void testAllValid() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, CNONCE, QOP, true, true);
}
@Test
public void testValidNoQop() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
null, null, null, null, true, true);
}
@Test
public void testValidQuery() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI + QUERY, false, true, REALM, true,
true, NC1, NC2, CNONCE, QOP, true, true);
}
@Test
public void testInvalidUriFail() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, true, true, REALM, true, true,
NC1, NC2, CNONCE, QOP, false, false);
}
@Test
public void testInvalidUriPass() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, true, false, REALM, true, true,
NC1, NC2, CNONCE, QOP, true, true);
}
@Test
public void testInvalidRealm() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, "null", true, true,
NC1, NC2, CNONCE, QOP, false, false);
}
@Test
public void testInvalidNonce() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, false, true,
NC1, NC2, CNONCE, QOP, false, true);
}
@Test
public void testInvalidOpaque() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, false,
NC1, NC2, CNONCE, QOP, false, true);
}
@Test
public void testInvalidNc1() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
"null", null, CNONCE, QOP, false, false);
}
@Test
public void testInvalidQop() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, CNONCE, "null", false, false);
}
@Test
public void testInvalidQopCombo1() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, CNONCE, null, false, false);
}
@Test
public void testInvalidQopCombo2() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, null, QOP, false, false);
}
@Test
public void testInvalidQopCombo3() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC2, null, null, false, false);
}
@Test
public void testInvalidQopCombo4() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
null, null, CNONCE, QOP, false, false);
}
@Test
public void testInvalidQopCombo5() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
null, null, CNONCE, null, false, false);
}
@Test
public void testInvalidQopCombo6() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
null, null, null, QOP, false, false);
}
@Test
public void testReplay() throws Exception {
doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true,
NC1, NC1, CNONCE, QOP, true, false);
}
public void doTest(String user, String pwd, String uri, boolean breakUri,
boolean validateUri, String realm, boolean useServerNonce,
boolean useServerOpaque, String nc1, String nc2, String cnonce,
String qop, boolean req2expect200, boolean req3expect200)
throws Exception {
if (!validateUri) {
DigestAuthenticator auth =
(DigestAuthenticator) getTomcatInstance().getHost().findChild(
CONTEXT_PATH).getPipeline().getFirst();
auth.setValidateUri(false);
}
getTomcatInstance().start();
String digestUri;
if (breakUri) {
digestUri = "/broken" + uri;
} else {
digestUri = uri;
}
List<String> auth = new ArrayList<>();
auth.add(buildDigestResponse(user, pwd, digestUri, realm, "null",
"null", nc1, cnonce, qop));
Map<String,List<String>> reqHeaders = new HashMap<>();
reqHeaders.put(CLIENT_AUTH_HEADER, auth);
Map<String,List<String>> respHeaders = new HashMap<>();
// The first request will fail - but we need to extract the nonce
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders,
respHeaders);
Assert.assertEquals(401, rc);
Assert.assertTrue(bc.getLength() > 0);
bc.recycle();
// Second request should succeed (if we use the server nonce)
auth.clear();
if (useServerNonce) {
if (useServerOpaque) {
auth.add(buildDigestResponse(user, pwd, digestUri, realm,
getNonce(respHeaders), getOpaque(respHeaders), nc1,
cnonce, qop));
} else {
auth.add(buildDigestResponse(user, pwd, digestUri, realm,
getNonce(respHeaders), "null", nc1, cnonce, qop));
}
} else {
auth.add(buildDigestResponse(user, pwd, digestUri, realm,
"null", getOpaque(respHeaders), nc1, cnonce, QOP));
}
rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders,
null);
if (req2expect200) {
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", bc.toString());
} else {
Assert.assertEquals(401, rc);
Assert.assertTrue(bc.getLength() > 0);
}
// Third request should succeed if we increment nc
auth.clear();
bc.recycle();
auth.add(buildDigestResponse(user, pwd, digestUri, realm,
getNonce(respHeaders), getOpaque(respHeaders), nc2, cnonce,
qop));
rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders,
null);
if (req3expect200) {
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", bc.toString());
} else {
Assert.assertEquals(401, rc);
Assert.assertTrue(bc.getLength() > 0);
}
}
@Override
public void setUp() throws Exception {
super.setUp();
// Configure a context with digest auth and a single protected resource
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctxt = tomcat.addContext(CONTEXT_PATH, null);
// Add protected servlet
Tomcat.addServlet(ctxt, "TesterServlet", new TesterServlet());
ctxt.addServletMappingDecoded(URI, "TesterServlet");
SecurityCollection collection = new SecurityCollection();
collection.addPatternDecoded(URI);
SecurityConstraint sc = new SecurityConstraint();
sc.addAuthRole(ROLE);
sc.addCollection(collection);
ctxt.addConstraint(sc);
// Configure the Realm
TesterMapRealm realm = new TesterMapRealm();
realm.addUser(USER, PWD);
realm.addUserRole(USER, ROLE);
ctxt.setRealm(realm);
// Configure the authenticator
LoginConfig lc = new LoginConfig();
lc.setAuthMethod("DIGEST");
lc.setRealmName(REALM);
ctxt.setLoginConfig(lc);
ctxt.getPipeline().addValve(new DigestAuthenticator());
}
protected static String getNonce(Map<String,List<String>> respHeaders) {
List<String> authHeaders =
respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME);
// Assume there is only one
String authHeader = authHeaders.iterator().next();
int start = authHeader.indexOf("nonce=\"") + 7;
int end = authHeader.indexOf('\"', start);
return authHeader.substring(start, end);
}
protected static String getOpaque(Map<String,List<String>> respHeaders) {
List<String> authHeaders =
respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME);
// Assume there is only one
String authHeader = authHeaders.iterator().next();
int start = authHeader.indexOf("opaque=\"") + 8;
int end = authHeader.indexOf('\"', start);
return authHeader.substring(start, end);
}
/*
* Notes from RFC2617
* H(data) = MD5(data)
* KD(secret, data) = H(concat(secret, ":", data))
* A1 = unq(username-value) ":" unq(realm-value) ":" passwd
* A2 = Method ":" digest-uri-value
* request-digest = <"> < KD ( H(A1), unq(nonce-value)
":" nc-value
":" unq(cnonce-value)
":" unq(qop-value)
":" H(A2)
) <">
*/
private static String buildDigestResponse(String user, String pwd,
String uri, String realm, String nonce, String opaque, String nc,
String cnonce, String qop) {
String a1 = user + ":" + realm + ":" + pwd;
String a2 = "GET:" + uri;
String md5a1 = digest(a1);
String md5a2 = digest(a2);
String response;
if (qop == null) {
response = md5a1 + ":" + nonce + ":" + md5a2;
} else {
response = md5a1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" +
qop + ":" + md5a2;
}
String md5response = digest(response);
StringBuilder auth = new StringBuilder();
auth.append("Digest username=\"");
auth.append(user);
auth.append("\", realm=\"");
auth.append(realm);
auth.append("\", nonce=\"");
auth.append(nonce);
auth.append("\", uri=\"");
auth.append(uri);
auth.append("\", opaque=\"");
auth.append(opaque);
auth.append("\", response=\"");
auth.append(md5response);
auth.append("\"");
if (qop != null) {
auth.append(", qop=");
auth.append(qop);
auth.append("");
}
if (nc != null) {
auth.append(", nc=");
auth.append(nc);
}
if (cnonce != null) {
auth.append(", cnonce=\"");
auth.append(cnonce);
auth.append("\"");
}
return auth.toString();
}
private static String digest(String input) {
return MD5Encoder.encode(
ConcurrentMessageDigest.digestMD5(input.getBytes()));
}
private static class TesterRequest extends Request {
@Override
public String getRemoteAddr() {
return "127.0.0.1";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
/**
* 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.authenticator;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.authenticator.jaspic.CallbackHandlerImpl;
import org.apache.catalina.connector.Request;
public class TestJaspicCallbackHandlerInAuthenticator {
@Test
public void testCustomCallbackHandlerCreation() throws Exception {
testCallbackHandlerCreation("org.apache.catalina.authenticator.TestCallbackHandlerImpl",
TestCallbackHandlerImpl.class);
}
@Test
public void testDefaultCallbackHandlerCreation() throws Exception {
testCallbackHandlerCreation(null, CallbackHandlerImpl.class);
}
private void testCallbackHandlerCreation(String callbackHandlerImplClassName,
Class<?> callbackHandlerImplClass)
throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
TestAuthenticator authenticator = new TestAuthenticator();
authenticator.setJaspicCallbackHandlerClass(callbackHandlerImplClassName);
Method createCallbackHandlerMethod =
AuthenticatorBase.class.getDeclaredMethod("createCallbackHandler");
createCallbackHandlerMethod.setAccessible(true);
CallbackHandler callbackHandler =
(CallbackHandler) createCallbackHandlerMethod.invoke(authenticator);
Assert.assertTrue(callbackHandlerImplClass.isInstance(callbackHandler));
}
private static class TestAuthenticator extends AuthenticatorBase {
@Override
protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
return false;
}
@Override
protected String getAuthMethod() {
return null;
}
}
}
class TestCallbackHandlerImpl implements CallbackHandler {
public TestCallbackHandlerImpl() {
// Default constructor required by reflection
}
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
// don't have to do anything; needed only for instantiation
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,274 @@
/*
* 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.authenticator;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Request;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.filters.TesterHttpServletResponse;
import org.apache.catalina.startup.TesterMapRealm;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.security.ConcurrentMessageDigest;
import org.apache.tomcat.util.security.MD5Encoder;
public class TesterDigestAuthenticatorPerformance {
private static String USER = "user";
private static String PWD = "pwd";
private static String ROLE = "role";
private static String METHOD = "GET";
private static String URI = "/protected";
private static String CONTEXT_PATH = "/foo";
private static String CLIENT_AUTH_HEADER = "authorization";
private static String REALM = "TestRealm";
private static String QOP = "auth";
private static final AtomicInteger nonceCount = new AtomicInteger(0);
private DigestAuthenticator authenticator = new DigestAuthenticator();
@Test
public void testSimple() throws Exception {
doTest(4, 1000000);
}
public void doTest(int threadCount, int requestCount) throws Exception {
TesterRunnable runnables[] = new TesterRunnable[threadCount];
Thread threads[] = new Thread[threadCount];
String nonce = authenticator.generateNonce(new TesterDigestRequest());
// Create the runnables & threads
for (int i = 0; i < threadCount; i++) {
runnables[i] =
new TesterRunnable(authenticator, nonce, requestCount);
threads[i] = new Thread(runnables[i]);
}
long start = System.currentTimeMillis();
// Start the threads
for (int i = 0; i < threadCount; i++) {
threads[i].start();
}
// Wait for the threads to finish
for (int i = 0; i < threadCount; i++) {
threads[i].join();
}
double wallTime = System.currentTimeMillis() - start;
// Gather the results...
double totalTime = 0;
int totalSuccess = 0;
for (int i = 0; i < threadCount; i++) {
System.out.println("Thread: " + i + " Success: " +
runnables[i].getSuccess());
totalSuccess = totalSuccess + runnables[i].getSuccess();
totalTime = totalTime + runnables[i].getTime();
}
System.out.println("Average time per request (user): " +
totalTime/(threadCount * requestCount));
System.out.println("Average time per request (wall): " +
wallTime/(threadCount * requestCount));
Assert.assertEquals(((long)requestCount) * threadCount, totalSuccess);
}
@Before
public void setUp() throws Exception {
ConcurrentMessageDigest.init("MD5");
// Configure the Realm
TesterMapRealm realm = new TesterMapRealm();
realm.addUser(USER, PWD);
realm.addUserRole(USER, ROLE);
// Add the Realm to the Context
Context context = new StandardContext();
context.setName(CONTEXT_PATH);
context.setRealm(realm);
// Configure the Login config
LoginConfig config = new LoginConfig();
config.setRealmName(REALM);
context.setLoginConfig(config);
// Make the Context and Realm visible to the Authenticator
authenticator.setContainer(context);
authenticator.setNonceCountWindowSize(8 * 1024);
authenticator.start();
}
private static class TesterRunnable implements Runnable {
private String nonce;
private int requestCount;
private int success = 0;
private long time = 0;
private TesterDigestRequest request;
private HttpServletResponse response;
private DigestAuthenticator authenticator;
private static final String A1 = USER + ":" + REALM + ":" + PWD;
private static final String A2 = METHOD + ":" + CONTEXT_PATH + URI;
private static final String MD5A1 = MD5Encoder.encode(
ConcurrentMessageDigest.digest("MD5", A1.getBytes()));
private static final String MD5A2 = MD5Encoder.encode(
ConcurrentMessageDigest.digest("MD5", A2.getBytes()));
// All init code should be in here. run() needs to be quick
public TesterRunnable(DigestAuthenticator authenticator,
String nonce, int requestCount) throws Exception {
this.authenticator = authenticator;
this.nonce = nonce;
this.requestCount = requestCount;
request = new TesterDigestRequest();
request.getMappingData().context = authenticator.context;
response = new TesterHttpServletResponse();
}
@Override
public void run() {
long start = System.currentTimeMillis();
for (int i = 0; i < requestCount; i++) {
try {
request.setAuthHeader(buildDigestResponse(nonce));
if (authenticator.authenticate(request, response)) {
success++;
}
// Clear out authenticated user ready for next iteration
request.setUserPrincipal(null);
} catch (IOException ioe) {
// Ignore
}
}
time = System.currentTimeMillis() - start;
}
public int getSuccess() {
return success;
}
public long getTime() {
return time;
}
private String buildDigestResponse(String nonce) {
String ncString = String.format("%1$08x",
Integer.valueOf(nonceCount.incrementAndGet()));
String cnonce = "cnonce";
String response = MD5A1 + ":" + nonce + ":" + ncString + ":" +
cnonce + ":" + QOP + ":" + MD5A2;
String md5response = MD5Encoder.encode(
ConcurrentMessageDigest.digest("MD5", response.getBytes()));
StringBuilder auth = new StringBuilder();
auth.append("Digest username=\"");
auth.append(USER);
auth.append("\", realm=\"");
auth.append(REALM);
auth.append("\", nonce=\"");
auth.append(nonce);
auth.append("\", uri=\"");
auth.append(CONTEXT_PATH + URI);
auth.append("\", opaque=\"");
auth.append(authenticator.getOpaque());
auth.append("\", response=\"");
auth.append(md5response);
auth.append("\"");
auth.append(", qop=");
auth.append(QOP);
auth.append(", nc=");
auth.append(ncString);
auth.append(", cnonce=\"");
auth.append(cnonce);
auth.append("\"");
return auth.toString();
}
}
private static class TesterDigestRequest extends Request {
private String authHeader = null;
@Override
public String getRemoteAddr() {
return "127.0.0.1";
}
public void setAuthHeader(String authHeader) {
this.authHeader = authHeader;
}
@Override
public String getHeader(String name) {
if (CLIENT_AUTH_HEADER.equalsIgnoreCase(name)) {
return authHeader;
} else {
return super.getHeader(name);
}
}
@Override
public String getMethod() {
return METHOD;
}
@Override
public String getQueryString() {
return null;
}
@Override
public String getRequestURI() {
return CONTEXT_PATH + URI;
}
@Override
public org.apache.coyote.Request getCoyoteRequest() {
return new org.apache.coyote.Request();
}
}
}

View File

@@ -0,0 +1,446 @@
/**
* 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.authenticator.jaspic;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.security.auth.message.config.AuthConfigFactory;
import javax.security.auth.message.config.AuthConfigProvider;
import javax.security.auth.message.config.RegistrationListener;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.catalina.Globals;
public class TestAuthConfigFactoryImpl {
private String oldCatalinaBase;
private static final File TEST_CONFIG_FILE = new File("test/conf/jaspic-providers.xml");
@Test
public void testRegistrationNullLayer() {
doTestResistration(null, "AC_1", ":AC_1");
}
@Test
public void testRegistrationNullAppContext() {
doTestResistration("L_1", null, "L_1:");
}
@Test
public void testRegistrationNullLayerAndNullAppContext() {
doTestResistration(null, null, ":");
}
@Test
public void testSearchNoMatch01() {
doTestSearchOrder("foo", "bar", 1);
}
@Test
public void testSearchNoMatch02() {
doTestSearchOrder(null, "bar", 1);
}
@Test
public void testSearchNoMatch03() {
doTestSearchOrder("foo", null, 1);
}
@Test
public void testSearchNoMatch04() {
doTestSearchOrder(null, null, 1);
}
@Test
public void testSearchOnlyAppContextMatch01() {
doTestSearchOrder("foo", "AC_1", 2);
}
@Test
public void testSearchOnlyAppContextMatch02() {
doTestSearchOrder(null, "AC_1", 2);
}
@Test
public void testSearchOnlyAppContextMatch03() {
doTestSearchOrder("L_2", "AC_1", 2);
}
@Test
public void testSearchOnlyLayerMatch01() {
doTestSearchOrder("L_1", "bar", 3);
}
@Test
public void testSearchOnlyLayerMatch02() {
doTestSearchOrder("L_1", null, 3);
}
@Test
public void testSearchOnlyLayerMatch03() {
doTestSearchOrder("L_1", "AC_2", 3);
}
@Test
public void testSearchBothMatch() {
doTestSearchOrder("L_2", "AC_2", 4);
}
private void doTestSearchOrder(String layer, String appContext, int expected) {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp1, null, null, "1");
AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp2, null, "AC_1", "2");
AuthConfigProvider acp3 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp3, "L_1", null, "3");
AuthConfigProvider acp4 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp4, "L_2", "AC_2", "4");
AuthConfigProvider searchResult = factory.getConfigProvider(layer, appContext, null);
int searchIndex;
if (searchResult == acp1) {
searchIndex = 1;
} else if (searchResult == acp2) {
searchIndex = 2;
} else if (searchResult == acp3) {
searchIndex = 3;
} else if (searchResult == acp4) {
searchIndex = 4;
} else {
searchIndex = -1;
}
Assert.assertEquals(expected, searchIndex);
}
private void doTestResistration(String layer, String appContext, String expectedRegId) {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
SimpleRegistrationListener listener = new SimpleRegistrationListener(layer, appContext);
String regId = factory.registerConfigProvider(acp1, layer, appContext, null);
Assert.assertEquals(expectedRegId, regId);
factory.getConfigProvider(layer, appContext, listener);
factory.removeRegistration(regId);
Assert.assertTrue(listener.wasCorrectlyCalled());
listener.reset();
factory.registerConfigProvider(acp1, layer, appContext, null);
factory.getConfigProvider(layer, appContext, listener);
// Replace it
AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp2, layer, appContext, null);
Assert.assertTrue(listener.wasCorrectlyCalled());
}
@Test
public void testRegistrationInsertExact01() {
doTestRegistrationInsert("L_3", "AC_2", "L_3", "AC_2");
}
@Test
public void testRegistrationInsertExact02() {
doTestRegistrationInsert("L_2", "AC_3", "L_2", "AC_3");
}
@Test
public void testRegistrationInsertExact03() {
doTestRegistrationInsert("L_4", "AC_4", "L_4", "AC_4");
}
@Test
public void testRegistrationInsertAppContext01() {
doTestRegistrationInsert(null, "AC_3", "L_2", "AC_3");
}
@Test
public void testRegistrationInsertAppContext02() {
doTestRegistrationInsert(null, "AC_4", "L_4", "AC_4");
}
@Test
public void testRegistrationInsertLayer01() {
doTestRegistrationInsert("L_4", null, "L_4", "AC_4");
}
private void doTestRegistrationInsert(String newLayer, String newAppContext,
String expectedListenerLayer, String expectedListenerAppContext) {
// Set up
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp2, null, "AC_2", null);
AuthConfigProvider acp3 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp3, "L_2", null, null);
AuthConfigProvider acp4 = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acp4, null, null, null);
SimpleRegistrationListener listener1 = new SimpleRegistrationListener("L_1", "AC_1");
factory.getConfigProvider("L_1", "AC_1", listener1);
SimpleRegistrationListener listener2 = new SimpleRegistrationListener("L_3", "AC_2");
factory.getConfigProvider("L_3", "AC_2", listener2);
SimpleRegistrationListener listener3 = new SimpleRegistrationListener("L_2", "AC_3");
factory.getConfigProvider("L_2", "AC_3", listener3);
SimpleRegistrationListener listener4 = new SimpleRegistrationListener("L_4", "AC_4");
factory.getConfigProvider("L_4", "AC_4", listener4);
List<SimpleRegistrationListener> listeners = new ArrayList<>();
listeners.add(listener1);
listeners.add(listener2);
listeners.add(listener3);
listeners.add(listener4);
// Register a new provider that will impact some existing registrations
AuthConfigProvider acpNew = new SimpleAuthConfigProvider(null, null);
factory.registerConfigProvider(acpNew, newLayer, newAppContext, null);
// Check to see if the expected listener fired.
for (SimpleRegistrationListener listener : listeners) {
if (listener.wasCalled()) {
Assert.assertEquals(listener.layer, expectedListenerLayer);
Assert.assertEquals(listener.appContext, expectedListenerAppContext);
Assert.assertTrue(listener.wasCorrectlyCalled());
} else {
Assert.assertFalse((listener.layer.equals(expectedListenerLayer) &&
listener.appContext.equals(expectedListenerAppContext)));
}
}
}
@Test
public void testDetachListenerNonexistingRegistration() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
String registrationId = factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
SimpleRegistrationListener listener1 = new SimpleRegistrationListener("L_1", "AC_1");
factory.getConfigProvider("L_1", "AC_1", listener1);
factory.removeRegistration(registrationId);
String[] registrationIds = factory.detachListener(listener1, "L_1", "AC_1");
Assert.assertTrue(registrationIds.length == 0);
}
@Test
public void testDetachListener() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
String registrationId = factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
SimpleRegistrationListener listener1 = new SimpleRegistrationListener("L_1", "AC_1");
factory.getConfigProvider("L_1", "AC_1", listener1);
String[] registrationIds = factory.detachListener(listener1, "L_1", "AC_1");
Assert.assertTrue(registrationIds.length == 1);
Assert.assertEquals(registrationId, registrationIds[0]);
}
@Test
public void testRegistrationNullListener() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
String registrationId = factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
factory.getConfigProvider("L_1", "AC_1", null);
boolean result = factory.removeRegistration(registrationId);
Assert.assertTrue(result);
}
@Test
public void testAllRegistrationIds() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
String registrationId1 = factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
String registrationId2 = factory.registerConfigProvider(acp2, "L_2", "AC_2", null);
String[] registrationIds = factory.getRegistrationIDs(null);
Assert.assertTrue(registrationIds.length == 2);
Set<String> ids = new HashSet<>(Arrays.asList(registrationIds));
Assert.assertTrue(ids.contains(registrationId1));
Assert.assertTrue(ids.contains(registrationId2));
}
@Before
public void setUp() {
// set CATALINA_BASE to test so that the file with persistent providers will be written in test/conf folder
oldCatalinaBase = System.getProperty(Globals.CATALINA_BASE_PROP);
System.setProperty(Globals.CATALINA_BASE_PROP, "test");
if (TEST_CONFIG_FILE.exists()) {
if (!TEST_CONFIG_FILE.delete()) {
Assert.fail("Failed to delete " + TEST_CONFIG_FILE);
}
}
}
@After
public void cleanUp() {
if (oldCatalinaBase != null ) {
System.setProperty(Globals.CATALINA_BASE_PROP, oldCatalinaBase);
} else {
System.clearProperty(Globals.CATALINA_BASE_PROP);
}
if (TEST_CONFIG_FILE.exists()) {
if (!TEST_CONFIG_FILE.delete()) {
Assert.fail("Failed to delete " + TEST_CONFIG_FILE);
}
}
}
@Test
public void testRemovePersistentRegistration() {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
factory.registerConfigProvider(
SimpleAuthConfigProvider.class.getName(), null, "L_1", "AC_1", null);
String registrationId2 = factory.registerConfigProvider(
SimpleAuthConfigProvider.class.getName(), null, "L_2", "AC_2", null);
factory.removeRegistration(registrationId2);
factory.refresh();
String[] registrationIds = factory.getRegistrationIDs(null);
for (String registrationId : registrationIds) {
Assert.assertNotEquals(registrationId2, registrationId);
}
}
@Test
public void testRegistrationNullClassName() {
doTestNullClassName(false, "L_1", "AC_1");
}
@Test
public void testRegistrationNullClassOverrideExisting() {
doTestNullClassName(true, "L_1", "AC_1");
}
@Test
public void testRegistrationNullClassNullLayerNullAppContext() {
doTestNullClassName(false, null, null);
}
private void doTestNullClassName(boolean shouldOverrideExistingProvider, String layer, String appContext) {
AuthConfigFactory factory = new AuthConfigFactoryImpl();
if (shouldOverrideExistingProvider) {
factory.registerConfigProvider(SimpleAuthConfigProvider.class.getName(), null, layer, appContext, null);
}
String registrationId = factory.registerConfigProvider(null, null, layer, appContext, null);
factory.refresh();
String[] registrationIds = factory.getRegistrationIDs(null);
Set<String> ids = new HashSet<>(Arrays.asList(registrationIds));
Assert.assertTrue(ids.contains(registrationId));
AuthConfigProvider provider = factory.getConfigProvider(layer, appContext, null);
Assert.assertNull(provider);
}
private static class SimpleRegistrationListener implements RegistrationListener {
private final String layer;
private final String appContext;
private boolean called = false;
private String layerNotified;
private String appContextNotified;
public SimpleRegistrationListener(String layer, String appContext) {
this.layer = layer;
this.appContext = appContext;
}
@Override
public void notify(String layer, String appContext) {
called = true;
layerNotified = layer;
appContextNotified = appContext;
}
public boolean wasCalled() {
return called;
}
public boolean wasCorrectlyCalled() {
return called && areTheSame(layer, layerNotified) &&
areTheSame(appContext, appContextNotified);
}
public void reset() {
called = false;
layerNotified = null;
appContextNotified = null;
}
private static boolean areTheSame(String a, String b) {
if (a == null) {
return b == null;
}
return a.equals(b);
}
}
}

View File

@@ -0,0 +1,135 @@
/**
* 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.authenticator.jaspic;
import java.io.File;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations.Provider;
import org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations.Providers;
public class TestPersistentProviderRegistrations {
@Test
public void testLoadEmpty() {
File f = new File("test/conf/jaspic-test-01.xml");
Providers result = PersistentProviderRegistrations.loadProviders(f);
Assert.assertEquals(0, result.getProviders().size());
}
@Test
public void testLoadSimple() {
File f = new File("test/conf/jaspic-test-02.xml");
Providers result = PersistentProviderRegistrations.loadProviders(f);
validateSimple(result);
}
private void validateSimple(Providers providers) {
Assert.assertEquals(1, providers.getProviders().size());
Provider p = providers.getProviders().get(0);
Assert.assertEquals("a", p.getClassName());
Assert.assertEquals("b", p.getLayer());
Assert.assertEquals("c", p.getAppContext());
Assert.assertEquals("d", p.getDescription());
Assert.assertEquals(2, p.getProperties().size());
Assert.assertEquals("f", p.getProperties().get("e"));
Assert.assertEquals("h", p.getProperties().get("g"));
}
@Test
public void testSaveSimple() {
File f = new File("test/conf/jaspic-test-03.xml");
if (f.exists()) {
Assert.assertTrue(f.delete());
}
// Create a config and write it out
Providers start = new Providers();
Provider p = new Provider();
p.setClassName("a");
p.setLayer("b");
p.setAppContext("c");
p.setDescription("d");
p.addProperty("e", "f");
p.addProperty("g", "h");
start.addProvider(p);
PersistentProviderRegistrations.writeProviders(start, f);
// Read it back
Providers end = PersistentProviderRegistrations.loadProviders(f);
validateSimple(end);
if (f.exists()) {
Assert.assertTrue("Failed to clean up [" + f + "]", f.delete());
}
}
@Test
public void testLoadProviderWithoutLayerAndAC() {
File f = new File("test/conf/jaspic-test-04.xml");
Providers providers = PersistentProviderRegistrations.loadProviders(f);
validateNoLayerAndAC(providers);
}
private void validateNoLayerAndAC(Providers providers) {
Assert.assertEquals(1, providers.getProviders().size());
Provider p = providers.getProviders().get(0);
Assert.assertEquals("a", p.getClassName());
Assert.assertNull(p.getLayer());
Assert.assertNull(p.getAppContext());
Assert.assertEquals("d", p.getDescription());
}
@Test
public void testSaveProviderWithoutLayerAndAC() {
File f = new File("test/conf/jaspic-test-05.xml");
if (f.exists()) {
Assert.assertTrue(f.delete());
}
// Create a config and write it out
Providers initialProviders = new Providers();
Provider p = new Provider();
p.setClassName("a");
p.setDescription("d");
initialProviders.addProvider(p);
PersistentProviderRegistrations.writeProviders(initialProviders, f);
// Read it back
Providers loadedProviders = PersistentProviderRegistrations.loadProviders(f);
try {
validateNoLayerAndAC(loadedProviders);
} finally {
if (f.exists()) {
if (!f.delete()) {
Assert.fail("Failed to delete " + f);
}
}
}
}
}

View File

@@ -0,0 +1,74 @@
/**
* 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.authenticator.jaspic;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.config.ServerAuthConfig;
import javax.security.auth.message.config.ServerAuthContext;
import org.junit.Assert;
import org.junit.Test;
public class TestSimpleServerAuthConfig {
private static final String SERVER_AUTH_MODULE_KEY_PREFIX =
"org.apache.catalina.authenticator.jaspic.ServerAuthModule.";
private static final Map<String,String> CONFIG_PROPERTIES;
static {
CONFIG_PROPERTIES = new HashMap<>();
CONFIG_PROPERTIES.put(SERVER_AUTH_MODULE_KEY_PREFIX + "1",
TesterServerAuthModuleA.class.getName());
}
@Test
public void testConfigOnServerAuthConfig() throws Exception {
ServerAuthConfig serverAuthConfig =
new SimpleServerAuthConfig(null, null, null, CONFIG_PROPERTIES);
ServerAuthContext serverAuthContext = serverAuthConfig.getAuthContext(null, null, null);
validateServerAuthContext(serverAuthContext);
}
@Test
public void testConfigOnGetAuthContext() throws Exception {
ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null);
ServerAuthContext serverAuthContext =
serverAuthConfig.getAuthContext(null, null, CONFIG_PROPERTIES);
validateServerAuthContext(serverAuthContext);
}
@Test(expected=AuthException.class)
public void testConfigNone() throws Exception {
ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null);
serverAuthConfig.getAuthContext(null, null, null);
}
private void validateServerAuthContext(ServerAuthContext serverAuthContext) throws Exception {
MessageInfo msgInfo = new TesterMessageInfo();
serverAuthContext.cleanSubject(msgInfo, null);
Assert.assertEquals("init()-cleanSubject()-", msgInfo.getMap().get("trace"));
}
}

View File

@@ -0,0 +1,55 @@
/**
* 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.authenticator.jaspic;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.message.MessageInfo;
public class TesterMessageInfo implements MessageInfo {
private Object requestMessage;
private Object responseMessage;
private final Map<String,String> map = new HashMap<>();
@Override
public Object getRequestMessage() {
return requestMessage;
}
@Override
public Object getResponseMessage() {
return responseMessage;
}
@Override
public void setRequestMessage(Object request) {
requestMessage = request;
}
@Override
public void setResponseMessage(Object response) {
responseMessage = response;
}
@SuppressWarnings("rawtypes")
@Override
public Map getMap() {
return map;
}
}

View File

@@ -0,0 +1,64 @@
/**
* 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.authenticator.jaspic;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.message.AuthException;
import javax.security.auth.message.AuthStatus;
import javax.security.auth.message.MessageInfo;
import javax.security.auth.message.MessagePolicy;
import javax.security.auth.message.module.ServerAuthModule;
public class TesterServerAuthModuleA implements ServerAuthModule {
private StringBuilder trace = new StringBuilder("init()-");
@Override
public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject,
Subject serviceSubject) throws AuthException {
return null;
}
@Override
public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject)
throws AuthException {
return null;
}
@SuppressWarnings("unchecked")
@Override
public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException {
trace.append("cleanSubject()-");
messageInfo.getMap().put("trace", trace.toString());
}
@Override
public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
CallbackHandler handler, @SuppressWarnings("rawtypes") Map options)
throws AuthException {
// NO-OP
}
@SuppressWarnings("rawtypes")
@Override
public Class[] getSupportedMessageTypes() {
return null;
}
}

View File

@@ -0,0 +1,173 @@
/*
* 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.connector;
import java.io.File;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Servlet;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.servlets.DefaultServlet;
import org.apache.catalina.servlets.WebdavServlet;
import org.apache.catalina.startup.TesterServlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
/**
* Test cases for {@link Connector}.
*/
public class TestConnector extends TomcatBaseTest {
@Test
public void testStop() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
Wrapper w =
Tomcat.addServlet(root, "tester", new TesterServlet());
w.setAsyncSupported(true);
root.addServletMappingDecoded("/", "tester");
Connector connector = tomcat.getConnector();
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + "/", bc, null, null);
Assert.assertEquals(200, rc);
Assert.assertEquals("OK", bc.toString());
rc = -1;
bc.recycle();
connector.stop();
try {
rc = getUrl("http://localhost:" + getPort() + "/", bc, 1000,
null, null);
} catch (SocketTimeoutException ste) {
// May also see this with NIO
// Make sure the test passes if we do
rc = 503;
}
Assert.assertEquals(503, rc);
}
@Test
public void testPort() throws Exception {
Tomcat tomcat = getTomcatInstance();
Connector connector1 = tomcat.getConnector();
connector1.setPort(0);
Connector connector2 = new Connector();
connector2.setPort(0);
tomcat.getService().addConnector(connector2);
tomcat.start();
int localPort1 = connector1.getLocalPort();
int localPort2 = connector2.getLocalPort();
Assert.assertTrue(localPort1 > 0);
Assert.assertTrue(localPort2 > 0);
}
@Test
public void testTraceAllowedDefault() throws Exception {
doTestTrace(new DefaultServlet(), true);
}
@Test
public void testTraceNotAllowedDefault() throws Exception {
doTestTrace(new DefaultServlet(), false);
}
@Test
public void testTraceAllowedWebDav() throws Exception {
doTestTrace(new WebdavServlet(), true);
}
@Test
public void testTraceNotAllowedWebDav() throws Exception {
doTestTrace(new WebdavServlet(), false);
}
@Test
public void testTraceAllowedCustom() throws Exception {
doTestTrace(new TesterServlet(), true);
}
@Test
public void testTraceNotAllowedCustom() throws Exception {
doTestTrace(new TesterServlet(), false);
}
private void doTestTrace(Servlet servlet, boolean allowTrace) throws Exception {
Tomcat tomcat = getTomcatInstance();
File appDir = new File("test/webapp");
Context root = tomcat.addContext("", appDir.getAbsolutePath());
Tomcat.addServlet(root, "default", servlet);
root.addServletMappingDecoded("/", "default");
Connector connector = tomcat.getConnector();
connector.setAllowTrace(allowTrace);
tomcat.start();
ByteChunk bc = new ByteChunk();
Map<String,List<String>> respHeaders = new HashMap<>();
int rc = methodUrl("http://localhost:" + getPort() + "/index.html",
bc, 30000, null, respHeaders, "OPTIONS");
Assert.assertEquals(200, rc);
boolean foundTrace = false;
for (String header : respHeaders.get("Allow")) {
if (header.contains("TRACE")) {
foundTrace = true;
break;
}
}
if (allowTrace) {
Assert.assertTrue(foundTrace);
} else {
Assert.assertFalse(foundTrace);
}
}
}

View File

@@ -0,0 +1,413 @@
/*
* 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.connector;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
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.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.startup.SimpleHttpClient;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
public class TestCoyoteAdapter extends TomcatBaseTest {
public static final String TEXT_8K;
public static final byte[] BYTES_8K;
static {
StringBuilder sb = new StringBuilder(8192);
for (int i = 0; i < 512; i++) {
sb.append("0123456789ABCDEF");
}
TEXT_8K = sb.toString();
BYTES_8K = TEXT_8K.getBytes(StandardCharsets.UTF_8);
}
@Test
public void testPathParmsRootNone() throws Exception {
pathParamTest("/", "none");
}
@Test
public void testPathParmsFooNone() throws Exception {
pathParamTest("/foo", "none");
}
@Test
public void testPathParmsRootSessionOnly() throws Exception {
pathParamTest("/;jsessionid=1234", "1234");
}
@Test
public void testPathParmsFooSessionOnly() throws Exception {
pathParamTest("/foo;jsessionid=1234", "1234");
}
@Test
public void testPathParmsFooSessionDummy() throws Exception {
pathParamTest("/foo;jsessionid=1234;dummy", "1234");
}
@Test
public void testPathParmsFooSessionDummyValue() throws Exception {
pathParamTest("/foo;jsessionid=1234;dummy=5678", "1234");
}
@Test
public void testPathParmsFooSessionValue() throws Exception {
pathParamTest("/foo;jsessionid=1234;=5678", "1234");
}
@Test
public void testPathParmsFooSessionBar() throws Exception {
pathParamTest("/foo;jsessionid=1234/bar", "1234");
}
@Test
public void testPathParamsRedirect() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// Must have a real docBase. Don't use java.io.tmpdir as it may not be
// writable.
File docBase = new File(getTemporaryDirectory(), "testCoyoteAdapter");
addDeleteOnTearDown(docBase);
if (!docBase.mkdirs() && !docBase.isDirectory()) {
Assert.fail("Failed to create: [" + docBase.toString() + "]");
}
// Create the folder that will trigger the redirect
File foo = new File(docBase, "foo");
addDeleteOnTearDown(foo);
if (!foo.mkdirs() && !foo.isDirectory()) {
Assert.fail("Unable to create foo directory in docBase");
}
Context ctx = tomcat.addContext("", docBase.getAbsolutePath());
Tomcat.addServlet(ctx, "servlet", new PathParamServlet());
ctx.addServletMappingDecoded("/", "servlet");
tomcat.start();
testPath("/", "none");
testPath("/;jsessionid=1234", "1234");
testPath("/foo;jsessionid=1234", "1234");
testPath("/foo;jsessionid=1234;dummy", "1234");
testPath("/foo;jsessionid=1234;dummy=5678", "1234");
testPath("/foo;jsessionid=1234;=5678", "1234");
testPath("/foo;jsessionid=1234/bar", "1234");
}
private void pathParamTest(String path, String expected) throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
Tomcat.addServlet(ctx, "servlet", new PathParamServlet());
ctx.addServletMappingDecoded("/", "servlet");
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + path);
Assert.assertEquals(expected, res.toString());
}
private void testPath(String path, String expected) throws Exception {
ByteChunk res = getUrl("http://localhost:" + getPort() + path);
Assert.assertEquals(expected, res.toString());
}
private static class PathParamServlet 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 sessionId = req.getRequestedSessionId();
if (sessionId == null) {
sessionId = "none";
}
pw.write(sessionId);
}
}
@Test
public void testPathParamExtRootNoParam() throws Exception {
pathParamExtensionTest("/testapp/blah.txt", "none");
}
@Test
public void testPathParamExtLevel1NoParam() throws Exception {
pathParamExtensionTest("/testapp/blah/blah.txt", "none");
}
@Test
public void testPathParamExtLevel1WithParam() throws Exception {
pathParamExtensionTest("/testapp/blah;x=y/blah.txt", "none");
}
private void pathParamExtensionTest(String path, String expected)
throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("/testapp", null);
Tomcat.addServlet(ctx, "servlet", new PathParamServlet());
ctx.addServletMappingDecoded("*.txt", "servlet");
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() + path);
Assert.assertEquals(expected, res.toString());
}
@Test
public void testBug54602a() throws Exception {
// No UTF-8
doTestUriDecoding("/foo", "UTF-8", "/foo");
}
@Test
public void testBug54602b() throws Exception {
// Valid UTF-8
doTestUriDecoding("/foo%c4%87", "UTF-8", "/foo\u0107");
}
@Test
public void testBug54602c() throws Exception {
// Partial UTF-8
doTestUriDecoding("/foo%c4", "UTF-8", "/foo\uFFFD");
}
@Test
public void testBug54602d() throws Exception {
// Invalid UTF-8
doTestUriDecoding("/foo%ff", "UTF-8", "/foo\uFFFD");
}
@Test
public void testBug54602e() throws Exception {
// Invalid UTF-8
doTestUriDecoding("/foo%ed%a0%80", "UTF-8", "/foo\uFFFD\uFFFD\uFFFD");
}
private void doTestUriDecoding(String path, String encoding,
String expectedPathInfo) throws Exception{
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
tomcat.getConnector().setURIEncoding(encoding);
// No file system docBase required
Context ctx = tomcat.addContext("", null);
PathInfoServlet servlet = new PathInfoServlet();
Tomcat.addServlet(ctx, "servlet", servlet);
ctx.addServletMappingDecoded("/*", "servlet");
tomcat.start();
int rc = getUrl("http://localhost:" + getPort() + path,
new ByteChunk(), null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertEquals(expectedPathInfo, servlet.getPathInfo());
}
private static class PathInfoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private volatile String pathInfo = null;
public String getPathInfo() {
return pathInfo;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Not thread safe. Concurrent requests to this servlet will
// over-write all the results but the last processed.
pathInfo = req.getPathInfo();
}
}
@Test
public void testBug54928() throws Exception {
// Setup Tomcat instance
Tomcat tomcat = getTomcatInstance();
// No file system docBase required
Context ctx = tomcat.addContext("", null);
AsyncServlet servlet = new AsyncServlet();
Wrapper w = Tomcat.addServlet(ctx, "async", servlet);
w.setAsyncSupported(true);
ctx.addServletMappingDecoded("/async", "async");
tomcat.start();
SimpleHttpClient client = new SimpleHttpClient() {
@Override
public boolean isResponseBodyOK() {
return true;
}
};
String request = "GET /async HTTP/1.1" + SimpleHttpClient.CRLF +
"Host: a" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF;
client.setPort(getPort());
client.setRequest(new String[] {request});
client.connect();
client.sendRequest();
for (int i = 0; i < 10; i++) {
String line = client.readLine();
if (line != null && line.length() > 20) {
log.info(line.subSequence(0, 20) + "...");
}
}
client.disconnect();
// Wait for server thread to stop
Thread t = servlet.getThread();
long startTime = System.nanoTime();
t.join(5000);
long endTime = System.nanoTime();
log.info("Waited for servlet thread to stop for "
+ (endTime - startTime) / 1000000 + " ms");
Assert.assertTrue(servlet.isCompleted());
}
@Test
public void testNormalize01() {
doTestNormalize("/foo/../bar", "/bar");
}
private void doTestNormalize(String input, String expected) {
MessageBytes mb = MessageBytes.newInstance();
byte[] b = input.getBytes(StandardCharsets.UTF_8);
mb.setBytes(b, 0, b.length);
boolean result = CoyoteAdapter.normalize(mb);
mb.toString();
if (expected == null) {
Assert.assertFalse(result);
} else {
Assert.assertTrue(result);
Assert.assertEquals(expected, mb.toString());
}
}
private class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// This is a hack that won't work generally as servlets are expected to
// handle more than one request.
private Thread t;
private volatile boolean completed = false;
public Thread getThread() {
return t;
}
public boolean isCompleted() {
return completed;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
final OutputStream os = resp.getOutputStream();
final AsyncContext asyncCtxt = req.startAsync();
asyncCtxt.setTimeout(3000);
t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
// Some tests depend on this write failing (e.g.
// because the client has gone away). In some cases
// there may be a large (ish) buffer to fill before
// the write fails.
for (int j = 0 ; j < 8; j++) {
os.write(BYTES_8K);
}
os.flush();
Thread.sleep(1000);
} catch (Exception e) {
log.info("Exception caught " + e);
try {
// Note if request times out before this
// exception is thrown and the complete call
// below is made, the complete call below will
// fail since the timeout will have completed
// the request.
asyncCtxt.complete();
break;
} finally {
completed = true;
}
}
}
}
});
t.setName("testBug54928");
t.start();
}
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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.connector;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
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 static org.apache.catalina.startup.SimpleHttpClient.CRLF;
import org.apache.catalina.Context;
import org.apache.catalina.servlets.DefaultServlet;
import org.apache.catalina.startup.SimpleHttpClient;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.unittest.TesterData;
/*
* Various requests, usually originating from fuzzing, that have triggered an
* incorrect response from Tomcat - usually a 500 response rather than a 400
* response.
*/
@RunWith(Parameterized.class)
public class TestCoyoteAdapterRequestFuzzing extends TomcatBaseTest {
private static final String VALUE_16K = TesterData.string('x', 16 * 1024);
// Default max header count is 100
private static final String HEADER_150 = TesterData.string("X-Tomcat-Test: a" + CRLF, 150);
// Default max header count is 200 (need to keep under maxHeaderCount as well)
private static final String COOKIE_250 = TesterData.string("Cookie: a=b;c=d;e=f;g=h" + CRLF, 75);
@Parameterized.Parameters(name = "{index}: requestline[{0}], expected[{2}]")
public static Collection<Object[]> parameters() {
List<Object[]> parameterSets = new ArrayList<>();
parameterSets.add(new Object[] { "GET /00 HTTP/1.1",
"Host: lÿ#" + CRLF,
"400" } );
parameterSets.add(new Object[] { "GET *; HTTP/1.1",
"Host: localhost" + CRLF,
"400" } );
parameterSets.add(new Object[] { "GET /02 HTTP/1.1",
"Host: localhost" + CRLF +
"Content-Length: \u00A0" + CRLF,
"400" } );
parameterSets.add(new Object[] { "GET /03 HTTP/1.1",
"Content-Length: 1" + CRLF +
"Content-Length: 1" + CRLF,
"400" } );
parameterSets.add(new Object[] { "GET /04 HTTP/1.1",
"Transfer-Encoding: " + VALUE_16K + CRLF,
"400" } );
parameterSets.add(new Object[] { "GET /05 HTTP/1.1",
"Expect: " + VALUE_16K + CRLF,
"400" } );
parameterSets.add(new Object[] { "GET /06 HTTP/1.1",
"Connection: " + VALUE_16K + CRLF,
"400" } );
parameterSets.add(new Object[] { "GET /07 HTTP/1.1",
"User-Agent: " + VALUE_16K + CRLF,
"400" } );
parameterSets.add(new Object[] { "GET /08 HTTP/1.1",
HEADER_150,
"400" } );
parameterSets.add(new Object[] { "GET http://host/09 HTTP/1.0",
HEADER_150,
"400" } );
parameterSets.add(new Object[] { "GET /10 HTTP/1.1",
"Host: localhost" + CRLF +
COOKIE_250,
"400" } );
return parameterSets;
}
@Parameter(0)
public String requestLine;
@Parameter(1)
public String headers;
@Parameter(2)
public String expected;
@Test
public void doTest() throws Exception {
Tomcat tomcat = getTomcatInstance();
tomcat.getConnector().setAttribute("restrictedUserAgents", "value-not-important");
File appDir = new File("test/webapp");
Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName());
ctxt.addServletMappingDecoded("/", "default");
tomcat.start();
Client client = new Client(tomcat.getConnector().getLocalPort());
client.setRequest(new String[] {requestLine + CRLF, headers + CRLF});
client.connect();
client.processRequest();
// Expected response
String line = client.getResponseLine();
Assert.assertTrue(line + CRLF + client.getResponseBody(), line.startsWith("HTTP/1.1 " + expected + " "));
}
private static final class Client extends SimpleHttpClient {
public Client(int port) {
setPort(port);
setRequestPause(0);
}
@Override
protected OutputStream createOutputStream(Socket socket) throws IOException {
// Override the default implementation so we can create a large
// enough buffer to hold the entire request.
// The default implementation uses the 8k buffer in the
// StreamEncoder. Since some requests are larger than this, those
// requests will be sent in several parts. If the first part is
// sufficient for Tomcat to determine the request is invalid, Tomcat
// will close the connection, causing the write of the remaining
// parts to fail which in turn causes the test to fail.
return new BufferedOutputStream(super.createOutputStream(socket), 32 * 1024);
}
@Override
public boolean isResponseBodyOK() {
// Response body varies. It is the response code that is of interest
// in these tests.
return true;
}
}
}

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.connector;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
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 TestCoyoteInputStream extends TomcatBaseTest {
@Test
public void testReadWithByteBuffer() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
Tomcat.addServlet(root, "testServlet", new TestServlet());
root.addServletMappingDecoded("/", "testServlet");
tomcat.start();
ByteChunk bc = new ByteChunk();
String requestBody = "HelloWorld";
int rc = postUrl(requestBody.getBytes(StandardCharsets.UTF_8),
"http://localhost:" + getPort() + "/", bc, null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertTrue(requestBody.equals(bc.toString()));
}
private static final class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
CoyoteInputStream is = (CoyoteInputStream) req.getInputStream();
ByteBuffer buffer = ByteBuffer.allocate(256);
is.read(buffer);
CoyoteOutputStream os = (CoyoteOutputStream) resp.getOutputStream();
os.write(buffer);
}
}
}

View File

@@ -0,0 +1,293 @@
/*
* 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.connector;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
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.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestCoyoteOutputStream extends TomcatBaseTest {
@Test
public void testNonBlockingWriteNoneBlockingWriteNoneContainerThread() throws Exception {
doNonBlockingTest(0, 0, true);
}
@Test
public void testNonBlockingWriteOnceBlockingWriteNoneContainerThread() throws Exception {
doNonBlockingTest(1, 0, true);
}
@Test
public void testNonBlockingWriteTwiceBlockingWriteNoneContainerThread() throws Exception {
doNonBlockingTest(2, 0, true);
}
@Test
public void testNonBlockingWriteNoneBlockingWriteOnceContainerThread() throws Exception {
doNonBlockingTest(0, 1, true);
}
@Test
public void testNonBlockingWriteOnceBlockingWriteOnceContainerThread() throws Exception {
doNonBlockingTest(1, 1, true);
}
@Test
public void testNonBlockingWriteTwiceBlockingWriteOnceContainerThread() throws Exception {
doNonBlockingTest(2, 1, true);
}
@Test
public void testNonBlockingWriteNoneBlockingWriteNoneNonContainerThread() throws Exception {
doNonBlockingTest(0, 0, false);
}
@Test
public void testNonBlockingWriteOnceBlockingWriteNoneNonContainerThread() throws Exception {
doNonBlockingTest(1, 0, false);
}
@Test
public void testNonBlockingWriteTwiceBlockingWriteNoneNonContainerThread() throws Exception {
doNonBlockingTest(2, 0, false);
}
@Test
public void testNonBlockingWriteNoneBlockingWriteOnceNonContainerThread() throws Exception {
doNonBlockingTest(0, 1, false);
}
@Test
public void testNonBlockingWriteOnceBlockingWriteOnceNonContainerThread() throws Exception {
doNonBlockingTest(1, 1, false);
}
@Test
public void testNonBlockingWriteTwiceBlockingWriteOnceNonContainerThread() throws Exception {
doNonBlockingTest(2, 1, false);
}
@Test
public void testWriteWithByteBuffer() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
Tomcat.addServlet(root, "testServlet", new TestServlet());
root.addServletMappingDecoded("/", "testServlet");
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + "/", bc, null, null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
File file = new File("test/org/apache/catalina/connector/test_content.txt");
try (RandomAccessFile raf = new RandomAccessFile(file, "r");) {
ByteChunk expected = new ByteChunk();
expected.append(raf.getChannel().map(MapMode.READ_ONLY, 0, file.length()));
Assert.assertTrue(expected.equals(bc));
}
}
private void doNonBlockingTest(int asyncWriteTarget, int syncWriteTarget,
boolean useContainerThreadToSetListener) throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
Wrapper w = Tomcat.addServlet(root, "nbWrite",
new NonBlockingWriteServlet(asyncWriteTarget, useContainerThreadToSetListener));
w.setAsyncSupported(true);
root.addServletMappingDecoded("/nbWrite", "nbWrite");
Tomcat.addServlet(root, "write",
new BlockingWriteServlet(asyncWriteTarget, syncWriteTarget));
w.setAsyncSupported(true);
root.addServletMappingDecoded("/write", "write");
tomcat.start();
ByteChunk bc = new ByteChunk();
// Extend timeout to 5 mins for debugging
int rc = getUrl("http://localhost:" + getPort() + "/nbWrite", bc,
300000, null, null);
int totalCount = asyncWriteTarget + syncWriteTarget;
StringBuilder sb = new StringBuilder(totalCount * 16);
for (int i = 0; i < totalCount; i++) {
sb.append("OK - " + i + System.lineSeparator());
}
String expected = null;
if (sb.length() > 0) {
expected = sb.toString();
}
Assert.assertEquals(200, rc);
Assert.assertEquals(expected, bc.toString());
}
private static final class NonBlockingWriteServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final int asyncWriteTarget;
private final AtomicInteger asyncWriteCount = new AtomicInteger(0);
private final boolean useContainerThreadToSetListener;
public NonBlockingWriteServlet(int asyncWriteTarget,
boolean useContainerThreadToSetListener) {
this.asyncWriteTarget = asyncWriteTarget;
this.useContainerThreadToSetListener = useContainerThreadToSetListener;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
ServletOutputStream sos = resp.getOutputStream();
AsyncContext asyncCtxt = req.startAsync();
asyncCtxt.setTimeout(5);
Runnable task = new AsyncTask(asyncCtxt, sos);
if (useContainerThreadToSetListener) {
asyncCtxt.start(task);
} else {
Thread t = new Thread(task);
t.start();
}
}
private void doAsyncWrite(AsyncContext asyncCtxt,
ServletOutputStream sos) throws IOException {
while (sos.isReady()) {
int next = asyncWriteCount.getAndIncrement();
if (next < asyncWriteTarget) {
sos.write(
("OK - " + next + System.lineSeparator()).getBytes(
StandardCharsets.UTF_8));
sos.flush();
} else {
asyncCtxt.dispatch("/write");
break;
}
}
}
private class AsyncTask implements Runnable {
private final AsyncContext asyncCtxt;
private final ServletOutputStream sos;
public AsyncTask(AsyncContext asyncCtxt, ServletOutputStream sos) {
this.asyncCtxt = asyncCtxt;
this.sos = sos;
}
@Override
public void run() {
sos.setWriteListener(new MyWriteListener(asyncCtxt, sos));
}
}
private final class MyWriteListener implements WriteListener {
private final AsyncContext asyncCtxt;
private final ServletOutputStream sos;
public MyWriteListener(AsyncContext asyncCtxt,
ServletOutputStream sos) {
this.asyncCtxt = asyncCtxt;
this.sos = sos;
}
@Override
public void onWritePossible() throws IOException {
doAsyncWrite(asyncCtxt, sos);
}
@Override
public void onError(Throwable throwable) {
// Not expected.
throwable.printStackTrace();
}
}
}
private static final class BlockingWriteServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final int start;
private final int len;
public BlockingWriteServlet(int start, int len) {
this.start = start;
this.len = len;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("UTF-8");
ServletOutputStream sos = resp.getOutputStream();
for (int i = start; i < start + len; i++) {
sos.write(("OK - " + i + System.lineSeparator()).getBytes(
StandardCharsets.UTF_8));
}
}
}
private static final class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
CoyoteOutputStream os = (CoyoteOutputStream) resp.getOutputStream();
File file = new File("test/org/apache/catalina/connector/test_content.txt");
try (RandomAccessFile raf = new RandomAccessFile(file, "r");) {
os.write(raf.getChannel().map(MapMode.READ_ONLY, 0, file.length()));
}
}
}
}

View File

@@ -0,0 +1,160 @@
/*
* 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.connector;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.MalformedInputException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
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;
import org.apache.tomcat.util.buf.TestUtf8;
import org.apache.tomcat.util.buf.TestUtf8.Utf8TestCase;
public class TestInputBuffer extends TomcatBaseTest {
@Test
public void testUtf8Body() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
Tomcat.addServlet(root, "Echo", new Utf8Echo());
root.addServletMappingDecoded("/test", "Echo");
tomcat.start();
for (Utf8TestCase testCase : TestUtf8.TEST_CASES) {
String expected = null;
if (testCase.invalidIndex == -1) {
expected = testCase.outputReplaced;
}
doUtf8BodyTest(testCase.description, testCase.input, expected);
}
}
@Test
public void testBug60400() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
Tomcat.addServlet(root, "Bug60400Servlet", new Bug60400Servlet());
root.addServletMappingDecoded("/", "Bug60400Servlet");
Assert.assertTrue(tomcat.getConnector().setProperty("socket.appReadBufSize", "9000"));
tomcat.start();
ByteChunk bc = new ByteChunk();
byte[] requestBody = new byte[9500];
Arrays.fill(requestBody, (byte) 1);
int rc = postUrl(requestBody, "http://localhost:" + getPort() + "/", bc, null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertEquals(requestBody.length, bc.getLength());
}
private void doUtf8BodyTest(String description, int[] input,
String expected) throws Exception {
byte[] bytes = new byte[input.length];
for (int i = 0; i < input.length; i++) {
bytes[i] = (byte) input[i];
}
ByteChunk bc = new ByteChunk();
int rc = postUrl(bytes, "http://localhost:" + getPort() + "/test", bc,
null);
if (expected == null) {
Assert.assertEquals(description, HttpServletResponse.SC_OK, rc);
Assert.assertEquals(description, "FAILED", bc.toString());
} else if (expected.length() == 0) {
Assert.assertNull(description, bc.toString());
} else {
bc.setCharset(StandardCharsets.UTF_8);
Assert.assertEquals(description, expected, bc.toString());
}
}
private static class Utf8Echo extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// Should use POST
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
Reader r = req.getReader();
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/plain");
Writer w = resp.getWriter();
try {
// Copy one character at a time
int c = r.read();
while (c != -1) {
w.write(c);
c = r.read();
}
w.close();
} catch (MalformedInputException mie) {
resp.resetBuffer();
w.write("FAILED");
}
}
}
private static class Bug60400Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
StringBuilder builder = new StringBuilder();
try (BufferedReader reader = req.getReader()) {
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
}
resp.getWriter().print(builder);
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* 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.connector;
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.SimpleHttpClient;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
public class TestKeepAliveCount extends TomcatBaseTest {
@Test
public void testHttp10() throws Exception {
TestKeepAliveClient client = new TestKeepAliveClient();
client.doHttp10Request();
}
@Test
public void testHttp11() throws Exception {
TestKeepAliveClient client = new TestKeepAliveClient();
client.doHttp11Request();
}
private class TestKeepAliveClient extends SimpleHttpClient {
private boolean init;
private synchronized void init() {
if (init) return;
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
Tomcat.addServlet(root, "Simple", new SimpleServlet());
root.addServletMappingDecoded("/test", "Simple");
Assert.assertTrue(tomcat.getConnector().setProperty("maxKeepAliveRequests", "5"));
Assert.assertTrue(tomcat.getConnector().setProperty("connectionTimeout", "20000"));
Assert.assertTrue(tomcat.getConnector().setProperty("keepAliveTimeout", "50000"));
init = true;
}
private void doHttp10Request() throws Exception {
Tomcat tomcat = getTomcatInstance();
init();
tomcat.start();
setPort(tomcat.getConnector().getLocalPort());
// Open connection
connect();
// Send request in two parts
String[] request = new String[1];
request[0] =
"GET /test HTTP/1.0" + CRLF + CRLF;
setRequest(request);
processRequest(false); // blocks until response has been read
boolean passed = (this.readLine()==null);
// Close the connection
disconnect();
reset();
tomcat.stop();
Assert.assertTrue(passed);
}
private void doHttp11Request() throws Exception {
Tomcat tomcat = getTomcatInstance();
init();
tomcat.start();
setPort(tomcat.getConnector().getLocalPort());
// Open connection
connect();
// Send request in two parts
String[] request = new String[1];
request[0] =
"GET /test HTTP/1.1" + CRLF +
"Host: localhost" + CRLF +
"Connection: Keep-Alive" + CRLF+
"Keep-Alive: 300"+ CRLF+ CRLF;
setRequest(request);
for (int i=0; i<5; i++) {
processRequest(false); // blocks until response has been read
Assert.assertTrue(getResponseLine()!=null && getResponseLine().startsWith("HTTP/1.1 200 "));
}
boolean passed = (this.readLine()==null);
// Close the connection
disconnect();
reset();
tomcat.stop();
Assert.assertTrue(passed);
}
@Override
public boolean isResponseBodyOK() {
return true;
}
}
private static class SimpleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentLength(0);
resp.flushBuffer();
}
}
}

View File

@@ -0,0 +1,162 @@
/*
* 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.connector;
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.Assume;
import org.junit.Test;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.SimpleHttpClient;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
public class TestMaxConnections extends TomcatBaseTest {
private static final int MAX_CONNECTIONS = 3;
public static final int soTimeout = 5000;
public static final int connectTimeout = 1000;
@Test
public void testConnector() throws Exception {
init();
Assume.assumeFalse("This feature is not available for NIO2 (BZ58103)",
getTomcatInstance().getConnector().getProtocolHandlerClassName().contains("Nio2"));
ConnectThread[] t = new ConnectThread[10];
for (int i=0; i<t.length; i++) {
t[i] = new ConnectThread();
t[i].setName("ConnectThread["+i+"]");
}
for (int i=0; i<t.length; i++) {
t[i].start();
Thread.sleep(50);
}
for (int i=0; i<t.length; i++) {
t[i].join();
}
Assert.assertEquals(MAX_CONNECTIONS, SimpleServlet.getMaxConnections());
}
private class ConnectThread extends Thread {
@Override
public void run() {
try {
TestClient client = new TestClient();
client.doHttp10Request();
} catch (Exception x) {
// NO-OP. Some connections are expected to fail.
}
}
}
private synchronized void init() throws Exception {
Tomcat tomcat = getTomcatInstance();
StandardContext root = (StandardContext) tomcat.addContext("", SimpleHttpClient.TEMP_DIR);
root.setUnloadDelay(soTimeout);
Tomcat.addServlet(root, "Simple", new SimpleServlet());
root.addServletMappingDecoded("/test", "Simple");
Assert.assertTrue(tomcat.getConnector().setProperty("maxKeepAliveRequests", "1"));
Assert.assertTrue(tomcat.getConnector().setProperty("maxThreads", "10"));
Assert.assertTrue(tomcat.getConnector().setProperty("connectionTimeout", "20000"));
Assert.assertTrue(tomcat.getConnector().setProperty("keepAliveTimeout", "50000"));
Assert.assertTrue(tomcat.getConnector().setProperty("maxConnections", Integer.toString(MAX_CONNECTIONS)));
Assert.assertTrue(tomcat.getConnector().setProperty("acceptCount", "1"));
tomcat.start();
}
private class TestClient extends SimpleHttpClient {
private void doHttp10Request() throws Exception {
setPort(getPort());
long start = System.currentTimeMillis();
// Open connection
connect(connectTimeout,soTimeout);
// Send request in two parts
String[] request = new String[1];
request[0] =
"GET /test HTTP/1.0" + CRLF + CRLF;
setRequest(request);
boolean passed = false;
processRequest(false); // blocks until response has been read
long stop = System.currentTimeMillis();
log.info(Thread.currentThread().getName()+" Request complete:"+(stop-start)+" ms.");
passed = (this.readLine()==null);
// Close the connection
disconnect();
reset();
Assert.assertTrue(passed);
}
@Override
public boolean isResponseBodyOK() {
return true;
}
}
private static class SimpleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static int currentConnections = 0;
private static int maxConnections = 0;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
increment();
System.out.println("Processing thread: " + Thread.currentThread().getName());
try {
Thread.sleep(TestMaxConnections.soTimeout*4/5);
} catch (InterruptedException x) {
}
resp.setContentLength(0);
resp.flushBuffer();
decrement();
}
private static synchronized void increment() {
currentConnections++;
if (currentConnections > maxConnections) {
maxConnections = currentConnections;
}
}
private static synchronized void decrement() {
currentConnections--;
}
public static synchronized int getMaxConnections() {
return maxConnections;
}
}
}

View File

@@ -0,0 +1,214 @@
/*
* 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.connector;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
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 TestOutputBuffer extends TomcatBaseTest{
/*
* Expect that the buffered results are slightly slower since Tomcat now has
* an internal buffer so an extra one just adds overhead.
*
* @see "https://bz.apache.org/bugzilla/show_bug.cgi?id=52328"
*/
@Test
public void testWriteSpeed() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
for (int i = 1; i <= WritingServlet.EXPECTED_CONTENT_LENGTH; i*=10) {
WritingServlet servlet = new WritingServlet(i);
Tomcat.addServlet(root, "servlet" + i, servlet);
root.addServletMappingDecoded("/servlet" + i, "servlet" + i);
}
tomcat.start();
ByteChunk bc = new ByteChunk();
for (int i = 1; i <= WritingServlet.EXPECTED_CONTENT_LENGTH; i*=10) {
int rc = getUrl("http://localhost:" + getPort() +
"/servlet" + i, bc, null, null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertEquals(
WritingServlet.EXPECTED_CONTENT_LENGTH, bc.getLength());
bc.recycle();
rc = getUrl("http://localhost:" + getPort() +
"/servlet" + i + "?useBuffer=y", bc, null, null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertEquals(
WritingServlet.EXPECTED_CONTENT_LENGTH, bc.getLength());
bc.recycle();
}
}
@Test
public void testBug52577() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
Bug52577Servlet bug52577 = new Bug52577Servlet();
Tomcat.addServlet(root, "bug52577", bug52577);
root.addServletMappingDecoded("/", "bug52577");
tomcat.start();
ByteChunk bc = new ByteChunk();
int rc = getUrl("http://localhost:" + getPort() + "/", bc, null, null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertEquals("OK", bc.toString());
}
private static class WritingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected static final int EXPECTED_CONTENT_LENGTH = 100000;
private final String writeString;
private final int writeCount;
public WritingServlet(int writeLength) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < writeLength; i++) {
sb.append('x');
}
writeString = sb.toString();
writeCount = EXPECTED_CONTENT_LENGTH / writeLength;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/plain");
resp.setCharacterEncoding("ISO-8859-1");
Writer w = resp.getWriter();
// Wrap with a buffer if necessary
String useBufferStr = req.getParameter("useBuffer");
if (useBufferStr != null) {
w = new BufferedWriter(w);
}
long start = System.nanoTime();
for (int i = 0; i < writeCount; i++) {
w.write(writeString);
}
if (useBufferStr != null) {
w.flush();
}
long lastRunNano = System.nanoTime() - start;
System.out.println("Write length: " + writeString.length() +
", Buffered: " + (useBufferStr == null ? "n" : "y") +
", Time: " + lastRunNano + "ns");
}
}
private static class Bug52577Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Writer w = resp.getWriter();
w.write("OK");
resp.resetBuffer();
w.write("OK");
}
}
@Test
public void testUtf8SurrogateBody() throws Exception {
// Create test data. This is carefully constructed to trigger the edge
// case. Small variations may cause the test to miss the edge case.
StringBuffer sb = new StringBuffer();
sb.append("a");
for (int i = 0x10000; i < 0x11000; i++) {
char[] chars = Character.toChars(i);
sb.append(chars);
}
String data = sb.toString();
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
Tomcat.addServlet(root, "Test", new Utf8WriteChars(data));
root.addServletMappingDecoded("/test", "Test");
tomcat.start();
ByteChunk bc = new ByteChunk();
getUrl("http://localhost:" + getPort() + "/test", bc, null);
bc.setCharset(StandardCharsets.UTF_8);
Assert.assertEquals(data, bc.toString());
}
private static class Utf8WriteChars extends HttpServlet {
private static final long serialVersionUID = 1L;
private final char[] chars;
public Utf8WriteChars(String data) {
chars = data.toCharArray();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/plain");
Writer w = resp.getWriter();
for (int i = 0; i < chars.length; i++) {
w.write(chars[i]);
}
}
}
}

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,83 @@
/*
* 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.connector;
import java.net.URI;
import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.startup.LoggingBaseTest;
import org.apache.tomcat.unittest.TesterRequest;
public class TestResponsePerformance extends LoggingBaseTest {
private final int ITERATIONS = 100000;
@Test
public void testToAbsolutePerformance() throws Exception {
Request req = new TesterRequest();
Response resp = new Response();
resp.setRequest(req);
// Warm up
doHomebrew(resp);
doUri();
// Note: Java 9 on my OSX laptop consistently shows doUri() is faster
// than doHomebrew(). Worth a closer look for Tomcat 10 on the
// assumption it will require java 9
// To allow for timing differences between runs, a "best of n" approach
// is taken for this test
final int bestOf = 5;
final int winTarget = (bestOf + 1) / 2;
int homebrewWin = 0;
int count = 0;
while (count < bestOf && homebrewWin < winTarget) {
long homebrew = doHomebrew(resp);
long uri = doUri();
log.info("Current 'home-brew': " + homebrew + "ms, Using URI: " + uri + "ms");
if (homebrew < uri) {
homebrewWin++;
}
count++;
}
Assert.assertTrue(homebrewWin == winTarget);
}
private long doHomebrew(Response resp) {
long start = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
resp.toAbsolute("bar.html");
}
return System.currentTimeMillis() - start;
}
private long doUri() {
long start = System.currentTimeMillis();
for (int i = 0; i < ITERATIONS; i++) {
URI base = URI.create(
"http://localhost:8080/level1/level2/foo.html");
base.resolve(URI.create("bar.html")).toASCIIString();
}
return System.currentTimeMillis() - start;
}
}

View File

@@ -0,0 +1,251 @@
/*
* 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.connector;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
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.Globals;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
import org.apache.tomcat.util.buf.ByteChunk;
public class TestSendFile extends TomcatBaseTest {
public static final int ITERATIONS = 10;
public static final int EXPECTED_CONTENT_LENGTH = 100000;
@Test
public void testSendFile() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context root = tomcat.addContext("", TEMP_DIR);
File[] files = new File[ITERATIONS];
for (int i = 0; i < ITERATIONS; i++) {
files[i] = generateFile(TEMP_DIR, "-" + i, EXPECTED_CONTENT_LENGTH * (i + 1));
}
try {
for (int i = 0; i < ITERATIONS; i++) {
WritingServlet servlet = new WritingServlet(files[i]);
Tomcat.addServlet(root, "servlet" + i, servlet);
root.addServletMappingDecoded("/servlet" + i, "servlet" + i);
}
tomcat.start();
ByteChunk bc = new ByteChunk();
Map<String, List<String>> respHeaders = new HashMap<>();
for (int i = 0; i < ITERATIONS; i++) {
long start = System.currentTimeMillis();
int rc = getUrl("http://localhost:" + getPort() + "/servlet" + i, bc, null,
respHeaders);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
System.out.println("Client received " + bc.getLength() + " bytes in "
+ (System.currentTimeMillis() - start) + " ms.");
Assert.assertEquals(EXPECTED_CONTENT_LENGTH * (i + 1L), bc.getLength());
bc.recycle();
}
} finally {
for (File f : files) {
Assert.assertTrue("Failed to clean up [" + f + "]", f.delete());
}
}
}
public File generateFile(String dir, String suffix, int size) throws IOException {
String name = "testSendFile-" + System.currentTimeMillis() + suffix + ".txt";
File f = new File(dir, name);
try (FileWriter fw = new FileWriter(f, false); BufferedWriter w = new BufferedWriter(fw);) {
int defSize = 8192;
while (size > 0) {
int bytes = Math.min(size, defSize);
char[] b = new char[bytes];
Arrays.fill(b, 'X');
w.write(b);
size = size - bytes;
}
w.flush();
}
System.out
.println("Created file:" + f.getAbsolutePath() + " with " + f.length() + " bytes.");
return f;
}
private static class WritingServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final File f;
public WritingServlet(File f) {
this.f = f;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("'application/octet-stream");
resp.setCharacterEncoding("ISO-8859-1");
resp.setContentLengthLong(f.length());
if (Boolean.TRUE.equals(req.getAttribute(Globals.SENDFILE_SUPPORTED_ATTR))) {
req.setAttribute(Globals.SENDFILE_FILENAME_ATTR, f.getAbsolutePath());
req.setAttribute(Globals.SENDFILE_FILE_START_ATTR, Long.valueOf(0));
req.setAttribute(Globals.SENDFILE_FILE_END_ATTR, Long.valueOf(f.length()));
} else {
byte[] c = new byte[8192];
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(f))) {
int len = 0;
int written = 0;
long start = System.currentTimeMillis();
do {
len = in.read(c);
if (len > 0) {
resp.getOutputStream().write(c, 0, len);
written += len;
}
} while (len > 0);
System.out.println("Server Wrote " + written + " bytes in "
+ (System.currentTimeMillis() - start) + " ms.");
}
}
}
}
@Test
public void testBug60409() throws Exception {
Tomcat tomcat = getTomcatInstance();
Context ctx = tomcat.addContext("", TEMP_DIR);
File file = generateFile(TEMP_DIR, "", EXPECTED_CONTENT_LENGTH);
Tomcat.addServlet(ctx, "test", new Bug60409Servlet(file));
ctx.addServletMappingDecoded("/", "test");
tomcat.start();
ByteChunk bc = new ByteChunk();
getUrl("http://localhost:" + getPort() + "/test/?" + Globals.SENDFILE_SUPPORTED_ATTR
+ "=true", bc, null);
CountDownLatch latch = new CountDownLatch(2);
List<Throwable> exceptions = new ArrayList<>();
new Thread(
new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, exceptions))
.start();
new Thread(
new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, exceptions))
.start();
latch.await(3000, TimeUnit.MILLISECONDS);
if (exceptions.size() > 0) {
Assert.fail();
}
}
private static final class Bug60409Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final File file;
Bug60409Servlet(File file) {
this.file = file;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (Boolean.valueOf(req.getParameter(Globals.SENDFILE_SUPPORTED_ATTR)).booleanValue()) {
resp.setContentType("'application/octet-stream");
resp.setCharacterEncoding("ISO-8859-1");
resp.setContentLengthLong(file.length());
req.setAttribute(Globals.SENDFILE_FILENAME_ATTR, file.getAbsolutePath());
req.setAttribute(Globals.SENDFILE_FILE_START_ATTR, Long.valueOf(0));
req.setAttribute(Globals.SENDFILE_FILE_END_ATTR, Long.valueOf(file.length()));
if (!file.delete()) {
throw new ServletException("Failed to delete [" + file + "]");
}
} else {
byte[] c = new byte[1024];
Random rd = new Random();
rd.nextBytes(c);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
resp.getOutputStream().write(c);
}
}
}
private static final class RequestExecutor implements Runnable {
private final String url;
private final CountDownLatch latch;
private final List<Throwable> exceptions;
RequestExecutor(String url, CountDownLatch latch, List<Throwable> exceptions) {
this.url = url;
this.latch = latch;
this.exceptions = exceptions;
}
@Override
public void run() {
try {
ByteChunk result = new ByteChunk();
int rc = getUrl(url, result, null);
Assert.assertEquals(HttpServletResponse.SC_OK, rc);
Assert.assertEquals(1024, result.getLength());
} catch (Throwable e) {
e.printStackTrace();
exceptions.add(e);
} finally {
latch.countDown();
}
}
}
}

View File

@@ -0,0 +1,19 @@
# 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.
# This is a test file for the WebappServiceLoader
# It contains comment lines and blank lines
test content

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);
}
}
}

Some files were not shown because too many files have changed in this diff Show More