From e2a2fd8c625b789908e3bcf12c1c753753326375 Mon Sep 17 00:00:00 2001 From: pmouawad Date: Tue, 27 Nov 2018 23:53:52 +0100 Subject: [PATCH] Bug 62959 - Ability to create a Test plan from a cURL command POC contributed by https://ubikloadpack.com --- .../org/apache/jmeter/util/JMeterUtils.java | 14 +- .../protocol/http/curl/BasicCurlParser.java | 255 ++++++++++++++++++ .../gui/action/ParseCurlCommandAction.java | 229 ++++++++++++++++ .../jmeter/curl/BasicCurlParserTest.java | 98 +++++++ 4 files changed, 595 insertions(+), 1 deletion(-) create mode 100644 src/protocol/http/org/apache/jmeter/protocol/http/curl/BasicCurlParser.java create mode 100644 src/protocol/http/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java create mode 100644 test/src/org/apache/jmeter/curl/BasicCurlParserTest.java diff --git a/src/core/org/apache/jmeter/util/JMeterUtils.java b/src/core/org/apache/jmeter/util/JMeterUtils.java index 84a0d6ec667..5bb7bec78dc 100644 --- a/src/core/org/apache/jmeter/util/JMeterUtils.java +++ b/src/core/org/apache/jmeter/util/JMeterUtils.java @@ -50,7 +50,9 @@ import javax.swing.ImageIcon; import javax.swing.JOptionPane; +import javax.swing.JScrollPane; import javax.swing.JTable; +import javax.swing.JTextArea; import javax.swing.SwingUtilities; import javax.swing.UIDefaults; import javax.swing.UIManager; @@ -889,7 +891,7 @@ public static void reportErrorToUser(String errorMsg, String titleMsg, Exception } try { JOptionPane.showMessageDialog(instance.getMainFrame(), - errorMsg, + formatMessage(errorMsg), titleMsg, JOptionPane.ERROR_MESSAGE); } catch (HeadlessException e) { @@ -897,6 +899,16 @@ public static void reportErrorToUser(String errorMsg, String titleMsg, Exception } } + private static JScrollPane formatMessage(String errorMsg) { + JTextArea ta = new JTextArea(10, 50); + ta.setText(errorMsg); + ta.setWrapStyleWord(true); + ta.setLineWrap(true); + ta.setCaretPosition(0); + ta.setEditable(false); + return new JScrollPane(ta); + } + /** * Takes an array of strings and a tokenizer character, and returns a string * of all the strings concatenated with the tokenizer string in between each diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/curl/BasicCurlParser.java b/src/protocol/http/org/apache/jmeter/protocol/http/curl/BasicCurlParser.java new file mode 100644 index 00000000000..547879665bb --- /dev/null +++ b/src/protocol/http/org/apache/jmeter/protocol/http/curl/BasicCurlParser.java @@ -0,0 +1,255 @@ +/* + * 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.jmeter.protocol.http.curl; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import org.apache.commons.cli.avalon.CLArgsParser; +import org.apache.commons.cli.avalon.CLOption; +import org.apache.commons.cli.avalon.CLOptionDescriptor; + +/** + * Basic cURL command parser that handles: + * -X + * -H + * --compressed + * --data POST with Body data + * + * @since 5.1 + */ +public class BasicCurlParser { + private static final int METHOD_OPT = 'X'; + private static final int COMPRESSED_OPT = 'c';// $NON-NLS-1$ + private static final int HEADER_OPT = 'H';// $NON-NLS-1$ + private static final int DATA_OPT = 'd';// $NON-NLS-1$ + + public static final class Request { + private boolean compressed; + private String url; + private Map headers = new LinkedHashMap<>(); + private String method = "GET"; + private String postData; + /** + */ + public Request() { + super(); + } + /** + * @return the compressed + */ + public boolean isCompressed() { + return compressed; + } + /** + * @param compressed the compressed to set + */ + public void setCompressed(boolean compressed) { + this.compressed = compressed; + } + + public void addHeader(String name, String value) { + headers.put(name, value); + } + /** + * @return the url + */ + public String getUrl() { + return url; + } + /** + * @param url the url to set + */ + public void setUrl(String url) { + this.url = url; + } + /** + * @return the headers + */ + public Map getHeaders() { + return headers; + } + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Request [compressed="); + builder.append(compressed); + builder.append(", url="); + builder.append(url); + builder.append(", method="); + builder.append(method); + builder.append(", headers="); + builder.append(headers); + builder.append("]"); + return builder.toString(); + } + public String getMethod() { + return method; + } + /** + * @param method the method to set + */ + public void setMethod(String method) { + this.method = method; + } + public void setPostData(String value) { + this.postData = value; + } + /** + * @return the postData + */ + public String getPostData() { + return postData; + } + } + private static final CLOptionDescriptor D_COMPRESSED_OPT = + new CLOptionDescriptor("compressed", CLOptionDescriptor.ARGUMENT_DISALLOWED, COMPRESSED_OPT, + "Request compressed response (using deflate or gzip)"); + private static final CLOptionDescriptor D_HEADER_OPT = + new CLOptionDescriptor("header", CLOptionDescriptor.ARGUMENT_REQUIRED | CLOptionDescriptor.DUPLICATES_ALLOWED, HEADER_OPT, + "Pass custom header LINE to server"); + private static final CLOptionDescriptor D_METHOD_OPT = + new CLOptionDescriptor("command", CLOptionDescriptor.ARGUMENT_REQUIRED, METHOD_OPT, + "Pass custom header LINE to server"); + private static final CLOptionDescriptor D_DATA_OPT = + new CLOptionDescriptor("data", CLOptionDescriptor.ARGUMENT_REQUIRED, DATA_OPT, + "HTTP POST data"); + + + private static final CLOptionDescriptor[] OPTIONS = new CLOptionDescriptor[] { + D_COMPRESSED_OPT, + D_HEADER_OPT, + D_METHOD_OPT, + D_DATA_OPT + }; + + public BasicCurlParser() { + super(); + } + + public Request parse(String commandLine) { + String[] args = translateCommandline(commandLine); + CLArgsParser parser = new CLArgsParser(args, OPTIONS); + String error = parser.getErrorString(); + if(error == null) { + List clOptions = parser.getArguments(); + Request request = new Request(); + for (CLOption option : clOptions) { + if (option.getDescriptor().getId() == CLOption.TEXT_ARGUMENT) { + // Curl or URL + if(!"CURL".equalsIgnoreCase(option.getArgument())) { + request.setUrl(option.getArgument()); + continue; + } + } else if (option.getDescriptor().getId() == COMPRESSED_OPT) { + request.setCompressed(true); + } else if (option.getDescriptor().getId() == HEADER_OPT) { + String nameAndValue = option.getArgument(0); + int indexOfSemicolon = nameAndValue.indexOf(':'); + String name = nameAndValue.substring(0, indexOfSemicolon).trim(); + String value = nameAndValue.substring(indexOfSemicolon+1).trim(); + request.addHeader(name, value); + } else if (option.getDescriptor().getId() == METHOD_OPT) { + String value = option.getArgument(0); + request.setMethod(value); + } else if (option.getDescriptor().getId() == DATA_OPT) { + String value = option.getArgument(0); + request.setMethod("POST"); + request.setPostData(value); + } + } + return request; + } else { + throw new IllegalArgumentException("Unexpected format for command line:"+commandLine+", error:"+error); + } + } + + /** + * Crack a command line. + * @param toProcess the command line to process. + * @return the command line broken into strings. + * An empty or null toProcess parameter results in a zero sized array. + */ + public static String[] translateCommandline(String toProcess) { + if (toProcess == null || toProcess.isEmpty()) { + //no command? no string + return new String[0]; + } + // parse with a simple finite state machine + + final int normal = 0; + final int inQuote = 1; + final int inDoubleQuote = 2; + int state = normal; + final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true); + final ArrayList result = new ArrayList<>(); + final StringBuilder current = new StringBuilder(); + boolean lastTokenHasBeenQuoted = false; + + while (tok.hasMoreTokens()) { + String nextTok = tok.nextToken(); + switch (state) { + case inQuote: + if ("\'".equals(nextTok)) { + lastTokenHasBeenQuoted = true; + state = normal; + } else { + current.append(nextTok); + } + break; + case inDoubleQuote: + if ("\"".equals(nextTok)) { + lastTokenHasBeenQuoted = true; + state = normal; + } else { + current.append(nextTok); + } + break; + default: + if ("\'".equals(nextTok)) { + state = inQuote; + } else if ("\"".equals(nextTok)) { + state = inDoubleQuote; + } else if (" ".equals(nextTok)) { + if (lastTokenHasBeenQuoted || current.length() > 0) { + result.add(current.toString()); + current.setLength(0); + } + } else { + current.append(nextTok); + } + lastTokenHasBeenQuoted = false; + break; + } + } + if (lastTokenHasBeenQuoted || current.length() > 0) { + result.add(current.toString()); + } + if (state == inQuote || state == inDoubleQuote) { + throw new IllegalArgumentException("unbalanced quotes in " + toProcess); + } + return result.toArray(new String[result.size()]); + } +} diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java b/src/protocol/http/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java new file mode 100644 index 00000000000..8e367b49c0a --- /dev/null +++ b/src/protocol/http/org/apache/jmeter/protocol/http/gui/action/ParseCurlCommandAction.java @@ -0,0 +1,229 @@ +/* + * 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.jmeter.protocol.http.gui.action; + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.MenuElement; +import javax.swing.tree.TreePath; + +import org.apache.jmeter.config.Arguments; +import org.apache.jmeter.control.LoopController; +import org.apache.jmeter.control.gui.TestPlanGui; +import org.apache.jmeter.exceptions.IllegalUserActionException; +import org.apache.jmeter.gui.GuiPackage; +import org.apache.jmeter.gui.action.AbstractAction; +import org.apache.jmeter.gui.action.ActionNames; +import org.apache.jmeter.gui.action.ActionRouter; +import org.apache.jmeter.gui.plugin.MenuCreator; +import org.apache.jmeter.gui.tree.JMeterTreeNode; +import org.apache.jmeter.protocol.http.control.Header; +import org.apache.jmeter.protocol.http.control.HeaderManager; +import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui; +import org.apache.jmeter.protocol.http.curl.BasicCurlParser; +import org.apache.jmeter.protocol.http.curl.BasicCurlParser.Request; +import org.apache.jmeter.protocol.http.gui.HeaderPanel; +import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory; +import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy; +import org.apache.jmeter.reporters.ResultCollector; +import org.apache.jmeter.services.FileServer; +import org.apache.jmeter.testelement.TestElement; +import org.apache.jmeter.testelement.TestPlan; +import org.apache.jmeter.threads.ThreadGroup; +import org.apache.jmeter.threads.gui.ThreadGroupGui; +import org.apache.jmeter.util.JMeterUtils; +import org.apache.jmeter.visualizers.ViewResultsFullVisualizer; +import org.apache.jorphan.collections.HashTree; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Parse a CURL command line and creates a test plan from it + * + */ +public class ParseCurlCommandAction extends AbstractAction implements MenuCreator { + + private static final Logger LOGGER = LoggerFactory.getLogger(ParseCurlCommandAction.class); + private static final String ACCEPT_ENCODING = "Accept-Encoding"; + private static final Set commands = new HashSet<>(); + public static final String IMPORT_CURL = "import_curl"; + static { + commands.add(IMPORT_CURL); + } + + /** + * + */ + public ParseCurlCommandAction() { + super(); + } + + @Override + public void doAction(ActionEvent e) { + ActionRouter.getInstance().doActionNow(new ActionEvent(e.getSource(), e.getID(), ActionNames.CLOSE)); + + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + DataFlavor df = new DataFlavor(String.class, String.class.getName()); + if (clipboard.isDataFlavorAvailable(df)) { + String curlCommand = null; + try { + curlCommand = (String) clipboard.getData(df); + LOGGER.info("Transforming CURL command {}", curlCommand); + BasicCurlParser basicCurlParser = new BasicCurlParser(); + BasicCurlParser.Request request = basicCurlParser.parse(curlCommand); + LOGGER.info("Parsed CURL command {} into {}", curlCommand, request); + GuiPackage guiPackage = GuiPackage.getInstance(); + guiPackage.updateCurrentNode(); + createTestPlan(e, request); + } catch (UnsupportedFlavorException | IOException ex) { + JMeterUtils.reportErrorToUser("Clipboard does not contain a CURL command", ex); + } catch (Exception ex) { + JMeterUtils.reportErrorToUser("Error creating Test Plan from CURL command:"+ex.getMessage(), ex); + } + } else { + JMeterUtils.reportErrorToUser("No CURL command in clipboard"); + return; + } + } + + private void createTestPlan(ActionEvent e, Request request) throws MalformedURLException, IllegalUserActionException { + GuiPackage guiPackage = GuiPackage.getInstance(); + + guiPackage.clearTestPlan(); + FileServer.getFileServer().setScriptName(null); + + ThreadGroup threadGroup = new ThreadGroup(); + threadGroup.setProperty(TestElement.GUI_CLASS, ThreadGroupGui.class.getName()); + threadGroup.setProperty(TestElement.NAME, "Thread Group"); + threadGroup.setNumThreads(1); + threadGroup.setRampUp(1); + + LoopController loopCtrl = new LoopController(); + loopCtrl.setLoops(1); + loopCtrl.setContinueForever(false); + threadGroup.setSamplerController(loopCtrl); + + TestPlan testPlan = new TestPlan(); + testPlan.setProperty(TestElement.NAME, "Test Plan"); + testPlan.setProperty(TestElement.GUI_CLASS, TestPlanGui.class.getName()); + + HashTree tree = new HashTree(); + HashTree testPlanHT = tree.add(testPlan); + HashTree threadGroupHT = testPlanHT.add(threadGroup); + + createHttpRequest(request, threadGroupHT); + + ResultCollector resultCollector = new ResultCollector(); + resultCollector.setProperty(TestElement.NAME, "View Results Tree"); + resultCollector.setProperty(TestElement.GUI_CLASS, ViewResultsFullVisualizer.class.getName()); + tree.add(tree.getArray()[0], resultCollector); + + final HashTree newTree = guiPackage.addSubTree(tree); + guiPackage.updateCurrentGui(); + guiPackage.getMainFrame().getTree().setSelectionPath( + new TreePath(((JMeterTreeNode) newTree.getArray()[0]).getPath())); + final HashTree subTree = guiPackage.getCurrentSubTree(); + // Send different event wether we are merging a test plan into another test plan, + // or loading a testplan from scratch + ActionEvent actionEvent = + new ActionEvent(subTree.get(subTree.getArray()[subTree.size() - 1]), e.getID(), ActionNames.SUB_TREE_LOADED); + ActionRouter.getInstance().actionPerformed(actionEvent); + } + + private HTTPSamplerProxy createHttpRequest(Request request, HashTree parentHT) throws MalformedURLException { + HTTPSamplerProxy httpSampler = (HTTPSamplerProxy) HTTPSamplerFactory.newInstance(HTTPSamplerFactory.DEFAULT_CLASSNAME); + httpSampler.setProperty(TestElement.GUI_CLASS, HttpTestSampleGui.class.getName()); + httpSampler.setProperty(TestElement.NAME, "HTTP Request"); + httpSampler.setProtocol(new URL(request.getUrl()).getProtocol()); + httpSampler.setPath(request.getUrl()); + httpSampler.setMethod(request.getMethod()); + + HashTree samplerHT = parentHT.add(httpSampler); + + HeaderManager headerManager = new HeaderManager(); + headerManager.setProperty(TestElement.GUI_CLASS, HeaderPanel.class.getName()); + headerManager.setProperty(TestElement.NAME, "HTTP HeaderManager"); + Map map = request.getHeaders(); + + boolean hasAcceptEncoding = false; + for (Map.Entry header : map.entrySet()) { + String key = header.getKey(); + hasAcceptEncoding = hasAcceptEncoding || key.equalsIgnoreCase(ACCEPT_ENCODING); + headerManager.getHeaders().addItem(new Header(key, header.getValue())); + } + if(!hasAcceptEncoding) { + headerManager.getHeaders().addItem(new Header(ACCEPT_ENCODING, "gzip, deflate")); + } + if (!"GET".equals(request.getMethod())) { + Arguments arguments = new Arguments(); + httpSampler.setArguments(arguments); + httpSampler.addNonEncodedArgument("", request.getPostData(), ""); + } + httpSampler.addTestElement(headerManager); + samplerHT.add(headerManager); + return httpSampler; + } + + @Override + public Set getActionNames() { + return commands; + } + + @Override + public JMenuItem[] getMenuItemsAtLocation(MENU_LOCATION location) { + if(location == MENU_LOCATION.HELP) { + JMenuItem menuItemIC = new JMenuItem( + "Import from cURL", KeyEvent.VK_UNDEFINED); + menuItemIC.setName(IMPORT_CURL); + menuItemIC.setActionCommand(IMPORT_CURL); + menuItemIC.setAccelerator(null); + menuItemIC.addActionListener(ActionRouter.getInstance()); + return new JMenuItem[]{menuItemIC}; + } + return new JMenuItem[0]; + } + + @Override + public JMenu[] getTopLevelMenus() { + return new JMenu[0]; + } + + @Override + public boolean localeChanged(MenuElement menu) { + return false; + } + + @Override + public void localeChanged() { + // NOOP + } +} diff --git a/test/src/org/apache/jmeter/curl/BasicCurlParserTest.java b/test/src/org/apache/jmeter/curl/BasicCurlParserTest.java new file mode 100644 index 00000000000..e02723f980b --- /dev/null +++ b/test/src/org/apache/jmeter/curl/BasicCurlParserTest.java @@ -0,0 +1,98 @@ +/* + * 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.jmeter.curl; + +import org.apache.jmeter.protocol.http.curl.BasicCurlParser; +import org.junit.Assert; +import org.junit.Test; + +/** + * @since 5.1 + */ +public class BasicCurlParserTest { + + /** + * + */ + public BasicCurlParserTest() { + super(); + } + + @Test + public void testFFParsing() { + String cmdLine = "curl 'http://jmeter.apache.org/' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:63.0) Gecko/20100101 Firefox/63.0' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Upgrade-Insecure-Requests: 1'"; + BasicCurlParser basicCurlParser = new BasicCurlParser(); + BasicCurlParser.Request request = basicCurlParser.parse(cmdLine); + Assert.assertEquals("http://jmeter.apache.org/", request.getUrl()); + Assert.assertEquals(6, request.getHeaders().size()); + Assert.assertTrue(request.isCompressed()); + Assert.assertEquals("GET", request.getMethod()); + } + + @Test + public void testChromeParsing() { + String cmdLine = "curl 'https://jmeter.apache.org/' -H 'Proxy-Connection: keep-alive' -H 'Proxy-Authorization: Basic XXXXXXXXX/' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Mobile Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.9,fr;q=0.8' --compressed"; + BasicCurlParser basicCurlParser = new BasicCurlParser(); + BasicCurlParser.Request request = basicCurlParser.parse(cmdLine); + Assert.assertEquals("https://jmeter.apache.org/", request.getUrl()); + Assert.assertEquals(7, request.getHeaders().size()); + Assert.assertTrue(request.isCompressed()); + Assert.assertEquals("GET", request.getMethod()); + } + + @Test + public void testChromeParsingNotCompressed() { + String cmdLine = "curl 'https://jmeter.apache.org/' -H 'Proxy-Connection: keep-alive' -H 'Proxy-Authorization: Basic XXXXXXXXX/' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Mobile Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.9,fr;q=0.8'"; + BasicCurlParser basicCurlParser = new BasicCurlParser(); + BasicCurlParser.Request request = basicCurlParser.parse(cmdLine); + Assert.assertEquals("https://jmeter.apache.org/", request.getUrl()); + Assert.assertEquals(7, request.getHeaders().size()); + Assert.assertFalse(request.isCompressed()); + Assert.assertEquals("GET", request.getMethod()); + } + + @Test + public void testChromeParsingNoHeaders() { + String cmdLine = "curl 'https://jmeter.apache.org/'"; + BasicCurlParser basicCurlParser = new BasicCurlParser(); + BasicCurlParser.Request request = basicCurlParser.parse(cmdLine); + Assert.assertEquals("https://jmeter.apache.org/", request.getUrl()); + Assert.assertTrue(request.getHeaders().isEmpty()); + Assert.assertFalse(request.isCompressed()); + Assert.assertEquals("GET", request.getMethod()); + } + + @Test + public void testPost() { + String cmdLine = "curl 'https://jmeter.apache.org/test' -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:63.0) Gecko/20100101 Firefox/63.0' -H 'Accept: */*' -H 'Accept-Language: en-US,en;q=0.5' --compressed -H 'Referer: https://www.example.com/' -H 'content-type: application/json;charset=UTF-8' -H 'Origin: https://www.example.com' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'TE: Trailers' --data '{\"abc\":\"123\",\"no\":\"matter on sunshine\"}'"; + BasicCurlParser basicCurlParser = new BasicCurlParser(); + BasicCurlParser.Request request = basicCurlParser.parse(cmdLine); + Assert.assertEquals("https://jmeter.apache.org/test", request.getUrl()); + Assert.assertEquals(9, request.getHeaders().size()); + Assert.assertTrue(request.isCompressed()); + Assert.assertEquals("POST", request.getMethod()); + } + + @Test(expected=IllegalArgumentException.class) + public void testError() { + String cmdLine = "curl 'https://jmeter.apache.org/' -u -H 'Proxy-Connection: keep-alive' -H 'Proxy-Authorization: Basic XXXXXXXXX/' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Mobile Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' -H 'Accept-Encoding: gzip, deflate' -H 'Accept-Language: en-US,en;q=0.9,fr;q=0.8'"; + BasicCurlParser basicCurlParser = new BasicCurlParser(); + basicCurlParser.parse(cmdLine); + } +}