Index: C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler.java
===================================================================
--- C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler.java (revision 522033)
+++ C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler.java (working copy)
@@ -68,7 +68,10 @@
}
private static final byte[] NULL_BA = new byte[0];// can share these
-
+
+ /** Handles writing of a post request */
+ private PostWriter postWriter;
+
/**
* Constructor for the HTTPSampler object.
*
@@ -86,7 +89,8 @@
* if an I/O exception occurs
*/
protected void setPostHeaders(URLConnection conn) throws IOException {
- PostWriter.setHeaders(conn, this);
+ postWriter = new PostWriter();
+ postWriter.setHeaders(conn, this);
}
private void setPutHeaders(URLConnection conn)
@@ -105,11 +109,12 @@
*
* @param connection
* URLConnection
where POST data should be sent
+ * @return a String show what was posted. Will not contain actual file upload content
* @exception IOException
* if an I/O exception occurs
*/
- protected void sendPostData(URLConnection connection) throws IOException {
- PostWriter.sendPostData(connection, this);
+ protected String sendPostData(URLConnection connection) throws IOException {
+ return postWriter.sendPostData(connection, this);
}
private void sendPutData(URLConnection conn) throws IOException {
@@ -147,8 +152,6 @@
* if an I/O Exception occurs
*/
protected HttpURLConnection setupConnection(URL u, String method, HTTPSampleResult res) throws IOException {
- HttpURLConnection conn;
-
SSLManager sslmgr = null;
if (PROTOCOL_HTTPS.equalsIgnoreCase(u.getProtocol())) {
try {
@@ -158,7 +161,7 @@
}
}
- conn = (HttpURLConnection) u.openConnection();
+ HttpURLConnection conn = (HttpURLConnection) u.openConnection();
// Update follow redirects setting just for this connection
conn.setInstanceFollowRedirects(getAutoRedirects());
@@ -189,9 +192,6 @@
setConnectionAuthorization(conn, u, getAuthManager());
if (method.equals(POST)) {
- if(res != null) {
- res.setQueryString(getQueryString());
- }
setPostHeaders(conn);
} else if (method.equals(PUT)) {
setPutHeaders(conn);
@@ -203,7 +203,7 @@
res.setRequestHeaders(getConnectionHeaders(conn));
res.setCookies(cookies);
}
-
+
return conn;
}
@@ -473,7 +473,8 @@
}
// Nice, we've got a connection. Finish sending the request:
if (method.equals(POST)) {
- sendPostData(conn);
+ String postBody = sendPostData(conn);
+ res.setQueryString(postBody);
} else if (method.equals(PUT)) {
sendPutData(conn);
}
Index: C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/PostWriter.java
===================================================================
--- C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/PostWriter.java (revision 522033)
+++ C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/PostWriter.java (working copy)
@@ -19,179 +19,383 @@
package org.apache.jmeter.protocol.http.sampler;
import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
-import java.net.HttpURLConnection;
import java.net.URLConnection;
+import java.net.URLEncoder;
-import org.apache.jmeter.config.Argument;
+import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.testelement.property.PropertyIterator;
/**
+ * Class for setting the necessary headers for a POST request, and sending the
+ * body of the POST.
*/
-
public class PostWriter {
-
- private static final String DASH_DASH = "--"; // $NON-NLS-1$
+ private static final String DASH_DASH = "--"; // $NON-NLS-1$
+ /** The bounday string between multiparts */
protected final static String BOUNDARY = "---------------------------7d159c1302d0y0"; // $NON-NLS-1$
private final static byte[] CRLF = { 0x0d, 0x0A };
protected static final String encoding = "iso-8859-1"; // $NON-NLS-1$
- // Not instantiable
- private PostWriter(){
-
- }
+ /** The form data that is going to be sent as url encoded */
+ private byte[] formDataUrlEncoded;
+ /** The form data that is going to be sent in post body */
+ private byte[] formDataPostBody;
+ /** The start of the file multipart to be sent */
+ private byte[] formDataFileStartMultipart;
+ /** The boundary string for multipart */
+ private String boundary;
+
+ /**
+ * Constructor for PostWriter.
+ * Uses the PostWriter.BOUNDARY as the boundary string
+ *
+ */
+ public PostWriter() {
+ this(BOUNDARY);
+ }
+
+ /**
+ * Constructor for PostWriter
+ *
+ * @param boundary the boundary string to use as marker between multipart parts
+ */
+ public PostWriter(String boundary) {
+ this.boundary = boundary;
+ }
+
/**
* Send POST data from Entry to the open connection.
+ *
+ * @return the post body sent. Actual file content is not returned, it
+ * is just shown as a placeholder text "actual file content"
*/
- public static void sendPostData(URLConnection connection, HTTPSampler sampler) throws IOException {
- // If filename was specified then send the post using multipart syntax
- String filename = sampler.getFilename();
- if ((filename != null) && (filename.trim().length() > 0)) {
- OutputStream out = connection.getOutputStream();
- // Check if not using multi-part:
- if (sampler.getSendFileAsPostBody())
- {
- InputStream in = getFileStream(filename);
- byte[] buf = new byte[1024];
- int read;
- while ((read = in.read(buf)) > 0)
- {
- out.write(buf, 0, read);
- }
- out.flush();
- in.close();
- return;
+ public String sendPostData(URLConnection connection, HTTPSampler sampler) throws IOException {
+ // Buffer to hold the post body, except file content
+ StringBuffer postedBody = new StringBuffer(1000);
+
+ // Check if we should do a multipart/form-data or an
+ // application/x-www-form-urlencoded post request
+ if(sampler.getUseMultipartForPost()) {
+ OutputStream out = connection.getOutputStream();
+
+ // Write the form data post body, which we have constructed
+ // in the setHeaders. This contains the multipart start divider
+ // and any form data, i.e. arguments
+ out.write(formDataPostBody);
+ // We get the posted bytes as UTF-8, since java is using UTF-8
+ postedBody.append(new String(formDataPostBody, "UTF-8")); // $NON-NLS-1$
+
+ // Add any files
+ if(sampler.hasUploadableFiles()) {
+ // First write the start multipart file
+ out.write(formDataFileStartMultipart);
+ // We get the posted bytes as UTF-8, since java is using UTF-8
+ postedBody.append(new String(formDataFileStartMultipart, "UTF-8")); // $NON-NLS-1$
+
+ // Write the actual file content
+ writeFileToStream(sampler.getFilename(), out);
+ // We just add placeholder text for file content
+ postedBody.append(""); // $NON-NLS-1$
+
+ // Write the end of multipart file
+ byte[] fileMultipartEndDivider = getFileMultipartEndDivider();
+ out.write(fileMultipartEndDivider);
+ // We get the posted bytes as UTF-8, since java is using UTF-8
+ postedBody.append(new String(fileMultipartEndDivider, "UTF-8")); // $NON-NLS-1$
}
- writeln(out, DASH_DASH + BOUNDARY);
- PropertyIterator args = sampler.getArguments().iterator();
- while (args.hasNext()) {
- Argument arg = (Argument) args.next().getObjectValue();
- writeFormMultipartStyle(out, arg.getName(), arg.getValue());
- writeln(out, DASH_DASH + BOUNDARY);
- }
- writeFileToURL(out, filename, sampler.getFileField(), getFileStream(filename), sampler.getMimetype());
- writeln(out, DASH_DASH + BOUNDARY + DASH_DASH);
- out.flush();
- out.close();
- }
+ // Write end of multipart
+ byte[] multipartEndDivider = getMultipartEndDivider();
+ out.write(multipartEndDivider);
+ // We get the posted bytes as UTF-8, since java is using UTF-8
+ postedBody.append(new String(multipartEndDivider, "UTF-8")); // $NON-NLS-1$
- // No filename specified, so send the post using normal syntax
- else {
- String postData = sampler.getQueryString();
- final String contentEncoding = sampler.getContentEncoding();
- OutputStreamWriter out;
- if (contentEncoding.length() > 0) {
- out = new OutputStreamWriter(connection.getOutputStream(), contentEncoding);
- } else {
- out = new OutputStreamWriter(connection.getOutputStream());
- }
- out.write(postData);
- out.flush();
+ out.flush();
out.close();
- }
+ }
+ else {
+ // 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()) {
+ OutputStream out = connection.getOutputStream();
+ writeFileToStream(sampler.getFilename(), out);
+ out.flush();
+ out.close();
+
+ // We just add placeholder text for file content
+ postedBody.append(""); // $NON-NLS-1$
+ }
+ else {
+ // In an application/x-www-form-urlencoded request, we only support
+ // parameters, no file upload is allowed
+ OutputStream out = connection.getOutputStream();
+ out.write(formDataUrlEncoded);
+ out.flush();
+ out.close();
+
+ // We get the posted bytes as UTF-8, since java is using UTF-8
+ postedBody.append(new String(formDataUrlEncoded, "UTF-8")); // $NON-NLS-1$
+ }
+ }
+ return postedBody.toString();
}
+
+ public void setHeaders(URLConnection connection, HTTPSampler sampler) throws IOException {
+ // Get the encoding to use for the request
+ String contentEncoding = sampler.getContentEncoding();
+ if(contentEncoding == null || contentEncoding.length() == 0) {
+ contentEncoding = encoding;
+ }
+ long contentLength = 0L;
+
+ // Check if we should do a multipart/form-data or an
+ // application/x-www-form-urlencoded post request
+ if(sampler.getUseMultipartForPost()) {
+ // Set the content type
+ connection.setRequestProperty(
+ HTTPSamplerBase.HEADER_CONTENT_TYPE,
+ HTTPSamplerBase.MULTIPART_FORM_DATA + "; boundary=" + getBoundary()); // $NON-NLS-1$
+
+ // Write the form section
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
- public static void setHeaders(URLConnection connection, HTTPSampler sampler) throws IOException {
- ((HttpURLConnection) connection).setRequestMethod(HTTPSamplerBase.POST);
+ // First the multipart start divider
+ bos.write(getMultipartDivider());
+ // Add any parameters
+ PropertyIterator args = sampler.getArguments().iterator();
+ while (args.hasNext()) {
+ HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
+ // End the previous multipart
+ bos.write(CRLF);
+ // Write multipart for parameter
+ writeFormMultipart(bos, arg.getName(), arg.getValue(), contentEncoding);
+ }
+ // If there are any files, we need to end the previous multipart
+ if(sampler.hasUploadableFiles()) {
+ // End the previous multipart
+ bos.write(CRLF);
+ }
+ bos.flush();
+ // Keep the content, will be sent later
+ formDataPostBody = bos.toByteArray();
+ bos.close();
+ contentLength = formDataPostBody.length;
- // If filename was specified then send the post using multipart syntax
- String filename = sampler.getFilename();
- if ((filename != null) && (filename.trim().length() > 0)) {
- if (!sampler.getSendFileAsPostBody()) { // unless the file is the body...
- String hct= connection.getRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE);
- if (hct == null || hct.length() == 0) {
- connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE,
- "multipart/form-data; boundary=" + BOUNDARY); // $NON-NLS-1$
- }
- }
- connection.setDoOutput(true);
- connection.setDoInput(true);
- }
+ // Now we just construct any multipart for the files
+ // We only construct the file multipart start, we do not write
+ // the actual file content
+ if(sampler.hasUploadableFiles()) {
+ bos = new ByteArrayOutputStream();
+ // Write multipart for file
+ writeStartFileMultipart(bos, sampler.getFilename(), sampler.getFileField(), sampler.getMimetype());
+ bos.flush();
+ formDataFileStartMultipart = bos.toByteArray();
+ bos.close();
+ contentLength += formDataFileStartMultipart.length;
+ // Add also the length of the file content
+ File uploadFile = new File(sampler.getFilename());
+ contentLength += uploadFile.length();
+ // And the end of the file multipart
+ contentLength += getFileMultipartEndDivider().length;
+ }
- // No filename specified, so send the post using normal syntax
- else {
- String postData = sampler.getQueryString();
- connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_LENGTH, Integer.toString(postData.length()));
- String hct= connection.getRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE);
- if (hct == null || hct.length() == 0) {
- connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE, HTTPSamplerBase.APPLICATION_X_WWW_FORM_URLENCODED);
+ // Add the end of multipart
+ contentLength += getMultipartEndDivider().length;
+
+ // Set the content length
+ connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_LENGTH, Long.toString(contentLength));
+
+ // Make the connection ready for sending post data
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+ }
+ else {
+ // Set the content type
+ connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_TYPE, HTTPSamplerBase.APPLICATION_X_WWW_FORM_URLENCODED);
+
+ // 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()) {
+ // Create the content length we are going to write
+ File inputFile = new File(sampler.getFilename());
+ contentLength = inputFile.length();
+ }
+ else {
+ // We create the post body content now, so we know the size
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ String postBody = getQueryStringForPostBody(sampler, contentEncoding);
+ // Query string should be encoded in UTF-8
+ bos.write(postBody.getBytes("UTF-8")); // $NON-NLS-1$
+ bos.flush();
+ bos.close();
+
+ // Keep the content, will be sent later
+ formDataUrlEncoded = bos.toByteArray();
+ contentLength = bos.toByteArray().length;
+ }
+
+ // Set the content length
+ connection.setRequestProperty(HTTPSamplerBase.HEADER_CONTENT_LENGTH, Long.toString(contentLength));
+
+ // Make the connection ready for sending post data
+ connection.setDoOutput(true);
+ }
+ }
+
+ /**
+ * Gets the query string for the sampler encoded for http post body.
+ * This method works differently from the getQueryString in HttpSamplerBase,
+ * becuase this method does not force the parameter value to be url encoded
+ * in utf8. Rather, it uses the specified encoding for the parameter value
+ *
+ * @return the querystring encoded as usable for a http post body request
+ */
+ private String getQueryStringForPostBody(HTTPSampler sampler, String contentEncoding) throws IOException {
+ StringBuffer buf = new StringBuffer();
+ PropertyIterator iter = sampler.getArguments().iterator();
+ boolean first = true;
+ while (iter.hasNext()) {
+ HTTPArgument arg = (HTTPArgument) iter.next().getObjectValue();
+
+ if (!first) {
+ buf.append("&");
+ } else {
+ first = false;
}
- connection.setDoOutput(true);
+ buf.append(arg.getEncodedName());
+ if (arg.getMetaData() == null) {
+ buf.append("=");
+ } else {
+ buf.append(arg.getMetaData());
+ }
+ buf.append(URLEncoder.encode(arg.getValue(), contentEncoding));
}
+ return buf.toString();
}
+
+ /**
+ * Get the boundary string, used to separate multiparts
+ *
+ * @return the boundary string
+ */
+ protected String getBoundary() {
+ return boundary;
+ }
- private static InputStream getFileStream(String filename) throws IOException {
- return new BufferedInputStream(new FileInputStream(filename));
- }
+ /**
+ * Get the bytes used to separate multiparts
+ *
+ * @return the bytes used to separate multiparts
+ * @throws IOException
+ */
+ private byte[] getMultipartDivider() throws IOException {
+ return new String(DASH_DASH + getBoundary()).getBytes(encoding);
+ }
- /*
- * NOTUSED private String getContentLength(MultipartUrlConfig config) { long
- * size = 0; size += BOUNDARY.length() + 2; PropertyIterator iter =
- * config.getArguments().iterator(); while (iter.hasNext()) { Argument item =
- * (Argument) iter.next().getObjectValue(); size += item.getName().length() +
- * item.getValue().toString().length(); size += CRLF.length * 4; size +=
- * BOUNDARY.length() + 2; size += 39; } size += new
- * File(config.getFilename()).length(); size += CRLF.length * 5; size +=
- * BOUNDARY.length() + 2; size +=
- * encode(config.getFileFieldName()).length(); size +=
- * encode(config.getFilename()).length(); size +=
- * config.getMimeType().length(); size += 66; size += 2 + (CRLF.length * 1);
- * return Long.toString(size); }
- */
+ /**
+ * Get the bytes used to end a file multipat
+ *
+ * @return the bytes used to end a file multipart
+ * @throws IOException
+ */
+ private byte[] getFileMultipartEndDivider() throws IOException{
+ byte[] ending = new String(DASH_DASH + getBoundary()).getBytes(encoding);
+ byte[] completeEnding = new byte[ending.length + CRLF.length];
+ System.arraycopy(CRLF, 0, completeEnding, 0, CRLF.length);
+ System.arraycopy(ending, 0, completeEnding, CRLF.length, ending.length);
+ return completeEnding;
+ }
- /**
- * Writes out the contents of a file in correct multipart format.
- */
- private static void writeFileToURL(OutputStream out, String filename,
- String fieldname, InputStream in, String mimetype)
+ /**
+ * Get the bytes used to end the multipart request
+ *
+ * @return the bytes used to end the multipart request
+ * @throws IOException
+ */
+ private byte[] getMultipartEndDivider() throws IOException{
+ byte[] ending = DASH_DASH.getBytes(encoding);
+ byte[] completeEnding = new byte[ending.length + CRLF.length];
+ System.arraycopy(ending, 0, completeEnding, 0, ending.length);
+ System.arraycopy(CRLF, 0, completeEnding, ending.length, CRLF.length);
+ return completeEnding;
+ }
+
+ /**
+ * Write the start of a file multipart, up to the point where the
+ * actual file content should be written
+ */
+ private void writeStartFileMultipart(OutputStream out, String filename,
+ String fieldname, String mimetype)
throws IOException {
write(out, "Content-Disposition: form-data; name=\""); // $NON-NLS-1$
write(out, HTTPSamplerBase.encodeBackSlashes(fieldname));
write(out, "\"; filename=\"");// $NON-NLS-1$
+ // TODO I think we should only include the filename, and not the full
+ // path to the file here.
write(out, HTTPSamplerBase.encodeBackSlashes(filename));
writeln(out, "\""); // $NON-NLS-1$
writeln(out, "Content-Type: " + mimetype); // $NON-NLS-1$
+ writeln(out, "Content-Transfer-Encoding: 8bit"); // $NON-NLS-1$
out.write(CRLF);
+ }
+ /**
+ * Write the content of a file to the output stream
+ *
+ * @param filename the filename of the file to write to the stream
+ * @param out the stream to write to
+ * @throws IOException
+ */
+ private void writeFileToStream(String filename, OutputStream out) throws IOException {
byte[] buf = new byte[1024];
// 1k - the previous 100k made no sense (there's tons of buffers
// elsewhere in the chain) and it caused OOM when many concurrent
// uploads were being done. Could be fixed by increasing the evacuation
// ratio in bin/jmeter[.bat], but this is better.
+ InputStream in = new BufferedInputStream(new FileInputStream(filename));
int read;
- while ((read = in.read(buf)) > 0) {
- out.write(buf, 0, read);
+ try {
+ while ((read = in.read(buf)) > 0) {
+ out.write(buf, 0, read);
+ }
}
- out.write(CRLF);
- in.close();
+ finally {
+ in.close();
+ }
}
/**
* Writes form data in multipart format.
*/
- private static void writeFormMultipartStyle(OutputStream out, String name, String value) throws IOException {
+ private void writeFormMultipart(OutputStream out, String name, String value, String charSet)
+ throws IOException {
writeln(out, "Content-Disposition: form-data; name=\"" + name + "\""); // $NON-NLS-1$ // $NON-NLS-2$
+ writeln(out, "Content-Type: text/plain; charset=" + charSet); // $NON-NLS-1$
+ writeln(out, "Content-Transfer-Encoding: 8bit"); // $NON-NLS-1$
+
out.write(CRLF);
- writeln(out, value);
+ out.write(value.getBytes(charSet));
+ out.write(CRLF);
+ // Write boundary end marker
+ out.write(getMultipartDivider());
}
- private static void write(OutputStream out, String value)
- throws UnsupportedEncodingException, IOException
- {
+ private void write(OutputStream out, String value)
+ throws UnsupportedEncodingException, IOException {
out.write(value.getBytes(encoding));
}
-
- private static void writeln(OutputStream out, String value) throws UnsupportedEncodingException, IOException {
+ private void writeln(OutputStream out, String value)
+ throws UnsupportedEncodingException, IOException {
out.write(value.getBytes(encoding));
out.write(CRLF);
}
Index: C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java
===================================================================
--- C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java (revision 522033)
+++ C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSampler2.java (working copy)
@@ -19,6 +19,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -63,12 +64,12 @@
import org.apache.commons.httpclient.params.HttpParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.jmeter.JMeter;
-import org.apache.jmeter.config.Argument;
import org.apache.jmeter.protocol.http.control.AuthManager;
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.SlowHttpClientSocketFactory;
+import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.testelement.property.PropertyIterator;
import org.apache.jmeter.util.JMeterUtils;
@@ -229,42 +230,136 @@
*
* @param connection
* URLConnection
where POST data should be sent
+ * @return a String show what was posted. Will not contain actual file upload content
* @exception IOException
* if an I/O exception occurs
*/
- private void sendPostData(PostMethod post) throws IOException {
- // If filename was specified then send the post using multipart syntax
- String filename = getFilename();
- final String contentEncoding = getContentEncoding();
- if ((filename != null) && (filename.trim().length() > 0)) {
- if (getSendFileAsPostBody()) {
- post.setRequestEntity(new FileRequestEntity(new File(filename),null));
- } else {
- int argc = getArguments().getArgumentCount();
- Part[] parts = new Part[argc+1];
- PropertyIterator args = getArguments().iterator();
- int i = 0;
- while (args.hasNext()) {
- Argument arg = (Argument) args.next().getObjectValue();
- parts[i++] = new StringPart(arg.getName(), arg.getValue());
- }
- File input = new File(filename);
- //TODO should allow charset to be defined ...
- parts[i]= new FilePart(getFileField(), input, getMimetype(), "UTF-8" );//$NON-NLS-1$
- post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams()));
- }
- } else {
- // 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
- if(contentEncoding != null && contentEncoding.trim().length() > 0) {
- post.getParams().setContentCharset(contentEncoding);
+ private String sendPostData(PostMethod post) throws IOException {
+ // Buffer to hold the post body, expect file content
+ StringBuffer postedBody = new StringBuffer(1000);
+
+ // Check if we should do a multipart/form-data or an
+ // application/x-www-form-urlencoded post request
+ if(getUseMultipartForPost()) {
+ // If a content encoding is specified, we use that es the
+ // encoding of any parameter values
+ String contentEncoding = getContentEncoding();
+ if(contentEncoding != null && contentEncoding.length() == 0) {
+ contentEncoding = null;
}
+
+ // Check how many parts we need, one for each parameter and file
+ int noParts = getArguments().getArgumentCount();
+ if(hasUploadableFiles())
+ {
+ noParts++;
+ }
+
+ // Create the parts
+ Part[] parts = new Part[noParts];
+ int partNo = 0;
+ // Add any parameters
PropertyIterator args = getArguments().iterator();
while (args.hasNext()) {
- Argument arg = (Argument) args.next().getObjectValue();
- post.addParameter(arg.getName(), arg.getValue());
+ HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
+ parts[partNo++] = new StringPart(arg.getName(), arg.getValue(), contentEncoding);
}
+
+ // Add any files
+ if(hasUploadableFiles()) {
+ File inputFile = new File(getFilename());
+ // We do not know the char set of the file to be uploaded, so we set it to null
+ ViewableFilePart filePart = new ViewableFilePart(getFileField(), inputFile, getMimetype(), null);
+ filePart.setCharSet(null); // We do not know what the char set of the file is
+ parts[partNo++] = filePart;
+ }
+
+ // Set the multipart for the post
+ MultipartRequestEntity multiPart = new MultipartRequestEntity(parts, post.getParams());
+ post.setRequestEntity(multiPart);
+
+ // Set the content type
+ String multiPartContentType = multiPart.getContentType();
+ post.setRequestHeader(HEADER_CONTENT_TYPE, multiPartContentType);
+
+ // If the Multipart is repeatable, we can send it first to
+ // our own stream, without the actual file content, so we can return it
+ if(multiPart.isRepeatable()) {
+ // For all the file multiparts, we must tell it to not include
+ // the actual file content
+ for(int i = 0; i < partNo; i++) {
+ if(parts[i] instanceof ViewableFilePart) {
+ ((ViewableFilePart) parts[i]).setHideFileData(true); // .sendMultipartWithoutFileContent(bos);
+ }
+ }
+ // Write the request to our own stream
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ multiPart.writeRequest(bos);
+ bos.flush();
+ // We get the posted bytes as UTF-8, since java is using UTF-8
+ postedBody.append(new String(bos.toByteArray() , "UTF-8")); // $NON-NLS-1$
+ bos.close();
+
+ // For all the file multiparts, we must revert the hiding of
+ // the actual file content
+ for(int i = 0; i < partNo; i++) {
+ if(parts[i] instanceof ViewableFilePart) {
+ ((ViewableFilePart) parts[i]).setHideFileData(false);
+ }
+ }
+ }
+ else {
+ postedBody.append(""); // $NON-NLS-1$
+ }
}
+ else {
+ // Set the content type
+ post.setRequestHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED);
+
+ // If there are no arguments, we can send a file as the body of the request
+ if(getArguments().getArgumentCount() == 0 && getSendFileAsPostBody()) {
+ FileRequestEntity fileRequestEntity = new FileRequestEntity(new File(getFilename()),null);
+ post.setRequestEntity(fileRequestEntity);
+
+ // We just add placeholder text for file content
+ postedBody.append(""); // $NON-NLS-1$
+ }
+ else {
+ // In an application/x-www-form-urlencoded request, we only support
+ // parameters, no file upload is allowed
+
+ // 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 the Multipart is repeatable, we can send it first to
+ // our own stream, so we can return it
+ if(post.getRequestEntity().isRepeatable()) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ post.getRequestEntity().writeRequest(bos);
+ bos.flush();
+ // We get the posted bytes as UTF-8, since java is using UTF-8
+ postedBody.append(new String(bos.toByteArray() , "UTF-8")); // $NON-NLS-1$
+ bos.close();
+ }
+ else {
+ postedBody.append(""); // $NON-NLS-1$
+ }
+ }
+ }
+ // Set the content length
+ post.setRequestHeader(HEADER_CONTENT_LENGTH, Long.toString(post.getRequestEntity().getContentLength()));
+
+ return postedBody.toString();
}
/**
@@ -586,8 +681,8 @@
client = setupConnection(url, httpMethod, res);
if (method.equals(POST)) {
- res.setQueryString(getQueryString());
- sendPostData((PostMethod)httpMethod);
+ String postBody = sendPostData((PostMethod)httpMethod);
+ res.setQueryString(postBody);
} else if (method.equals(PUT)) {
setPutHeaders((PutMethod) httpMethod);
}
@@ -740,6 +835,34 @@
}
}
}
+
+ /**
+ * Class extending FilePart, so that we can send placeholder text
+ * instead of the actual file content
+ */
+ private class ViewableFilePart extends FilePart {
+ private boolean hideFileData;
+
+ public ViewableFilePart(String name, File file, String contentType, String charset) throws FileNotFoundException {
+ super(name, file, contentType, charset);
+ this.hideFileData = false;
+ }
+
+ public void setHideFileData(boolean hideFileData) {
+ this.hideFileData = hideFileData;
+ }
+
+ protected void sendData(OutputStream out) throws IOException {
+ // Check if we should send only placeholder text for the
+ // file content, or the real file content
+ if(hideFileData) {
+ out.write("".getBytes("UTF-8"));
+ }
+ else {
+ super.sendData(out);
+ }
+ }
+ }
/**
* From the HttpMethod
, store all the "set-cookie" key-pair
Index: C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java
===================================================================
--- C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (revision 522033)
+++ C:/Documents and Settings/alf/workspace/Jmeter/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (working copy)
@@ -135,6 +135,8 @@
public final static String USE_KEEPALIVE = "HTTPSampler.use_keepalive"; // $NON-NLS-1$
public final static String FILE_NAME = "HTTPSampler.FILE_NAME"; // $NON-NLS-1$
+
+ public final static String DO_MULTIPART_POST = "HTTPSampler.DO_MULTIPART_POST"; // $NON-NLS-1$
/* Shown as Parameter Name on the GUI */
public final static String FILE_FIELD = "HTTPSampler.FILE_FIELD"; // $NON-NLS-1$
@@ -205,8 +207,10 @@
protected static final String HEADER_LOCATION = "Location"; // $NON-NLS-1$
- protected static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
-
+ protected 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$
+
// Derive the mapping of content types to parsers
private static Map parsersForType = new HashMap();
// Not synch, but it is not modified after creation
@@ -290,6 +294,23 @@
return getFileField().length()== 0 && getMimetype().length() == 0;
}
+ /**
+ * Determine if we should use multipart/form-data or
+ * application/x-www-form-urlencoded for the post
+ *
+ * @return true if multipart/form-data should be used
+ */
+ public boolean getUseMultipartForPost(){
+ // We use multipart if we have been told so, or files are present
+ // and the files should not be send as the post body
+ if(getDoMultipartPost() || (hasUploadableFiles() && !getSendFileAsPostBody())) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
public void setProtocol(String value) {
setProperty(PROTOCOL, value.toLowerCase());
}
@@ -371,6 +392,16 @@
return getPropertyAsBoolean(USE_KEEPALIVE);
}
+ public void setDoMultipartPost(boolean value) {
+ setProperty(new BooleanProperty(DO_MULTIPART_POST, value));
+ }
+
+ public boolean getDoMultipartPost() {
+ // TODO - Maybe provide a setting in the properties file
+ // to control the default value for this property
+ return getPropertyAsBoolean(DO_MULTIPART_POST, false);
+ }
+
public void setMonitor(String value) {
this.setProperty(MONITOR, value);
}
@@ -1052,6 +1083,13 @@
return newValue.toString();
}
+ /**
+ * Method to tell if the request has any files to be uploaded
+ */
+ protected boolean hasUploadableFiles() {
+ return getFilename() != null && getFilename().length() > 0;
+ }
+
public static String[] getValidMethodsAsArray(){
return (String[]) METHODLIST.toArray(new String[0]);
}
Index: C:/Documents and Settings/alf/workspace/Jmeter/test/src/org/apache/jmeter/protocol/http/sampler/PostWriterTest.java
===================================================================
--- C:/Documents and Settings/alf/workspace/Jmeter/test/src/org/apache/jmeter/protocol/http/sampler/PostWriterTest.java (revision 522033)
+++ C:/Documents and Settings/alf/workspace/Jmeter/test/src/org/apache/jmeter/protocol/http/sampler/PostWriterTest.java (working copy)
@@ -42,10 +42,12 @@
private URLConnection connection;
private HTTPSampler sampler;
private File temporaryFile;
+ private PostWriter postWriter;
protected void setUp() throws Exception {
connection = new StubURLConnection("http://fake_url/test");
sampler = new HTTPSampler();// This must be the original (Java) HTTP sampler
+ postWriter = new PostWriter();
// create a temporary file to make sure we always have a file to give to the PostWriter
// Whereever we are or Whatever the current path is.
@@ -68,7 +70,8 @@
setupFilename(sampler);
setupCommons(sampler);
- PostWriter.sendPostData(connection, sampler);
+ postWriter.setHeaders(connection, sampler);
+ postWriter.sendPostData(connection, sampler);
assertEquals(createExpectedOutputStream().toString(), connection.getOutputStream().toString());
}
@@ -80,21 +83,39 @@
setupNoFilename(sampler);
setupCommons(sampler);
- PostWriter.sendPostData(connection, sampler);
+ postWriter.setHeaders(connection, sampler);
+ postWriter.sendPostData(connection, sampler);
assertEquals("title=mytitle&description=mydescription", connection.getOutputStream().toString());
+ assertEquals("39", connection.getRequestProperty("Content-Length"));
}
/*
+ * Test method for 'org.apache.jmeter.protocol.http.sampler.PostWriter.sendPostData(URLConnection, HTTPSampler)'
+ */
+ public void testSendPostData_FormdataAsMultipart() throws IOException {
+ setupNoFilename(sampler);
+ setupCommons(sampler);
+ sampler.setDoMultipartPost(true);
+
+ postWriter.setHeaders(connection, sampler);
+ postWriter.sendPostData(connection, sampler);
+
+ String postBody = connection.getOutputStream().toString();
+ assertEquals(createExpectedOutputStreamFormData().toString(), postBody);
+ assertEquals("multipart/form-data; boundary=" + postWriter.getBoundary(), connection.getRequestProperty("Content-Type"));
+ }
+
+ /*
* Test method for 'org.apache.jmeter.protocol.http.sampler.PostWriter.setHeaders(URLConnection, HTTPSampler)'
*/
public void testSetHeaders() throws IOException {
setupFilename(sampler);
setupCommons(sampler);
- PostWriter.setHeaders(connection, sampler);
+ postWriter.setHeaders(connection, sampler);
- assertEquals("multipart/form-data; boundary=" + PostWriter.BOUNDARY, connection.getRequestProperty("Content-Type"));
+ assertEquals("multipart/form-data; boundary=" + postWriter.getBoundary(), connection.getRequestProperty("Content-Type"));
}
/*
@@ -104,7 +125,7 @@
setupNoFilename(sampler);
setupCommons(sampler);
- PostWriter.setHeaders(connection, sampler);
+ postWriter.setHeaders(connection, sampler);
assertEquals(HTTPSamplerBase.APPLICATION_X_WWW_FORM_URLENCODED, connection.getRequestProperty("Content-Type"));
assertEquals("39", connection.getRequestProperty("Content-Length"));
@@ -173,14 +194,22 @@
output.write(CRLF);
output.write("Content-Disposition: form-data; name=\"title\"".getBytes());
output.write(CRLF);
+ output.write("Content-Type: text/plain; charset=iso-8859-1".getBytes());
output.write(CRLF);
+ output.write("Content-Transfer-Encoding: 8bit".getBytes());
+ output.write(CRLF);
+ output.write(CRLF);
output.write("mytitle".getBytes());
output.write(CRLF);
output.write("-----------------------------7d159c1302d0y0".getBytes());
output.write(CRLF);
output.write("Content-Disposition: form-data; name=\"description\"".getBytes());
output.write(CRLF);
+ output.write("Content-Type: text/plain; charset=iso-8859-1".getBytes());
output.write(CRLF);
+ output.write("Content-Transfer-Encoding: 8bit".getBytes());
+ output.write(CRLF);
+ output.write(CRLF);
output.write("mydescription".getBytes());
output.write(CRLF);
output.write("-----------------------------7d159c1302d0y0".getBytes());
@@ -191,7 +220,9 @@
output.write(CRLF);
output.write("Content-Type: text/plain".getBytes());
output.write(CRLF);
+ output.write("Content-Transfer-Encoding: 8bit".getBytes());
output.write(CRLF);
+ output.write(CRLF);
output.write("foo content".getBytes());
output.write(CRLF);
output.write("-----------------------------7d159c1302d0y0--".getBytes());
@@ -202,6 +233,40 @@
}
/**
+ * Create the expected output with CRLF for the form data as multipart.
+ */
+ private OutputStream createExpectedOutputStreamFormData() throws IOException {
+ final OutputStream output = new ByteArrayOutputStream();
+ output.write("-----------------------------7d159c1302d0y0".getBytes());
+ output.write(CRLF);
+ output.write("Content-Disposition: form-data; name=\"title\"".getBytes());
+ output.write(CRLF);
+ output.write("Content-Type: text/plain; charset=iso-8859-1".getBytes());
+ output.write(CRLF);
+ output.write("Content-Transfer-Encoding: 8bit".getBytes());
+ output.write(CRLF);
+ output.write(CRLF);
+ output.write("mytitle".getBytes());
+ output.write(CRLF);
+ output.write("-----------------------------7d159c1302d0y0".getBytes());
+ output.write(CRLF);
+ output.write("Content-Disposition: form-data; name=\"description\"".getBytes());
+ output.write(CRLF);
+ output.write("Content-Type: text/plain; charset=iso-8859-1".getBytes());
+ output.write(CRLF);
+ output.write("Content-Transfer-Encoding: 8bit".getBytes());
+ output.write(CRLF);
+ output.write(CRLF);
+ output.write("mydescription".getBytes());
+ output.write(CRLF);
+ output.write("-----------------------------7d159c1302d0y0--".getBytes());
+ output.write(CRLF);
+ output.flush();
+ output.close();
+ return output;
+ }
+
+ /**
* Mock an HttpURLConnection.
* extends HttpURLConnection instead of just URLConnection because there is a cast in PostWriter.
*/