This is an automated email from the ASF dual-hosted git repository.

fschumacher pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git


The following commit(s) were added to refs/heads/master by this push:
     new 33ad930  Allow unquoted tokens for values in content-disposition header
33ad930 is described below

commit 33ad9306b2fbe823fd9fd1bd207262cd7a0ec4a6
Author: Felix Schumacher <[email protected]>
AuthorDate: Wed Feb 16 21:54:44 2022 +0100

    Allow unquoted tokens for values in content-disposition header
    
    Use httpcomponents BasicHeaderValueParser instead of our simple parser, as
    we were assuming parameters would always be surrounded by quotes.
    
    Bugzilla Id: 65884
---
 .../protocol/http/config/MultipartUrlConfig.java   | 47 +++++++++----
 .../http/config/MultipartUrlConfigTest.java        | 82 +++++++++++++++++++++-
 xdocs/changes.xml                                  |  1 +
 3 files changed, 114 insertions(+), 16 deletions(-)

diff --git 
a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/MultipartUrlConfig.java
 
b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/MultipartUrlConfig.java
index e420fde..f4eeae9 100644
--- 
a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/MultipartUrlConfig.java
+++ 
b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/MultipartUrlConfig.java
@@ -20,6 +20,10 @@ package org.apache.jmeter.protocol.http.config;
 import java.io.Serializable;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HeaderElement;
+import org.apache.http.NameValuePair;
+import org.apache.http.ParseException;
+import org.apache.http.message.BasicHeaderValueParser;
 import org.apache.jmeter.config.Arguments;
 import org.apache.jmeter.protocol.http.util.HTTPArgument;
 import org.apache.jmeter.protocol.http.util.HTTPFileArgs;
@@ -28,6 +32,8 @@ import org.apache.jorphan.util.JOrphanUtils;
 import org.apache.oro.text.regex.Pattern;
 import org.apache.oro.text.regex.Perl5Compiler;
 import org.apache.oro.text.regex.Perl5Matcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Configuration element which handles HTTP Parameters and files to be uploaded
