Index: java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java,v retrieving revision 1.3 diff -u -r1.3 ByteArrayRequestEntity.java --- java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java 13 May 2004 02:26:08 -0000 1.3 +++ java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java 26 Sep 2004 15:05:20 -0000 @@ -85,8 +85,8 @@ /* (non-Javadoc) * @see org.apache.commons.httpclient.RequestEntity#writeRequest(java.io.OutputStream) */ - public void writeRequest(OutputStream out) throws IOException { - out.write(content); + public int writeRequest(final OutputStream out, final Monitor monitor) throws IOException { + return EntityWriter.writeContent(this.content, out, monitor); } /** 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.39 diff -u -r1.39 EntityEnclosingMethod.java --- java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java 3 Jul 2004 14:27:03 -0000 1.39 +++ java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java 26 Sep 2004 15:05:21 -0000 @@ -1,5 +1,5 @@ /* - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java,v 1.39 2004/07/03 14:27:03 olegk Exp $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java,v 1.39 2004/07/03 14:27:03 olegk Exp $ * $Revision: 1.39 $ * $Date: 2004/07/03 14:27:03 $ * @@ -454,7 +454,7 @@ * @throws HttpException if a protocol exception occurs. Usually protocol exceptions * cannot be recovered from. */ - protected boolean writeRequestBody(HttpState state, HttpConnection conn) + protected boolean writeRequestBody(final HttpState state, final HttpConnection conn) throws IOException, HttpException { LOG.trace( "enter EntityEnclosingMethod.writeRequestBody(HttpState, HttpConnection)"); @@ -492,16 +492,37 @@ outstream = new ChunkedOutputStream(outstream); } - requestEntity.writeRequest(outstream); + RequestEntity.Monitor monitor = new RequestEntity.Monitor() { + + public boolean isResponseAvailable() throws IOException { + return conn.isResponseAvailable(); + } + + }; + + // An HTTP/1.1 (or later) client sending a message-body SHOULD monitor + // the network connection for an error status while it is transmitting + // the request. If the client sees an error status, it SHOULD + // immediately cease transmitting the body. If the body is being sent + // using a "chunked" encoding (section 3.6), a zero length chunk and + // empty trailer MAY be used to prematurely mark the end of the message. + // If the body was preceded by a Content-Length header, the client MUST + // close the connection. - // This is hardly the most elegant solution to closing chunked stream + int result = requestEntity.writeRequest(outstream, monitor); + // TODO: This is hardly the most elegant solution to closing chunked stream + // Consider a better solution if (outstream instanceof ChunkedOutputStream) { ((ChunkedOutputStream) outstream).finish(); } - outstream.flush(); - - LOG.debug("Request body sent"); + if (result == RequestEntity.WRITE_COMPLETE) { + LOG.debug("Request body sent"); + } + if (contentLength != CONTENT_LENGTH_CHUNKED && result == RequestEntity.WRITE_PARTIAL) { + LOG.debug("Request has not been fully sent"); + setConnectionCloseForced(true); + } return true; } Index: java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java,v retrieving revision 1.4 diff -u -r1.4 InputStreamRequestEntity.java --- java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java 17 May 2004 21:46:03 -0000 1.4 +++ java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java 26 Sep 2004 15:05:22 -0000 @@ -1,5 +1,5 @@ /* - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java,v 1.4 2004/05/17 21:46:03 olegk Exp $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java,v 1.4 2004/05/17 21:46:03 olegk Exp $ * $Revision: 1.4 $ * $Date: 2004/05/17 21:46:03 $ * @@ -164,18 +164,12 @@ /* (non-Javadoc) * @see org.apache.commons.httpclient.RequestEntity#writeRequest(java.io.OutputStream) */ - public void writeRequest(OutputStream out) throws IOException { + public int writeRequest(final OutputStream out, final Monitor monitor) throws IOException { - if (content != null) { - byte[] tmp = new byte[4096]; - int total = 0; - int i = 0; - while ((i = content.read(tmp)) >= 0) { - out.write(tmp, 0, i); - total += i; - } - } else if (buffer != null) { - out.write(buffer); + if (this.content != null) { + return EntityWriter.writeContent(this.content, out, monitor); + } else if (this.buffer != null) { + return EntityWriter.writeContent(this.buffer, out, monitor); } else { throw new IllegalStateException("Content must be set before entity is written"); } Index: java/org/apache/commons/httpclient/methods/RequestEntity.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/RequestEntity.java,v retrieving revision 1.4 diff -u -r1.4 RequestEntity.java --- java/org/apache/commons/httpclient/methods/RequestEntity.java 17 May 2004 21:46:03 -0000 1.4 +++ java/org/apache/commons/httpclient/methods/RequestEntity.java 26 Sep 2004 15:05:22 -0000 @@ -1,5 +1,5 @@ /* - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/RequestEntity.java,v 1.4 2004/05/17 21:46:03 olegk Exp $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/RequestEntity.java,v 1.4 2004/05/17 21:46:03 olegk Exp $ * $Revision: 1.4 $ * $Date: 2004/05/17 21:46:03 $ * @@ -37,6 +37,31 @@ */ public interface RequestEntity { + public static int WRITE_COMPLETE = 0; + + public static int WRITE_PARTIAL = -1; + + /** + * As suggested in the HTTP/1.1 spec, the client sending a message-body SHOULD monitor + * the network connection for an error status while it is transmitting the request. + * If the client sees an error status, it SHOULD immediately cease transmitting the body. + * + * This interface defines a connection monitor for request entities. + */ + public static interface Monitor { + + /** + * Tests whether a response is already available. + * + * @return true is a response is already available, + * false otherwise. + * + * @throws IOException if an I/O error occurs + */ + boolean isResponseAvailable() throws IOException; + + } + /** * Tests if {@link #writeRequest(OutputStream)} can be called more than once. * @@ -47,10 +72,16 @@ /** * Writes the request entity to the given stream. - * @param out - * @throws IOException + * @param out output stream + * @param monitor connection monitor + * + * @return WRITE_COMPLETE is the request entity has been fully transmitted. + * WRITE_PARTIAL is the request entity has not been fully transmitted + * and the connection may need to be closed. + * + * @throws IOException if an I/O error occurs */ - void writeRequest(OutputStream out) throws IOException; + int writeRequest(OutputStream out, Monitor monitor) throws IOException; /** * Gets the request entity's length. @@ -66,5 +97,5 @@ * @see org.apache.commons.httpclient.HttpMethod#setRequestHeader(String, String) */ String getContentType(); - + } Index: java/org/apache/commons/httpclient/methods/StringRequestEntity.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/StringRequestEntity.java,v retrieving revision 1.3 diff -u -r1.3 StringRequestEntity.java --- java/org/apache/commons/httpclient/methods/StringRequestEntity.java 3 Jul 2004 14:27:03 -0000 1.3 +++ java/org/apache/commons/httpclient/methods/StringRequestEntity.java 26 Sep 2004 15:05:23 -0000 @@ -1,5 +1,5 @@ /* - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/StringRequestEntity.java,v 1.3 2004/07/03 14:27:03 olegk Exp $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/StringRequestEntity.java,v 1.3 2004/07/03 14:27:03 olegk Exp $ * $Revision: 1.3 $ * $Date: 2004/07/03 14:27:03 $ * @@ -132,12 +132,8 @@ /* (non-Javadoc) * @see org.apache.commons.httpclient.RequestEntity#writeRequest(java.io.OutputStream) */ - public void writeRequest(OutputStream out) throws IOException { - if (out == null) { - throw new IllegalArgumentException("Output stream may not be null"); - } - out.write(this.content); - out.flush(); + public int writeRequest(final OutputStream out, Monitor monitor) throws IOException { + return EntityWriter.writeContent(this.content, out, monitor); } /** Index: test/org/apache/commons/httpclient/TestMethodCharEncoding.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestMethodCharEncoding.java,v retrieving revision 1.10 diff -u -r1.10 TestMethodCharEncoding.java --- test/org/apache/commons/httpclient/TestMethodCharEncoding.java 3 Jul 2004 14:27:03 -0000 1.10 +++ test/org/apache/commons/httpclient/TestMethodCharEncoding.java 26 Sep 2004 15:05:24 -0000 @@ -170,8 +170,16 @@ assertNotNull("Request body", entity); + RequestEntity.Monitor monitor = new RequestEntity.Monitor() { + + public boolean isResponseAvailable() throws IOException { + return false; + } + + }; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); - entity.writeRequest(bos); + entity.writeRequest(bos, monitor); InputStream instream = new ByteArrayInputStream(bos.toByteArray()); for (int i = 0; i < sample.length; i++) { @@ -258,8 +266,16 @@ httppost.setRequestHeader("Content-Type", PostMethod.FORM_URL_ENCODED_CONTENT_TYPE + "; charset=" + CHARSET_UTF8); + RequestEntity.Monitor monitor = new RequestEntity.Monitor() { + + public boolean isResponseAvailable() throws IOException { + return false; + } + + }; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); - httppost.getRequestEntity().writeRequest(bos); + httppost.getRequestEntity().writeRequest(bos, monitor); Map params = new HashMap(); StringTokenizer tokenizer = new StringTokenizer( Index: test/org/apache/commons/httpclient/TestMethodsNoHost.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestMethodsNoHost.java,v retrieving revision 1.23 diff -u -r1.23 TestMethodsNoHost.java --- test/org/apache/commons/httpclient/TestMethodsNoHost.java 12 May 2004 20:43:54 -0000 1.23 +++ test/org/apache/commons/httpclient/TestMethodsNoHost.java 26 Sep 2004 15:05:25 -0000 @@ -1,5 +1,5 @@ /* - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestMethodsNoHost.java,v 1.23 2004/05/12 20:43:54 olegk Exp $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestMethodsNoHost.java,v 1.23 2004/05/12 20:43:54 olegk Exp $ * $Revision: 1.23 $ * $Date: 2004/05/12 20:43:54 $ * ==================================================================== @@ -31,6 +31,7 @@ package org.apache.commons.httpclient; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; @@ -77,8 +78,17 @@ // ----------------------------------------------------------------- Tests private String getRequestAsString(RequestEntity entity) throws Exception { + + RequestEntity.Monitor monitor = new RequestEntity.Monitor() { + + public boolean isResponseAvailable() throws IOException { + return false; + } + + }; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); - entity.writeRequest(bos); + entity.writeRequest(bos, monitor); return new String(bos.toByteArray(), "UTF-8"); } Index: test/org/apache/commons/httpclient/TestWebappPostMethod.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebappPostMethod.java,v retrieving revision 1.7 diff -u -r1.7 TestWebappPostMethod.java --- test/org/apache/commons/httpclient/TestWebappPostMethod.java 12 May 2004 20:43:54 -0000 1.7 +++ test/org/apache/commons/httpclient/TestWebappPostMethod.java 26 Sep 2004 15:05:25 -0000 @@ -1,5 +1,5 @@ /* - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebappPostMethod.java,v 1.7 2004/05/12 20:43:54 olegk Exp $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestWebappPostMethod.java,v 1.7 2004/05/12 20:43:54 olegk Exp $ * $Revision: 1.7 $ * $Date: 2004/05/12 20:43:54 $ * @@ -33,6 +33,7 @@ import junit.framework.*; import org.apache.commons.httpclient.methods.*; + import java.io.*; /** @@ -177,8 +178,17 @@ } private String getRequestAsString(RequestEntity entity) throws Exception { + + RequestEntity.Monitor monitor = new RequestEntity.Monitor() { + + public boolean isResponseAvailable() throws IOException { + return false; + } + + }; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); - entity.writeRequest(bos); + entity.writeRequest(bos, monitor); return new String(bos.toByteArray(), "UTF-8"); } Index: java/org/apache/commons/httpclient/methods/EntityWriter.java =================================================================== RCS file: java/org/apache/commons/httpclient/methods/EntityWriter.java diff -N java/org/apache/commons/httpclient/methods/EntityWriter.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ java/org/apache/commons/httpclient/methods/EntityWriter.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,99 @@ +/* + * $Header$ + * $Revision$ + * $Date$ + * + * ==================================================================== + * + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ==================================================================== + * + * 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 + * . + * + * [Additional notices, if required by prior licensing conditions] + * + */ +package org.apache.commons.httpclient.methods; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * @since 3.0 + */ +public abstract class EntityWriter implements RequestEntity { + + private EntityWriter() { + super(); + } + + public static int writeContent(final byte[] content, + final OutputStream out, Monitor monitor) throws IOException { + if (content == null) { + throw new IllegalArgumentException("Content may not be null"); + } + if (out == null) { + throw new IllegalArgumentException("Output stream may not be null"); + } + if (monitor == null) { + throw new IllegalArgumentException("Feedback interface may not be null"); + } + int i = 0; + int remaining = content.length; + int chunk = 1024; + while (remaining > 0) { + if (monitor.isResponseAvailable()) { + return WRITE_PARTIAL; + } + if (remaining < 1024) { + chunk = remaining; + } else { + chunk = 1024; + } + out.write(content, i, chunk); + i += chunk; + remaining -= chunk; + } + out.flush(); + return WRITE_COMPLETE; + } + + public static int writeContent(final InputStream in, + final OutputStream out, Monitor monitor) throws IOException { + if (in == null) { + throw new IllegalArgumentException("Input stream may not be null"); + } + if (out == null) { + throw new IllegalArgumentException("Output stream may not be null"); + } + if (monitor == null) { + throw new IllegalArgumentException("Feedback interface may not be null"); + } + byte[] tmp = new byte[1024]; + int i = 0; + while ((i = in.read(tmp)) >= 0) { + if (monitor.isResponseAvailable()) { + return WRITE_PARTIAL; + } + out.write(tmp, 0, i); + } + out.flush(); + return WRITE_COMPLETE; + } +}