Index: C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/test/src/org/apache/jmeter/protocol/http/proxy/TestHttpRequestHdr.java =================================================================== --- C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/test/src/org/apache/jmeter/protocol/http/proxy/TestHttpRequestHdr.java (revision 529143) +++ C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/test/src/org/apache/jmeter/protocol/http/proxy/TestHttpRequestHdr.java (working copy) @@ -18,48 +18,140 @@ package org.apache.jmeter.protocol.http.proxy; +import java.io.ByteArrayInputStream; +import java.io.IOException; + import org.apache.jmeter.config.Arguments; import org.apache.jmeter.junit.JMeterTestCase; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase; -import org.apache.jmeter.testelement.property.JMeterProperty; -import org.apache.jmeter.testelement.property.PropertyIterator; +import org.apache.jmeter.protocol.http.util.HTTPArgument; public class TestHttpRequestHdr extends JMeterTestCase { - public TestHttpRequestHdr(String name) { - super(name); - } + public TestHttpRequestHdr(String name) { + super(name); + } - public void setUp() { - } + public void testRepeatedArguments() throws Exception { + // A HTTP GET request + String TEST_GET_REQ = + "GET http://localhost/matrix.html" + + "?update=yes&d=1&d=2&d=&d=&d=&d=&d=&d=1&d=2&d=1&d=&d= " + + "HTTP/1.0\n\n"; + HTTPSamplerBase s = getSamplerForRequest(TEST_GET_REQ, "UTF-8"); + assertEquals(HTTPSamplerBase.GET, s.getMethod()); - public void testRepeatedArguments() throws Exception { - String TEST_REQ = - "GET http://localhost/matrix.html" - + "?update=yes&d=1&d=2&d=&d=&d=&d=&d=&d=1&d=2&d=1&d=&d= " - + "HTTP/1.0\n\n"; - HttpRequestHdr req = new HttpRequestHdr(); - req.parse(new java.io.ByteArrayInputStream(TEST_REQ.getBytes())); - HTTPSamplerBase s = req.getSampler(); - assertEquals(13,s.getArguments().getArguments().size()); - } + // Check arguments + Arguments arguments = s.getArguments(); + assertEquals(13, arguments.getArgumentCount()); + checkArgument((HTTPArgument)arguments.getArgument(0), "update", "yes", false); + checkArgument((HTTPArgument)arguments.getArgument(1), "d", "1", false); + checkArgument((HTTPArgument)arguments.getArgument(2), "d", "2", false); + checkArgument((HTTPArgument)arguments.getArgument(3), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(4), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(5), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(6), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(7), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(8), "d", "1", false); + checkArgument((HTTPArgument)arguments.getArgument(9), "d", "2", false); + checkArgument((HTTPArgument)arguments.getArgument(10), "d", "1", false); + checkArgument((HTTPArgument)arguments.getArgument(11), "d", "", false); + // I see that the value gets trimmed, not sure if that is correct + checkArgument((HTTPArgument)arguments.getArgument(12), "d", "", false); + + // A HTTP POST request + String postBody = "update=yes&d=1&d=2&d=&d=&d=&d=&d=&d=1&d=2&d=1&d=&d= "; + String TEST_POST_REQ = "POST http://localhost/matrix.html HTTP/1.0\n" + + "Content-type: " + + HTTPSamplerBase.APPLICATION_X_WWW_FORM_URLENCODED + + "; charset=UTF-8\n" + + postBody; + s = getSamplerForRequest(TEST_POST_REQ, "UTF-8"); + assertEquals(HTTPSamplerBase.POST, s.getMethod()); + + // Check arguments + arguments = s.getArguments(); + assertEquals(13, arguments.getArgumentCount()); + checkArgument((HTTPArgument)arguments.getArgument(0), "update", "yes", false); + checkArgument((HTTPArgument)arguments.getArgument(1), "d", "1", false); + checkArgument((HTTPArgument)arguments.getArgument(2), "d", "2", false); + checkArgument((HTTPArgument)arguments.getArgument(3), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(4), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(5), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(6), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(7), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(8), "d", "1", false); + checkArgument((HTTPArgument)arguments.getArgument(9), "d", "2", false); + checkArgument((HTTPArgument)arguments.getArgument(10), "d", "1", false); + checkArgument((HTTPArgument)arguments.getArgument(11), "d", "", false); + checkArgument((HTTPArgument)arguments.getArgument(12), "d", " ", false); + + // A HTTP POST request, with content-type text/plain + TEST_POST_REQ = "POST http://localhost/matrix.html HTTP/1.0\n" + + "Content-type: text/plain; charset=UTF-8\n" + + postBody; + s = getSamplerForRequest(TEST_POST_REQ, "UTF-8"); + assertEquals(HTTPSamplerBase.POST, s.getMethod()); + + // Check arguments + // We should have one argument, with the value equal to the post body + arguments = s.getArguments(); + assertEquals(1, arguments.getArgumentCount()); + checkArgument((HTTPArgument)arguments.getArgument(0), "", postBody, false); + } - // TODO: will need changing if arguments can be saved in decoded form - public void testEncodedArguments() throws Exception { - String TEST_REQ = - "GET http://localhost:80/matrix.html" - + "?abc" - + "?SPACE=a+b" - +"&space=a%20b" - +"&query=What?" - + " HTTP/1.1\n\n"; - HttpRequestHdr req = new HttpRequestHdr(); - req.parse(new java.io.ByteArrayInputStream(TEST_REQ.getBytes())); - Arguments arguments = req.getSampler().getArguments(); - assertEquals(3,arguments.getArguments().size()); - PropertyIterator pi= arguments.iterator(); - JMeterProperty next; - next = pi.next(); assertEquals("abc?SPACE=a+b",next.getStringValue()); - next = pi.next(); assertEquals("space=a%20b",next.getStringValue()); - next = pi.next(); assertEquals("query=What?",next.getStringValue()); - } + // TODO: will need changing if arguments can be saved in decoded form + public void testEncodedArguments() throws Exception { + // A HTTP GET request + String TEST_GET_REQ = "GET http://localhost:80/matrix.html" + + "?abc" + + "?SPACE=a+b" + + "&space=a%20b" + + "&query=What?" + + " HTTP/1.1\n\n"; + HTTPSamplerBase s = getSamplerForRequest(TEST_GET_REQ, "UTF-8"); + assertEquals(HTTPSamplerBase.GET, s.getMethod()); + + // Check arguments + Arguments arguments = s.getArguments(); + assertEquals(3, arguments.getArgumentCount()); + checkArgument((HTTPArgument)arguments.getArgument(0), "abc?SPACE", "a+b", false); + checkArgument((HTTPArgument)arguments.getArgument(1), "space", "a%20b", false); + checkArgument((HTTPArgument)arguments.getArgument(2), "query", "What?", false); + + // A HTTP POST request + String postBody = "abc?SPACE=a+b&space=a%20b&query=What?"; + String TEST_POST_REQ = "POST http://localhost:80/matrix.html HTTP/1.1\n" + + "Content-type: " + + HTTPSamplerBase.APPLICATION_X_WWW_FORM_URLENCODED + + "; charset=UTF-8\n" + + postBody; + s = getSamplerForRequest(TEST_POST_REQ, "UTF-8"); + assertEquals(HTTPSamplerBase.POST, s.getMethod()); + + // Check arguments + arguments = s.getArguments(); + assertEquals(3, arguments.getArgumentCount()); + checkArgument((HTTPArgument)arguments.getArgument(0), "abc?SPACE", "a+b", false); + checkArgument((HTTPArgument)arguments.getArgument(1), "space", "a%20b", false); + checkArgument((HTTPArgument)arguments.getArgument(2), "query", "What?", false); + } + + private HTTPSamplerBase getSamplerForRequest(String request, String contentEncoding) + throws IOException { + HttpRequestHdr req = new HttpRequestHdr(); + ByteArrayInputStream bis = new ByteArrayInputStream(request.getBytes(contentEncoding)); + req.parse(bis); + bis.close(); + return req.getSampler(); + } + + private void checkArgument( + HTTPArgument arg, + String expectedName, + String expectedValue, + boolean expectedEncoded) { + assertEquals(expectedName, arg.getName()); + assertEquals(expectedValue, arg.getValue()); + assertEquals(expectedEncoded, arg.isAlwaysEncoded()); + } } Index: C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java =================================================================== --- C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java (revision 529143) +++ C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java (working copy) @@ -136,7 +136,7 @@ readLength++; } } - postData = line.toString().trim(); + postData = line.toString(); if (log.isDebugEnabled()){ log.debug("postData: " + postData); log.debug("Request: " + clientRequest.toString()); @@ -230,11 +230,13 @@ if (contentTypeHeader != null) { return contentTypeHeader.getValue(); } - return ""; // $NON-NLS-1$ + else { + return null; + } } private boolean isMultipart(String contentType) { - if (contentType != null && contentType.startsWith(MultipartUrlConfig.MULTIPART_FORM)) { + if (contentType != null && contentType.startsWith(HTTPSamplerBase.MULTIPART_FORM_DATA)) { return true; } else { return false; @@ -243,9 +245,7 @@ private MultipartUrlConfig getMultipartConfig(String contentType) { if(isMultipart(contentType)) { - // Get the boundary string for the multiparts from the - // content type - //int startOfBoundaryValuePos = contentType.toLowerCase().substring(beginIndex) + // Get the boundary string for the multiparts from the content type String boundaryString = contentType.substring(contentType.toLowerCase().indexOf("boundary=") + "boundary=".length()); return new MultipartUrlConfig(boundaryString); } @@ -286,31 +286,46 @@ log.debug("Proxy setting default protocol to: http"); sampler.setProtocol(HTTP); } - - // Check if it was a multipart http post request - MultipartUrlConfig urlConfig = getMultipartConfig(getContentType()); - if (urlConfig != null) { - urlConfig.parseArguments(postData); - // Tell the sampler to do a multipart post - sampler.setDoMultipartPost(true); - // Remove the header for content-type and content-length, since - // those values will most likely be incorrect when the sampler - // performs the multipart request, because the boundary string - // will change - getHeaderManager().removeHeaderNamed(CONTENT_TYPE); - getHeaderManager().removeHeaderNamed(CONTENT_LENGTH); + + // If it was a HTTP GET request, then all parameters in the URL + // has been handled by the sampler.setPath above, so we just need + // to do parse the rest of the request if it is not a GET request + if(!HTTPSamplerBase.GET.equals(method)) { + // Check if it was a multipart http post request + final String contentType = getContentType(); + MultipartUrlConfig urlConfig = getMultipartConfig(contentType); + if (urlConfig != null) { + urlConfig.parseArguments(postData); + // Tell the sampler to do a multipart post + sampler.setDoMultipartPost(true); + // Remove the header for content-type and content-length, since + // those values will most likely be incorrect when the sampler + // performs the multipart request, because the boundary string + // will change + getHeaderManager().removeHeaderNamed(CONTENT_TYPE); + getHeaderManager().removeHeaderNamed(CONTENT_LENGTH); - // Set the form data - sampler.setArguments(urlConfig.getArguments()); - // Set the file uploads - sampler.setFileField(urlConfig.getFileFieldName()); - sampler.setFilename(urlConfig.getFilename()); - sampler.setMimetype(urlConfig.getMimeType()); - } else if (postData != null && postData.trim().startsWith(" 0) { + // Just put the whole postbody as the value of a parameter + sampler.addNonEncodedArgument("", postData, ""); //used when postData is pure xml (ex. an xml-rpc call) + } + } if (log.isDebugEnabled()) log.debug("sampler path = " + sampler.getPath()); } Index: C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/sampler/PostWriter.java =================================================================== --- C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/sampler/PostWriter.java (revision 529143) +++ C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/sampler/PostWriter.java (working copy) @@ -27,7 +27,6 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.URLConnection; -import java.net.URLEncoder; import org.apache.jmeter.protocol.http.util.HTTPArgument; import org.apache.jmeter.testelement.property.PropertyIterator; @@ -220,20 +219,54 @@ connection.setDoInput(true); } else { - // Set the content type - connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE, HTTPSamplerBase.APPLICATION_X_WWW_FORM_URLENCODED); + // Check if the header manager had a content type header + // This allows the user to specify his own content-type for a POST request + String contentTypeHeader = connection.getRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE); + boolean hasContentTypeHeader = contentTypeHeader != null && contentTypeHeader.length() > 0; // If there are no arguments, we can send a file as the body of the request if(sampler.getArguments() != null && sampler.getArguments().getArgumentCount() == 0 && sampler.getSendFileAsPostBody()) { + if(!hasContentTypeHeader) { + // Allow the mimetype of the file to control the content type + if(sampler.getMimetype() != null && sampler.getMimetype().length() > 0) { + connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE, sampler.getMimetype()); + } + else { + connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE, HTTPSamplerBase.APPLICATION_X_WWW_FORM_URLENCODED); + } + } + // Create the content length we are going to write File inputFile = new File(sampler.getFilename()); contentLength = inputFile.length(); } else { + // Set the content type + if(!hasContentTypeHeader) { + connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE, HTTPSamplerBase.APPLICATION_X_WWW_FORM_URLENCODED); + } + // We create the post body content now, so we know the size ByteArrayOutputStream bos = new ByteArrayOutputStream(); - String postBody = sampler.getQueryString(contentEncoding); + // If none of the arguments have a name specified, we + // just send all the values as the post body + String postBody = null; + if(!sampler.getSendParameterValuesAsPostBody()) { + // It is a normal post request, with parameter names and values + postBody = sampler.getQueryString(contentEncoding); + } + else { + // Just append all the parameter values, and use that as the post body + StringBuffer postBodyBuffer = new StringBuffer(); + PropertyIterator args = sampler.getArguments().iterator(); + while (args.hasNext()) { + HTTPArgument arg = (HTTPArgument) args.next().getObjectValue(); + postBodyBuffer.append(arg.getValue()); + } + postBody = postBodyBuffer.toString(); + } + // Query string should be encoded in UTF-8 bos.write(postBody.getBytes("UTF-8")); // $NON-NLS-1$ bos.flush(); Index: C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java =================================================================== --- C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java (revision 529143) +++ C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java (working copy) @@ -25,6 +25,7 @@ import java.io.OutputStream; import java.net.InetAddress; import java.net.URL; +import java.net.URLDecoder; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; @@ -54,6 +55,7 @@ import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.methods.RequestEntity; +import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.httpclient.methods.TraceMethod; import org.apache.commons.httpclient.methods.multipart.FilePart; import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity; @@ -68,6 +70,7 @@ import org.apache.jmeter.protocol.http.control.Authorization; import org.apache.jmeter.protocol.http.control.CookieManager; import org.apache.jmeter.protocol.http.control.HeaderManager; +import org.apache.jmeter.protocol.http.util.EncoderCache; import org.apache.jmeter.protocol.http.util.SlowHttpClientSocketFactory; import org.apache.jmeter.protocol.http.util.HTTPArgument; import org.apache.jmeter.testelement.property.CollectionProperty; @@ -313,11 +316,23 @@ } } else { - // Set the content type - post.setRequestHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED); + // Check if the header manager had a content type header + // This allows the user to specify his own content-type for a POST request + Header contentTypeHeader = post.getRequestHeader(HEADER_CONTENT_TYPE); + boolean hasContentTypeHeader = contentTypeHeader != null && contentTypeHeader.getValue() != null && contentTypeHeader.getValue().length() > 0; // If there are no arguments, we can send a file as the body of the request if(getArguments().getArgumentCount() == 0 && getSendFileAsPostBody()) { + if(!hasContentTypeHeader) { + // Allow the mimetype of the file to control the content type + if(getMimetype() != null && getMimetype().length() > 0) { + post.setRequestHeader(HEADER_CONTENT_TYPE, getMimetype()); + } + else { + post.setRequestHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED); + } + } + FileRequestEntity fileRequestEntity = new FileRequestEntity(new File(getFilename()),null); post.setRequestEntity(fileRequestEntity); @@ -327,21 +342,41 @@ else { // In an application/x-www-form-urlencoded request, we only support // parameters, no file upload is allowed - + if(!hasContentTypeHeader) { + // Set the content type + post.setRequestHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED); + } + // If a content encoding is specified, we set it as http parameter, so that // the post body will be encoded in the specified content encoding final String contentEncoding = getContentEncoding(); if(contentEncoding != null && contentEncoding.trim().length() > 0) { post.getParams().setContentCharset(contentEncoding); } - - PropertyIterator args = getArguments().iterator(); - while (args.hasNext()) { - HTTPArgument arg = (HTTPArgument) args.next().getObjectValue(); - post.addParameter(arg.getName(), arg.getValue()); + + // If none of the arguments have a name specified, we + // just send all the values as the post body + if(!getSendParameterValuesAsPostBody()) { + // It is a normal post request, with parameter names and values + PropertyIterator args = getArguments().iterator(); + while (args.hasNext()) { + HTTPArgument arg = (HTTPArgument) args.next().getObjectValue(); + post.addParameter(arg.getName(), arg.getValue()); + } } + else { + // Just append all the parameter values, and use that as the post body + StringBuffer postBody = new StringBuffer(); + PropertyIterator args = getArguments().iterator(); + while (args.hasNext()) { + HTTPArgument arg = (HTTPArgument) args.next().getObjectValue(); + postBody.append(arg.getValue()); + } + StringRequestEntity requestEntity = new StringRequestEntity(postBody.toString(), post.getRequestHeader(HEADER_CONTENT_TYPE).getValue(), post.getRequestCharSet()); + post.setRequestEntity(requestEntity); + } - // If the Multipart is repeatable, we can send it first to + // If the request entity is repeatable, we can send it first to // our own stream, so we can return it if(post.getRequestEntity().isRepeatable()) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); Index: C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java =================================================================== --- C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (revision 529143) +++ C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (working copy) @@ -209,9 +209,9 @@ protected static final String HEADER_LOCATION = "Location"; // $NON-NLS-1$ - protected static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded"; // $NON-NLS-1$ + public static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded"; // $NON-NLS-1$ - protected static final String MULTIPART_FORM_DATA = "multipart/form-data"; // $NON-NLS-1$ + public static final String MULTIPART_FORM_DATA = "multipart/form-data"; // $NON-NLS-1$ // Derive the mapping of content types to parsers private static Map parsersForType = new HashMap(); @@ -290,11 +290,32 @@ * i.e. without any additional wrapping * * @return true if specified file is to be sent as the body, - * i.e. both FileField and MimeType are blank + * i.e. both FileField is blank */ - public boolean getSendFileAsPostBody(){ - return getFileField().length()== 0 && getMimetype().length() == 0; + public boolean getSendFileAsPostBody() { + // If no file field is specified, the file is sent as post body + return getFileField().length()== 0 && getFilename().length() > 0; } + + /** + * Determine if none of the parameters have a name, and if that + * is the case, it means that the parameter values should be sent + * as the post body + * + * @return true if none of the parameters have a name specified + */ + public boolean getSendParameterValuesAsPostBody() { + boolean noArgumentsHasName = true; + PropertyIterator args = getArguments().iterator(); + while (args.hasNext()) { + HTTPArgument arg = (HTTPArgument) args.next().getObjectValue(); + if(arg.getName() != null && arg.getName().length() > 0) { + noArgumentsHasName = false; + break; + } + } + return noArgumentsHasName; + } /** * Determine if we should use multipart/form-data or Index: C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/config/MultipartUrlConfig.java =================================================================== --- C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/config/MultipartUrlConfig.java (revision 529143) +++ C:/Documents and Settings/alf.hogemark/workspace/Jmeter 2.2 official/src/protocol/http/org/apache/jmeter/protocol/http/config/MultipartUrlConfig.java (working copy) @@ -29,7 +29,7 @@ * @version $Revision$ */ public class MultipartUrlConfig implements Serializable { - + /** @deprecated use HTTPSamplerBase.MULTIPART_FORM_DATA instead */ public static final String MULTIPART_FORM = "multipart/form-data"; private String boundary, filename, fileField, mimetype;