hc-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Oleg Kalnichevski <o.kalnichev...@dplanet.ch>
Subject Re: [PATCH] PostMethod & PutMethod revision (take 1)
Date Wed, 29 Jan 2003 18:32:39 GMT
Hey Mike

In order to avoid double buffering, one can simply provide a request
body via setRequestBody(InputStream) AND setRequestContentLength(int) to
an integer value other than CONTENT_LENGTH_AUTO. The request body
content will be read out of the InputStream without having to be stored
in a intermediary buffer

Cheers

Oleg

On Wed, 2003-01-29 at 19:03, Michael Becke wrote:
> Hi Oleg,
> 
> Just a quick question.  Why do we want to keep from having to calculate 
> the content length? Is this just for the case of when the Post content 
> is set via setRequestBody(InputStream)?
> 
> Thanks,
> 
> Mike
> 
> Kalnichevski, Oleg wrote:
> > Bug fixes:
> > 
> > http://nagoya.apache.org/bugzilla/show_bug.cgi?id=11095
> > 
> > Changelog:
> > 
> > - Abstract EntityEnclosingMethod class has been introduced to encapsulate common behaviour of all entity enclosing methods 
> > - Limited "Expect: 100-continue" support in all entity enclosing methods (HttpClient hangs indefinitely if status code 100 is not sent when expected)
> > - Support for chunk encoded requests in all entity enclosing methods
> > - More robust (or so I'd like to hope) request content buffering logic
> > - PostMethod inherited from EntityEnclosingMethod class
> > - PutMethod inherited from EntityEnclosingMethod class
> > 
> > To be done next:
> > 
> > http://nagoya.apache.org/bugzilla/show_bug.cgi?id=11653
> > http://nagoya.apache.org/bugzilla/show_bug.cgi?id=14731
> > 
> > Feedback, critique welcome as always. Feel free to start throwing bad tomatoes at me
> > 
> > Cheers
> > 
> > Oleg
> > 
> > 
> > ------------------------------------------------------------------------
> > 
> > Index: java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java
> > ===================================================================
> > RCS file: java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java
> > diff -N java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java
> > --- /dev/null	1 Jan 1970 00:00:00 -0000
> > +++ java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java	29 Jan 2003 16:12:02 -0000
> > @@ -0,0 +1,512 @@
> > +/*
> > + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/GetMethod.java,v 1.21 2003/01/23 22:48:06 jsdever Exp $
> > + * $Revision: 1.21 $
> > + * $Date: 2003/01/23 22:48:06 $
> > + * ====================================================================
> > + *
> > + * 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.methods;
> > +
> > +import java.io.File;
> > +import java.io.IOException;
> > +import java.io.OutputStream;
> > +import java.io.ByteArrayOutputStream;
> > +import java.io.InputStream;
> > +import java.io.ByteArrayInputStream;
> > +import java.io.Reader;
> > +import java.io.InputStreamReader;
> > +import java.io.UnsupportedEncodingException;
> > +
> > +import org.apache.commons.httpclient.HttpConstants;
> > +import org.apache.commons.httpclient.HttpConnection;
> > +import org.apache.commons.httpclient.HttpException;
> > +import org.apache.commons.httpclient.HttpState;
> > +import org.apache.commons.httpclient.HttpStatus;
> > +import org.apache.commons.httpclient.ChunkedOutputStream;
> > +import org.apache.commons.httpclient.ContentLengthInputStream;
> > +import org.apache.commons.logging.Log;
> > +import org.apache.commons.logging.LogFactory;
> > +
> > +/**
> > + * This abstract class serves as a foundation for all HTTP methods 
> > + * that can enclose an entity within requests 
> > + *
> > + * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
> > + * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
> > + * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
> > + * @author Ortwin Glück
> > + * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
> > + * @since 2.0
> > + */
> > +
> > +public abstract class EntityEnclosingMethod extends GetMethod
> > +{
> > +    // ----------------------------------------- Static variables/initializers
> > +
> > +    /**
> > +     * The content length will be calculated automatically. This implies
> > +     * buffering of the content.
> > +     */
> > +    public static final int CONTENT_LENGTH_AUTO = -2;
> > +
> > +    /**
> > +     * The request will use chunked transfer encoding. Content length is not
> > +     * calculated and the content is not buffered.<br>
> > +     */
> > +    public static final int CONTENT_LENGTH_CHUNKED = -1;
> > +
> > +    /** Log object for this class. */
> > +    private static final Log log = LogFactory.getLog(EntityEnclosingMethod.class);
> > +
> > +    /** The buffered request body, if any. */
> > +    private byte[] buffer = null;
> > +
> > +    /** The unbuffered request body, if any. */
> > +    private InputStream requestBodyStream = null;
> > +
> > +    /** Counts how often the request was sent to the server. */
> > +    private int repeatCount = 0;
> > +
> > +    /** The content length of the <code>requestBodyStream</code> or one of
> > +     *  <code>CONTENT_LENGTH_AUTO</code> and <code>CONTENT_LENGTH_CHUNKED</code>.
> > +     */
> > +    private int requestContentLength = CONTENT_LENGTH_AUTO;
> > +
> > +    private boolean useExpectHeader = true;
> > +    
> > +    // ----------------------------------------------------------- Constructors
> > +
> > +    /**
> > +     * No-arg constructor.
> > +     *
> > +     * @since 2.0
> > +     */
> > +    public EntityEnclosingMethod() {
> > +        super();
> > +        setFollowRedirects(false);
> > +    }
> > +
> > +    /**
> > +     * Constructor specifying a URI.
> > +     *
> > +     * @param uri either an absolute or relative URI
> > +     *
> > +     * @since 2.0
> > +     */
> > +    public EntityEnclosingMethod(String uri) {
> > +        super(uri);
> > +        setFollowRedirects(false);
> > +    }
> > +
> > +    /**
> > +     * Constructor specifying a URI and a tempDir.
> > +     *
> > +     * @param uri either an absolute or relative URI
> > +     * @param tempDir directory to store temp files in
> > +     *
> > +     * @since 2.0
> > +     */
> > +    public EntityEnclosingMethod(String uti, String tempDir) {
> > +        super(uti, tempDir);
> > +        setFollowRedirects(false);
> > +    }
> > +
> > +    /**
> > +     * Constructor specifying a URI, tempDir and tempFile.
> > +     *
> > +     * @param uri either an absolute or relative URI
> > +     * @param tempDir directory to store temp files in
> > +     * @param tempFile file to store temporary data in
> > +     *
> > +     * @since 2.0
> > +     */
> > +    public EntityEnclosingMethod(String uri, String tempDir, String tempFile) {
> > +        super(uri, tempDir, tempFile);
> > +        setFollowRedirects(false);
> > +    }
> > +
> > +
> > +    /**
> > +     * Returns the useExpectHeader.
> > +     * @return boolean
> > +     */
> > +    public boolean getUseExpectHeader()
> > +    {
> > +        return this.useExpectHeader;
> > +    }
> > +
> > +    /**
> > +     * Sets the useExpectHeader.
> > +     * @param useExpectHeader The useExpectHeader to set
> > +     */
> > +    public void setUseExpectHeader(boolean value)
> > +    {
> > +        this.useExpectHeader = value;
> > +    }
> > +
> > +    /** 
> > +     * Returns true if request body has been buffered. Otherwise returns false.
> > +     *
> > +     * @since 2.0
> > +     */
> > +    
> > +    protected boolean hasBufferedRequestBody() {
> > +        return this.buffer != null;
> > +    }
> > +
> > +    /** 
> > +     * Returns true if request body has been set. Otherwise returns false.
> > +     *
> > +     * @since 2.0
> > +     */
> > +
> > +    protected boolean hasRequestBody() {
> > +        return (this.requestBodyStream != null) || (this.buffer != null);
> > +    }
> > +
> > +    /**
> > +     * Sets length information about the request body.
> > +     *
> > +     * <p>
> > +     * Note: If you specify a content length the request is unbuffered. This
> > +     * prevents redirection and automatic retry if a request fails the first
> > +     * time. This means that the HttpClient can not perform authorization
> > +     * automatically but will throw an Exception. You will have to set the
> > +     * necessary 'Authorization' or 'Proxy-Authorization' headers manually.
> > +     * </p>
> > +     *
> > +     * @param length size in bytes or any of CONTENT_LENGTH_AUTO,
> > +     *        CONTENT_LENGTH_CHUNKED. If number of bytes or CONTENT_LENGTH_CHUNKED
> > +     *        is specified the content will not be buffered internally and the
> > +     *        Content-Length header of the request will be used. In this case
> > +     *        the user is responsible to supply the correct content length.
> > +     *        If CONTENT_LENGTH_AUTO is specified the request will be buffered
> > +     *        before it is sent over the network.
> > +     *
> > +     * @since 2.0
> > +     */
> > +
> > +    public void setRequestContentLength(int length) {
> > +        log.trace("enter EntityEnclosingMethod.setRequestContentLength(int)");
> > +        this.requestContentLength = length;
> > +    }
> > +
> > +    /**
> > +     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > +     * to return the length of the request body.
> > +     *
> > +     * @return number of bytes in the request body
> > +     *
> > +     * @since 2.0
> > +     */
> > +
> > +    protected int getRequestContentLength() {
> > +        log.trace("enter EntityEnclosingMethod.getRequestContentLength()");
> > +
> > +        if (this.requestContentLength != CONTENT_LENGTH_AUTO) {
> > +            return this.requestContentLength;
> > +        }
> > +        bufferContent();
> > +        if (this.buffer != null) {
> > +            return this.buffer.length;
> > +        }
> > +        else {
> > +            return 0;
> > +        }
> > +    }
> > +
> > +    /**
> > +     * Sets the request body to be the specified inputstream.
> > +     *
> > +     * @param body Request body content as {@link java.io.InputStream}
> > +     *
> > +     * @since 2.0
> > +     */
> > +
> > +    public void setRequestBody(InputStream body) {
> > +        log.trace("enter EntityEnclosingMethod.setRequestBody(InputStream)");
> > +        this.requestBodyStream = body;
> > +        this.buffer = null;
> > +    }
> > +
> > +    /**
> > +     * Gets the request body as a stream.
> > +     *
> > +     * @return The request body {@link java.io.InputStream} if it has been set.
> > +     *
> > +     * @throws IllegalStateException if request body is not buferred
> > +     * 
> > +     * @since 2.0
> > +     */
> > +
> > +    public InputStream getRequestBody() {
> > +        log.trace("enter EntityEnclosingMethod.getRequestBody()");
> > +        if (this.buffer != null) {
> > +            return new ByteArrayInputStream(this.buffer); 
> > +        }
> > +        else {
> > +            return this.requestBodyStream;
> > +        }
> > +    }
> > +
> > +    /**
> > +     * Sets the request body to be the specified string.
> > +     *
> > +     * @param body Request body content as a string
> > +     *
> > +     * @since 2.0
> > +     */
> > +
> > +    public void setRequestBody(String body) {
> > +        log.trace("enter EntityEnclosingMethod.setRequestBody(String)");
> > +
> > +        if (body == null) {
> > +            this.requestBodyStream = null;
> > +            this.buffer = null;
> > +            return;
> > +        }
> > +        this.buffer = HttpConstants.getContentBytes(body, getRequestCharSet());
> > +    }
> > +
> > +    /**
> > +     * Gets the request body as a string.
> > +     *
> > +     * @return the request body as a string
> > +     * 
> > +     * @throws IOException when i/o errors occur reading the request
> > +     * @throws IllegalStateException if request body is not buferred
> > +     *
> > +     * @since 2.0
> > +     */
> > +
> > +    public String getRequestBodyAsString() throws IOException {
> > +        log.trace("enter EntityEnclosingMethod.getRequestBodyAsString()");
> > +        Reader instream = null;
> > +        try {
> > +            instream = new InputStreamReader(getRequestBody(), getRequestCharSet());
> > +        }
> > +        catch(UnsupportedEncodingException e) {
> > +            if (log.isWarnEnabled()) {
> > +                log.warn("Unsupported encoding: " + e.getMessage());
> > +            }
> > +            instream = new InputStreamReader(getRequestBody());
> > +        }
> > +        StringBuffer buffer = new StringBuffer();
> > +        char[] tmp = new char[4096];
> > +        int l = 0;
> > +        while((l = instream.read(tmp)) >= 0) {
> > +            buffer.append(tmp, 0, l);
> > +        }
> > +        return buffer.toString();
> > +    }
> > +
> > +
> > +    /**
> > +     * Override the method of {@link HttpMethodBase}
> > +     * to set the <tt>Expect</tt> header if it has
> > +     * not already been set, in addition to the "standard"
> > +     * set of headers.
> > +     *
> > +     * @throws HttpException when a protocol error occurs or state is invalid
> > +     * @throws IOException when i/o errors occur reading the response
> > +     * 
> > +     * @since 2.0
> > +     */
> > +
> > +    protected void addRequestHeaders(HttpState state, HttpConnection conn)
> > +    throws IOException, HttpException {
> > +        log.trace("enter EntityEnclosingMethod.addRequestHeaders(HttpState, HttpConnection)");
> > +        
> > +        if (!isHttp11() && getUseExpectHeader()) {
> > +            throw new HttpException(
> > +                "100-continue not allowed for HTTP/1.0");
> > +        }
> > +
> > +        super.addRequestHeaders(state, conn);
> > +        // Send expectation header, provided there's something to be sent
> > +        if(isHttp11() && hasRequestBody()) {
> > +            if (getRequestHeader("Expect") == null) {
> > +                setRequestHeader("Expect", "100-continue");
> > +            }
> > +        }
> > +    }
> > +
> > +    /**
> > +     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > +     * to write request parameters as the request body.  The input stream will
> > +     * be truncated after the specified content length.
> > +     *
> > +     * @param state the client state
> > +     * @param conn the connection to write to
> > +     *
> > +     * @return <tt>true</tt>
> > +     * @throws IOException when i/o errors occur reading the response
> > +     * @throws HttpException when a protocol error occurs or state is invalid
> > +     *
> > +     * @since 2.0
> > +     */
> > +    
> > +    protected boolean writeRequestBody(HttpState state, HttpConnection conn)
> > +    throws IOException, HttpException {
> > +        log.trace(
> > +            "enter EntityEnclosingMethod.writeRequestBody(HttpState, HttpConnection)");
> > +        
> > +        if (getStatusLine() == null) {
> > +             return false;
> > +        }
> > +        if (getRequestHeader("Expect") != null) {
> > +            if (getStatusLine().getStatusCode() != HttpStatus.SC_CONTINUE) {
> > +                return false;
> > +            }
> > +        }
> > +
> > +        int contentLength = getRequestContentLength();
> > +
> > +        if ((contentLength == CONTENT_LENGTH_CHUNKED) && !isHttp11()) {
> > +            throw new HttpException(
> > +                "Chunked transfer encoding not allowed for HTTP/1.0");
> > +        }
> > +        InputStream instream = getRequestBody();
> > +        if (instream == null) {
> > +            return true;
> > +        }
> > +
> > +        if ((this.repeatCount > 0) && (this.buffer == null)) {
> > +            throw new HttpException(
> > +                "Unbuffered entity enclosing request can not be repeated.");
> > +        }
> > +
> > +        this.repeatCount++;
> > +
> > +        OutputStream outstream = conn.getRequestOutputStream();
> > +        if (contentLength == CONTENT_LENGTH_CHUNKED) {
> > +            outstream = new ChunkedOutputStream(outstream);
> > +        }
> > +        if (contentLength >= 0) {
> > +            // don't need a watcher here - we're reading from something local,
> > +            // not server-side.
> > +            instream = new ContentLengthInputStream(instream, contentLength);
> > +        }
> > +
> > +        byte[] tmp = new byte[4096];
> > +        int total = 0;
> > +        int i = 0;
> > +        while ((i = instream.read(tmp)) >= 0) {
> > +            outstream.write(tmp, 0, i);
> > +            total += i;
> > +        }
> > +        // This is hardly the most elegant solution to closing chunked stream
> > +        if (outstream instanceof ChunkedOutputStream) {
> > +            ((ChunkedOutputStream)outstream).writeClosingChunk();
> > +        }
> > +        if ((contentLength > 0) && (total < contentLength)) {
> > +            throw new IOException("Unexpected end of input stream after "
> > +                + total + " bytes (expected " + contentLength + " bytes)");
> > +        }
> > +        return true;
> > +    }
> > +
> > +    /**
> > +     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > +     * to clear my request body.
> > +     *
> > +     * @since 2.0
> > +     */
> > +    public void recycle() {
> > +        log.trace("enter EntityEnclosingMethod.recycle()");
> > +        super.recycle();
> > +        this.requestContentLength = CONTENT_LENGTH_AUTO;
> > +        this.requestBodyStream = null;
> > +        this.buffer = null;
> > +        this.repeatCount = 0;
> > +    }
> > +
> > +    /**
> > +     * Buffers the request body and calculates the content length. If the
> > +     * method was called earlier it returns immediately.
> > +     *
> > +     * @since 2.0
> > +     */
> > +    
> > +    protected void bufferContent() {
> > +        log.trace("enter EntityEnclosingMethod.bufferContent()");
> > +
> > +        if (this.buffer != null) {
> > +            return;
> > +        }
> > +        if (this.requestBodyStream == null) {
> > +            return;
> > +        }
> > +        try {
> > +            ByteArrayOutputStream tmp = new ByteArrayOutputStream();
> > +            byte[] data = new byte[4096];
> > +            int l = 0;
> > +            while ((l = this.requestBodyStream.read(data)) >= 0) {
> > +                tmp.write(data, 0, l);
> > +            }
> > +            this.buffer = tmp.toByteArray();
> > +            this.requestBodyStream = null;
> > +        } catch (IOException e) {
> > +            if (log.isErrorEnabled()) {
> > +                log.error(e.toString(), e );
> > +            }
> > +            this.buffer = null;
> > +            this.requestBodyStream = null;
> > +        }
> > +    }
> > +}
> > Index: java/org/apache/commons/httpclient/methods/PostMethod.java
> > ===================================================================
> > RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v
> > retrieving revision 1.34
> > diff -u -r1.34 PostMethod.java
> > --- java/org/apache/commons/httpclient/methods/PostMethod.java	28 Jan 2003 22:25:26 -0000	1.34
> > +++ java/org/apache/commons/httpclient/methods/PostMethod.java	29 Jan 2003 16:12:02 -0000
> > @@ -1,8 +1,7 @@
> >  /*
> > - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v 1.34 2003/01/28 22:25:26 jsdever Exp $
> > - * $Revision: 1.34 $
> > - * $Date: 2003/01/28 22:25:26 $
> > - *
> > + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v 1.33 2003/01/23 22:48:08 jsdever Exp $
> > + * $Revision: 1.33 $
> > + * $Date: 2003/01/23 22:48:08 $
> >   * ====================================================================
> >   *
> >   * The Apache Software License, Version 1.1
> > @@ -60,27 +59,21 @@
> >   * [Additional notices, if required by prior licensing conditions]
> >   *
> >   */
> > -
> >  package org.apache.commons.httpclient.methods;
> >  
> > -import java.io.ByteArrayInputStream;
> > -import java.io.ByteArrayOutputStream;
> >  import java.io.IOException;
> >  import java.io.InputStream;
> > -import java.io.OutputStream;
> > +import java.io.ByteArrayInputStream;
> > +import java.util.Vector;
> >  import java.util.Iterator;
> >  import java.util.List;
> > -import java.util.Vector;
> > -
> >  import org.apache.commons.httpclient.HttpConstants;
> > -import org.apache.commons.httpclient.Header;
> >  import org.apache.commons.httpclient.HttpConnection;
> >  import org.apache.commons.httpclient.HttpException;
> >  import org.apache.commons.httpclient.HttpState;
> > +import org.apache.commons.httpclient.Header;
> >  import org.apache.commons.httpclient.NameValuePair;
> >  import org.apache.commons.httpclient.URIException;
> > -import org.apache.commons.httpclient.ChunkedOutputStream;
> > -import org.apache.commons.httpclient.ContentLengthInputStream;
> >  import org.apache.commons.httpclient.util.URIUtil;
> >  import org.apache.commons.logging.Log;
> >  import org.apache.commons.logging.LogFactory;
> > @@ -116,54 +109,24 @@
> >   * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
> >   * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
> >   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
> > - * @author Ortwin Gl�
> > - * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
> > + * @author Ortwin Glück
> > + * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
> >   * @since 1.0
> >   */
> > -public class PostMethod extends GetMethod {
> > -    //~ Static variables/initializers ������������������������������������������
> > -
> > -    /**
> > -     * The content length will be calculated automatically. This implies
> > -     * buffering of the content.
> > -     */
> > -    public static final int CONTENT_LENGTH_AUTO = -2;
> > -
> > -    /**
> > -     * The request will use chunked transfer encoding. Content length is not
> > -     * calculated and the content is not buffered.<br>
> > -     */
> > -    public static final int CONTENT_LENGTH_CHUNKED = -1;
> > -
> > +public class PostMethod extends EntityEnclosingMethod {
> >      // -------------------------------------------------------------- Constants
> >  
> >      /** Log object for this class. */
> > -    private static final Log LOG = LogFactory.getLog(PostMethod.class);
> > +    private static final Log log = LogFactory.getLog(PostMethod.class);
> >  
> >      /** The Content-Type header for www-form-urlcoded. */
> > -    private static final Header CONTENT_TYPE = new Header("Content-Type",
> > +    static final Header CONTENT_TYPE = new Header("Content-Type",
> >          "application/x-www-form-urlencoded");
> >  
> > -    /** The buffered request body. */
> > -    protected ByteArrayOutputStream buffer = null;
> > -
> > -    /** The unbuffered request body. */
> > -    protected InputStream requestBody = null;
> > -
> >      /** The buffered request body consisting of <code>NameValuePair</code>s */
> >      protected Vector parameters = new Vector();
> >  
> > -    /** Counts how often the request was sent to the server. */
> > -    protected int repeatCount = 0;
> > -
> > -    /**
> > -     * The content length of the <code>requestBody</code> or one of
> > -     * <code>{@link #CONTENT_LENGTH_AUTO}</code> and
> > -     * <code>{@link #CONTENT_LENGTH_CHUNKED}</code>.
> > -     */
> > -    protected int requestContentLength = CONTENT_LENGTH_AUTO;
> > -
> > -    //~ Constructors �����������������������������������������������������������
> > +    // ----------------------------------------------------------- Constructors
> >  
> >      /**
> >       * No-arg constructor.
> > @@ -195,8 +158,8 @@
> >       *
> >       * @since 1.0
> >       */
> > -    public PostMethod(String uri, String tempDir) {
> > -        super(uri, tempDir);
> > +    public PostMethod(String uti, String tempDir) {
> > +        super(uti, tempDir);
> >          setFollowRedirects(false);
> >      }
> >  
> > @@ -216,8 +179,6 @@
> >  
> >      // ----------------------------------------------------------- Constructors
> >  
> > -    //~ Methods ����������������������������������������������������������������
> > -
> >      /**
> >       * A POST request can only be redirected if input is buffered. Overrides
> >       * method of {@link org.apache.commons.httpclient.HttpMethodBase}.
> > @@ -228,11 +189,7 @@
> >       * @since 2.0
> >       */
> >      public boolean getFollowRedirects() {
> > -        if (!super.getFollowRedirects()) {
> > -            return false;
> > -        }
> > -
> > -        return (buffer != null);
> > +        return (hasBufferedRequestBody() && super.getFollowRedirects());
> >      }
> >  
> >      // ----------------------------------------------------- Instance Methods
> > @@ -252,24 +209,23 @@
> >       * Set the value of parameter with parameterName to parameterValue. Does
> >       * not preserve the initial insertion order.
> >       *
> > -     * @param parameterName DOCUMENT ME!
> > -     * @param parameterValue DOCUMENT ME!
> > +     * @param parameterName name of the parameter
> > +     * @param parameterValue value of the parameter
> >       *
> >       * @throws IllegalStateException if my request body has already been
> >       *         generated.
> >       *
> >       * @since 2.0
> > +     * 
> >       * @deprecated use {@link #removeParameter(String,String)} followed by
> >       *             {@link #addParameter(String,String)}.
> >       */
> > -    public void setParameter(String parameterName, String parameterValue) 
> > -        throws IllegalStateException {
> > -        LOG.trace("enter PostMethod.setParameter(String, String)");
> > +    public void setParameter(String parameterName, String parameterValue) {
> > +        log.trace("enter PostMethod.setParameter(String, String)");
> >  
> > -        if (null != requestBody) {
> > +        if (hasRequestBody()) {
> >              throw new IllegalStateException("Request body already generated.");
> >          }
> > -
> >          removeParameter(parameterName, parameterValue);
> >          addParameter(parameterName, parameterValue);
> >      }
> > @@ -278,7 +234,7 @@
> >       * Gets the parameter of the specified name. If there exists more than one
> >       * parameter with the name paramName, then only the first one is returned.
> >       *
> > -     * @param paramName DOCUMENT ME!
> > +     * @param paramName name of the parameter
> >       *
> >       * @return If a parameter exists with the name argument, the coresponding
> >       *         NameValuePair is returned.  Otherwise null.
> > @@ -286,7 +242,7 @@
> >       * @since 2.0
> >       */
> >      public NameValuePair getParameter(String paramName) {
> > -        LOG.trace("enter PostMethod.getParameter(String)");
> > +        log.trace("enter PostMethod.getParameter(String)");
> >  
> >          if (paramName == null) {
> >              return null;
> > @@ -301,7 +257,6 @@
> >                  return parameter;
> >              }
> >          }
> > -
> >          return null;
> >      }
> >  
> > @@ -317,7 +272,7 @@
> >       * @see #getParameter(java.lang.String)
> >       */
> >      public NameValuePair[] getParameters() {
> > -        LOG.trace("enter PostMethod.getParameters()");
> > +        log.trace("enter PostMethod.getParameters()");
> >  
> >          int numPairs = parameters.size();
> >          Object[] objectArr = parameters.toArray();
> > @@ -331,142 +286,6 @@
> >      }
> >  
> >      /**
> > -     * Sets the request body to be the specified string.
> > -     *
> > -     * <p>
> > -     * Once this method has been invoked,  the request parameters  cannot be
> > -     * altered until I am {@link #recycle recycled}.
> > -     * </p>
> > -     *
> > -     * @param body Request content as a string
> > -     *
> > -     * @throws IllegalStateException if request params have been added
> > -     *
> > -     * @since 2.0
> > -     */
> > -    public void setRequestBody(String body) throws IllegalStateException {
> > -        LOG.trace("enter PostMethod.setRequestBody(String)");
> > -
> > -        if (!parameters.isEmpty()) {
> > -            throw new IllegalStateException(
> > -                "Request parameters have already been added.");
> > -        }
> > -
> > -        if (body == null) {
> > -            this.requestBody = null;
> > -            return;
> > -        }
> > -
> > -        this.requestBody = new ByteArrayInputStream(
> > -          HttpConstants.getContentBytes(body, getRequestCharSet()));
> > -    }
> > -
> > -    /**
> > -     * Sets the request body to be the specified inputstream.
> > -     *
> > -     * <p>
> > -     * Once this method has been invoked,  the request parameters  cannot be
> > -     * altered until I am {@link #recycle recycled}.
> > -     * </p>
> > -     *
> > -     * @param body DOCUMENT ME!
> > -     *
> > -     * @throws IllegalStateException if request params have been added
> > -     *
> > -     * @since 2.0
> > -     */
> > -    public void setRequestBody(InputStream body) throws IllegalStateException {
> > -        LOG.trace("enter PostMethod.getRequestBody(InputStream)");
> > -
> > -        if (!parameters.isEmpty()) {
> > -            throw new IllegalStateException(
> > -                "Request parameters have already been added.");
> > -        }
> > -
> > -        this.requestBody = body;
> > -    }
> > -
> > -    /**
> > -     * Gets the requestBody as it would be if it was executed.
> > -     *
> > -     * @return The request body if it has been set.  The generated  request
> > -     *         body from the paramters if they exist.  Null otherwise.
> > -     *
> > -     * @since 2.0
> > -     */
> > -    public InputStream getRequestBody() {
> > -        LOG.trace("enter PostMethod.getRequestBody()");
> > -
> > -        if (requestBody != null) {
> > -            return requestBody;
> > -        } else if (!parameters.isEmpty()) {
> > -            return generateRequestBody(parameters);
> > -        } else {
> > -            return null;
> > -        }
> > -    }
> > -
> > -    /**
> > -     * Return the request body as a string.
> > -     *
> > -     * @return the request body as a string
> > -     * @throws IOException If an IO problem occurs.
> > -     *
> > -     * @since 2.0
> > -     */
> > -    public String getRequestBodyAsString() throws IOException {
> > -        LOG.trace("enter PostMethod.getRequestBodyAsString()");
> > -
> > -        StringBuffer buffer = new StringBuffer();
> > -        InputStream requestBody = getRequestBody();
> > -        int data = requestBody.read();
> > -
> > -        while (data != -1) {
> > -            buffer.append((char) data);
> > -            data = requestBody.read();
> > -        }
> > -
> > -        return buffer.toString();
> > -    }
> > -
> > -    /**
> > -     * Sets length information about the request body.
> > -     *
> > -     * <p>
> > -     * Note: If you specify a content length the request is unbuffered. This
> > -     * prevents redirection and automatic retry if a request fails the first
> > -     * time. This means that the HttpClient can not perform authorization
> > -     * automatically but will throw an Exception. You will have to set the
> > -     * necessary 'Authorization' or 'Proxy-Authorization' headers manually.
> > -     * </p>
> > -     *
> > -     * @param length size in bytes or any of CONTENT_LENGTH_AUTO,
> > -     *        CONTENT_LENGTH_CHUNKED. If number of bytes or CONTENT_LENGTH_CHUNKED
> > -     *        is specified the content will not be buffered internally and the
> > -     *        Content-Length header of the request will be used. In this case
> > -     *        the user is responsible to supply the correct content length.
> > -     *        If CONTENT_LENGTH_AUTO is specified the request will be buffered
> > -     *        before it is sent over the network.
> > -     * @throws RuntimeException if chunked transfer encoding is requested for
> > -     *         a HTTP 1.0 request
> > -     *
> > -     * @since 2.0
> > -     */
> > -    public void setRequestContentLength(int length) 
> > -        throws RuntimeException {
> > -        //TODO: We should be throwing a more specific exception than this.
> > -            
> > -        LOG.trace("enter PostMethod.setRequestContentLength(int)");
> > -
> > -        if ((length == CONTENT_LENGTH_CHUNKED) && !isHttp11()) {
> > -            throw new RuntimeException(
> > -                "Chunked transfer encoding not allowed for HTTP/1.0");
> > -        }
> > -
> > -        requestContentLength = length;
> > -    }
> > -
> > -    /**
> >       * Add a new parameter to be used in the POST request body.
> >       *
> >       * @param paramName The parameter name to add.
> > @@ -478,12 +297,10 @@
> >       *
> >       * @since 1.0
> >       */
> > -    public void addParameter(String paramName, String paramValue) 
> > -        throws IllegalStateException, IllegalArgumentException {
> > -            
> > -        LOG.trace("enter PostMethod.addParameter(String, String)");
> > +    public void addParameter(String paramName, String paramValue) {
> > +        log.trace("enter PostMethod.addParameter(String, String)");
> >  
> > -        if (null != requestBody) {
> > +        if (hasRequestBody()) {
> >              throw new IllegalStateException("Request body already generated.");
> >          }
> >  
> > @@ -508,18 +325,14 @@
> >       * @since 2.0
> >       * @see #addParameter(String,String)
> >       */
> > -    public void addParameter(NameValuePair param) 
> > -        throws IllegalStateException, IllegalArgumentException {
> > -            
> > -        LOG.trace("enter PostMethod.addParameter(NameValuePair)");
> > +    public void addParameter(NameValuePair param) {
> > +        log.trace("enter PostMethod.addParameter(NameValuePair)");
> >  
> > -        if (null != requestBody) {
> > +        if (hasRequestBody()) {
> >              throw new IllegalStateException("Request body already generated.");
> >          }
> > -
> > -        if (null == param) {
> > -            throw new IllegalArgumentException(
> > -                "Argument to addParameter(NameValuePair) cannot be null");
> > +        if (param == null) {
> > +            throw new IllegalArgumentException("NameValuePair may not be null");
> >          } else {
> >              addParameter(param.getName(), param.getValue());
> >          }
> > @@ -537,17 +350,14 @@
> >       * @since 2.0
> >       * @see #addParameter(org.apache.commons.httpclient.NameValuePair)
> >       */
> > -    public void addParameters(NameValuePair[] parameters) 
> > -        throws IllegalStateException {
> > -            
> > -        LOG.trace("enter PostMethod.addParameters(NameValuePair[])");
> > +    public void addParameters(NameValuePair[] parameters) {
> > +        log.trace("enter PostMethod.addParameters(NameValuePair[])");
> >  
> > -        if (null != requestBody) {
> > +        if (hasRequestBody()) {
> >              throw new IllegalStateException("Request body already generated.");
> >          }
> > -
> > -        if (null == parameters) {
> > -            LOG.warn("Attempt to addParameters(null) ignored");
> > +        if (parameters == null) {
> > +            log.warn("Attempt to addParameters(null) ignored");
> >          } else {
> >              for (int i = 0; i < parameters.length; i++) {
> >                  addParameter(parameters[i]);
> > @@ -556,22 +366,6 @@
> >      }
> >  
> >      /**
> > -     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > -     * to clear my request body.
> > -     *
> > -     * @since 1.0
> > -     */
> > -    public void recycle() {
> > -        LOG.trace("enter PostMethod.recycle()");
> > -        super.recycle();
> > -        requestBody = null;
> > -        requestContentLength = CONTENT_LENGTH_AUTO;
> > -        buffer = null;
> > -        repeatCount = 0;
> > -        parameters.clear();
> > -    }
> > -
> > -    /**
> >       * Removes all parameters with the given paramName. If there is more than
> >       * one parameter with the given paramName, all of them are removed.  If
> >       * there is just one, it is removed.  If there are none, then the request
> > @@ -587,20 +381,16 @@
> >       *
> >       * @since 2.0
> >       */
> > -    public boolean removeParameter(String paramName) 
> > -        throws IllegalArgumentException, IllegalStateException {
> > -            
> > -        LOG.trace("enter PostMethod.removeParameter(String)");
> > +    public boolean removeParameter(String paramName) {
> > +        log.trace("enter PostMethod.removeParameter(String)");
> >  
> > -        if (null != requestBody) {
> > +        if (hasRequestBody()) {
> >              throw new IllegalStateException("Request body already generated.");
> >          }
> > -
> >          if (paramName == null) {
> >              throw new IllegalArgumentException(
> >                  "Argument passed to removeParameter(String) cannot be null");
> >          }
> > -
> >          boolean removed = true;
> >          Iterator iter = parameters.iterator();
> >  
> > @@ -612,7 +402,6 @@
> >                  removed = true;
> >              }
> >          }
> > -
> >          return removed;
> >      }
> >  
> > @@ -632,19 +421,17 @@
> >       *
> >       * @since 2.0
> >       */
> > -    public boolean removeParameter(String paramName, String paramValue)
> > -        throws IllegalArgumentException, IllegalStateException {
> > -            
> > -        LOG.trace("enter PostMethod.removeParameter(String, String)");
> > +    public boolean removeParameter(String paramName, String paramValue) {
> > +        log.trace("enter PostMethod.removeParameter(String, String)");
> >  
> > -        if (null != requestBody) {
> > +        if (hasRequestBody()) {
> >              throw new IllegalStateException("Request body already generated.");
> >          }
> > -
> > -        if ((paramName == null) || (paramValue == null)) {
> > -            throw new IllegalArgumentException(
> > -                "Argument passed to removeParameter(String,String) cannot be "
> > -                + "null");
> > +        if (paramName == null) {
> > +            throw new IllegalArgumentException("Parameter name may not be null");
> > +        }
> > +        if (paramValue == null) {
> > +            throw new IllegalArgumentException("Parameter value may not be null");
> >          }
> >  
> >          Iterator iter = parameters.iterator();
> > @@ -655,221 +442,220 @@
> >              if (paramName.equals(pair.getName())
> >                  && paramValue.equals(pair.getValue())) {
> >                  iter.remove();
> > -
> >                  return true;
> >              }
> >          }
> > -
> >          return false;
> >      }
> >  
> >      /**
> > -     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > -     * to return the length of the request body.
> > +     * Encode the list of parameters into a query string.
> >       *
> > -     * @return number of bytes in the request body
> > +     * @param params the list of query name and value
> > +     *
> > +     * @return the query string
> >       *
> >       * @since 2.0
> >       */
> > -    protected int getRequestContentLength() {
> > -        LOG.trace("enter PostMethod.getRequestContentLength()");
> > +    protected static String generateRequestBody(List params) {
> > +        log.trace("enter PostMethod.generateRequestBodyAsString(List)");
> >  
> > -        if (null == requestBody) {
> > -            requestBody = generateRequestBody(parameters);
> > -            bufferContent();
> > -        }
> > -
> > -        if (requestContentLength != CONTENT_LENGTH_AUTO) {
> > -            return requestContentLength;
> > -        }
> > +        Iterator it = params.iterator();
> > +        StringBuffer buff = new StringBuffer();
> >  
> > -        bufferContent();
> > +        while (it.hasNext()) {
> > +            NameValuePair parameter = (NameValuePair) it.next();
> >  
> > -        return requestContentLength;
> > +            String queryName = null;
> > +            try {
> > +                queryName = URIUtil.encodeWithinQuery(parameter.getName());
> > +            } catch (URIException urie) {
> > +                log.error("encoding error within query name", urie);
> > +                queryName = parameter.getName();
> > +            }
> > +            buff.append(queryName).append("=");
> > +            String queryValue = null;
> > +            try {
> > +                queryValue = URIUtil.encodeWithinQuery(parameter.getValue());
> > +            } catch (URIException urie) {
> > +                log.error("encoding error within query value", urie);
> > +                queryValue = parameter.getValue();
> > +            }
> > +            buff.append(queryValue);
> > +            if (it.hasNext()) {
> > +                buff.append("&");
> > +            }
> > +        }
> > +        return buff.toString();
> >      }
> >  
> >      /**
> > -     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > -     * to  also add <tt>Content-Type</tt> header when appropriate.
> > +     * Sets the request body to be the specified string.
> >       *
> > -     * @param state DOCUMENT ME!
> > -     * @param conn DOCUMENT ME!
> > -     * @throws IOException DOCUMENT ME!
> > -     * @throws HttpException DOCUMENT ME!
> > +     * <p>
> > +     * Once this method has been invoked,  the request parameters  cannot be
> > +     * altered until I am {@link #recycle recycled}.
> > +     * </p>
> > +     *
> > +     * @param body Request body content as a string
> > +     *
> > +     * @throws IllegalStateException if request params have been added
> >       *
> >       * @since 2.0
> >       */
> > -    protected void addRequestHeaders(HttpState state, HttpConnection conn)
> > -    throws IOException, HttpException {
> > -        super.addRequestHeaders(state, conn);
> > +    public void setRequestBody(String body) {
> > +        log.trace("enter PostMethod.setRequestBody(String)");
> >  
> >          if (!parameters.isEmpty()) {
> > -            //there are some parameters, so set the contentType header
> > -            setRequestHeader(CONTENT_TYPE);
> > +            throw new IllegalStateException(
> > +                "Request parameters have already been added.");
> >          }
> > +        super.setRequestBody(body);
> >      }
> >  
> >      /**
> > -     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > -     * to write request parameters as the request body.  The input stream will
> > -     * be truncated after the specified content length.
> > +     * Sets the request body to be the specified inputstream.
> >       *
> > -     * @param state DOCUMENT ME!
> > -     * @param conn DOCUMENT ME!
> > +     * <p>
> > +     * Once this method has been invoked,  the request parameters  cannot be
> > +     * altered until I am {@link #recycle recycled}.
> > +     * </p>
> >       *
> > -     * @return always returns true
> > +     * @param body Request body content as {@link java.io.InputStream}
> >       *
> > -     * @throws IOException if the stream ends before the specified content
> > -     *         length. <p>
> > -     * @throws HttpException DOCUMENT ME!
> > +     * @throws IllegalStateException if request params have been added
> >       *
> >       * @since 2.0
> >       */
> > -    protected boolean writeRequestBody(HttpState state, HttpConnection conn)
> > -    throws IOException, HttpException {
> > -        LOG.trace(
> > -            "enter PostMethod.writeRequestBody(HttpState, HttpConnection)");
> > +    public void setRequestBody(InputStream body) {
> > +        log.trace("enter PostMethod.getRequestBody(InputStream)");
> >  
> > -        if (null == requestBody) {
> > -            requestBody = generateRequestBody(parameters);
> > +        if (!parameters.isEmpty()) {
> > +            throw new IllegalStateException(
> > +                "Request parameters have already been added.");
> >          }
> > +        super.setRequestBody(body);
> > +    }
> >  
> > -        if ((repeatCount > 0) && (buffer == null)) {
> > -            throw new HttpException(
> > -                "Sorry, unbuffered POST request can not be repeated.");
> > +    /**
> > +     * Gets the requestBody as it would be if it was executed.
> > +     *
> > +     * @return The request body if it has been set.  The generated  request
> > +     *         body from the paramters if they exist.  Null otherwise.
> > +     *
> > +     * @since 2.0
> > +     */
> > +    public InputStream getRequestBody() {
> > +        log.trace("enter PostMethod.getRequestBody()");
> > +        if (!parameters.isEmpty()) {
> > +            return new ByteArrayInputStream(
> > +              HttpConstants.getContentBytes(
> > +                generateRequestBody(parameters), getRequestCharSet()));
> >          }
> > +        else {
> > +            return super.getRequestBody();
> > +        }
> > +    }
> >  
> > -        repeatCount++;
> > -
> > -        InputStream instream = this.requestBody;
> > -        OutputStream outstream = conn.getRequestOutputStream();
> > +    /**
> > +     * Gets the request body string as it would be if it was executed.
> > +     *
> > +     * @return the request body as a string
> > +     * 
> > +     * @throws IOException when i/o errors occur reading the request
> > +     *
> > +     * @since 2.0
> > +     */
> >  
> > -        if (this.requestContentLength == CONTENT_LENGTH_CHUNKED) {
> > -            outstream = new ChunkedOutputStream(outstream);
> > +    public String getRequestBodyAsString() throws IOException {
> > +        log.trace("enter PostMethod.getRequestBody()");
> > +        if (!parameters.isEmpty()) {
> > +            return generateRequestBody(parameters);
> >          }
> > -        if (this.requestContentLength >= 0) {
> > -            // don't need a watcher here - we're reading from something local,
> > -            // not server-side.
> > -            instream = new ContentLengthInputStream(instream, this.requestContentLength);
> > +        else {
> > +            return super.getRequestBodyAsString();
> >          }
> > +    }
> >  
> > -        byte[] tmp = new byte[4096];
> > -        int total = 0;
> > -        int i = 0;
> > -        while ((i = instream.read(tmp)) >= 0) {
> > -            outstream.write(tmp, 0, i);
> > -            total += i;
> > -        }
> > -        if (outstream instanceof ChunkedOutputStream) {
> > -            ((ChunkedOutputStream) outstream).writeClosingChunk();
> > -        }
> > -        if ((this.requestContentLength > 0) && (total < this.requestContentLength)) {
> > -            throw new IOException("Unexpected end of input stream after "
> > -                + total + " bytes (expected " 
> > -                + this.requestContentLength + " bytes)");
> > -        }
> > +    /**
> > +     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > +     * to return the length of the request body.
> > +     *
> > +     * @return number of bytes in the request body
> > +     *
> > +     * @since 2.0
> > +     */
> > +    protected int getRequestContentLength() {
> > +        log.trace("enter PostMethod.getRequestContentLength()");
> >  
> > -        if (buffer != null) {
> > -            //restore buffered content for repeated requests
> > -            requestBody = new ByteArrayInputStream(buffer.toByteArray());
> > +        if (!hasRequestBody()) {
> > +            super.setRequestBody(generateRequestBody(parameters));
> > +            bufferContent();
> >          }
> > -
> > -        return true;
> > +        return super.getRequestContentLength();
> >      }
> >  
> > -
> >      /**
> > -     * Encode the list of parameters into a query stream.
> > -     *
> > -     * @param params the list of query name and value
> > +     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > +     * to  also add <tt>Content-Type</tt> header when appropriate.
> >       *
> > -     * @return the query stream
> > +     * @param state the client state
> > +     * @param conn the {@link HttpConnection} the headers will eventually be
> > +     *        written to
> > +     * @throws IOException when an error occurs writing the request
> > +     * @throws HttpException when a HTTP protocol error occurs
> >       *
> > -     * @since 1.0
> > +     * @since 2.0
> >       */
> > -    protected InputStream generateRequestBody(List params) {
> > -        LOG.trace("enter PostMethod.generateRequestBody(List)");
> > -        String body = generateRequestBodyAsString(params);
> > +    
> > +    protected void addRequestHeaders(HttpState state, HttpConnection conn)
> > +    throws IOException, HttpException {
> > +        super.addRequestHeaders(state, conn);
> >  
> > -        return new ByteArrayInputStream(
> > -          HttpConstants.getContentBytes(body, getRequestCharSet()));
> > +        if (!parameters.isEmpty()) {
> > +            //there are some parameters, so set the contentType header
> > +            setRequestHeader(CONTENT_TYPE);
> > +        }
> >      }
> >  
> > -
> > -    // ------------------------------------------------------------Class Methods
> > -
> >      /**
> > -     * Encode the list of parameters into a query string.
> > +     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > +     * to write request parameters as the request body.  The input stream will
> > +     * be truncated after the specified content length.
> > +     * 
> > +     * @param state the client state
> > +     * @param conn the connection to write to
> >       *
> > -     * @param params the list of query name and value
> > +     * @return always returns true
> >       *
> > -     * @return the query string
> > +     * @throws IOException if the stream ends before the specified content
> > +     *         length. <p>
> > +     * @throws HttpException when a protocol error occurs or state is invalid
> >       *
> >       * @since 2.0
> >       */
> > -    protected static String generateRequestBodyAsString(List params) {
> > -        LOG.trace("enter PostMethod.generateRequestBodyAsString(List)");
> >  
> > -        Iterator it = params.iterator();
> > -        StringBuffer buff = new StringBuffer();
> > -
> > -        while (it.hasNext()) {
> > -            NameValuePair parameter = (NameValuePair) it.next();
> > +    protected boolean writeRequestBody(HttpState state, HttpConnection conn)
> > +    throws IOException, HttpException {
> > +        log.trace(
> > +            "enter PostMethod.writeRequestBody(HttpState, HttpConnection)");
> >  
> > -            String queryName = null;
> > -            try {
> > -                queryName = URIUtil.encodeWithinQuery(parameter.getName());
> > -            } catch (URIException urie) {
> > -                LOG.error("encoding error within query name", urie);
> > -                queryName = parameter.getName();
> > -            }
> > -            buff.append(queryName).append("=");
> > -            String queryValue = null;
> > -            try {
> > -                queryValue = URIUtil.encodeWithinQuery(parameter.getValue());
> > -            } catch (URIException urie) {
> > -                LOG.error("encoding error within query value", urie);
> > -                queryValue = parameter.getValue();
> > -            }
> > -            buff.append(queryValue);
> > -            if (it.hasNext()) {
> > -                buff.append("&");
> > -            }
> > +        if (!hasRequestBody()) {
> > +            super.setRequestBody(generateRequestBody(parameters));
> >          }
> > -        return buff.toString();
> > +        return super.writeRequestBody(state, conn);
> >      }
> >  
> >      /**
> > -     * Buffers the request body and calculates the content length. If the
> > -     * method was called earlier it returns immediately.
> > +     * Override method of {@link org.apache.commons.httpclient.HttpMethodBase}
> > +     * to clear my request body.
> >       *
> >       * @since 1.0
> >       */
> > -    private void bufferContent() {
> > -        LOG.trace("enter PostMethod.bufferContent()");
> > -
> > -        if (buffer != null) {
> > -            return;
> > -        }
> > -
> > -        try {
> > -            buffer = new ByteArrayOutputStream();
> > -
> > -            byte[] data = new byte[10000];
> > -            int l = requestBody.read(data);
> > -            int total = 0;
> > -
> > -            while (l > 0) {
> > -                buffer.write(data, 0, l);
> > -                total += l;
> > -                l = requestBody.read(data);
> > -            }
> > -
> > -            requestBody = new ByteArrayInputStream(buffer.toByteArray());
> > -            requestContentLength = total;
> > -        } catch (IOException e) {
> > -            requestBody = null;
> > -            requestContentLength = 0;
> > -        }
> > +    public void recycle() {
> > +        log.trace("enter PostMethod.recycle()");
> > +        super.recycle();
> > +        this.parameters.clear();
> >      }
> > +
> >  }
> > Index: java/org/apache/commons/httpclient/methods/PutMethod.java
> > ===================================================================
> > RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PutMethod.java,v
> > retrieving revision 1.20
> > diff -u -r1.20 PutMethod.java
> > --- java/org/apache/commons/httpclient/methods/PutMethod.java	28 Jan 2003 22:25:28 -0000	1.20
> > +++ java/org/apache/commons/httpclient/methods/PutMethod.java	29 Jan 2003 16:12:02 -0000
> > @@ -1,8 +1,7 @@
> >  /*
> > - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PutMethod.java,v 1.20 2003/01/28 22:25:28 jsdever Exp $
> > - * $Revision: 1.20 $
> > - * $Date: 2003/01/28 22:25:28 $
> > - *
> > + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PutMethod.java,v 1.19 2003/01/23 22:48:09 jsdever Exp $
> > + * $Revision: 1.19 $
> > + * $Date: 2003/01/23 22:48:09 $
> >   * ====================================================================
> >   *
> >   * The Apache Software License, Version 1.1
> > @@ -63,34 +62,15 @@
> >  
> >  package org.apache.commons.httpclient.methods;
> >  
> > -import org.apache.commons.httpclient.HttpConstants;
> > -import org.apache.commons.httpclient.HttpConnection;
> > -import org.apache.commons.httpclient.HttpException;
> > -import org.apache.commons.httpclient.HttpMethodBase;
> > -import org.apache.commons.httpclient.HttpState;
> > -import org.apache.commons.httpclient.HttpStatus;
> > -import org.apache.commons.logging.Log;
> > -import org.apache.commons.logging.LogFactory;
> > -
> > -import java.io.ByteArrayInputStream;
> > -import java.io.ByteArrayOutputStream;
> > -import java.io.File;
> > -import java.io.FileInputStream;
> > -import java.io.IOException;
> > -import java.io.InputStream;
> > -import java.io.OutputStream;
> > -import java.net.URL;
> > -
> > -
> >  /**
> >   * PUT Method.
> >   *
> >   * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
> > - * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
> > + * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
> >   *
> >   * @since 1.0
> >   */
> > -public class PutMethod extends HttpMethodBase {
> > +public class PutMethod extends EntityEnclosingMethod {
> >  
> >  
> >      // ----------------------------------------------------------- Constructors
> > @@ -118,28 +98,6 @@
> >          setFollowRedirects(false);
> >      }
> >  
> > -
> > -    // ------------------------------------------------------- Instance Methods
> > -
> > -
> > -    /**
> > -     * Request body content to be sent.
> > -     */
> > -    private byte[] data = null;
> > -
> > -
> > -    /**
> > -    * Request body content to be sent.
> > -     */
> > -    private File file = null;
> > -
> > -
> > -    /**
> > -     * Request body content to be sent.
> > -     */
> > -    private URL url = null;
> > -
> > -
> >      // --------------------------------------------------------- Public Methods
> >  
> >      /**
> > @@ -151,187 +109,4 @@
> >      public String getName() {
> >          return "PUT";
> >      }
> > -
> > -    /**
> > -     * Set my request body content to the contents of a file.
> > -     *
> > -     * @param file The file
> > -     * @throws IOException if an IO problem occurs
> > -     * @since 2.0
> > -     */
> > -    public void setRequestBody(File file) throws IOException {
> > -        checkNotUsed();
> > -        this.file = file;
> > -    }
> > -
> > -    /**
> > -     * Set my request body content to the resource at the specified URL.
> > -     *
> > -     * @param url The URL
> > -     * @throws IOException If an IO problem occurs.
> > -     * @since 2.0
> > -     */
> > -    public void setRequestBody(URL url) throws IOException {
> > -        checkNotUsed();
> > -        this.url = url;
> > -    }
> > -
> > -    /**
> > -     * Set my request body content to the contents of a byte array.
> > -     *
> > -     * @param bodydata The new content.
> > -     * @since 2.0
> > -     */
> > -    public void setRequestBody(byte[] bodydata) {
> > -        checkNotUsed();
> > -        this.data = bodydata;
> > -    }
> > -
> > -    /**
> > -     * Set my request body content to the contents of a string.
> > -     *
> > -     * @param bodydata The new content
> > -     * @since 2.0
> > -     */
> > -    public void setRequestBody(String bodydata) {
> > -        checkNotUsed();
> > -        setRequestBody(HttpConstants.getContentBytes(bodydata, getRequestCharSet()));
> > -    }
> > -
> > -    /**
> > -     * Set my request body content to the contents of an input stream. The
> > -     * contents will be buffered into memory. To upload large entities, it is
> > -     * recommended to first buffer the data into a temporary file, and then send
> > -     * that file.
> > -     *
> > -     * @param is The input stream.
> > -     * @throws IOException If an IO problem occurs
> > -     * @since 2.0
> > -     */
> > -    public void setRequestBody(InputStream is) throws IOException {
> > -        LOG.trace("enter PutMethod.setRequestBody(InputStream)");
> > -
> > -        checkNotUsed();
> > -        byte[] buffer = new byte[4096];
> > -        ByteArrayOutputStream os = new ByteArrayOutputStream();
> > -        int nb = 0;
> > -        while (true) {
> > -            nb = is.read(buffer);
> > -            if (nb == -1) {
> > -                break;
> > -            }
> > -            os.write(buffer, 0, nb);
> > -        }
> > -        data = os.toByteArray();
> > -    }
> > -
> > -
> > -    // ------------------------------------------------- HttpMethodBase Methods
> > -
> > -    /**
> > -     * Override the method of {@link HttpMethodBase} to set the <tt>Expect</tt>
> > -     * header if it has not already been set, in addition to the "standard" set
> > -     * of headers.
> > -     * 
> > -     * @param state The state.
> > -     * @param conn The connection.
> > -     * @throws IOException If an IO problem occurs
> > -     * @throws HttpException Never.
> > -     * @since 2.0
> > -     */
> > -    protected void addRequestHeaders(HttpState state, HttpConnection conn)
> > -    throws IOException, HttpException {
> > -        // TODO: Determine why this method is declared to throw HttpException
> > -        // since it never actually does throw it.
> > -        LOG.trace("enter PutMethod.addRequestHeaders(HttpState, HttpConnection)");
> > -
> > -        super.addRequestHeaders(state, conn);
> > -        // Send expectation header
> > -        if (isHttp11() && null == getRequestHeader("expect")) {
> > -            setRequestHeader("Expect", "100-continue");
> > -        }
> > -    }
> > -
> > -    /**
> > -     * Override the method of {@link HttpMethodBase} to not send any data until
> > -     * the <tt>100 Continue</tt> status has not be read.
> > -     *
> > -     * @param state The state
> > -     * @param conn The connection
> > -     * @return true if the data was written.
> > -     * @throws IOException If an IO problem occurs
> > -     * @throws HttpException This doesn't ever seem to be thrown.
> > -     * @since 2.0
> > -     */
> > -    protected boolean writeRequestBody(HttpState state, HttpConnection conn)
> > -    throws IOException, HttpException {
> > -        LOG.trace("enter PutMethod.writeRequestBody(HttpState, HttpConnection)");
> > -         if (getStatusLine() == null) {
> > -             return false;
> > -         }
> > -         if (null != getRequestHeader("expect") 
> > -             && getStatusLine().getStatusCode() != HttpStatus.SC_CONTINUE) {
> > -            return false;
> > -        }
> > -        OutputStream out = conn.getRequestOutputStream((isHttp11() 
> > -            && (null == getRequestHeader("Content-Length"))));
> > -
> > -        InputStream inputStream = null;
> > -        if (file != null && file.exists()) {
> > -            inputStream = new FileInputStream(file);
> > -        } else if (url != null) {
> > -            inputStream = url.openConnection().getInputStream();
> > -        } else if (data != null) {
> > -            inputStream = new ByteArrayInputStream(data);
> > -        } else {
> > -            return true;
> > -        }
> > -
> > -        byte[] buffer = new byte[4096];
> > -        int nb = 0;
> > -        while (true) {
> > -            nb = inputStream.read(buffer);
> > -            if (nb == -1) {
> > -                break;
> > -            }
> > -            out.write(buffer, 0, nb);
> > -        }
> > -        out.flush();
> > -        return true;
> > -    }
> > -
> > -    /**
> > -     * Override the method of {@link HttpMethodBase}
> > -     * to return the appropriate content length.
> > -     *
> > -     * @return the content length
> > -     * @since 2.0
> > -     */
> > -    protected int getRequestContentLength() {
> > -        LOG.trace("enter PutMethod.getRequestContentLength()");
> > -
> > -        if (null != data) {
> > -            return data.length;
> > -        } else if (null != file && file.exists()) {
> > -            return (int) (file.length());
> > -        } else if (url != null) {
> > -            return -1;
> > -        } else {
> > -            return 0;
> > -        }
> > -    }
> > -
> > -    /**
> > -     *
> > -     * @since 1.0
> > -     */
> > -    public void recycle() {
> > -        super.recycle();
> > -        data = null;
> > -        url = null;
> > -        file = null;
> > -    }
> > -
> > -    /** Log object for this class. */
> > -    private static final Log LOG = LogFactory.getLog(PutMethod.class);
> >  }
> > 
> > 
> > ------------------------------------------------------------------------
> > 
> > ---------------------------------------------------------------------
> > 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
-- 
Oleg Kalnichevski <o.kalnichevski@dplanet.ch>


Mime
View raw message