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.55 diff -u -r1.55 HttpConnection.java --- java/org/apache/commons/httpclient/HttpConnection.java 10 Apr 2003 23:36:36 -0000 1.55 +++ java/org/apache/commons/httpclient/HttpConnection.java 16 Apr 2003 18:25:39 -0000 @@ -63,7 +63,6 @@ package org.apache.commons.httpclient; -import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; Index: java/org/apache/commons/httpclient/HttpConstants.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConstants.java,v retrieving revision 1.9 diff -u -r1.9 HttpConstants.java --- java/org/apache/commons/httpclient/HttpConstants.java 10 Apr 2003 17:18:49 -0000 1.9 +++ java/org/apache/commons/httpclient/HttpConstants.java 16 Apr 2003 18:25:41 -0000 @@ -1,5 +1,5 @@ /* - * $Header: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConstants.java,v 1.9 2003/04/10 17:18:49 olegk Exp $ + * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConstants.java,v 1.9 2003/04/10 17:18:49 olegk Exp $ * $Revision: 1.9 $ * $Date: 2003/04/10 17:18:49 $ * @@ -70,7 +70,7 @@ /** - * DOCUMENT ME! + * HTTP content conversion routines. * * @author Oleg Kalnichevski * @author Mike Bowler 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.14 diff -u -r1.14 EntityEnclosingMethod.java --- java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java 1 Apr 2003 19:04:19 -0000 1.14 +++ java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java 16 Apr 2003 18:25:44 -0000 @@ -67,10 +67,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.Reader; -import java.io.UnsupportedEncodingException; import org.apache.commons.httpclient.ChunkedOutputStream; import org.apache.commons.httpclient.ContentLengthInputStream; @@ -114,8 +111,16 @@ private byte[] buffer = null; /** The unbuffered request body, if any. */ - private InputStream requestBodyStream = null; + 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; + /** Counts how often the request was sent to the server. */ private int repeatCount = 0; @@ -179,15 +184,65 @@ /** * Returns true if there is a request body to be sent. - * 'Expect: 100-continue' handshake may not be used if request - * body is not present + * + *

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

* * @return boolean * * @since 2.0beta1 */ protected boolean hasRequestContent() { - return (this.requestBodyStream != null) || (this.buffer != null); + LOG.trace("enter EntityEnclosingMethod.hasRequestContent()"); + return (this.buffer != null) || + (this.requestStream != null) || + (this.requestString != null); + } + + /** + * Clears request body + * + *

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

+ * + * @since 2.0beta1 + */ + protected void clearRequestBody() { + LOG.trace("enter EntityEnclosingMethod.clearRequestBody()"); + this.requestStream = null; + this.requestString = null; + this.buffer = null; + this.contentCache = null; + } + + + /** + * Generates request body. + * + * @return request body as an array of bytes. If the request content + * has not been set, returns null. + * + *

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

+ * + * @since 2.0beta1 + */ + protected byte[] generateRequestBody() { + LOG.trace("enter EntityEnclosingMethod.renerateRequestBody()"); + if (this.requestStream != null) { + bufferContent(); + } + + if (this.buffer != null) { + return this.buffer; + } else if (this.requestString != null) { + return HttpConstants.getContentBytes(this.requestString, getRequestCharSet()); + } else { + return null; + } } /** @@ -256,9 +311,10 @@ if (this.requestContentLength != CONTENT_LENGTH_AUTO) { return this.requestContentLength; } - bufferContent(); - - return (this.buffer == null) ? 0 : this.buffer.length; + if (this.contentCache == null) { + this.contentCache = generateRequestBody(); + } + return (this.contentCache == null) ? 0 : this.contentCache.length; } /** @@ -268,21 +324,23 @@ */ public void setRequestBody(InputStream body) { LOG.trace("enter EntityEnclosingMethod.setRequestBody(InputStream)"); - this.requestBodyStream = body; - this.buffer = null; + clearRequestBody(); + this.requestStream = body; } /** * Gets the request body as a stream. + * Calling this method will cause the content to be buffered. * * @return The request body {@link java.io.InputStream} if it has been set. */ public InputStream getRequestBody() { LOG.trace("enter EntityEnclosingMethod.getRequestBody()"); - if (this.buffer != null) { - return new ByteArrayInputStream(this.buffer); + byte [] content = generateRequestBody(); + if (content != null) { + return new ByteArrayInputStream(content); } else { - return this.requestBodyStream; + return new ByteArrayInputStream(new byte[] {}); } } @@ -293,17 +351,13 @@ */ public void setRequestBody(String body) { LOG.trace("enter EntityEnclosingMethod.setRequestBody(String)"); - - if (body == null) { - this.requestBodyStream = null; - this.buffer = null; - return; - } - this.buffer = HttpConstants.getContentBytes(body, getRequestCharSet()); + clearRequestBody(); + this.requestString = body; } /** * Gets the request body as a String. + * Calling this method will cause the content to be buffered. * * @return the request body as a string * @@ -311,25 +365,12 @@ */ public String getRequestBodyAsString() throws IOException { LOG.trace("enter EntityEnclosingMethod.getRequestBodyAsString()"); - - Reader instream = null; - try { - instream = new InputStreamReader(getRequestBody(), getRequestCharSet()); - } catch (UnsupportedEncodingException e) { - if (LOG.isWarnEnabled()) { - LOG.warn("Unsupported encoding: " + e.getMessage()); - } - instream = new InputStreamReader(getRequestBody()); - } - - StringBuffer buffer = new StringBuffer(); - char[] tmp = new char[4096]; - int l = 0; - while ((l = instream.read(tmp)) >= 0) { - buffer.append(tmp, 0, l); + byte [] content = generateRequestBody(); + if (content != null) { + return HttpConstants.getContentString(content, getRequestCharSet()); + } else { + return null; } - - return buffer.toString(); } @@ -350,19 +391,38 @@ LOG.trace( "enter EntityEnclosingMethod.writeRequestBody(HttpState, HttpConnection)"); + if (!hasRequestContent()) { + LOG.debug("Request body has not been specified"); + return true; + } + int contentLength = getRequestContentLength(); if ((contentLength == CONTENT_LENGTH_CHUNKED) && !isHttp11()) { throw new HttpException( "Chunked transfer encoding not allowed for HTTP/1.0"); } - InputStream instream = getRequestBody(); + + 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) { LOG.debug("Request body is empty"); return true; } - if ((this.repeatCount > 0) && (this.buffer == null)) { + if ((this.repeatCount > 0) && (this.contentCache == null)) { throw new HttpException( "Unbuffered entity enclosing request can not be repeated."); } @@ -405,39 +465,37 @@ */ public void recycle() { LOG.trace("enter EntityEnclosingMethod.recycle()"); + clearRequestBody(); this.requestContentLength = CONTENT_LENGTH_AUTO; - this.requestBodyStream = null; - this.buffer = null; this.repeatCount = 0; super.recycle(); } /** - * Buffers the request body and calculates the content length. If the - * method was called earlier it returns immediately. + * Buffers request body input stream. */ - protected void bufferContent() { + private void bufferContent() { LOG.trace("enter EntityEnclosingMethod.bufferContent()"); if (this.buffer != null) { + // Already been buffered return; } - if (this.requestBodyStream == null) { - return; - } - try { - ByteArrayOutputStream tmp = new ByteArrayOutputStream(); - byte[] data = new byte[4096]; - int l = 0; - while ((l = this.requestBodyStream.read(data)) >= 0) { - tmp.write(data, 0, l); + 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; } - this.buffer = tmp.toByteArray(); - this.requestBodyStream = null; - } catch (IOException e) { - LOG.error(e.getMessage(), e); - this.buffer = null; - this.requestBodyStream = null; } } } Index: java/org/apache/commons/httpclient/methods/PostMethod.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v retrieving revision 1.40 diff -u -r1.40 PostMethod.java --- java/org/apache/commons/httpclient/methods/PostMethod.java 4 Apr 2003 02:37:03 -0000 1.40 +++ java/org/apache/commons/httpclient/methods/PostMethod.java 16 Apr 2003 18:25:47 -0000 @@ -63,11 +63,11 @@ package org.apache.commons.httpclient.methods; import java.io.IOException; -import java.io.InputStream; import java.util.Iterator; import java.util.Vector; import org.apache.commons.httpclient.HttpConnection; +import org.apache.commons.httpclient.HttpConstants; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpState; import org.apache.commons.httpclient.NameValuePair; @@ -113,25 +113,14 @@ /** Log object for this class. */ private static final Log LOG = LogFactory.getLog(PostMethod.class); - /** Custom content encoding. */ - private static final int CUSTOM_CONTENT = 0; - - /** URL encoded content. */ - private static final int URL_ENCODED_CONTENT = 1; - - /** My content encoding. */ - private int contentEncoding = CUSTOM_CONTENT; - /** The Content-Type for www-form-urlcoded. */ public static final String FORM_URL_ENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"; /** * The buffered request body consisting of NameValuePairs. - * @deprecated Parameters will not be buffered in the future but converted - * into an InputStream immediately. */ - private Vector deprecated_parameters = new Vector(); + private Vector params = new Vector(); // ----------------------------------------------------------- Constructors @@ -195,6 +184,96 @@ return "POST"; } + + /** + * Returns true if there is a request body to be sent. + * + *

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

+ * + * @return boolean + * + * @since 2.0beta1 + */ + protected boolean hasRequestContent() { + LOG.trace("enter PostMethod.hasRequestContent()"); + if (!this.params.isEmpty()) { + return true; + } else { + return super.hasRequestContent(); + } + } + + /** + * Clears request body + * + *

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

+ * + * @since 2.0beta1 + */ + protected void clearRequestBody() { + LOG.trace("enter PostMethod.clearRequestBody()"); + this.params.clear(); + super.clearRequestBody(); + } + + + /** + * Generates request body. + * + * @return request body as an array of bytes. If the request content + * has not been set, returns null. + * + *

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

+ * + * @since 2.0beta1 + */ + protected byte[] generateRequestBody() { + LOG.trace("enter PostMethod.renerateRequestBody()"); + if (!this.params.isEmpty()) { + String charset = getRequestCharSet(); + StringBuffer buff = new StringBuffer(); + + for (int i = 0; i < this.params.size(); i++) { + if (i > 0) { + buff.append("&"); + } + NameValuePair parameter = (NameValuePair)this.params.get(i); + String queryName = null; + try { + queryName = URIUtil.encodeWithinQuery(parameter.getName(), charset); + } catch (URIException e) { + if (LOG.isWarnEnabled()) { + LOG.warn("Encosing error: " + e.toString()); + } + queryName = parameter.getName(); + } + + buff.append(queryName).append("="); + String queryValue = null; + + try { + queryValue = URIUtil.encodeWithinQuery(parameter.getValue(), charset); + } catch (URIException e) { + if (LOG.isWarnEnabled()) { + LOG.warn("Encosing error: " + e.toString()); + } + queryValue = parameter.getValue(); + } + buff.append(queryValue); + } + return HttpConstants.getContentBytes(buff.toString()); + } else { + return super.generateRequestBody(); + } + } + + /** * Set the value of parameter with parameterName to parameterValue. Does * not preserve the initial insertion order. @@ -203,8 +282,6 @@ * @param parameterValue value of the parameter * * @since 2.0 - * - * @deprecated use {@link #setRequestBody(NameValuePair[])}. */ public void setParameter(String parameterName, String parameterValue) { LOG.trace("enter PostMethod.setParameter(String, String)"); @@ -224,8 +301,6 @@ * * @since 2.0 * - * @deprecated use {@link #getRequestBody()} or - * {@link #getRequestBodyAsString()}. */ public NameValuePair getParameter(String paramName) { LOG.trace("enter PostMethod.getParameter(String)"); @@ -234,7 +309,7 @@ return null; } - Iterator iter = deprecated_parameters.iterator(); + Iterator iter = this.params.iterator(); while (iter.hasNext()) { NameValuePair parameter = (NameValuePair) iter.next(); @@ -256,14 +331,12 @@ * * @since 2.0 * - * @deprecated use {@link #getRequestBody()} or - * {@link #getRequestBodyAsString()}. */ public NameValuePair[] getParameters() { LOG.trace("enter PostMethod.getParameters()"); - int numPairs = deprecated_parameters.size(); - Object[] objectArr = deprecated_parameters.toArray(); + int numPairs = this.params.size(); + Object[] objectArr = this.params.toArray(); NameValuePair[] nvPairArr = new NameValuePair[numPairs]; for (int i = 0; i < numPairs; i++) { @@ -282,8 +355,6 @@ * @throws IllegalArgumentException if either argument is null * * @since 1.0 - * - * @deprecated use {@link #setRequestBody(NameValuePair[])}. */ public void addParameter(String paramName, String paramValue) throws IllegalArgumentException { @@ -292,10 +363,9 @@ if ((paramName == null) || (paramValue == null)) { throw new IllegalArgumentException( "Arguments to addParameter(String, String) cannot be null"); - } else { - deprecated_parameters.add(new NameValuePair(paramName, paramValue)); } - setRequestBody(getParameters()); + super.clearRequestBody(); + this.params.add(new NameValuePair(paramName, paramValue)); } /** @@ -307,8 +377,6 @@ * null values * * @since 2.0 - * - * @deprecated use {@link #setRequestBody(NameValuePair[])}. */ public void addParameter(NameValuePair param) throws IllegalArgumentException { @@ -316,9 +384,8 @@ if (param == null) { throw new IllegalArgumentException("NameValuePair may not be null"); - } else { - addParameter(param.getName(), param.getValue()); } + addParameter(param.getName(), param.getValue()); } /** @@ -328,8 +395,6 @@ * @param parameters The array of parameters to add. * * @since 2.0 - * - * @deprecated use {@link #setRequestBody(NameValuePair[])}. */ public void addParameters(NameValuePair[] parameters) { LOG.trace("enter PostMethod.addParameters(NameValuePair[])"); @@ -356,8 +421,6 @@ * @throws IllegalArgumentException When the parameter name passed is null * * @since 2.0 - * - * @deprecated use {@link #setRequestBody(NameValuePair[])}. */ public boolean removeParameter(String paramName) throws IllegalArgumentException { @@ -368,7 +431,7 @@ "Argument passed to removeParameter(String) cannot be null"); } boolean removed = true; - Iterator iter = deprecated_parameters.iterator(); + Iterator iter = this.params.iterator(); while (iter.hasNext()) { NameValuePair pair = (NameValuePair) iter.next(); @@ -378,7 +441,6 @@ removed = true; } } - setRequestBody(getParameters()); return removed; } @@ -395,8 +457,6 @@ * @throws IllegalArgumentException when param name or value are null * * @since 2.0 - * - * @deprecated use {@link #setRequestBody(NameValuePair[])}. */ public boolean removeParameter(String paramName, String paramValue) throws IllegalArgumentException { @@ -409,7 +469,7 @@ throw new IllegalArgumentException("Parameter value may not be null"); } - Iterator iter = deprecated_parameters.iterator(); + Iterator iter = this.params.iterator(); while (iter.hasNext()) { NameValuePair pair = (NameValuePair) iter.next(); @@ -425,99 +485,6 @@ } /** - * Encode the list of parameters into a query string. - * - * @param parameters the list of query name and value - * @param charset the charset to use for encoding the parameters - * - * @return url encoded form of the parameters - * - * @throws IllegalArgumentException if parameters is null - */ - protected static String generateRequestBody(NameValuePair[] parameters, final String charset) - throws IllegalArgumentException { - LOG.trace("enter PostMethod.generateRequestBody(NameValuePair[])"); - - if (parameters == null) { - throw new IllegalArgumentException("Array of parameters may not be null"); - } - StringBuffer buff = new StringBuffer(); - - for (int i = 0; i < parameters.length; i++) { - if (i > 0) { - buff.append("&"); - } - - NameValuePair parameter = parameters[i]; - - String queryName = null; - try { - queryName = URIUtil.encodeWithinQuery(parameter.getName(), charset); - } catch (URIException e) { - if (LOG.isWarnEnabled()) { - LOG.warn("Encosing error: " + e.toString()); - } - queryName = parameter.getName(); - } - - buff.append(queryName).append("="); - String queryValue = null; - - try { - queryValue = URIUtil.encodeWithinQuery(parameter.getValue(), charset); - } catch (URIException e) { - if (LOG.isWarnEnabled()) { - LOG.warn("Encosing error: " + e.toString()); - } - queryValue = parameter.getValue(); - } - buff.append(queryValue); - } - - return buff.toString(); - } - - /** - * Sets the request body to be the specified string. - * Once this method has been invoked, the request parameters cannot be - * altered until I am {@link #recycle recycled}. - * - * @param stringBody Request body content as a string - * - * @throws IllegalArgumentException if stringBody is null - */ - public void setRequestBody(String stringBody) - throws IllegalArgumentException { - LOG.trace("enter PostMethod.setRequestBody(String)"); - - if (stringBody == null) { - throw new IllegalArgumentException("String body not be null"); - } - super.setRequestBody(stringBody); - this.contentEncoding = CUSTOM_CONTENT; - } - - /** - * Sets the request body to be the specified inputstream. - * Once this method has been invoked, the request parameters cannot be - * altered until I am {@link #recycle recycled}. - * - * @param streamBody Request body content as {@link java.io.InputStream} - * - * @throws IllegalArgumentException if streamBody is null - */ - public void setRequestBody(InputStream streamBody) - throws IllegalArgumentException { - LOG.trace("enter PostMethod.setRequestBody(InputStream)"); - - if (streamBody == null) { - throw new IllegalArgumentException("Stream body may not be null"); - } - super.setRequestBody(streamBody); - this.contentEncoding = CUSTOM_CONTENT; - } - - /** * Set an Array of parameters to be used in the POST request body * * @param parametersBody The array of parameters to add. @@ -533,8 +500,8 @@ if (parametersBody == null) { throw new IllegalArgumentException("Array of parameters may not be null"); } - super.setRequestBody(generateRequestBody(parametersBody, getRequestCharSet())); - this.contentEncoding = URL_ENCODED_CONTENT; + clearRequestBody(); + addParameters(parametersBody); } /** @@ -553,24 +520,12 @@ throws IOException, HttpException { super.addRequestHeaders(state, conn); - if (this.contentEncoding == URL_ENCODED_CONTENT) { + if (!this.params.isEmpty()) { //there are some parameters, so set the contentType header if (getRequestHeader("Content-Type") == null) { setRequestHeader("Content-Type", FORM_URL_ENCODED_CONTENT_TYPE); } } - } - - /** - * Prepare the method for reuse. - * - * @since 1.0 - */ - public void recycle() { - LOG.trace("enter PostMethod.recycle()"); - this.contentEncoding = CUSTOM_CONTENT; - this.deprecated_parameters.clear(); - super.recycle(); } } Index: test/org/apache/commons/httpclient/TestChallengeParser.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestChallengeParser.java,v retrieving revision 1.1 diff -u -r1.1 TestChallengeParser.java --- test/org/apache/commons/httpclient/TestChallengeParser.java 27 Mar 2003 21:09:57 -0000 1.1 +++ test/org/apache/commons/httpclient/TestChallengeParser.java 16 Apr 2003 18:25:48 -0000 @@ -62,11 +62,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; - -import java.util.Hashtable; import java.util.Map; -import java.util.StringTokenizer; - import org.apache.commons.httpclient.auth.AuthChallengeParser; import org.apache.commons.httpclient.auth.MalformedChallengeException; Index: test/org/apache/commons/httpclient/TestMethodCharEncoding.java =================================================================== RCS file: test/org/apache/commons/httpclient/TestMethodCharEncoding.java diff -N test/org/apache/commons/httpclient/TestMethodCharEncoding.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/org/apache/commons/httpclient/TestMethodCharEncoding.java 16 Apr 2003 18:25:50 -0000 @@ -0,0 +1,274 @@ +/* + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2003 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * 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; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.util.URIUtil; + +/** + * @author Oleg Kalnichevski + * @author Andre John Mas + * @author Laura Werner + */ + +public class TestMethodCharEncoding extends TestCase { + + static final String CHARSET_DEFAULT = HttpConstants.DEFAULT_CONTENT_CHARSET; + static final String CHARSET_ASCII = "US-ASCII"; + static final String CHARSET_UTF8 = "UTF-8"; + static final String CHARSET_KOI8_R = "KOI8_R"; + static final String CHARSET_WIN1251 = "Cp1251"; + + static final int SWISS_GERMAN_STUFF_UNICODE [] = { + 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x20, 0x7A, 0xE4, 0x6D, 0xE4 + }; + + static final int SWISS_GERMAN_STUFF_ISO8859_1 [] = { + 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x20, 0x7A, 0xE4, 0x6D, 0xE4 + }; + + static final int SWISS_GERMAN_STUFF_UTF8 [] = { + 0x47, 0x72, 0xC3, 0xBC, 0x65, 0x7A, 0x69, 0x20, 0x7A, 0xC3, 0xA4, + 0x6D, 0xC3, 0xA4 + }; + + static final int RUSSIAN_STUFF_UNICODE [] = { + 0x412, 0x441, 0x435, 0x43C, 0x20, 0x43F, 0x440, 0x438, + 0x432, 0x435, 0x442 + }; + + static final int RUSSIAN_STUFF_UTF8 [] = { + 0xD0, 0x92, 0xD1, 0x81, 0xD0, 0xB5, 0xD0, 0xBC, 0x20, + 0xD0, 0xBF, 0xD1, 0x80, 0xD0, 0xB8, 0xD0, 0xB2, 0xD0, + 0xB5, 0xD1, 0x82 + }; + + static final int RUSSIAN_STUFF_KOI8R [] = { + 0xF7, 0xD3, 0xC5, 0xCD, 0x20, 0xD0, 0xD2, 0xC9, 0xD7, + 0xC5, 0xD4 + }; + + static final int RUSSIAN_STUFF_WIN1251 [] = { + 0xC2, 0xF1, 0xE5, 0xEC, 0x20, 0xEF, 0xF0, 0xE8, 0xE2, + 0xE5, 0xF2 + }; + + // ------------------------------------------------------------ Constructor + + public TestMethodCharEncoding(String testName) { + super(testName); + } + + // ------------------------------------------------------- TestCase Methods + + public static Test suite() { + return new TestSuite(TestMethodCharEncoding.class); + } + + // ----------------------------------------------------------------- Tests + + + public void testRequestCharEncoding() throws IOException { + + GetMethod httpget = new GetMethod("/"); + assertEquals(CHARSET_DEFAULT, httpget.getRequestCharSet()); + httpget.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_ASCII); + assertEquals(CHARSET_ASCII, httpget.getRequestCharSet()); + httpget.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_UTF8); + assertEquals(CHARSET_UTF8, httpget.getRequestCharSet()); + + } + + public void testResponseCharEncoding() throws IOException { + + SimpleHttpConnection conn = new SimpleHttpConnection(); + String body = "stuff"; + String headers1 = "HTTP/1.1 200 OK\r\n" + +"Content-Length: 4\r\n"; + conn.addResponse(headers1, body); + conn.open(); + GetMethod httpget = new GetMethod("/"); + httpget.execute(new HttpState(), conn); + assertEquals(CHARSET_DEFAULT, httpget.getResponseCharSet()); + conn.close(); + httpget.recycle(); + + String headers2 = "HTTP/1.1 200 OK\r\n" + +"Content-Type: text/plain\r\n" + +"Content-Length: 4\r\n"; + conn.addResponse(headers2, body); + conn.open(); + httpget.setPath("/"); + httpget.execute(new HttpState(), conn); + assertEquals(CHARSET_DEFAULT, httpget.getResponseCharSet()); + conn.close(); + httpget.recycle(); + + String headers3 = "HTTP/1.1 200 OK\r\n" + +"Content-Type: text/plain; charset=" + CHARSET_UTF8 + "\r\n" + +"Content-Length: 4\r\n"; + conn.addResponse(headers3, body); + conn.open(); + httpget.setPath("/"); + httpget.execute(new HttpState(), conn); + assertEquals(CHARSET_UTF8, httpget.getResponseCharSet()); + conn.close(); + httpget.recycle(); + + } + + + private String constructString(int [] unicodeChars) { + StringBuffer buffer = new StringBuffer(); + if (unicodeChars != null) { + for (int i = 0; i < unicodeChars.length; i++) { + buffer.append((char)unicodeChars[i]); + } + } + return buffer.toString(); + } + + + private void verifyEncoding(final InputStream instream, final int[] sample) + throws IOException { + assertNotNull("Request body", instream); + + for (int i = 0; i < sample.length; i++) { + int b = instream.read(); + assertTrue("Unexpected end of stream", b != -1); + if (sample[i] != b) { + fail("Invalid request body encoding"); + } + } + assertTrue("End of stream expected", instream.read() == -1); + } + + + public void testLatinAccentInRequestBody() throws IOException { + + PostMethod httppost = new PostMethod("/"); + httppost.setRequestBody(constructString(SWISS_GERMAN_STUFF_UNICODE)); + // Test default encoding ISO-8859-1 + verifyEncoding(httppost.getRequestBody(), SWISS_GERMAN_STUFF_ISO8859_1); + // Test UTF-8 encoding + httppost.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_UTF8); + verifyEncoding(httppost.getRequestBody(), SWISS_GERMAN_STUFF_UTF8); + + } + + public void testRussianInRequestBody() throws IOException { + + PostMethod httppost = new PostMethod("/"); + httppost.setRequestBody(constructString(RUSSIAN_STUFF_UNICODE)); + + // Test UTF-8 encoding + httppost.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_UTF8); + verifyEncoding(httppost.getRequestBody(), RUSSIAN_STUFF_UTF8); + // Test KOI8-R + httppost.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_KOI8_R); + verifyEncoding(httppost.getRequestBody(), RUSSIAN_STUFF_KOI8R); + // Test WIN1251 + httppost.setRequestHeader("Content-Type", "text/plain; charset=" + CHARSET_WIN1251); + verifyEncoding(httppost.getRequestBody(), RUSSIAN_STUFF_WIN1251); + + } + + public void testUrlEncodedRequestBody() throws IOException { + + PostMethod httppost = new PostMethod("/"); + + String ru_msg = constructString(RUSSIAN_STUFF_UNICODE); + String ch_msg = constructString(SWISS_GERMAN_STUFF_UNICODE); + + httppost.setRequestBody(new NameValuePair[] { + new NameValuePair("ru", ru_msg), + new NameValuePair("ch", ch_msg) + }); + + httppost.setRequestHeader("Content-Type", PostMethod.FORM_URL_ENCODED_CONTENT_TYPE + + "; charset=" + CHARSET_UTF8); + + Map params = new HashMap(); + StringTokenizer tokenizer = new StringTokenizer( + httppost.getRequestBodyAsString(), "&"); + while (tokenizer.hasMoreTokens()) { + String s = tokenizer.nextToken(); + int i = s.indexOf('='); + assertTrue("Invalid url-encoded parameters", i != -1); + String name = s.substring(0, i).trim(); + String value = s.substring(i + 1, s.length()).trim(); + value = URIUtil.decode(value, CHARSET_UTF8); + params.put(name, value); + } + assertEquals(ru_msg, params.get("ru")); + assertEquals(ch_msg, params.get("ch")); + } + +} 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.16 diff -u -r1.16 TestMethodsNoHost.java --- test/org/apache/commons/httpclient/TestMethodsNoHost.java 7 Feb 2003 04:50:03 -0000 1.16 +++ test/org/apache/commons/httpclient/TestMethodsNoHost.java 16 Apr 2003 18:25:51 -0000 @@ -72,7 +72,6 @@ import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.HeadMethod; -import org.apache.commons.httpclient.util.URIUtil; /** * @author Rodney Waldhoff @@ -87,13 +86,11 @@ static final String NAME0 = "name0", VALUE0 = "value0"; static final String NAME1 = "name1", VALUE1 = "value1"; static final String NAME2 = "name2", VALUE2 = "value2"; - static final String NAME3 = "name3", FUNNY_CHARS = "üöäéèà"; static final NameValuePair PAIR = new NameValuePair(NAME, VALUE); static final NameValuePair PAIR0 = new NameValuePair(NAME0, VALUE0); static final NameValuePair PAIR1 = new NameValuePair(NAME1, VALUE1); static final NameValuePair PAIR2 = new NameValuePair(NAME2, VALUE2); - static final NameValuePair PAIR3 = new NameValuePair(NAME3, FUNNY_CHARS); // ------------------------------------------------------------ Constructor @@ -122,10 +119,6 @@ assertEquals("name=value&name1=value1&name2=value2&hasSpace=a%20b%20c%20d", post.getRequestBodyAsString()); - post.setRequestBody(new NameValuePair[]{ PAIR3 }); - assertEquals("name3=" + URIUtil.encodeWithinQuery( - FUNNY_CHARS, HttpConstants.DEFAULT_CONTENT_CHARSET), - post.getRequestBodyAsString()); } public void testPostSetRequestBody() throws Exception { Index: test/org/apache/commons/httpclient/TestNoHost.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestNoHost.java,v retrieving revision 1.21 diff -u -r1.21 TestNoHost.java --- test/org/apache/commons/httpclient/TestNoHost.java 27 Mar 2003 20:58:28 -0000 1.21 +++ test/org/apache/commons/httpclient/TestNoHost.java 16 Apr 2003 18:25:52 -0000 @@ -103,6 +103,7 @@ suite.addTest(TestStatusLine.suite()); suite.addTest(TestRequestLine.suite()); suite.addTest(TestPartsNoHost.suite()); + suite.addTest(TestMethodCharEncoding.suite()); return suite; }