You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@hc.apache.org by Oleg Kalnichevski <o....@dplanet.ch> on 2003/02/13 21:20:53 UTC

[PATCH]: 100-continue support in MultipartPost, non-compliant emptry chunk-enconded content fix

Bug fixes

http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16892
http://nagoya.apache.org/bugzilla/show_bug.cgi?id=14036

- Chunk-encoding problem related to non-compliant empty content streams
generated by IIS has been fixed
- MultipartPost method now supports "expect: 100-continue" handshake

All changes are incremental. I'll commit the patch before midnight (+1
GMT) if nobody loudly objects

Cheers

Oleg

Re: [PATCH]: 100-continue support in MultipartPost, non-compliant emptry chunk-enconded content fix

Posted by Oleg Kalnichevski <o....@dplanet.ch>.
On Thu, 2003-02-13 at 21:30, Jeffrey Dever wrote:
> +    public boolean responseAvaliable()

> Might be a little more standard by using:
> 
> +    public boolean isResponseAvaliable()
> 
> 

Agreed. 


> +        LOG.trace("enter recycle()");
> 
> should be:
> 
> +        LOG.trace("enter MultiPartPostMethod.recycle()");
> 
> But I kinda dispise those statements anyway.  I'm looking into aspectJ 
> for handling stuff like that, but not till 2.1
> 

So do I. I find trace verbosity next to being useless. I'll correct the
messages

Oleg


Re: [PATCH]: 100-continue support in MultipartPost, non-compliant emptry chunk-enconded content fix

Posted by Jeffrey Dever <js...@sympatico.ca>.
+    public boolean responseAvaliable()

Might be a little more standard by using:

+    public boolean isResponseAvaliable()


+        LOG.trace("enter recycle()");

should be:

+        LOG.trace("enter MultiPartPostMethod.recycle()");

But I kinda dispise those statements anyway.  I'm looking into aspectJ 
for handling stuff like that, but not till 2.1

Jandalf.


Oleg Kalnichevski wrote:

