View Javadoc

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