Index: C:/Documents and Settings/Ortwin/My Documents/code/HttpClient-3.1/src/java/org/apache/commons/httpclient/HttpContentTooLargeException.java =================================================================== --- C:/Documents and Settings/Ortwin/My Documents/code/HttpClient-3.1/src/java/org/apache/commons/httpclient/HttpContentTooLargeException.java (revision 0) +++ C:/Documents and Settings/Ortwin/My Documents/code/HttpClient-3.1/src/java/org/apache/commons/httpclient/HttpContentTooLargeException.java (revision 0) @@ -0,0 +1,22 @@ +package org.apache.commons.httpclient; + +/** + * Signals that the response content was larger than anticipated. + * + * @author Ortwin Glück + */ +public class HttpContentTooLargeException extends HttpException { + private int maxlen; + + public HttpContentTooLargeException(String message, int maxlen) { + super(message); + this.maxlen = maxlen; + } + + /** + * @return the maximum anticipated content length in bytes. + */ + public int getMaxLength() { + return maxlen; + } +} Index: C:/Documents and Settings/Ortwin/My Documents/code/HttpClient-3.1/src/java/org/apache/commons/httpclient/HttpMethodBase.java =================================================================== --- C:/Documents and Settings/Ortwin/My Documents/code/HttpClient-3.1/src/java/org/apache/commons/httpclient/HttpMethodBase.java (revision 478198) +++ C:/Documents and Settings/Ortwin/My Documents/code/HttpClient-3.1/src/java/org/apache/commons/httpclient/HttpMethodBase.java (working copy) @@ -33,7 +33,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.InterruptedIOException; +import java.io.Reader; import java.util.Collection; import org.apache.commons.httpclient.auth.AuthState; @@ -697,7 +699,7 @@ * Returns the response body of the HTTP method, if any, as an {@link InputStream}. * If response body is not available, returns null * - * @return The response body + * @return The response body or null. * * @throws IOException If an I/O (transport) problem occurs while obtaining the * response body. @@ -725,7 +727,7 @@ * recommended, to use getResponseAsStream if the content length of the response * is unknown or resonably large. * - * @return The response body. + * @return The response body or null. * * @throws IOException If an I/O (transport) problem occurs while obtaining the * response body. @@ -741,6 +743,59 @@ return null; } } + + /** + * Returns the response body of the HTTP method, if any, as a {@link String}. + * If response body is not available or cannot be read, returns null + * The string conversion on the data is done using the character encoding specified + * in Content-Type header.

+ * + * Note: This will cause the entire response body to be buffered in memory. This method is + * safe if the content length of the response is unknown, because the amount of memory used + * is limited.

+ * + * If the response is large this method involves lots of array copying and many object + * allocations, which makes it unsuitable for high-performance / low-footprint applications. + * Those applications should use {@link #getResponseBodyAsStream()}. + * + * @param maxlen the maximum content length to accept (number of bytes). Note that, + * depending on the encoding, this is not equal to the number of characters. + * @return The response body or null. + * + * @throws IOException If an I/O (transport) problem occurs while obtaining the + * response body. + */ + public String getResponseBodyAsString(int maxlen) throws IOException { + if (maxlen < 0) throw new IllegalArgumentException("maxlen must be positive"); + + // we might already know that the content is larger + long contentLength = getResponseContentLength(); + if ((contentLength != -1) && (contentLength > maxlen)) { + throw new HttpContentTooLargeException("Content-Length is "+ contentLength, maxlen); + } + + ByteArrayOutputStream rawdata = new ByteArrayOutputStream(); + InputStream in = getResponseBodyAsStream(); + if (in == null) return null; + byte[] buffer = new byte[2048]; + int pos = 0; + int len; + do { + len = in.read(buffer, 0, Math.min(buffer.length, maxlen-pos)); + if (len == -1) break; + rawdata.write(buffer, 0, len); + pos += len; + } while (pos < maxlen); + + // check if there is even more data + if (pos == maxlen) { + if (in.read() != -1) + throw new HttpContentTooLargeException("Content-Length not known but larger than " + + maxlen, maxlen); + } + + return EncodingUtil.getString(rawdata.toByteArray(), 0, pos, getResponseCharSet()); + } /** * Returns an array of the response footers that the HTTP method currently has Index: C:/Documents and Settings/Ortwin/My Documents/code/HttpClient-3.1/src/test/org/apache/commons/httpclient/TestHttpMethodFundamentals.java =================================================================== --- C:/Documents and Settings/Ortwin/My Documents/code/HttpClient-3.1/src/test/org/apache/commons/httpclient/TestHttpMethodFundamentals.java (revision 478198) +++ C:/Documents and Settings/Ortwin/My Documents/code/HttpClient-3.1/src/test/org/apache/commons/httpclient/TestHttpMethodFundamentals.java (working copy) @@ -248,6 +248,11 @@ assertEquals(HttpStatus.SC_OK, httpget.getStatusCode()); String response = httpget.getResponseBodyAsString(); assertNull(response); + + this.client.executeMethod(httpget); + assertEquals(HttpStatus.SC_OK, httpget.getStatusCode()); + response = httpget.getResponseBodyAsString(1); + assertNull(response); } finally { httpget.releaseConnection(); } @@ -268,6 +273,34 @@ } } + public void testLongBodyAsString() throws Exception { + this.server.setHttpService(new SimpleChunkedService()); + + GetMethod httpget = new GetMethod("/test/"); + try { + this.client.executeMethod(httpget); + assertEquals(HttpStatus.SC_OK, httpget.getStatusCode()); + try { + httpget.getResponseBodyAsString(5); // too small + } catch(HttpContentTooLargeException e) { + /* expected */ + assertEquals(5, e.getMaxLength()); + } + + this.client.executeMethod(httpget); + assertEquals(HttpStatus.SC_OK, httpget.getStatusCode()); + String response = httpget.getResponseBodyAsString(13); // exact size + assertEquals("1234567890123", response); + + this.client.executeMethod(httpget); + assertEquals(HttpStatus.SC_OK, httpget.getStatusCode()); + response = httpget.getResponseBodyAsString(128); // plenty + assertEquals("1234567890123", response); + } finally { + httpget.releaseConnection(); + } + } + public void testUrlGetMethodWithPathQuery() { GetMethod method = new GetMethod("http://www.fubar.com/path1/path2?query=string"); try {