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- & Post-Processors</h3>