hc-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Jeffrey Dever <jsde...@sympatico.ca>
Subject Re: [PATCH] MultipartPost revisited (take 1)
Date Tue, 11 Feb 2003 13:51:30 GMT
Yea.  Java does not have all the answers to visibility needs.  What we 
really need is a "jar" access level.  Perhaps we'll get that in java 
version 3 ;-)

Jandalf.

Oleg Kalnichevski wrote:

>Jeff,
>
>Wire, WireLogOutputStream, WireLogInputStream classes need to be
>accessible by classes from the following packages:
>org.apache.commons.httpclient
>org.apache.commons.httpclient.methods
>org.apache.commons.httpclient.methods.multipart
>
>Making wire logging related classes non-public does not seem feasible
>with the present package structure. 
>
>Cheers
>
>Oleg
>
>On Tue, 2003-02-11 at 04:19, Jeffrey Dever wrote:
>  
>
>>I like the refactoring. Only complaint is that the classes are public. 
>>Package access would be prefered, but this may not be possible.
>>
>>
>>Oleg Kalnichevski wrote:
>>
>>    
>>
>>>Fixes the following bugs:
>>>
>>>http://nagoya.apache.org/bugzilla/show_bug.cgi?id=14782
>>>
>>>Change log:
>>>- MultipartPost related methods clean-up
>>>- Part#send(OutputStream out) &  long Part#length() are no more final
>>>- Content encoding can be specified 
>>>- FilePart's content type can be specified 
>>>- FilePart's transfer encoding can be specified 
>>>- Wire logging improvement
>>>
>>>While working on multi-part post stuff I have been through many pains
>>>debugging modified classes and trying to figure out what exactly gets
>>>sent to the server. Currently wire logging can be regarded as
>>>inconsistent at best. In those parts where wire logging is implemented I
>>>find it not particularly visually appealing. While "scratching my own
>>>itch" I have taken liberty of rewriting wire logging. Please comment if
>>>you find these changes acceptable. Suggestions on possible improvements
>>>are welcome.
>>>
>>>To be done next:
>>>
>>>- Fix bug #14036
>>>- Apply new wire logging consistently throughout the HttpClient codebase
>>>(provided new wire logging meets your approval)
>>> 
>>>
>>>------------------------------------------------------------------------
>>>
>>>Index: src/java/org/apache/commons/httpclient/HttpConnection.java
>>>===================================================================
>>>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v
>>>retrieving revision 1.42
>>>diff -u -r1.42 HttpConnection.java
>>>--- src/java/org/apache/commons/httpclient/HttpConnection.java	8 Feb 2003 19:22:49 -0000	1.42
>>>+++ src/java/org/apache/commons/httpclient/HttpConnection.java	10 Feb 2003 22:02:08 -0000
>>>@@ -75,6 +75,7 @@
>>>import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
>>>import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
>>>import org.apache.commons.httpclient.util.TimeoutController;
>>>+import org.apache.commons.httpclient.util.Wire;
>>>import org.apache.commons.logging.Log;
>>>import org.apache.commons.logging.LogFactory;
>>>
>>>@@ -727,9 +728,8 @@
>>>
>>>        assertOpen();
>>>
>>>-        if (WIRE_LOG.isDebugEnabled()) {
>>>-            String dataString = new String(data, offset, length, "ISO-8859-1");
>>>-            WIRE_LOG.debug(">> \"" + dataString + "\" [\\r\\n]");
>>>+        if (Wire.enabled()) {
>>>+            Wire.output(data, offset, length, HttpConstants.HTTP_ELEMENT_CHARSET);
>>>        }
>>>        try {
>>>            outputStream.write(data, offset, length);
>>>@@ -758,9 +758,8 @@
>>>        LOG.trace("enter HttpConnection.writeLine(byte[])");
>>>
>>>        assertOpen();
>>>-        if (WIRE_LOG.isDebugEnabled() && (data.length > 0)) {
>>>-            String dataString = HttpConstants.getContentString(data);
>>>-            WIRE_LOG.debug(">> \"" + dataString.trim() + "\" [\\r\\n]");
>>>+        if (Wire.enabled()) {
>>>+            Wire.output(data, HttpConstants.HTTP_ELEMENT_CHARSET);
>>>        }
>>>        try {
>>>            outputStream.write(data);
>>>@@ -786,7 +785,9 @@
>>>        throws IOException, IllegalStateException, HttpRecoverableException {
>>>        LOG.trace("enter HttpConnection.writeLine()");
>>>
>>>-        WIRE_LOG.debug(">> [\\r\\n]");
>>>+        if (Wire.enabled()) {
>>>+            Wire.output(CRLF, HttpConstants.HTTP_ELEMENT_CHARSET);
>>>+        }
>>>        try {
>>>            outputStream.write(CRLF);
>>>        } catch (SocketException se) {
>>>@@ -871,8 +872,8 @@
>>>            buf.append((char) ch);
>>>            ch = inputStream.read();
>>>        }
>>>-        if (WIRE_LOG.isDebugEnabled()) {
>>>-            WIRE_LOG.debug("<< \"" + buf.toString() + (ch>0 ? "\" [\\r\\n]" : ""));
>>>+        if (Wire.enabled()) {
>>>+            Wire.input(HttpConstants.getBytes(buf.toString()), HttpConstants.HTTP_ELEMENT_CHARSET);
>>>        }
>>>        return (buf.toString());
>>>    }
>>>@@ -1059,9 +1060,6 @@
>>>
>>>    /** Log object for this class. */
>>>    private static final Log LOG = LogFactory.getLog(HttpConnection.class);
>>>-    
>>>-    /** Log for any wire messages. */
>>>-    private static final Log WIRE_LOG = LogFactory.getLog("httpclient.wire");
>>>    
>>>    // ----------------------------------------------------- Instance Variables    
>>>    /** My host. */
>>>Index: src/java/org/apache/commons/httpclient/HttpMethodBase.java
>>>===================================================================
>>>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v
>>>retrieving revision 1.110
>>>diff -u -r1.110 HttpMethodBase.java
>>>--- src/java/org/apache/commons/httpclient/HttpMethodBase.java	8 Feb 2003 19:22:49 -0000	1.110
>>>+++ src/java/org/apache/commons/httpclient/HttpMethodBase.java	10 Feb 2003 22:02:13 -0000
>>>@@ -1752,7 +1752,7 @@
>>>        Header transferEncodingHeader = getResponseHeader("Transfer-Encoding");
>>>        InputStream is = conn.getResponseInputStream();
>>>        if (WIRE_LOG.isDebugEnabled()) {
>>>-            is = new WireLogInputStream(is);
>>>+            is = new WireLogInputStream(is, getResponseCharSet());
>>>        }
>>>        InputStream result = null;
>>>        // We use Transfer-Encoding if present and ignore Content-Length.
>>>Index: src/java/org/apache/commons/httpclient/WireLogInputStream.java
>>>===================================================================
>>>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/WireLogInputStream.java,v
>>>retrieving revision 1.8
>>>diff -u -r1.8 WireLogInputStream.java
>>>--- src/java/org/apache/commons/httpclient/WireLogInputStream.java	31 Jan 2003 00:33:36 -0000	1.8
>>>+++ src/java/org/apache/commons/httpclient/WireLogInputStream.java	10 Feb 2003 22:02:13 -0000
>>>@@ -68,26 +68,38 @@
>>>import java.io.InputStream;
>>>import org.apache.commons.logging.Log;
>>>import org.apache.commons.logging.LogFactory;
>>>+import org.apache.commons.httpclient.util.Wire; 
>>>
>>>/**
>>> * Logs all data read to the wire LOG.
>>> *
>>> * @author Ortwin Gl?©?
>>> * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
>>>+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
>>> * 
>>> * @since 2.0
>>> */
>>>
>>>class WireLogInputStream extends FilterInputStream {
>>>+
>>>    /** Log for any wire messages. */
>>>    private static final Log WIRE_LOG = LogFactory.getLog("httpclient.wire");
>>>
>>>+    /** Content encoding. */
>>>+    private String charset = "US-ASCII";
>>>+    
>>>+    /** Original input stream. */
>>>+    private InputStream in;    
>>>    /**
>>>     * Create an instance that wraps the specified input stream.
>>>     * @param in The input stream.
>>>     */
>>>-    public WireLogInputStream(InputStream in) {
>>>+    public WireLogInputStream(InputStream in, String charset) {
>>>        super(in);
>>>+        if (charset != null) {
>>>+            this.charset = charset;
>>>+        }
>>>+        this.in = in;
>>>    }
>>>
>>>    /**
>>>@@ -95,8 +107,10 @@
>>>     * @see java.io.InputStream#read(byte[], int, int)
>>>     */
>>>    public int read(byte[] b, int off, int len) throws IOException {
>>>-        int l = super.read(b,  off,  len);
>>>-        WIRE_LOG.debug("<< " + new String(b, off, len));
>>>+        int l = this.in.read(b,  off,  len);
>>>+        if (l > 0) {
>>>+            Wire.input(b, off, len, null);
>>>+        }
>>>        return l;
>>>    }
>>>
>>>@@ -105,9 +119,9 @@
>>>     * @see java.io.InputStream#read()
>>>     */
>>>    public int read() throws IOException {
>>>-        int l = super.read();
>>>+        int l = this.in.read();
>>>        if (l > 0) { 
>>>-            WIRE_LOG.debug("<< " + (char) l);
>>>+            Wire.input(l, null);
>>>        }
>>>        return l;
>>>    }
>>>@@ -117,8 +131,10 @@
>>>     * @see java.io.InputStream#read(byte[])
>>>     */
>>>    public int read(byte[] b) throws IOException {
>>>-        int l = super.read(b);
>>>-        WIRE_LOG.debug("<< " + HttpConstants.getString(b));
>>>+        int l = this.in.read(b);
>>>+        if (l > 0) {
>>>+            Wire.input(b, null);
>>>+        }
>>>        return l;
>>>    }
>>>}
>>>Index: src/java/org/apache/commons/httpclient/WireLogOutputStream.java
>>>===================================================================
>>>RCS file: src/java/org/apache/commons/httpclient/WireLogOutputStream.java
>>>diff -N src/java/org/apache/commons/httpclient/WireLogOutputStream.java
>>>--- /dev/null	1 Jan 1970 00:00:00 -0000
>>>+++ src/java/org/apache/commons/httpclient/WireLogOutputStream.java	10 Feb 2003 22:02:14 -0000
>>>@@ -0,0 +1,132 @@
>>>+/*
>>>+ * ====================================================================
>>>+ *
>>>+ * The Apache Software License, Version 1.1
>>>+ *
>>>+ * Copyright (c) 1999-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 apache@apache.org.
>>>+ *
>>>+ * 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 java.io.FilterOutputStream;
>>>+import java.io.IOException;
>>>+import java.io.InputStream;
>>>+import java.io.OutputStream;
>>>+import java.io.ByteArrayInputStream;
>>>+import java.io.Reader;
>>>+import java.io.InputStreamReader;
>>>+import java.io.BufferedReader;
>>>+import java.io.UnsupportedEncodingException;
>>>+import org.apache.commons.logging.Log;
>>>+import org.apache.commons.logging.LogFactory;
>>>+import org.apache.commons.httpclient.util.Wire; 
>>>+
>>>+/**
>>>+ * Logs all data written to the wire LOG.
>>>+ *
>>>+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
>>>+ * 
>>>+ * @since 2.0beta1
>>>+ */
>>>+
>>>+public class WireLogOutputStream extends FilterOutputStream {
>>>+
>>>+    /** Log for any wire messages. */
>>>+    private static final Log WIRE_LOG = LogFactory.getLog("httpclient.wire");
>>>+
>>>+    /** Content encoding. */
>>>+    private String charset = "US-ASCII";
>>>+    
>>>+    /** Original input stream. */
>>>+    private OutputStream out;    
>>>+
>>>+    /**
>>>+     * Create an instance that wraps the specified output stream.
>>>+     * @param out The output stream.
>>>+     */
>>>+    public WireLogOutputStream(OutputStream out, String charset) {
>>>+        super(out);
>>>+        if (charset != null) {
>>>+            this.charset = charset;
>>>+        }
>>>+        this.out = out;
>>>+    }
>>>+    
>>>+    /**
>>>+     * 
>>>+     * @see java.io.OutputStream#write(byte[], int, int)
>>>+     */
>>>+    public void write(byte[] b, int off, int len) throws IOException {
>>>+        this.out.write(b,  off,  len);
>>>+        Wire.output(b, off, len, this.charset);
>>>+    }
>>>+
>>>+    /**
>>>+     * 
>>>+     * @see java.io.OutputStream#write()
>>>+     */
>>>+    public void write(int b) throws IOException {
>>>+        this.out.write(b);
>>>+        Wire.output(b, this.charset);
>>>+    }
>>>+
>>>+    /**
>>>+     * 
>>>+     * @see java.io.OutputStream#write(byte[])
>>>+     */
>>>+    public void write(byte[] b) throws IOException {
>>>+        this.out.write(b);
>>>+        Wire.output(b, this.charset);
>>>+    }
>>>+}
>>>Index: src/java/org/apache/commons/httpclient/methods/MultipartPostMethod.java
>>>===================================================================
>>>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/MultipartPostMethod.java,v
>>>retrieving revision 1.8
>>>diff -u -r1.8 MultipartPostMethod.java
>>>--- src/java/org/apache/commons/httpclient/methods/MultipartPostMethod.java	2 Feb 2003 04:30:13 -0000	1.8
>>>+++ src/java/org/apache/commons/httpclient/methods/MultipartPostMethod.java	10 Feb 2003 22:02:15 -0000
>>>@@ -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);
>>>        }
>>>    }
>>>}
>>>Index: src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java
>>>===================================================================
>>>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java,v
>>>retrieving revision 1.10
>>>diff -u -r1.10 FilePart.java
>>>--- src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java	28 Jan 2003 22:25:31 -0000	1.10
>>>+++ src/java/org/apache/commons/httpclient/methods/multipart/FilePart.java	10 Feb 2003 22:02:15 -0000
>>>@@ -82,27 +82,84 @@
>>> * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
>>> * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a>
>>> * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
>>>+ * @author <a href="mailto:oleg@ural.ru">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
>>>Index: src/java/org/apache/commons/httpclient/methods/multipart/Part.java
>>>===================================================================
>>>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v
>>>retrieving revision 1.7
>>>diff -u -r1.7 Part.java
>>>--- src/java/org/apache/commons/httpclient/methods/multipart/Part.java	28 Jan 2003 22:25:31 -0000	1.7
>>>+++ src/java/org/apache/commons/httpclient/methods/multipart/Part.java	10 Feb 2003 22:02:16 -0000
>>>@@ -63,10 +63,13 @@
>>>
>>>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.httpclient.WireLogOutputStream;
>>>import org.apache.commons.logging.Log;
>>>import org.apache.commons.logging.LogFactory;
>>>
>>>@@ -77,6 +80,7 @@
>>> * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
>>> * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
>>> * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
>>>+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
>>> *
>>> * @since 2.0
>>> */
>>>@@ -85,26 +89,65 @@
>>>    /** Log object for this class. */
>>>    private static final Log LOG = LogFactory.getLog(Part.class);
>>>
>>>+    /** Log for any wire messages. */
>>>+    private static final Log WIRE_LOG = LogFactory.getLog("httpclient.wire");
>>>+
>>>    //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 +157,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 +193,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 +257,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.
>>>@@ -249,51 +281,60 @@
>>>        out.write(CRLF_BYTES);
>>>    }
>>>    
>>>-    /**
>>>-     * 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. 
>>>-     */
>>>-
>>>+/*
>>>+    protected static void byteArrayToWire(final byte[] rawdata, final String charset) 
>>>+      throws IOException {
>>>+        Reader reader = null;
>>>+        try {
>>>+            reader = new InputStreamReader(new ByteArrayInputStream(rawdata), charset);
>>>+        } catch (UnsupportedEncodingException e) {
>>>+            reader = new InputStreamReader(new ByteArrayInputStream(rawdata));
>>>+        }
>>>+        BufferedReader in = new BufferedReader(reader);
>>>+        String line;
>>>+        while( (line = in.readLine()) != null ) {
>>>+            WIRE_LOG.debug(">> \"" + line + "\" [\\r\\n]");
>>>+        }
>>>+    } 
>>>+  */  
>>>    /**
>>>     * 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 +345,55 @@
>>>    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"); 
>>>+        }
>>>+        if (WIRE_LOG.isDebugEnabled()) {
>>>+            out = new WireLogOutputStream(out, "US-ACSII"); 
>>>+        }
>>>+        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;
>>>+    }        
>>>}
>>>Index: src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java
>>>===================================================================
>>>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java,v
>>>retrieving revision 1.5
>>>diff -u -r1.5 StringPart.java
>>>--- src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java	28 Jan 2003 22:25:31 -0000	1.5
>>>+++ src/java/org/apache/commons/httpclient/methods/multipart/StringPart.java	10 Feb 2003 22:02:17 -0000
>>>@@ -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:mattalbright@yahoo.com">Matthew Albright</a>
>>> * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
>>> * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
>>>+ * @author <a href="mailto:oleg@ural.ru">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;
>>>    }
>>>}
>>>Index: src/java/org/apache/commons/httpclient/util/Wire.java
>>>===================================================================
>>>RCS file: src/java/org/apache/commons/httpclient/util/Wire.java
>>>diff -N src/java/org/apache/commons/httpclient/util/Wire.java
>>>--- /dev/null	1 Jan 1970 00:00:00 -0000
>>>+++ src/java/org/apache/commons/httpclient/util/Wire.java	10 Feb 2003 22:02:14 -0000
>>>@@ -0,0 +1,167 @@
>>>+/*
>>>+ * ====================================================================
>>>+ *
>>>+ * The Apache Software License, Version 1.1
>>>+ *
>>>+ * Copyright (c) 1999-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 apache@apache.org.
>>>+ *
>>>+ * 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.util;
>>>+
>>>+import java.io.IOException;
>>>+import java.io.InputStream;
>>>+import java.io.ByteArrayInputStream;
>>>+import java.io.Reader;
>>>+import java.io.InputStreamReader;
>>>+import java.io.BufferedReader;
>>>+import java.io.UnsupportedEncodingException;
>>>+import org.apache.commons.logging.Log;
>>>+import org.apache.commons.logging.LogFactory;
>>>+
>>>+/**
>>>+ * Logs data to the wire LOG.
>>>+ *
>>>+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
>>>+ * 
>>>+ * @since 2.0beta1
>>>+ */
>>>+
>>>+public class Wire {
>>>+
>>>+    /** Log for any wire messages. */
>>>+    private static final Log WIRE_LOG = LogFactory.getLog("httpclient.wire");
>>>+
>>>+    private static void wire(String header, InputStream instream, String charset)
>>>+      throws IOException {
>>>+        if (charset == null) {
>>>+            charset = "US-ASCII";
>>>+        }
>>>+        Reader reader = null;
>>>+        try {
>>>+            reader = new InputStreamReader(instream, charset);
>>>+        } catch (UnsupportedEncodingException e) {
>>>+            reader = new InputStreamReader(instream);
>>>+        }
>>>+        StringBuffer buffer = new StringBuffer();
>>>+        int ch;
>>>+        while ((ch = reader.read()) != -1) {
>>>+            if (ch == 13) {
>>>+                buffer.append("[\\r]");
>>>+            } else if (ch == 10){
>>>+                    buffer.append("[\\n]\"");
>>>+                    buffer.insert(0, "\"");
>>>+                    buffer.insert(0, header);
>>>+                    WIRE_LOG.debug(buffer.toString());
>>>+                    buffer.setLength(0);
>>>+            } else if ((ch < 32) || (ch > 127)) {
>>>+                buffer.append("[0x");
>>>+                buffer.append(Integer.toHexString(ch));
>>>+                buffer.append("]");
>>>+            } else {
>>>+                buffer.append((char)ch);
>>>+            }
>>>+        } 
>>>+        if (buffer.length() > 0) {
>>>+            buffer.append("\"");
>>>+            buffer.insert(0, "\"");
>>>+            buffer.insert(0, header);
>>>+            WIRE_LOG.debug(buffer.toString());
>>>+        }
>>>+    }
>>>+
>>>+
>>>+    public static final boolean enabled() {
>>>+        return WIRE_LOG.isDebugEnabled();
>>>+    }    
>>>+    
>>>+    public static final void output(InputStream instream, String charset)
>>>+      throws IOException {
>>>+        wire(">> ", instream, charset);
>>>+    }
>>>+
>>>+    public static final void input(InputStream instream, String charset)
>>>+      throws IOException {
>>>+        wire("<< ", instream, charset);
>>>+    }
>>>+
>>>+    public static final void output(byte[] b, int off, int len, String charset)
>>>+      throws IOException {
>>>+        wire(">> ", new ByteArrayInputStream(b, off, len), charset);
>>>+    }
>>>+
>>>+    public static final void input(byte[] b, int off, int len, String charset)
>>>+      throws IOException {
>>>+        wire("<< ", new ByteArrayInputStream(b, off, len), charset);
>>>+    }
>>>+
>>>+    public static final void output(byte[] b, String charset)
>>>+      throws IOException {
>>>+        wire(">> ", new ByteArrayInputStream(b), charset);
>>>+    }
>>>+
>>>+    public static final void input(byte[] b, String charset)
>>>+      throws IOException {
>>>+        wire("<< ", new ByteArrayInputStream(b), charset);
>>>+    }
>>>+
>>>+    public static final void output(int b, String charset)
>>>+      throws IOException {
>>>+        output(new byte[] {(byte)b}, charset);
>>>+    }
>>>+
>>>+    public static final void input(int b, String charset)
>>>+      throws IOException {
>>>+        input(new byte[] {(byte)b}, charset);
>>>+    }
>>>+}
>>>Index: src/test/org/apache/commons/httpclient/TestWebapp.java
>>>===================================================================
>>>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebapp.java,v
>>>retrieving revision 1.6
>>>diff -u -r1.6 TestWebapp.java
>>>--- src/test/org/apache/commons/httpclient/TestWebapp.java	1 Feb 2003 16:10:48 -0000	1.6
>>>+++ src/test/org/apache/commons/httpclient/TestWebapp.java	10 Feb 2003 22:02:05 -0000
>>>@@ -1,5 +1,5 @@
>>>/*
>>>- * $Header: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebapp.java,v 1.6 2003/02/01 16:10:48 olegk Exp $
>>>+ * $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebapp.java,v 1.6 2003/02/01 16:10:48 olegk Exp $
>>> * $Revision: 1.6 $
>>> * $Date: 2003/02/01 16:10:48 $
>>> * ====================================================================
>>>@@ -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;
>>>    }
>>>Index: src/test/org/apache/commons/httpclient/TestWebappMultiPostMethod.java
>>>===================================================================
>>>RCS file: src/test/org/apache/commons/httpclient/TestWebappMultiPostMethod.java
>>>diff -N src/test/org/apache/commons/httpclient/TestWebappMultiPostMethod.java
>>>--- /dev/null	1 Jan 1970 00:00:00 -0000
>>>+++ src/test/org/apache/commons/httpclient/TestWebappMultiPostMethod.java	10 Feb 2003 22:02:06 -0000
>>>@@ -0,0 +1,146 @@
>>>+/*
>>>+ * $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebappPostMethod.java,v 1.2 2003/02/03 21:21:19 olegk Exp $
>>>+ * $Revision: 1.2 $
>>>+ * $Date: 2003/02/03 21:21:19 $
>>>+ *
>>>+ * ====================================================================
>>>+ *
>>>+ * 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 apache@apache.org.
>>>+ *
>>>+ * 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="oleg@ural.ru">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: commons-httpclient-dev-unsubscribe@jakarta.apache.org
>>>For additional commands, e-mail: commons-httpclient-dev-help@jakarta.apache.org
>>>
>>>      
>>>
>>
>>---------------------------------------------------------------------
>>To unsubscribe, e-mail: commons-httpclient-dev-unsubscribe@jakarta.apache.org
>>For additional commands, e-mail: commons-httpclient-dev-help@jakarta.apache.org
>>
>>    
>>
>
>
>---------------------------------------------------------------------
>To unsubscribe, e-mail: commons-httpclient-dev-unsubscribe@jakarta.apache.org
>For additional commands, e-mail: commons-httpclient-dev-help@jakarta.apache.org
>
>
>  
>



Mime
View raw message