Index: trunk/http-core/src/test/org/apache/http/impl/TestDefaultEntityGenerator.java =================================================================== --- trunk/http-core/src/test/org/apache/http/impl/TestDefaultEntityGenerator.java (revision 375039) +++ trunk/http-core/src/test/org/apache/http/impl/TestDefaultEntityGenerator.java (working copy) @@ -28,6 +28,8 @@ package org.apache.http.impl; +import java.io.InputStream; + import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpMutableMessage; @@ -205,8 +207,9 @@ assertNotNull(entity); assertEquals(1, entity.getContentLength()); assertFalse(entity.isChunked()); - assertNotNull(entity.getContent()); - assertTrue(entity.getContent() instanceof ContentLengthInputStream); + InputStream instream = entity.getContent(); + assertNotNull(instream); + assertTrue(instream instanceof ContentLengthInputStream); // strict mode message.getParams().setBooleanParameter(HttpProtocolParams.STRICT_TRANSFER_ENCODING, true); @@ -233,8 +236,9 @@ assertNotNull(entity); assertEquals(1, entity.getContentLength()); assertFalse(entity.isChunked()); - assertNotNull(entity.getContent()); - assertTrue(entity.getContent() instanceof ContentLengthInputStream); + InputStream instream = entity.getContent(); + assertNotNull(instream); + assertTrue(instream instanceof ContentLengthInputStream); // strict mode message.getParams().setBooleanParameter(HttpProtocolParams.STRICT_TRANSFER_ENCODING, true); @@ -260,9 +264,10 @@ assertNotNull(entity); assertEquals(-1, entity.getContentLength()); assertFalse(entity.isChunked()); - assertNotNull(entity.getContent()); - assertFalse(entity.getContent() instanceof ContentLengthInputStream); - assertTrue(entity.getContent() instanceof HttpDataInputStream); + InputStream instream = entity.getContent(); + assertNotNull(instream); + assertFalse(instream instanceof ContentLengthInputStream); + assertTrue(instream instanceof HttpDataInputStream); // strict mode message.getParams().setBooleanParameter(HttpProtocolParams.STRICT_TRANSFER_ENCODING, true); @@ -287,9 +292,10 @@ assertNotNull(entity); assertEquals(-1, entity.getContentLength()); assertFalse(entity.isChunked()); - assertNotNull(entity.getContent()); - assertFalse(entity.getContent() instanceof ContentLengthInputStream); - assertTrue(entity.getContent() instanceof HttpDataInputStream); + InputStream instream = entity.getContent(); + assertNotNull(instream); + assertFalse(instream instanceof ContentLengthInputStream); + assertTrue(instream instanceof HttpDataInputStream); // strict mode message.getParams().setBooleanParameter(HttpProtocolParams.STRICT_TRANSFER_ENCODING, true); @@ -312,10 +318,11 @@ assertNotNull(entity); assertEquals(-1, entity.getContentLength()); assertFalse(entity.isChunked()); - assertNotNull(entity.getContent()); - assertFalse(entity.getContent() instanceof ContentLengthInputStream); - assertFalse(entity.getContent() instanceof ChunkedInputStream); - assertTrue(entity.getContent() instanceof HttpDataInputStream); + InputStream instream = entity.getContent(); + assertNotNull(instream); + assertFalse(instream instanceof ContentLengthInputStream); + assertFalse(instream instanceof ChunkedInputStream); + assertTrue(instream instanceof HttpDataInputStream); } } Index: trunk/http-core/src/test/org/apache/http/util/TestEncodingUtils.java =================================================================== --- trunk/http-core/src/test/org/apache/http/util/TestEncodingUtils.java (revision 375039) +++ trunk/http-core/src/test/org/apache/http/util/TestEncodingUtils.java (working copy) @@ -191,4 +191,17 @@ assertEquals(s1, s2); } -} \ No newline at end of file + public void testIllegalEncoding() { + String s = constructString(SWISS_GERMAN_HELLO); + byte[] b1 = s.getBytes(); + byte[] b2 = EncodingUtils.getBytes(s, "some chars,illegal/are!"); + assertEquals(b1.length, b2.length); + for (int i = 0; i < b1.length; i++) { + assertEquals(b1[i], b2[i]); + } + String s1 = new String(b1); + String s2 = EncodingUtils.getString(b1, "some chars,illegal/are!"); + assertEquals(s1, s2); + } + +} Index: trunk/http-core/src/test/org/apache/http/util/TestEntityUtils.java =================================================================== --- trunk/http-core/src/test/org/apache/http/util/TestEntityUtils.java (revision 375039) +++ trunk/http-core/src/test/org/apache/http/util/TestEntityUtils.java (working copy) @@ -29,6 +29,7 @@ package org.apache.http.util; +import java.io.InputStream; import java.io.ByteArrayInputStream; import org.apache.http.Header; @@ -68,8 +69,11 @@ } public void testEmptyContentToByteArray() throws Exception { - BasicHttpEntity httpentity = new BasicHttpEntity(); - httpentity.setContent(null); + NullHttpEntity httpentity = new NullHttpEntity(); + //BasicHttpEntity httpentity = new BasicHttpEntity(); + //httpentity.setContent(null); + //byte[] content = new byte[0]; + //httpentity.setContent(new ByteArrayInputStream(content)); byte[] bytes = EntityUtils.toByteArray(httpentity); assertNotNull(bytes); assertEquals(0, bytes.length); @@ -125,7 +129,7 @@ public void testNullContentTypeGetContentCharset() throws Exception { BasicHttpEntity httpentity = new BasicHttpEntity(); - httpentity.setContentType(null); + httpentity.setContentType((Header)null); assertNull(EntityUtils.getContentCharSet(httpentity)); } @@ -151,8 +155,11 @@ } public void testEmptyContentToString() throws Exception { - BasicHttpEntity httpentity = new BasicHttpEntity(); - httpentity.setContent(null); + NullHttpEntity httpentity = new NullHttpEntity(); + //BasicHttpEntity httpentity = new BasicHttpEntity(); + //httpentity.setContent(null); + //byte[] content = new byte[0]; + //httpentity.setContent(new ByteArrayInputStream(content)); String s = EntityUtils.toString(httpentity); assertNotNull(s); assertEquals("", s); @@ -237,5 +244,22 @@ String s = EntityUtils.toString(httpentity, "ISO-8859-1"); assertEquals(content, s); } - -} + + /** + * Helper class that returns null as the content. + */ + public static class NullHttpEntity extends BasicHttpEntity { + + // default constructor + /** + * Obtains no content. + * This method disables the state checks in the base class. + * + * @return null + */ + public InputStream getContent() { + return null; + } + } // class NullEntity + +} // class TestEntityUtils Index: trunk/http-core/src/contrib/org/apache/http/contrib/compress/GzipDecompressingEntity.java =================================================================== --- trunk/http-core/src/contrib/org/apache/http/contrib/compress/GzipDecompressingEntity.java (revision 375039) +++ trunk/http-core/src/contrib/org/apache/http/contrib/compress/GzipDecompressingEntity.java (working copy) @@ -47,26 +47,22 @@ */ public class GzipDecompressingEntity extends HttpEntityWrapper { - private InputStream instream = null; - public GzipDecompressingEntity(final HttpEntity entity) { super(entity); } - public InputStream getContent() throws IOException { - if (this.instream == null) { - this.instream = new GZIPInputStream(wrappedEntity.getContent()); - } - return this.instream; + public InputStream getContent() + throws IOException, IllegalStateException { + + // the wrapped entity's getContent() decides about repeatability + InputStream wrappedin = wrappedEntity.getContent(); + + return new GZIPInputStream(wrappedin); } - + public long getContentLength() { + // length of ungzipped content not known in advance return -1; } - - public boolean isRepeatable() { - // not repeatable, GZIPInputStream is created only once - return false; - } - -} // class GzipDecompressingEntity \ No newline at end of file + +} // class GzipDecompressingEntity Index: trunk/http-core/src/java/org/apache/http/HttpEntity.java =================================================================== --- trunk/http-core/src/java/org/apache/http/HttpEntity.java (revision 375039) +++ trunk/http-core/src/java/org/apache/http/HttpEntity.java (working copy) @@ -130,10 +130,17 @@ * Creates a new InputStream object of the entity. * It is a programming error * to return the same InputStream object more than once. + * Entities that are not {@link #isRepeatable repeatable} + * will throw an exception if this method is called multiple times. + * * @return a new input stream that returns the entity data. + * * @throws IOException if the stream could not be created + * @throws IllegalStateException + * if this entity is not repeatable and the stream + * has already been obtained previously */ - InputStream getContent() throws IOException; + InputStream getContent() throws IOException, IllegalStateException; /** * Writes the entity content to the output stream. Index: trunk/http-core/src/java/org/apache/http/entity/BasicHttpEntity.java =================================================================== --- trunk/http-core/src/java/org/apache/http/entity/BasicHttpEntity.java (revision 375039) +++ trunk/http-core/src/java/org/apache/http/entity/BasicHttpEntity.java (working copy) @@ -45,62 +45,90 @@ * * @since 4.0 */ -public class BasicHttpEntity implements HttpEntity { - - private Header contentType = null; - private Header contentEncoding = null; - private InputStream content = null; - private long length = -1; - private boolean chunked = false; - +public class BasicHttpEntity extends AbstractHttpEntity + implements HttpEntity { + + private InputStream content; + private boolean contentObtained; + private long length; + + + /** + * Creates a new basic entity. + * The content is initially missing, the content length + * is set to a negative number. + */ public BasicHttpEntity() { super(); + length = -1; } + + // non-javadoc, see interface HttpEntity public long getContentLength() { return this.length; } - public Header getContentType() { - return this.contentType; - } - - public Header getContentEncoding() { - return this.contentEncoding; - } - - public InputStream getContent() { + + /** + * Obtains the content, once only. + * + * @return the content, if this is the first call to this method + * since {@link #setContent setContent} has been called + * + * @throws IllegalStateException + * if the content has been obtained before, or + * has not yet been provided + */ + public InputStream getContent() + throws IllegalStateException { + + if (this.content == null) + throw new IllegalStateException("content has not been provided"); + if (contentObtained) + throw new IllegalStateException("content is not repeatable"); + + contentObtained = true; return this.content; - } - - public boolean isChunked() { - return this.chunked; - } - + + } // getContent + + + /** + * Tells that this entity is not repeatable. + * + * @return false + */ public boolean isRepeatable() { return false; } - - public void setChunked(boolean b) { - this.chunked = b; - } - + + + /** + * Specifies the length of the content. + * + * @param len the number of bytes in the content, or + * a negative number to indicate an unknown length + */ public void setContentLength(long len) { this.length = len; } - - public void setContentType(final Header contentType) { - this.contentType = contentType; - } - - public void setContentEncoding(final Header contentEncoding) { - this.contentEncoding = contentEncoding; - } - + + + /** + * Specifies the content. + * + * @param instream the stream to return with the next call to + * {@link #getContent getContent} + */ public void setContent(final InputStream instream) { - this.content = instream; + this.content = instream; + if (instream != null) + contentObtained = false; } - + + + // non-javadoc, see interface HttpEntity public void writeTo(final OutputStream outstream) throws IOException { if (outstream == null) { throw new IllegalArgumentException("Output stream may not be null"); Index: trunk/http-core/src/java/org/apache/http/util/EncodingUtils.java =================================================================== --- trunk/http-core/src/java/org/apache/http/util/EncodingUtils.java (revision 375039) +++ trunk/http-core/src/java/org/apache/http/util/EncodingUtils.java (working copy) @@ -29,6 +29,7 @@ package org.apache.http.util; import java.io.UnsupportedEncodingException; +import java.nio.charset.IllegalCharsetNameException; import org.apache.http.protocol.HTTP; @@ -70,14 +71,26 @@ throw new IllegalArgumentException("charset may not be null or empty"); } + String result = null; try { - return new String(data, offset, length, charset); + result = new String(data, offset, length, charset); + } catch (UnsupportedEncodingException e) { - return new String(data, offset, length); + // ignore, result remains null to trigger fallback + } catch (IllegalCharsetNameException e) { + // ignore, result remains null to trigger fallback } - } + if (result == null) { + // fallback: use default platform encoding + result = new String(data, offset, length); + } + return result; + + } // getString + + /** * Converts the byte array of HTTP content characters to a string. If * the specified charset is not supported, default system encoding Index: trunk/http-core/src/java/org/apache/http/util/EntityUtils.java =================================================================== --- trunk/http-core/src/java/org/apache/http/util/EntityUtils.java (revision 375039) +++ trunk/http-core/src/java/org/apache/http/util/EntityUtils.java (working copy) @@ -101,7 +101,8 @@ if (entity == null) { throw new IllegalArgumentException("HTTP entity may not be null"); } - if (entity.getContent() == null) { + InputStream instream = entity.getContent(); + if (instream == null) { return ""; } if (entity.getContentLength() > Integer.MAX_VALUE) { @@ -118,7 +119,7 @@ if (charset == null) { charset = HTTP.DEFAULT_CONTENT_CHARSET; } - Reader reader = new InputStreamReader(entity.getContent(), charset); + Reader reader = new InputStreamReader(instream, charset); CharArrayBuffer buffer = new CharArrayBuffer(i); char[] tmp = new char[1024]; int l;