1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package org.apache.jmeter.protocol.http.config; 20 21 import java.io.Serializable; 22 23 import org.apache.jmeter.config.Arguments; 24 import org.apache.jmeter.protocol.http.util.HTTPArgument; 25 import org.apache.jmeter.protocol.http.util.HTTPFileArgs; 26 import org.apache.jmeter.util.JMeterUtils; 27 import org.apache.jorphan.util.JOrphanUtils; 28 import org.apache.oro.text.regex.Pattern; 29 import org.apache.oro.text.regex.Perl5Compiler; 30 import org.apache.oro.text.regex.Perl5Matcher; 31 32 /** 33 * Configuration element which handles HTTP Parameters and files to be uploaded 34 */ 35 public class MultipartUrlConfig implements Serializable { 36 37 private final String boundary; 38 39 private final Arguments args; 40 41 /** 42 * HTTPFileArgs list to be uploaded with http request. 43 */ 44 private final HTTPFileArgs files; 45 46 /** 47 * @deprecated only for use by unit tests 48 */ 49 public MultipartUrlConfig(){ 50 this(null); 51 } 52 53 // called by HttpRequestHdr 54 public MultipartUrlConfig(String boundary) { 55 args = new Arguments(); 56 files = new HTTPFileArgs(); 57 this.boundary = boundary; 58 } 59 60 public String getBoundary() { 61 return boundary; 62 } 63 64 public Arguments getArguments() { 65 return args; 66 } 67 68 public void addArgument(String name, String value) { 69 Arguments myArgs = this.getArguments(); 70 myArgs.addArgument(new HTTPArgument(name, value)); 71 } 72 73 public void addArgument(String name, String value, String metadata) { 74 Arguments myArgs = this.getArguments(); 75 myArgs.addArgument(new HTTPArgument(name, value, metadata)); 76 } 77 78 public HTTPFileArgs getHTTPFileArgs() { 79 return files; 80 } 81 82 // NOT USED 83 // /** 84 // * @deprecated values in a multipart/form-data are not urlencoded, 85 // * so it does not make sense to add a value as a encoded value 86 // */ 87 // public void addEncodedArgument(String name, String value) { 88 // Arguments myArgs = getArguments(); 89 // HTTPArgument arg = new HTTPArgument(name, value, true); 90 // if (arg.getName().equals(arg.getEncodedName()) && arg.getValue().equals(arg.getEncodedValue())) { 91 // arg.setAlwaysEncoded(false); 92 // } 93 // myArgs.addArgument(arg); 94 // } 95 96 /** 97 * Add a value that is not URL encoded, and make sure it 98 * appears in the GUI that it will not be encoded when 99 * the request is sent. 100 * 101 * @param name 102 * @param value 103 */ 104 private void addNonEncodedArgument(String name, String value) { 105 Arguments myArgs = getArguments(); 106 // The value is not encoded 107 HTTPArgument arg = new HTTPArgument(name, value, false); 108 // Let the GUI show that it will not be encoded 109 arg.setAlwaysEncoded(false); 110 myArgs.addArgument(arg); 111 } 112 113 /** 114 * This method allows a proxy server to send over the raw text from a 115 * browser's output stream to be parsed and stored correctly into the 116 * UrlConfig object. 117 */ 118 public void parseArguments(String queryString) { 119 String[] parts = JOrphanUtils.split(queryString, "--" + getBoundary()); //$NON-NLS-1$ 120 for (int i = 0; i < parts.length; i++) { 121 String contentDisposition = getHeaderValue("Content-disposition", parts[i]); //$NON-NLS-1$ 122 String contentType = getHeaderValue("Content-type", parts[i]); //$NON-NLS-1$ 123 // Check if it is form data 124 if (contentDisposition != null && contentDisposition.indexOf("form-data") > -1) { //$NON-NLS-1$ 125 // Get the form field name 126 final String namePrefix = "name=\""; //$NON-NLS-1$ 127 int index = contentDisposition.indexOf(namePrefix) + namePrefix.length(); 128 String name = contentDisposition.substring(index, contentDisposition.indexOf("\"", index)); //$NON-NLS-1$ 129 130 // Check if it is a file being uploaded 131 final String filenamePrefix = "filename=\""; //$NON-NLS-1$ 132 if (contentDisposition.indexOf(filenamePrefix) > -1) { 133 // Get the filename 134 index = contentDisposition.indexOf(filenamePrefix) + filenamePrefix.length(); 135 String path = contentDisposition.substring(index, contentDisposition.indexOf("\"", index)); //$NON-NLS-1$ 136 if(path != null && path.length() > 0) { 137 // Set the values retrieved for the file upload 138 files.addHTTPFileArg(path, name, contentType); 139 } 140 } 141 else { 142 // Find the first empty line of the multipart, it signals end of headers for multipart 143 int indexEmptyLfCrLfLinePos = parts[i].indexOf("\n\r\n"); //$NON-NLS-1$ 144 int indexEmptyLfLfLinePos = parts[i].indexOf("\n\n"); //$NON-NLS-1$ 145 String value = null; 146 if(indexEmptyLfCrLfLinePos > -1) { 147 value = parts[i].substring(indexEmptyLfCrLfLinePos).trim(); 148 } 149 else if(indexEmptyLfLfLinePos > -1) { 150 value = parts[i].substring(indexEmptyLfLfLinePos).trim(); 151 } 152 this.addNonEncodedArgument(name, value); 153 } 154 } 155 } 156 } 157 158 private String getHeaderValue(String headerName, String multiPart) { 159 String regularExpression = headerName + "\\s*:\\s*(.*)$"; //$NON-NLS-1$ 160 Perl5Matcher localMatcher = JMeterUtils.getMatcher(); 161 Pattern pattern = JMeterUtils.getPattern(regularExpression, Perl5Compiler.READ_ONLY_MASK | Perl5Compiler.CASE_INSENSITIVE_MASK | Perl5Compiler.MULTILINE_MASK); 162 if(localMatcher.contains(multiPart, pattern)) { 163 return localMatcher.getMatch().group(1).trim(); 164 } 165 else { 166 return null; 167 } 168 } 169 }