From 21a90ec85e3b2091de7b980cc7ad626a0b0ee05e Mon Sep 17 00:00:00 2001
From: Felix Schumacher <felix.schumacher@internetallee.de>
Date: Wed, 23 Dec 2020 10:45:44 +0100
Subject: [PATCH] bug65024-curl-stuff-part-two

---
 src/protocol/build.gradle.kts                 |  8 ++++++
 .../protocol/http/curl/BasicCurlParser.java   | 25 ++++++++++---------
 .../gui/action/ParseCurlCommandAction.java    | 19 +++++++++-----
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/src/protocol/build.gradle.kts b/src/protocol/build.gradle.kts
index d0855cff4a..263eec8df1 100644
--- a/src/protocol/build.gradle.kts
+++ b/src/protocol/build.gradle.kts
@@ -89,6 +89,14 @@ project("http") {
         implementation("com.fasterxml.jackson.core:jackson-databind")
         testImplementation(testFixtures(project(":src:testkit-wiremock")))
         testImplementation("com.github.tomakehurst:wiremock-jre8")
+        // For some reason JMeter bundles just tika-core and tika-parsers without transitive
+        // dependencies. So we exclude those
+        implementation("org.apache.tika:tika-core") {
+            isTransitive = false
+        }
+        runtimeOnly("org.apache.tika:tika-parsers") {
+            isTransitive = false
+        }
     }
 }
 
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/curl/BasicCurlParser.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/curl/BasicCurlParser.java
index 9471130243..4a78a5d29b 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/curl/BasicCurlParser.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/curl/BasicCurlParser.java
@@ -43,6 +43,7 @@ import org.apache.commons.cli.avalon.CLArgsParser;
 import org.apache.commons.cli.avalon.CLOption;
 import org.apache.commons.cli.avalon.CLOptionDescriptor;
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.jmeter.protocol.http.control.AuthManager.Mechanism;
 import org.apache.jmeter.protocol.http.control.Authorization;
 import org.apache.jmeter.protocol.http.control.Cookie;
