Index: org/apache/commons/httpclient/methods/EntityEnclosingMethod.java =================================================================== retrieving revision 1.29 diff -u -r1.29 EntityEnclosingMethod.java --- org/apache/commons/httpclient/methods/EntityEnclosingMethod.java 22 Feb 2004 18:08:48 -0000 1.29 +++ org/apache/commons/httpclient/methods/EntityEnclosingMethod.java 6 Mar 2004 18:01:47 -0000 @@ -38,12 +38,11 @@ import java.io.OutputStream; import org.apache.commons.httpclient.ChunkedOutputStream; -import org.apache.commons.httpclient.ContentLengthInputStream; import org.apache.commons.httpclient.HttpConnection; import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpState; import org.apache.commons.httpclient.HttpVersion; import org.apache.commons.httpclient.ProtocolException; -import org.apache.commons.httpclient.HttpState; import org.apache.commons.httpclient.util.EncodingUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -77,19 +76,13 @@ /** 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 requestStream = null; /** The request body as string, if any. */ private String requestString = null; - /** for optimization purpose, the generated request body may be - * cached when the method is being executed. - */ - private byte[] contentCache = null; + private RequestEntity requestEntity; /** Counts how often the request was sent to the server. */ private int repeatCount = 0; @@ -136,7 +129,7 @@ */ protected boolean hasRequestContent() { LOG.trace("enter EntityEnclosingMethod.hasRequestContent()"); - return (this.buffer != null) + return (this.requestEntity != null) || (this.requestStream != null) || (this.requestString != null); } @@ -153,8 +146,7 @@ LOG.trace("enter EntityEnclosingMethod.clearRequestBody()"); this.requestStream = null; this.requestString = null; - this.buffer = null; - this.contentCache = null; + this.requestEntity = null; } /** @@ -170,19 +162,33 @@ */ protected byte[] generateRequestBody() { LOG.trace("enter EntityEnclosingMethod.renerateRequestBody()"); - if (this.requestStream != null) { - bufferContent(); - } - - if (this.buffer != null) { - return this.buffer; + return null; + } + + protected RequestEntity generateRequestEntity() { + + byte[] requestBody = generateRequestBody(); + if (requestBody != null) { + // use the request body, if it exists. + // this is just for backwards compatability + ByteArrayRequestEntity entity = new ByteArrayRequestEntity(); + entity.setContent(requestBody); + this.requestEntity = entity; + } else if (this.requestStream != null) { + InputStreamRequestEntity entity = new InputStreamRequestEntity(); + entity.setInputStream(requestStream); + entity.setContentLength(requestContentLength); + this.requestStream = null; + this.requestEntity = entity; } else if (this.requestString != null) { - return EncodingUtil.getBytes(this.requestString, getRequestCharSet()); - } else { - return null; + ByteArrayRequestEntity entity = new ByteArrayRequestEntity(); + entity.setContent(EncodingUtil.getBytes(this.requestString, getRequestCharSet())); + this.requestEntity = entity; } - } + return this.requestEntity; + } + /** * Entity enclosing requests cannot be redirected without user intervention * according to RFC 2616. @@ -272,13 +278,15 @@ if (!hasRequestContent()) { return 0; } + // TODO what to do about setting request content and content length if (this.requestContentLength != CONTENT_LENGTH_AUTO) { return this.requestContentLength; } - if (this.contentCache == null) { - this.contentCache = generateRequestBody(); + + if (this.requestEntity == null) { + this.requestEntity = generateRequestEntity(); } - return (this.contentCache == null) ? 0 : this.contentCache.length; + return (this.requestEntity == null) ? 0 : this.requestEntity.getContentLength(); } /** @@ -365,9 +373,18 @@ */ public InputStream getRequestBody() { LOG.trace("enter EntityEnclosingMethod.getRequestBody()"); - byte [] content = generateRequestBody(); - if (content != null) { - return new ByteArrayInputStream(content); + + // TODO we need a better way of handling this + RequestEntity entity = generateRequestEntity(); + if (entity != null) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + entity.writeRequest(os); + return new ByteArrayInputStream(os.toByteArray()); + } catch (IOException e) { + LOG.error("Error buffering content", e); + return new ByteArrayInputStream(new byte[] {}); + } } else { return new ByteArrayInputStream(new byte[] {}); } @@ -401,9 +418,11 @@ */ public String getRequestBodyAsString() throws IOException { LOG.trace("enter EntityEnclosingMethod.getRequestBodyAsString()"); - byte [] content = generateRequestBody(); - if (content != null) { - return EncodingUtil.getString(content, getRequestCharSet()); + RequestEntity entity = generateRequestEntity(); + if (entity != null) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + entity.writeRequest(os); + return EncodingUtil.getString(os.toByteArray(), getRequestCharSet()); } else { return null; } @@ -443,26 +462,13 @@ getHttpVersion().toString()); } - InputStream instream = null; - if (this.requestStream != null) { - LOG.debug("Using unbuffered request body"); - instream = this.requestStream; - } else { - if (this.contentCache == null) { - this.contentCache = generateRequestBody(); - } - if (this.contentCache != null) { - LOG.debug("Using buffered request body"); - instream = new ByteArrayInputStream(this.contentCache); - } - } - - if (instream == null) { + this.requestEntity = generateRequestEntity(); + if (requestEntity == null) { LOG.debug("Request body is empty"); return true; } - if ((this.repeatCount > 0) && (this.contentCache == null)) { + if ((this.repeatCount > 0) && !requestEntity.isRepeatable()) { // TODO: Is this the right exception to throw here? throw new ProtocolException( "Unbuffered entity enclosing request can not be repeated."); @@ -475,27 +481,14 @@ 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; - } + requestEntity.writeRequest(outstream); + // 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)"); - } + outstream.flush(); LOG.debug("Request body sent"); return true; } @@ -517,30 +510,17 @@ } /** - * Buffers request body input stream. + * @return Returns the requestEntity. */ - private void bufferContent() { - LOG.trace("enter EntityEnclosingMethod.bufferContent()"); + public RequestEntity getRequestEntity() { + return requestEntity; + } - if (this.buffer != null) { - // Already been buffered - return; - } - if (this.requestStream != null) { - try { - ByteArrayOutputStream tmp = new ByteArrayOutputStream(); - byte[] data = new byte[4096]; - int l = 0; - while ((l = this.requestStream.read(data)) >= 0) { - tmp.write(data, 0, l); - } - this.buffer = tmp.toByteArray(); - this.requestStream = null; - } catch (IOException e) { - LOG.error(e.getMessage(), e); - this.buffer = null; - this.requestStream = null; - } - } + /** + * @param requestEntity The requestEntity to set. + */ + public void setRequestEntity(RequestEntity requestEntity) { + this.requestEntity = requestEntity; } + } Index: org/apache/commons/httpclient/methods/PostMethod.java =================================================================== retrieving revision 1.52 diff -u -r1.52 PostMethod.java --- org/apache/commons/httpclient/methods/PostMethod.java 22 Feb 2004 18:08:48 -0000 1.52 +++ org/apache/commons/httpclient/methods/PostMethod.java 6 Mar 2004 18:01:48 -0000 @@ -158,29 +158,20 @@ super.clearRequestBody(); } - /** - * Generates the request body. - * - *

This method must be overwritten by sub-classes that implement - * alternative request content input methods - *

- * - * @return request body as an array of bytes. If the request content - * has not been set, returns null. - * - * @since 2.0beta1 + /* (non-Javadoc) + * @see org.apache.commons.httpclient.methods.EntityEnclosingMethod#generateRequestEntity() */ - protected byte[] generateRequestBody() { - LOG.trace("enter PostMethod.renerateRequestBody()"); + protected RequestEntity generateRequestEntity() { if (!this.params.isEmpty()) { String content = EncodingUtil.formUrlEncode(getParameters(), getRequestCharSet()); - return EncodingUtil.getAsciiBytes(content); + ByteArrayRequestEntity entity = new ByteArrayRequestEntity(); + entity.setContent(EncodingUtil.getAsciiBytes(content)); + return entity; } else { - return super.generateRequestBody(); + return super.generateRequestEntity(); } } - - + /** * Sets the value of parameter with parameterName to parameterValue. This method * does not preserve the initial insertion order. @@ -439,5 +430,4 @@ } } } - } Index: src/java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java =================================================================== RCS file: src/java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java diff -N src/java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,87 @@ +/* + * $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.OutputStream; + +/** + */ +public class ByteArrayRequestEntity implements RequestEntity { + + private byte[] content; + + /** + * + */ + public ByteArrayRequestEntity() { + super(); + } + + /* (non-Javadoc) + * @see org.apache.commons.httpclient.RequestEntity#isRepeatable() + */ + public boolean isRepeatable() { + return true; + } + + /* (non-Javadoc) + * @see org.apache.commons.httpclient.RequestEntity#writeRequest(java.io.OutputStream) + */ + public void writeRequest(OutputStream out) throws IOException { + if (content == null) { + throw new IllegalStateException("Content must be set before entity is written"); + } + out.write(content); + } + + /* (non-Javadoc) + * @see org.apache.commons.httpclient.RequestEntity#getConentLength() + */ + public long getContentLength() { + return content.length; + } + + /** + * @return Returns the content. + */ + public byte[] getContent() { + return content; + } + + /** + * @param content The content to set. + */ + public void setContent(byte[] content) { + this.content = content; + } + +} Index: src/java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java =================================================================== RCS file: src/java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java diff -N src/java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,146 @@ +/* + * $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.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + */ +public class InputStreamRequestEntity implements RequestEntity { + + private static final Log LOG = LogFactory.getLog(InputStreamRequestEntity.class); + + private long contentLength; + + private InputStream inputStream; + + /** The buffered request body, if any. */ + private byte[] buffer = null; + + public InputStreamRequestEntity() { + + } + + /** + * Buffers request body input stream. + */ + private void bufferContent() { + + if (this.buffer != null) { + // Already been buffered + return; + } + if (this.inputStream != null) { + try { + ByteArrayOutputStream tmp = new ByteArrayOutputStream(); + byte[] data = new byte[4096]; + int l = 0; + while ((l = this.inputStream.read(data)) >= 0) { + tmp.write(data, 0, l); + } + this.buffer = tmp.toByteArray(); + this.inputStream = null; + this.contentLength = buffer.length; + } catch (IOException e) { + LOG.error(e.getMessage(), e); + this.buffer = null; + this.inputStream = null; + this.contentLength = 0; + } + } + } + + + /* (non-Javadoc) + * @see org.apache.commons.httpclient.RequestEntity#isRepeatable() + */ + public boolean isRepeatable() { + return false; + } + + /* (non-Javadoc) + * @see org.apache.commons.httpclient.RequestEntity#writeRequest(java.io.OutputStream) + */ + public void writeRequest(OutputStream out) throws IOException { + + if (inputStream != null) { + byte[] tmp = new byte[4096]; + int total = 0; + int i = 0; + while ((i = inputStream.read(tmp)) >= 0) { + out.write(tmp, 0, i); + total += i; + } + } else if (buffer != null) { + out.write(buffer); + } else { + throw new IllegalStateException("Content must be set before entity is written"); + } + } + + /* (non-Javadoc) + * @see org.apache.commons.httpclient.RequestEntity#getConentLength() + */ + public long getContentLength() { + if (contentLength == EntityEnclosingMethod.CONTENT_LENGTH_AUTO && buffer == null) { + bufferContent(); + } + return contentLength; + } + + /** + * @param contentLength The contentLength to set. + */ + public void setContentLength(long contentLength) { + this.contentLength = contentLength; + } + + /** + * @return Returns the inputStream. + */ + public InputStream getInputStream() { + return inputStream; + } + + /** + * @param inputStream The inputStream to set. + */ + public void setInputStream(InputStream inputStream) { + this.inputStream = inputStream; + } + +} Index: src/java/org/apache/commons/httpclient/methods/RequestEntity.java =================================================================== RCS file: src/java/org/apache/commons/httpclient/methods/RequestEntity.java diff -N src/java/org/apache/commons/httpclient/methods/RequestEntity.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/java/org/apache/commons/httpclient/methods/RequestEntity.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,61 @@ +/* + * $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.OutputStream; + +/** + */ +public interface RequestEntity { + + /** + * Tests if {@link #writeRequest(OutputStream)} can be called more than once. + * @return + */ + boolean isRepeatable(); + + /** + * Writes the request entity to the given stream. + * @param out + * @throws IOException + */ + void writeRequest(OutputStream out) throws IOException; + + /** + * Gets the request entity's length. + * @return either a number >= 0 or + * {@link org.apache.commons.httpclient.methods.EntityEnclosingMethod#CONTENT_LENGTH_CHUNKED} + */ + long getContentLength(); + +}