From 361742465850507ca5960a4f134ac1f87e5cb13b Mon Sep 17 00:00:00 2001 From: markt Date: Sun, 22 Aug 2010 21:02:33 +0000 Subject: [PATCH] Provide 100 Continue responses if required during FORM authentication Side issues from investigating https://issues.apache.org/bugzilla/show_bug.cgi?id=49779 git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@987948 13f79535-47bb-0310-9956-ffa450edef68 --- .../catalina/authenticator/FormAuthenticator.java | 8 +- .../authenticator/TestFormAuthenticator.java | 159 +++++++++++++++++++++ webapps/docs/changelog.xml | 4 + 3 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 test/org/apache/catalina/authenticator/TestFormAuthenticator.java diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java b/java/org/apache/catalina/authenticator/FormAuthenticator.java index 4b31f7f50..609adcb29 100644 --- a/java/org/apache/catalina/authenticator/FormAuthenticator.java +++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java @@ -246,8 +246,9 @@ public class FormAuthenticator return (false); } - // Yes -- Validate the specified credentials and redirect - // to the error page if they are not correct + // Yes -- Acknowledge the request, validate the specified credentials + // and redirect to the error page if they are not correct + request.getResponse().sendAcknowledgement(); Realm realm = context.getRealm(); if (characterEncoding != null) { request.setCharacterEncoding(characterEncoding); @@ -511,6 +512,9 @@ public class FormAuthenticator } if ("POST".equalsIgnoreCase(request.getMethod())) { + // May need to acknowledge a 100-continue expectation + request.getResponse().sendAcknowledgement(); + ByteChunk body = new ByteChunk(); body.setLimit(request.getConnector().getMaxSavePostSize()); diff --git a/test/org/apache/catalina/authenticator/TestFormAuthenticator.java b/test/org/apache/catalina/authenticator/TestFormAuthenticator.java new file mode 100644 index 000000000..aead9c34f --- /dev/null +++ b/test/org/apache/catalina/authenticator/TestFormAuthenticator.java @@ -0,0 +1,159 @@ +/* + * 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 org.apache.catalina.Context; +import org.apache.catalina.startup.SimpleHttpClient; +import org.apache.catalina.startup.TestTomcat.MapRealm; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; + +public class TestFormAuthenticator extends TomcatBaseTest { + + public void testBug49779() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File appDir = new File(getBuildDirectory(), "webapps/examples"); + Context ctx = + tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); + + MapRealm realm = new MapRealm(); + realm.addUser("tomcat", "tomcat"); + realm.addUserRole("tomcat", "tomcat"); + ctx.setRealm(realm); + + tomcat.start(); + + Bug49779ClientStage1 client = new Bug49779ClientStage1(); + client.setPort(getPort()); + + // First request for authenticated resource + Exception e = client.doRequest(null); + assertNull(e); + assertTrue(client.isResponse200()); + assertTrue(client.isResponseBodyOK()); + + String sessionID = client.getSessionId(); + + // Second request for the login page + client.reset(); + e = client.doRequest(sessionID); + assertNull(e); + assertTrue(client.isResponse302()); + assertTrue(client.isResponseBodyOK()); + + // Third request - follow the redirect + client.reset(); + e = client.doRequest(sessionID); + assertNull(e); + assertTrue(client.isResponse200()); + assertTrue(client.isResponseBodyOK()); + + // Session ID changes after successful authentication + sessionID = client.getSessionId(); + + // Subsequent requests - direct to the resource + for (int i = 0; i < 5; i++) { + client.reset(); + e = client.doRequest(sessionID); + assertNull(e); + assertTrue(client.isResponse200()); + assertTrue(client.isResponseBodyOK()); + } + } + + private final class Bug49779ClientStage1 extends SimpleHttpClient { + + private int requestCount = 0; + + private Exception doRequest(String sessionId) { + try { + String request[] = new String[2]; + if (requestCount == 1) { + request[0] = + "POST /examples/jsp/security/protected/j_security_check HTTP/1.1" + CRLF; + } else if (requestCount == 2) { + request[0] = + "GET /examples/jsp/security/protected/index.jsp HTTP/1.1" + CRLF; + } else { + request[0] = + "POST /examples/jsp/security/protected/index.jsp HTTP/1.1" + CRLF; + } + + request[0] = request[0] + + "Host: localhost" + CRLF + + "Expect: 100-continue" + CRLF + + "Connection: close" + CRLF; + + if (sessionId != null) { + request[0] = request[0] + + "Cookie: JSESSIONID=" + sessionId + CRLF; + } + + if (requestCount == 1) { + request[0] = request[0] + + "Content-Type: application/x-www-form-urlencoded" + CRLF + + "Content-length: 35" + CRLF + + CRLF; + request[1] = "j_username=tomcat&j_password=tomcat"; + } else if (requestCount ==2) { + request[1] = CRLF; + } else { + request[0] = request[0] + + "Content-Type: application/x-www-form-urlencoded" + CRLF + + "Content-length: 7" + CRLF + + CRLF; + request[1] = "foo=bar"; + } + + setRequest(request); + if (requestCount != 2) { + setUseContinue(true); + } + + connect(); + processRequest(); + disconnect(); + + requestCount++; + } catch (Exception e) { + e.printStackTrace(); + return e; + } + return null; + } + + @Override + public boolean isResponseBodyOK() { + String expected; + + if (requestCount == 1) { + // First request should result in the login page + expected = "Login Page for Examples"; + } else if (requestCount == 2) { + // Second request should result in a redirect + return true; + } else { + // Subsequent requests should result in the protected page + expected = "Protected Page for Examples"; + } + return getResponseBody().contains(expected); + } + + } +} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index d3ac079ac..a5c604ac5 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -50,6 +50,10 @@ 49757: Correct some generics warnings. Based on a patch provided by Gábor. (markt) + + Provide 100 Continue responses at appropriate points during FORM + authentication if client indicates that they are expected. (markt) + -- 2.11.0