@@ -123,7 +124,7 @@ public class BasicCurlParser {
     public static final class Request {
         private boolean compressed;
         private String url;
-        private Map<String, String> headers = new LinkedHashMap<>();
+        private List<Pair<String, String>> headers = new ArrayList<>();
         private String method = "GET";
         private String postData;
         private String interfaceName;
@@ -133,8 +134,8 @@ public class BasicCurlParser {
         private String filepathCookie="";
         private Authorization authorization = new Authorization();
         private String caCert = "";
-        private Map<String, String> formData = new LinkedHashMap<>();
-        private Map<String, String> formStringData = new LinkedHashMap<>();
+        private List<Pair<String, String>> formData = new ArrayList<>();
+        private List<Pair<String, String>> formStringData = new ArrayList<>();
         private Set<String> dnsServers = new HashSet<>();
         private boolean isKeepAlive = true;
         private double maxTime = -1;
@@ -201,7 +202,7 @@ public class BasicCurlParser {
             if ("COOKIE".equalsIgnoreCase(name)) {
                 this.cookieInHeaders = value;
             } else if (!HEADERS_TO_IGNORE.contains(name)) {
-                headers.put(name, value);
+                headers.add(Pair.of(name, value));
             }
         }
 
@@ -239,8 +240,8 @@ public class BasicCurlParser {
         /**
          * @return the headers
          */
-        public Map<String, String> getHeaders() {
-            return Collections.unmodifiableMap(this.headers);
+        public List<Pair<String, String>> getHeaders() {
+            return Collections.unmodifiableList(this.headers);
         }
 
         /**
@@ -406,8 +407,8 @@ public class BasicCurlParser {
         /**
          * @return the map of form data
          */
-        public Map<String, String> getFormStringData() {
-            return Collections.unmodifiableMap(this.formStringData);
+        public List<Pair<String,String>> getFormStringData() {
+            return Collections.unmodifiableList(this.formStringData);
         }
 
         /**
@@ -415,14 +416,14 @@ public class BasicCurlParser {
          * @param value the value of form data
          */
         public void addFormStringData(String key, String value) {
-            formStringData.put(key, value);
+            formStringData.add(Pair.of(key, value));
         }
 
         /**
          * @return the map of form data
          */
-        public Map<String, String> getFormData() {
-            return Collections.unmodifiableMap(this.formData);
+        public List<Pair<String,String>> getFormData() {
+            return Collections.unmodifiableList(this.formData);
         }
 
         /**
@@ -430,7 +431,7 @@ public class BasicCurlParser {
          * @param value the value of form data
          */
         public void addFormData(String key, String value) {
-            formData.put(key, value);
+            formData.add(Pair.of(key, value));
         }
 
         /**
diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java
index e03ae17066..2a310cfb70 100644
--- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java
+++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java
@@ -38,7 +38,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import javax.activation.MimetypesFileTypeMap;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
 import javax.swing.JLabel;
@@ -52,6 +51,7 @@ import javax.swing.tree.TreePath;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.jmeter.config.Arguments;
 import org.apache.jmeter.config.KeystoreConfig;
 import org.apache.jmeter.control.Controller;
@@ -101,6 +101,7 @@ import org.apache.jmeter.visualizers.ViewResultsFullVisualizer;
 import org.apache.jorphan.collections.HashTree;
 import org.apache.jorphan.gui.ComponentUtil;
 import org.apache.jorphan.gui.JMeterUIDefaults;
+import org.apache.tika.Tika;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -118,6 +119,7 @@ public class ParseCurlCommandAction extends AbstractAction implements MenuCreato
     private static final String CREATE_REQUEST = "CREATE_REQUEST";
     private static final String TYPE_FORM = ";type=";
     private static final String CERT = "cert";
+    private Logger log = LoggerFactory.getLogger(getClass());
     /** A panel allowing results to be saved. */
     private FilePanel filePanel = null;
     static {
@@ -126,6 +128,7 @@ public class ParseCurlCommandAction extends AbstractAction implements MenuCreato
     private JSyntaxTextArea cURLCommandTA;
     private JLabel statusText;
     private JCheckBox uploadCookiesCheckBox;
+    private final Tika tika = new Tika();
     public ParseCurlCommandAction() {
         super();
     }
@@ -344,9 +347,8 @@ public class ParseCurlCommandAction extends AbstractAction implements MenuCreato
         headerManager.setProperty(TestElement.GUI_CLASS, HeaderPanel.class.getName());
         headerManager.setProperty(TestElement.NAME, "HTTP HeaderManager");
         headerManager.setProperty(TestElement.COMMENTS, getDefaultComment());
-        Map<String, String> map = request.getHeaders();
         boolean hasAcceptEncoding = false;
-        for (Map.Entry<String, String> header : map.entrySet()) {
+        for (Pair<String, String> header : request.getHeaders()) {
             String key = header.getKey();
             hasAcceptEncoding = hasAcceptEncoding || key.equalsIgnoreCase(ACCEPT_ENCODING);
             headerManager.getHeaders().addItem(new Header(key, header.getValue()));
@@ -515,12 +517,12 @@ public class ParseCurlCommandAction extends AbstractAction implements MenuCreato
             throw new IllegalArgumentException("--form and --data can't appear in the same command");
         }
         List<HTTPFileArg> httpFileArgs = new ArrayList<>();
-        for (Map.Entry<String, String> entry : request.getFormStringData().entrySet()) {
+        for (Pair<String, String> entry : request.getFormStringData()) {
             String formName = entry.getKey();
             String formValue = entry.getValue();
             httpSampler.addNonEncodedArgument(formName, formValue, "");
         }
-        for (Map.Entry<String, String> entry : request.getFormData().entrySet()) {
+        for (Pair<String, String> entry : request.getFormData()) {
             String formName = entry.getKey();
             String formValue = entry.getValue();
             boolean isContainsFile = "@".equals(formValue.substring(0, 1));
@@ -533,7 +535,12 @@ public class ParseCurlCommandAction extends AbstractAction implements MenuCreato
                     formValue = formValueWithType[0];
                     contentType = formValueWithType[1];
                 } else {
-                    contentType = new MimetypesFileTypeMap().getContentType(formValue);
+                    try {
+                        contentType = tika.detect(new File(formValue));
+                    } catch (IOException e) {
+                        log.info("Could not detect contentType for file {} by content, so falling back to detection by filename");
+                        contentType = tika.detect(formValue);
+                    }
                 }
                 httpFileArgs.add(new HTTPFileArg(formValue, formName, contentType));
             } else {
-- 
2.25.1