@@ -46,6 +52,8 @@ public class MultipartUrlConfig implements Serializable {
 
     private final Arguments args;
 
+    private static final Logger log = 
LoggerFactory.getLogger(MultipartUrlConfig.class);
+
     /**
      * HTTPFileArgs list to be uploaded with http request.
      */
@@ -129,20 +137,25 @@ public class MultipartUrlConfig implements Serializable {
             // Check if it is form data
             if (contentDisposition != null && 
contentDisposition.contains("form-data")) { //$NON-NLS-1$
                 // Get the form field name
-                final String namePrefix = "name=\""; //$NON-NLS-1$
-                int index = contentDisposition.indexOf(namePrefix) + 
namePrefix.length();
-                String name = contentDisposition.substring(index, 
contentDisposition.indexOf('\"', index)); //$NON-NLS-1$
-
-                // Check if it is a file being uploaded
-                final String filenamePrefix = "filename=\""; //$NON-NLS-1$
-                if (contentDisposition.contains(filenamePrefix)) {
-                    // Get the filename
-                    index = contentDisposition.indexOf(filenamePrefix) + 
filenamePrefix.length();
-                    String path = contentDisposition.substring(index, 
contentDisposition.indexOf('\"', index)); //$NON-NLS-1$
-                    if (path != null && path.length() > 0) {
-                        // Set the values retrieved for the file upload
-                        files.addHTTPFileArg(path, name, contentType);
+                HeaderElement[] headerElements = null;
+                try {
+                    headerElements = BasicHeaderValueParser.parseElements(
+                            contentDisposition,
+                            BasicHeaderValueParser.INSTANCE);
+                } catch (ParseException e) {
+                    log.info("Can't parse header {}", contentDisposition, e);
+                }
+                String name = "";
+                String path = null;
+                if (headerElements != null) {
+                    for (HeaderElement element : headerElements) {
+                        name = getParameterValue(element, "name", "");
+                        path = getParameterValue(element, "filename", null);
                     }
+                }
+                if (path != null && !path.isEmpty()) {
+                    // Set the values retrieved for the file upload
+                    files.addHTTPFileArg(path, name, contentType);
                 } else {
                     // Find the first empty line of the multipart, it signals 
end of headers for multipart
                     // Agents are supposed to terminate lines in CRLF:
@@ -160,6 +173,14 @@ public class MultipartUrlConfig implements Serializable {
         }
     }
 
+    private static String getParameterValue(HeaderElement element, String 
name, String defaultValue) {
+        NameValuePair parameter = element.getParameterByName(name);
+        if (parameter == null) {
+            return defaultValue;
+        }
+        return parameter.getValue();
+    }
+
     private static String getHeaderValue(String headerName, String multiPart) {
         String regularExpression = headerName + "\\s*:\\s*(.*)$"; //$NON-NLS-1$
         Perl5Matcher localMatcher = JMeterUtils.getMatcher();
diff --git 
a/src/protocol/http/src/test/java/org/apache/jmeter/protocol/http/config/MultipartUrlConfigTest.java
 
b/src/protocol/http/src/test/java/org/apache/jmeter/protocol/http/config/MultipartUrlConfigTest.java
index 214d3ec..e31cebe 100644
--- 
a/src/protocol/http/src/test/java/org/apache/jmeter/protocol/http/config/MultipartUrlConfigTest.java
+++ 
b/src/protocol/http/src/test/java/org/apache/jmeter/protocol/http/config/MultipartUrlConfigTest.java
@@ -19,17 +19,24 @@ package org.apache.jmeter.protocol.http.config;
 
 import static org.junit.Assert.assertEquals;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
 import org.apache.jmeter.config.Argument;
 import org.apache.jmeter.config.Arguments;
 import org.apache.jmeter.protocol.http.util.HTTPFileArg;
 import org.apache.jmeter.protocol.http.util.HTTPFileArgs;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
 
 public class MultipartUrlConfigTest {
 
     @Test
     @SuppressWarnings("deprecation")
-    public void testConstructors() {
+    void testConstructors() {
         MultipartUrlConfig muc = new MultipartUrlConfig();
         assertEquals(0, muc.getArguments().getArgumentCount());
         assertEquals(0, muc.getHTTPFileArgs().getHTTPFileArgCount());
@@ -41,7 +48,7 @@ public class MultipartUrlConfigTest {
 
     // TODO - should LF-only EOL be allowed?
     @Test
-    public void testParseArgumentsLF() {
+    void testParseArgumentsLF() {
         String queryString
             = "Content-Disposition: form-data; name=\"aa\"\n"
             + "Content-Type: text/plain; charset=ISO-8859-1\n"
@@ -90,7 +97,7 @@ public class MultipartUrlConfigTest {
     }
 
     @Test
-    public void testParseArgumentsCRLF() {
+    void testParseArgumentsCRLF() {
         String queryString
             = "Content-Disposition: form-data; name=\"aa\"\r\n"
             + "Content-Type: text/plain; charset=ISO-8859-1\r\n"
@@ -137,4 +144,73 @@ public class MultipartUrlConfigTest {
         assertEquals("abc", arg.getName());
         assertEquals("xyz  \r\nxyz  ", arg.getValue());
     }
+
+    private static Stream<org.junit.jupiter.params.provider.Arguments> 
quotedMultiPartArgs() {
+        String boundary = "7d159c1302d0y0";
+        HTTPFileArgs fileArgs = new HTTPFileArgs();
+        List<Argument> args = new ArrayList<>();
+        List<String> queryLines = new ArrayList<>();
+        int counter = 1;
+        for (boolean quoteName: Arrays.asList(true, false)) {
+            String paramName = "abc" + counter;
+            counter++;
+            String quoteStringName = quoteName ? '"' + paramName + '"' : 
paramName;
+            String value = "some value for " + paramName;
+            queryLines.add(String.format("Content-Disposition: form-data; 
name=%s", quoteStringName));
+            queryLines.add("Content-Type: text/plain; charset=ISO-8859-1");
+            queryLines.add("Content-Transfer-Encoding: 8bit");
+            queryLines.add("");
+            queryLines.add(value);
+            queryLines.add("--" + boundary);
+            args.add(new Argument(paramName, value));
+            for (boolean quoteFilename: Arrays.asList(true, false)) {
+                String filenameName = "def" + counter;
+                counter++;
+                String quoteStringFile = quoteFilename ? '"' +filenameName + 
'"' : filenameName ;
+                String content = "some value for " + paramName + " and " + 
filenameName ;
+                queryLines.add(String.format("Content-Disposition: form-data; 
name=%s; filename=%s",
+                        quoteStringName,
+                        quoteStringFile
+                        ));
+                queryLines.add("Content-Type: text/plain");
+                queryLines.add("Content-Transfer-Encoding: binary");
+                queryLines.add("");
+                queryLines.add(content);
+                queryLines.add("");
+                queryLines.add("--" + boundary);
+                fileArgs.addHTTPFileArg(filenameName, paramName, "text/plain");
+            }
+        }
+        queryLines.remove(queryLines.size()-1);
+        queryLines.add("");
+        return Stream.of(
+                org.junit.jupiter.params.provider.Arguments.of(
+                        boundary, String.join("\n", queryLines), fileArgs, 
args),
+                org.junit.jupiter.params.provider.Arguments.of(
+                        boundary, String.join("\r\n", queryLines), fileArgs, 
args));
+    }
+
+    @ParameterizedTest()
+    @MethodSource("quotedMultiPartArgs")
+    void testParseArgumentsQuotingStyle(String boundary, String queryString, 
HTTPFileArgs expectedFiles, List<Argument> expectedArgs) {
+        MultipartUrlConfig muc = new MultipartUrlConfig(boundary);
+        muc.parseArguments(queryString);
+        HTTPFileArgs files = muc.getHTTPFileArgs();
+        assertEquals(expectedFiles.getHTTPFileArgCount(), 
files.getHTTPFileArgCount());
+        for (int i=0; i<files.getHTTPFileArgCount(); i++) {
+            HTTPFileArg got = files.getHTTPFileArg(i);
+            HTTPFileArg expected = expectedFiles.getHTTPFileArg(i);
+            assertEquals(expected.getParamName(), got.getParamName());
+            assertEquals(expected.getPath(), got.getPath());
+            assertEquals(expected.getMimeType(), got.getMimeType());
+        }
+        Arguments args = muc.getArguments();
+        assertEquals(expectedArgs.size(), args.getArgumentCount());
+        for (int i=0; i<args.getArgumentCount(); i++) {
+            Argument got = args.getArgument(i);
+            Argument expected = expectedArgs.get(i);
+            assertEquals(expected.getName(), got.getName());
+            assertEquals(expected.getValue(), got.getValue());
+        }
+    }
 }
diff --git a/xdocs/changes.xml b/xdocs/changes.xml
index 44a09ce..8a3aaeb 100644
--- a/xdocs/changes.xml
+++ b/xdocs/changes.xml
@@ -230,6 +230,7 @@ however, the profile can't be updated while the test is 
running.
 <ul>
   <li><bug>64962</bug>Save CSV sub-results recursively from View Results 
Tree</li>
   <li><bug>65784</bug>No Graphs displayed in Aggregate Report/Response Time 
Graph</li>
+  <li><bug>65884</bug>GUI doesn't display response for multipart request 
<em>manually</em> encoded</li>
 </ul>
 
 <h3>Timers, Assertions, Config, Pre- &amp; Post-Processors</h3>

Reply via email to