    1   /*
    2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v 1.222 2005/01/14 21:16:40 olegk Exp $
    3    * $Revision: 5562 $
    4    * $Date: 2007-11-16 00:53:10 +0000 (Fri, 16 Nov 2007) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Copyright 1999-2004 The Apache Software Foundation
    9    *
    10   *  Licensed under the Apache License, Version 2.0 (the "License");
    11   *  you may not use this file except in compliance with the License.
    12   *  You may obtain a copy of the License at
    13   *
    14   *      http://www.apache.org/licenses/LICENSE-2.0
    15   *
    16   *  Unless required by applicable law or agreed to in writing, software
    17   *  distributed under the License is distributed on an "AS IS" BASIS,
    18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    19   *  See the License for the specific language governing permissions and
    20   *  limitations under the License.
    21   * ====================================================================
    22   *
    23   * This software consists of voluntary contributions made by many
    24   * individuals on behalf of the Apache Software Foundation.  For more
    25   * information on the Apache Software Foundation, please see
    26   * <http://www.apache.org/>.
    27   *
    28   */
    29
    30  package org.apache.commons.httpclient;
    31
    32  import java.io.ByteArrayInputStream;
    33  import java.io.ByteArrayOutputStream;
    34  import java.io.IOException;
    35  import java.io.InputStream;
    36  import java.io.InterruptedIOException;
    37  import java.util.Collection;
    38
    39  import org.apache.commons.httpclient.auth.AuthState;
    40  import org.apache.commons.httpclient.cookie.CookiePolicy;
    41  import org.apache.commons.httpclient.cookie.CookieSpec;
    42  import org.apache.commons.httpclient.cookie.MalformedCookieException;
    43  import org.apache.commons.httpclient.params.HttpMethodParams;
    44  import org.apache.commons.httpclient.protocol.Protocol;
    45  import org.apache.commons.httpclient.util.EncodingUtil;
    46  import org.apache.commons.httpclient.util.ExceptionUtil;
    47  import org.apache.commons.logging.Log;
    48  import org.apache.commons.logging.LogFactory;
    49
    50  /***
    51   * An abstract base implementation of HttpMethod.
    52   * <p>
    53   * At minimum, subclasses will need to override:
    54   * <ul>
    55   *   <li>{@link #getName} to return the approriate name for this method
    56   *   </li>
    57   * </ul>
    58   * </p>
    59   *
    60   * <p>
    61   * When a method requires additional request headers, subclasses will typically
    62   * want to override:
    63   * <ul>
    64   *   <li>{@link #addRequestHeaders addRequestHeaders(HttpState,HttpConnection)}
    65   *      to write those headers
    66   *   </li>
    67   * </ul>
    68   * </p>
    69   *
    70   * <p>
    71   * When a method expects specific response headers, subclasses may want to
    72   * override:
    73   * <ul>
    74   *   <li>{@link #processResponseHeaders processResponseHeaders(HttpState,HttpConnection)}
    75   *     to handle those headers
    76   *   </li>
    77   * </ul>
    78   * </p>
    79   *
    80   *
    81   * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
    82   * @author Rodney Waldhoff
    83   * @author Sean C. Sullivan
    84   * @author <a href="mailto:dion@apache.org">dIon Gillard</a>
    85   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
    86   * @author <a href="mailto:dims@apache.org">Davanum Srinivas</a>
    87   * @author Ortwin Glueck
    88   * @author Eric Johnson
    89   * @author Michael Becke
    90   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
    91   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
    92   * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
    93   * @author Christian Kohlschuetter
    94   *
    95   * @version $Revision: 5562 $ $Date: 2007-11-16 00:53:10 +0000 (Fri, 16 Nov 2007) $
    96   */
    97  @SuppressWarnings("deprecation") // <- // IA/HERITRIX change
    98  public abstract class HttpMethodBase implements HttpMethod {
    99
    100     // -------------------------------------------------------------- Constants
    101
    102     /*** Log object for this class. */
    103     private static final Log LOG = LogFactory.getLog(HttpMethodBase.class);
    104
    105     // ----------------------------------------------------- Instance variables
    106
    107     /*** Request headers, if any. */
    108     private HeaderGroup requestHeaders = new HeaderGroup();
    109
    110     /*** The Status-Line from the response. */
    111     private StatusLine statusLine = null;
    112
    113     /*** Response headers, if any. */
    114     private HeaderGroup responseHeaders = new HeaderGroup();
    115
    116     /*** Response trailer headers, if any. */
    117     private HeaderGroup responseTrailerHeaders = new HeaderGroup();
    118
    119     /*** Path of the HTTP method. */
    120     private String path = null;
    121
    122     /*** Query string of the HTTP method, if any. */
    123     private String queryString = null;
    124
    125     /*** The response body of the HTTP method, assuming it has not be
    126      * intercepted by a sub-class. */
    127     private InputStream responseStream = null;
    128
    129     /*** The connection that the response stream was read from. */
    130     private HttpConnection responseConnection = null;
    131
    132     /*** Buffer for the response */
    133     private byte[] responseBody = null;
    134
    135     /*** True if the HTTP method should automatically follow HTTP redirects.*/
    136     private boolean followRedirects = false;
    137
    138     /*** True if the HTTP method should automatically handle
    139     *  HTTP authentication challenges. */
    140     private boolean doAuthentication = true;
    141
    142     /*** HTTP protocol parameters. */
    143     private HttpMethodParams params = new HttpMethodParams();
    144
    145     /*** Host authentication state */
    146     private AuthState hostAuthState = new AuthState();
    147
    148     /*** Proxy authentication state */
    149     private AuthState proxyAuthState = new AuthState();
    150
    151     /*** True if this method has already been executed. */
    152     private boolean used = false;
    153
    154     /*** Count of how many times did this HTTP method transparently handle
    155     * a recoverable exception. */
    156     private int recoverableExceptionCount = 0;
    157
    158     /*** the host for this HTTP method, can be null */
    159     private HttpHost httphost = null;
    160
    161     /***
    162      * Handles method retries
    163      *
    164      * @deprecated no loner used
    165      */
    166     private MethodRetryHandler methodRetryHandler;
    167
    168     /*** True if the connection must be closed when no longer needed */
    169     private boolean connectionCloseForced = false;
    170
    171     /*** Number of milliseconds to wait for 100-contunue response. */
    172     private static final int RESPONSE_WAIT_TIME_MS = 3000;
    173
    174     /*** HTTP protocol version used for execution of this method. */
    175     private HttpVersion effectiveVersion = null;
    176
    177     /*** Whether the execution of this method has been aborted */
    178     private transient boolean aborted = false;
    179
    180     /*** Whether the HTTP request has been transmitted to the target
    181      * server it its entirety */
    182     private boolean requestSent = false;
    183
    184     /*** Actual cookie policy */
    185     private CookieSpec cookiespec = null;
    186
    187     /*** Default initial size of the response buffer if content length is unknown. */
    188     private static final int DEFAULT_INITIAL_BUFFER_SIZE = 4*1024; // 4 kB
    189
    190     // ----------------------------------------------------------- Constructors
    191
    192     /***
    193      * No-arg constructor.
    194      */
    195     public HttpMethodBase() {
    196     }
    197
    198     /***
    199      * Constructor specifying a URI.
    200      * It is responsibility of the caller to ensure that URI elements
    201      * (path & query parameters) are properly encoded (URL safe).
    202      *
    203      * @param uri either an absolute or relative URI. The URI is expected
    204      *            to be URL-encoded
    205      *
    206      * @throws IllegalArgumentException when URI is invalid
    207      * @throws IllegalStateException when protocol of the absolute URI is not recognised
    208      */
    209     public HttpMethodBase(String uri)
    210         throws IllegalArgumentException, IllegalStateException {
    211
    212         try {
    213
    214             // create a URI and allow for null/empty uri values
    215             if (uri == null || uri.equals("")) {
    216                 uri = "/";
    217             }
    218 // BEGIN IA/HERITRIX CHANGES
    219 //        setURI(new URI(uri, true));
    220           setURI(new org.archive.net.LaxURI(uri, true));
    221 // END IA/HERITRIX CHANGES
    222         } catch (URIException e) {
    223             throw new IllegalArgumentException("Invalid uri '"
    224                 + uri + "': " + e.getMessage()
    225             );
    226         }
    227     }
    228
    229     // ------------------------------------------- Property Setters and Getters
    230
    231     /***
    232      * Obtains the name of the HTTP method as used in the HTTP request line,
    233      * for example <tt>"GET"</tt> or <tt>"POST"</tt>.
    234      *
    235      * @return the name of this method
    236      */
    237     public abstract String getName();
    238
    239     /***
    240      * Returns the URI of the HTTP method
    241      *
    242      * @return The URI
    243      *
    244      * @throws URIException If the URI cannot be created.
    245      *
    246      * @see org.apache.commons.httpclient.HttpMethod#getURI()
    247      */
    248     public URI getURI() throws URIException {
    249         StringBuffer buffer = new StringBuffer();
    250         if (this.httphost != null) {
    251             buffer.append(this.httphost.getProtocol().getScheme());
    252             buffer.append("://");
    253             buffer.append(this.httphost.getHostName());
    254             int port = this.httphost.getPort();
    255             if (port != -1 && port != this.httphost.getProtocol().getDefaultPort()) {
    256                 buffer.append(":");
    257                 buffer.append(port);
    258             }
    259         }
    260         buffer.append(this.path);
    261         if (this.queryString != null) {
    262             buffer.append('?');
    263             buffer.append(this.queryString);
    264         }
    265 //      BEGIN IA/HERITRIX CHANGES
    266 //      return new URI(buffer.toString(), true);
    267         return new org.archive.net.LaxURI(buffer.toString(), true);
    268 //      END IA/HERITRIX CHANGES
    269     }
    270
    271     /***
    272      * Sets the URI for this method.
    273      *
    274      * @param uri URI to be set
    275      *
    276      * @throws URIException if a URI cannot be set
    277      *
    278      * @since 3.0
    279      */
    280     public void setURI(URI uri) throws URIException {
    281         // only set the host if specified by the URI
    282         if (uri.isAbsoluteURI()) {
    283             this.httphost = new HttpHost(uri);
    284         }
    285         // set the path, defaulting to root
    286         setPath(
    287             uri.getPath() == null
    288             ? "/"
    289             : uri.getEscapedPath()
    290         );
    291         setQueryString(uri.getEscapedQuery());
    292     }
    293
    294     /***
    295      * Sets whether or not the HTTP method should automatically follow HTTP redirects
    296      * (status code 302, etc.)
    297      *
    298      * @param followRedirects <tt>true</tt> if the method will automatically follow redirects,
    299      * <tt>false</tt> otherwise.
    300      */
    301     public void setFollowRedirects(boolean followRedirects) {
    302         this.followRedirects = followRedirects;
    303     }
    304
    305     /***
    306      * Returns <tt>true</tt> if the HTTP method should automatically follow HTTP redirects
    307      * (status code 302, etc.), <tt>false</tt> otherwise.
    308      *
    309      * @return <tt>true</tt> if the method will automatically follow HTTP redirects,
    310      * <tt>false</tt> otherwise.
    311      */
    312     public boolean getFollowRedirects() {
    313         return this.followRedirects;
    314     }
    315
    316     /*** Sets whether version 1.1 of the HTTP protocol should be used per default.
    317      *
    318      * @param http11 <tt>true</tt> to use HTTP/1.1, <tt>false</tt> to use 1.0
    319      *
    320      * @deprecated Use {@link HttpMethodParams#setVersion(HttpVersion)}
    321      */
    322     public void setHttp11(boolean http11) {
    323         if (http11) {
    324             this.params.setVersion(HttpVersion.HTTP_1_1);
    325         } else {
    326             this.params.setVersion(HttpVersion.HTTP_1_0);
    327         }
    328     }
    329
    330     /***
    331      * Returns <tt>true</tt> if the HTTP method should automatically handle HTTP
    332      * authentication challenges (status code 401, etc.), <tt>false</tt> otherwise
    333      *
    334      * @return <tt>true</tt> if authentication challenges will be processed
    335      * automatically, <tt>false</tt> otherwise.
    336      *
    337      * @since 2.0
    338      */
    339     public boolean getDoAuthentication() {
    340         return doAuthentication;
    341     }
    342
    343     /***
    344      * Sets whether or not the HTTP method should automatically handle HTTP
    345      * authentication challenges (status code 401, etc.)
    346      *
    347      * @param doAuthentication <tt>true</tt> to process authentication challenges
    348      * authomatically, <tt>false</tt> otherwise.
    349      *
    350      * @since 2.0
    351      */
    352     public void setDoAuthentication(boolean doAuthentication) {
    353         this.doAuthentication = doAuthentication;
    354     }
    355
    356     // ---------------------------------------------- Protected Utility Methods
    357
    358     /***
    359      * Returns <tt>true</tt> if version 1.1 of the HTTP protocol should be
    360      * used per default, <tt>false</tt> if version 1.0 should be used.
    361      *
    362      * @return <tt>true</tt> to use HTTP/1.1, <tt>false</tt> to use 1.0
    363      *
    364      * @deprecated Use {@link HttpMethodParams#getVersion()}
    365      */
    366     public boolean isHttp11() {
    367         return this.params.getVersion().equals(HttpVersion.HTTP_1_1);
    368     }
    369
    370     /***
    371      * Sets the path of the HTTP method.
    372      * It is responsibility of the caller to ensure that the path is
    373      * properly encoded (URL safe).
    374      *
    375      * @param path the path of the HTTP method. The path is expected
    376      *        to be URL-encoded
    377      */
    378     public void setPath(String path) {
    379         this.path = path;
    380     }
    381
    382     /***
    383      * Adds the specified request header, NOT overwriting any previous value.
    384      * Note that header-name matching is case insensitive.
    385      *
    386      * @param header the header to add to the request
    387      */
    388     public void addRequestHeader(Header header) {
    389         LOG.trace("HttpMethodBase.addRequestHeader(Header)");
    390
    391         if (header == null) {
    392             LOG.debug("null header value ignored");
    393         } else {
    394             getRequestHeaderGroup().addHeader(header);
    395         }
    396     }
    397
    398     /***
    399      * Use this method internally to add footers.
    400      *
    401      * @param footer The footer to add.
    402      */
    403     public void addResponseFooter(Header footer) {
    404         getResponseTrailerHeaderGroup().addHeader(footer);
    405     }
    406
    407     /***
    408      * Gets the path of this HTTP method.
    409      * Calling this method <em>after</em> the request has been executed will
    410      * return the <em>actual</em> path, following any redirects automatically
    411      * handled by this HTTP method.
    412      *
    413      * @return the path to request or "/" if the path is blank.
    414      */
    415     public String getPath() {
    416         return (path == null || path.equals("")) ? "/" : path;
    417     }
    418
    419     /***
    420      * Sets the query string of this HTTP method. The caller must ensure that the string
    421      * is properly URL encoded. The query string should not start with the question
    422      * mark character.
    423      *
    424      * @param queryString the query string
    425      *
    426      * @see EncodingUtil#formUrlEncode(NameValuePair[], String)
    427      */
    428     public void setQueryString(String queryString) {
    429         this.queryString = queryString;
    430     }
    431
    432     /***
    433      * Sets the query string of this HTTP method.  The pairs are encoded as UTF-8 characters.
    434      * To use a different charset the parameters can be encoded manually using EncodingUtil
    435      * and set as a single String.
    436      *
    437      * @param params an array of {@link NameValuePair}s to add as query string
    438      *        parameters. The name/value pairs will be automcatically
    439      *        URL encoded
    440      *
    441      * @see EncodingUtil#formUrlEncode(NameValuePair[], String)
    442      * @see #setQueryString(String)
    443      */
    444     public void setQueryString(NameValuePair[] params) {
    445         LOG.trace("enter HttpMethodBase.setQueryString(NameValuePair[])");
    446         queryString = EncodingUtil.formUrlEncode(params, "UTF-8");
    447     }
    448
    449     /***
    450      * Gets the query string of this HTTP method.
    451      *
    452      * @return The query string
    453      */
    454     public String getQueryString() {
    455         return queryString;
    456     }
    457
    458     /***
    459      * Set the specified request header, overwriting any previous value. Note
    460      * that header-name matching is case-insensitive.
    461      *
    462      * @param headerName the header's name
    463      * @param headerValue the header's value
    464      */
    465     public void setRequestHeader(String headerName, String headerValue) {
    466         Header header = new Header(headerName, headerValue);
    467         setRequestHeader(header);
    468     }
    469
    470     /***
    471      * Sets the specified request header, overwriting any previous value.
    472      * Note that header-name matching is case insensitive.
    473      *
    474      * @param header the header
    475      */
    476     public void setRequestHeader(Header header) {
    477
    478         Header[] headers = getRequestHeaderGroup().getHeaders(header.getName());
    479
    480         for (int i = 0; i < headers.length; i++) {
    481             getRequestHeaderGroup().removeHeader(headers[i]);
    482         }
    483
    484         getRequestHeaderGroup().addHeader(header);
    485
    486     }
    487
    488     /***
    489      * Returns the specified request header. Note that header-name matching is
    490      * case insensitive. <tt>null</tt> will be returned if either
    491      * <i>headerName</i> is <tt>null</tt> or there is no matching header for
    492      * <i>headerName</i>.
    493      *
    494      * @param headerName The name of the header to be returned.
    495      *
    496      * @return The specified request header.
    497      *
    498      * @since 3.0
    499      */
    500     public Header getRequestHeader(String headerName) {
    501         if (headerName == null) {
    502             return null;
    503         } else {
    504             return getRequestHeaderGroup().getCondensedHeader(headerName);
    505         }
    506     }
    507
    508     /***
    509      * Returns an array of the requests headers that the HTTP method currently has
    510      *
    511      * @return an array of my request headers.
    512      */
    513     public Header[] getRequestHeaders() {
    514         return getRequestHeaderGroup().getAllHeaders();
    515     }
    516
    517     /***
    518      * @see org.apache.commons.httpclient.HttpMethod#getRequestHeaders(java.lang.String)
    519      */
    520     public Header[] getRequestHeaders(String headerName) {
    521         return getRequestHeaderGroup().getHeaders(headerName);
    522     }
    523
    524     /***
    525      * Gets the {@link HeaderGroup header group} storing the request headers.
    526      *
    527      * @return a HeaderGroup
    528      *
    529      * @since 2.0beta1
    530      */
    531     protected HeaderGroup getRequestHeaderGroup() {
    532         return requestHeaders;
    533     }
    534
    535     /***
    536      * Gets the {@link HeaderGroup header group} storing the response trailer headers
    537      * as per RFC 2616 section 3.6.1.
    538      *
    539      * @return a HeaderGroup
    540      *
    541      * @since 2.0beta1
    542      */
    543     protected HeaderGroup getResponseTrailerHeaderGroup() {
    544         return responseTrailerHeaders;
    545     }
    546
    547     /***
    548      * Gets the {@link HeaderGroup header group} storing the response headers.
    549      *
    550      * @return a HeaderGroup
    551      *
    552      * @since 2.0beta1
    553      */
    554     protected HeaderGroup getResponseHeaderGroup() {
    555         return responseHeaders;
    556     }
    557
    558     /***
    559      * @see org.apache.commons.httpclient.HttpMethod#getResponseHeaders(java.lang.String)
    560      *
    561      * @since 3.0
    562      */
    563     public Header[] getResponseHeaders(String headerName) {
    564         return getResponseHeaderGroup().getHeaders(headerName);
    565     }
    566
    567     /***
    568      * Returns the response status code.
    569      *
    570      * @return the status code associated with the latest response.
    571      */
    572     public int getStatusCode() {
    573         return statusLine.getStatusCode();
    574     }
    575
    576     /***
    577      * Provides access to the response status line.
    578      *
    579      * @return the status line object from the latest response.
    580      * @since 2.0
    581      */
    582     public StatusLine getStatusLine() {
    583         return statusLine;
    584     }
    585
    586     /***
    587      * Checks if response data is available.
    588      * @return <tt>true</tt> if response data is available, <tt>false</tt> otherwise.
    589      */
    590     private boolean responseAvailable() {
    591         return (responseBody != null) || (responseStream != null);
    592     }
    593
    594     /***
    595      * Returns an array of the response headers that the HTTP method currently has
    596      * in the order in which they were read.
    597      *
    598      * @return an array of response headers.
    599      */
    600     public Header[] getResponseHeaders() {
    601         return getResponseHeaderGroup().getAllHeaders();
    602     }
    603
    604     /***
    605      * Gets the response header associated with the given name. Header name
    606      * matching is case insensitive. <tt>null</tt> will be returned if either
    607      * <i>headerName</i> is <tt>null</tt> or there is no matching header for
    608      * <i>headerName</i>.
    609      *
    610      * @param headerName the header name to match
    611      *
    612      * @return the matching header
    613      */
    614     public Header getResponseHeader(String headerName) {
    615         if (headerName == null) {
    616             return null;
    617         } else {
    618             return getResponseHeaderGroup().getCondensedHeader(headerName);
    619         }
    620     }
    621
    622
    623     /***
    624      * Return the length (in bytes) of the response body, as specified in a
    625      * <tt>Content-Length</tt> header.
    626      *
    627      * <p>
    628      * Return <tt>-1</tt> when the content-length is unknown.
    629      * </p>
    630      *
    631      * @return content length, if <tt>Content-Length</tt> header is available.
    632      *          <tt>0</tt> indicates that the request has no body.
    633      *          If <tt>Content-Length</tt> header is not present, the method
    634      *          returns  <tt>-1</tt>.
    635      */
    636     public long getResponseContentLength() {
    637         Header[] headers = getResponseHeaderGroup().getHeaders("Content-Length");
    638         if (headers.length == 0) {
    639             return -1;
    640         }
    641         if (headers.length > 1) {
    642             LOG.warn("Multiple content-length headers detected");
    643         }
    644         for (int i = headers.length - 1; i >= 0; i--) {
    645             Header header = headers[i];
    646             try {
    647                 return Long.parseLong(header.getValue());
    648             } catch (NumberFormatException e) {
    649                 if (LOG.isWarnEnabled()) {
    650                     LOG.warn("Invalid content-length value: " + e.getMessage());
    651                 }
    652             }
    653             // See if we can have better luck with another header, if present
    654         }
    655         return -1;
    656     }
    657
    658
    659     /***
    660      * Returns the response body of the HTTP method, if any, as an array of bytes.
    661      * If response body is not available or cannot be read, returns <tt>null</tt>
    662      *
    663      * Note: This will cause the entire response body to be buffered in memory. A
    664      * malicious server may easily exhaust all the VM memory. It is strongly
    665      * recommended, to use getResponseAsStream if the content length of the response
    666      * is unknown or resonably large.
    667      *
    668      * @return The response body.
    669      *
    670      * @throws IOException If an I/O (transport) problem occurs while obtaining the
    671      * response body.
    672      */
    673     public byte[] getResponseBody() throws IOException {
    674         if (this.responseBody == null) {
    675             InputStream instream = getResponseBodyAsStream();
    676             if (instream != null) {
    677                 long contentLength = getResponseContentLength();
    678                 if (contentLength > Integer.MAX_VALUE) { //guard below cast from overflow
    679                     throw new IOException("Content too large to be buffered: "+ contentLength +" bytes");
    680                 }
    681                 int limit = getParams().getIntParameter(HttpMethodParams.BUFFER_WARN_TRIGGER_LIMIT, 1024*1024);
    682                 if ((contentLength == -1) || (contentLength > limit)) {
    683                     LOG.warn("Going to buffer response body of large or unknown size. "
    684                             +"Using getResponseBodyAsStream instead is recommended.");
    685                 }
    686                 LOG.debug("Buffering response body");
    687                 ByteArrayOutputStream outstream = new ByteArrayOutputStream(
    688                         contentLength > 0 ? (int) contentLength : DEFAULT_INITIAL_BUFFER_SIZE);
    689                 byte[] buffer = new byte[4096];
    690                 int len;
    691                 while ((len = instream.read(buffer)) > 0) {
    692                     outstream.write(buffer, 0, len);
    693                 }
    694                 outstream.close();
    695                 setResponseStream(null);
    696                 this.responseBody = outstream.toByteArray();
    697             }
    698         }
    699         return this.responseBody;
    700     }
    701
    702     /***
    703      * Returns the response body of the HTTP method, if any, as an {@link InputStream}.
    704      * If response body is not available, returns <tt>null</tt>
    705      *
    706      * @return The response body
    707      *
    708      * @throws IOException If an I/O (transport) problem occurs while obtaining the
    709      * response body.
    710      */
    711     public InputStream getResponseBodyAsStream() throws IOException {
    712         if (responseStream != null) {
    713             return responseStream;
    714         }
    715         if (responseBody != null) {
    716             InputStream byteResponseStream = new ByteArrayInputStream(responseBody);
    717             LOG.debug("re-creating response stream from byte array");
    718             return byteResponseStream;
    719         }
    720         return null;
    721     }
    722
    723     /***
    724      * Returns the response body of the HTTP method, if any, as a {@link String}.
    725      * If response body is not available or cannot be read, returns <tt>null</tt>
    726      * The string conversion on the data is done using the character encoding specified
    727      * in <tt>Content-Type</tt> header.
    728      *
    729      * Note: This will cause the entire response body to be buffered in memory. A
    730      * malicious server may easily exhaust all the VM memory. It is strongly
    731      * recommended, to use getResponseAsStream if the content length of the response
    732      * is unknown or resonably large.
    733      *
    734      * @return The response body.
    735      *
    736      * @throws IOException If an I/O (transport) problem occurs while obtaining the
    737      * response body.
    738      */
    739     public String getResponseBodyAsString() throws IOException {
    740         byte[] rawdata = null;
    741         if (responseAvailable()) {
    742             rawdata = getResponseBody();
    743         }
    744         if (rawdata != null) {
    745             return EncodingUtil.getString(rawdata, getResponseCharSet());
    746         } else {
    747             return null;
    748         }
    749     }
    750
    751     /***
    752      * Returns an array of the response footers that the HTTP method currently has
    753      * in the order in which they were read.
    754      *
    755      * @return an array of footers
    756      */
    757     public Header[] getResponseFooters() {
    758         return getResponseTrailerHeaderGroup().getAllHeaders();
    759     }
    760
    761     /***
    762      * Gets the response footer associated with the given name.
    763      * Footer name matching is case insensitive.
    764      * <tt>null</tt> will be returned if either <i>footerName</i> is
    765      * <tt>null</tt> or there is no matching footer for <i>footerName</i>
    766      * or there are no footers available.  If there are multiple footers
    767      * with the same name, there values will be combined with the ',' separator
    768      * as specified by RFC2616.
    769      *
    770      * @param footerName the footer name to match
    771      * @return the matching footer
    772      */
    773     public Header getResponseFooter(String footerName) {
    774         if (footerName == null) {
    775             return null;
    776         } else {
    777             return getResponseTrailerHeaderGroup().getCondensedHeader(footerName);
    778         }
    779     }
    780
    781     /***
    782      * Sets the response stream.
    783      * @param responseStream The new response stream.
    784      */
    785     protected void setResponseStream(InputStream responseStream) {
    786         this.responseStream = responseStream;
    787     }
    788
    789     /***
    790      * Returns a stream from which the body of the current response may be read.
    791      * If the method has not yet been executed, if <code>responseBodyConsumed</code>
    792      * has been called, or if the stream returned by a previous call has been closed,
    793      * <code>null</code> will be returned.
    794      *
    795      * @return the current response stream
    796      */
    797     protected InputStream getResponseStream() {
    798         return responseStream;
    799     }
    800
    801     /***
    802      * Returns the status text (or "reason phrase") associated with the latest
    803      * response.
    804      *
    805      * @return The status text.
    806      */
    807     public String getStatusText() {
    808         return statusLine.getReasonPhrase();
    809     }
    810
    811     /***
    812      * Defines how strictly HttpClient follows the HTTP protocol specification
    813      * (RFC 2616 and other relevant RFCs). In the strict mode HttpClient precisely
    814      * implements the requirements of the specification, whereas in non-strict mode
    815      * it attempts to mimic the exact behaviour of commonly used HTTP agents,
    816      * which many HTTP servers expect.
    817      *
    818      * @param strictMode <tt>true</tt> for strict mode, <tt>false</tt> otherwise
    819      *
    820      * @deprecated Use {@link org.apache.commons.httpclient.params.HttpParams#setParameter(String, Object)}
    821      * to exercise a more granular control over HTTP protocol strictness.
    822      */
    823     public void setStrictMode(boolean strictMode) {
    824         if (strictMode) {
    825             this.params.makeStrict();
    826         } else {
    827             this.params.makeLenient();
    828         }
    829     }
    830
    831     /***
    832      * @deprecated Use {@link org.apache.commons.httpclient.params.HttpParams#setParameter(String, Object)}
    833      * to exercise a more granular control over HTTP protocol strictness.
    834      *
    835      * @return <tt>false</tt>
    836      */
    837     public boolean isStrictMode() {
    838         return false;
    839     }
    840
    841     /***
    842      * Adds the specified request header, NOT overwriting any previous value.
    843      * Note that header-name matching is case insensitive.
    844      *
    845      * @param headerName the header's name
    846      * @param headerValue the header's value
    847      */
    848     public void addRequestHeader(String headerName, String headerValue) {
    849         addRequestHeader(new Header(headerName, headerValue));
    850     }
    851
    852     /***
    853      * Tests if the connection should be force-closed when no longer needed.
    854      *
    855      * @return <code>true</code> if the connection must be closed
    856      */
    857     protected boolean isConnectionCloseForced() {
    858         return this.connectionCloseForced;
    859     }
    860
    861     /***
    862      * Sets whether or not the connection should be force-closed when no longer
    863      * needed. This value should only be set to <code>true</code> in abnormal
    864      * circumstances, such as HTTP protocol violations.
    865      *
    866      * @param b <code>true</code> if the connection must be closed, <code>false</code>
    867      * otherwise.
    868      */
    869     protected void setConnectionCloseForced(boolean b) {
    870         if (LOG.isDebugEnabled()) {
    871             LOG.debug("Force-close connection: " + b);
    872         }
    873         this.connectionCloseForced = b;
    874     }
    875
    876     /***
    877      * Tests if the connection should be closed after the method has been executed.
    878      * The connection will be left open when using HTTP/1.1 or if <tt>Connection:
    879      * keep-alive</tt> header was sent.
    880      *
    881      * @param conn the connection in question
    882      *
    883      * @return boolean true if we should close the connection.
    884      */
    885     protected boolean shouldCloseConnection(HttpConnection conn) {
    886         // Connection must be closed due to an abnormal circumstance
    887         if (isConnectionCloseForced()) {
    888             LOG.debug("Should force-close connection.");
    889             return true;
    890         }
    891
    892         Header connectionHeader = null;
    893         // In case being connected via a proxy server
    894         if (!conn.isTransparent()) {
    895             // Check for 'proxy-connection' directive
    896             connectionHeader = responseHeaders.getFirstHeader("proxy-connection");
    897         }
    898         // In all cases Check for 'connection' directive
    899         // some non-complaint proxy servers send it instread of
    900         // expected 'proxy-connection' directive
    901         if (connectionHeader == null) {
    902             connectionHeader = responseHeaders.getFirstHeader("connection");
    903         }
    904         // In case the response does not contain any explict connection
    905         // directives, check whether the request does
    906         if (connectionHeader == null) {
    907             connectionHeader = requestHeaders.getFirstHeader("connection");
    908         }
    909         if (connectionHeader != null) {
    910             if (connectionHeader.getValue().equalsIgnoreCase("close")) {
    911                 if (LOG.isDebugEnabled()) {
    912                     LOG.debug("Should close connection in response to directive: "
    913                         + connectionHeader.getValue());
    914                 }
    915                 return true;
    916             } else if (connectionHeader.getValue().equalsIgnoreCase("keep-alive")) {
    917                 if (LOG.isDebugEnabled()) {
    918                     LOG.debug("Should NOT close connection in response to directive: "
    919                         + connectionHeader.getValue());
    920                 }
    921                 return false;
    922             } else {
    923                 if (LOG.isDebugEnabled()) {
    924                     LOG.debug("Unknown directive: " + connectionHeader.toExternalForm());
    925                 }
    926             }
    927         }
    928         LOG.debug("Resorting to protocol version default close connection policy");
    929         // missing or invalid connection header, do the default
    930         if (this.effectiveVersion.greaterEquals(HttpVersion.HTTP_1_1)) {
    931             if (LOG.isDebugEnabled()) {
    932                 LOG.debug("Should NOT close connection, using " + this.effectiveVersion.toString());
    933             }
    934         } else {
    935             if (LOG.isDebugEnabled()) {
    936                 LOG.debug("Should close connection, using " + this.effectiveVersion.toString());
    937             }
    938         }
    939         return this.effectiveVersion.lessEquals(HttpVersion.HTTP_1_0);
    940     }
    941
    942     /***
    943      * Tests if the this method is ready to be executed.
    944      *
    945      * @param state the {@link HttpState state} information associated with this method
    946      * @param conn the {@link HttpConnection connection} to be used
    947      * @throws HttpException If the method is in invalid state.
    948      */
    949     private void checkExecuteConditions(HttpState state, HttpConnection conn)
    950     throws HttpException {
    951
    952         if (state == null) {
    953             throw new IllegalArgumentException("HttpState parameter may not be null");
    954         }
    955         if (conn == null) {
    956             throw new IllegalArgumentException("HttpConnection parameter may not be null");
    957         }
    958         if (this.aborted) {
    959             throw new IllegalStateException("Method has been aborted");
    960         }
    961         if (!validate()) {
    962             throw new ProtocolException("HttpMethodBase object not valid");
    963         }
    964     }
    965
    966     /***
    967      * Executes this method using the specified <code>HttpConnection</code> and
    968      * <code>HttpState</code>.
    969      *
    970      * @param state {@link HttpState state} information to associate with this
    971      *        request. Must be non-null.
    972      * @param conn the {@link HttpConnection connection} to used to execute
    973      *        this HTTP method. Must be non-null.
    974      *
    975      * @return the integer status code if one was obtained, or <tt>-1</tt>
    976      *
    977      * @throws IOException if an I/O (transport) error occurs
    978      * @throws HttpException  if a protocol exception occurs.
    979      */
    980     public int execute(HttpState state, HttpConnection conn)
    981         throws HttpException, IOException {
    982
    983         LOG.trace("enter HttpMethodBase.execute(HttpState, HttpConnection)");
    984
    985         // this is our connection now, assign it to a local variable so
    986         // that it can be released later
    987         this.responseConnection = conn;
    988
    989         checkExecuteConditions(state, conn);
    990         this.statusLine = null;
    991         this.connectionCloseForced = false;
    992
    993         conn.setLastResponseInputStream(null);
    994
    995         // determine the effective protocol version
    996         if (this.effectiveVersion == null) {
    997             this.effectiveVersion = this.params.getVersion();
    998         }
    999
    1000         writeRequest(state, conn);
    1001         this.requestSent = true;
    1002         readResponse(state, conn);
    1003         // the method has successfully executed
    1004         used = true;
    1005
    1006         return statusLine.getStatusCode();
    1007     }
    1008
    1009     /***
    1010      * Aborts the execution of this method.
    1011      *
    1012      * @since 3.0
    1013      */
    1014     public void abort() {
    1015         if (this.aborted) {
    1016             return;
    1017         }
    1018         this.aborted = true;
    1019         HttpConnection conn = this.responseConnection;
    1020         if (conn != null) {
    1021             conn.close();
    1022         }
    1023     }
    1024
    1025     /***
    1026      * Returns <tt>true</tt> if the HTTP method has been already {@link #execute executed},
    1027      * but not {@link #recycle recycled}.
    1028      *
    1029      * @return <tt>true</tt> if the method has been executed, <tt>false</tt> otherwise
    1030      */
    1031     public boolean hasBeenUsed() {
    1032         return used;
    1033     }
    1034
    1035     /***
    1036      * Recycles the HTTP method so that it can be used again.
    1037      * Note that all of the instance variables will be reset
    1038      * once this method has been called. This method will also
    1039      * release the connection being used by this HTTP method.
    1040      *
    1041      * @see #releaseConnection()
    1042      *
    1043      * @deprecated no longer supported and will be removed in the future
    1044      *             version of HttpClient
    1045      */
    1046     public void recycle() {
    1047         LOG.trace("enter HttpMethodBase.recycle()");
    1048
    1049         releaseConnection();
    1050
    1051         path = null;
    1052         followRedirects = false;
    1053         doAuthentication = true;
    1054         queryString = null;
    1055         getRequestHeaderGroup().clear();
    1056         getResponseHeaderGroup().clear();
    1057         getResponseTrailerHeaderGroup().clear();
    1058         statusLine = null;
    1059         effectiveVersion = null;
    1060         aborted = false;
    1061         used = false;
    1062         params = new HttpMethodParams();
    1063         responseBody = null;
    1064         recoverableExceptionCount = 0;
    1065         connectionCloseForced = false;
    1066         hostAuthState.invalidate();
    1067         proxyAuthState.invalidate();
    1068         cookiespec = null;
    1069         requestSent = false;
    1070     }
    1071
    1072     /***
    1073      * Releases the connection being used by this HTTP method. In particular the
    1074      * connection is used to read the response(if there is one) and will be held
    1075      * until the response has been read. If the connection can be reused by other
    1076      * HTTP methods it is NOT closed at this point.
    1077      *
    1078      * @since 2.0
    1079      */
    1080     public void releaseConnection() {
    1081         try {
    1082             if (this.responseStream != null) {
    1083                 try {
    1084                     // FYI - this may indirectly invoke responseBodyConsumed.
    1085                     this.responseStream.close();
    1086                 } catch (IOException ignore) {
    1087                 }
    1088             }
    1089         } finally {
    1090             ensureConnectionRelease();
    1091         }
    1092     }
    1093
    1094     /***
    1095      * Remove the request header associated with the given name. Note that
    1096      * header-name matching is case insensitive.
    1097      *
    1098      * @param headerName the header name
    1099      */
    1100     public void removeRequestHeader(String headerName) {
    1101
    1102         Header[] headers = getRequestHeaderGroup().getHeaders(headerName);
    1103         for (int i = 0; i < headers.length; i++) {
    1104             getRequestHeaderGroup().removeHeader(headers[i]);
    1105         }
    1106
    1107     }
    1108
    1109     /***
    1110      * Removes the given request header.
    1111      *
    1112      * @param header the header
    1113      */
    1114     public void removeRequestHeader(final Header header) {
    1115         if (header == null) {
    1116             return;
    1117         }
    1118         getRequestHeaderGroup().removeHeader(header);
    1119     }
    1120
    1121     // ---------------------------------------------------------------- Queries
    1122
    1123     /***
    1124      * Returns <tt>true</tt> the method is ready to execute, <tt>false</tt> otherwise.
    1125      *
    1126      * @return This implementation always returns <tt>true</tt>.
    1127      */
    1128     public boolean validate() {
    1129         return true;
    1130     }
    1131
    1132
    1133     /***
    1134      * Returns the actual cookie policy
    1135      *
    1136      * @param state HTTP state. TODO: to be removed in the future
    1137      *
    1138      * @return cookie spec
    1139      */
    1140     private CookieSpec getCookieSpec(final HttpState state) {
    1141         if (this.cookiespec == null) {
    1142             int i = state.getCookiePolicy();
    1143             if (i == -1) {
    1144                 this.cookiespec = CookiePolicy.getCookieSpec(this.params.getCookiePolicy());
    1145             } else {
    1146                 this.cookiespec = CookiePolicy.getSpecByPolicy(i);
    1147             }
    1148             this.cookiespec.setValidDateFormats(
    1149                     (Collection)this.params.getParameter(HttpMethodParams.DATE_PATTERNS));
    1150         }
    1151         return this.cookiespec;
    1152     }
    1153
    1154     /***
    1155      * Generates <tt>Cookie</tt> request headers for those {@link Cookie cookie}s
    1156      * that match the given host, port and path.
    1157      *
    1158      * @param state the {@link HttpState state} information associated with this method
    1159      * @param conn the {@link HttpConnection connection} used to execute
    1160      *        this HTTP method
    1161      *
    1162      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1163      *                     can be recovered from.
    1164      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1165      *                    cannot be recovered from.
    1166      */
    1167     protected void addCookieRequestHeader(HttpState state, HttpConnection conn)
    1168         throws IOException, HttpException {
    1169
    1170         LOG.trace("enter HttpMethodBase.addCookieRequestHeader(HttpState, "
    1171                   + "HttpConnection)");
    1172
    1173         Header[] cookieheaders = getRequestHeaderGroup().getHeaders("Cookie");
    1174         for (int i = 0; i < cookieheaders.length; i++) {
    1175             Header cookieheader = cookieheaders[i];
    1176             if (cookieheader.isAutogenerated()) {
    1177                 getRequestHeaderGroup().removeHeader(cookieheader);
    1178             }
    1179         }
    1180
    1181         CookieSpec matcher = getCookieSpec(state);
    1182         String host = this.params.getVirtualHost();
    1183         if (host == null) {
    1184             host = conn.getHost();
    1185         }
    1186         // BEGIN IA/HERITRIX CHANGES
    1187         Cookie[] cookies = matcher.match(host, conn.getPort(),
    1188             getPath(), conn.isSecure(), state.getCookiesMap());
    1189         // END IA/HERITRIX CHANGES
    1190         if ((cookies != null) && (cookies.length > 0)) {
    1191             if (getParams().isParameterTrue(HttpMethodParams.SINGLE_COOKIE_HEADER)) {
    1192                 // In strict mode put all cookies on the same header
    1193                 String s = matcher.formatCookies(cookies);
    1194                 getRequestHeaderGroup().addHeader(new Header("Cookie", s, true));
    1195             } else {
    1196                 // In non-strict mode put each cookie on a separate header
    1197                 for (int i = 0; i < cookies.length; i++) {
    1198                     String s = matcher.formatCookie(cookies[i]);
    1199                     getRequestHeaderGroup().addHeader(new Header("Cookie", s, true));
    1200                 }
    1201             }
    1202         }
    1203     }
    1204
    1205     /***
    1206      * Generates <tt>Host</tt> request header, as long as no <tt>Host</tt> request
    1207      * header already exists.
    1208      *
    1209      * @param state the {@link HttpState state} information associated with this method
    1210      * @param conn the {@link HttpConnection connection} used to execute
    1211      *        this HTTP method
    1212      *
    1213      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1214      *                     can be recovered from.
    1215      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1216      *                    cannot be recovered from.
    1217      */
    1218     protected void addHostRequestHeader(HttpState state, HttpConnection conn)
    1219     throws IOException, HttpException {
    1220         LOG.trace("enter HttpMethodBase.addHostRequestHeader(HttpState, "
    1221                   + "HttpConnection)");
    1222
    1223         // Per 19.6.1.1 of RFC 2616, it is legal for HTTP/1.0 based
    1224         // applications to send the Host request-header.
    1225         // TODO: Add the ability to disable the sending of this header for
    1226         //       HTTP/1.0 requests.
    1227         String host = this.params.getVirtualHost();
    1228         if (host != null) {
    1229             LOG.debug("Using virtual host name: " + host);
    1230         } else {
    1231             host = conn.getHost();
    1232         }
    1233         int port = conn.getPort();
    1234
    1235         // Note: RFC 2616 uses the term "internet host name" for what goes on the
    1236         // host line.  It would seem to imply that host should be blank if the
    1237         // host is a number instead of an name.  Based on the behavior of web
    1238         // browsers, and the fact that RFC 2616 never defines the phrase "internet
    1239         // host name", and the bad behavior of HttpClient that follows if we
    1240         // send blank, I interpret this as a small misstatement in the RFC, where
    1241         // they meant to say "internet host".  So IP numbers get sent as host
    1242         // entries too. -- Eric Johnson 12/13/2002
    1243         if (LOG.isDebugEnabled()) {
    1244             LOG.debug("Adding Host request header");
    1245         }
    1246
    1247         //appends the port only if not using the default port for the protocol
    1248         if (conn.getProtocol().getDefaultPort() != port) {
    1249             host += (":" + port);
    1250         }
    1251
    1252         setRequestHeader("Host", host);
    1253     }
    1254
    1255     /***
    1256      * Generates <tt>Proxy-Connection: Keep-Alive</tt> request header when
    1257      * communicating via a proxy server.
    1258      *
    1259      * @param state the {@link HttpState state} information associated with this method
    1260      * @param conn the {@link HttpConnection connection} used to execute
    1261      *        this HTTP method
    1262      *
    1263      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1264      *                     can be recovered from.
    1265      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1266      *                    cannot be recovered from.
    1267      */
    1268     protected void addProxyConnectionHeader(HttpState state,
    1269                                             HttpConnection conn)
    1270     throws IOException, HttpException {
    1271         LOG.trace("enter HttpMethodBase.addProxyConnectionHeader("
    1272                   + "HttpState, HttpConnection)");
    1273         if (!conn.isTransparent()) {
    1274             if (getRequestHeader("Proxy-Connection") == null) {
    1275                 addRequestHeader("Proxy-Connection", "Keep-Alive");
    1276             }
    1277         }
    1278     }
    1279
    1280     /***
    1281      * Generates all the required request {@link Header header}s
    1282      * to be submitted via the given {@link HttpConnection connection}.
    1283      *
    1284      * <p>
    1285      * This implementation adds <tt>User-Agent</tt>, <tt>Host</tt>,
    1286      * <tt>Cookie</tt>, <tt>Authorization</tt>, <tt>Proxy-Authorization</tt>
    1287      * and <tt>Proxy-Connection</tt> headers, when appropriate.
    1288      * </p>
    1289      *
    1290      * <p>
    1291      * Subclasses may want to override this method to to add additional
    1292      * headers, and may choose to invoke this implementation (via
    1293      * <tt>super</tt>) to add the "standard" headers.
    1294      * </p>
    1295      *
    1296      * @param state the {@link HttpState state} information associated with this method
    1297      * @param conn the {@link HttpConnection connection} used to execute
    1298      *        this HTTP method
    1299      *
    1300      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1301      *                     can be recovered from.
    1302      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1303      *                    cannot be recovered from.
    1304      *
    1305      * @see #writeRequestHeaders
    1306      */
    1307     protected void addRequestHeaders(HttpState state, HttpConnection conn)
    1308     throws IOException, HttpException {
    1309         LOG.trace("enter HttpMethodBase.addRequestHeaders(HttpState, "
    1310             + "HttpConnection)");
    1311
    1312         addUserAgentRequestHeader(state, conn);
    1313         addHostRequestHeader(state, conn);
    1314         addCookieRequestHeader(state, conn);
    1315         addProxyConnectionHeader(state, conn);
    1316     }
    1317
    1318     /***
    1319      * Generates default <tt>User-Agent</tt> request header, as long as no
    1320      * <tt>User-Agent</tt> request header already exists.
    1321      *
    1322      * @param state the {@link HttpState state} information associated with this method
    1323      * @param conn the {@link HttpConnection connection} used to execute
    1324      *        this HTTP method
    1325      *
    1326      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1327      *                     can be recovered from.
    1328      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1329      *                    cannot be recovered from.
    1330      */
    1331     protected void addUserAgentRequestHeader(HttpState state,
    1332                                              HttpConnection conn)
    1333     throws IOException, HttpException {
    1334         LOG.trace("enter HttpMethodBase.addUserAgentRequestHeaders(HttpState, "
    1335             + "HttpConnection)");
    1336
    1337         if (getRequestHeader("User-Agent") == null) {
    1338             String agent = (String)getParams().getParameter(HttpMethodParams.USER_AGENT);
    1339             if (agent == null) {
    1340                 agent = "Jakarta Commons-HttpClient";
    1341             }
    1342             setRequestHeader("User-Agent", agent);
    1343         }
    1344     }
    1345
    1346     /***
    1347      * Throws an {@link IllegalStateException} if the HTTP method has been already
    1348      * {@link #execute executed}, but not {@link #recycle recycled}.
    1349      *
    1350      * @throws IllegalStateException if the method has been used and not
    1351      *      recycled
    1352      */
    1353     protected void checkNotUsed() throws IllegalStateException {
    1354         if (used) {
    1355             throw new IllegalStateException("Already used.");
    1356         }
    1357     }
    1358
    1359     /***
    1360      * Throws an {@link IllegalStateException} if the HTTP method has not been
    1361      * {@link #execute executed} since last {@link #recycle recycle}.
    1362      *
    1363      *
    1364      * @throws IllegalStateException if not used
    1365      */
    1366     protected void checkUsed()  throws IllegalStateException {
    1367         if (!used) {
    1368             throw new IllegalStateException("Not Used.");
    1369         }
    1370     }
    1371
    1372     // ------------------------------------------------- Static Utility Methods
    1373
    1374     /***
    1375      * Generates HTTP request line according to the specified attributes.
    1376      *
    1377      * @param connection the {@link HttpConnection connection} used to execute
    1378      *        this HTTP method
    1379      * @param name the method name generate a request for
    1380      * @param requestPath the path string for the request
    1381      * @param query the query string for the request
    1382      * @param version the protocol version to use (e.g. HTTP/1.0)
    1383      *
    1384      * @return HTTP request line
    1385      */
    1386     protected static String generateRequestLine(HttpConnection connection,
    1387         String name, String requestPath, String query, String version) {
    1388         LOG.trace("enter HttpMethodBase.generateRequestLine(HttpConnection, "
    1389             + "String, String, String, String)");
    1390
    1391         StringBuffer buf = new StringBuffer();
    1392         // Append method name
    1393         buf.append(name);
    1394         buf.append(" ");
    1395         // Absolute or relative URL?
    1396         if (!connection.isTransparent()) {
    1397             Protocol protocol = connection.getProtocol();
    1398             buf.append(protocol.getScheme().toLowerCase());
    1399             buf.append("://");
    1400             buf.append(connection.getHost());
    1401             if ((connection.getPort() != -1)
    1402                 && (connection.getPort() != protocol.getDefaultPort())
    1403             ) {
    1404                 buf.append(":");
    1405                 buf.append(connection.getPort());
    1406             }
    1407         }
    1408         // Append path, if any
    1409         if (requestPath == null) {
    1410             buf.append("/");
    1411         } else {
    1412             if (!connection.isTransparent() && !requestPath.startsWith("/")) {
    1413                 buf.append("/");
    1414             }
    1415             buf.append(requestPath);
    1416         }
    1417         // Append query, if any
    1418         if (query != null) {
    1419             if (query.indexOf("?") != 0) {
    1420                 buf.append("?");
    1421             }
    1422             buf.append(query);
    1423         }
    1424         // Append protocol
    1425         buf.append(" ");
    1426         buf.append(version);
    1427         buf.append("\r\n");
    1428
    1429         return buf.toString();
    1430     }
    1431
    1432     /***
    1433      * This method is invoked immediately after
    1434      * {@link #readResponseBody(HttpState,HttpConnection)} and can be overridden by
    1435      * sub-classes in order to provide custom body processing.
    1436      *
    1437      * <p>
    1438      * This implementation does nothing.
    1439      * </p>
    1440      *
    1441      * @param state the {@link HttpState state} information associated with this method
    1442      * @param conn the {@link HttpConnection connection} used to execute
    1443      *        this HTTP method
    1444      *
    1445      * @see #readResponse
    1446      * @see #readResponseBody
    1447      */
    1448     protected void processResponseBody(HttpState state, HttpConnection conn) {
    1449     }
    1450
    1451     /***
    1452      * This method is invoked immediately after
    1453      * {@link #readResponseHeaders(HttpState,HttpConnection)} and can be overridden by
    1454      * sub-classes in order to provide custom response headers processing.
    1455
    1456      * <p>
    1457      * This implementation will handle the <tt>Set-Cookie</tt> and
    1458      * <tt>Set-Cookie2</tt> headers, if any, adding the relevant cookies to
    1459      * the given {@link HttpState}.
    1460      * </p>
    1461      *
    1462      * @param state the {@link HttpState state} information associated with this method
    1463      * @param conn the {@link HttpConnection connection} used to execute
    1464      *        this HTTP method
    1465      *
    1466      * @see #readResponse
    1467      * @see #readResponseHeaders
    1468      */
    1469     protected void processResponseHeaders(HttpState state,
    1470         HttpConnection conn) {
    1471         LOG.trace("enter HttpMethodBase.processResponseHeaders(HttpState, "
    1472             + "HttpConnection)");
    1473
    1474         Header[] headers = getResponseHeaderGroup().getHeaders("set-cookie2");
    1475         //Only process old style set-cookie headers if new style headres
    1476         //are not present
    1477         if (headers.length == 0) {
    1478             headers = getResponseHeaderGroup().getHeaders("set-cookie");
    1479         }
    1480
    1481         CookieSpec parser = getCookieSpec(state);
    1482         String host = this.params.getVirtualHost();
    1483         if (host == null) {
    1484             host = conn.getHost();
    1485         }
    1486         for (int i = 0; i < headers.length; i++) {
    1487             Header header = headers[i];
    1488             Cookie[] cookies = null;
    1489             try {
    1490                 cookies = parser.parse(
    1491                   host,
    1492                   conn.getPort(),
    1493                   getPath(),
    1494                   conn.isSecure(),
    1495                   header);
    1496             } catch (MalformedCookieException e) {
    1497                 if (LOG.isWarnEnabled()) {
    1498                     LOG.warn("Invalid cookie header: \""
    1499                         + header.getValue()
    1500                         + "\". " + e.getMessage());
    1501                 }
    1502             }
    1503             if (cookies != null) {
    1504                 for (int j = 0; j < cookies.length; j++) {
    1505                     Cookie cookie = cookies[j];
    1506                     try {
    1507                         parser.validate(
    1508                           host,
    1509                           conn.getPort(),
    1510                           getPath(),
    1511                           conn.isSecure(),
    1512                           cookie);
    1513                         state.addCookie(cookie);
    1514                         if (LOG.isDebugEnabled()) {
    1515                             LOG.debug("Cookie accepted: \""
    1516                                 + parser.formatCookie(cookie) + "\"");
    1517                         }
    1518                     } catch (MalformedCookieException e) {
    1519                         if (LOG.isWarnEnabled()) {
    1520                             LOG.warn("Cookie rejected: \"" + parser.formatCookie(cookie)
    1521                                 + "\". " + e.getMessage());
    1522                         }
    1523                     }
    1524                 }
    1525             }
    1526         }
    1527     }
    1528
    1529     /***
    1530      * This method is invoked immediately after
    1531      * {@link #readStatusLine(HttpState,HttpConnection)} and can be overridden by
    1532      * sub-classes in order to provide custom response status line processing.
    1533      *
    1534      * @param state the {@link HttpState state} information associated with this method
    1535      * @param conn the {@link HttpConnection connection} used to execute
    1536      *        this HTTP method
    1537      *
    1538      * @see #readResponse
    1539      * @see #readStatusLine
    1540      */
    1541     protected void processStatusLine(HttpState state, HttpConnection conn) {
    1542     }
    1543
    1544     /***
    1545      * Reads the response from the given {@link HttpConnection connection}.
    1546      *
    1547      * <p>
    1548      * The response is processed as the following sequence of actions:
    1549      *
    1550      * <ol>
    1551      * <li>
    1552      * {@link #readStatusLine(HttpState,HttpConnection)} is
    1553      * invoked to read the request line.
    1554      * </li>
    1555      * <li>
    1556      * {@link #processStatusLine(HttpState,HttpConnection)}
    1557      * is invoked, allowing the method to process the status line if
    1558      * desired.
    1559      * </li>
    1560      * <li>
    1561      * {@link #readResponseHeaders(HttpState,HttpConnection)} is invoked to read
    1562      * the associated headers.
    1563      * </li>
    1564      * <li>
    1565      * {@link #processResponseHeaders(HttpState,HttpConnection)} is invoked, allowing
    1566      * the method to process the headers if desired.
    1567      * </li>
    1568      * <li>
    1569      * {@link #readResponseBody(HttpState,HttpConnection)} is
    1570      * invoked to read the associated body (if any).
    1571      * </li>
    1572      * <li>
    1573      * {@link #processResponseBody(HttpState,HttpConnection)} is invoked, allowing the
    1574      * method to process the response body if desired.
    1575      * </li>
    1576      * </ol>
    1577      *
    1578      * Subclasses may want to override one or more of the above methods to to
    1579      * customize the processing. (Or they may choose to override this method
    1580      * if dramatically different processing is required.)
    1581      * </p>
    1582      *
    1583      * @param state the {@link HttpState state} information associated with this method
    1584      * @param conn the {@link HttpConnection connection} used to execute
    1585      *        this HTTP method
    1586      *
    1587      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1588      *                     can be recovered from.
    1589      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1590      *                    cannot be recovered from.
    1591      */
    1592     protected void readResponse(HttpState state, HttpConnection conn)
    1593     throws IOException, HttpException {
    1594         LOG.trace(
    1595         "enter HttpMethodBase.readResponse(HttpState, HttpConnection)");
    1596         // Status line & line may have already been received
    1597         // if 'expect - continue' handshake has been used
    1598         while (this.statusLine == null) {
    1599             readStatusLine(state, conn);
    1600             processStatusLine(state, conn);
    1601             readResponseHeaders(state, conn);
    1602             processResponseHeaders(state, conn);
    1603
    1604             int status = this.statusLine.getStatusCode();
    1605             if ((status >= 100) && (status < 200)) {
    1606                 if (LOG.isInfoEnabled()) {
    1607                     LOG.info("Discarding unexpected response: " + this.statusLine.toString());
    1608                 }
    1609                 this.statusLine = null;
    1610             }
    1611         }
    1612         readResponseBody(state, conn);
    1613         processResponseBody(state, conn);
    1614     }
    1615
    1616     /***
    1617      * Read the response body from the given {@link HttpConnection}.
    1618      *
    1619      * <p>
    1620      * The current implementation wraps the socket level stream with
    1621      * an appropriate stream for the type of response (chunked, content-length,
    1622      * or auto-close).  If there is no response body, the connection associated
    1623      * with the request will be returned to the connection manager.
    1624      * </p>
    1625      *
    1626      * <p>
    1627      * Subclasses may want to override this method to to customize the
    1628      * processing.
    1629      * </p>
    1630      *
    1631      * @param state the {@link HttpState state} information associated with this method
    1632      * @param conn the {@link HttpConnection connection} used to execute
    1633      *        this HTTP method
    1634      *
    1635      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1636      *                     can be recovered from.
    1637      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1638      *                    cannot be recovered from.
    1639      *
    1640      * @see #readResponse
    1641      * @see #processResponseBody
    1642      */
    1643     protected void readResponseBody(HttpState state, HttpConnection conn)
    1644     throws IOException, HttpException {
    1645         LOG.trace(
    1646             "enter HttpMethodBase.readResponseBody(HttpState, HttpConnection)");
    1647
    1648         // assume we are not done with the connection if we get a stream
    1649         InputStream stream = readResponseBody(conn);
    1650         if (stream == null) {
    1651             // done using the connection!
    1652             responseBodyConsumed();
    1653         } else {
    1654             conn.setLastResponseInputStream(stream);
    1655             setResponseStream(stream);
    1656         }
    1657     }
    1658
    1659     /***
    1660      * Returns the response body as an {@link InputStream input stream}
    1661      * corresponding to the values of the <tt>Content-Length</tt> and
    1662      * <tt>Transfer-Encoding</tt> headers. If no response body is available
    1663      * returns <tt>null</tt>.
    1664      * <p>
    1665      *
    1666      * @see #readResponse
    1667      * @see #processResponseBody
    1668      *
    1669      * @param conn the {@link HttpConnection connection} used to execute
    1670      *        this HTTP method
    1671      *
    1672      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1673      *                     can be recovered from.
    1674      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1675      *                    cannot be recovered from.
    1676      */
    1677     private InputStream readResponseBody(HttpConnection conn)
    1678         throws HttpException, IOException {
    1679
    1680         LOG.trace("enter HttpMethodBase.readResponseBody(HttpConnection)");
    1681
    1682         responseBody = null;
    1683         InputStream is = conn.getResponseInputStream();
    1684         if (Wire.CONTENT_WIRE.enabled()) {
    1685             is = new WireLogInputStream(is, Wire.CONTENT_WIRE);
    1686         }
    1687         boolean canHaveBody = canResponseHaveBody(statusLine.getStatusCode());
    1688         InputStream result = null;
    1689         Header transferEncodingHeader = responseHeaders.getFirstHeader("Transfer-Encoding");
    1690         // We use Transfer-Encoding if present and ignore Content-Length.
    1691         // RFC2616, 4.4 item number 3
    1692         if (transferEncodingHeader != null) {
    1693
    1694             String transferEncoding = transferEncodingHeader.getValue();
    1695             if (!"chunked".equalsIgnoreCase(transferEncoding)
    1696                 && !"identity".equalsIgnoreCase(transferEncoding)) {
    1697                 if (LOG.isWarnEnabled()) {
    1698                     LOG.warn("Unsupported transfer encoding: " + transferEncoding);
    1699                 }
    1700             }
    1701             HeaderElement[] encodings = transferEncodingHeader.getElements();
    1702             // The chunked encoding must be the last one applied
    1703             // RFC2616, 14.41
    1704             int len = encodings.length;
    1705             if ((len > 0) && ("chunked".equalsIgnoreCase(encodings[len - 1].getName()))) {
    1706                 // if response body is empty
    1707                 if (conn.isResponseAvailable(conn.getParams().getSoTimeout())) {
    1708                     result = new ChunkedInputStream(is, this);
    1709                 } else {
    1710                     if (getParams().isParameterTrue(HttpMethodParams.STRICT_TRANSFER_ENCODING)) {
    1711                         throw new ProtocolException("Chunk-encoded body declared but not sent");
    1712                     } else {
    1713                         LOG.warn("Chunk-encoded body missing");
    1714                     }
    1715                 }
    1716             } else {
    1717                 LOG.info("Response content is not chunk-encoded");
    1718                 // The connection must be terminated by closing
    1719                 // the socket as per RFC 2616, 3.6
    1720                 setConnectionCloseForced(true);
    1721                 result = is;
    1722             }
    1723         } else {
    1724             long expectedLength = getResponseContentLength();
    1725             if (expectedLength == -1) {
    1726                 if (canHaveBody && this.effectiveVersion.greaterEquals(HttpVersion.HTTP_1_1)) {
    1727                     Header connectionHeader = responseHeaders.getFirstHeader("Connection");
    1728                     String connectionDirective = null;
    1729                     if (connectionHeader != null) {
    1730                         connectionDirective = connectionHeader.getValue();
    1731                     }
    1732                     if (!"close".equalsIgnoreCase(connectionDirective)) {
    1733                         LOG.info("Response content length is not known");
    1734                         setConnectionCloseForced(true);
    1735                     }
    1736                 }
    1737                 result = is;
    1738             } else {
    1739                 result = new ContentLengthInputStream(is, expectedLength);
    1740             }
    1741         }
    1742
    1743         // See if the response is supposed to have a response body
    1744         if (!canHaveBody) {
    1745             result = null;
    1746         }
    1747         // if there is a result - ALWAYS wrap it in an observer which will
    1748         // close the underlying stream as soon as it is consumed, and notify
    1749         // the watcher that the stream has been consumed.
    1750         if (result != null) {
    1751
    1752             result = new AutoCloseInputStream(
    1753                 result,
    1754                 new ResponseConsumedWatcher() {
    1755                     public void responseConsumed() {
    1756                         responseBodyConsumed();
    1757                     }
    1758                 }
    1759             );
    1760         }
    1761
    1762         return result;
    1763     }
    1764
    1765     /***
    1766      * Reads the response headers from the given {@link HttpConnection connection}.
    1767      *
    1768      * <p>
    1769      * Subclasses may want to override this method to to customize the
    1770      * processing.
    1771      * </p>
    1772      *
    1773      * <p>
    1774      * "It must be possible to combine the multiple header fields into one
    1775      * "field-name: field-value" pair, without changing the semantics of the
    1776      * message, by appending each subsequent field-value to the first, each
    1777      * separated by a comma." - HTTP/1.0 (4.3)
    1778      * </p>
    1779      *
    1780      * @param state the {@link HttpState state} information associated with this method
    1781      * @param conn the {@link HttpConnection connection} used to execute
    1782      *        this HTTP method
    1783      *
    1784      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1785      *                     can be recovered from.
    1786      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1787      *                    cannot be recovered from.
    1788      *
    1789      * @see #readResponse
    1790      * @see #processResponseHeaders
    1791      */
    1792     protected void readResponseHeaders(HttpState state, HttpConnection conn)
    1793     throws IOException, HttpException {
    1794         LOG.trace("enter HttpMethodBase.readResponseHeaders(HttpState,"
    1795             + "HttpConnection)");
    1796
    1797         getResponseHeaderGroup().clear();
    1798
    1799         Header[] headers = HttpParser.parseHeaders(
    1800             conn.getResponseInputStream(), getParams().getHttpElementCharset());
    1801         if (Wire.HEADER_WIRE.enabled()) {
    1802             for (int i = 0; i < headers.length; i++) {
    1803                 Wire.HEADER_WIRE.input(headers[i].toExternalForm());
    1804             }
    1805         }
    1806         getResponseHeaderGroup().setHeaders(headers);
    1807     }
    1808
    1809     /***
    1810      * Read the status line from the given {@link HttpConnection}, setting my
    1811      * {@link #getStatusCode status code} and {@link #getStatusText status
    1812      * text}.
    1813      *
    1814      * <p>
    1815      * Subclasses may want to override this method to to customize the
    1816      * processing.
    1817      * </p>
    1818      *
    1819      * @param state the {@link HttpState state} information associated with this method
    1820      * @param conn the {@link HttpConnection connection} used to execute
    1821      *        this HTTP method
    1822      *
    1823      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1824      *                     can be recovered from.
    1825      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1826      *                    cannot be recovered from.
    1827      *
    1828      * @see StatusLine
    1829      */
    1830     protected void readStatusLine(HttpState state, HttpConnection conn)
    1831     throws IOException, HttpException {
    1832         LOG.trace("enter HttpMethodBase.readStatusLine(HttpState, HttpConnection)");
    1833
    1834         final int maxGarbageLines = getParams().
    1835             getIntParameter(HttpMethodParams.STATUS_LINE_GARBAGE_LIMIT, Integer.MAX_VALUE);
    1836
    1837         //read out the HTTP status string
    1838         int count = 0;
    1839         String s;
    1840         do {
    1841             s = conn.readLine(getParams().getHttpElementCharset());
    1842             if (s == null && count == 0) {
    1843                 // The server just dropped connection on us
    1844                 throw new NoHttpResponseException("The server " + conn.getHost() +
    1845                     " failed to respond");
    1846             }
    1847             if (Wire.HEADER_WIRE.enabled()) {
    1848                 Wire.HEADER_WIRE.input(s + "\r\n");
    1849             }
    1850             if (s != null && StatusLine.startsWithHTTP(s)) {
    1851                 // Got one
    1852                 break;
    1853             } else if (s == null || count >= maxGarbageLines) {
    1854                 // Giving up
    1855                 throw new ProtocolException("The server " + conn.getHost() +
    1856                         " failed to respond with a valid HTTP response");
    1857             }
    1858             count++;
    1859         } while(true);
    1860
    1861         //create the status line from the status string
    1862         statusLine = new StatusLine(s);
    1863
    1864         //check for a valid HTTP-Version
    1865         String versionStr = statusLine.getHttpVersion();
    1866         if (getParams().isParameterFalse(HttpMethodParams.UNAMBIGUOUS_STATUS_LINE)
    1867            && versionStr.equals("HTTP")) {
    1868             getParams().setVersion(HttpVersion.HTTP_1_0);
    1869             if (LOG.isWarnEnabled()) {
    1870                 LOG.warn("Ambiguous status line (HTTP protocol version missing):" +
    1871                 statusLine.toString());
    1872             }
    1873         } else {
    1874             this.effectiveVersion = HttpVersion.parse(versionStr);
    1875         }
    1876
    1877     }
    1878
    1879     // ------------------------------------------------------ Protected Methods
    1880
    1881     /***
    1882      * <p>
    1883      * Sends the request via the given {@link HttpConnection connection}.
    1884      * </p>
    1885      *
    1886      * <p>
    1887      * The request is written as the following sequence of actions:
    1888      * </p>
    1889      *
    1890      * <ol>
    1891      * <li>
    1892      * {@link #writeRequestLine(HttpState, HttpConnection)} is invoked to
    1893      * write the request line.
    1894      * </li>
    1895      * <li>
    1896      * {@link #writeRequestHeaders(HttpState, HttpConnection)} is invoked
    1897      * to write the associated headers.
    1898      * </li>
    1899      * <li>
    1900      * <tt>\r\n</tt> is sent to close the head part of the request.
    1901      * </li>
    1902      * <li>
    1903      * {@link #writeRequestBody(HttpState, HttpConnection)} is invoked to
    1904      * write the body part of the request.
    1905      * </li>
    1906      * </ol>
    1907      *
    1908      * <p>
    1909      * Subclasses may want to override one or more of the above methods to to
    1910      * customize the processing. (Or they may choose to override this method
    1911      * if dramatically different processing is required.)
    1912      * </p>
    1913      *
    1914      * @param state the {@link HttpState state} information associated with this method
    1915      * @param conn the {@link HttpConnection connection} used to execute
    1916      *        this HTTP method
    1917      *
    1918      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    1919      *                     can be recovered from.
    1920      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    1921      *                    cannot be recovered from.
    1922      */
    1923     protected void writeRequest(HttpState state, HttpConnection conn)
    1924     throws IOException, HttpException {
    1925         LOG.trace(
    1926             "enter HttpMethodBase.writeRequest(HttpState, HttpConnection)");
    1927         writeRequestLine(state, conn);
    1928         writeRequestHeaders(state, conn);
    1929         conn.writeLine(); // close head
    1930         if (Wire.HEADER_WIRE.enabled()) {
    1931             Wire.HEADER_WIRE.output("\r\n");
    1932         }
    1933
    1934         HttpVersion ver = getParams().getVersion();
    1935         Header expectheader = getRequestHeader("Expect");
    1936         String expectvalue = null;
    1937         if (expectheader != null) {
    1938             expectvalue = expectheader.getValue();
    1939         }
    1940         if ((expectvalue != null)
    1941          && (expectvalue.compareToIgnoreCase("100-continue") == 0)) {
    1942             if (ver.greaterEquals(HttpVersion.HTTP_1_1)) {
    1943
    1944                 // make sure the status line and headers have been sent
    1945                 conn.flushRequestOutputStream();
    1946
    1947                 int readTimeout = conn.getParams().getSoTimeout();
    1948                 try {
    1949                     conn.setSocketTimeout(RESPONSE_WAIT_TIME_MS);
    1950                     readStatusLine(state, conn);
    1951                     processStatusLine(state, conn);
    1952                     readResponseHeaders(state, conn);
    1953                     processResponseHeaders(state, conn);
    1954
    1955                     if (this.statusLine.getStatusCode() == HttpStatus.SC_CONTINUE) {
    1956                         // Discard status line
    1957                         this.statusLine = null;
    1958                         LOG.debug("OK to continue received");
    1959                     } else {
    1960                         return;
    1961                     }
    1962                 } catch (InterruptedIOException e) {
    1963                     if (!ExceptionUtil.isSocketTimeoutException(e)) {
    1964                         throw e;
    1965                     }
    1966                     // Most probably Expect header is not recongnized
    1967                     // Remove the header to signal the method
    1968                     // that it's okay to go ahead with sending data
    1969                     removeRequestHeader("Expect");
    1970                     LOG.info("100 (continue) read timeout. Resume sending the request");
    1971                 } finally {
    1972                     conn.setSocketTimeout(readTimeout);
    1973                 }
    1974
    1975             } else {
    1976                 removeRequestHeader("Expect");
    1977                 LOG.info("'Expect: 100-continue' handshake is only supported by "
    1978                     + "HTTP/1.1 or higher");
    1979             }
    1980         }
    1981
    1982         writeRequestBody(state, conn);
    1983         // make sure the entire request body has been sent
    1984         conn.flushRequestOutputStream();
    1985     }
    1986
    1987     /***
    1988      * Writes the request body to the given {@link HttpConnection connection}.
    1989      *
    1990      * <p>
    1991      * This method should return <tt>true</tt> if the request body was actually
    1992      * sent (or is empty), or <tt>false</tt> if it could not be sent for some
    1993      * reason.
    1994      * </p>
    1995      *
    1996      * <p>
    1997      * This implementation writes nothing and returns <tt>true</tt>.
    1998      * </p>
    1999      *
    2000      * @param state the {@link HttpState state} information associated with this method
    2001      * @param conn the {@link HttpConnection connection} used to execute
    2002      *        this HTTP method
    2003      *
    2004      * @return <tt>true</tt>
    2005      *
    2006      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    2007      *                     can be recovered from.
    2008      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    2009      *                    cannot be recovered from.
    2010      */
    2011     protected boolean writeRequestBody(HttpState state, HttpConnection conn)
    2012     throws IOException, HttpException {
    2013         return true;
    2014     }
    2015
    2016     /***
    2017      * Writes the request headers to the given {@link HttpConnection connection}.
    2018      *
    2019      * <p>
    2020      * This implementation invokes {@link #addRequestHeaders(HttpState,HttpConnection)},
    2021      * and then writes each header to the request stream.
    2022      * </p>
    2023      *
    2024      * <p>
    2025      * Subclasses may want to override this method to to customize the
    2026      * processing.
    2027      * </p>
    2028      *
    2029      * @param state the {@link HttpState state} information associated with this method
    2030      * @param conn the {@link HttpConnection connection} used to execute
    2031      *        this HTTP method
    2032      *
    2033      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    2034      *                     can be recovered from.
    2035      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    2036      *                    cannot be recovered from.
    2037      *
    2038      * @see #addRequestHeaders
    2039      * @see #getRequestHeaders
    2040      */
    2041     protected void writeRequestHeaders(HttpState state, HttpConnection conn)
    2042     throws IOException, HttpException {
    2043         LOG.trace("enter HttpMethodBase.writeRequestHeaders(HttpState,"
    2044             + "HttpConnection)");
    2045         addRequestHeaders(state, conn);
    2046
    2047         String charset = getParams().getHttpElementCharset();
    2048
    2049         Header[] headers = getRequestHeaders();
    2050         for (int i = 0; i < headers.length; i++) {
    2051             String s = headers[i].toExternalForm();
    2052             if (Wire.HEADER_WIRE.enabled()) {
    2053                 Wire.HEADER_WIRE.output(s);
    2054             }
    2055             conn.print(s, charset);
    2056         }
    2057     }
    2058
    2059     /***
    2060      * Writes the request line to the given {@link HttpConnection connection}.
    2061      *
    2062      * <p>
    2063      * Subclasses may want to override this method to to customize the
    2064      * processing.
    2065      * </p>
    2066      *
    2067      * @param state the {@link HttpState state} information associated with this method
    2068      * @param conn the {@link HttpConnection connection} used to execute
    2069      *        this HTTP method
    2070      *
    2071      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
    2072      *                     can be recovered from.
    2073      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions
    2074      *                    cannot be recovered from.
    2075      *
    2076      * @see #generateRequestLine
    2077      */
    2078     protected void writeRequestLine(HttpState state, HttpConnection conn)
    2079     throws IOException, HttpException {
    2080         LOG.trace(
    2081             "enter HttpMethodBase.writeRequestLine(HttpState, HttpConnection)");
    2082         String requestLine = getRequestLine(conn);
    2083         if (Wire.HEADER_WIRE.enabled()) {
    2084             Wire.HEADER_WIRE.output(requestLine);
    2085         }
    2086         conn.print(requestLine, getParams().getHttpElementCharset());
    2087     }
    2088
    2089     /***
    2090      * Returns the request line.
    2091      *
    2092      * @param conn the {@link HttpConnection connection} used to execute
    2093      *        this HTTP method
    2094      *
    2095      * @return The request line.
    2096      */
    2097     private String getRequestLine(HttpConnection conn) {
    2098         return  HttpMethodBase.generateRequestLine(conn, getName(),
    2099                 getPath(), getQueryString(), this.effectiveVersion.toString());
    2100     }
    2101
    2102     /***
    2103      * Returns {@link HttpMethodParams HTTP protocol parameters} associated with this method.
    2104      *
    2105      * @return HTTP parameters.
    2106      *
    2107      * @since 3.0
    2108      */
    2109     public HttpMethodParams getParams() {
    2110         return this.params;
    2111     }
    2112
    2113     /***
    2114      * Assigns {@link HttpMethodParams HTTP protocol parameters} for this method.
    2115      *
    2116      * @since 3.0
    2117      *
    2118      * @see HttpMethodParams
    2119      */
    2120     public void setParams(final HttpMethodParams params) {
    2121         if (params == null) {
    2122             throw new IllegalArgumentException("Parameters may not be null");
    2123         }
    2124         this.params = params;
    2125     }
    2126
    2127     /***
    2128      * Returns the HTTP version used with this method (may be <tt>null</tt>
    2129      * if undefined, that is, the method has not been executed)
    2130      *
    2131      * @return HTTP version.
    2132      *
    2133      * @since 3.0
    2134      */
    2135     public HttpVersion getEffectiveVersion() {
    2136         return this.effectiveVersion;
    2137     }
    2138
    2139     /***
    2140      * Per RFC 2616 section 4.3, some response can never contain a message
    2141      * body.
    2142      *
    2143      * @param status - the HTTP status code
    2144      *
    2145      * @return <tt>true</tt> if the message may contain a body, <tt>false</tt> if it can not
    2146      *         contain a message body
    2147      */
    2148     private static boolean canResponseHaveBody(int status) {
    2149         LOG.trace("enter HttpMethodBase.canResponseHaveBody(int)");
    2150
    2151         boolean result = true;
    2152
    2153         if ((status >= 100 && status <= 199) || (status == 204)
    2154             || (status == 304)) { // NOT MODIFIED
    2155             result = false;
    2156         }
    2157
    2158         return result;
    2159     }
    2160
    2161     /***
    2162      * Returns proxy authentication realm, if it has been used during authentication process.
    2163      * Otherwise returns <tt>null</tt>.
    2164      *
    2165      * @return proxy authentication realm
    2166      *
    2167      * @deprecated use #getProxyAuthState()
    2168      */
    2169     public String getProxyAuthenticationRealm() {
    2170         return this.proxyAuthState.getRealm();
    2171     }
    2172
    2173     /***
    2174      * Returns authentication realm, if it has been used during authentication process.
    2175      * Otherwise returns <tt>null</tt>.
    2176      *
    2177      * @return authentication realm
    2178      *
    2179      * @deprecated use #getHostAuthState()
    2180      */
    2181     public String getAuthenticationRealm() {
    2182         return this.hostAuthState.getRealm();
    2183     }
    2184
    2185     /***
    2186      * Returns the character set from the <tt>Content-Type</tt> header.
    2187      *
    2188      * @param contentheader The content header.
    2189      * @return String The character set.
    2190      */
    2191     protected String getContentCharSet(Header contentheader) {
    2192         LOG.trace("enter getContentCharSet( Header contentheader )");
    2193         String charset = null;
    2194         if (contentheader != null) {
    2195             HeaderElement values[] = contentheader.getElements();
    2196             // I expect only one header element to be there
    2197             // No more. no less
    2198             if (values.length == 1) {
    2199                 NameValuePair param = values[0].getParameterByName("charset");
    2200                 if (param != null) {
    2201                     // If I get anything "funny"
    2202                     // UnsupportedEncondingException will result
    2203                     charset = param.getValue();
    2204                 }
    2205             }
    2206         }
    2207         if (charset == null) {
    2208             charset = getParams().getContentCharset();
    2209             if (LOG.isDebugEnabled()) {
    2210                 LOG.debug("Default charset used: " + charset);
    2211             }
    2212         }
    2213         return charset;
    2214     }
    2215
    2216
    2217     /***
    2218      * Returns the character encoding of the request from the <tt>Content-Type</tt> header.
    2219      *
    2220      * @return String The character set.
    2221      */
    2222     public String getRequestCharSet() {
    2223         return getContentCharSet(getRequestHeader("Content-Type"));
    2224     }
    2225
    2226
    2227     /***
    2228      * Returns the character encoding of the response from the <tt>Content-Type</tt> header.
    2229      *
    2230      * @return String The character set.
    2231      */
    2232     public String getResponseCharSet() {
    2233         return getContentCharSet(getResponseHeader("Content-Type"));
    2234     }
    2235
    2236     /***
    2237      * @deprecated no longer used
    2238      *
    2239      * Returns the number of "recoverable" exceptions thrown and handled, to
    2240      * allow for monitoring the quality of the connection.
    2241      *
    2242      * @return The number of recoverable exceptions handled by the method.
    2243      */
    2244     public int getRecoverableExceptionCount() {
    2245         return recoverableExceptionCount;
    2246     }
    2247
    2248     /***
    2249      * A response has been consumed.
    2250      *
    2251      * <p>The default behavior for this class is to check to see if the connection
    2252      * should be closed, and close if need be, and to ensure that the connection
    2253      * is returned to the connection manager - if and only if we are not still
    2254      * inside the execute call.</p>
    2255      *
    2256      */
    2257     protected void responseBodyConsumed() {
    2258
    2259         // make sure this is the initial invocation of the notification,
    2260         // ignore subsequent ones.
    2261         responseStream = null;
    2262         if (responseConnection != null) {
    2263             responseConnection.setLastResponseInputStream(null);
    2264
    2265             // At this point, no response data should be available.
    2266             // If there is data available, regard the connection as being
    2267             // unreliable and close it.
    2268
    2269             if (shouldCloseConnection(responseConnection)) {
    2270                 responseConnection.close();
    2271             } else {
    2272                 try {
    2273                     if(responseConnection.isResponseAvailable()) {
    2274                         boolean logExtraInput =
    2275                             getParams().isParameterTrue(HttpMethodParams.WARN_EXTRA_INPUT);
    2276
    2277                         if(logExtraInput) {
    2278                             LOG.warn("Extra response data detected - closing connection");
    2279                         }
    2280                         responseConnection.close();
    2281                     }
    2282                 }
    2283                 catch (IOException e) {
    2284                     LOG.warn(e.getMessage());
    2285                     responseConnection.close();
    2286                 }
    2287             }
    2288         }
    2289         this.connectionCloseForced = false;
    2290         ensureConnectionRelease();
    2291     }
    2292
    2293     /***
    2294      * Insure that the connection is released back to the pool.
    2295      */
    2296     private void ensureConnectionRelease() {
    2297         if (responseConnection != null) {
    2298             responseConnection.releaseConnection();
    2299             responseConnection = null;
    2300         }
    2301     }
    2302
    2303     /***
    2304      * Returns the {@link HostConfiguration host configuration}.
    2305      *
    2306      * @return the host configuration
    2307      *
    2308      * @deprecated no longer applicable
    2309      */
    2310     public HostConfiguration getHostConfiguration() {
    2311         HostConfiguration hostconfig = new HostConfiguration();
    2312         hostconfig.setHost(this.httphost);
    2313         return hostconfig;
    2314     }
    2315     /***
    2316      * Sets the {@link HostConfiguration host configuration}.
    2317      *
    2318      * @param hostconfig The hostConfiguration to set
    2319      *
    2320      * @deprecated no longer applicable
    2321      */
    2322     public void setHostConfiguration(final HostConfiguration hostconfig) {
    2323         if (hostconfig != null) {
    2324             this.httphost = new HttpHost(
    2325                     hostconfig.getHost(),
    2326                     hostconfig.getPort(),
    2327                     hostconfig.getProtocol());
    2328         } else {
    2329             this.httphost = null;
    2330         }
    2331     }
    2332
    2333     /***
    2334      * Returns the {@link MethodRetryHandler retry handler} for this HTTP method
    2335      *
    2336      * @return the methodRetryHandler
    2337      *
    2338      * @deprecated use {@link HttpMethodParams}
    2339      */
    2340     public MethodRetryHandler getMethodRetryHandler() {
    2341         return methodRetryHandler;
    2342     }
    2343
    2344     /***
    2345      * Sets the {@link MethodRetryHandler retry handler} for this HTTP method
    2346      *
    2347      * @param handler the methodRetryHandler to use when this method executed
    2348      *
    2349      * @deprecated use {@link HttpMethodParams}
    2350      */
    2351     public void setMethodRetryHandler(MethodRetryHandler handler) {
    2352         methodRetryHandler = handler;
    2353     }
    2354
    2355     /***
    2356      * This method is a dirty hack intended to work around
    2357      * current (2.0) design flaw that prevents the user from
    2358      * obtaining correct status code, headers and response body from the
    2359      * preceding HTTP CONNECT method.
    2360      *
    2361      * TODO: Remove this crap as soon as possible
    2362      */
    2363     void fakeResponse(
    2364         StatusLine statusline,
    2365         HeaderGroup responseheaders,
    2366         InputStream responseStream
    2367     ) {
    2368         // set used so that the response can be read
    2369         this.used = true;
    2370         this.statusLine = statusline;
    2371         this.responseHeaders = responseheaders;
    2372         this.responseBody = null;
    2373         this.responseStream = responseStream;
    2374     }
    2375
    2376     /***
    2377      * Returns the target host {@link AuthState authentication state}
    2378      *
    2379      * @return host authentication state
    2380      *
    2381      * @since 3.0
    2382      */
    2383     public AuthState getHostAuthState() {
    2384         return this.hostAuthState;
    2385     }
    2386
    2387     /***
    2388      * Returns the proxy {@link AuthState authentication state}
    2389      *
    2390      * @return host authentication state
    2391      *
    2392      * @since 3.0
    2393      */
    2394     public AuthState getProxyAuthState() {
    2395         return this.proxyAuthState;
    2396     }
    2397
    2398     /***
    2399      * Tests whether the execution of this method has been aborted
    2400      *
    2401      * @return <tt>true</tt> if the execution of this method has been aborted,
    2402      *  <tt>false</tt> otherwise
    2403      *
    2404      * @since 3.0
    2405      */
    2406     public boolean isAborted() {
    2407         return this.aborted;
    2408     }
    2409
    2410     /***
    2411      * Returns <tt>true</tt> if the HTTP has been transmitted to the target
    2412      * server in its entirety, <tt>false</tt> otherwise. This flag can be useful
    2413      * for recovery logic. If the request has not been transmitted in its entirety,
    2414      * it is safe to retry the failed method.
    2415      *
    2416      * @return <tt>true</tt> if the request has been sent, <tt>false</tt> otherwise
    2417      */
    2418     public boolean isRequestSent() {
    2419         return this.requestSent;
    2420     }
    2421
    2422 }