>Bug fixes
>
>http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16892
>http://nagoya.apache.org/bugzilla/show_bug.cgi?id=14036
>
>- Chunk-encoding problem related to non-compliant empty content streams
>generated by IIS has been fixed
>- MultipartPost method now supports "expect: 100-continue" handshake
>
>All changes are incremental. I'll commit the patch before midnight (+1
>GMT) if nobody loudly objects
>
>Cheers
>
>Oleg
>  
>
>------------------------------------------------------------------------
>
>Index: java/org/apache/commons/httpclient/ChunkedInputStream.java
>===================================================================
>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/ChunkedInputStream.java,v
>retrieving revision 1.13
>diff -u -r1.13 ChunkedInputStream.java
>--- java/org/apache/commons/httpclient/ChunkedInputStream.java	11 Feb 2003 03:23:05 -0000	1.13
>+++ java/org/apache/commons/httpclient/ChunkedInputStream.java	13 Feb 2003 20:08:35 -0000
>@@ -87,6 +87,7 @@
>  * @author Eric Johnson
>  * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
>  * @author Michael Becke
>+ * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
>  *
>  * @since 2.0
>  *
>@@ -130,7 +131,7 @@
>       }
>         this.in = in;
>         this.method = method;
>-        this.chunkSize = getChunkSizeFromInputStream(in);
>+        this.chunkSize = getChunkSizeFromInputStream(in, false);
>         if (chunkSize == 0) {
>             eof = true;
>             parseTrailerHeaders();
>@@ -239,11 +240,16 @@
>      * comment\r\n" Positions the stream at the start of the next line.
>      *
>      * @param in The new input stream.
>+     * @param required <tt>true<tt/> if a valid chunk must be present,
>+     *                 <tt>false<tt/> otherwise.
>+     * 
>      * @return the chunk size as integer
>+     * 
>      * @throws IOException when the chunk size could not be parsed
>      */
>     private static int getChunkSizeFromInputStream(
>-        final InputStream in) throws IOException {
>+      final InputStream in, boolean required) 
>+      throws IOException {
>             
>         ByteArrayOutputStream baos = new ByteArrayOutputStream();
>         // States: 0=normal, 1=\r was scanned, 2=inside quoted string, -1=end
>@@ -251,7 +257,11 @@
>         while (state != -1) {
>         int b = in.read();
>             if (b == -1) { 
>-                throw new IOException("chunked stream ended unexpectedly");
>+                if (required) {
>+                    throw new IOException("chunked stream ended unexpectedly");
>+                } else {
>+                    return 0;
>+                }
>             }
>             switch (state) {
>                 case 0: 
>@@ -310,6 +320,21 @@
>         return result;
>     }
> 
>+    /**
>+     * Expects the stream to start with a chunksize in hex with optional
>+     * comments after a semicolon. The line must end with a CRLF: "a3; some
>+     * comment\r\n" Positions the stream at the start of the next line.
>+     *
>+     * @param in The new input stream.
>+     * 
>+     * @return the chunk size as integer
>+     * 
>+     * @throws IOException when the chunk size could not be parsed
>+     */
>+    private static int getChunkSizeFromInputStream(final InputStream in) 
>+      throws IOException {
>+        return getChunkSizeFromInputStream(in, true);
>+    }
>     /**
>      * Reads and stores the Trailer headers.
>      * @throws IOException If an IO problem occurs
>Index: 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.43
>diff -u -r1.43 HttpConnection.java
>--- java/org/apache/commons/httpclient/HttpConnection.java	11 Feb 2003 03:23:05 -0000	1.43
>+++ java/org/apache/commons/httpclient/HttpConnection.java	13 Feb 2003 20:08:33 -0000
>@@ -692,11 +692,29 @@
>     }
> 
>     /**
>+     * Tests if input data avaialble.
>+     * 
>+     * @return boolean <tt>true</tt> if input data is availble, 
>+     *                 <tt>false</tt> otherwise.
>+     * 
>+     * @throws IOException If an IO problem occurs
>+     * @throws IllegalStateException If the connection isn't open.
>+     */
>+    public boolean responseAvaliable() 
>+        throws IOException {
>+        LOG.trace("enter HttpConnection.responseAvaliable()");
>+        assertOpen();
>+        return this.inputStream.available() > 0;
>+    }
>+
>+
>+    /**
>      * Waits for the specified number of milliseconds for input data to become available
>      * 
>      * @param timeout_ms Number of milliseconds to wait for input data.
>      * 
>-     * @return boolean <tt>true</tt> if input data is availble, <tt>false</tt> otherwise.
>+     * @return boolean <tt>true</tt> if input data is availble, 
>+     *                 <tt>false</tt> otherwise.
>      * 
>      * @throws IOException If an IO problem occurs
>      * @throws IllegalStateException If the connection isn't open.
>@@ -704,13 +722,12 @@
>     public boolean waitForResponse(long timeout_ms)
>         throws IOException, IllegalStateException {
>         LOG.trace("enter HttpConnection.waitForResponse(int)");
>-        assertOpen();
>         if (timeout_ms < 0) {
>             throw new IllegalArgumentException("Timeout value may not be negative");
>         }
>         long overtime = System.currentTimeMillis() + timeout_ms;
>         while (System.currentTimeMillis() < overtime) {
>-            if (inputStream.available() > 0) {
>+            if (responseAvaliable()) {
>                 return true;
>             }
>         }
>Index: java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java
>===================================================================
>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java,v
>retrieving revision 1.8
>diff -u -r1.8 EntityEnclosingMethod.java
>--- java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java	8 Feb 2003 19:22:49 -0000	1.8
>+++ java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java	13 Feb 2003 20:08:38 -0000
>@@ -126,7 +126,8 @@
>      */
>     private int requestContentLength = CONTENT_LENGTH_AUTO;
> 
>-    
>+    /** This flag specifies whether "expect: 100-continue" handshake is
>+     * to be used prior to sending the requesst body */
>     private boolean useExpectHeader = true;
>     
>     // ----------------------------------------------------------- Constructors
>Index: 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.9
>diff -u -r1.9 MultipartPostMethod.java
>--- java/org/apache/commons/httpclient/methods/MultipartPostMethod.java	12 Feb 2003 12:30:44 -0000	1.9
>+++ java/org/apache/commons/httpclient/methods/MultipartPostMethod.java	13 Feb 2003 20:08:40 -0000
>@@ -73,6 +73,7 @@
> 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.methods.multipart.FilePart;
> import org.apache.commons.httpclient.methods.multipart.Part;
> import org.apache.commons.httpclient.methods.multipart.StringPart;
>@@ -104,6 +105,10 @@
>     /** The parameters for this method */
>     private final List parameters = new ArrayList();
> 
>+    /** This flag specifies whether "expect: 100-continue" handshake is
>+     * to be used prior to sending the request body */
>+    private boolean useExpectHeader = true;
>+
>     /**
>      * No-arg constructor.
>      */
>@@ -141,7 +146,6 @@
>         super(uri, tempDir, tempFile);
>     }
> 
>-
>     /**
>      * Returns <tt>"POST"</tt>.
>      * @return <tt>"POST"</tt>
>@@ -151,12 +155,19 @@
>     }
> 
>     /**
>-     * Clear my request body.
>+     * Returns the useExpectHeader.
>+     * @return boolean
>      */
>-    public void recycle() {
>-        LOG.trace("enter recycle()");
>-        super.recycle();
>-        parameters.clear();
>+    public boolean getUseExpectHeader() {
>+        return this.useExpectHeader;
>+    }
>+
>+    /**
>+     * Sets the useExpectHeader.
>+     * @param value The useExpectHeader to set
>+     */
>+    public void setUseExpectHeader(boolean value) {
>+        this.useExpectHeader = value;
>     }
> 
>     /**
>@@ -216,11 +227,14 @@
>         return (Part[])parameters.toArray(new Part[parameters.size()]);
>     }
>     /**
>-     * Add a request header.
>+     * Add content type header and set the <tt>Expect</tt> header 
>+     * if it has not already been set, in addition to the "standard"
>+     * set of headers
>      * 
>      * @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
>      */
>@@ -237,6 +251,16 @@
>             }
>             setRequestHeader("Content-Type", buffer.toString());
>         }
>+        boolean headerPresent = (getRequestHeader("Expect") != null);
>+        if (getUseExpectHeader() && isHttp11()) { 
>+            if (!headerPresent) {
>+                setRequestHeader("Expect", "100-continue");
>+            }
>+        } else {
>+            if (headerPresent) {
>+                removeRequestHeader("Expect");
>+            }
>+        }
>     }
> 
>     /**
>@@ -252,6 +276,16 @@
>     protected boolean writeRequestBody(HttpState state, HttpConnection conn) 
>     throws IOException, HttpException {
>         LOG.trace("enter writeRequestBody(HttpState state, HttpConnection conn)");
>+        if (getRequestHeader("Expect") != null) {
>+            if (getStatusLine() == null) {
>+                LOG.debug("Expecting response");
>+                return false;
>+            }
>+            if (getStatusLine().getStatusCode() != HttpStatus.SC_CONTINUE) {
>+                LOG.debug("Expecting 100-continue");
>+                return false;
>+            }
>+        }
>         OutputStream out = conn.getRequestOutputStream();
>         Part.sendParts(out, getParts());
>         return true;
>@@ -280,4 +314,15 @@
>             throw new RuntimeException(e.toString());
>         }
>     }
>+
>+
>+    /**
>+     * Clear my request body.
>+     */
>+    public void recycle() {
>+        LOG.trace("enter recycle()");
>+        super.recycle();
>+        parameters.clear();
>+    }
>+
> }
>Index: test/org/apache/commons/httpclient/TestStreams.java
>===================================================================
>RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestStreams.java,v
>retrieving revision 1.9
>diff -u -r1.9 TestStreams.java
>--- test/org/apache/commons/httpclient/TestStreams.java	11 Feb 2003 03:23:05 -0000	1.9
>+++ test/org/apache/commons/httpclient/TestStreams.java	13 Feb 2003 20:08:27 -0000
>@@ -152,6 +152,19 @@
>         assertEquals(0, out.size());
>     }
> 
>+    public void testNoncompliantEmptyChunkedInputStream() throws IOException {
>+        HttpMethod method = new SimpleHttpMethod();
>+
>+        InputStream in = new ChunkedInputStream(new ByteArrayInputStream(new byte[] {}), method);
>+        byte[] buffer = new byte[300];
>+        ByteArrayOutputStream out = new ByteArrayOutputStream();
>+        int len;
>+        while ((len = in.read(buffer)) > 0) {
>+            out.write(buffer, 0, len);
>+        }
>+        assertEquals(0, out.size());
>+    }
>+
>     public void testContentLengthInputStream() throws IOException {
>         String correct = "1234567890123456";
>         InputStream in = new ContentLengthInputStream(new ByteArrayInputStream(HttpConstants.getBytes(correct)), 10);
>
>  
>
>------------------------------------------------------------------------
>
>---------------------------------------------------------------------
>To unsubscribe, e-mail: commons-httpclient-dev-unsubscribe@jakarta.apache.org
>For additional commands, e-mail: commons-httpclient-dev-help@jakarta.apache.org
>