Author: sebb
Date: Thu Jul  1 22:36:07 2010
New Revision: 959801

URL: http://svn.apache.org/viewvc?rev=959801&view=rev
Log:
Bug 49083 - collapse '/pathsegment/..' in redirect URLs

Modified:
    
jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java
    
jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/util/ConversionUtils.java
    
jakarta/jmeter/trunk/test/src/org/apache/jmeter/protocol/http/util/TestHTTPUtils.java
    jakarta/jmeter/trunk/xdocs/changes.xml
    jakarta/jmeter/trunk/xdocs/usermanual/component_reference.xml

Modified: 
jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java
URL: 
http://svn.apache.org/viewvc/jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java?rev=959801&r1=959800&r2=959801&view=diff
==============================================================================
--- 
jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java
 (original)
+++ 
jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java
 Thu Jul  1 22:36:07 2010
@@ -204,7 +204,7 @@ public abstract class HTTPSamplerBase ex
     private static final String RESPONSE_PARSERS= // list of parsers
         JMeterUtils.getProperty("HTTPResponse.parsers");//$NON-NLS-1$
 
-   static{
+    static{
         String []parsers = JOrphanUtils.split(RESPONSE_PARSERS, " " , true);// 
returns empty array for null
         for (int i=0;i<parsers.length;i++){
             final String parser = parsers[i];
@@ -231,6 +231,10 @@ public abstract class HTTPSamplerBase ex
         }
     }
 
+    // Bug 49083
+    /** Whether to remove '/pathsegment/..' from redirects; default true */
+    private static boolean REMOVESLASHDOTDOT = 
JMeterUtils.getPropDefault("httpsampler.redirect.removeslashdotdot", true);
+
     ////////////////////// Variables //////////////////////
 
     private boolean dynamicPath = false;// Set false if spaces are already 
encoded
@@ -1223,7 +1227,11 @@ public abstract class HTTPSamplerBase ex
             // Browsers seem to tolerate Location headers with spaces,
             // replacing them automatically with %20. We want to emulate
             // this behaviour.
-            String location = encodeSpaces(lastRes.getRedirectLocation());
+            String location = lastRes.getRedirectLocation(); 
+            if (REMOVESLASHDOTDOT) {
+                location = ConversionUtils.removeSlashDotDot(location);
+            }
+            location = encodeSpaces(location);
             try {
                 lastRes = 
sample(ConversionUtils.makeRelativeURL(lastRes.getURL(), location), GET, true, 
frameDepth);
             } catch (MalformedURLException e) {

Modified: 
jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/util/ConversionUtils.java
URL: 
http://svn.apache.org/viewvc/jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/util/ConversionUtils.java?rev=959801&r1=959800&r2=959801&view=diff
==============================================================================
--- 
jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/util/ConversionUtils.java
 (original)
+++ 
jakarta/jmeter/trunk/src/protocol/http/org/apache/jmeter/protocol/http/util/ConversionUtils.java
 Thu Jul  1 22:36:07 2010
@@ -21,6 +21,9 @@ package org.apache.jmeter.protocol.http.
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -35,6 +38,11 @@ public class ConversionUtils {
 
     private static final String CHARSET_EQ = "charset="; // $NON-NLS-1$
     private static final int CHARSET_EQ_LEN = CHARSET_EQ.length();
+    
+       private static final String SLASHDOTDOT = "/..";
+       private static final String DOTDOT = "..";
+       private static final String SLASH = "/";
+       private static final String COLONSLASHSLASH = "://";
 
     /**
      * Extract the encoding (charset) from the Content-Type,
@@ -85,6 +93,7 @@ public class ConversionUtils {
      */
     public static URL makeRelativeURL(URL baseURL, String location) throws 
MalformedURLException{
         URL initial = new URL(baseURL,location);
+        
         // skip expensive processing if it cannot apply
         if (!location.startsWith("../")){// $NON-NLS-1$
             return initial;
@@ -101,4 +110,116 @@ public class ConversionUtils {
         }
         return initial;
     }
+
+       /**
+        * collapses absolute or relative URLs containing '/..' converting
+        * http://host/path1/../path2 to http://host/path2 or /one/two/../three 
to
+        * /one/three
+        * 
+        * @param url
+        * @return collapsed URL
+        */
+    public static String removeSlashDotDot(String url)
+    {
+        if (url == null || (url = url.trim()).length() < 4 || 
!url.contains(SLASHDOTDOT))
+        {
+            return url;
+        }
+
+        /**
+         * http://a...@host:port/path1/path2/path3/?query#anchor
+         */
+
+        // get to 'path' part of the URL, preserving schema, auth, host if
+        // present
+
+        // find index of path start
+
+        int dotSlashSlashIndex = url.indexOf(COLONSLASHSLASH);
+        final int pathStartIndex;
+        if (dotSlashSlashIndex >= 0)
+        {
+            // absolute URL
+            pathStartIndex = url.indexOf(SLASH, dotSlashSlashIndex + 
COLONSLASHSLASH.length());
+        } else
+        {
+            // document or context-relative URL like:
+            // '/path/to'
+            // OR '../path/to'
+            // OR '/path/to/../path/'
+            pathStartIndex = 0;
+        }
+
+        // find path endIndex
+        int pathEndIndex = url.length();
+
+        int questionMarkIdx = url.indexOf('?');
+        if (questionMarkIdx > 0)
+        {
+            pathEndIndex = questionMarkIdx;
+        } else {
+            int anchorIdx = url.indexOf('#');
+            if (anchorIdx > 0)
+            {
+                pathEndIndex = anchorIdx;
+            }
+        }
+
+        // path is between idx='pathStartIndex' (inclusive) and
+        // idx='pathEndIndex' (exclusive)
+        String currentPath = url.substring(pathStartIndex, pathEndIndex);
+
+        final boolean startsWithSlash = currentPath.startsWith(SLASH);
+        final boolean endsWithSlash = currentPath.endsWith(SLASH);
+
+        StringTokenizer st = new StringTokenizer(currentPath, SLASH);
+        List<String> tokens = new ArrayList<String>();
+        while (st.hasMoreTokens())
+        {
+            tokens.add(st.nextToken());
+        }
+
+        for (int i = 0; i < tokens.size(); i++)
+        {
+            if (i < tokens.size() - 1)
+            {
+                final String thisToken = tokens.get(i);
+
+                // Verify for a ".." component at next iteration
+                if (thisToken.length() > 0 && !thisToken.equals(DOTDOT) && 
tokens.get(i + 1).equals(DOTDOT))
+                {
+                    tokens.remove(i);
+                    tokens.remove(i);
+                    i = i - 2;
+                    if (i < -1)
+                    {
+                        i = -1;
+                    }
+                }
+            }
+
+        }
+
+        StringBuilder newPath = new StringBuilder();
+        if (startsWithSlash) {
+            newPath.append(SLASH);
+        }
+        for (int i = 0; i < tokens.size(); i++)
+        {
+            newPath.append(tokens.get(i));
+
+            // append '/' if this isn't the last token or it is but the 
original
+            // path terminated w/ a '/'
+            boolean appendSlash = i < (tokens.size() - 1) ? true : 
endsWithSlash;
+            if (appendSlash)
+            {
+                newPath.append(SLASH);
+            }
+        }
+
+        // install new path
+        StringBuilder s = new StringBuilder(url);
+        s.replace(pathStartIndex, pathEndIndex, newPath.toString());
+        return s.toString();
+    }
 }

Modified: 
jakarta/jmeter/trunk/test/src/org/apache/jmeter/protocol/http/util/TestHTTPUtils.java
URL: 
http://svn.apache.org/viewvc/jakarta/jmeter/trunk/test/src/org/apache/jmeter/protocol/http/util/TestHTTPUtils.java?rev=959801&r1=959800&r2=959801&view=diff
==============================================================================
--- 
jakarta/jmeter/trunk/test/src/org/apache/jmeter/protocol/http/util/TestHTTPUtils.java
 (original)
+++ 
jakarta/jmeter/trunk/test/src/org/apache/jmeter/protocol/http/util/TestHTTPUtils.java
 Thu Jul  1 22:36:07 2010
@@ -59,4 +59,25 @@ public class TestHTTPUtils extends TestC
             assertEquals(new 
URL("http://192.168.0.1/../d";),ConversionUtils.makeRelativeURL(base,"/../d"));
             assertEquals(new 
URL("http://192.168.0.1/a/b/c/d";),ConversionUtils.makeRelativeURL(base,"./d"));
         }
+               
+               public void testRemoveSlashDotDot()
+               {
+                       assertEquals("/path/", 
ConversionUtils.removeSlashDotDot("/path/"));
+                       assertEquals("http://host/";, 
ConversionUtils.removeSlashDotDot("http://host/";));
+            assertEquals("http://host/one";, 
ConversionUtils.removeSlashDotDot("http://host/one";));
+                       assertEquals("/two", 
ConversionUtils.removeSlashDotDot("/one/../two"));
+                       assertEquals("http://host:8080/two";, 
ConversionUtils.removeSlashDotDot("http://host:8080/one/../two";));
+                       assertEquals("http://host:8080/two/";, 
ConversionUtils.removeSlashDotDot("http://host:8080/one/../two/";));
+                       assertEquals("http://u...@host:8080/two/";, 
ConversionUtils.removeSlashDotDot("http://u...@host:8080/one/../two/";));
+                       assertEquals("http://host:8080/two/?query#anchor";, 
ConversionUtils.removeSlashDotDot("http://host:8080/one/../two/?query#anchor";));
+                       assertEquals("one", 
ConversionUtils.removeSlashDotDot("one/two/.."));
+                       assertEquals("../../path", 
ConversionUtils.removeSlashDotDot("../../path"));
+                       assertEquals("/", 
ConversionUtils.removeSlashDotDot("/one/.."));
+                       assertEquals("/", 
ConversionUtils.removeSlashDotDot("/one/../"));
+                       assertEquals("/?a", 
ConversionUtils.removeSlashDotDot("/one/..?a"));
+                       assertEquals("http://host/one";, 
ConversionUtils.removeSlashDotDot("http://host/one/../one";));
+                       assertEquals("http://host/one/two";, 
ConversionUtils.removeSlashDotDot("http://host/one/two/../../one/two";));
+            assertEquals("http://host/..";, 
ConversionUtils.removeSlashDotDot("http://host/..";));
+            assertEquals("http://host/../abc";, 
ConversionUtils.removeSlashDotDot("http://host/../abc";));
+               }
 }

Modified: jakarta/jmeter/trunk/xdocs/changes.xml
URL: 
http://svn.apache.org/viewvc/jakarta/jmeter/trunk/xdocs/changes.xml?rev=959801&r1=959800&r2=959801&view=diff
==============================================================================
--- jakarta/jmeter/trunk/xdocs/changes.xml (original)
+++ jakarta/jmeter/trunk/xdocs/changes.xml Thu Jul  1 22:36:07 2010
@@ -161,6 +161,7 @@ This does not affect existing test plans
 <li>Bug 48153 - Support for Cache-Control and Expires headers</li>
 <li>Bug 47946 - Proxy should enable Grouping inside a Transaction 
Controller</li>
 <li>Bug 48300 - Allow override of IP source address for HTTP HttpClient 
requests</li>
+<li>Bug 49083 - collapse '/pathsegment/..' in redirect URLs</li>
 </ul>
 
 <h3>Other samplers</h3>

Modified: jakarta/jmeter/trunk/xdocs/usermanual/component_reference.xml
URL: 
http://svn.apache.org/viewvc/jakarta/jmeter/trunk/xdocs/usermanual/component_reference.xml?rev=959801&r1=959800&r2=959801&view=diff
==============================================================================
--- jakarta/jmeter/trunk/xdocs/usermanual/component_reference.xml (original)
+++ jakarta/jmeter/trunk/xdocs/usermanual/component_reference.xml Thu Jul  1 
22:36:07 2010
@@ -197,6 +197,11 @@ https.default.protocol=SSLv3
                Note that the HttpClient sampler may log the following 
message:<br/>
                "Redirect requested but followRedirects is disabled"<br/>
                This can be ignored.
+        <br/>
+        In versions after 2.3.4, JMeter will collapse paths of the form 
'/../segment' in
+        both absolute and relative URLs. For example http://host/one/../two => 
http://host/two.
+        If necessary, this behaviour can be suppressed by setting the JMeter 
property
+        <code>httpsampler.redirect.removeslashdotdot=false</code>
                </property>
                <property name="Use KeepAlive" required="Yes">JMeter sets the 
Connection: keep-alive header. This does not work properly with the default 
HTTP implementation, as connection re-use is not under user-control. 
                   It does work with the Jakarta httpClient 
implementation.</property>



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to