View Javadoc

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