View Javadoc

1   /*
2    * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v 1.220 2004/11/09 19:25:42 olegk Exp $
3    * $Revision: 1.220 $
4    * $Date: 2004/11/09 19:25:42 $
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.220 $ $Date: 2004/11/09 19:25:42 $
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         if (connectionHeader != null) {
907             if (connectionHeader.getValue().equalsIgnoreCase("close")) {
908                 if (LOG.isDebugEnabled()) {
909                     LOG.debug("Should close connection in response to " 
910                         + connectionHeader.toExternalForm());
911                 }
912                 return true;
913             } else if (connectionHeader.getValue().equalsIgnoreCase("keep-alive")) {
914                 if (LOG.isDebugEnabled()) {
915                     LOG.debug("Should NOT close connection in response to " 
916                         + connectionHeader.toExternalForm());
917                 }
918                 return false;
919             } else {
920                 if (LOG.isDebugEnabled()) {
921                     LOG.debug("Unknown directive: " + connectionHeader.toExternalForm());
922                 }
923             }
924         }
925         LOG.debug("Resorting to protocol version default close connection policy");
926         // missing or invalid connection header, do the default
927         if (this.effectiveVersion.greaterEquals(HttpVersion.HTTP_1_1)) {
928             if (LOG.isDebugEnabled()) {
929                 LOG.debug("Should NOT close connection, using " + this.effectiveVersion.toString());
930             }
931         } else {
932             if (LOG.isDebugEnabled()) {
933                 LOG.debug("Should close connection, using " + this.effectiveVersion.toString());
934             }
935         }
936         return this.effectiveVersion.lessEquals(HttpVersion.HTTP_1_0);
937     }
938     
939     /***
940      * Tests if the this method is ready to be executed.
941      * 
942      * @param state the {@link HttpState state} information associated with this method
943      * @param conn the {@link HttpConnection connection} to be used
944      * @throws HttpException If the method is in invalid state.
945      */
946     private void checkExecuteConditions(HttpState state, HttpConnection conn)
947     throws HttpException {
948 
949         if (state == null) {
950             throw new IllegalArgumentException("HttpState parameter may not be null");
951         }
952         if (conn == null) {
953             throw new IllegalArgumentException("HttpConnection parameter may not be null");
954         }
955         if (this.aborted) {
956             throw new IllegalStateException("Method has been aborted");
957         }
958         if (!validate()) {
959             throw new ProtocolException("HttpMethodBase object not valid");
960         }
961     }
962 
963     /***
964      * Executes this method using the specified <code>HttpConnection</code> and
965      * <code>HttpState</code>. 
966      *
967      * @param state {@link HttpState state} information to associate with this
968      *        request. Must be non-null.
969      * @param conn the {@link HttpConnection connection} to used to execute
970      *        this HTTP method. Must be non-null.
971      *
972      * @return the integer status code if one was obtained, or <tt>-1</tt>
973      *
974      * @throws IOException if an I/O (transport) error occurs
975      * @throws HttpException  if a protocol exception occurs.
976      */
977     public int execute(HttpState state, HttpConnection conn)
978         throws HttpException, IOException {
979                 
980         LOG.trace("enter HttpMethodBase.execute(HttpState, HttpConnection)");
981 
982         // this is our connection now, assign it to a local variable so 
983         // that it can be released later
984         this.responseConnection = conn;
985 
986         checkExecuteConditions(state, conn);
987         this.statusLine = null;
988         this.connectionCloseForced = false;
989 
990         conn.setLastResponseInputStream(null);
991 
992         // determine the effective protocol version
993         if (this.effectiveVersion == null) {
994             this.effectiveVersion = this.params.getVersion(); 
995         }
996 
997         writeRequest(state, conn);
998         this.requestSent = true;
999         readResponse(state, conn);
1000         // the method has successfully executed
1001         used = true; 
1002 
1003         return statusLine.getStatusCode();
1004     }
1005 
1006     /***
1007      * Aborts the execution of this method.
1008      * 
1009      * @since 3.0
1010      */
1011     public void abort() {
1012         if (this.aborted) {
1013             return;
1014         }
1015         this.aborted = true;
1016         HttpConnection conn = this.responseConnection; 
1017         if (conn != null) {
1018             conn.close();
1019         }
1020     }
1021 
1022     /***
1023      * Returns <tt>true</tt> if the HTTP method has been already {@link #execute executed},
1024      * but not {@link #recycle recycled}.
1025      * 
1026      * @return <tt>true</tt> if the method has been executed, <tt>false</tt> otherwise
1027      */
1028     public boolean hasBeenUsed() {
1029         return used;
1030     }
1031 
1032     /***
1033      * Recycles the HTTP method so that it can be used again.
1034      * Note that all of the instance variables will be reset
1035      * once this method has been called. This method will also
1036      * release the connection being used by this HTTP method.
1037      * 
1038      * @see #releaseConnection()
1039      * 
1040      * @deprecated no longer supported and will be removed in the future
1041      *             version of HttpClient
1042      */
1043     public void recycle() {
1044         LOG.trace("enter HttpMethodBase.recycle()");
1045 
1046         releaseConnection();
1047 
1048         path = null;
1049         followRedirects = false;
1050         doAuthentication = true;
1051         queryString = null;
1052         getRequestHeaderGroup().clear();
1053         getResponseHeaderGroup().clear();
1054         getResponseTrailerHeaderGroup().clear();
1055         statusLine = null;
1056         effectiveVersion = null;
1057         aborted = false;
1058         used = false;
1059         params = new HttpMethodParams();
1060         responseBody = null;
1061         recoverableExceptionCount = 0;
1062         connectionCloseForced = false;
1063         hostAuthState.invalidate();
1064         proxyAuthState.invalidate();
1065         cookiespec = null;
1066         requestSent = false;
1067     }
1068 
1069     /***
1070      * Releases the connection being used by this HTTP method. In particular the
1071      * connection is used to read the response(if there is one) and will be held
1072      * until the response has been read. If the connection can be reused by other 
1073      * HTTP methods it is NOT closed at this point.
1074      *
1075      * @since 2.0
1076      */
1077     public void releaseConnection() {
1078 
1079         if (responseStream != null) {
1080             try {
1081                 // FYI - this may indirectly invoke responseBodyConsumed.
1082                 responseStream.close();
1083             } catch (IOException e) {
1084                 // the connection may not have been released, let's make sure
1085                 ensureConnectionRelease();
1086             }
1087         } else {
1088             // Make sure the connection has been released. If the response 
1089             // stream has not been set, this is the only way to release the 
1090             // connection. 
1091             ensureConnectionRelease();
1092         }
1093     }
1094 
1095     /***
1096      * Remove the request header associated with the given name. Note that
1097      * header-name matching is case insensitive.
1098      *
1099      * @param headerName the header name
1100      */
1101     public void removeRequestHeader(String headerName) {
1102         
1103         Header[] headers = getRequestHeaderGroup().getHeaders(headerName);
1104         for (int i = 0; i < headers.length; i++) {
1105             getRequestHeaderGroup().removeHeader(headers[i]);
1106         }
1107         
1108     }
1109     
1110     /***
1111      * Removes the given request header.
1112      * 
1113      * @param header the header
1114      */
1115     public void removeRequestHeader(final Header header) {
1116         if (header == null) {
1117             return;
1118         }
1119         getRequestHeaderGroup().removeHeader(header);
1120     }
1121 
1122     // ---------------------------------------------------------------- Queries
1123 
1124     /***
1125      * Returns <tt>true</tt> the method is ready to execute, <tt>false</tt> otherwise.
1126      * 
1127      * @return This implementation always returns <tt>true</tt>.
1128      */
1129     public boolean validate() {
1130         return true;
1131     }
1132 
1133 
1134     /*** 
1135      * Returns the actual cookie policy
1136      * 
1137      * @param state HTTP state. TODO: to be removed in the future
1138      * 
1139      * @return cookie spec
1140      */
1141     private CookieSpec getCookieSpec(final HttpState state) {
1142     	if (this.cookiespec == null) {
1143     		int i = state.getCookiePolicy();
1144     		if (i == -1) {
1145         		this.cookiespec = CookiePolicy.getCookieSpec(this.params.getCookiePolicy());
1146     		} else {
1147         		this.cookiespec = CookiePolicy.getSpecByPolicy(i);
1148     		}
1149     		this.cookiespec.setValidDateFormats(
1150             		(Collection)this.params.getParameter(HttpMethodParams.DATE_PATTERNS));
1151     	}
1152     	return this.cookiespec;
1153     }
1154 
1155     /***
1156      * Generates <tt>Cookie</tt> request headers for those {@link Cookie cookie}s
1157      * that match the given host, port and path.
1158      *
1159      * @param state the {@link HttpState state} information associated with this method
1160      * @param conn the {@link HttpConnection connection} used to execute
1161      *        this HTTP method
1162      *
1163      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1164      *                     can be recovered from.
1165      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1166      *                    cannot be recovered from.
1167      */
1168     protected void addCookieRequestHeader(HttpState state, HttpConnection conn)
1169         throws IOException, HttpException {
1170 
1171         LOG.trace("enter HttpMethodBase.addCookieRequestHeader(HttpState, "
1172                   + "HttpConnection)");
1173 
1174         Header[] cookieheaders = getRequestHeaderGroup().getHeaders("Cookie");
1175         for (int i = 0; i < cookieheaders.length; i++) {
1176             Header cookieheader = cookieheaders[i];
1177             if (cookieheader.isAutogenerated()) {
1178                 getRequestHeaderGroup().removeHeader(cookieheader);
1179             }
1180         }
1181 
1182         CookieSpec matcher = getCookieSpec(state);
1183         Cookie[] cookies = matcher.match(conn.getHost(), conn.getPort(),
1184             getPath(), conn.isSecure(), state.getCookies());
1185         if ((cookies != null) && (cookies.length > 0)) {
1186             if (getParams().isParameterTrue(HttpMethodParams.SINGLE_COOKIE_HEADER)) {
1187                 // In strict mode put all cookies on the same header
1188                 String s = matcher.formatCookies(cookies);
1189                 getRequestHeaderGroup().addHeader(new Header("Cookie", s, true));
1190             } else {
1191                 // In non-strict mode put each cookie on a separate header
1192                 for (int i = 0; i < cookies.length; i++) {
1193                     String s = matcher.formatCookie(cookies[i]);
1194                     getRequestHeaderGroup().addHeader(new Header("Cookie", s, true));
1195                 }
1196             }
1197         }
1198     }
1199 
1200     /***
1201      * Generates <tt>Host</tt> request header, as long as no <tt>Host</tt> request
1202      * header already exists.
1203      *
1204      * @param state the {@link HttpState state} information associated with this method
1205      * @param conn the {@link HttpConnection connection} used to execute
1206      *        this HTTP method
1207      *
1208      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1209      *                     can be recovered from.
1210      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1211      *                    cannot be recovered from.
1212      */
1213     protected void addHostRequestHeader(HttpState state, HttpConnection conn)
1214     throws IOException, HttpException {
1215         LOG.trace("enter HttpMethodBase.addHostRequestHeader(HttpState, "
1216                   + "HttpConnection)");
1217 
1218         // Per 19.6.1.1 of RFC 2616, it is legal for HTTP/1.0 based
1219         // applications to send the Host request-header.
1220         // TODO: Add the ability to disable the sending of this header for
1221         //       HTTP/1.0 requests.
1222         String host = this.params.getVirtualHost();
1223         if (host != null) {
1224             LOG.debug("Using virtual host name: " + host);
1225         } else {
1226             host = conn.getHost();
1227         }
1228         int port = conn.getPort();
1229 
1230         // Note: RFC 2616 uses the term "internet host name" for what goes on the
1231         // host line.  It would seem to imply that host should be blank if the
1232         // host is a number instead of an name.  Based on the behavior of web
1233         // browsers, and the fact that RFC 2616 never defines the phrase "internet
1234         // host name", and the bad behavior of HttpClient that follows if we
1235         // send blank, I interpret this as a small misstatement in the RFC, where
1236         // they meant to say "internet host".  So IP numbers get sent as host
1237         // entries too. -- Eric Johnson 12/13/2002
1238         if (LOG.isDebugEnabled()) {
1239             LOG.debug("Adding Host request header");
1240         }
1241 
1242         //appends the port only if not using the default port for the protocol
1243         if (conn.getProtocol().getDefaultPort() != port) {
1244             host += (":" + port);
1245         }
1246 
1247         setRequestHeader("Host", host);
1248     }
1249 
1250     /***
1251      * Generates <tt>Proxy-Connection: Keep-Alive</tt> request header when 
1252      * communicating via a proxy server.
1253      *
1254      * @param state the {@link HttpState state} information associated with this method
1255      * @param conn the {@link HttpConnection connection} used to execute
1256      *        this HTTP method
1257      *
1258      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1259      *                     can be recovered from.
1260      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1261      *                    cannot be recovered from.
1262      */
1263     protected void addProxyConnectionHeader(HttpState state,
1264                                             HttpConnection conn)
1265     throws IOException, HttpException {
1266         LOG.trace("enter HttpMethodBase.addProxyConnectionHeader("
1267                   + "HttpState, HttpConnection)");
1268         if (!conn.isTransparent()) {
1269             setRequestHeader("Proxy-Connection", "Keep-Alive");
1270         }
1271     }
1272 
1273     /***
1274      * Generates all the required request {@link Header header}s 
1275      * to be submitted via the given {@link HttpConnection connection}.
1276      *
1277      * <p>
1278      * This implementation adds <tt>User-Agent</tt>, <tt>Host</tt>,
1279      * <tt>Cookie</tt>, <tt>Authorization</tt>, <tt>Proxy-Authorization</tt>
1280      * and <tt>Proxy-Connection</tt> headers, when appropriate.
1281      * </p>
1282      *
1283      * <p>
1284      * Subclasses may want to override this method to to add additional
1285      * headers, and may choose to invoke this implementation (via
1286      * <tt>super</tt>) to add the "standard" headers.
1287      * </p>
1288      *
1289      * @param state the {@link HttpState state} information associated with this method
1290      * @param conn the {@link HttpConnection connection} used to execute
1291      *        this HTTP method
1292      *
1293      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1294      *                     can be recovered from.
1295      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1296      *                    cannot be recovered from.
1297      *
1298      * @see #writeRequestHeaders
1299      */
1300     protected void addRequestHeaders(HttpState state, HttpConnection conn)
1301     throws IOException, HttpException {
1302         LOG.trace("enter HttpMethodBase.addRequestHeaders(HttpState, "
1303             + "HttpConnection)");
1304 
1305         addUserAgentRequestHeader(state, conn);
1306         addHostRequestHeader(state, conn);
1307         addCookieRequestHeader(state, conn);
1308         addProxyConnectionHeader(state, conn);
1309     }
1310 
1311     /***
1312      * Generates default <tt>User-Agent</tt> request header, as long as no
1313      * <tt>User-Agent</tt> request header already exists.
1314      *
1315      * @param state the {@link HttpState state} information associated with this method
1316      * @param conn the {@link HttpConnection connection} used to execute
1317      *        this HTTP method
1318      *
1319      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1320      *                     can be recovered from.
1321      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1322      *                    cannot be recovered from.
1323      */
1324     protected void addUserAgentRequestHeader(HttpState state,
1325                                              HttpConnection conn)
1326     throws IOException, HttpException {
1327         LOG.trace("enter HttpMethodBase.addUserAgentRequestHeaders(HttpState, "
1328             + "HttpConnection)");
1329 
1330         if (getRequestHeader("User-Agent") == null) {
1331             String agent = (String)getParams().getParameter(HttpMethodParams.USER_AGENT);
1332             if (agent == null) {
1333                 agent = "Jakarta Commons-HttpClient";
1334             }
1335             setRequestHeader("User-Agent", agent);
1336         }
1337     }
1338 
1339     /***
1340      * Throws an {@link IllegalStateException} if the HTTP method has been already
1341      * {@link #execute executed}, but not {@link #recycle recycled}.
1342      *
1343      * @throws IllegalStateException if the method has been used and not
1344      *      recycled
1345      */
1346     protected void checkNotUsed() throws IllegalStateException {
1347         if (used) {
1348             throw new IllegalStateException("Already used.");
1349         }
1350     }
1351 
1352     /***
1353      * Throws an {@link IllegalStateException} if the HTTP method has not been
1354      * {@link #execute executed} since last {@link #recycle recycle}.
1355      *
1356      *
1357      * @throws IllegalStateException if not used
1358      */
1359     protected void checkUsed()  throws IllegalStateException {
1360         if (!used) {
1361             throw new IllegalStateException("Not Used.");
1362         }
1363     }
1364 
1365     // ------------------------------------------------- Static Utility Methods
1366 
1367     /***
1368      * Generates HTTP request line according to the specified attributes.
1369      *
1370      * @param connection the {@link HttpConnection connection} used to execute
1371      *        this HTTP method
1372      * @param name the method name generate a request for
1373      * @param requestPath the path string for the request
1374      * @param query the query string for the request
1375      * @param version the protocol version to use (e.g. HTTP/1.0)
1376      *
1377      * @return HTTP request line
1378      */
1379     protected static String generateRequestLine(HttpConnection connection,
1380         String name, String requestPath, String query, String version) {
1381         LOG.trace("enter HttpMethodBase.generateRequestLine(HttpConnection, "
1382             + "String, String, String, String)");
1383 
1384         StringBuffer buf = new StringBuffer();
1385         // Append method name
1386         buf.append(name);
1387         buf.append(" ");
1388         // Absolute or relative URL?
1389         if (!connection.isTransparent()) {
1390             Protocol protocol = connection.getProtocol();
1391             buf.append(protocol.getScheme().toLowerCase());
1392             buf.append("://");
1393             buf.append(connection.getHost());
1394             if ((connection.getPort() != -1) 
1395                 && (connection.getPort() != protocol.getDefaultPort())
1396             ) {
1397                 buf.append(":");
1398                 buf.append(connection.getPort());
1399             }
1400         }
1401         // Append path, if any
1402         if (requestPath == null) {
1403             buf.append("/");
1404         } else {
1405             if (!connection.isTransparent() && !requestPath.startsWith("/")) {
1406                 buf.append("/");
1407             }
1408             buf.append(requestPath);
1409         }
1410         // Append query, if any
1411         if (query != null) {
1412             if (query.indexOf("?") != 0) {
1413                 buf.append("?");
1414             }
1415             buf.append(query);
1416         }
1417         // Append protocol
1418         buf.append(" ");
1419         buf.append(version);
1420         buf.append("\r\n");
1421         
1422         return buf.toString();
1423     }
1424     
1425     /***
1426      * This method is invoked immediately after 
1427      * {@link #readResponseBody(HttpState,HttpConnection)} and can be overridden by
1428      * sub-classes in order to provide custom body processing.
1429      *
1430      * <p>
1431      * This implementation does nothing.
1432      * </p>
1433      *
1434      * @param state the {@link HttpState state} information associated with this method
1435      * @param conn the {@link HttpConnection connection} used to execute
1436      *        this HTTP method
1437      *
1438      * @see #readResponse
1439      * @see #readResponseBody
1440      */
1441     protected void processResponseBody(HttpState state, HttpConnection conn) {
1442     }
1443 
1444     /***
1445      * This method is invoked immediately after 
1446      * {@link #readResponseHeaders(HttpState,HttpConnection)} and can be overridden by
1447      * sub-classes in order to provide custom response headers processing.
1448 
1449      * <p>
1450      * This implementation will handle the <tt>Set-Cookie</tt> and
1451      * <tt>Set-Cookie2</tt> headers, if any, adding the relevant cookies to
1452      * the given {@link HttpState}.
1453      * </p>
1454      *
1455      * @param state the {@link HttpState state} information associated with this method
1456      * @param conn the {@link HttpConnection connection} used to execute
1457      *        this HTTP method
1458      *
1459      * @see #readResponse
1460      * @see #readResponseHeaders
1461      */
1462     protected void processResponseHeaders(HttpState state,
1463         HttpConnection conn) {
1464         LOG.trace("enter HttpMethodBase.processResponseHeaders(HttpState, "
1465             + "HttpConnection)");
1466 
1467         Header[] headers = getResponseHeaderGroup().getHeaders("set-cookie2");
1468         //Only process old style set-cookie headers if new style headres
1469         //are not present
1470         if (headers.length == 0) { 
1471             headers = getResponseHeaderGroup().getHeaders("set-cookie");
1472         }
1473         
1474         CookieSpec parser = getCookieSpec(state);
1475         for (int i = 0; i < headers.length; i++) {
1476             Header header = headers[i];
1477             Cookie[] cookies = null;
1478             try {
1479                 cookies = parser.parse(
1480                   conn.getHost(),
1481                   conn.getPort(),
1482                   getPath(),
1483                   conn.isSecure(),
1484                   header);
1485             } catch (MalformedCookieException e) {
1486                 if (LOG.isWarnEnabled()) {
1487                     LOG.warn("Invalid cookie header: \"" 
1488                         + header.getValue() 
1489                         + "\". " + e.getMessage());
1490                 }
1491             }
1492             if (cookies != null) {
1493                 for (int j = 0; j < cookies.length; j++) {
1494                     Cookie cookie = cookies[j];
1495                     try {
1496                         parser.validate(
1497                           conn.getHost(),
1498                           conn.getPort(),
1499                           getPath(),
1500                           conn.isSecure(),
1501                           cookie);
1502                         state.addCookie(cookie);
1503                         if (LOG.isDebugEnabled()) {
1504                             LOG.debug("Cookie accepted: \"" 
1505                                 + parser.formatCookie(cookie) + "\"");
1506                         }
1507                     } catch (MalformedCookieException e) {
1508                         if (LOG.isWarnEnabled()) {
1509                             LOG.warn("Cookie rejected: \"" + parser.formatCookie(cookie) 
1510                                 + "\". " + e.getMessage());
1511                         }
1512                     }
1513                 }
1514             }
1515         }
1516     }
1517 
1518     /***
1519      * This method is invoked immediately after 
1520      * {@link #readStatusLine(HttpState,HttpConnection)} and can be overridden by
1521      * sub-classes in order to provide custom response status line processing.
1522      *
1523      * @param state the {@link HttpState state} information associated with this method
1524      * @param conn the {@link HttpConnection connection} used to execute
1525      *        this HTTP method
1526      *
1527      * @see #readResponse
1528      * @see #readStatusLine
1529      */
1530     protected void processStatusLine(HttpState state, HttpConnection conn) {
1531     }
1532 
1533     /***
1534      * Reads the response from the given {@link HttpConnection connection}.
1535      *
1536      * <p>
1537      * The response is processed as the following sequence of actions:
1538      *
1539      * <ol>
1540      * <li>
1541      * {@link #readStatusLine(HttpState,HttpConnection)} is
1542      * invoked to read the request line.
1543      * </li>
1544      * <li>
1545      * {@link #processStatusLine(HttpState,HttpConnection)}
1546      * is invoked, allowing the method to process the status line if
1547      * desired.
1548      * </li>
1549      * <li>
1550      * {@link #readResponseHeaders(HttpState,HttpConnection)} is invoked to read
1551      * the associated headers.
1552      * </li>
1553      * <li>
1554      * {@link #processResponseHeaders(HttpState,HttpConnection)} is invoked, allowing
1555      * the method to process the headers if desired.
1556      * </li>
1557      * <li>
1558      * {@link #readResponseBody(HttpState,HttpConnection)} is
1559      * invoked to read the associated body (if any).
1560      * </li>
1561      * <li>
1562      * {@link #processResponseBody(HttpState,HttpConnection)} is invoked, allowing the
1563      * method to process the response body if desired.
1564      * </li>
1565      * </ol>
1566      *
1567      * Subclasses may want to override one or more of the above methods to to
1568      * customize the processing. (Or they may choose to override this method
1569      * if dramatically different processing is required.)
1570      * </p>
1571      *
1572      * @param state the {@link HttpState state} information associated with this method
1573      * @param conn the {@link HttpConnection connection} used to execute
1574      *        this HTTP method
1575      *
1576      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1577      *                     can be recovered from.
1578      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1579      *                    cannot be recovered from.
1580      */
1581     protected void readResponse(HttpState state, HttpConnection conn)
1582     throws IOException, HttpException {
1583         LOG.trace(
1584         "enter HttpMethodBase.readResponse(HttpState, HttpConnection)");
1585         // Status line & line may have already been received
1586         // if 'expect - continue' handshake has been used
1587         while (this.statusLine == null) {
1588             readStatusLine(state, conn);
1589             processStatusLine(state, conn);
1590             readResponseHeaders(state, conn);
1591             processResponseHeaders(state, conn);
1592             
1593             int status = this.statusLine.getStatusCode();
1594             if ((status >= 100) && (status < 200)) {
1595                 if (LOG.isInfoEnabled()) {
1596                     LOG.info("Discarding unexpected response: " + this.statusLine.toString()); 
1597                 }
1598                 this.statusLine = null;
1599             }
1600         }
1601         readResponseBody(state, conn);
1602         processResponseBody(state, conn);
1603     }
1604 
1605     /***
1606      * Read the response body from the given {@link HttpConnection}.
1607      *
1608      * <p>
1609      * The current implementation wraps the socket level stream with
1610      * an appropriate stream for the type of response (chunked, content-length,
1611      * or auto-close).  If there is no response body, the connection associated
1612      * with the request will be returned to the connection manager.
1613      * </p>
1614      *
1615      * <p>
1616      * Subclasses may want to override this method to to customize the
1617      * processing.
1618      * </p>
1619      *
1620      * @param state the {@link HttpState state} information associated with this method
1621      * @param conn the {@link HttpConnection connection} used to execute
1622      *        this HTTP method
1623      *
1624      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1625      *                     can be recovered from.
1626      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1627      *                    cannot be recovered from.
1628      *
1629      * @see #readResponse
1630      * @see #processResponseBody
1631      */
1632     protected void readResponseBody(HttpState state, HttpConnection conn)
1633     throws IOException, HttpException {
1634         LOG.trace(
1635             "enter HttpMethodBase.readResponseBody(HttpState, HttpConnection)");
1636 
1637         // assume we are not done with the connection if we get a stream
1638         InputStream stream = readResponseBody(conn);
1639         if (stream == null) {
1640             // done using the connection!
1641             responseBodyConsumed();
1642         } else {
1643             conn.setLastResponseInputStream(stream);
1644             setResponseStream(stream);
1645         }
1646     }
1647 
1648     /***
1649      * Returns the response body as an {@link InputStream input stream}
1650      * corresponding to the values of the <tt>Content-Length</tt> and 
1651      * <tt>Transfer-Encoding</tt> headers. If no response body is available
1652      * returns <tt>null</tt>.
1653      * <p>
1654      *
1655      * @see #readResponse
1656      * @see #processResponseBody
1657      *
1658      * @param conn the {@link HttpConnection connection} used to execute
1659      *        this HTTP method
1660      *
1661      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1662      *                     can be recovered from.
1663      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1664      *                    cannot be recovered from.
1665      */
1666     private InputStream readResponseBody(HttpConnection conn)
1667         throws HttpException, IOException {
1668 
1669         LOG.trace("enter HttpMethodBase.readResponseBody(HttpConnection)");
1670 
1671         responseBody = null;
1672         InputStream is = conn.getResponseInputStream();
1673         if (Wire.CONTENT_WIRE.enabled()) {
1674             is = new WireLogInputStream(is, Wire.CONTENT_WIRE);
1675         }
1676         InputStream result = null;
1677         Header transferEncodingHeader = responseHeaders.getFirstHeader("Transfer-Encoding");
1678         // We use Transfer-Encoding if present and ignore Content-Length.
1679         // RFC2616, 4.4 item number 3
1680         if (transferEncodingHeader != null) {
1681 
1682             String transferEncoding = transferEncodingHeader.getValue();
1683             if (!"chunked".equalsIgnoreCase(transferEncoding) 
1684                 && !"identity".equalsIgnoreCase(transferEncoding)) {
1685                 if (LOG.isWarnEnabled()) {
1686                     LOG.warn("Unsupported transfer encoding: " + transferEncoding);
1687                 }
1688             }
1689             HeaderElement[] encodings = transferEncodingHeader.getElements();
1690             // The chunked encoding must be the last one applied
1691             // RFC2616, 14.41
1692             int len = encodings.length;            
1693             if ((len > 0) && ("chunked".equalsIgnoreCase(encodings[len - 1].getName()))) { 
1694                 // if response body is empty
1695                 if (conn.isResponseAvailable(conn.getParams().getSoTimeout())) {
1696                     result = new ChunkedInputStream(is, this);
1697                 } else {
1698                     if (getParams().isParameterTrue(HttpMethodParams.STRICT_TRANSFER_ENCODING)) {
1699                         throw new ProtocolException("Chunk-encoded body declared but not sent");
1700                     } else {
1701                         LOG.warn("Chunk-encoded body missing");
1702                     }
1703                 }
1704             } else {
1705                 LOG.info("Response content is not chunk-encoded");
1706                 // The connection must be terminated by closing 
1707                 // the socket as per RFC 2616, 3.6
1708                 setConnectionCloseForced(true);
1709                 result = is;  
1710             }
1711         } else {
1712             long expectedLength = getResponseContentLength();
1713             if (expectedLength == -1) {
1714                 if (canResponseHaveBody(statusLine.getStatusCode())) {
1715                     Header connectionHeader = responseHeaders.getFirstHeader("Connection");
1716                     String connectionDirective = null;
1717                     if (connectionHeader != null) {
1718                         connectionDirective = connectionHeader.getValue();
1719                     }
1720                     if (this.effectiveVersion.greaterEquals(HttpVersion.HTTP_1_1) && 
1721                         !"close".equalsIgnoreCase(connectionDirective)) {
1722                         LOG.info("Response content length is not known");
1723                         setConnectionCloseForced(true);
1724                     }
1725                     result = is;            
1726                 }
1727             } else {
1728                 result = new ContentLengthInputStream(is, expectedLength);
1729             }
1730         } 
1731         // if there is a result - ALWAYS wrap it in an observer which will
1732         // close the underlying stream as soon as it is consumed, and notify
1733         // the watcher that the stream has been consumed.
1734         if (result != null) {
1735 
1736             result = new AutoCloseInputStream(
1737                 result,
1738                 new ResponseConsumedWatcher() {
1739                     public void responseConsumed() {
1740                         responseBodyConsumed();
1741                     }
1742                 }
1743             );
1744         }
1745 
1746         return result;
1747     }
1748 
1749     /***
1750      * Reads the response headers from the given {@link HttpConnection connection}.
1751      *
1752      * <p>
1753      * Subclasses may want to override this method to to customize the
1754      * processing.
1755      * </p>
1756      *
1757      * <p>
1758      * "It must be possible to combine the multiple header fields into one
1759      * "field-name: field-value" pair, without changing the semantics of the
1760      * message, by appending each subsequent field-value to the first, each
1761      * separated by a comma." - HTTP/1.0 (4.3)
1762      * </p>
1763      *
1764      * @param state the {@link HttpState state} information associated with this method
1765      * @param conn the {@link HttpConnection connection} used to execute
1766      *        this HTTP method
1767      *
1768      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1769      *                     can be recovered from.
1770      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1771      *                    cannot be recovered from.
1772      *
1773      * @see #readResponse
1774      * @see #processResponseHeaders
1775      */
1776     protected void readResponseHeaders(HttpState state, HttpConnection conn)
1777     throws IOException, HttpException {
1778         LOG.trace("enter HttpMethodBase.readResponseHeaders(HttpState,"
1779             + "HttpConnection)");
1780 
1781         getResponseHeaderGroup().clear();
1782         
1783         Header[] headers = HttpParser.parseHeaders(
1784             conn.getResponseInputStream(), getParams().getHttpElementCharset());
1785         if (Wire.HEADER_WIRE.enabled()) {
1786             for (int i = 0; i < headers.length; i++) {
1787                 Wire.HEADER_WIRE.input(headers[i].toExternalForm());
1788             }
1789         }
1790         getResponseHeaderGroup().setHeaders(headers);
1791     }
1792 
1793     /***
1794      * Read the status line from the given {@link HttpConnection}, setting my
1795      * {@link #getStatusCode status code} and {@link #getStatusText status
1796      * text}.
1797      *
1798      * <p>
1799      * Subclasses may want to override this method to to customize the
1800      * processing.
1801      * </p>
1802      *
1803      * @param state the {@link HttpState state} information associated with this method
1804      * @param conn the {@link HttpConnection connection} used to execute
1805      *        this HTTP method
1806      *
1807      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1808      *                     can be recovered from.
1809      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1810      *                    cannot be recovered from.
1811      *
1812      * @see StatusLine
1813      */
1814     protected void readStatusLine(HttpState state, HttpConnection conn)
1815     throws IOException, HttpException {
1816         LOG.trace("enter HttpMethodBase.readStatusLine(HttpState, HttpConnection)");
1817 
1818         final int maxGarbageLines = getParams().
1819             getIntParameter(HttpMethodParams.STATUS_LINE_GARBAGE_LIMIT, Integer.MAX_VALUE);
1820 
1821         //read out the HTTP status string
1822         int count = 0;
1823         String s;
1824         do {
1825             s = conn.readLine(getParams().getHttpElementCharset());
1826             if (s == null && count == 0) {
1827                 // The server just dropped connection on us
1828                 throw new NoHttpResponseException("The server " + conn.getHost() + 
1829                     " failed to respond");
1830             }
1831             if (Wire.HEADER_WIRE.enabled()) {
1832                 Wire.HEADER_WIRE.input(s + "\r\n");
1833             }
1834             if (s != null && StatusLine.startsWithHTTP(s)) {
1835                 // Got one
1836                 break;
1837             } else if (s == null || count >= maxGarbageLines) {
1838                 // Giving up
1839                 throw new ProtocolException("The server " + conn.getHost() + 
1840                         " failed to respond with a valid HTTP response");
1841             }
1842             count++;
1843         } while(true);
1844 
1845         //create the status line from the status string
1846         statusLine = new StatusLine(s);
1847 
1848         //check for a valid HTTP-Version
1849         String versionStr = statusLine.getHttpVersion();
1850         if (getParams().isParameterFalse(HttpMethodParams.UNAMBIGUOUS_STATUS_LINE) 
1851            && versionStr.equals("HTTP")) {
1852             getParams().setVersion(HttpVersion.HTTP_1_0);
1853             if (LOG.isWarnEnabled()) {
1854                 LOG.warn("Ambiguous status line (HTTP protocol version missing):" +
1855                 statusLine.toString());
1856             }
1857         } else {
1858             this.effectiveVersion = HttpVersion.parse(versionStr);
1859         }
1860 
1861     }
1862 
1863     // ------------------------------------------------------ Protected Methods
1864 
1865     /***
1866      * <p>
1867      * Sends the request via the given {@link HttpConnection connection}.
1868      * </p>
1869      *
1870      * <p>
1871      * The request is written as the following sequence of actions:
1872      * </p>
1873      *
1874      * <ol>
1875      * <li>
1876      * {@link #writeRequestLine(HttpState, HttpConnection)} is invoked to 
1877      * write the request line.
1878      * </li>
1879      * <li>
1880      * {@link #writeRequestHeaders(HttpState, HttpConnection)} is invoked 
1881      * to write the associated headers.
1882      * </li>
1883      * <li>
1884      * <tt>\r\n</tt> is sent to close the head part of the request.
1885      * </li>
1886      * <li>
1887      * {@link #writeRequestBody(HttpState, HttpConnection)} is invoked to 
1888      * write the body part of the request.
1889      * </li>
1890      * </ol>
1891      *
1892      * <p>
1893      * Subclasses may want to override one or more of the above methods to to
1894      * customize the processing. (Or they may choose to override this method
1895      * if dramatically different processing is required.)
1896      * </p>
1897      *
1898      * @param state the {@link HttpState state} information associated with this method
1899      * @param conn the {@link HttpConnection connection} used to execute
1900      *        this HTTP method
1901      *
1902      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1903      *                     can be recovered from.
1904      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1905      *                    cannot be recovered from.
1906      */
1907     protected void writeRequest(HttpState state, HttpConnection conn)
1908     throws IOException, HttpException {
1909         LOG.trace(
1910             "enter HttpMethodBase.writeRequest(HttpState, HttpConnection)");
1911         writeRequestLine(state, conn);
1912         writeRequestHeaders(state, conn);
1913         conn.writeLine(); // close head
1914         // make sure the status line and headers have been sent
1915         conn.flushRequestOutputStream();
1916         if (Wire.HEADER_WIRE.enabled()) {
1917             Wire.HEADER_WIRE.output("\r\n");
1918         }
1919 
1920         HttpVersion ver = getParams().getVersion();
1921         Header expectheader = getRequestHeader("Expect");
1922         String expectvalue = null;
1923         if (expectheader != null) {
1924             expectvalue = expectheader.getValue();
1925         }
1926         if ((expectvalue != null) 
1927          && (expectvalue.compareToIgnoreCase("100-continue") == 0)) {
1928             if (ver.greaterEquals(HttpVersion.HTTP_1_1)) {
1929                 int readTimeout = conn.getParams().getSoTimeout();
1930                 try {
1931                     conn.setSocketTimeout(RESPONSE_WAIT_TIME_MS);
1932                     readStatusLine(state, conn);
1933                     processStatusLine(state, conn);
1934                     readResponseHeaders(state, conn);
1935                     processResponseHeaders(state, conn);
1936 
1937                     if (this.statusLine.getStatusCode() == HttpStatus.SC_CONTINUE) {
1938                         // Discard status line
1939                         this.statusLine = null;
1940                         LOG.debug("OK to continue received");
1941                     } else {
1942                         return;
1943                     }
1944                 } catch (InterruptedIOException e) {
1945                     if (!ExceptionUtil.isSocketTimeoutException(e)) {
1946                         throw e;
1947                     }
1948                     // Most probably Expect header is not recongnized
1949                     // Remove the header to signal the method 
1950                     // that it's okay to go ahead with sending data
1951                     removeRequestHeader("Expect");
1952                     LOG.info("100 (continue) read timeout. Resume sending the request");
1953                 } finally {
1954                     conn.setSocketTimeout(readTimeout);
1955                 }
1956                 
1957             } else {
1958                 removeRequestHeader("Expect");
1959                 LOG.info("'Expect: 100-continue' handshake is only supported by "
1960                     + "HTTP/1.1 or higher");
1961             }
1962         }
1963 
1964         writeRequestBody(state, conn);
1965         // make sure the entire request body has been sent
1966         conn.flushRequestOutputStream();
1967     }
1968 
1969     /***
1970      * Writes the request body to the given {@link HttpConnection connection}.
1971      *
1972      * <p>
1973      * This method should return <tt>true</tt> if the request body was actually
1974      * sent (or is empty), or <tt>false</tt> if it could not be sent for some
1975      * reason.
1976      * </p>
1977      *
1978      * <p>
1979      * This implementation writes nothing and returns <tt>true</tt>.
1980      * </p>
1981      *
1982      * @param state the {@link HttpState state} information associated with this method
1983      * @param conn the {@link HttpConnection connection} used to execute
1984      *        this HTTP method
1985      *
1986      * @return <tt>true</tt>
1987      *
1988      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1989      *                     can be recovered from.
1990      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1991      *                    cannot be recovered from.
1992      */
1993     protected boolean writeRequestBody(HttpState state, HttpConnection conn)
1994     throws IOException, HttpException {
1995         return true;
1996     }
1997 
1998     /***
1999      * Writes the request headers to the given {@link HttpConnection connection}.
2000      *
2001      * <p>
2002      * This implementation invokes {@link #addRequestHeaders(HttpState,HttpConnection)},
2003      * and then writes each header to the request stream.
2004      * </p>
2005      *
2006      * <p>
2007      * Subclasses may want to override this method to to customize the
2008      * processing.
2009      * </p>
2010      *
2011      * @param state the {@link HttpState state} information associated with this method
2012      * @param conn the {@link HttpConnection connection} used to execute
2013      *        this HTTP method
2014      *
2015      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2016      *                     can be recovered from.
2017      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
2018      *                    cannot be recovered from.
2019      *
2020      * @see #addRequestHeaders
2021      * @see #getRequestHeaders
2022      */
2023     protected void writeRequestHeaders(HttpState state, HttpConnection conn)
2024     throws IOException, HttpException {
2025         LOG.trace("enter HttpMethodBase.writeRequestHeaders(HttpState,"
2026             + "HttpConnection)");
2027         addRequestHeaders(state, conn);
2028 
2029         String charset = getParams().getHttpElementCharset();
2030         
2031         Header[] headers = getRequestHeaders();
2032         for (int i = 0; i < headers.length; i++) {
2033             String s = headers[i].toExternalForm();
2034             if (Wire.HEADER_WIRE.enabled()) {
2035                 Wire.HEADER_WIRE.output(s);
2036             }
2037             conn.print(s, charset);
2038         }
2039     }
2040 
2041     /***
2042      * Writes the request line to the given {@link HttpConnection connection}.
2043      *
2044      * <p>
2045      * Subclasses may want to override this method to to customize the
2046      * processing.
2047      * </p>
2048      *
2049      * @param state the {@link HttpState state} information associated with this method
2050      * @param conn the {@link HttpConnection connection} used to execute
2051      *        this HTTP method
2052      *
2053      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2054      *                     can be recovered from.
2055      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
2056      *                    cannot be recovered from.
2057      *
2058      * @see #generateRequestLine
2059      */
2060     protected void writeRequestLine(HttpState state, HttpConnection conn)
2061     throws IOException, HttpException {
2062         LOG.trace(
2063             "enter HttpMethodBase.writeRequestLine(HttpState, HttpConnection)");
2064         String requestLine = getRequestLine(conn);
2065         if (Wire.HEADER_WIRE.enabled()) {
2066             Wire.HEADER_WIRE.output(requestLine);
2067         }
2068         conn.print(requestLine, getParams().getHttpElementCharset());
2069     }
2070 
2071     /***
2072      * Returns the request line.
2073      * 
2074      * @param conn the {@link HttpConnection connection} used to execute
2075      *        this HTTP method
2076      * 
2077      * @return The request line.
2078      */
2079     private String getRequestLine(HttpConnection conn) {
2080         return  HttpMethodBase.generateRequestLine(conn, getName(),
2081                 getPath(), getQueryString(), this.effectiveVersion.toString());
2082     }
2083 
2084     /***
2085      * Returns {@link HttpMethodParams HTTP protocol parameters} associated with this method.
2086      *
2087      * @return HTTP parameters.
2088      *
2089      * @since 3.0
2090      */
2091     public HttpMethodParams getParams() {
2092         return this.params;
2093     }
2094 
2095     /***
2096      * Assigns {@link HttpMethodParams HTTP protocol parameters} for this method.
2097      * 
2098      * @since 3.0
2099      * 
2100      * @see HttpMethodParams
2101      */
2102     public void setParams(final HttpMethodParams params) {
2103         if (params == null) {
2104             throw new IllegalArgumentException("Parameters may not be null");
2105         }
2106         this.params = params;
2107     }
2108 
2109     /***
2110      * Returns the HTTP version used with this method (may be <tt>null</tt>
2111      * if undefined, that is, the method has not been executed)
2112      *
2113      * @return HTTP version.
2114      *
2115      * @since 3.0
2116      */
2117     public HttpVersion getEffectiveVersion() {
2118         return this.effectiveVersion;
2119     }
2120 
2121     /***
2122      * Per RFC 2616 section 4.3, some response can never contain a message
2123      * body.
2124      *
2125      * @param status - the HTTP status code
2126      *
2127      * @return <tt>true</tt> if the message may contain a body, <tt>false</tt> if it can not
2128      *         contain a message body
2129      */
2130     private static boolean canResponseHaveBody(int status) {
2131         LOG.trace("enter HttpMethodBase.canResponseHaveBody(int)");
2132 
2133         boolean result = true;
2134 
2135         if ((status >= 100 && status <= 199) || (status == 204)
2136             || (status == 304)) { // NOT MODIFIED
2137             result = false;
2138         }
2139 
2140         return result;
2141     }
2142 
2143     /***
2144      * Returns proxy authentication realm, if it has been used during authentication process. 
2145      * Otherwise returns <tt>null</tt>.
2146      * 
2147      * @return proxy authentication realm
2148      * 
2149      * @deprecated use #getProxyAuthState()
2150      */
2151     public String getProxyAuthenticationRealm() {
2152         return this.proxyAuthState.getRealm();
2153     }
2154 
2155     /***
2156      * Returns authentication realm, if it has been used during authentication process. 
2157      * Otherwise returns <tt>null</tt>.
2158      * 
2159      * @return authentication realm
2160      * 
2161      * @deprecated use #getHostAuthState()
2162      */
2163     public String getAuthenticationRealm() {
2164         return this.hostAuthState.getRealm();
2165     }
2166 
2167     /***
2168      * Returns the character set from the <tt>Content-Type</tt> header.
2169      * 
2170      * @param contentheader The content header.
2171      * @return String The character set.
2172      */
2173     protected String getContentCharSet(Header contentheader) {
2174         LOG.trace("enter getContentCharSet( Header contentheader )");
2175         String charset = null;
2176         if (contentheader != null) {
2177             HeaderElement values[] = contentheader.getElements();
2178             // I expect only one header element to be there
2179             // No more. no less
2180             if (values.length == 1) {
2181                 NameValuePair param = values[0].getParameterByName("charset");
2182                 if (param != null) {
2183                     // If I get anything "funny" 
2184                     // UnsupportedEncondingException will result
2185                     charset = param.getValue();
2186                 }
2187             }
2188         }
2189         if (charset == null) {
2190             charset = getParams().getContentCharset();
2191             if (LOG.isDebugEnabled()) {
2192                 LOG.debug("Default charset used: " + charset);
2193             }
2194         }
2195         return charset;
2196     }
2197 
2198 
2199     /***
2200      * Returns the character encoding of the request from the <tt>Content-Type</tt> header.
2201      * 
2202      * @return String The character set.
2203      */
2204     public String getRequestCharSet() {
2205         return getContentCharSet(getRequestHeader("Content-Type"));
2206     }
2207 
2208 
2209     /***  
2210      * Returns the character encoding of the response from the <tt>Content-Type</tt> header.
2211      * 
2212      * @return String The character set.
2213      */
2214     public String getResponseCharSet() {
2215         return getContentCharSet(getResponseHeader("Content-Type"));
2216     }
2217 
2218     /***
2219      * @deprecated no longer used
2220      * 
2221      * Returns the number of "recoverable" exceptions thrown and handled, to
2222      * allow for monitoring the quality of the connection.
2223      *
2224      * @return The number of recoverable exceptions handled by the method.
2225      */
2226     public int getRecoverableExceptionCount() {
2227         return recoverableExceptionCount;
2228     }
2229 
2230     /***
2231      * A response has been consumed.
2232      *
2233      * <p>The default behavior for this class is to check to see if the connection
2234      * should be closed, and close if need be, and to ensure that the connection
2235      * is returned to the connection manager - if and only if we are not still
2236      * inside the execute call.</p>
2237      *
2238      */
2239     protected void responseBodyConsumed() {
2240 
2241         // make sure this is the initial invocation of the notification,
2242         // ignore subsequent ones.
2243         responseStream = null;
2244         if (responseConnection != null) {
2245             responseConnection.setLastResponseInputStream(null);
2246 
2247             // At this point, no response data should be available.
2248             // If there is data available, regard the connection as being
2249             // unreliable and close it.
2250             
2251             if (shouldCloseConnection(responseConnection)) {
2252                 responseConnection.close();
2253             } else {
2254                 try {
2255                     if(responseConnection.isResponseAvailable()) {
2256                         boolean logExtraInput =
2257                             getParams().isParameterTrue(HttpMethodParams.WARN_EXTRA_INPUT);
2258 
2259                         if(logExtraInput) {
2260                             LOG.warn("Extra response data detected - closing connection");
2261                         } 
2262                         responseConnection.close();
2263                     }
2264                 }
2265                 catch (IOException e) {
2266                     LOG.warn(e.getMessage());
2267                     responseConnection.close();
2268                 }
2269             }
2270         }
2271         this.connectionCloseForced = false;
2272         ensureConnectionRelease();
2273     }
2274 
2275     /***
2276      * Insure that the connection is released back to the pool.
2277      */
2278     private void ensureConnectionRelease() {
2279         if (responseConnection != null) {
2280             responseConnection.releaseConnection();
2281             responseConnection = null;
2282         }
2283     }
2284 
2285     /***
2286      * Returns the {@link HostConfiguration host configuration}.
2287      * 
2288      * @return the host configuration
2289      * 
2290      * @deprecated no longer applicable
2291      */
2292     public HostConfiguration getHostConfiguration() {
2293         HostConfiguration hostconfig = new HostConfiguration();
2294         hostconfig.setHost(this.httphost);
2295         return hostconfig;
2296     }
2297     /***
2298      * Sets the {@link HostConfiguration host configuration}.
2299      * 
2300      * @param hostConfiguration The hostConfiguration to set
2301      * 
2302      * @deprecated no longer applicable
2303      */
2304     public void setHostConfiguration(final HostConfiguration hostconfig) {
2305         if (hostconfig != null) {
2306             this.httphost = new HttpHost(
2307                     hostconfig.getHost(),
2308                     hostconfig.getPort(),
2309                     hostconfig.getProtocol());
2310         } else {
2311             this.httphost = null;
2312         }
2313     }
2314 
2315     /***
2316      * Returns the {@link MethodRetryHandler retry handler} for this HTTP method
2317      * 
2318      * @return the methodRetryHandler
2319      * 
2320      * @deprecated use {@link HttpMethodParams}
2321      */
2322     public MethodRetryHandler getMethodRetryHandler() {
2323         return methodRetryHandler;
2324     }
2325 
2326     /***
2327      * Sets the {@link MethodRetryHandler retry handler} for this HTTP method
2328      * 
2329      * @param handler the methodRetryHandler to use when this method executed
2330      * 
2331      * @deprecated use {@link HttpMethodParams}
2332      */
2333     public void setMethodRetryHandler(MethodRetryHandler handler) {
2334         methodRetryHandler = handler;
2335     }
2336 
2337     /***
2338      * This method is a dirty hack intended to work around 
2339      * current (2.0) design flaw that prevents the user from
2340      * obtaining correct status code, headers and response body from the 
2341      * preceding HTTP CONNECT method.
2342      * 
2343      * TODO: Remove this crap as soon as possible
2344      */
2345     void fakeResponse(
2346         StatusLine statusline, 
2347         HeaderGroup responseheaders,
2348         InputStream responseStream
2349     ) {
2350         // set used so that the response can be read
2351         this.used = true;
2352         this.statusLine = statusline;
2353         this.responseHeaders = responseheaders;
2354         this.responseBody = null;
2355         this.responseStream = responseStream;
2356     }
2357     
2358     /***
2359      * Returns the target host {@link AuthState authentication state}
2360      * 
2361      * @return host authentication state
2362      * 
2363      * @since 3.0
2364      */
2365     public AuthState getHostAuthState() {
2366         return this.hostAuthState;
2367     }
2368 
2369     /***
2370      * Returns the proxy {@link AuthState authentication state}
2371      * 
2372      * @return host authentication state
2373      * 
2374      * @since 3.0
2375      */
2376     public AuthState getProxyAuthState() {
2377         return this.proxyAuthState;
2378     }
2379     
2380     /***
2381      * Tests whether the execution of this method has been aborted
2382      * 
2383      * @return <tt>true</tt> if the execution of this method has been aborted,
2384      *  <tt>false</tt> otherwise
2385      * 
2386      * @since 3.0
2387      */
2388     public boolean isAborted() {
2389         return this.aborted;
2390     }
2391     
2392     /***
2393      * Returns <tt>true</tt> if the HTTP has been transmitted to the target
2394      * server in its entirety, <tt>false</tt> otherwise. This flag can be useful 
2395      * for recovery logic. If the request has not been transmitted in its entirety,
2396      * it is safe to retry the failed method.
2397      * 
2398      * @return <tt>true</tt> if the request has been sent, <tt>false</tt> otherwise
2399      */
2400     public boolean isRequestSent() {
2401         return this.requestSent;
2402     }
2403     
2404 }