mbecke 2004/05/11 20:07:03 Modified: httpclient/src/java/org/apache/commons/httpclient/methods PostMethod.java EntityEnclosingMethod.java RequestEntity.java InputStreamRequestEntity.java ByteArrayRequestEntity.java httpclient release_notes.txt Added: httpclient/src/java/org/apache/commons/httpclient/methods StringRequestEntity.java Log: Moved/added content type handling to RequestEntity. PR: 28645 Submitted by: Michael Becke and Oleg Kalnichevski Revision Changes Path 1.55 +13 -38 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java Index: PostMethod.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/PostMethod.java,v retrieving revision 1.54 retrieving revision 1.55 diff -u -r1.54 -r1.55 --- PostMethod.java 28 Apr 2004 02:23:17 -0000 1.54 +++ PostMethod.java 12 May 2004 03:07:03 -0000 1.55 @@ -29,13 +29,9 @@ package org.apache.commons.httpclient.methods; -import java.io.IOException; import java.util.Iterator; import java.util.Vector; -import org.apache.commons.httpclient.HttpConnection; -import org.apache.commons.httpclient.HttpException; -import org.apache.commons.httpclient.HttpState; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.util.EncodingUtil; import org.apache.commons.logging.Log; @@ -162,9 +158,16 @@ */ protected RequestEntity generateRequestEntity() { if (!this.params.isEmpty()) { + // Use a ByteArrayRequestEntity instead of a StringRequestEntity. + // This is to avoid potential encoding issues. Form url encoded strings + // are ASCII by definition but the content type may not be. Treating the content + // as bytes allows us to keep the current charset without worrying about how + // this charset will effect the encoding of the form url encoded string. String content = EncodingUtil.formUrlEncode(getParameters(), getRequestCharSet()); - ByteArrayRequestEntity entity = new ByteArrayRequestEntity(); - entity.setContent(EncodingUtil.getAsciiBytes(content)); + ByteArrayRequestEntity entity = new ByteArrayRequestEntity( + EncodingUtil.getAsciiBytes(content), + FORM_URL_ENCODED_CONTENT_TYPE + ); return entity; } else { return super.generateRequestEntity(); @@ -400,33 +403,5 @@ } clearRequestBody(); addParameters(parametersBody); - } - - /** - * Adds <tt>Content Type: application/x-www-form-urlencoded</tt> header in - * addition to the "standard" set of headers, if no <tt>Content Type</tt> - * header has been set by the user - * - * @param state the [EMAIL PROTECTED] HttpState state} information associated with this method - * @param conn the [EMAIL PROTECTED] HttpConnection connection} used to execute - * this HTTP method - * - * @throws IOException if an I/O (transport) error occurs. Some transport exceptions - * can be recovered from. - * @throws HttpException if a protocol exception occurs. Usually protocol exceptions - * cannot be recovered from. - * - * @since 2.0 - */ - protected void addRequestHeaders(HttpState state, HttpConnection conn) - throws IOException, HttpException { - super.addRequestHeaders(state, conn); - - 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); - } - } } } 1.34 +44 -15 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java Index: EntityEnclosingMethod.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java,v retrieving revision 1.33 retrieving revision 1.34 diff -u -r1.33 -r1.34 --- EntityEnclosingMethod.java 8 May 2004 10:12:07 -0000 1.33 +++ EntityEnclosingMethod.java 12 May 2004 03:07:03 -0000 1.34 @@ -34,12 +34,12 @@ import java.io.OutputStream; import org.apache.commons.httpclient.ChunkedOutputStream; +import org.apache.commons.httpclient.Header; 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.util.EncodingUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -60,12 +60,14 @@ /** * The content length will be calculated automatically. This implies * buffering of the content. + * @deprecated Use [EMAIL PROTECTED] InputStreamRequestEntity#CONTENT_LENGTH_AUTO}. */ public static final long CONTENT_LENGTH_AUTO = -2; /** * The request will use chunked transfer encoding. Content length is not * calculated and the content is not buffered.<br> + * @deprecated Use [EMAIL PROTECTED] #setContentChunked(boolean)}. */ public static final long CONTENT_LENGTH_CHUNKED = -1; @@ -167,19 +169,17 @@ 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; + this.requestEntity = new ByteArrayRequestEntity(requestBody); } else if (this.requestStream != null) { - InputStreamRequestEntity entity = new InputStreamRequestEntity(); - entity.setContent(requestStream); - entity.setContentLength(requestContentLength); + this.requestEntity = new InputStreamRequestEntity( + requestStream, + requestContentLength); this.requestStream = null; - this.requestEntity = entity; } else if (this.requestString != null) { - ByteArrayRequestEntity entity = new ByteArrayRequestEntity(); - entity.setContent(EncodingUtil.getBytes(this.requestString, getRequestCharSet())); - this.requestEntity = entity; + this.requestEntity = new StringRequestEntity( + requestString, + null, + getRequestCharSet()); } return this.requestEntity; @@ -238,6 +238,25 @@ this.requestContentLength = length; } + /* (non-Javadoc) + * @see org.apache.commons.httpclient.HttpMethodBase#getRequestCharSet() + */ + public String getRequestCharSet() { + if (getRequestHeader("Content-Type") == null) { + // check the content type from request entity + // We can't call getRequestEntity() since it will probably call + // this method. + if (this.requestEntity != null) { + return getContentCharSet( + new Header("Content-Type", requestEntity.getContentType())); + } else { + return super.getRequestCharSet(); + } + } else { + return super.getRequestCharSet(); + } + } + /** * Sets length information about the request body. * @@ -330,7 +349,17 @@ super.addRequestHeaders(state, conn); addContentLengthRequestHeader(state, conn); + + // only use the content type of the request entity if it has not already been + // set manually + if (getRequestHeader("Content-Type") == null) { + RequestEntity requestEntity = getRequestEntity(); + if (requestEntity != null && requestEntity.getContentType() != null) { + setRequestHeader("Content-Type", requestEntity.getContentType()); + } + } } + /** * Generates <tt>Content-Length</tt> or <tt>Transfer-Encoding: Chunked</tt> * request header, as long as no <tt>Content-Length</tt> request header 1.2 +11 -3 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/RequestEntity.java Index: RequestEntity.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/RequestEntity.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- RequestEntity.java 28 Apr 2004 02:23:17 -0000 1.1 +++ RequestEntity.java 12 May 2004 03:07:03 -0000 1.2 @@ -56,4 +56,12 @@ */ long getContentLength(); + /** + * Gets the entity's content type. This content type will be used as the value for the + * "Content-Type" header. + * @return + * @see org.apache.commons.httpclient.HttpMethod#setRequestHeader(String, String) + */ + String getContentType(); + } 1.2 +70 -28 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java Index: InputStreamRequestEntity.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/InputStreamRequestEntity.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- InputStreamRequestEntity.java 28 Apr 2004 02:23:17 -0000 1.1 +++ InputStreamRequestEntity.java 12 May 2004 03:07:03 -0000 1.2 @@ -43,21 +43,83 @@ */ public class InputStreamRequestEntity implements RequestEntity { + /** + * The content length will be calculated automatically. This implies + * buffering of the content. + */ + public static final int CONTENT_LENGTH_AUTO = -2; + private static final Log LOG = LogFactory.getLog(InputStreamRequestEntity.class); - private long contentLength = EntityEnclosingMethod.CONTENT_LENGTH_AUTO; + private long contentLength; - private InputStream content = null; + private InputStream content; /** The buffered request body, if any. */ private byte[] buffer = null; + + /** The content type */ + private String contentType; /** - * Creates a new InputStreamRequestEntity with no content. + * Creates a new InputStreamRequestEntity with the given content and a content type of + * [EMAIL PROTECTED] RequestEntity#CONTENT_LENGTH_AUTO}. + * @param content The content to set. */ - public InputStreamRequestEntity() { + public InputStreamRequestEntity(InputStream content) { + this(content, null); + } + + /** + * Creates a new InputStreamRequestEntity with the given content, content type, and a + * content length of [EMAIL PROTECTED] RequestEntity#CONTENT_LENGTH_AUTO}. + * @param content The content to set. + * @param contentType The type of the content, or <code>null</code>. + */ + public InputStreamRequestEntity(InputStream content, String contentType) { + this(content, CONTENT_LENGTH_AUTO, contentType); + } + + /** + * Creates a new InputStreamRequestEntity with the given content and content length. + * @param content The content to set. + * @param contentLength The content size in bytes or any of + * [EMAIL PROTECTED] EntityEnclosingMethod#CONTENT_LENGTH_AUTO CONTENT_LENGTH_AUTO}, + * [EMAIL PROTECTED] EntityEnclosingMethod#CONTENT_LENGTH_CHUNKED CONTENT_LENGTH_CHUNKED}. If the number + * of bytes or <code>CONTENT_LENGTH_CHUNKED</code> is specified the content will not be + * buffered when [EMAIL PROTECTED] #getContentLength()} is called. + */ + public InputStreamRequestEntity(InputStream content, long contentLength) { + this(content, contentLength, null); + } + + /** + * Creates a new InputStreamRequestEntity with the given content, content length, and + * content type. + * @param content The content to set. + * @param contentLength The content size in bytes or any of + * [EMAIL PROTECTED] EntityEnclosingMethod#CONTENT_LENGTH_AUTO CONTENT_LENGTH_AUTO}, + * [EMAIL PROTECTED] EntityEnclosingMethod#CONTENT_LENGTH_CHUNKED CONTENT_LENGTH_CHUNKED}. If the number + * of bytes or <code>CONTENT_LENGTH_CHUNKED</code> is specified the content will not be + * buffered when [EMAIL PROTECTED] #getContentLength()} is called. + * @param contentType The type of the content, or <code>null</code>. + */ + public InputStreamRequestEntity(InputStream content, long contentLength, String contentType) { + if (content == null) { + throw new IllegalArgumentException("The content cannot be null"); + } + this.content = content; + this.contentLength = contentLength; + this.contentType = contentType; } + /* (non-Javadoc) + * @see org.apache.commons.httpclient.methods.RequestEntity#getContentType() + */ + public String getContentType() { + return contentType; + } + /** * Buffers request body input stream. */ @@ -125,37 +187,17 @@ * @see #setContentLength(long) */ public long getContentLength() { - if (contentLength == EntityEnclosingMethod.CONTENT_LENGTH_AUTO && buffer == null) { + if (contentLength == CONTENT_LENGTH_AUTO && buffer == null) { bufferContent(); } return contentLength; } /** - * Sets the content length. - * - * @param contentLength The content size in bytes or any of - * [EMAIL PROTECTED] EntityEnclosingMethod#CONTENT_LENGTH_AUTO CONTENT_LENGTH_AUTO}, - * [EMAIL PROTECTED] EntityEnclosingMethod#CONTENT_LENGTH_CHUNKED CONTENT_LENGTH_CHUNKED}. If the number - * of bytes or <code>CONTENT_LENGTH_CHUNKED</code> is specified the content will not be - * buffered when [EMAIL PROTECTED] #getContentLength()} is called. - */ - public void setContentLength(long contentLength) { - this.contentLength = contentLength; - } - - /** * @return Returns the content. */ public InputStream getContent() { return content; - } - - /** - * @param inputStream The content to set. - */ - public void setContent(InputStream inputStream) { - this.content = inputStream; } } 1.2 +30 -19 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java Index: ByteArrayRequestEntity.java =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/ByteArrayRequestEntity.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- ByteArrayRequestEntity.java 28 Apr 2004 02:23:17 -0000 1.1 +++ ByteArrayRequestEntity.java 12 May 2004 03:07:03 -0000 1.2 @@ -41,12 +41,29 @@ /** The content */ private byte[] content; + /** The content type */ + private String contentType; + + /** + * Creates a new entity with the given content. + * @param content The content to set. + */ + public ByteArrayRequestEntity(byte[] content) { + this(content, null); + } + /** - * Creates a new entity with no content. The content must be set before this entity can be - * used. + * Creates a new entity with the given content and content type. + * @param content The content to set. + * @param contentType The content type to set or <code>null</code>. */ - public ByteArrayRequestEntity() { + public ByteArrayRequestEntity(byte[] content, String contentType) { super(); + if (content == null) { + throw new IllegalArgumentException("The content cannot be null"); + } + this.content = content; + this.contentType = contentType; } /** @@ -57,12 +74,16 @@ } /* (non-Javadoc) + * @see org.apache.commons.httpclient.methods.RequestEntity#getContentType() + */ + public String getContentType() { + return contentType; + } + + /* (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); } @@ -78,16 +99,6 @@ */ public byte[] getContent() { return content; - } - - /** - * @param content The content to set. - */ - public void setContent(byte[] content) { - if (content == null) { - throw new IllegalArgumentException("The content cannot be null"); - } - this.content = content; } } 1.1 jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/StringRequestEntity.java Index: StringRequestEntity.java =================================================================== /* * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/methods/StringRequestEntity.java,v 1.1 2004/05/12 03:07:03 mbecke Exp $ * $Revision: 1.1 $ * $Date: 2004/05/12 03:07:03 $ * * ==================================================================== * * 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 * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.commons.httpclient.methods; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import org.apache.commons.httpclient.HeaderElement; import org.apache.commons.httpclient.NameValuePair; /** * A RequestEntity that contains a String. */ public class StringRequestEntity implements RequestEntity { /** The content */ private String content; /** The charset */ private String charset; /** The content type (i.e. text/html; charset=EUC-JP). */ private String contentType; /** * Creates a new entity with the given content * * @param content The content to set. */ public StringRequestEntity(String content) { this(content, null, null); } /** * Creates a new entity with the given content, content type, and charset. * * @param content The content to set. * @param contentType The type of the content, or <code>null</code>. The value retured * by [EMAIL PROTECTED] #getContentType()}. If this content type contains a charset and the charset * parameter is null, the content's type charset will be used. * @param charset The charset of the content, or <code>null</code>. Used to convert the * content to bytes. If the content type does not contain a charset and charset is not null, * then the charset will be appended to the content type. */ public StringRequestEntity(String content, String contentType, String charset) { super(); if (content == null) { throw new IllegalArgumentException("The content cannot be null"); } this.content = content; this.contentType = contentType; this.charset = charset; // resolve the content type and the charset if (contentType != null) { HeaderElement[] values = HeaderElement.parseElements(contentType); NameValuePair charsetPair = null; for (int i = 0; i < values.length; i++) { if ((charsetPair = values[i].getParameterByName("charset")) != null) { // charset found break; } } if (charset == null && charsetPair != null) { // use the charset from the content type this.charset = charsetPair.getValue(); } else if (charset != null && charsetPair == null) { // append the charset to the content type this.contentType = contentType + "; charset=" + charset; } } } /* (non-Javadoc) * @see org.apache.commons.httpclient.methods.RequestEntity#getContentType() */ public String getContentType() { return contentType; } /** * @return <code>true</code> */ public boolean isRepeatable() { return true; } /* (non-Javadoc) * @see org.apache.commons.httpclient.RequestEntity#writeRequest(java.io.OutputStream) */ public void writeRequest(OutputStream out) throws IOException { Writer writer = null; if (this.charset != null) { writer = new OutputStreamWriter(out, this.charset); } else { writer = new OutputStreamWriter(out); } writer.write(content); writer.flush(); } /** * @return The length of the content. */ public long getContentLength() { return content.length(); } /** * @return Returns the content. */ public String getContent() { return this.content; } /** * @return Returns the charset used to convert the content to bytes. <code>null</code> if * no charset as been specified. */ public String getCharset() { return charset; } } 1.24 +3 -0 jakarta-commons/httpclient/release_notes.txt Index: release_notes.txt =================================================================== RCS file: /home/cvs/jakarta-commons/httpclient/release_notes.txt,v retrieving revision 1.23 retrieving revision 1.24 diff -u -r1.23 -r1.24 --- release_notes.txt 11 May 2004 20:48:12 -0000 1.23 +++ release_notes.txt 12 May 2004 03:07:03 -0000 1.24 @@ -50,6 +50,9 @@ Changes on the CVS trunk: ------------------------- + * 28645 - Moved/added content type handling to the RequestEntity. + Contributed by Michael Becke <mbecke at apache.org>, Oleg Kalnichevski <olegk at apache.org> + * 20288 - Added ability to abort execution of HTTP methods Contributed by Oleg Kalnichevski <olegk at apache.org>
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]