olegk 2003/02/12 04:30:44 Modified: httpclient/src/java/org/apache/commons/httpclient/methods MultipartPostMethod.java httpclient/src/java/org/apache/commons/httpclient/methods/multipart FilePart.java Part.java StringPart.java httpclient/src/test/org/apache/commons/httpclient TestWebapp.java Added: httpclient/src/test/org/apache/commons/httpclient TestWebappMultiPostMethod.java Log: Bug fix: http://nagoya.apache.org/bugzilla/show_bug.cgi?id=14782 Change log: - MultipartPost clean-up - Part#send(OutputStream out) & long Part#length() are no longer final - Custom character set can be specified for all multipart classes - Custom content type can be specified for FilePart derived multipart classes Contributed by Oleg Kalnichevski Revision Changes Path 1.9 +28 -30 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/MultipartPostMethod.java Index: MultipartPostMethod.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/MultipartPostMethod.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- MultipartPostMethod.java 2 Feb 2003 04:30:13 -0000 1.8 +++ MultipartPostMethod.java 12 Feb 2003 12:30:44 -0000 1.9 @@ -94,6 +94,10 @@ */ public class MultipartPostMethod extends GetMethod { + /** The Content-Type for multipart/form-data. */ + public static final String MULTIPART_FORM_CONTENT_TYPE = + "multipart/form-data"; + /** Log object for this class. */ private static final Log LOG = LogFactory.getLog(MultipartPostMethod.class); @@ -204,6 +208,14 @@ } /** + * Return all parts. + * + * @return an array of containing all parts + */ + public Part[] getParts() { + return (Part[])parameters.toArray(new Part[parameters.size()]); + } + /** * Add a request header. * * @param state the client state @@ -218,8 +230,12 @@ super.addRequestHeaders(state, conn); if (!parameters.isEmpty()) { - setRequestHeader("Content-Type", - "multipart/form-data; boundary=" + Part.getBoundary()); + StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE); + if (Part.getBoundary() != null) { + buffer.append("; boundary="); + buffer.append(Part.getBoundary()); + } + setRequestHeader("Content-Type", buffer.toString()); } } @@ -237,16 +253,7 @@ throws IOException, HttpException { LOG.trace("enter writeRequestBody(HttpState state, HttpConnection conn)"); OutputStream out = conn.getRequestOutputStream(); - - for (Iterator it = parameters.iterator(); it.hasNext();) { - final Part part = (Part) it.next(); - part.send(out); - } - - Part.sendLastBoundary(out); - - out.flush(); - + Part.sendParts(out, getParts()); return true; } @@ -260,26 +267,17 @@ */ protected int getRequestContentLength() { LOG.trace("enter getRequestContentLength()"); - long length = 0; - try { - for (Iterator it = parameters.iterator(); it.hasNext();) { - final Part part = (Part) it.next(); - - length += part.length(); + long len = Part.getLengthOfParts(getParts()); + // Chop the length to the max int value. + if (len <= Integer.MAX_VALUE) { + return (int)len; + } else { + return (Integer.MAX_VALUE); } - length += Part.lengthOfLastBoundary(); - } catch (IOException e) { // Can't throw an IOException and still override throw new RuntimeException(e.toString()); - } - - // Chop the length to the max int value. - if (length <= Integer.MAX_VALUE) { - return ((new Long(length)).intValue()); - } else { - return (Integer.MAX_VALUE); } } } 1.11 +142 -71 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java Index: FilePart.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- FilePart.java 28 Jan 2003 22:25:31 -0000 1.10 +++ FilePart.java 12 Feb 2003 12:30:44 -0000 1.11 @@ -82,27 +82,84 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Michael Becke</a> * @author <a href="mailto:[EMAIL PROTECTED]">Mark Diggory</a> * @author <a href="mailto:[EMAIL PROTECTED]">Mike Bowler</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Oleg Kalnichevski</a> * * @since 2.0 * */ public class FilePart extends Part { + /** Default content encoding of file attachments. */ + public static final String DEFAULT_CONTENT_TYPE = "application/octet-stream"; + + /** Default charset of file attachments. */ + public static final String DEFAULT_CHARSET = HttpConstants.DEFAULT_CONTENT_CHARSET; + + /** Default transfer encoding of file attachments. */ + public static final String DEFAULT_TRANSFER_ENCODING = "binary"; + /** Log object for this class. */ private static final Log LOG = LogFactory.getLog(FilePart.class); - /** - * <p>The maximum buffer size.</p> - * TODO: make this configurable - */ - private static final int MAX_BUFF_SIZE = 1 * 1024 * 1024; // 1 MiBs - + + /** Attachment's file name */ + protected static final String FILE_NAME = "; filename="; + + /** Attachment's file name as a byte array */ + protected static final byte[] FILE_NAME_BYTES = + HttpConstants.getAsciiBytes(FILE_NAME); + /** Name of the file part. */ private String name; /** Source of the file part. */ private PartSource source; + /** Content type of the file part. */ + private String contentType; + + /** Content encoding of the file part. */ + private String charset; + + /** + * FilePart Constructor. + * + * @param name the name for this part + * @param partSource the source for this part + * @param contentType the content type for this part + * @param charset the charset encoding for this part + */ + public FilePart(String name, PartSource partSource, String contentType, String charset) { + LOG.trace("enter FilePart(String, PartSource, String, String)"); + if (name == null) { + throw new IllegalArgumentException("Name may not be null"); + } + this.name = name; + if (partSource == null) { + throw new IllegalArgumentException("Source may not be null"); + } + if (partSource.getLength() < 0) { + throw new IllegalArgumentException("Source length must be >= 0"); + } + this.source = partSource; + if (contentType != null) { + this.contentType = contentType; + } else { + this.contentType = DEFAULT_CONTENT_TYPE; + } + this.charset = charset; + } + + /** + * FilePart Constructor. + * + * @param name the name for this part + * @param partSource the source for this part + */ + public FilePart(String name, PartSource partSource) { + this(name, partSource, null, null); + } + /** * FilePart Constructor. * @@ -114,7 +171,23 @@ */ public FilePart(String name, File file) throws FileNotFoundException { - this(name, new FilePartSource(file)); + this(name, new FilePartSource(file), null, null); + } + + /** + * FilePart Constructor. + * + * @param name the name of the file part + * @param file the file to post + * @param contentType the content type for the file + * @param charset the charset encoding of the file + * + * @throws FileNotFoundException if the <i>file</i> is not a normal + * file or if it is not readable. + */ + public FilePart(String name, File file, String contentType, String charset) + throws FileNotFoundException { + this(name, new FilePartSource(file), contentType, charset); } /** @@ -129,71 +202,77 @@ */ public FilePart(String name, String fileName, File file) throws FileNotFoundException { - this(name, new FilePartSource(fileName, file)); + this(name, new FilePartSource(fileName, file), null, null); } - /** + /** * FilePart Constructor. * - * @param name the name for this part - * @param partSource the source for this part + * @param name the name of the file part + * @param fileName the file name + * @param file the file to post + * @param contentType the content type for the file + * @param charset the charset encoding of the file + * + * @throws FileNotFoundException if the <i>file</i> is not a normal + * file or if it is not readable. */ - public FilePart(String name, PartSource partSource) { - - if (partSource.getLength() < 0) { - throw new IllegalArgumentException("fileLength must be >= 0"); - } - - this.name = name; - this.source = partSource; - + public FilePart(String name, String fileName, File file, String contentType, String charset) + throws FileNotFoundException { + this(name, new FilePartSource(fileName, file), contentType, charset); } - + /** - * Write the header to the output stream - * @param out The output stream - * @throws IOException If an IO problem occurs - * @see org.apache.commons.httpclient.methods.multipart.Part#sendHeader(OutputStream) + * Return the name. + * @return The name. + * @see org.apache.commons.httpclient.methods.multipart.Part#getName() */ - protected void sendHeader(OutputStream out) - throws IOException { - LOG.trace("enter sendHeader(OutputStream out)"); - super.sendHeader(out); - sendFilename(out); - sendContentType(out); + public String getName() { + return this.name; } - + /** - * Write the filename to the output stream - * @param out The output stream - * @throws IOException If an IO problem occurs + * Return the content type of this part. + * @return String The name. */ - protected void sendFilename(OutputStream out) - throws IOException { - LOG.trace("enter sendFilename(OutputStream out)"); - String filename = "; filename=\"" + source.getFileName() + "\""; - out.write(HttpConstants.getBytes(filename)); + public String getContentType() { + return this.contentType; } /** - * Write the Content-Type header to the output stream - * @param out The output stream. - * @throws IOException If an IO problem occurs + * Return the character encoding of this part. + * @return String The name. */ - protected void sendContentType(OutputStream out) - throws IOException { - LOG.trace("enter sendContentType(OutputStream out)"); - out.write(CRLF_BYTES); - out.write(HttpConstants.getBytes("Content-Type: application/octet-stream")); - } + public String getCharSet() { + return this.charset; + } /** - * Return the name. - * @return The name. - * @see org.apache.commons.httpclient.methods.multipart.Part#getName() + * Return the transfer encoding of this part. + * @return String The name. */ - public String getName() { - return name; + + public String getTransferEncoding() { + return DEFAULT_TRANSFER_ENCODING; + } + + /** + * Write the disposition header to the output stream + * @param out The output stream + * @throws IOException If an IO problem occurs + * @see org.apache.commons.httpclient.methods.multipart.Part#sendHeader(OutputStream) + */ + protected void sendDispositionHeader(OutputStream out) + throws IOException { + LOG.trace("enter sendDispositionHeader(OutputStream out)"); + super.sendDispositionHeader(out); + String filename = this.source.getFileName(); + if (filename != null) { + out.write(FILE_NAME_BYTES); + out.write(QUOTE_BYTES); + out.write(HttpConstants.getAsciiBytes(filename)); + out.write(QUOTE_BYTES); + } } /** @@ -204,9 +283,6 @@ */ protected void sendData(OutputStream out) throws IOException { LOG.trace("enter sendData(OutputStream out)"); - - byte[] buff; - if (lengthOfData() == 0) { // this file contains no data, so there is nothing to send. @@ -214,20 +290,15 @@ // cause an infinite loop when reading. LOG.debug("No data to send."); return; - - } else if (lengthOfData() > MAX_BUFF_SIZE) { - buff = new byte[MAX_BUFF_SIZE]; - } else { - buff = new byte[(new Long(lengthOfData())).intValue()]; } - InputStream is = source.createInputStream(); + byte[] tmp = new byte[4096]; + InputStream instream = source.createInputStream(); int len; - while ((len = is.read(buff)) != -1) { - out.write(buff, 0, len); + while ((len = instream.read(tmp)) >= 0) { + out.write(tmp, 0, len); } - } /** @@ -237,8 +308,8 @@ * @see org.apache.commons.httpclient.methods.multipart.Part#lengthOfData() */ protected long lengthOfData() throws IOException { + LOG.trace("enter lengthOfData()"); return source.getLength(); } -} - +} \ No newline at end of file 1.8 +173 -104 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java Index: Part.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- Part.java 28 Jan 2003 22:25:31 -0000 1.7 +++ Part.java 12 Feb 2003 12:30:44 -0000 1.8 @@ -63,9 +63,11 @@ package org.apache.commons.httpclient.methods.multipart; +import java.io.IOException; import java.io.OutputStream; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.IOException; + import org.apache.commons.httpclient.HttpConstants; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -77,6 +79,7 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Jeff Dever</a> * @author <a href="mailto:[EMAIL PROTECTED]">Adrian Sutton</a> * @author <a href="mailto:[EMAIL PROTECTED]">Mike Bowler</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Oleg Kalnichevski</a> * * @since 2.0 */ @@ -88,23 +91,59 @@ //TODO: Make this configurable /** The boundary */ - private static final String BOUNDARY = "----------------314159265358979323846"; + protected static final String BOUNDARY = "----------------314159265358979323846"; /** The boundary as a byte array */ - private static final byte[] BOUNDARY_BYTES = HttpConstants.getBytes(BOUNDARY); + protected static final byte[] BOUNDARY_BYTES = HttpConstants.getAsciiBytes(BOUNDARY); /** Carriage return/linefeed */ - private static final String CRLF = "\r\n"; + protected static final String CRLF = "\r\n"; /** Carriage return/linefeed as a byte array */ - protected static final byte[] CRLF_BYTES = HttpConstants.getBytes(CRLF); + protected static final byte[] CRLF_BYTES = HttpConstants.getAsciiBytes(CRLF); + + /** Content dispostion characters */ + protected static final String QUOTE = "\""; + /** Content dispostion as a byte array */ + protected static final byte[] QUOTE_BYTES = + HttpConstants.getAsciiBytes(QUOTE); + /** Extra characters */ - private static final String EXTRA = "--"; + protected static final String EXTRA = "--"; /** Extra characters as a byte array */ - private static final byte[] EXTRA_BYTES = HttpConstants.getBytes(EXTRA); + protected static final byte[] EXTRA_BYTES = + HttpConstants.getAsciiBytes(EXTRA); + + /** Content dispostion characters */ + protected static final String CONTENT_DISPOSITION = "Content-Disposition: form-data; name="; + /** Content dispostion as a byte array */ + protected static final byte[] CONTENT_DISPOSITION_BYTES = + HttpConstants.getAsciiBytes(CONTENT_DISPOSITION); + + /** Content type header */ + protected static final String CONTENT_TYPE = "Content-Type: "; + + /** Content type header as a byte array */ + protected static final byte[] CONTENT_TYPE_BYTES = + HttpConstants.getAsciiBytes(CONTENT_TYPE); + + /** Content charset */ + protected static final String CHARSET = "; charset="; + + /** Content charset as a byte array */ + protected static final byte[] CHARSET_BYTES = + HttpConstants.getAsciiBytes(CHARSET); + + /** Content type header */ + protected static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding: "; + + /** Content type header as a byte array */ + protected static final byte[] CONTENT_TRANSFER_ENCODING_BYTES = + HttpConstants.getAsciiBytes(CONTENT_TRANSFER_ENCODING); + /** * Return the boundary string. * @return the boundary string @@ -114,40 +153,29 @@ } /** - * Write the last boundary to the specified output stream - * @param out The output stream - * @throws IOException If an IO problem occurs. + * Return the name of this part. + * @return String The name. */ - public static void sendLastBoundary(OutputStream out) - throws IOException { - LOG.trace("enter sendLastBoundary(OutputStream out)"); - out.write(EXTRA_BYTES); - out.write(BOUNDARY_BYTES); - out.write(EXTRA_BYTES); - out.write(CRLF_BYTES); - } + public abstract String getName(); /** - * Return the length of the last boundary string - * - * @return int The length of the last boundary string - * @throws IOException If an IO problem occurs + * Return the content type of this part. + * @return String The name. */ - public static int lengthOfLastBoundary() throws IOException { - LOG.trace("enter lengthOfLastBoundary()"); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - sendLastBoundary(out); - - return out.size(); - } - + public abstract String getContentType(); + /** - * Return the name of this part. + * Return the character encoding of this part. * @return String The name. */ - public abstract String getName(); - + public abstract String getCharSet(); + + /** + * Return the transfer encoding of this part. + * @return String The name. + */ + public abstract String getTransferEncoding(); + /** * Write the start to the specified output stream * @param out The output stream @@ -161,44 +189,58 @@ } /** - * Return the length of the starting data - * @return int The length of the data - * @throws IOException If an IO problem occurs + * Write the content disposition header to the specified output stream + * + * @param out The output stream + * @throws IOException If an IO problem occurs. */ - protected int lengthOfStart() throws IOException { - LOG.trace("enter lengthOfStart()"); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - sendStart(out); - return out.size(); + protected void sendDispositionHeader(OutputStream out) throws IOException { + LOG.trace("enter sendDispositionHeader(OutputStream out)"); + out.write(CONTENT_DISPOSITION_BYTES); + out.write(QUOTE_BYTES); + out.write(HttpConstants.getAsciiBytes(getName())); + out.write(QUOTE_BYTES); } /** - * Write the header to the specified output stream + * Write the content type header to the specified output stream * @param out The output stream * @throws IOException If an IO problem occurs. */ - protected void sendHeader(OutputStream out) throws IOException { - LOG.trace("enter sendHeader(OutputStream out)"); - String contentDisposition = "Content-Disposition: form-data; name=\"" - + getName() + "\""; - - out.write(HttpConstants.getBytes(contentDisposition)); + + protected void sendContentTypeHeader(OutputStream out) throws IOException { + LOG.trace("enter sendContentTypeHeader(OutputStream out)"); + String contentType = getContentType(); + if (contentType != null) { + out.write(CRLF_BYTES); + out.write(CONTENT_TYPE_BYTES); + out.write(HttpConstants.getAsciiBytes(contentType)); + String charSet = getCharSet(); + if (charSet != null) { + out.write(CHARSET_BYTES); + out.write(HttpConstants.getAsciiBytes(charSet)); + } + } } - + /** - * Return the length of the header + * Write the content transfer encoding header to the specified + * output stream * - * @return long The length. - * @throws IOException If an IO problem occurs + * @param out The output stream + * @throws IOException If an IO problem occurs. */ - protected int lengthOfHeader() throws IOException { - LOG.trace("enter lengthOfHeader()"); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - sendHeader(out); - return (out.size()); + + protected void sendTransferEncodingHeader(OutputStream out) throws IOException { + LOG.trace("enter sendTransferEncodingHeader(OutputStream out)"); + String transferEncoding = getTransferEncoding(); + if (transferEncoding != null) { + out.write(CRLF_BYTES); + out.write(CONTENT_TRANSFER_ENCODING_BYTES); + out.write(HttpConstants.getAsciiBytes(transferEncoding)); + } } - - + /** * Write the end of the header to the output stream * @param out The output stream @@ -211,20 +253,6 @@ } /** - * Return the length of the end of header - * - * @return long The length. - * @throws IOException If an IO problem occurs - */ - protected int lengthOfEndOfHeader() throws IOException { - LOG.trace("enter lengthOfEndOfHeader()"); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - sendEndOfHeader(out); - return out.size(); - } - - - /** * Write the data to the specified output stream * @param out The output stream * @throws IOException If an IO problem occurs. @@ -250,50 +278,43 @@ } /** - * Return the length of the end data - * - * @return long The length. - * @throws IOException If an IO problem occurs - */ - protected int lengthOfEnd() throws IOException { - LOG.trace("enter lengthOfEnd()"); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - sendEnd(out); - return out.size(); - } - - /* The following 2 methods don't need to be final, but they DO need - * to be overridden as a pair, and the only way to make sure of that - * is to make sure they AREN'T overridden. - */ - - /** * Write all the data to the output stream. + * If you override this method make sure to override + * #length() as well + * * @param out The output stream * @throws IOException If an IO problem occurs. */ - public final void send(OutputStream out) throws IOException { + public void send(OutputStream out) throws IOException { LOG.trace("enter send(OutputStream out)"); sendStart(out); - sendHeader(out); + sendDispositionHeader(out); + sendContentTypeHeader(out); + sendTransferEncodingHeader(out); sendEndOfHeader(out); sendData(out); sendEnd(out); } - + + /** * Return the full length of all the data. + * If you override this method make sure to override + * #send(OutputStream) as well * * @return long The length. * @throws IOException If an IO problem occurs */ - public final long length() throws IOException { + public long length() throws IOException { LOG.trace("enter length()"); - return lengthOfStart() - + lengthOfHeader() - + lengthOfEndOfHeader() - + lengthOfData() - + lengthOfEnd(); + ByteArrayOutputStream overhead = new ByteArrayOutputStream(); + sendStart(overhead); + sendDispositionHeader(overhead); + sendContentTypeHeader(overhead); + sendTransferEncodingHeader(overhead); + sendEndOfHeader(overhead); + sendEnd(overhead); + return overhead.size() + lengthOfData(); } /** @@ -304,4 +325,52 @@ public String toString() { return this.getName(); } + + + /** + * Write all parts and the last boundary to the specified output stream + * + * @param out The output stream + * @param parts The array of parts to be sent + * + * @throws IOException If an IO problem occurs. + */ + public static void sendParts(OutputStream out, final Part[] parts) + throws IOException { + LOG.trace("enter sendParts(OutputStream out, Parts[])"); + if (parts == null) { + throw new IllegalArgumentException("Parts may not be null"); + } + for (int i = 0; i < parts.length; i++) { + parts[i].send(out); + } + out.write(EXTRA_BYTES); + out.write(BOUNDARY_BYTES); + out.write(EXTRA_BYTES); + out.write(CRLF_BYTES); + } + + /** + * Return the total sum of all parts and that of the last boundary + * + * @param parts The array of parts + * + * @throws IOException If an IO problem occurs. + */ + public static long getLengthOfParts(final Part[] parts) + throws IOException { + LOG.trace("getLengthOfParts(Parts[])"); + if (parts == null) { + throw new IllegalArgumentException("Parts may not be null"); + } + long total = 0; + for (int i = 0; i < parts.length; i++) { + total += parts[i].length(); + } + total += EXTRA_BYTES.length; + total += BOUNDARY_BYTES.length; + total += EXTRA_BYTES.length; + total += CRLF_BYTES.length; + return total; + } } 1.6 +79 -13 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java Index: StringPart.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- StringPart.java 28 Jan 2003 22:25:31 -0000 1.5 +++ StringPart.java 12 Feb 2003 12:30:44 -0000 1.6 @@ -66,6 +66,8 @@ import java.io.OutputStream; import java.io.IOException; import org.apache.commons.httpclient.HttpConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Simple string parameter for a multipart post @@ -73,26 +75,69 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Matthew Albright</a> * @author <a href="mailto:[EMAIL PROTECTED]">Jeff Dever</a> * @author <a href="mailto:[EMAIL PROTECTED]">Mike Bowler</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Oleg Kalnichevski</a> * * @since 2.0 */ public class StringPart extends Part { + /** Log object for this class. */ + private static final Log LOG = LogFactory.getLog(StringPart.class); + + /** Default content encoding of string parameters. */ + public static final String DEFAULT_CONTENT_TYPE = "text/plain"; + + /** Default charset of string parameters*/ + public static final String DEFAULT_CHARSET = "US-ASCII"; + + /** Default transfer encoding of string parameters*/ + public static final String DEFAULT_TRANSFER_ENCODING = "8bit"; + /** Name of this StringPart. */ private String name; /** Contents of this StringPart. */ - private String value; + private byte[] content; + + /** Charset of this StringPart. */ + private String charset; /** * Constructor. * * @param name The name of the part * @param value the string to post + * @param charset the charset to be used to encode the string */ - public StringPart(String name, String value) { + public StringPart(String name, String value, String charset) { + LOG.trace("enter StringPart(String, String, String)"); + if (name == null) { + throw new IllegalArgumentException("Name may not be null"); + } this.name = name; - this.value = value; + if (charset != null) { + this.charset = charset; + } else { + this.charset = DEFAULT_CHARSET; + } + if (value == null) { + throw new IllegalArgumentException("Value may not be null"); + } + if (value.indexOf(0) != -1) { + // See RFC 2048, 2.8. "8bit Data" + throw new IllegalArgumentException("NULs may not be present in string parts"); + } + this.content = HttpConstants.getContentBytes(value, this.charset); + } + + /** + * Constructor. + * + * @param name The name of the part + * @param value the string to post + */ + public StringPart(String name, String value) { + this(name, value, null); } /** @@ -102,15 +147,35 @@ public String getName() { return name; } - + /** - * Write the data to the specified output stream - * @param out The output stream. - * @throws IOException If an IO problem occurs - * @see org.apache.commons.httpclient.methods.multipart.Part#sendData(OutputStream) + * Return the content type of this part. + * @return String The name. + */ + public String getContentType() { + return DEFAULT_CONTENT_TYPE; + } + + /** + * Return the character encoding of this part. + * @return String The name. */ + public String getCharSet() { + return this.charset; + } + + /** + * Return the transfer encoding of this part. + * @return String The name. + */ + + public String getTransferEncoding() { + return DEFAULT_TRANSFER_ENCODING; + } + protected void sendData(OutputStream out) throws IOException { - out.write(HttpConstants.getBytes(value)); + LOG.trace("enter sendData(OutputStream)"); + out.write(this.content); } /** @@ -120,6 +185,7 @@ * @see org.apache.commons.httpclient.methods.multipart.Part#lengthOfData() */ protected long lengthOfData() throws IOException { - return HttpConstants.getBytes(value).length; + LOG.trace("enter lengthOfData()"); + return this.content.length; } } 1.7 +5 -4 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebapp.java Index: TestWebapp.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebapp.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- TestWebapp.java 1 Feb 2003 16:10:48 -0000 1.6 +++ TestWebapp.java 12 Feb 2003 12:30:44 -0000 1.7 @@ -100,6 +100,7 @@ suite.addTest(TestWebappBasicAuth.suite()); suite.addTest(TestWebappCookie.suite()); suite.addTest(TestWebappPostMethod.suite()); + suite.addTest(TestWebappMultiPostMethod.suite()); suite.addTest(TestWebappNoncompliant.suite()); return suite; } 1.1 jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebappMultiPostMethod.java Index: TestWebappMultiPostMethod.java =================================================================== /* * $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebappMultiPostMethod.java,v 1.1 2003/02/12 12:30:44 olegk Exp $ * $Revision: 1.1 $ * $Date: 2003/02/12 12:30:44 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.commons.httpclient; import junit.framework.*; import org.apache.commons.httpclient.methods.*; import org.apache.commons.httpclient.methods.multipart.*; import java.io.*; /** * Webapp tests specific to the MultiPostMethod. * * @author <a href="[EMAIL PROTECTED]">Oleg Kalnichevski</a> */ public class TestWebappMultiPostMethod extends TestWebappBase { HttpClient httpClient; final static String paramsUrl = "http://" + host + ":" + port + "/" + context + "/params"; final static String bodyUrl = "http://" + host + ":" + port + "/" + context + "/body"; public TestWebappMultiPostMethod(String testName) { super(testName); } public static Test suite() { TestSuite suite = new TestSuite(TestWebappMultiPostMethod.class); return suite; } public static void main(String args[]) { String[] testCaseName = { TestWebappMultiPostMethod.class.getName() }; junit.textui.TestRunner.main(testCaseName); } public void setUp() { httpClient = new HttpClient(); } // ------------------------------------------------------------------ Tests /** * Test that the body consisting of a string part can be posted. */ public void testPostStringPart() throws Exception { MultipartPostMethod method = new MultipartPostMethod(bodyUrl); method.addPart(new StringPart("param", "Hello", "ISO-8859-1")); httpClient.executeMethod(method); assertEquals(200,method.getStatusCode()); String body = method.getResponseBodyAsString(); assertTrue(body.indexOf("Content-Disposition: form-data; name=\"param\"") >= 0); assertTrue(body.indexOf("Content-Type: text/plain; charset=ISO-8859-1") >= 0); assertTrue(body.indexOf("Content-Transfer-Encoding: 8bit") >= 0); assertTrue(body.indexOf("Hello") >= 0); } /** * Test that the body consisting of a file part can be posted. */ public void testPostFilePart() throws Exception { MultipartPostMethod method = new MultipartPostMethod(bodyUrl); byte[] content = new byte[] {'H', 'e', 'l', 'l', 'o' }; method.addPart( new FilePart( "param1", new ByteArrayPartSource("filename.txt", content), "text/plain", "ISO-8859-1")); httpClient.executeMethod(method); assertEquals(200,method.getStatusCode()); String body = method.getResponseBodyAsString(); assertTrue(body.indexOf("Content-Disposition: form-data; name=\"param1\"; filename=\"filename.txt\"") >= 0); assertTrue(body.indexOf("Content-Type: text/plain; charset=ISO-8859-1") >= 0); assertTrue(body.indexOf("Content-Transfer-Encoding: binary") >= 0); assertTrue(body.indexOf("Hello") >= 0); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]