View Javadoc

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