--- /dev/null
+/*
+ */
+package org.apache.tomcat.lite;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+import org.apache.tomcat.lite.coyote.CoyoteHttp;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class LiteTestHelper {
+
+ public static void initServletsAndRun(TomcatLite lite, int port) throws ServletException, IOException {
+ ServletContextImpl ctx =
+ (ServletContextImpl) lite.addServletContext(null, null, "/test1");
+
+ ctx.addServlet("test", new SimpleServlet());
+ ctx.addMapping("/1stTest", "test");
+
+ ctx.addServlet("testException", new HttpServlet() {
+ public void doGet(HttpServletRequest req, HttpServletResponse res)
+ throws IOException {
+ throw new NullPointerException();
+ }
+ });
+ ctx.addMapping("/testException", "testException");
+
+ lite.init();
+ lite.start();
+
+
+ if (port > 0) {
+ // This should be added after all local initialization to avoid
+ // the server from responding.
+ // Alternatively, you can load this early but set it to return
+ // 'unavailable' if load balancers depend on this.
+ addConnector(lite, port, true);
+
+ // At this point we can add contexts and inject requests, if we want to
+ // do it over HTTP need to start the connector as well.
+ lite.startConnector();
+ }
+ }
+
+ public static void addConnector(TomcatLite lite,
+ int port, boolean daemon) {
+ CoyoteHttp coyoteAdapter = (CoyoteHttp) lite.getConnector();
+ coyoteAdapter.getConnectors().setPort(port);
+ coyoteAdapter.getConnectors().setDaemon(daemon);
+ }
+
+ /**
+ * Get url using URLConnection.
+ */
+ public static ByteChunk getUrl(String path) throws IOException {
+
+ ByteChunk out = new ByteChunk();
+
+ URL url = new URL(path);
+ URLConnection connection = url.openConnection();
+ connection.setReadTimeout(5000);
+ connection.connect();
+ InputStream is = connection.getInputStream();
+ BufferedInputStream bis = new BufferedInputStream(is);
+ byte[] buf = new byte[2048];
+ int rd = 0;
+ while((rd = bis.read(buf)) > 0) {
+ out.append(buf, 0, rd);
+ }
+ return out;
+ }
+
+ /**
+ * Create a ServletRequestImpl object that can be used with
+ * TomcatLite.service(request).
+ *
+ * All output will be added to the ByteChunk out.
+ *
+ * This requires no HTTP connector.
+ *
+ * @see TomcatLiteNoConnector
+ */
+ public static ServletRequestImpl createMessage(TomcatLite lite,
+ String uri,
+ final ByteChunk out) {
+ ServletRequestImpl req = lite.createMessage();
+ req.setRequestURI(uri);
+ ServletResponseImpl res = req.getResponse();
+ res.getCoyoteResponse().setOutputBuffer(
+ new ByteChunkOutputBuffer(out));
+ return req;
+ }
+
+
+ static class ByteChunkOutputBuffer implements OutputBuffer {
+
+ protected ByteChunk output = null;
+
+ public ByteChunkOutputBuffer(ByteChunk output) {
+ this.output = output;
+ }
+
+ public int doWrite(ByteChunk chunk, Response response)
+ throws IOException {
+ output.append(chunk);
+ return chunk.getLength();
+ }
+ }
+
+
+}
--- /dev/null
+/*
+ * 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.tomcat.lite;
+
+
+import junit.framework.Test;
+
+public class LiteWatchdogJspTests extends LiteWatchdogServletTests {
+
+ public LiteWatchdogJspTests() {
+ super();
+ testMatch =
+ //"precompileNegativeTest";
+ null;
+ // Test we know are failing - need to fix at some point.
+ exclude = new String[] {
+ "negativeDuplicateExtendsFatalTranslationErrorTest",
+ "negativeDuplicateErrorPageFatalTranslationErrorTest",
+ "negativeDuplicateInfoFatalTranslationErrorTest",
+ "negativeDuplicateLanguageFatalTranslationErrorTest",
+ "negativeDuplicateSessionFatalTranslationErrorTest",
+ "positiveIncludeCtxRelativeHtmlTest",
+ "precompileNegativeTest"
+ };
+ file = base + "/src/conf/jsp-gtest.xml";
+ goldenDir =
+ base + "/src/clients/org/apache/jcheck/jsp/client/";
+ targetMatch = "jsp-test";
+
+ }
+
+ public static Test suite() {
+ return new LiteWatchdogJspTests().getSuite();
+ }
+
+}
+
--- /dev/null
+/*
+ * 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.tomcat.lite;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.apache.tomcat.test.watchdog.WatchdogClient;
+
+
+public class LiteWatchdogServletTests extends WatchdogClient {
+
+
+ public LiteWatchdogServletTests() {
+ goldenDir = base + "/src/clients/org/apache/jcheck/servlet/client/";
+ testMatch =
+ //"HttpServletResponseWrapperSetStatusMsgTest";
+ //"ServletContextAttributeAddedEventTest";
+ null;
+ // ex: "ServletToJSP";
+ file = base + "/src/conf/servlet-gtest.xml";
+ targetMatch = "gtestservlet-test";
+ }
+
+ protected void beforeSuite() {
+ // required for the tests
+ System.setProperty("org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER",
+ "true");
+ String path = System.getProperty("watchdog.home");
+ if (path != null) {
+ base = path;
+ }
+
+ try {
+ initServerWithWatchdog(base);
+ } catch (ServletException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public void initServerWithWatchdog(String wdDir) throws ServletException,
+ IOException {
+
+ File f = new File(wdDir + "/build/webapps");
+
+ //CoyoteServer connector = new CoyoteServer();
+ //connector.addAdapter("/", new MapperAdapter());
+
+ TomcatLite liteServer = new TomcatLite();
+ LiteTestHelper.addConnector(liteServer, 8080, true);
+ liteServer.init("webapps/ROOT", "/");
+
+ for (String s : new String[] {
+ "servlet-compat",
+ "servlet-tests",
+ "jsp-tests"} ) {
+ liteServer.init(f.getCanonicalPath() + "/" + s,
+ "/" + s);
+ }
+
+ //connector.init();
+ liteServer.init();
+ liteServer.start();
+
+ liteServer.startConnector();
+ }
+
+
+
+ protected void afterSuite(TestResult res) {
+ // no need to stop it - using daemon threads.
+ System.err.println("DONE");
+ }
+
+
+ /**
+ * Magic JUnit method
+ */
+ public static Test suite() {
+ // The individual targets are dups - and bad ones,
+ // RequestWrapper are missing part of the URL
+ return new LiteWatchdogServletTests().getSuite();
+ }
+}
--- /dev/null
+/*
+ */
+package org.apache.tomcat.lite;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import junit.framework.TestCase;
+
+import org.apache.tomcat.integration.simple.SimpleObjectManager;
+
+
+public class PropertiesSpiTest extends TestCase {
+
+ SimpleObjectManager spi;
+
+ public void setUp() {
+ spi = new SimpleObjectManager();
+
+ spi.getProperties().put("obj1.name", "foo");
+ spi.getProperties().put("obj1.(class)", BoundObj.class.getName());
+
+ }
+
+ public void testArgs() throws IOException {
+ Properties res = new Properties();
+ SimpleObjectManager.processArgs(new String[] {
+ "-a=1", "-b", "2"}, res);
+
+ assertEquals("1", res.get("a"));
+ assertEquals("2", res.get("b"));
+
+
+ }
+
+ public static class BoundObj {
+ String name;
+
+ public void setName(String n) {
+ this.name = n;
+ }
+ }
+
+ public void testBind() throws Exception {
+ BoundObj bo = new BoundObj();
+ spi.bind("obj1", bo);
+ assertEquals(bo.name, "foo");
+ }
+
+ public void testCreate() throws Exception {
+ BoundObj bo = (BoundObj) spi.get("obj1");
+ assertEquals(bo.name, "foo");
+ }
+}
--- /dev/null
+/*
+ */
+package org.apache.tomcat.lite;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class SimpleServlet extends HttpServlet {
+ public void doGet(HttpServletRequest req, HttpServletResponse res)
+ throws IOException {
+ res.setHeader("Foo", "Bar");
+ res.getWriter().write("Hello world");
+ }
+ public void doPost(HttpServletRequest req, HttpServletResponse res)
+ throws IOException {
+ res.setHeader("Foo", "Post");
+ res.getWriter().write("Hello post world");
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.tomcat.lite;
+
+
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.apache.coyote.OutputBuffer;
+import org.apache.coyote.Response;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TomcatLiteNoConnectorTest extends TestCase {
+
+ TomcatLite lite = new TomcatLite();
+
+ public void setUp() throws Exception {
+ LiteTestHelper.initServletsAndRun(lite, 0);
+ }
+
+ public void tearDown() throws Exception {
+ lite.stop();
+ }
+
+
+
+ public void testSimpleRequest() throws Exception {
+ ByteChunk out = new ByteChunk();
+
+ ServletRequestImpl req =
+ LiteTestHelper.createMessage(lite, "/test1/1stTest", out);
+
+ // more changes can be made to the req, populate fields that a
+ // connector would
+
+ ServletResponseImpl res = lite.service(req);
+
+ assertEquals("Hello world", out.toString());
+ // Headers are still in the response
+ assertEquals(res.getHeader("Foo"), "Bar");
+ assertEquals(res.getStatus(), 200);
+ }
+
+ public void testPostRequest() throws Exception {
+ ByteChunk out = new ByteChunk();
+ ServletRequestImpl req =
+ LiteTestHelper.createMessage(lite, "/test1/1stTest", out);
+ req.setMethod("POST");
+
+ ServletResponseImpl res = lite.service(req);
+
+ assertEquals("Hello post world", out.toString());
+ // Headers are still in the response
+ assertEquals(res.getHeader("Foo"), "Post");
+ assertEquals(res.getStatus(), 200);
+ }
+
+ public void testException() throws IOException, Exception {
+ ByteChunk out = new ByteChunk();
+ ServletRequestImpl req =
+ LiteTestHelper.createMessage(lite, "/test1/testException", out);
+ ServletResponseImpl res = lite.service(req);
+ assertEquals(res.getStatus(), 500);
+ }
+
+}
--- /dev/null
+/*
+ * 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.tomcat.lite;
+
+import junit.framework.TestCase;
+
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TomcatLiteSimpleTest extends TestCase {
+
+ TomcatLite lite = new TomcatLite();
+
+ void initServer() throws Exception {
+ LiteTestHelper.initServletsAndRun(lite, 8804);
+ }
+
+ public void setUp() throws Exception {
+ initServer();
+ }
+
+ public void tearDown() throws Exception {
+ lite.stop();
+ }
+
+ public void testSimpleRequest() throws Exception {
+ ByteChunk resb =
+ LiteTestHelper.getUrl("http://localhost:8804/test1/1stTest");
+ assertTrue(resb.length() > 0);
+ assertEquals("Hello world", resb.toString());
+ }
+
+
+}
--- /dev/null
+/*
+ * 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.tomcat.test.watchdog;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
+
+/**
+ * Represents a collection of Cookie instances.
+ * <p>
+ * Fires events when the cookies have been changed internally. Deals
+ * with management of cookies in terms of saving and loading them,
+ * and disabling them.
+ *
+ * @author Ramesh.Mandava
+ */
+public class CookieController {
+
+// private VetoableChangeSupport vceListeners;
+
+ private static Hashtable cookieJar = new Hashtable();
+
+ /* public no arg constructor for bean */
+ public CookieController() {
+ }
+
+/////////////////////////////////////////////////////////////
+ /**
+ * Records any cookies which have been sent as part of an HTTP response.
+ * The connection parameter must be already have been opened, so that
+ * the response headers are available. It's ok to pass a non-HTTP
+ * URL connection, or one which does not have any set-cookie headers.
+ */
+ public void recordAnyCookies(Vector rcvVectorOfCookies , URL url ) {
+
+ if ((rcvVectorOfCookies == null) || ( rcvVectorOfCookies.size()== 0) ) {
+ // no headers here
+ return;
+ }
+ try {
+ /*
+ Properties properties = new Properties();
+ FileInputStream fin = new FileInputStream("ServerAutoRun.properties");
+ properties.load(fin);
+
+ String cookiepolicy = properties.getProperty("cookie.acceptpolicy");
+ if (cookiepolicy == null || cookiepolicy.equals("none")) {
+ return;
+ }
+ */
+
+
+
+
+ for (int hi = 0; hi<rcvVectorOfCookies.size(); hi++) {
+
+ String cookieValue = (String)rcvVectorOfCookies.elementAt(hi) ;
+ recordCookie(url, cookieValue); // What to do here
+ }
+
+ }
+ catch( Exception e )
+ {
+ System.out.println("exception : " + e );
+ }
+ }
+
+
+ /**
+ * Create a cookie from the cookie, and use the HttpURLConnection to
+ * fill in unspecified values in the cookie with defaults.
+ */
+ public void recordCookie(URL url,
+ String cookieValue) {
+ HttpCookie cookie = new HttpCookie(url, cookieValue);
+
+ // First, check to make sure the cookie's domain matches the
+ // server's, and has the required number of '.'s
+ String twodot[]=
+ {"com", "edu", "net", "org", "gov", "mil", "int"};
+ String domain = cookie.getDomain();
+ if( domain == null )
+ return;
+ int index = domain.indexOf(':');
+ if (index != -1) {
+ int portCookie;
+ try {
+ portCookie = (Integer.valueOf(domain.substring(index+1,domain.length()))).intValue();
+ } catch (Exception e) {
+ return;
+ }
+ portCookie = ( portCookie == -1 ) ? 80 : portCookie;
+ domain=domain.substring(0,index);
+ }
+ domain.toLowerCase();
+
+ String host = url.getHost();
+ host.toLowerCase();
+
+ boolean domainOK = host.equals(domain);
+ if( !domainOK && host.endsWith( domain ) ) {
+ int dotsNeeded = 2;
+ for( int i = 0; i < twodot.length; i++ ) {
+ if( domain.endsWith( twodot[i] ) ) {
+ dotsNeeded = 1;
+ }
+ }
+
+ int lastChar = domain.length();
+ for( ; lastChar > 0 && dotsNeeded > 0; dotsNeeded-- ) {
+ lastChar = domain.lastIndexOf( '.', lastChar-1 );
+ }
+
+ if( lastChar > 0 )
+ domainOK = true;
+ }
+
+ if( domainOK ) {
+ recordCookie(cookie);
+
+ }
+ }
+
+
+ /**
+ * Record the cookie in the in-memory container of cookies. If there
+ * is already a cookie which is in the exact same domain with the
+ * exact same
+ */
+ public void recordCookie(HttpCookie cookie) {
+ if (!checkIfCookieOK(cookie)) {
+ return;
+ }
+ synchronized (cookieJar) {
+
+ String domain = cookie.getDomain().toLowerCase();
+
+ Vector cookieList = (Vector)cookieJar.get(domain);
+ if (cookieList == null) {
+ cookieList = new Vector();
+ }
+
+ addOrReplaceCookie(cookieList, cookie);
+ cookieJar.put(domain, cookieList);
+
+ }
+
+ }
+
+ public boolean checkIfCookieOK(HttpCookie cookie) {
+ return true;
+ }
+
+ /**
+ * Scans the vector of cookies looking for an exact match with the
+ * given cookie. Replaces it if there is one, otherwise adds
+ * one at the end. The vector is presumed to have cookies which all
+ * have the same domain, so the domain of the cookie is not checked.
+ * <p>
+ * <p>
+ * If this is called, it is assumed that the cookie jar is exclusively
+ * held by the current thread.
+ *
+ */
+ private void addOrReplaceCookie(Vector cookies,
+ HttpCookie cookie) {
+ int numCookies = cookies.size();
+
+ String path = cookie.getPath();
+ String name = cookie.getName();
+ HttpCookie replaced = null;
+ int replacedIndex = -1;
+
+ for (int i = 0; i < numCookies; i++) {
+ HttpCookie existingCookie = (HttpCookie)cookies.elementAt(i);
+
+ String existingPath = existingCookie.getPath();
+ if (path.equals(existingPath)) {
+ String existingName = existingCookie.getName();
+ if (name.equals(existingName)) {
+ // need to replace this one!
+ replaced = existingCookie;
+ replacedIndex = i;
+ break;
+ }
+ }
+ }
+
+
+ // Do the replace - if cookie has already expired, remove
+ // the replaced cookie.
+ if (replaced != null) {
+ if (cookie.isSaveableInMemory()) {
+ cookies.setElementAt(cookie, replacedIndex);
+ //System.out.println("REPLACED existing cookie with " + cookie);
+ } else {
+ cookies.removeElementAt(replacedIndex);
+ //System.out.println("Removed cookie b/c or expr " + cookie);
+ }
+
+ } else { // only save the cookie in memory if it is non persistent
+ // or not expired.
+ if (cookie.isSaveableInMemory()) {
+ cookies.addElement(cookie);
+ //System.out.println("RECORDED new cookie " + cookie);
+ }
+
+ }
+
+ }
+
+ public String applyRelevantCookies(URL url ) {
+
+ try {
+ /*
+ Properties properties = new Properties();
+ FileInputStream fin = new FileInputStream("ServerAutoRun.properties");
+ properties.load(fin);
+ // check current accept policy instead enableCookies
+ String cookiepolicy = properties.getProperty("cookie.acceptpolicy");
+ if (cookiepolicy == null || cookiepolicy.equals("none")) {
+ return null;
+ }
+
+ */
+
+ return applyCookiesForHost(url);
+
+ }
+ catch ( Exception e )
+ {
+ System.out.println("Exception : " +e );
+ return null;
+ }
+
+
+
+
+ }
+
+
+ /**
+ * Host may be a FQDN, or a partial domain name starting with a dot.
+ * Adds any cookies which match the host and path to the
+ * cookie set on the URL connection.
+ */
+ private String applyCookiesForHost(URL url ){
+ String cookieString = null;
+ Vector cookieVector = getAllRelevantCookies(url);
+
+ if (cookieVector != null) {
+
+ for (Enumeration e = cookieVector.elements(); e.hasMoreElements();) {
+ HttpCookie cookie = (HttpCookie)e.nextElement();
+ if( cookieString == null ) {
+ cookieString = cookie.getNameValue();
+ } else {
+ cookieString = cookieString + "; " + cookie.getNameValue();
+ }
+ }
+
+ /*
+
+ if( cookieString != null ) {
+ httpConn.setRequestProperty("Cookie", cookieString);
+
+// System.out.println("Returned cookie string: " + cookieString + " for HOST = " + host);
+ }
+
+ */
+
+
+ }
+// System.out.println("Returned cookie string: " + cookieString + " for HOST = " + host);
+ return cookieString;
+
+ }
+
+ private Vector getAllRelevantCookies(URL url) {
+ String host = url.getHost();
+ Vector cookieVector = getSubsetRelevantCookies(host, url);
+
+ Vector tempVector;
+ int index;
+
+ while ((index = host.indexOf('.', 1)) >= 0) {
+ // trim off everything up to, and including the dot.
+ host = host.substring(index+1);
+
+ // add onto cookieVector
+ tempVector = getSubsetRelevantCookies(host,url);
+ if (tempVector != null ) {
+ for (Enumeration e = tempVector.elements(); e.hasMoreElements(); ) {
+ if (cookieVector == null) {
+ cookieVector = new Vector(2);
+ }
+
+ cookieVector.addElement(e.nextElement());
+
+ }
+ }
+ }
+ return cookieVector;
+ }
+
+ private Vector getSubsetRelevantCookies(String host, URL url) {
+
+ Vector cookieList = (Vector)cookieJar.get(host);
+
+// System.out.println("getRelevantCookies() .. for host, url " + host +", "+url);
+ Vector cookiePortList = (Vector)cookieJar.get(host+":"+((url.getPort() == -1) ? 80 : url.getPort()));
+ if (cookiePortList != null) {
+ if (cookieList == null) {
+ cookieList = new Vector(10);
+ }
+ Enumeration cookies = cookiePortList.elements();
+ while (cookies.hasMoreElements()) {
+ cookieList.addElement(cookies.nextElement());
+ }
+ }
+
+
+ if (cookieList == null) {
+ return null;
+ }
+
+ String path = url.getFile();
+// System.out.println(" path is " + path + "; protocol = " + url.getProtocol());
+
+
+ int queryInd = path.indexOf('?');
+ if (queryInd > 0) {
+ // strip off the part following the ?
+ path = path.substring(0, queryInd);
+ }
+
+ Enumeration cookies = cookieList.elements();
+ Vector cookiesToSend = new Vector(10);
+
+ while (cookies.hasMoreElements()) {
+ HttpCookie cookie = (HttpCookie)cookies.nextElement();
+
+ String cookiePath = cookie.getPath();
+
+ if (path.startsWith(cookiePath)) {
+ // larrylf: Actually, my documentation (from Netscape)
+ // says that /foo should
+ // match /foobar and /foo/bar. Yuck!!!
+
+ if (!cookie.hasExpired()) {
+ cookiesToSend.addElement(cookie);
+ }
+
+/*
+ We're keeping this piece of commented out code around just in
+ case we decide to put it back. the spec does specify the above,
+ but it is so disgusting!
+
+ int cookiePathLen = cookiePath.length();
+
+ // verify that /foo does not match /foobar by mistake
+ if ((path.length() == cookiePathLen)
+ || (path.length() > cookiePathLen &&
+ path.charAt(cookiePathLen) == '/')) {
+
+ // We have a matching cookie!
+
+ if (!cookie.hasExpired()) {
+ cookiesToSend.addElement(cookie);
+ }
+ }
+*/
+ }
+ }
+
+ // Now, sort the cookies in most to least specific order
+ // Yes, its the deaded bubblesort!! Wah Ha-ha-ha-ha....
+ // (it should be a small vector, so perf is not an issue...)
+ if( cookiesToSend.size() > 1 ) {
+ for( int i = 0; i < cookiesToSend.size()-1; i++ ) {
+ HttpCookie headC = (HttpCookie)cookiesToSend.elementAt(i);
+ String head = headC.getPath();
+ // This little excercise is a cheap way to get
+ // '/foo' to read more specfic then '/'
+ if( !head.endsWith("/") ) {
+ head = head + "/";
+ }
+ for( int j = i+1; j < cookiesToSend.size(); j++ ) {
+ HttpCookie scanC = (HttpCookie)cookiesToSend.elementAt(j);
+ String scan = scanC.getPath();
+ if( !scan.endsWith("/") ) {
+ scan = scan + "/";
+ }
+
+ int headCount = 0;
+ int index = -1;
+ while( (index=head.indexOf('/', index+1)) != -1 ) {
+ headCount++;
+ }
+ index = -1;
+
+ int scanCount = 0;
+ while( (index=scan.indexOf('/', index+1)) != -1 ) {
+ scanCount++;
+ }
+
+ if( scanCount > headCount ) {
+ cookiesToSend.setElementAt(headC, j);
+ cookiesToSend.setElementAt(scanC, i);
+ headC = scanC;
+ head = scan;
+ }
+ }
+ }
+ }
+
+
+ return cookiesToSend;
+
+ }
+
+ /*
+ * Writes cookies out to PrintWriter if they are persistent
+ * (i.e. have a expr date)
+ * and haven't expired. Will remove cookies that have expired as well
+ */
+ private void saveCookiesToStream(PrintWriter pw) {
+
+ Enumeration cookieLists = cookieJar.elements();
+
+ while (cookieLists.hasMoreElements()) {
+ Vector cookieList = (Vector)cookieLists.nextElement();
+
+ Enumeration cookies = cookieList.elements();
+
+ while (cookies.hasMoreElements()) {
+ HttpCookie cookie = (HttpCookie)cookies.nextElement();
+
+ if (cookie.getExpirationDate() != null) {
+ if (cookie.isSaveable()) {
+ pw.println(cookie);
+ } else { // the cookie must have expired,
+ //remove from Vector cookieList
+ cookieList.removeElement(cookie);
+ }
+
+ }
+ }
+ }
+ // Must print something to the printwriter in the case that
+ // the cookieJar has been cleared - otherwise the old cookie
+ // file will continue to exist.
+ pw.print("");
+ }
+/////////////////////////////////////////////////////////////
+ /* adds cookieList to the existing cookie jar*/
+ public void addToCookieJar(HttpCookie[] cookieList) {
+
+ if (cookieList != null) {
+ for (int i = 0; i < cookieList.length; i++) {
+
+ recordCookie(cookieList[i]);
+ }
+ }
+
+ }
+
+ /*adds one cookie to the Cookie Jar */
+ public void addToCookieJar(String cookieString, URL docURL) {
+ recordCookie(new HttpCookie(docURL, cookieString));
+ }
+
+ /* loads the cookies from the given filename */
+ public void loadCookieJarFromFile(String cookieFileName) {
+ try {
+ FileReader fr = new FileReader(cookieFileName);
+
+ BufferedReader in = new BufferedReader(fr);
+
+ try {
+ String cookieString;
+ while ((cookieString = in.readLine()) != null) {
+ HttpCookie cookie = new HttpCookie(cookieString);
+ // Record the cookie, without notification. We don't
+ // do a notification for cookies that are read at
+ // program start-up.
+ recordCookie(cookie);
+ }
+ } finally {
+ in.close();
+ }
+
+
+ } catch (IOException e) {
+ // do nothing; it's not an error not to have persistent cookies
+ }
+
+ }
+
+ /* saves the cookies to the given file specified by fname */
+ public void saveCookieJarToFile(String cookieFileName) {
+ try {
+ FileWriter fw = new FileWriter(cookieFileName);
+ PrintWriter pw = new PrintWriter(fw, false);
+
+ try {
+ saveCookiesToStream(pw);
+ } finally {
+ pw.close();
+ }
+
+ } catch (IOException e) {
+ // REMIND: I18N
+ System.err.println("Saving cookies failed " + e.getMessage());
+ }
+ }
+
+ /**
+ * Return an array with all of the cookies represented by this
+ * jar. This is useful when the bean is shutting down, and the client
+ * wants to make the cookie jar persist.
+ */
+ public HttpCookie[] getAllCookies() {
+
+ Vector result = new Vector();
+ Hashtable jar;
+ jar = (Hashtable) cookieJar.clone();
+
+ synchronized (jar) {
+
+ for (Enumeration e = jar.elements(); e.hasMoreElements() ;) {
+ Vector v = (Vector) e.nextElement();
+ for (int i = 0; i < v.size(); i++) {
+ HttpCookie hc = (HttpCookie) v.elementAt(i);
+ result.addElement(hc);
+
+ }
+
+ }
+ }
+
+ HttpCookie[] resultA = new HttpCookie[result.size()];
+ for (int i = 0; i < result.size(); i++) {
+ resultA[i] = (HttpCookie) result.elementAt(i);
+ }
+ return resultA;
+ }
+
+ /* Gets all cookies that applies for the URL */
+ public HttpCookie[] getCookiesForURL(URL url) {
+
+ Vector cookieVector = getAllRelevantCookies(url);
+
+ if (cookieVector == null) {
+ return null;
+ }
+
+ int i = 0;
+ HttpCookie[] cookieArr = new HttpCookie[cookieVector.size()];
+
+ for (Enumeration e = cookieVector.elements(); e.hasMoreElements(); ) {
+
+ cookieArr[i++] = (HttpCookie)e.nextElement();
+// System.out.println("cookieArr["+(i-1)+"] = " +cookieArr[i-1].toString());
+ }
+
+ return cookieArr;
+ }
+
+ /* this will set the property of enableCookies to isDisabled */
+ public void setCookieDisable(boolean isDisabled) {
+
+ // Pending visit back this again
+ try {
+ Properties properties = new Properties();
+ properties.load(new FileInputStream("ServerAutoRun.properties") );
+
+
+ properties.put("enableCookies", isDisabled ? "false" : "true");
+ properties.store(new FileOutputStream("ServerAutoRun.properties"),"comments");
+ }
+ catch ( Exception e )
+ {
+ System.out.println("Exception : " + e );
+ }
+ }
+
+ public void discardAllCookies() {
+ cookieJar.clear();
+
+ }
+
+ /*
+ * purges any expired cookies in the Cookie hashtable.
+ */
+ public void purgeExpiredCookies() {
+ Enumeration cookieLists = cookieJar.elements();
+
+ while (cookieLists.hasMoreElements()) {
+ Vector cookieList = (Vector)cookieLists.nextElement();
+
+ Enumeration cookies = cookieList.elements();
+
+ while (cookies.hasMoreElements()) {
+ HttpCookie cookie = (HttpCookie)cookies.nextElement();
+
+ if (cookie.hasExpired()) {
+ cookieList.removeElement(cookie);
+ }
+ }
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+/**
+* @Author Costin, Ramesh.Mandava
+*/
+
+package org.apache.tomcat.test.watchdog;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tomcat.util.buf.HexUtils;
+
+
+// derived from Jsp
+
+public class GTest {
+
+ int failureCount = 0;
+ int passCount = 0;
+ Throwable lastError;
+ boolean hasFailed = false;
+
+ String prefix = "http";
+ String host = "localhost";
+ String localHost = null;
+ String localIP = null;
+ int port = 8080;
+ int debug = 0;
+
+ String description = "No description";
+
+ String request;
+ HashMap requestHeaders = new HashMap();
+ String content;
+
+ // true if task is nested
+ private boolean nested = false;
+
+ // Expected response
+ boolean magnitude = true;
+ boolean exactMatch = false;
+
+ // expect a response body
+ boolean expectResponseBody = true;
+
+ // Match the body against a golden file
+ String goldenFile;
+ // Match the body against a string
+ String responseMatch;
+ // the response should include the following headers
+ HashMap expectHeaders = new HashMap();
+
+ // Headers that should not be found in response
+ HashMap unexpectedHeaders = new HashMap();
+
+ // Match request line
+ String returnCode = "";
+ String returnCodeMsg = "";
+
+ // Actual response
+ String responseLine;
+ byte[] responseBody;
+ HashMap headers;
+
+
+ // For Report generation
+ StringBuffer resultOut = new StringBuffer();
+
+ boolean firstTask = false;
+ boolean lastTask = false;
+ String expectedString;
+ String actualString;
+
+ String testName;
+ String assertion;
+ String testStrategy;
+
+ // For Session Tracking
+ static Hashtable sessionHash;
+ static Hashtable cookieHash;
+
+ String testSession;
+ Vector cookieVector;
+ URL requestURL;
+ CookieController cookieController ;
+
+ /**
+ * Creates a new <code>GTest</code> instance.
+ *
+ */
+ public GTest() {
+ }
+
+ /**
+ * <code>setTestSession</code> adds a
+ * CookieController for the value of sessionName
+ *
+ * @param sessionName a <code>String</code> value
+ */
+ public void setTestSession( String sessionName ) {
+ testSession = sessionName;
+
+ if ( sessionHash == null ) {
+ sessionHash = new Hashtable();
+ } else if ( sessionHash.get( sessionName ) == null ) {
+ sessionHash.put ( sessionName, new CookieController() );
+ }
+ }
+
+ /**
+ * <code>setTestName</code> sets the current test name.
+ *
+ * @param tn current testname.
+ */
+ public void setTestName ( String tn ) {
+ testName = tn;
+ }
+
+ /**
+ * <code>setAssertion</code> sets the assertion text
+ * for the current test.
+ *
+ * @param assertion assertion text
+ */
+ public void setAssertion ( String assertion ) {
+ this.assertion = assertion;
+ }
+
+ /**
+ * <code>setTestStrategy</code> sets the test strategy
+ * for the current test.
+ *
+ * @param strategy test strategy text
+ */
+ public void setTestStrategy ( String strategy ) {
+ testStrategy = strategy;
+ }
+
+ /**
+ * <code>getTestName</code> returns the current
+ * test name.
+ *
+ * @return a <code>String</code> value
+ */
+ public String getTestName( ) {
+ return testName;
+ }
+
+ /**
+ * <code>getAssertion</code> returns the current
+ * assertion text.
+ *
+ * @return a <code>String</code> value
+ */
+ public String getAssertion( ) {
+ return assertion;
+ }
+
+ /**
+ * <code>getTestStrategy</code> returns the current
+ * test strategy test.
+ *
+ * @return a <code>String</code> value
+ */
+ public String getTestStrategy( ) {
+ return testStrategy;
+ }
+
+ /**
+ * <code>setFirstTask</code> denotes that current task
+ * being executed is the first task within the list.
+ *
+ * @param a <code>boolean</code> value
+ */
+ public void setFirstTask( boolean val ) {
+ firstTask = val;
+ }
+
+
+ /**
+ * <code>setLastTask</code> denotes that the current task
+ * being executed is the last task within the list.
+ *
+ * @param a <code>boolean</code> value
+ */
+ public void setLastTask ( boolean val ) {
+ lastTask = val;
+ }
+
+ /**
+ * <code>setPrefix</code> sets the protocol
+ * prefix. Defaults to "http"
+ *
+ * @param prefix Either http or https
+ */
+ public void setPrefix( String prefix ) {
+ this.prefix = prefix;
+ }
+
+ /**
+ * <code>setHost</code> sets hostname where
+ * the target server is running. Defaults to
+ * "localhost"
+ *
+ * @param h a <code>String</code> value
+ */
+ public void setHost( String h ) {
+ this.host = h;
+ }
+
+ /**
+ * <code>setPort</code> sets the port
+ * that the target server is listening on.
+ * Defaults to "8080"
+ *
+ * @param portS a <code>String</code> value
+ */
+ public void setPort( String portS ) {
+ this.port = Integer.valueOf( portS ).intValue();
+ }
+
+ /**
+ * <code>setExactMatch</code> determines if a
+ * byte-by-byte comparsion is made of the server's
+ * response and the test's goldenFile, or if
+ * a token comparison is made. By default, only
+ * a token comparison is made ("false").
+ *
+ * @param exact a <code>String</code> value
+ */
+ public void setExactMatch( String exact ) {
+ exactMatch = Boolean.valueOf( exact ).booleanValue();
+ }
+
+ /**
+ * <code>setContent</code> String value upon which
+ * the request header Content-Length is based upon.
+ *
+ * @param s a <code>String</code> value
+ */
+ public void setContent( String s ) {
+ this.content = s;
+ }
+
+ /**
+ * <code>setDebug</code> enables debug output.
+ * By default, this is disabled ( value of "0" ).
+ *
+ * @param debugS a <code>String</code> value
+ */
+ public void setDebug( String debugS ) {
+ debug = Integer.valueOf( debugS ).intValue();
+ }
+
+ /**
+ * <code>setMagnitude</code> Expected return
+ * value of the test execution.
+ * Defaults to "true"
+ *
+ * @param magnitudeS a <code>String</code> value
+ */
+ public void setMagnitude( String magnitudeS ) {
+ magnitude = Boolean.valueOf( magnitudeS ).booleanValue();
+ }
+
+ /**
+ * <code>setGoldenFile</code> Sets the goldenfile
+ * that will be used to validate the server's response.
+ *
+ * @param s fully qualified path and filename
+ */
+ public void setGoldenFile( String s ) {
+ this.goldenFile = s;
+ }
+
+ /**
+ * <code>setExpectResponseBody</code> sets a flag
+ * to indicate if a response body is expected from the
+ * server or not
+ *
+ * @param b a <code>boolean</code> value
+ */
+ public void setExpectResponseBody( boolean b ) {
+ this.expectResponseBody = b;
+ }
+
+ /**
+ * <code>setExpectHeaders</code> Configures GTest
+ * to look for the header passed in the server's
+ * response.
+ *
+ * @param s a <code>String</code> value in the
+ * format of <header-field>:<header-value>
+ */
+ public void setExpectHeaders( String s ) {
+ this.expectHeaders = new HashMap();
+ StringTokenizer tok = new StringTokenizer( s, "|" );
+ while ( tok.hasMoreElements() ) {
+ String header = (String) tok.nextElement();
+ setHeaderDetails( header, expectHeaders, false );
+ }
+ }
+
+ /**
+ * <code>setUnexpectedHeaders</code> Configures GTest
+ * to look for the header passed to validate that it
+ * doesn't exist in the server's response.
+ *
+ * @param s a <code>String</code> value in the
+ * format of <header-field>:<header-value>
+ */
+ public void setUnexpectedHeaders( String s ) {
+ this.unexpectedHeaders = new HashMap();
+ setHeaderDetails( s, unexpectedHeaders, false );
+ }
+
+ public void setNested( String s ) {
+ nested = Boolean.valueOf( s ).booleanValue();
+ }
+
+ /**
+ * <code>setResponseMatch</code> Match the
+ * passed value in the server's response.
+ *
+ * @param s a <code>String</code> value
+ */
+ public void setResponseMatch( String s ) {
+ this.responseMatch = s;
+ }
+
+ /**
+ * <code>setRequest</code> Sets the HTTP/HTTPS
+ * request to be sent to the target server
+ * Ex.
+ * GET /servlet_path/val HTTP/1.0
+ *
+ * @param s a <code>String</code> value in the form
+ * of METHOD PATH HTTP_VERSION
+ */
+ public void setRequest ( String s ) {
+ this.request = s;
+ }
+
+ /**
+ * <code>setReturnCode</code> Sets the expected
+ * return code from the server's response.
+ *
+ * @param code a valid HTTP response status code
+ */
+ public void setReturnCode( String code ) {
+ this.returnCode = code;
+ }
+
+ /**
+ * Describe <code>setReturnCodeMsg</code> Sets the expected
+ * return message to be found in the server's
+ * response.
+ *
+ * @param code a valid HTTP resonse status code
+ * @param message a <code>String</code> value
+ */
+ public void setReturnCodeMsg( String message ) {
+ this.returnCodeMsg = message;
+ }
+
+ /**
+ * <code>setRequestHeaders</code> Configures the request
+ * headers GTest should send to the target server.
+ *
+ * @param s a <code>String</code> value in for format
+ * of <field-name>:<field-value>
+ */
+ public void setRequestHeaders( String s ) {
+ requestHeaders = new HashMap();
+ StringTokenizer tok = new StringTokenizer( s, "|" );
+ while ( tok.hasMoreElements() ) {
+ String header = (String) tok.nextElement();
+ setHeaderDetails( header, requestHeaders, true );
+ }
+ }
+
+ // Inner tests are not used currently, can be reworked
+
+// /**
+// * Add a Task to this container
+// *
+// * @param Task to add
+// */
+// public void addTask(Task task) {
+// children.add(task);
+// }
+
+ /**
+ * <code>execute</code> Executes the test.
+ *
+ * @exception BuildException if an error occurs
+ */
+ public void execute() {
+
+ try {
+
+ if ( resultOut != null && !nested ) {
+ resultOut.append("\ntestName: " + testName);
+ resultOut.append("\nreq: " + request);
+ resultOut.append("\nassertion: " + assertion);
+ resultOut.append("\ntestStrategy: " + testStrategy);
+ }
+
+ WatchdogHttpClient.dispatch(this);
+
+ hasFailed = !checkResponse( magnitude );
+
+
+// if ( !children.isEmpty() ) {
+// Iterator iter = children.iterator();
+// while (iter.hasNext()) {
+// Task task = (Task) iter.next();
+// task.perform();
+// }
+// }
+
+ if ( !hasFailed && !nested ) {
+ passCount++;
+ if ( resultOut != null ) {
+ resultOut.append( "<result>PASS</result>\n" );
+ }
+ System.out.println( " PASSED " + testName + "\n (" + request + ")" );
+ } else if ( hasFailed && !nested ){
+ failureCount++;
+ if ( resultOut != null ) {
+ resultOut.append( "<result>FAIL</result>\n" );
+ }
+ System.out.println( " FAILED " + testName + "\n (" + request + ")\n" +
+ resultOut.toString());
+ }
+
+ } catch ( Exception ex ) {
+ failureCount++;
+ System.out.println( " FAIL " + description + " (" + request + ")" );
+ lastError = ex;
+ ex.printStackTrace();
+ } finally {
+ if ( !nested ) {
+ hasFailed = false;
+ }
+ }
+ }
+
+ /**
+ * <code>checkResponse</code> Executes various response
+ * checking mechanisms against the server's response.
+ * Checks include:
+ * <ul>
+ * <li>expected headers
+ * <li>unexpected headers
+ * <li>return codes and messages in the Status-Line
+ * <li>response body comparison againt a goldenfile
+ * </ul>
+ *
+ * @param testCondition a <code>boolean</code> value
+ * @return a <code>boolean</code> value
+ * @exception Exception if an error occurs
+ */
+ private boolean checkResponse( boolean testCondition )
+ throws Exception {
+ boolean match = false;
+
+ if ( responseLine != null ) {
+ // If returnCode doesn't match
+ if ( responseLine.indexOf( "HTTP/1." ) > -1 ) {
+
+ if ( !returnCode.equals( "" ) ) {
+ boolean resCode = ( responseLine.indexOf( returnCode ) > -1 );
+ boolean resMsg = ( responseLine.indexOf( returnCodeMsg ) > -1 );
+
+ if ( returnCodeMsg.equals( "" ) ) {
+ match = resCode;
+ } else {
+ match = ( resCode && resMsg );
+ }
+
+ if ( match != testCondition ) {
+
+ if ( resultOut != null ) {
+ String expectedStatusCode = "<expectedStatusCode>" + returnCode + "</expectedReturnCode>\n";
+ String expectedReasonPhrase = "<expectedReasonPhrase>" + returnCodeMsg + "</expectedReasonPhrase>";
+ actualString = "<actualStatusLine>" + responseLine + "</actualStatusLine>\n";
+ resultOut.append( expectedStatusCode );
+ resultOut.append( expectedReasonPhrase );
+ resultOut.append( actualString );
+ }
+
+ return false;
+ }
+ }
+ } else {
+ resultOut.append("\n<failure>Wrong Http version: " + responseLine +
+ "</failure>");
+ return false;
+ }
+ } else {
+ resultOut.append("\n<failure>No response from server</failure>" );
+ return false;
+ }
+
+ /*
+ * Check for headers the test expects to be in the server's response
+ */
+
+ // Duplicate set of response headers
+ HashMap copiedHeaders = cloneHeaders( headers );
+
+ // used for error reporting
+ String currentHeaderField = null;
+ String currentHeaderValue = null;
+
+ if ( !expectHeaders.isEmpty() ) {
+ boolean found = false;
+ String expHeader = null;
+
+ if ( !headers.isEmpty() ) {
+ Iterator expectIterator = expectHeaders.keySet().iterator();
+ while ( expectIterator.hasNext() ) {
+ found = false;
+ String expFieldName = (String) expectIterator.next();
+ currentHeaderField = expFieldName;
+ ArrayList expectValues = (ArrayList) expectHeaders.get( expFieldName );
+ Iterator headersIterator = copiedHeaders.keySet().iterator();
+
+ while( headersIterator.hasNext() ) {
+ String headerFieldName = (String) headersIterator.next();
+ ArrayList headerValues = (ArrayList) copiedHeaders.get( headerFieldName );
+
+ // compare field names and values in an HTTP 1.x compliant fashion
+ if ( ( headerFieldName.equalsIgnoreCase( expFieldName ) ) ) {
+ int hSize = headerValues.size();
+ int eSize = expectValues.size();
+
+ // number of expected headers found in server response
+ int numberFound = 0;
+
+ for ( int i = 0; i < eSize; i++ ) {
+ currentHeaderValue = (String) expectValues.get( i );
+
+ /*
+ * Handle the Content-Type header appropriately
+ * based on the the test is configured to look for.
+ */
+ if ( currentHeaderField.equalsIgnoreCase( "content-type" ) ) {
+ String resVal = (String) headerValues.get( 0 );
+ if ( currentHeaderValue.indexOf( ';' ) > -1 ) {
+ if ( currentHeaderValue.equals( resVal ) ) {
+ numberFound++;
+ headerValues.remove( 0 );
+ }
+ } else if ( resVal.indexOf( currentHeaderValue ) > -1 ) {
+ numberFound++;
+ headerValues.remove( 0 );
+ }
+ } else if ( currentHeaderField.equalsIgnoreCase( "location" ) ) {
+ String resVal = (String) headerValues.get( 0 );
+ int idx = currentHeaderValue.indexOf( ":80/" );
+ if ( idx > -1 ) {
+ String tempValue = currentHeaderValue.substring( 0, idx ) +
+ currentHeaderValue.substring( idx + 3 );
+ if ( currentHeaderValue.equals( resVal ) ||
+ tempValue.equals( resVal ) ) {
+ numberFound++;
+ headerValues.remove( 0 );
+ }
+ } else {
+ if ( currentHeaderValue.equals( resVal ) ) {
+ numberFound++;
+ headerValues.remove( 0 );
+ }
+ }
+ } else if ( headerValues.contains( currentHeaderValue ) ) {
+ numberFound++;
+ headerValues.remove( headerValues.indexOf( currentHeaderValue ) );
+ }
+ }
+ if ( numberFound == eSize ) {
+ found = true;
+ }
+ }
+ }
+ if ( !found ) {
+ /*
+ * Expected headers not found in server response.
+ * Break the processing loop.
+ */
+ break;
+ }
+ }
+ }
+
+ if ( !found ) {
+ StringBuffer actualBuffer = new StringBuffer( 128 );
+ if ( resultOut != null ) {
+ expectedString = "<expectedHeaderNotFound>" + currentHeaderField + ": " + currentHeaderValue + "</expectedHeader>\n";
+ }
+ if ( !headers.isEmpty() ) {
+ Iterator iter = headers.keySet().iterator();
+ while ( iter.hasNext() ) {
+ String headerName = (String) iter.next();
+ ArrayList vals = (ArrayList) headers.get( headerName );
+ String[] val = (String[]) vals.toArray( new String[ vals.size() ] );
+ for ( int i = 0; i < val.length; i++ ) {
+ if ( resultOut != null ) {
+ actualBuffer.append( "<actualHeader>" + headerName + ": " + val[ i ] + "</actualHeader>\n" );
+ }
+ }
+ }
+ if ( resultOut != null ) {
+ resultOut.append( expectedString );
+ resultOut.append( actualBuffer.toString() );
+ }
+ }
+ return false;
+ }
+ }
+
+ /*
+ * Check to see if we're looking for unexpected headers.
+ * If we are, compare the values in the unexectedHeaders
+ * ArrayList against the headers from the server response.
+ * if the unexpected header is found, then return false.
+ */
+
+ if ( !unexpectedHeaders.isEmpty() ) {
+ boolean found = false;
+ String unExpHeader = null;
+ // Check if we got any unexpected headers
+
+ if ( !copiedHeaders.isEmpty() ) {
+ Iterator unexpectedIterator = unexpectedHeaders.keySet().iterator();
+ while ( unexpectedIterator.hasNext() ) {
+ found = false;
+ String unexpectedFieldName = (String) unexpectedIterator.next();
+ ArrayList unexpectedValues = (ArrayList) unexpectedHeaders.get( unexpectedFieldName );
+ Iterator headersIterator = copiedHeaders.keySet().iterator();
+
+ while ( headersIterator.hasNext() ) {
+ String headerFieldName = (String) headersIterator.next();
+ ArrayList headerValues = (ArrayList) copiedHeaders.get( headerFieldName );
+
+ // compare field names and values in an HTTP 1.x compliant fashion
+ if ( ( headerFieldName.equalsIgnoreCase( unexpectedFieldName ) ) ) {
+ int hSize = headerValues.size();
+ int eSize = unexpectedValues.size();
+ int numberFound = 0;
+ for ( int i = 0; i < eSize; i++ ) {
+ if ( headerValues.contains( unexpectedValues.get( i ) ) ) {
+ numberFound++;
+ headerValues.remove( headerValues.indexOf( headerFieldName ) );
+ }
+ }
+ if ( numberFound == eSize ) {
+ found = true;
+ }
+ }
+ }
+ if ( !found ) {
+ /*
+ * Expected headers not found in server response.
+ * Break the processing loop.
+ */
+ break;
+ }
+ }
+ }
+
+ if ( found ) {
+ resultOut.append( "\n Unexpected header received from server: " + unExpHeader );
+ return false;
+ }
+ }
+
+
+
+ if ( responseMatch != null ) {
+ // check if we got the string we wanted
+ if ( expectResponseBody && responseBody == null ) {
+ resultOut.append( "\n ERROR: got no response, expecting " + responseMatch );
+ return false;
+ }
+ String responseBodyString = new String( responseBody );
+ if ( responseBodyString.indexOf( responseMatch ) < 0 ) {
+ resultOut.append( "\n ERROR: expecting match on " + responseMatch );
+ resultOut.append( "\n Received: \n" + responseBodyString );
+ }
+ }
+
+ if ( !expectResponseBody && responseBody != null ) {
+ resultOut.append("Received a response body from the server where none was expected" );
+ return false;
+ }
+
+ // compare the body
+ if ( goldenFile == null )
+ return true;
+
+ // Get the expected result from the "golden" file.
+ byte[] expResult = getExpectedResult();
+ String expResultS = (expResult == null) ? "" : new String(expResult);
+ // Compare the results and set the status
+ boolean cmp = true;
+
+ if ( exactMatch ) {
+ cmp = compare( responseBody, expResult );
+ } else {
+ cmp = compareWeak( responseBody, expResult );
+ }
+
+ if ( cmp != testCondition ) {
+
+ if ( resultOut != null ) {
+ expectedString = "<expectedBody>" + new String(expResult) + "</expectedBody>\n";
+ actualString = "<actualBody>" +
+ (responseBody != null ? new String(responseBody) : "null" ) +
+ "</actualBody>\n";
+ resultOut.append( expectedString );
+ resultOut.append( actualString );
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Replaces any |client.ip| and |client.host| parameter marks
+ * with the host and IP values of the host upon which Watchdog
+ * is running.
+ *
+ * @param request An HTTP request.
+ */
+ String replaceMarkers( String req, Socket socket ) {
+
+ final String CLIENT_IP = "client.ip";
+ final String CLIENT_HOME = "client.host";
+
+ if (localIP == null || localHost == null) {
+ InetAddress addr = socket.getLocalAddress();
+ localHost = addr.getHostName();
+ localIP = addr.getHostAddress();
+ }
+
+ if (req.indexOf('|') > -1) {
+ StringTokenizer tok = new StringTokenizer( request, "|" );
+ StringBuffer sb = new StringBuffer( 50 );
+
+ while ( tok.hasMoreElements() ) {
+ String token = tok.nextToken();
+ if ( token.equals( CLIENT_IP ) ) {
+ sb.append( localIP );
+ } else if ( token.equals( CLIENT_HOME ) ) {
+ sb.append( localHost );
+ } else {
+ sb.append( token );
+ }
+ }
+ return sb.toString();
+ } else {
+ return req;
+ }
+ }
+
+
+
+ /**
+ * <code>getExpectedResult</code> returns a byte array
+ * containing the content of the configured goldenfile
+ *
+ * @return goldenfile as a byte[]
+ * @exception IOException if an error occurs
+ */
+ private byte[] getExpectedResult()
+ throws IOException {
+ byte[] expResult = { 'N','O',' ',
+ 'G','O','L','D','E','N','F','I','L','E',' ',
+ 'F','O','U','N','D' };
+
+ try {
+ InputStream in = new BufferedInputStream(
+ new FileInputStream( goldenFile ) );
+ return readBody ( in );
+ } catch ( Exception ex ) {
+ System.out.println( "Golden file not found: " + goldenFile );
+ return expResult;
+ }
+ }
+
+ /**
+ * <code>compare</code> compares the two byte arrays passed
+ * in to verify that the lengths of the arrays are equal, and
+ * that the content of the two arrays, byte for byte are equal.
+ *
+ * @param fromServer a <code>byte[]</code> value
+ * @param fromGoldenFile a <code>byte[]</code> value
+ * @return <code>boolean</code> true if equal, otherwise false
+ */
+ private boolean compare( byte[] fromServer, byte[] fromGoldenFile ) {
+ if ( fromServer == null || fromGoldenFile == null ) {
+ return false;
+ }
+
+ /*
+ * Check to see that the respose and golden file lengths
+ * are equal. If they are not, dump the hex and don't
+ * bother comparing the bytes. If they are equal,
+ * iterate through the byte arrays and compare each byte.
+ * If the bytes don't match, dump the hex representation
+ * of the server response and the goldenfile and return
+ * false.
+ */
+ if ( fromServer.length != fromGoldenFile.length ) {
+ StringBuffer sb = new StringBuffer( 50 );
+ sb.append( " Response and golden files lengths do not match!\n" );
+ sb.append( " Server response length: " );
+ sb.append( fromServer.length );
+ sb.append( "\n Goldenfile length: " );
+ sb.append( fromGoldenFile.length );
+ resultOut.append( sb.toString() );
+ sb = null;
+ // dump the hex representation of the byte arrays
+ dumpHex( fromServer, fromGoldenFile );
+
+ return false;
+ } else {
+
+ int i = 0;
+ int j = 0;
+
+ while ( ( i < fromServer.length ) && ( j < fromGoldenFile.length ) ) {
+ if ( fromServer[ i ] != fromGoldenFile[ j ] ) {
+ resultOut.append( "\n Error at position " + ( i + 1 ) );
+ // dump the hex representation of the byte arrays
+ dumpHex( fromServer, fromGoldenFile );
+
+ return false;
+ }
+
+ i++;
+ j++;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * <code>compareWeak</code> creates new Strings from the passed arrays
+ * and then uses a StringTokenizer to compare non-whitespace tokens.
+ *
+ * @param fromServer a <code>byte[]</code> value
+ * @param fromGoldenFile a <code>byte[]</code> value
+ * @return a <code>boolean</code> value
+ */
+ private boolean compareWeak( byte[] fromServer, byte[] fromGoldenFile ) {
+ if ( fromServer == null || fromGoldenFile == null ) {
+ return false;
+ }
+
+ boolean status = true;
+
+ String server = new String( fromServer );
+ String golden = new String( fromGoldenFile );
+
+ StringTokenizer st1 = new StringTokenizer( server );
+
+ StringTokenizer st2 = new StringTokenizer( golden );
+
+ while ( st1.hasMoreTokens() && st2.hasMoreTokens() ) {
+ String tok1 = st1.nextToken();
+ String tok2 = st2.nextToken();
+
+ if ( !tok1.equals( tok2 ) ) {
+ resultOut.append( "\t FAIL*** : Rtok1 = " + tok1
+ + ", Etok2 = " + tok2 );
+ status = false;
+ }
+ }
+
+ if ( st1.hasMoreTokens() || st2.hasMoreTokens() ) {
+ status = false;
+ }
+
+ if ( !status ) {
+ StringBuffer sb = new StringBuffer( 255 );
+ sb.append( "ERROR: Server's response and configured goldenfile do not match!\n" );
+ sb.append( "Response received from server:\n" );
+ sb.append( "---------------------------------------------------------\n" );
+ sb.append( server );
+ sb.append( "\nContent of Goldenfile:\n" );
+ sb.append( "---------------------------------------------------------\n" );
+ sb.append( golden );
+ sb.append( "\n" );
+ resultOut.append( sb.toString() );
+ }
+ return status;
+ }
+
+ /**
+ * <code>readBody</code> reads the body of the response
+ * from the InputStream.
+ *
+ * @param input an <code>InputStream</code>
+ * @return a <code>byte[]</code> representation of the response
+ */
+ private byte[] readBody( InputStream input ) {
+ StringBuffer sb = new StringBuffer( 255 );
+ while ( true ) {
+ try {
+ int ch = input.read();
+
+ if ( ch < 0 ) {
+ if ( sb.length() == 0 ) {
+ return ( null );
+ } else {
+ break;
+ }
+ }
+ sb.append( ( char ) ch );
+
+ } catch ( IOException ex ) {
+ return null;
+ }
+ }
+ return sb.toString().getBytes();
+ }
+
+ /**
+ * <code>setHeaderDetails</code> Wrapper method for parseHeader.
+ * Allows easy addition of headers to the specified
+ * HashMap
+ *
+ * @param line a <code>String</code> value
+ * @param headerMap a <code>HashMap</code> value
+ * @param isRequest a <code>boolean</code> indicating if the passed Header
+ * HashMap is for request headers
+ */
+ void setHeaderDetails( String line, HashMap headerHash, boolean isRequest ) {
+ StringTokenizer stk = new StringTokenizer( line, "##" );
+
+ while ( stk.hasMoreElements( ) ) {
+ String presentHeader = stk.nextToken();
+ parseHeader( presentHeader, headerHash, isRequest );
+ }
+ }
+
+ /**
+ * <code>parseHeader</code> parses input headers in format of "key:value"
+ * The parsed header field-name will be used as a key in the passed
+ * HashMap object, and the values found will be stored in an ArrayList
+ * associated with the field-name key.
+ *
+ * @param line String representation of an HTTP header line.
+ * @param headers a<code>HashMap</code> to store key/value header objects.
+ * @param isRequest set to true if the headers being processed are
+ * requestHeaders.
+ */
+ void parseHeader( String line, HashMap headerMap, boolean isRequest ) {
+ // Parse the header name and value
+ int colon = line.indexOf( ":" );
+
+ if ( colon < 0 ) {
+ resultOut.append( "\n ERROR: Header is in incorrect format: " + line );
+ return ;
+ }
+
+ String name = line.substring( 0, colon ).trim();
+ String value = line.substring( colon + 1 ).trim();
+
+ if ( ( cookieVector != null ) && ( name.equalsIgnoreCase( "Set-Cookie" ) ) ) {
+ cookieVector.addElement( value );
+ /*
+ if ( ( value.indexOf("JSESSIONID") > -1 ) || (value.indexOf("jsessionid") > -1 ) )
+ {
+ String sessionId= value.substring( value.indexOf("=")+1);
+ if ( testSession != null )
+ {
+ sessionHash.put( testSession, sessionId );
+ }
+ System.out.println("Got Session-ID : " + sessionId );
+ }
+ */
+ }
+
+ // System.out.println("HEADER: " +name + " " + value);
+
+ ArrayList values = (ArrayList) headerMap.get( name );
+ if ( values == null ) {
+ values = new ArrayList();
+ }
+ // HACK
+ if ( value.indexOf( ',' ) > -1 && !isRequest && !name.equalsIgnoreCase( "Date" ) ) {
+ StringTokenizer st = new StringTokenizer( value, "," );
+ while ( st.hasMoreElements() ) {
+ values.add( st.nextToken() );
+ }
+ } else {
+ values.add( value );
+ }
+
+ headerMap.put( name, values );
+ }
+
+ /**
+ * <code>dumpHex</code> helper method to dump formatted
+ * hex output of the server response and the goldenfile.
+ *
+ * @param serverResponse a <code>byte[]</code> value
+ * @param goldenFile a <code>byte[]</code> value
+ */
+ private void dumpHex( byte[] serverResponse, byte[] goldenFile ) {
+ StringBuffer outBuf = new StringBuffer( ( serverResponse.length + goldenFile.length ) * 2 );
+
+ String fromServerString = HexUtils.getHexDump( serverResponse, 0, serverResponse.length, true );
+ String fromGoldenFileString = HexUtils.getHexDump( goldenFile, 0, goldenFile.length, true );
+
+ outBuf.append( " Hex dump of server response and goldenfile below.\n\n### RESPONSE FROM SERVER ###\n" );
+ outBuf.append( "----------------------------\n" );
+ outBuf.append( fromServerString );
+ outBuf.append( "\n\n### GOLDEN FILE ###\n" );
+ outBuf.append( "-------------------\n" );
+ outBuf.append( fromGoldenFileString );
+ outBuf.append( "\n\n### END OF DUMP ###\n" );
+
+ resultOut.append( outBuf.toString() );
+
+ }
+
+ /**
+ * <code>cloneHeaders</code> returns a "cloned"
+ * HashMap of the map passed in.
+ *
+ * @param map a <code>HashMap</code> value
+ * @return a <code>HashMap</code> value
+ */
+ private HashMap cloneHeaders( HashMap map ) {
+ HashMap dupMap = new HashMap();
+ Iterator iter = map.keySet().iterator();
+
+ while ( iter.hasNext() ) {
+ String key = new String( (String) iter.next() );
+ ArrayList origValues = (ArrayList) map.get( key );
+ ArrayList dupValues = new ArrayList();
+
+ String[] dupVal = (String[]) origValues.toArray( new String[ origValues.size() ] );
+ for ( int i = 0; i < dupVal.length; i++ ) {
+ dupValues.add( new String( dupVal[ i ] ) );
+ }
+
+ dupMap.put( key, dupValues );
+ }
+ return dupMap;
+ }
+
+}
--- /dev/null
+/*
+ * 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.tomcat.test.watchdog;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Date;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * An object which represents an HTTP cookie. Can be constructed by
+ * parsing a string from the set-cookie: header.
+ *
+ * Syntax: Set-Cookie: NAME=VALUE; expires=DATE;
+ * path=PATH; domain=DOMAIN_NAME; secure
+ *
+ * All but the first field are optional.
+ *
+ * @author Ramesh.Mandava
+ */
+
+public class HttpCookie {
+ private Date expirationDate = null;
+ private String nameAndValue;
+ private String path;
+ private String domain;
+ private boolean isSecure = false;
+ private static boolean defaultSet = true;
+ private static long defExprTime = 100;
+
+ public HttpCookie(String cookieString) {
+ /*
+ System.out.println("Calling default expiration :");
+ getDefaultExpiration();
+ */
+ parseCookieString(cookieString);
+ }
+
+ //
+ // Constructor for use by the bean
+ //
+ public HttpCookie(Date expirationDate,
+ String nameAndValue,
+ String path,
+ String domain,
+ boolean isSecure) {
+ this.expirationDate = expirationDate;
+ this.nameAndValue = nameAndValue;
+ this.path = path;
+ this.domain = domain;
+ this.isSecure = isSecure;
+ }
+
+ public HttpCookie(URL url, String cookieString) {
+ parseCookieString(cookieString);
+ applyDefaults(url);
+ }
+
+ /**
+ * Fills in default values for domain, path, etc. from the URL
+ * after creation of the cookie.
+ */
+ private void applyDefaults(URL url) {
+ if (domain == null) {
+ domain = url.getHost()+":"+((url.getPort() == -1) ? 80 : url.getPort());
+ }
+
+ if (path == null) {
+ path = url.getFile();
+
+ // larrylf: The documentation for cookies say that the path is
+ // by default, the path of the document, not the filename of the
+ // document. This could be read as not including that document
+ // name itself, just its path (this is how NetScape intrprets it)
+ // so amputate the document name!
+ int last = path.lastIndexOf("/");
+ if( last > -1 ) {
+ path = path.substring(0, last);
+ }
+ }
+ }
+
+
+ /**
+ * Parse the given string into its individual components, recording them
+ * in the member variables of this object.
+ */
+ private void parseCookieString(String cookieString) {
+ StringTokenizer tokens = new StringTokenizer(cookieString, ";");
+
+ if (!tokens.hasMoreTokens()) {
+ // REMIND: make this robust against parse errors
+ nameAndValue="=";
+ return;
+ }
+
+ nameAndValue = tokens.nextToken().trim();
+
+ while (tokens.hasMoreTokens()) {
+ String token = tokens.nextToken().trim();
+
+ if (token.equalsIgnoreCase("secure")) {
+ isSecure = true;
+ } else {
+ int equIndex = token.indexOf("=");
+
+ if (equIndex < 0) {
+ continue;
+ // REMIND: malformed cookie
+ }
+
+ String attr = token.substring(0, equIndex);
+ String val = token.substring(equIndex+1);
+
+ if (attr.equalsIgnoreCase("path")) {
+ path = val;
+ } else if (attr.equalsIgnoreCase("domain")) {
+ if( val.indexOf(".") == 0 ) {
+ // spec seems to allow for setting the domain in
+ // the form 'domain=.eng.sun.com'. We want to
+ // trim off the leading '.' so we can allow for
+ // both leading dot and non leading dot forms
+ // without duplicate storage.
+ domain = val.substring(1);
+ } else {
+ domain = val;
+ }
+ } else if (attr.equalsIgnoreCase("expires")) {
+ expirationDate = parseExpireDate(val);
+ } else {
+ // unknown attribute -- do nothing
+ }
+ }
+ }
+
+ // commented the following out, b/c ok to have no expirationDate
+ // that means that the cookie should last only for that particular
+ // session.
+ // if (expirationDate == null) {
+ // expirationDate = getDefaultExpiration();
+ // }
+ }
+
+ /* Returns the default expiration, which is the current time + default
+ expiration as specified in the properties file.
+ This uses reflection to get at the properties file, since Globals is
+ not in the utils/ directory
+ */
+ private Date getDefaultExpiration() {
+ if (defaultSet == false) {
+ Properties props = new Properties();
+
+ try {
+ FileInputStream fin = new FileInputStream("ServerAutoRun.properties");
+ props.load( fin );
+
+ System.out.println("Got properties from ServerAutoRun.properties");
+ props.list(System.out);
+
+ } catch (IOException ex) {
+ System.out.println("HttpCookie getDefaultExpiration : ServerAutoRun.properties not found!" + ex);
+ }
+
+ // defExprTime = props.getProperty("cookies.default.expiration");
+ defExprTime = Long.parseLong( props.getProperty("cookies.default.expiration") );
+
+ }
+ defaultSet = true;
+
+ return (new Date(System.currentTimeMillis() + defExprTime));
+
+ }
+
+ //======================================================================
+ //
+ // Accessor functions
+ //
+
+
+
+ public String getNameValue() {
+ return nameAndValue;
+ }
+
+ /**
+ * Returns just the name part of the cookie
+ */
+ public String getName() {
+
+ // it probably can't have null value, but doesn't hurt much
+ // to check.
+ if (nameAndValue == null) {
+ return "=";
+ }
+ int index = nameAndValue.indexOf("=");
+ return (index < 0) ? "=" : nameAndValue.substring(0, index);
+ }
+
+
+ /**
+ * Returns the domain of the cookie as it was presented
+ */
+ public String getDomain() {
+ // REMIND: add port here if appropriate
+ return domain;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public Date getExpirationDate() {
+ return expirationDate;
+ }
+
+ public boolean hasExpired() {
+ if(expirationDate == null) {
+ return false;
+ }
+ return (expirationDate.getTime() <= System.currentTimeMillis());
+ }
+
+ /**
+ * Returns true if the cookie has an expiration date (meaning it's
+ * persistent), and if the date nas not expired;
+ */
+ public boolean isSaveable() {
+ return (expirationDate != null)
+ && (expirationDate.getTime() > System.currentTimeMillis());
+ }
+
+ public boolean isSaveableInMemory() {
+ return ((expirationDate == null) ||
+ (expirationDate != null && expirationDate.getTime() > System.currentTimeMillis()));
+ }
+
+ public boolean isSecure() {
+ return isSecure;
+ }
+
+ private Date parseExpireDate(String dateString) {
+ // format is wdy, DD-Mon-yyyy HH:mm:ss GMT
+ RfcDateParser parser = new RfcDateParser(dateString);
+ Date theDate = parser.getDate();
+ if (theDate == null) {
+ // Expire in some intelligent default time
+ theDate = getDefaultExpiration();
+ }
+ return theDate;
+ }
+
+ public String toString() {
+
+ String result = (nameAndValue == null) ? "=" : nameAndValue;
+ if (expirationDate != null) {
+ result += "; expires=" + expirationDate;
+ }
+
+ if (path != null) {
+ result += "; path=" + path;
+ }
+
+ if (domain != null) {
+ result += "; domain=" + domain;
+ }
+
+ if (isSecure) {
+ result += "; secure";
+ }
+
+ return result;
+ }
+}
--- /dev/null
+/*
+ * 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.tomcat.test.watchdog;
+
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * A parser for date strings commonly found in http and email headers that
+ * follow various RFC conventions. Given a date-string, the parser will
+ * attempt to parse it by trying matches with a set of patterns, returning
+ * null on failure, a Date object on success.
+ *
+ * @author Ramesh.Mandava
+ */
+public class RfcDateParser {
+
+ private boolean isGMT = false;
+
+ static final String[] standardFormats = {
+ "EEEE', 'dd-MMM-yy HH:mm:ss z", // RFC 850 (obsoleted by 1036)
+ "EEEE', 'dd-MMM-yy HH:mm:ss", // ditto but no tz. Happens too often
+ "EEE', 'dd-MMM-yyyy HH:mm:ss z", // RFC 822/1123
+ "EEE', 'dd MMM yyyy HH:mm:ss z", // REMIND what rfc? Apache/1.1
+ "EEEE', 'dd MMM yyyy HH:mm:ss z", // REMIND what rfc? Apache/1.1
+ "EEE', 'dd MMM yyyy hh:mm:ss z", // REMIND what rfc? Apache/1.1
+ "EEEE', 'dd MMM yyyy hh:mm:ss z", // REMIND what rfc? Apache/1.1
+ "EEE MMM dd HH:mm:ss z yyyy", // Date's string output format
+ "EEE MMM dd HH:mm:ss yyyy", // ANSI C asctime format()
+ "EEE', 'dd-MMM-yy HH:mm:ss", // No time zone 2 digit year RFC 1123
+ "EEE', 'dd-MMM-yyyy HH:mm:ss" // No time zone RFC 822/1123
+ };
+
+ /* because there are problems with JDK1.1.6/SimpleDateFormat with
+ * recognizing GMT, we have to create this workaround with the following
+ * hardcoded strings */
+ static final String[] gmtStandardFormats = {
+ "EEEE',' dd-MMM-yy HH:mm:ss 'GMT'", // RFC 850 (obsoleted by 1036)
+ "EEE',' dd-MMM-yyyy HH:mm:ss 'GMT'", // RFC 822/1123
+ "EEE',' dd MMM yyyy HH:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
+ "EEEE',' dd MMM yyyy HH:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
+ "EEE',' dd MMM yyyy hh:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
+ "EEEE',' dd MMM yyyy hh:mm:ss 'GMT'", // REMIND what rfc? Apache/1.1
+ "EEE MMM dd HH:mm:ss 'GMT' yyyy" // Date's string output format
+ };
+
+ String dateString;
+
+ public RfcDateParser(String dateString) {
+ this.dateString = dateString.trim();
+ if (this.dateString.indexOf("GMT") != -1) {
+ isGMT = true;
+ }
+ }
+
+ public Date getDate() {
+
+ int arrayLen = isGMT ? gmtStandardFormats.length : standardFormats.length;
+ for (int i = 0; i < arrayLen; i++) {
+ Date d = null;
+
+ if (isGMT) {
+ d = tryParsing(gmtStandardFormats[i]);
+ } else {
+ d = tryParsing(standardFormats[i]);
+ }
+ if (d != null) {
+ return d;
+ }
+
+ }
+
+ return null;
+ }
+
+ private Date tryParsing(String format) {
+
+ java.text.SimpleDateFormat df = new java.text.SimpleDateFormat(format, Locale.US);
+ if (isGMT) {
+ df.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+ try {
+ return df.parse(dateString);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+} /* class RfcDateParser */
--- /dev/null
+/*
+ * 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.tomcat.test.watchdog;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.apache.tomcat.util.DomUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+public class WatchdogClient {
+
+ protected String base = "../watchdog";
+
+ protected String goldenDir;
+ protected String testMatch;
+ protected String file;
+ protected String[] exclude = null;
+ protected String[] slow =
+ {
+ "SingleModelTest" // slow
+ };
+
+ protected String targetMatch;
+
+
+ Properties props = new Properties();
+
+ protected void beforeSuite() {
+
+ }
+
+ protected void afterSuite(TestResult res) {
+
+ }
+
+ /**
+ * Return a test suite for running a watchdog-like
+ * test file.
+ *
+ * @param base base dir for the watchdog dir
+ * @param testMatch Prefix of tests to be run
+ * @return
+ */
+ public Test getSuite() {
+ TestSuite tests = new WatchdogTests();
+ tests.setName(this.getClass().getSimpleName());
+
+ props.setProperty("port", "8080");
+ props.setProperty("host", "localhost");
+ props.setProperty("wgdir",
+ goldenDir);
+
+
+ try {
+ Document doc = DomUtil.readXml(new FileInputStream(file));
+ Element docE = doc.getDocumentElement();
+ NodeList targetsL = docE.getElementsByTagName("target");
+ for (int i = 0; i < targetsL.getLength(); i++) {
+ Element target = (Element) targetsL.item(i);
+ String targetName = target.getAttribute("name");
+ if (targetMatch != null && !targetName.equals(targetMatch)) {
+ continue;
+ }
+
+ // Tests are duplicated
+ //TestSuite targetSuite = new TestSuite(targetName);
+
+ NodeList watchDogL = target.getElementsByTagName("watchdog");
+ for (int j = 0; j < watchDogL.getLength(); j++) {
+ Element watchE = (Element) watchDogL.item(j);
+ String testName = watchE.getAttribute("testName");
+ if (testMatch != null) {
+ if (!testName.startsWith(testMatch)) {
+ continue;
+ }
+ }
+ if (exclude != null) {
+ boolean found = false;
+ for (String e: exclude) {
+ if (e.equals(testName)) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ continue;
+ }
+ }
+ testName = testName + ";" + this.getClass().getName();
+ WatchdogTest test = new WatchdogTest(watchE, props, testName);
+ tests.addTest(test);
+ }
+
+ // if (targetSuite.countTestCases() > 0) {
+ // tests.addTest(targetSuite);
+ // }
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (SAXException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (ParserConfigurationException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return tests;
+ }
+
+ // --------- Inner classes -------------
+
+ public class WatchdogTests extends TestSuite {
+ public void run(TestResult res) {
+ beforeSuite();
+ super.run(res);
+ afterSuite(res);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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.tomcat.test.watchdog;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Vector;
+
+
+
+public class WatchdogHttpClient {
+ private static final String CRLF = "\r\n";
+ private static final int LINE_FEED = 10;
+
+ static int debug = 0;
+
+ public static void dispatch(GTest client) throws Exception {
+ HashMap requestHeaders = client.requestHeaders;
+ String host = client.host;
+ int port = client.port;
+ String content = client.content;
+ String request = client.request;
+
+ // XXX headers are ignored
+ Socket socket = new Socket( host, port );
+
+ //socket obtained, rebuild the request.
+ rebuildRequest(client, client.request, socket);
+
+ InputStream in = new CRBufferedInputStream( socket.getInputStream() );
+
+ // Write the request
+ socket.setSoLinger( true, 1000 );
+
+ OutputStream out = new BufferedOutputStream(
+ socket.getOutputStream() );
+ StringBuffer reqbuf = new StringBuffer( 128 );
+
+ // set the Host header
+ client.setHeaderDetails( "Host:" + host + ":" + port, requestHeaders, true );
+
+ // set the Content-Length header
+ if ( content != null ) {
+ client.setHeaderDetails( "Content-Length:" + content.length(),
+ requestHeaders, true );
+ }
+
+ // set the Cookie header
+ if ( client.testSession != null ) {
+ client.cookieController = ( CookieController ) client.sessionHash.get( client.testSession );
+
+ if ( client.cookieController != null ) {
+
+ String releventCookieString = client.cookieController.applyRelevantCookies( client.requestURL );
+
+ if ( ( releventCookieString != null ) && ( !releventCookieString.trim().equals( "" ) ) ) {
+ client.setHeaderDetails( "Cookie:" + releventCookieString, requestHeaders, true );
+ }
+ }
+ }
+
+ if ( debug > 0 ) {
+ System.out.println( " REQUEST: " + request );
+ }
+ reqbuf.append( client.request ).append( CRLF );
+
+ // append all request headers
+ if ( !requestHeaders.isEmpty() ) {
+ Iterator iter = requestHeaders.keySet().iterator();
+
+ while ( iter.hasNext() ) {
+ StringBuffer tmpBuf = new StringBuffer(32);
+ String headerKey = ( String ) iter.next();
+ ArrayList values = (ArrayList) requestHeaders.get( headerKey );
+ String[] value = (String[]) values.toArray( new String[ values.size() ] );
+ tmpBuf.append( headerKey ).append(": ");
+ for ( int i = 0; i < value.length; i++ ) {
+ if ((i + 1) == value.length) {
+ tmpBuf.append( value[ i ] );
+ } else {
+ tmpBuf.append( value[ i ] ).append(", ");
+ }
+ }
+ if ( debug > 0 ) {
+ System.out.println( " REQUEST HEADER: " + tmpBuf.toString());
+ }
+ tmpBuf.append( CRLF );
+ reqbuf.append(tmpBuf.toString());
+ }
+ }
+
+ /*
+
+ if ( ( testSession != null ) && ( sessionHash.get( testSession ) != null ) ) {
+ System.out.println("Sending Session Id : " + (String)sessionHash.get( testSession ) );
+ pw.println("JSESSIONID:" + (String)sessionHash.get( testSession) );
+ }
+
+ */
+
+ if ( request.indexOf( "HTTP/1." ) > -1 ) {
+ reqbuf.append( "" ).append( CRLF );
+ }
+
+ // append request content
+ if ( content != null ) {
+ reqbuf.append( content );
+ // XXX no CRLF at the end -see HTTP specs!
+ }
+
+ byte[] reqbytes = reqbuf.toString().getBytes();
+
+ try {
+ // write the request
+ out.write( reqbytes, 0, reqbytes.length );
+ out.flush();
+ reqbuf = null;
+ } catch ( Exception ex1 ) {
+ System.out.println( " Error writing request " + ex1 );
+ if ( debug > 0 ) {
+ System.out.println( "Message: " + ex1.getMessage() );
+ ex1.printStackTrace();
+ }
+ }
+
+ // read the response
+ try {
+
+ client.responseLine = read( in );
+
+ if ( debug > 0 ) {
+ System.out.println( " RESPONSE STATUS-LINE: " + client.responseLine );
+ }
+
+ client.headers = parseHeaders( client, in );
+
+ byte[] result = readBody( in );
+
+ if ( result != null ) {
+ client.responseBody = result;
+ if ( debug > 0 ) {
+ System.out.println( " RESPONSE BODY:\n" + new String( client.responseBody ) );
+ }
+ }
+
+ } catch ( SocketException ex ) {
+ System.out.println( " Socket Exception: " + ex );
+ ex.printStackTrace();
+ } finally {
+ if ( debug > 0 ) {
+ System.out.println( " closing socket" );
+ }
+ socket.close();
+ socket = null;
+ }
+
+ }
+
+ /**
+ * <code>readBody</code> reads the body of the response
+ * from the InputStream.
+ *
+ * @param input an <code>InputStream</code>
+ * @return a <code>byte[]</code> representation of the response
+ */
+ private static byte[] readBody( InputStream input ) {
+ StringBuffer sb = new StringBuffer( 255 );
+ while ( true ) {
+ try {
+ int ch = input.read();
+
+ if ( ch < 0 ) {
+ if ( sb.length() == 0 ) {
+ return ( null );
+ } else {
+ break;
+ }
+ }
+ sb.append( ( char ) ch );
+
+ } catch ( IOException ex ) {
+ return null;
+ }
+ }
+ return sb.toString().getBytes();
+ }
+
+
+
+ /**
+ * Read a line from the specified servlet input stream, and strip off
+ * the trailing carriage return and newline (if any). Return the remaining
+ * characters that were read as a string.7
+ *
+ * @returns The line that was read, or <code>null</code> if end of file
+ * was encountered
+ *
+ * @exception IOException if an input/output error occurred
+ */
+ private static String read( InputStream input ) throws IOException {
+ // Read the next line from the input stream
+ StringBuffer sb = new StringBuffer();
+
+ while ( true ) {
+ try {
+ int ch = input.read();
+ // System.out.println("XXX " + (char)ch );
+ if ( ch < 0 ) {
+ if ( sb.length() == 0 ) {
+ if ( debug > 0 )
+ System.out.println( " Error reading line " + ch + " " + sb.toString() );
+ return "";
+ } else {
+ break;
+ }
+ } else if ( ch == LINE_FEED ) {
+ break;
+ }
+
+ sb.append( ( char ) ch );
+ } catch ( IOException ex ) {
+ System.out.println( " Error reading : " + ex );
+ debug = 1;
+
+ if ( debug > 0 ) {
+ System.out.println( "Partial read: " + sb.toString() );
+ ex.printStackTrace();
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+
+ // ==================== Code from JSERV !!! ====================
+ /**
+ * Parse the incoming HTTP request headers, and set the corresponding
+ * request properties.
+ *
+ *
+ * @exception IOException if an input/output error occurs
+ */
+ private static HashMap parseHeaders( GTest client, InputStream is ) throws IOException {
+ HashMap headers = new HashMap();
+ client.cookieVector = new Vector();
+
+ while ( true ) {
+ // Read the next header line
+ String line = read( is );
+
+ if ( ( line == null ) || ( line.length() < 1 ) ) {
+ break;
+ }
+
+ client.parseHeader( line, headers, false );
+
+ if ( debug > 0 ) {
+ System.out.println( " RESPONSE HEADER: " + line );
+ }
+
+ }
+
+ if ( client.testSession != null ) {
+ client.cookieController = ( CookieController ) client.sessionHash.get( client.testSession );
+
+ if ( client.cookieController != null ) {
+ client.cookieController.recordAnyCookies( client.cookieVector, client.requestURL );
+ }
+ }
+
+ return headers;
+ }
+
+
+
+ /**
+ * Private utility method to 'massage' a request string that
+ * may or may not have replacement markers for the request parameters.
+ *
+ * @param req the request to manipulate
+ * @param socket local socket. Used to rebuild specified query strings.
+ *
+ * @exception Exception if an error occurs
+ */
+ private static void rebuildRequest(GTest client, String req, Socket socket) throws Exception {
+ client.request = client.replaceMarkers(req, socket );
+ String addressString = client.request.substring( client.request.indexOf( "/" ), client.request.indexOf( "HTTP" ) ).trim();
+
+ if ( addressString.indexOf( "?" ) > -1 ) {
+ addressString = addressString.substring( 0, addressString.indexOf( "?" ) ) ;
+ }
+
+ client.requestURL = new URL( "http", client.host, client.port, addressString );
+ }
+
+
+
+ /**
+ * <code>CRBufferedInputStream</code> is a modified version of
+ * the java.io.BufferedInputStream class. The fill code is
+ * the same, but the read is modified in that if a carriage return
+ * is found in the response stream from the target server,
+ * it will skip that byte and return the next in the stream.
+ */
+ private static class CRBufferedInputStream extends BufferedInputStream {
+ private static final int CARRIAGE_RETURN = 13;
+
+ private static final int DEFAULT_BUFFER = 2048;
+
+ /**
+ * Creates a new <code>CRBufferedInputStream</code> instance.
+ *
+ * @param in an <code>InputStream</code> value
+ */
+ public CRBufferedInputStream( InputStream in ) {
+ super( in, DEFAULT_BUFFER );
+ }
+
+ /**
+ * <code>read</code> reads a single byte value per call.
+ * If, the byte read, is a carriage return, the next byte
+ * in the stream in returned instead.
+ *
+ * @return an <code>int</code> value
+ * @exception IOException if an error occurs
+ */
+ public int read() throws IOException {
+ if ( in == null ) {
+ throw new IOException ( "Stream closed" );
+ }
+ if ( pos >= count ) {
+ fill();
+ if ( pos >= count ) {
+ return -1;
+ }
+ }
+ int val = buf[pos++] & 0xff;
+ if ( val == CARRIAGE_RETURN ) {
+ if (pos >= count) {
+ fill();
+ if (pos >= count) {
+ return -1;
+ }
+ }
+ return buf[pos++] & 0xff;
+ }
+ return val;
+ }
+
+ /**
+ * <code>fill</code> is used to fill the internal
+ * buffer used by this BufferedInputStream class.
+ *
+ * @exception IOException if an error occurs
+ */
+ private void fill() throws IOException {
+ if (markpos < 0)
+ pos = 0; /* no mark: throw away the buffer */
+ else if (pos >= buf.length) /* no room left in buffer */
+ if (markpos > 0) { /* can throw away early part of the buffer */
+ int sz = pos - markpos;
+ System.arraycopy(buf, markpos, buf, 0, sz);
+ pos = sz;
+ markpos = 0;
+ } else if (buf.length >= marklimit) {
+ markpos = -1; /* buffer got too big, invalidate mark */
+ pos = 0; /* drop buffer contents */
+ } else { /* grow buffer */
+ int nsz = pos * 2;
+ if (nsz > marklimit)
+ nsz = marklimit;
+ byte nbuf[] = new byte[nsz];
+ System.arraycopy(buf, 0, nbuf, 0, pos);
+ buf = nbuf;
+ }
+ count = pos;
+ int n = in.read(buf, pos, buf.length - pos);
+ if (n > 0)
+ count = n + pos;
+ }
+ }
+
+}
--- /dev/null
+/*
+ */
+package org.apache.tomcat.test.watchdog;
+
+import java.util.Properties;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.apache.tomcat.util.IntrospectionUtils;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+public class WatchdogTest extends TestCase {
+ String testName;
+
+ Element watchE;
+
+ private Properties props;
+
+ private WatchdogTest delegate;
+ private WatchdogClient wc;
+
+ public WatchdogTest(String s) throws Throwable {
+ String[] comp = s.split(";");
+ Class c = Class.forName(comp[1]);
+ wc = (WatchdogClient) c.newInstance();
+ TestSuite suite = (TestSuite) wc.getSuite();
+ // need to encode the base, file, etc in the test name
+
+ System.err.println(s);
+
+ for (int i = 0; i < suite.testCount(); i++) {
+ WatchdogTest t = (WatchdogTest) suite.testAt(i);
+ if (s.equals(t.getName())) {
+ delegate = t;
+ return;
+ }
+ }
+ }
+
+ public WatchdogTest(Element watchE, Properties props, String testName) {
+ this.testName = testName;
+ this.watchE = watchE;
+ this.props = props;
+ }
+
+ public int countTestCases() {
+ return 1;
+ }
+
+ public String getName() {
+ return testName;
+ }
+
+ public void run(TestResult res) {
+ if (delegate != null) {
+ // Single method run
+ wc.beforeSuite();
+ delegate.run(res);
+ wc.afterSuite(res);
+ return;
+ }
+ GTest test = new GTest();
+ NamedNodeMap attrs = watchE.getAttributes();
+
+ for (int i = 0; i < attrs.getLength(); i++) {
+ Node n = attrs.item(i);
+ String name = n.getNodeName();
+ String value = n.getNodeValue();
+ value = IntrospectionUtils.replaceProperties(value, props, null);
+ try {
+ IntrospectionUtils.setProperty(test, name, value);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ try {
+ res.startTest(this);
+ IntrospectionUtils.execute(test, "execute");
+ } catch (Throwable e) {
+ res.addError(this, e);
+ // res.stop();
+ }
+
+ if (test.passCount == 1) {
+ res.endTest(this);
+ return;
+ } else {
+ if (test.lastError == null) {
+ res.addFailure(this, new AssertionFailedError(test.request
+ + " " + test.description + "\n" + test.resultOut));
+ } else {
+ res.addError(this, test.lastError);
+ }
+ }
+ res.endTest(this);
+ }
+
+}
--- /dev/null
+/*
+ */
+package org.apache.tomcat.util.buf;
+
+import junit.framework.TestCase;
+
+public class UEncoderTest extends TestCase {
+ UEncoder enc=new UEncoder();
+
+ /*
+ *
+ * Test method for 'org.apache.tomcat.util.buf.UEncoder.encodeURL(String)'
+ * TODO: find the relevant rfc and apache tests and add more
+ */
+ public void testEncodeURL() {
+
+ String eurl1=enc.encodeURL("test");
+ assertEquals("test", eurl1);
+
+ eurl1=enc.encodeURL("/test");
+ assertEquals("%2ftest", eurl1);
+
+ // safe ranges
+ eurl1=enc.encodeURL("test$-_.");
+ assertEquals("test$-_.", eurl1);
+
+ eurl1=enc.encodeURL("test$-_.!*'(),");
+ assertEquals("test$-_.!*'(),", eurl1);
+
+ eurl1=enc.encodeURL("//test");
+ assertEquals("%2f%2ftest", eurl1);
+
+
+ }
+
+}