All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.httpclient.methods.EntityEnclosingMethod Maven / Gradle / Ivy

/*
 * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/EntityEnclosingMethod.java,v 1.39 2004/07/03 14:27:03 olegk Exp $
 * $Revision: 480424 $
 * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
 *
 * ====================================================================
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You 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
 * .
 *
 */

package org.apache.commons.httpclient.methods;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

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.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This abstract class serves as a foundation for all HTTP methods 
 * that can enclose an entity within requests 
 *
 * @author Oleg Kalnichevski
 * @author Jeff Dever
 *
 * @since 2.0beta1
 * @version $Revision: 480424 $
 */
public abstract class EntityEnclosingMethod extends ExpectContinueMethod {

    // ----------------------------------------- Static variables/initializers

    /**
     * The content length will be calculated automatically. This implies
     * buffering of the content.
     * @deprecated Use {@link InputStreamRequestEntity#CONTENT_LENGTH_AUTO}.
     */
    public static final long CONTENT_LENGTH_AUTO = InputStreamRequestEntity.CONTENT_LENGTH_AUTO;

    /**
     * The request will use chunked transfer encoding. Content length is not
     * calculated and the content is not buffered.
* @deprecated Use {@link #setContentChunked(boolean)}. */ public static final long CONTENT_LENGTH_CHUNKED = -1; /** LOG object for this class. */ private static final Log LOG = LogFactory.getLog(EntityEnclosingMethod.class); /** The unbuffered request body, if any. */ private InputStream requestStream = null; /** The request body as string, if any. */ private String requestString = null; private RequestEntity requestEntity; /** Counts how often the request was sent to the server. */ private int repeatCount = 0; /** The content length of the requestBodyStream or one of * CONTENT_LENGTH_AUTO and CONTENT_LENGTH_CHUNKED. * * @deprecated */ private long requestContentLength = InputStreamRequestEntity.CONTENT_LENGTH_AUTO; private boolean chunked = false; // ----------------------------------------------------------- Constructors /** * No-arg constructor. * * @since 2.0 */ public EntityEnclosingMethod() { super(); setFollowRedirects(false); } /** * Constructor specifying a URI. * * @param uri either an absolute or relative URI * * @since 2.0 */ public EntityEnclosingMethod(String uri) { super(uri); setFollowRedirects(false); } /** * Returns true if there is a request body to be sent. * *

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

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

This method must be overridden 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.requestEntity = null; } /** * Generates the request body. * *

This method must be overridden 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 */ protected byte[] generateRequestBody() { LOG.trace("enter EntityEnclosingMethod.renerateRequestBody()"); return null; } protected RequestEntity generateRequestEntity() { byte[] requestBody = generateRequestBody(); if (requestBody != null) { // use the request body, if it exists. // this is just for backwards compatability this.requestEntity = new ByteArrayRequestEntity(requestBody); } else if (this.requestStream != null) { this.requestEntity = new InputStreamRequestEntity( requestStream, requestContentLength); this.requestStream = null; } else if (this.requestString != null) { String charset = getRequestCharSet(); try { this.requestEntity = new StringRequestEntity( requestString, null, charset); } catch (UnsupportedEncodingException e) { if (LOG.isWarnEnabled()) { LOG.warn(charset + " not supported"); } try { this.requestEntity = new StringRequestEntity( requestString, null, null); } catch (UnsupportedEncodingException ignore) { } } } return this.requestEntity; } /** * Entity enclosing requests cannot be redirected without user intervention * according to RFC 2616. * * @return false. * * @since 2.0 */ public boolean getFollowRedirects() { return false; } /** * Entity enclosing requests cannot be redirected without user intervention * according to RFC 2616. * * @param followRedirects must always be false */ public void setFollowRedirects(boolean followRedirects) { if (followRedirects == true) { throw new IllegalArgumentException("Entity enclosing requests cannot be redirected without user intervention"); } super.setFollowRedirects(false); } /** * Sets length information about the request body. * *

* Note: If you specify a content length the request is unbuffered. This * prevents redirection and automatic retry if a request fails the first * time. This means that the HttpClient can not perform authorization * automatically but will throw an Exception. You will have to set the * necessary 'Authorization' or 'Proxy-Authorization' headers manually. *

* * @param length size in bytes or any of CONTENT_LENGTH_AUTO, * CONTENT_LENGTH_CHUNKED. If number of bytes or CONTENT_LENGTH_CHUNKED * is specified the content will not be buffered internally and the * Content-Length header of the request will be used. In this case * the user is responsible to supply the correct content length. * If CONTENT_LENGTH_AUTO is specified the request will be buffered * before it is sent over the network. * * @deprecated Use {@link #setContentChunked(boolean)} or * {@link #setRequestEntity(RequestEntity)} */ public void setRequestContentLength(int length) { LOG.trace("enter EntityEnclosingMethod.setRequestContentLength(int)"); this.requestContentLength = length; } /** * Returns the request's charset. The charset is parsed from the request entity's * content type, unless the content type header has been set manually. * * @see RequestEntity#getContentType() * * @since 3.0 */ 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. * *

* Note: If you specify a content length the request is unbuffered. This * prevents redirection and automatic retry if a request fails the first * time. This means that the HttpClient can not perform authorization * automatically but will throw an Exception. You will have to set the * necessary 'Authorization' or 'Proxy-Authorization' headers manually. *

* * @param length size in bytes or any of CONTENT_LENGTH_AUTO, * CONTENT_LENGTH_CHUNKED. If number of bytes or CONTENT_LENGTH_CHUNKED * is specified the content will not be buffered internally and the * Content-Length header of the request will be used. In this case * the user is responsible to supply the correct content length. * If CONTENT_LENGTH_AUTO is specified the request will be buffered * before it is sent over the network. * * @deprecated Use {@link #setContentChunked(boolean)} or * {@link #setRequestEntity(RequestEntity)} */ public void setRequestContentLength(long length) { LOG.trace("enter EntityEnclosingMethod.setRequestContentLength(int)"); this.requestContentLength = length; } /** * Sets whether or not the content should be chunked. * * @param chunked true if the content should be chunked * * @since 3.0 */ public void setContentChunked(boolean chunked) { this.chunked = chunked; } /** * Returns the length of the request body. * * @return number of bytes in the request body */ protected long getRequestContentLength() { LOG.trace("enter EntityEnclosingMethod.getRequestContentLength()"); if (!hasRequestContent()) { return 0; } if (this.chunked) { return -1; } if (this.requestEntity == null) { this.requestEntity = generateRequestEntity(); } return (this.requestEntity == null) ? 0 : this.requestEntity.getContentLength(); } /** * Populates the request headers map to with additional * {@link org.apache.commons.httpclient.Header headers} to be submitted to * the given {@link HttpConnection}. * *

* This implementation adds tt>Content-Length or Transfer-Encoding * headers. *

* *

* Subclasses may want to override this method to to add additional * headers, and may choose to invoke this implementation (via * super) to add the "standard" headers. *

* * @param state the {@link HttpState state} information associated with this method * @param conn the {@link 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. * * @see #writeRequestHeaders * * @since 3.0 */ protected void addRequestHeaders(HttpState state, HttpConnection conn) throws IOException, HttpException { LOG.trace("enter EntityEnclosingMethod.addRequestHeaders(HttpState, " + "HttpConnection)"); 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 Content-Length or Transfer-Encoding: Chunked * request header, as long as no Content-Length request header * already exists. * * @param state current state of http requests * @param conn the connection to use for I/O * * @throws IOException when errors occur reading or writing to/from the * connection * @throws HttpException when a recoverable error occurs */ protected void addContentLengthRequestHeader(HttpState state, HttpConnection conn) throws IOException, HttpException { LOG.trace("enter EntityEnclosingMethod.addContentLengthRequestHeader(" + "HttpState, HttpConnection)"); if ((getRequestHeader("content-length") == null) && (getRequestHeader("Transfer-Encoding") == null)) { long len = getRequestContentLength(); if (len < 0) { if (getEffectiveVersion().greaterEquals(HttpVersion.HTTP_1_1)) { addRequestHeader("Transfer-Encoding", "chunked"); } else { throw new ProtocolException(getEffectiveVersion() + " does not support chunk encoding"); } } else { addRequestHeader("Content-Length", String.valueOf(len)); } } } /** * Sets the request body to be the specified inputstream. * * @param body Request body content as {@link java.io.InputStream} * * @deprecated use {@link #setRequestEntity(RequestEntity)} */ public void setRequestBody(InputStream body) { LOG.trace("enter EntityEnclosingMethod.setRequestBody(InputStream)"); clearRequestBody(); this.requestStream = body; } /** * Sets the request body to be the specified string. * The string will be submitted, using the encoding * specified in the Content-Type request header.
* Example: setRequestHeader("Content-type", "text/xml; charset=UTF-8");
* Would use the UTF-8 encoding. * If no charset is specified, the * {@link org.apache.commons.httpclient.HttpConstants#DEFAULT_CONTENT_CHARSET default} * content encoding is used (ISO-8859-1). * * @param body Request body content as a string * * @deprecated use {@link #setRequestEntity(RequestEntity)} */ public void setRequestBody(String body) { LOG.trace("enter EntityEnclosingMethod.setRequestBody(String)"); clearRequestBody(); this.requestString = body; } /** * Writes the request body to the given {@link HttpConnection connection}. * * @param state the {@link HttpState state} information associated with this method * @param conn the {@link HttpConnection connection} used to execute * this HTTP method * * @return true * * @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. */ protected boolean writeRequestBody(HttpState state, HttpConnection conn) throws IOException, HttpException { LOG.trace( "enter EntityEnclosingMethod.writeRequestBody(HttpState, HttpConnection)"); if (!hasRequestContent()) { LOG.debug("Request body has not been specified"); return true; } if (this.requestEntity == null) { this.requestEntity = generateRequestEntity(); } if (requestEntity == null) { LOG.debug("Request body is empty"); return true; } long contentLength = getRequestContentLength(); if ((this.repeatCount > 0) && !requestEntity.isRepeatable()) { throw new ProtocolException( "Unbuffered entity enclosing request can not be repeated."); } this.repeatCount++; OutputStream outstream = conn.getRequestOutputStream(); if (contentLength < 0) { outstream = new ChunkedOutputStream(outstream); } requestEntity.writeRequest(outstream); // This is hardly the most elegant solution to closing chunked stream if (outstream instanceof ChunkedOutputStream) { ((ChunkedOutputStream) outstream).finish(); } outstream.flush(); LOG.debug("Request body sent"); return true; } /** * Recycles the HTTP method so that it can be used again. * Note that all of the instance variables will be reset * once this method has been called. This method will also * release the connection being used by this HTTP method. * * @see #releaseConnection() * * @deprecated no longer supported and will be removed in the future * version of HttpClient */ public void recycle() { LOG.trace("enter EntityEnclosingMethod.recycle()"); clearRequestBody(); this.requestContentLength = InputStreamRequestEntity.CONTENT_LENGTH_AUTO; this.repeatCount = 0; this.chunked = false; super.recycle(); } /** * @return Returns the requestEntity. * * @since 3.0 */ public RequestEntity getRequestEntity() { return generateRequestEntity(); } /** * @param requestEntity The requestEntity to set. * * @since 3.0 */ public void setRequestEntity(RequestEntity requestEntity) { clearRequestBody(); this.requestEntity = requestEntity; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy