/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
import java.io.*;
import java.net.*;
import com.sun.ejte.ccl.reporter.*;
import org.apache.catalina.startup.SimpleHttpClient;
/*
* Unit test for https://issues.apache.org/bugzilla/show_bug.cgi?id=49779
* 501 Method not implemented with successive POST requests
*/
public class WebTest {
private static final String TEST_NAME =
"test-form-authenticator";
private static final SimpleReporterAdapter stat =
new SimpleReporterAdapter("appserv-tests");
private String host;
private int port;
private String contextRoot;
private String adminUser;
private String adminPassword;
public WebTest(String[] args) {
host = args[0];
port = Integer.parseInt(args[1]);
contextRoot = args[2];
adminUser = args[3];
adminPassword = args[4];
}
public static void main(String[] args) {
stat.addDescription("Unit test for Tomcat bug 49779");
WebTest webTest = new WebTest(args);
try {
webTest.run();
stat.addStatus(TEST_NAME, stat.PASS);
} catch( Exception ex) {
ex.printStackTrace();
stat.addStatus(TEST_NAME, stat.FAIL);
}
stat.printSummary();
}
public void run() throws Exception {
testGet();
testPostNoContinue();
testPostWithContinue();
testPostNoContinuePostRedirect();
testPostWithContinuePostRedirect();
}
public void testGet() throws Exception {
doTest("GET", "GET", false);
}
public void testPostNoContinue() throws Exception {
doTest("POST", "GET", false);
}
public void testPostWithContinue() throws Exception {
doTest("POST", "GET", true);
}
// Bugzilla Bug 49779
public void testPostNoContinuePostRedirect() throws Exception {
doTest("POST", "POST", false);
}
// Bugzilla Bug 49779
public void testPostWithContinuePostRedirect() throws Exception {
doTest("POST", "POST", true);
}
public void doTest(String resourceMethod, String redirectMethod,
boolean useContinue) throws Exception {
FormAuthClient client = new FormAuthClient();
// First request for authenticated resource
client.setUseContinue(useContinue);
client.doResourceRequest(resourceMethod);
assertTrue(client.isResponse200());
assertTrue(client.isResponseBodyOK());
client.reset();
// Second request for the login page
client.setUseContinue(useContinue);
client.doLoginRequest();
assertTrue(client.isResponse302());
assertTrue(client.isResponseBodyOK());
client.reset();
// Third request - follow the redirect
client.doResourceRequest(redirectMethod);
if ("POST".equals(redirectMethod)) {
client.setUseContinue(useContinue);
}
assertTrue(client.isResponse200());
assertTrue(client.isResponseBodyOK());
client.reset();
// Subsequent requests - direct to the resource
for (int i = 0; i < 5; i++) {
client.setUseContinue(useContinue);
client.doResourceRequest(resourceMethod);
assertTrue(client.isResponse200());
assertTrue(client.isResponseBodyOK());
client.reset();
}
}
private static void assertTrue(boolean status) {
if (!status) {
throw new RuntimeException();
}
}
private final class FormAuthClient extends SimpleHttpClient {
private static final String LOGIN_PAGE = "j_security_check";
private String protectedPage = "index.jsp";
private String protectedLocation = contextRoot;
private int requestCount = 0;
private String sessionId = null;
private FormAuthClient() {
setHost(host);
setPort(port);
}
private void doResourceRequest(String method) throws Exception {
StringBuilder requestHead = new StringBuilder(128);
String requestTail;
requestHead.append(method).append(" ").append(protectedLocation)
.append("/").append(protectedPage);
if ("GET".equals(method)) {
requestHead.append("?role=bar");
}
requestHead.append(" HTTP/1.1").append(CRLF);
requestHead.append("Host: " + host).append(CRLF);
requestHead.append("Connection: close").append(CRLF);
if (getUseContinue()) {
requestHead.append("Expect: 100-continue").append(CRLF);
}
if (sessionId != null) {
requestHead.append("Cookie: JSESSIONID=").append(sessionId)
.append(CRLF);
}
if ("POST".equals(method)) {
requestHead.append(
"Content-Type: application/x-www-form-urlencoded")
.append(CRLF);
requestHead.append("Content-length: 8").append(CRLF);
requestHead.append(CRLF);
requestTail = "role=bar";
} else {
requestTail = CRLF;
}
String request[] = new String[2];
request[0] = requestHead.toString();
request[1] = requestTail;
doRequest(request);
}
private void doLoginRequest() throws Exception {
StringBuilder requestHead = new StringBuilder(128);
requestHead.append("POST ").append(protectedLocation)
.append("/").append(LOGIN_PAGE).append(" HTTP/1.1").append(CRLF);
requestHead.append("Host: " + host).append(CRLF);
requestHead.append("Connection: close").append(CRLF);
if (getUseContinue()) {
requestHead.append("Expect: 100-continue").append(CRLF);
}
if (sessionId != null) {
requestHead.append("Cookie: JSESSIONID=").append(sessionId)
.append(CRLF);
}
requestHead.append(
"Content-Type: application/x-www-form-urlencoded").append(
CRLF);
requestHead.append("Content-length: 35").append(CRLF);
requestHead.append(CRLF);
String request[] = new String[2];
request[0] = requestHead.toString();
request[1] = "j_username=" + adminUser + "&j_password=" + adminPassword;
doRequest(request);
}
private void doRequest(String request[]) throws Exception {
setRequest(request);
try {
connect();
processRequest();
String newSessionId = getSessionId();
if (newSessionId != null) {
sessionId = newSessionId;
}
} finally {
disconnect();
}
requestCount++;
}
@Override
public boolean isResponseBodyOK() {
if (requestCount == 1) {
// First request should result in the login page
assertContains(getResponseBody(), "A JSP Login Page");
return true;
} else if (requestCount == 2) {
// Second request should result in a redirect
return true;
} else {
// Subsequent requests should result in the protected page
// The role parameter should have reached the page
String body = getResponseBody();
assertContains(body, "Hello javaee, bar");
/*
assertContains(body,
"<input type=\"text\" name=\"role\" value=\"bar\"");
*/
return true;
}
}
private void assertContains(String body, String expected) {
if (!body.contains(expected)) {
throw new RuntimeException("Response body check failure.\n"
+ "Expected to contain substring: [" + expected
+ "]\nActual: [" + body + "]");
}
}
}
}