HttpMethodBase.java
   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: 539441 $
   4   * $Date: 2007-05-18 14:56:55 +0200 (Fri, 18 May 2007) $
   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: 539441 $ $Date: 2007-05-18 14:56:55 +0200 (Fri, 18 May 2007) $
  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       * Buffers the response and this method can be called several times yielding
 660       * the same result each time.
 661       * 
 662       * Note: This will cause the entire response body to be buffered in memory. A
 663       * malicious server may easily exhaust all the VM memory. It is strongly
 664       * recommended, to use getResponseAsStream if the content length of the response
 665       * is unknown or resonably large.
 666       *  
 667       * @return The response body.
 668       * 
 669       * @throws IOException If an I/O (transport) problem occurs while obtaining the 
 670       * response body.
 671       */
 672      public byte[] getResponseBody() throws IOException {
 673          if (this.responseBody == null) {
 674              InputStream instream = getResponseBodyAsStream();
 675              if (instream != null) {
 676                  long contentLength = getResponseContentLength();
 677                  if (contentLength > Integer.MAX_VALUE) { //guard below cast from overflow
 678                      throw new IOException("Content too large to be buffered: "+ contentLength +" bytes");
 679                  }
 680                  int limit = getParams().getIntParameter(HttpMethodParams.BUFFER_WARN_TRIGGER_LIMIT, 1024*1024);
 681                  if ((contentLength == -1) || (contentLength > limit)) {
 682                      LOG.warn("Going to buffer response body of large or unknown size. "
 683                              +"Using getResponseBodyAsStream instead is recommended.");
 684                  }
 685                  LOG.debug("Buffering response body");
 686                  ByteArrayOutputStream outstream = new ByteArrayOutputStream(
 687                          contentLength > 0 ? (int) contentLength : DEFAULT_INITIAL_BUFFER_SIZE);
 688                  byte[] buffer = new byte[4096];
 689                  int len;
 690                  while ((len = instream.read(buffer)) > 0) {
 691                      outstream.write(buffer, 0, len);
 692                  }
 693                  outstream.close();
 694                  setResponseStream(null);
 695                  this.responseBody = outstream.toByteArray();
 696              }
 697          }
 698          return this.responseBody;
 699      }
 700  
 701      /**
 702       * Returns the response body of the HTTP method, if any, as an array of bytes.
 703       * If response body is not available or cannot be read, returns <tt>null</tt>.
 704       * Buffers the response and this method can be called several times yielding
 705       * the same result each time.
 706       * 
 707       * Note: This will cause the entire response body to be buffered in memory. This method is
 708       * safe if the content length of the response is unknown, because the amount of memory used
 709       * is limited.<p>
 710       * 
 711       * If the response is large this method involves lots of array copying and many object 
 712       * allocations, which makes it unsuitable for high-performance / low-footprint applications.
 713       * Those applications should use {@link #getResponseBodyAsStream()}.
 714       * 
 715       * @param maxlen the maximum content length to accept (number of bytes). 
 716       * @return The response body.
 717       * 
 718       * @throws IOException If an I/O (transport) problem occurs while obtaining the 
 719       * response body.
 720       */
 721      public byte[] getResponseBody(int maxlen) throws IOException {
 722          if (maxlen < 0) throw new IllegalArgumentException("maxlen must be positive");
 723          if (this.responseBody == null) {
 724              InputStream instream = getResponseBodyAsStream();
 725              if (instream != null) {
 726                  // we might already know that the content is larger
 727                  long contentLength = getResponseContentLength();
 728                  if ((contentLength != -1) && (contentLength > maxlen)) {
 729                      throw new HttpContentTooLargeException(
 730                              "Content-Length is " + contentLength, maxlen);
 731                  }
 732                  
 733                  LOG.debug("Buffering response body");
 734                  ByteArrayOutputStream rawdata = new ByteArrayOutputStream(
 735                          contentLength > 0 ? (int) contentLength : DEFAULT_INITIAL_BUFFER_SIZE);
 736                  byte[] buffer = new byte[2048];
 737                  int pos = 0;
 738                  int len;
 739                  do {
 740                      len = instream.read(buffer, 0, Math.min(buffer.length, maxlen-pos));
 741                      if (len == -1) break;
 742                      rawdata.write(buffer, 0, len);
 743                      pos += len;
 744                  } while (pos < maxlen);
 745                  
 746                  setResponseStream(null);
 747                  // check if there is even more data
 748                  if (pos == maxlen) {
 749                      if (instream.read() != -1)
 750                          throw new HttpContentTooLargeException(
 751                                  "Content-Length not known but larger than "
 752                                  + maxlen, maxlen);
 753                  }
 754                  this.responseBody = rawdata.toByteArray();
 755              }
 756          }
 757          return this.responseBody;
 758      }
 759  
 760      /**
 761       * Returns the response body of the HTTP method, if any, as an {@link InputStream}. 
 762       * If response body is not available, returns <tt>null</tt>. If the response has been
 763       * buffered this method returns a new stream object on every call. If the response
 764       * has not been buffered the returned stream can only be read once.
 765       * 
 766       * @return The response body or <code>null</code>.
 767       * 
 768       * @throws IOException If an I/O (transport) problem occurs while obtaining the 
 769       * response body.
 770       */
 771      public InputStream getResponseBodyAsStream() throws IOException {
 772          if (responseStream != null) {
 773              return responseStream;
 774          }
 775          if (responseBody != null) {
 776              InputStream byteResponseStream = new ByteArrayInputStream(responseBody);
 777              LOG.debug("re-creating response stream from byte array");
 778              return byteResponseStream;
 779          }
 780          return null;
 781      }
 782  
 783      /**
 784       * Returns the response body of the HTTP method, if any, as a {@link String}. 
 785       * If response body is not available or cannot be read, returns <tt>null</tt>
 786       * The string conversion on the data is done using the character encoding specified
 787       * in <tt>Content-Type</tt> header. Buffers the response and this method can be 
 788       * called several times yielding the same result each time.
 789       * 
 790       * Note: This will cause the entire response body to be buffered in memory. A
 791       * malicious server may easily exhaust all the VM memory. It is strongly
 792       * recommended, to use getResponseAsStream if the content length of the response
 793       * is unknown or resonably large.
 794       * 
 795       * @return The response body or <code>null</code>.
 796       * 
 797       * @throws IOException If an I/O (transport) problem occurs while obtaining the 
 798       * response body.
 799       */
 800      public String getResponseBodyAsString() throws IOException {
 801          byte[] rawdata = null;
 802          if (responseAvailable()) {
 803              rawdata = getResponseBody();
 804          }
 805          if (rawdata != null) {
 806              return EncodingUtil.getString(rawdata, getResponseCharSet());
 807          } else {
 808              return null;
 809          }
 810      }
 811      
 812      /**
 813       * Returns the response body of the HTTP method, if any, as a {@link String}. 
 814       * If response body is not available or cannot be read, returns <tt>null</tt>
 815       * The string conversion on the data is done using the character encoding specified
 816       * in <tt>Content-Type</tt> header. Buffers the response and this method can be 
 817       * called several times yielding the same result each time.</p>
 818       * 
 819       * Note: This will cause the entire response body to be buffered in memory. This method is
 820       * safe if the content length of the response is unknown, because the amount of memory used
 821       * is limited.<p>
 822       * 
 823       * If the response is large this method involves lots of array copying and many object 
 824       * allocations, which makes it unsuitable for high-performance / low-footprint applications.
 825       * Those applications should use {@link #getResponseBodyAsStream()}.
 826       * 
 827       * @param maxlen the maximum content length to accept (number of bytes). Note that,
 828       * depending on the encoding, this is not equal to the number of characters.
 829       * @return The response body or <code>null</code>.
 830       * 
 831       * @throws IOException If an I/O (transport) problem occurs while obtaining the 
 832       * response body.
 833       */
 834      public String getResponseBodyAsString(int maxlen) throws IOException {
 835          if (maxlen < 0) throw new IllegalArgumentException("maxlen must be positive");
 836          byte[] rawdata = null;
 837          if (responseAvailable()) {
 838              rawdata = getResponseBody(maxlen);
 839          }
 840          if (rawdata != null) {
 841              return EncodingUtil.getString(rawdata, getResponseCharSet());
 842          } else {
 843              return null;
 844          }
 845      }
 846  
 847      /**
 848       * Returns an array of the response footers that the HTTP method currently has
 849       * in the order in which they were read.
 850       *
 851       * @return an array of footers
 852       */
 853      public Header[] getResponseFooters() {
 854          return getResponseTrailerHeaderGroup().getAllHeaders();
 855      }
 856  
 857      /**
 858       * Gets the response footer associated with the given name.
 859       * Footer name matching is case insensitive.
 860       * <tt>null</tt> will be returned if either <i>footerName</i> is
 861       * <tt>null</tt> or there is no matching footer for <i>footerName</i>
 862       * or there are no footers available.  If there are multiple footers
 863       * with the same name, there values will be combined with the ',' separator
 864       * as specified by RFC2616.
 865       * 
 866       * @param footerName the footer name to match
 867       * @return the matching footer
 868       */
 869      public Header getResponseFooter(String footerName) {
 870          if (footerName == null) {
 871              return null;
 872          } else {
 873              return getResponseTrailerHeaderGroup().getCondensedHeader(footerName);
 874          }
 875      }
 876  
 877      /**
 878       * Sets the response stream.
 879       * @param responseStream The new response stream.
 880       */
 881      protected void setResponseStream(InputStream responseStream) {
 882          this.responseStream = responseStream;
 883      }
 884  
 885      /**
 886       * Returns a stream from which the body of the current response may be read.
 887       * If the method has not yet been executed, if <code>responseBodyConsumed</code>
 888       * has been called, or if the stream returned by a previous call has been closed,
 889       * <code>null</code> will be returned.
 890       *
 891       * @return the current response stream
 892       */
 893      protected InputStream getResponseStream() {
 894          return responseStream;
 895      }
 896      
 897      /**
 898       * Returns the status text (or "reason phrase") associated with the latest
 899       * response.
 900       * 
 901       * @return The status text.
 902       */
 903      public String getStatusText() {
 904          return statusLine.getReasonPhrase();
 905      }
 906  
 907      /**
 908       * Defines how strictly HttpClient follows the HTTP protocol specification  
 909       * (RFC 2616 and other relevant RFCs). In the strict mode HttpClient precisely
 910       * implements the requirements of the specification, whereas in non-strict mode 
 911       * it attempts to mimic the exact behaviour of commonly used HTTP agents, 
 912       * which many HTTP servers expect.
 913       * 
 914       * @param strictMode <tt>true</tt> for strict mode, <tt>false</tt> otherwise
 915       * 
 916       * @deprecated Use {@link org.apache.commons.httpclient.params.HttpParams#setParameter(String, Object)}
 917       * to exercise a more granular control over HTTP protocol strictness.
 918       */
 919      public void setStrictMode(boolean strictMode) {
 920          if (strictMode) {
 921              this.params.makeStrict();
 922          } else {
 923              this.params.makeLenient();
 924          }
 925      }
 926  
 927      /**
 928       * @deprecated Use {@link org.apache.commons.httpclient.params.HttpParams#setParameter(String, Object)}
 929       * to exercise a more granular control over HTTP protocol strictness.
 930       *
 931       * @return <tt>false</tt>
 932       */
 933      public boolean isStrictMode() {
 934          return false;
 935      }
 936  
 937      /**
 938       * Adds the specified request header, NOT overwriting any previous value.
 939       * Note that header-name matching is case insensitive.
 940       *
 941       * @param headerName the header's name
 942       * @param headerValue the header's value
 943       */
 944      public void addRequestHeader(String headerName, String headerValue) {
 945          addRequestHeader(new Header(headerName, headerValue));
 946      }
 947  
 948      /**
 949       * Tests if the connection should be force-closed when no longer needed.
 950       * 
 951       * @return <code>true</code> if the connection must be closed
 952       */
 953      protected boolean isConnectionCloseForced() {
 954          return this.connectionCloseForced;
 955      }
 956  
 957      /**
 958       * Sets whether or not the connection should be force-closed when no longer 
 959       * needed. This value should only be set to <code>true</code> in abnormal 
 960       * circumstances, such as HTTP protocol violations. 
 961       * 
 962       * @param b <code>true</code> if the connection must be closed, <code>false</code>
 963       * otherwise.
 964       */
 965      protected void setConnectionCloseForced(boolean b) {
 966          if (LOG.isDebugEnabled()) {
 967              LOG.debug("Force-close connection: " + b);
 968          }
 969          this.connectionCloseForced = b;
 970      }
 971  
 972      /**
 973       * Tests if the connection should be closed after the method has been executed.
 974       * The connection will be left open when using HTTP/1.1 or if <tt>Connection: 
 975       * keep-alive</tt> header was sent.
 976       * 
 977       * @param conn the connection in question
 978       * 
 979       * @return boolean true if we should close the connection.
 980       */
 981      protected boolean shouldCloseConnection(HttpConnection conn) {
 982          // Connection must be closed due to an abnormal circumstance 
 983          if (isConnectionCloseForced()) {
 984              LOG.debug("Should force-close connection.");
 985              return true;
 986          }
 987  
 988          Header connectionHeader = null;
 989          // In case being connected via a proxy server
 990          if (!conn.isTransparent()) {
 991              // Check for 'proxy-connection' directive
 992              connectionHeader = responseHeaders.getFirstHeader("proxy-connection");
 993          }
 994          // In all cases Check for 'connection' directive
 995          // some non-complaint proxy servers send it instread of
 996          // expected 'proxy-connection' directive
 997          if (connectionHeader == null) {
 998              connectionHeader = responseHeaders.getFirstHeader("connection");
 999          }
1000          // In case the response does not contain any explict connection
1001          // directives, check whether the request does
1002          if (connectionHeader == null) {
1003              connectionHeader = requestHeaders.getFirstHeader("connection");
1004          }
1005          if (connectionHeader != null) {
1006              if (connectionHeader.getValue().equalsIgnoreCase("close")) {
1007                  if (LOG.isDebugEnabled()) {
1008                      LOG.debug("Should close connection in response to directive: " 
1009                          + connectionHeader.getValue());
1010                  }
1011                  return true;
1012              } else if (connectionHeader.getValue().equalsIgnoreCase("keep-alive")) {
1013                  if (LOG.isDebugEnabled()) {
1014                      LOG.debug("Should NOT close connection in response to directive: " 
1015                          + connectionHeader.getValue());
1016                  }
1017                  return false;
1018              } else {
1019                  if (LOG.isDebugEnabled()) {
1020                      LOG.debug("Unknown directive: " + connectionHeader.toExternalForm());
1021                  }
1022              }
1023          }
1024          LOG.debug("Resorting to protocol version default close connection policy");
1025          // missing or invalid connection header, do the default
1026          if (this.effectiveVersion.greaterEquals(HttpVersion.HTTP_1_1)) {
1027              if (LOG.isDebugEnabled()) {
1028                  LOG.debug("Should NOT close connection, using " + this.effectiveVersion.toString());
1029              }
1030          } else {
1031              if (LOG.isDebugEnabled()) {
1032                  LOG.debug("Should close connection, using " + this.effectiveVersion.toString());
1033              }
1034          }
1035          return this.effectiveVersion.lessEquals(HttpVersion.HTTP_1_0);
1036      }
1037      
1038      /**
1039       * Tests if the this method is ready to be executed.
1040       * 
1041       * @param state the {@link HttpState state} information associated with this method
1042       * @param conn the {@link HttpConnection connection} to be used
1043       * @throws HttpException If the method is in invalid state.
1044       */
1045      private void checkExecuteConditions(HttpState state, HttpConnection conn)
1046      throws HttpException {
1047  
1048          if (state == null) {
1049              throw new IllegalArgumentException("HttpState parameter may not be null");
1050          }
1051          if (conn == null) {
1052              throw new IllegalArgumentException("HttpConnection parameter may not be null");
1053          }
1054          if (this.aborted) {
1055              throw new IllegalStateException("Method has been aborted");
1056          }
1057          if (!validate()) {
1058              throw new ProtocolException("HttpMethodBase object not valid");
1059          }
1060      }
1061  
1062      /**
1063       * Executes this method using the specified <code>HttpConnection</code> and
1064       * <code>HttpState</code>. 
1065       *
1066       * @param state {@link HttpState state} information to associate with this
1067       *        request. Must be non-null.
1068       * @param conn the {@link HttpConnection connection} to used to execute
1069       *        this HTTP method. Must be non-null.
1070       *
1071       * @return the integer status code if one was obtained, or <tt>-1</tt>
1072       *
1073       * @throws IOException if an I/O (transport) error occurs
1074       * @throws HttpException  if a protocol exception occurs.
1075       */
1076      public int execute(HttpState state, HttpConnection conn)
1077          throws HttpException, IOException {
1078                  
1079          LOG.trace("enter HttpMethodBase.execute(HttpState, HttpConnection)");
1080  
1081          // this is our connection now, assign it to a local variable so 
1082          // that it can be released later
1083          this.responseConnection = conn;
1084  
1085          checkExecuteConditions(state, conn);
1086          this.statusLine = null;
1087          this.connectionCloseForced = false;
1088  
1089          conn.setLastResponseInputStream(null);
1090  
1091          // determine the effective protocol version
1092          if (this.effectiveVersion == null) {
1093              this.effectiveVersion = this.params.getVersion(); 
1094          }
1095  
1096          writeRequest(state, conn);
1097          this.requestSent = true;
1098          readResponse(state, conn);
1099          // the method has successfully executed
1100          used = true; 
1101  
1102          return statusLine.getStatusCode();
1103      }
1104  
1105      /**
1106       * Aborts the execution of this method.
1107       * 
1108       * @since 3.0
1109       */
1110      public void abort() {
1111          if (this.aborted) {
1112              return;
1113          }
1114          this.aborted = true;
1115          HttpConnection conn = this.responseConnection; 
1116          if (conn != null) {
1117              conn.close();
1118          }
1119      }
1120  
1121      /**
1122       * Returns <tt>true</tt> if the HTTP method has been already {@link #execute executed},
1123       * but not {@link #recycle recycled}.
1124       * 
1125       * @return <tt>true</tt> if the method has been executed, <tt>false</tt> otherwise
1126       */
1127      public boolean hasBeenUsed() {
1128          return used;
1129      }
1130  
1131      /**
1132       * Recycles the HTTP method so that it can be used again.
1133       * Note that all of the instance variables will be reset
1134       * once this method has been called. This method will also
1135       * release the connection being used by this HTTP method.
1136       * 
1137       * @see #releaseConnection()
1138       * 
1139       * @deprecated no longer supported and will be removed in the future
1140       *             version of HttpClient
1141       */
1142      public void recycle() {
1143          LOG.trace("enter HttpMethodBase.recycle()");
1144  
1145          releaseConnection();
1146  
1147          path = null;
1148          followRedirects = false;
1149          doAuthentication = true;
1150          queryString = null;
1151          getRequestHeaderGroup().clear();
1152          getResponseHeaderGroup().clear();
1153          getResponseTrailerHeaderGroup().clear();
1154          statusLine = null;
1155          effectiveVersion = null;
1156          aborted = false;
1157          used = false;
1158          params = new HttpMethodParams();
1159          responseBody = null;
1160          recoverableExceptionCount = 0;
1161          connectionCloseForced = false;
1162          hostAuthState.invalidate();
1163          proxyAuthState.invalidate();
1164          cookiespec = null;
1165          requestSent = false;
1166      }
1167  
1168      /**
1169       * Releases the connection being used by this HTTP method. In particular the
1170       * connection is used to read the response(if there is one) and will be held
1171       * until the response has been read. If the connection can be reused by other 
1172       * HTTP methods it is NOT closed at this point.
1173       *
1174       * @since 2.0
1175       */
1176      public void releaseConnection() {
1177          try {
1178              if (this.responseStream != null) {
1179                  try {
1180                      // FYI - this may indirectly invoke responseBodyConsumed.
1181                      this.responseStream.close();
1182                  } catch (IOException ignore) {
1183                  }
1184              }
1185          } finally {
1186              ensureConnectionRelease();
1187          }
1188      }
1189  
1190      /**
1191       * Remove the request header associated with the given name. Note that
1192       * header-name matching is case insensitive.
1193       *
1194       * @param headerName the header name
1195       */
1196      public void removeRequestHeader(String headerName) {
1197          
1198          Header[] headers = getRequestHeaderGroup().getHeaders(headerName);
1199          for (int i = 0; i < headers.length; i++) {
1200              getRequestHeaderGroup().removeHeader(headers[i]);
1201          }
1202          
1203      }
1204      
1205      /**
1206       * Removes the given request header.
1207       * 
1208       * @param header the header
1209       */
1210      public void removeRequestHeader(final Header header) {
1211          if (header == null) {
1212              return;
1213          }
1214          getRequestHeaderGroup().removeHeader(header);
1215      }
1216  
1217      // ---------------------------------------------------------------- Queries
1218  
1219      /**
1220       * Returns <tt>true</tt> the method is ready to execute, <tt>false</tt> otherwise.
1221       * 
1222       * @return This implementation always returns <tt>true</tt>.
1223       */
1224      public boolean validate() {
1225          return true;
1226      }
1227  
1228  
1229      /** 
1230       * Returns the actual cookie policy
1231       * 
1232       * @param state HTTP state. TODO: to be removed in the future
1233       * 
1234       * @return cookie spec
1235       */
1236      private CookieSpec getCookieSpec(final HttpState state) {
1237          if (this.cookiespec == null) {
1238              int i = state.getCookiePolicy();
1239              if (i == -1) {
1240                  this.cookiespec = CookiePolicy.getCookieSpec(this.params.getCookiePolicy());
1241              } else {
1242                  this.cookiespec = CookiePolicy.getSpecByPolicy(i);
1243              }
1244              this.cookiespec.setValidDateFormats(
1245                      (Collection)this.params.getParameter(HttpMethodParams.DATE_PATTERNS));
1246          }
1247          return this.cookiespec;
1248      }
1249  
1250      /**
1251       * Generates <tt>Cookie</tt> request headers for those {@link Cookie cookie}s
1252       * that match the given host, port and path.
1253       *
1254       * @param state the {@link HttpState state} information associated with this method
1255       * @param conn the {@link HttpConnection connection} used to execute
1256       *        this HTTP method
1257       *
1258       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1259       *                     can be recovered from.
1260       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1261       *                    cannot be recovered from.
1262       */
1263      protected void addCookieRequestHeader(HttpState state, HttpConnection conn)
1264          throws IOException, HttpException {
1265  
1266          LOG.trace("enter HttpMethodBase.addCookieRequestHeader(HttpState, "
1267                    + "HttpConnection)");
1268  
1269          Header[] cookieheaders = getRequestHeaderGroup().getHeaders("Cookie");
1270          for (int i = 0; i < cookieheaders.length; i++) {
1271              Header cookieheader = cookieheaders[i];
1272              if (cookieheader.isAutogenerated()) {
1273                  getRequestHeaderGroup().removeHeader(cookieheader);
1274              }
1275          }
1276  
1277          CookieSpec matcher = getCookieSpec(state);
1278          String host = this.params.getVirtualHost();
1279          if (host == null) {
1280              host = conn.getHost();
1281          }
1282          Cookie[] cookies = matcher.match(host, conn.getPort(),
1283              getPath(), conn.isSecure(), state.getCookies());
1284          if ((cookies != null) && (cookies.length > 0)) {
1285              if (getParams().isParameterTrue(HttpMethodParams.SINGLE_COOKIE_HEADER)) {
1286                  // In strict mode put all cookies on the same header
1287                  String s = matcher.formatCookies(cookies);
1288                  getRequestHeaderGroup().addHeader(new Header("Cookie", s, true));
1289              } else {
1290                  // In non-strict mode put each cookie on a separate header
1291                  for (int i = 0; i < cookies.length; i++) {
1292                      String s = matcher.formatCookie(cookies[i]);
1293                      getRequestHeaderGroup().addHeader(new Header("Cookie", s, true));
1294                  }
1295              }
1296              if (matcher instanceof CookieVersionSupport) {
1297                  CookieVersionSupport versupport = (CookieVersionSupport) matcher;
1298                  int ver = versupport.getVersion();
1299                  boolean needVersionHeader = false;
1300                  for (int i = 0; i < cookies.length; i++) {
1301                      if (ver != cookies[i].getVersion()) {
1302                          needVersionHeader = true;
1303                      }
1304                  }
1305                  if (needVersionHeader) {
1306                      // Advertise cookie version support
1307                      getRequestHeaderGroup().addHeader(versupport.getVersionHeader());
1308                  }
1309              }
1310          }
1311      }
1312  
1313      /**
1314       * Generates <tt>Host</tt> request header, as long as no <tt>Host</tt> request
1315       * header already exists.
1316       *
1317       * @param state the {@link HttpState state} information associated with this method
1318       * @param conn the {@link HttpConnection connection} used to execute
1319       *        this HTTP method
1320       *
1321       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1322       *                     can be recovered from.
1323       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1324       *                    cannot be recovered from.
1325       */
1326      protected void addHostRequestHeader(HttpState state, HttpConnection conn)
1327      throws IOException, HttpException {
1328          LOG.trace("enter HttpMethodBase.addHostRequestHeader(HttpState, "
1329                    + "HttpConnection)");
1330  
1331          // Per 19.6.1.1 of RFC 2616, it is legal for HTTP/1.0 based
1332          // applications to send the Host request-header.
1333          // TODO: Add the ability to disable the sending of this header for
1334          //       HTTP/1.0 requests.
1335          String host = this.params.getVirtualHost();
1336          if (host != null) {
1337              LOG.debug("Using virtual host name: " + host);
1338          } else {
1339              host = conn.getHost();
1340          }
1341          int port = conn.getPort();
1342  
1343          // Note: RFC 2616 uses the term "internet host name" for what goes on the
1344          // host line.  It would seem to imply that host should be blank if the
1345          // host is a number instead of an name.  Based on the behavior of web
1346          // browsers, and the fact that RFC 2616 never defines the phrase "internet
1347          // host name", and the bad behavior of HttpClient that follows if we
1348          // send blank, I interpret this as a small misstatement in the RFC, where
1349          // they meant to say "internet host".  So IP numbers get sent as host
1350          // entries too. -- Eric Johnson 12/13/2002
1351          if (LOG.isDebugEnabled()) {
1352              LOG.debug("Adding Host request header");
1353          }
1354  
1355          //appends the port only if not using the default port for the protocol
1356          if (conn.getProtocol().getDefaultPort() != port) {
1357              host += (":" + port);
1358          }
1359  
1360          setRequestHeader("Host", host);
1361      }
1362  
1363      /**
1364       * Generates <tt>Proxy-Connection: Keep-Alive</tt> request header when 
1365       * communicating via a proxy server.
1366       *
1367       * @param state the {@link HttpState state} information associated with this method
1368       * @param conn the {@link HttpConnection connection} used to execute
1369       *        this HTTP method
1370       *
1371       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1372       *                     can be recovered from.
1373       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1374       *                    cannot be recovered from.
1375       */
1376      protected void addProxyConnectionHeader(HttpState state,
1377                                              HttpConnection conn)
1378      throws IOException, HttpException {
1379          LOG.trace("enter HttpMethodBase.addProxyConnectionHeader("
1380                    + "HttpState, HttpConnection)");
1381          if (!conn.isTransparent()) {
1382              if (getRequestHeader("Proxy-Connection") == null) {
1383                  addRequestHeader("Proxy-Connection", "Keep-Alive");
1384              }
1385          }
1386      }
1387  
1388      /**
1389       * Generates all the required request {@link Header header}s 
1390       * to be submitted via the given {@link HttpConnection connection}.
1391       *
1392       * <p>
1393       * This implementation adds <tt>User-Agent</tt>, <tt>Host</tt>,
1394       * <tt>Cookie</tt>, <tt>Authorization</tt>, <tt>Proxy-Authorization</tt>
1395       * and <tt>Proxy-Connection</tt> headers, when appropriate.
1396       * </p>
1397       *
1398       * <p>
1399       * Subclasses may want to override this method to to add additional
1400       * headers, and may choose to invoke this implementation (via
1401       * <tt>super</tt>) to add the "standard" headers.
1402       * </p>
1403       *
1404       * @param state the {@link HttpState state} information associated with this method
1405       * @param conn the {@link HttpConnection connection} used to execute
1406       *        this HTTP method
1407       *
1408       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1409       *                     can be recovered from.
1410       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1411       *                    cannot be recovered from.
1412       *
1413       * @see #writeRequestHeaders
1414       */
1415      protected void addRequestHeaders(HttpState state, HttpConnection conn)
1416      throws IOException, HttpException {
1417          LOG.trace("enter HttpMethodBase.addRequestHeaders(HttpState, "
1418              + "HttpConnection)");
1419  
1420          addUserAgentRequestHeader(state, conn);
1421          addHostRequestHeader(state, conn);
1422          addCookieRequestHeader(state, conn);
1423          addProxyConnectionHeader(state, conn);
1424      }
1425  
1426      /**
1427       * Generates default <tt>User-Agent</tt> request header, as long as no
1428       * <tt>User-Agent</tt> request header already exists.
1429       *
1430       * @param state the {@link HttpState state} information associated with this method
1431       * @param conn the {@link HttpConnection connection} used to execute
1432       *        this HTTP method
1433       *
1434       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1435       *                     can be recovered from.
1436       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1437       *                    cannot be recovered from.
1438       */
1439      protected void addUserAgentRequestHeader(HttpState state,
1440                                               HttpConnection conn)
1441      throws IOException, HttpException {
1442          LOG.trace("enter HttpMethodBase.addUserAgentRequestHeaders(HttpState, "
1443              + "HttpConnection)");
1444  
1445          if (getRequestHeader("User-Agent") == null) {
1446              String agent = (String)getParams().getParameter(HttpMethodParams.USER_AGENT);
1447              if (agent == null) {
1448                  agent = "Jakarta Commons-HttpClient";
1449              }
1450              setRequestHeader("User-Agent", agent);
1451          }
1452      }
1453  
1454      /**
1455       * Throws an {@link IllegalStateException} if the HTTP method has been already
1456       * {@link #execute executed}, but not {@link #recycle recycled}.
1457       *
1458       * @throws IllegalStateException if the method has been used and not
1459       *      recycled
1460       */
1461      protected void checkNotUsed() throws IllegalStateException {
1462          if (used) {
1463              throw new IllegalStateException("Already used.");
1464          }
1465      }
1466  
1467      /**
1468       * Throws an {@link IllegalStateException} if the HTTP method has not been
1469       * {@link #execute executed} since last {@link #recycle recycle}.
1470       *
1471       *
1472       * @throws IllegalStateException if not used
1473       */
1474      protected void checkUsed()  throws IllegalStateException {
1475          if (!used) {
1476              throw new IllegalStateException("Not Used.");
1477          }
1478      }
1479  
1480      // ------------------------------------------------- Static Utility Methods
1481  
1482      /**
1483       * Generates HTTP request line according to the specified attributes.
1484       *
1485       * @param connection the {@link HttpConnection connection} used to execute
1486       *        this HTTP method
1487       * @param name the method name generate a request for
1488       * @param requestPath the path string for the request
1489       * @param query the query string for the request
1490       * @param version the protocol version to use (e.g. HTTP/1.0)
1491       *
1492       * @return HTTP request line
1493       */
1494      protected static String generateRequestLine(HttpConnection connection,
1495          String name, String requestPath, String query, String version) {
1496          LOG.trace("enter HttpMethodBase.generateRequestLine(HttpConnection, "
1497              + "String, String, String, String)");
1498  
1499          StringBuffer buf = new StringBuffer();
1500          // Append method name
1501          buf.append(name);
1502          buf.append(" ");
1503          // Absolute or relative URL?
1504          if (!connection.isTransparent()) {
1505              Protocol protocol = connection.getProtocol();
1506              buf.append(protocol.getScheme().toLowerCase());
1507              buf.append("://");
1508              buf.append(connection.getHost());
1509              if ((connection.getPort() != -1) 
1510                  && (connection.getPort() != protocol.getDefaultPort())
1511              ) {
1512                  buf.append(":");
1513                  buf.append(connection.getPort());
1514              }
1515          }
1516          // Append path, if any
1517          if (requestPath == null) {
1518              buf.append("/");
1519          } else {
1520              if (!connection.isTransparent() && !requestPath.startsWith("/")) {
1521                  buf.append("/");
1522              }
1523              buf.append(requestPath);
1524          }
1525          // Append query, if any
1526          if (query != null) {
1527              if (query.indexOf("?") != 0) {
1528                  buf.append("?");
1529              }
1530              buf.append(query);
1531          }
1532          // Append protocol
1533          buf.append(" ");
1534          buf.append(version);
1535          buf.append("\r\n");
1536          
1537          return buf.toString();
1538      }
1539      
1540      /**
1541       * This method is invoked immediately after 
1542       * {@link #readResponseBody(HttpState,HttpConnection)} and can be overridden by
1543       * sub-classes in order to provide custom body processing.
1544       *
1545       * <p>
1546       * This implementation does nothing.
1547       * </p>
1548       *
1549       * @param state the {@link HttpState state} information associated with this method
1550       * @param conn the {@link HttpConnection connection} used to execute
1551       *        this HTTP method
1552       *
1553       * @see #readResponse
1554       * @see #readResponseBody
1555       */
1556      protected void processResponseBody(HttpState state, HttpConnection conn) {
1557      }
1558  
1559      /**
1560       * This method is invoked immediately after 
1561       * {@link #readResponseHeaders(HttpState,HttpConnection)} and can be overridden by
1562       * sub-classes in order to provide custom response headers processing.
1563  
1564       * <p>
1565       * This implementation will handle the <tt>Set-Cookie</tt> and
1566       * <tt>Set-Cookie2</tt> headers, if any, adding the relevant cookies to
1567       * the given {@link HttpState}.
1568       * </p>
1569       *
1570       * @param state the {@link HttpState state} information associated with this method
1571       * @param conn the {@link HttpConnection connection} used to execute
1572       *        this HTTP method
1573       *
1574       * @see #readResponse
1575       * @see #readResponseHeaders
1576       */
1577      protected void processResponseHeaders(HttpState state,
1578          HttpConnection conn) {
1579          LOG.trace("enter HttpMethodBase.processResponseHeaders(HttpState, "
1580              + "HttpConnection)");
1581  
1582          CookieSpec parser = getCookieSpec(state);
1583  
1584          // process set-cookie headers
1585          Header[] headers = getResponseHeaderGroup().getHeaders("set-cookie");
1586          processCookieHeaders(parser, headers, state, conn);
1587  
1588          // see if the cookie spec supports cookie versioning.
1589          if (parser instanceof CookieVersionSupport) {
1590              CookieVersionSupport versupport = (CookieVersionSupport) parser;
1591              if (versupport.getVersion() > 0) {
1592                  // process set-cookie2 headers.
1593                  // Cookie2 will replace equivalent Cookie instances
1594                  headers = getResponseHeaderGroup().getHeaders("set-cookie2");
1595                  processCookieHeaders(parser, headers, state, conn);
1596              }
1597          }
1598      }
1599  
1600      /**
1601       * This method processes the specified cookie headers. It is invoked from
1602       * within {@link #processResponseHeaders(HttpState,HttpConnection)}
1603       *
1604       * @param headers cookie {@link Header}s to be processed
1605       * @param state the {@link HttpState state} information associated with
1606       *        this HTTP method
1607       * @param conn the {@link HttpConnection connection} used to execute
1608       *        this HTTP method
1609       */
1610      protected void processCookieHeaders(
1611              final CookieSpec parser, 
1612              final Header[] headers, 
1613              final HttpState state, 
1614              final HttpConnection conn) {
1615          LOG.trace("enter HttpMethodBase.processCookieHeaders(Header[], HttpState, "
1616                    + "HttpConnection)");
1617  
1618          String host = this.params.getVirtualHost();
1619          if (host == null) {
1620              host = conn.getHost();
1621          }
1622          for (int i = 0; i < headers.length; i++) {
1623              Header header = headers[i];
1624              Cookie[] cookies = null;
1625              try {
1626                  cookies = parser.parse(
1627                    host,
1628                    conn.getPort(),
1629                    getPath(),
1630                    conn.isSecure(),
1631                    header);
1632              } catch (MalformedCookieException e) {
1633                  if (LOG.isWarnEnabled()) {
1634                      LOG.warn("Invalid cookie header: \"" 
1635                          + header.getValue() 
1636                          + "\". " + e.getMessage());
1637                  }
1638              }
1639              if (cookies != null) {
1640                  for (int j = 0; j < cookies.length; j++) {
1641                      Cookie cookie = cookies[j];
1642                      try {
1643                          parser.validate(
1644                            host,
1645                            conn.getPort(),
1646                            getPath(),
1647                            conn.isSecure(),
1648                            cookie);
1649                          state.addCookie(cookie);
1650                          if (LOG.isDebugEnabled()) {
1651                              LOG.debug("Cookie accepted: \"" 
1652                                  + parser.formatCookie(cookie) + "\"");
1653                          }
1654                      } catch (MalformedCookieException e) {
1655                          if (LOG.isWarnEnabled()) {
1656                              LOG.warn("Cookie rejected: \"" + parser.formatCookie(cookie) 
1657                                  + "\". " + e.getMessage());
1658                          }
1659                      }
1660                  }
1661              }
1662          }
1663      }
1664  
1665      /**
1666       * This method is invoked immediately after 
1667       * {@link #readStatusLine(HttpState,HttpConnection)} and can be overridden by
1668       * sub-classes in order to provide custom response status line processing.
1669       *
1670       * @param state the {@link HttpState state} information associated with this method
1671       * @param conn the {@link HttpConnection connection} used to execute
1672       *        this HTTP method
1673       *
1674       * @see #readResponse
1675       * @see #readStatusLine
1676       */
1677      protected void processStatusLine(HttpState state, HttpConnection conn) {
1678      }
1679  
1680      /**
1681       * Reads the response from the given {@link HttpConnection connection}.
1682       *
1683       * <p>
1684       * The response is processed as the following sequence of actions:
1685       *
1686       * <ol>
1687       * <li>
1688       * {@link #readStatusLine(HttpState,HttpConnection)} is
1689       * invoked to read the request line.
1690       * </li>
1691       * <li>
1692       * {@link #processStatusLine(HttpState,HttpConnection)}
1693       * is invoked, allowing the method to process the status line if
1694       * desired.
1695       * </li>
1696       * <li>
1697       * {@link #readResponseHeaders(HttpState,HttpConnection)} is invoked to read
1698       * the associated headers.
1699       * </li>
1700       * <li>
1701       * {@link #processResponseHeaders(HttpState,HttpConnection)} is invoked, allowing
1702       * the method to process the headers if desired.
1703       * </li>
1704       * <li>
1705       * {@link #readResponseBody(HttpState,HttpConnection)} is
1706       * invoked to read the associated body (if any).
1707       * </li>
1708       * <li>
1709       * {@link #processResponseBody(HttpState,HttpConnection)} is invoked, allowing the
1710       * method to process the response body if desired.
1711       * </li>
1712       * </ol>
1713       *
1714       * Subclasses may want to override one or more of the above methods to to
1715       * customize the processing. (Or they may choose to override this method
1716       * if dramatically different processing is required.)
1717       * </p>
1718       *
1719       * @param state the {@link HttpState state} information associated with this method
1720       * @param conn the {@link HttpConnection connection} used to execute
1721       *        this HTTP method
1722       *
1723       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1724       *                     can be recovered from.
1725       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1726       *                    cannot be recovered from.
1727       */
1728      protected void readResponse(HttpState state, HttpConnection conn)
1729      throws IOException, HttpException {
1730          LOG.trace(
1731          "enter HttpMethodBase.readResponse(HttpState, HttpConnection)");
1732          // Status line & line may have already been received
1733          // if 'expect - continue' handshake has been used
1734          while (this.statusLine == null) {
1735              readStatusLine(state, conn);
1736              processStatusLine(state, conn);
1737              readResponseHeaders(state, conn);
1738              processResponseHeaders(state, conn);
1739              
1740              int status = this.statusLine.getStatusCode();
1741              if ((status >= 100) && (status < 200)) {
1742                  if (LOG.isInfoEnabled()) {
1743                      LOG.info("Discarding unexpected response: " + this.statusLine.toString()); 
1744                  }
1745                  this.statusLine = null;
1746              }
1747          }
1748          readResponseBody(state, conn);
1749          processResponseBody(state, conn);
1750      }
1751  
1752      /**
1753       * Read the response body from the given {@link HttpConnection}.
1754       *
1755       * <p>
1756       * The current implementation wraps the socket level stream with
1757       * an appropriate stream for the type of response (chunked, content-length,
1758       * or auto-close).  If there is no response body, the connection associated
1759       * with the request will be returned to the connection manager.
1760       * </p>
1761       *
1762       * <p>
1763       * Subclasses may want to override this method to to customize the
1764       * processing.
1765       * </p>
1766       *
1767       * @param state the {@link HttpState state} information associated with this method
1768       * @param conn the {@link HttpConnection connection} used to execute
1769       *        this HTTP method
1770       *
1771       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1772       *                     can be recovered from.
1773       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1774       *                    cannot be recovered from.
1775       *
1776       * @see #readResponse
1777       * @see #processResponseBody
1778       */
1779      protected void readResponseBody(HttpState state, HttpConnection conn)
1780      throws IOException, HttpException {
1781          LOG.trace(
1782              "enter HttpMethodBase.readResponseBody(HttpState, HttpConnection)");
1783  
1784          // assume we are not done with the connection if we get a stream
1785          InputStream stream = readResponseBody(conn);
1786          if (stream == null) {
1787              // done using the connection!
1788              responseBodyConsumed();
1789          } else {
1790              conn.setLastResponseInputStream(stream);
1791              setResponseStream(stream);
1792          }
1793      }
1794  
1795      /**
1796       * Returns the response body as an {@link InputStream input stream}
1797       * corresponding to the values of the <tt>Content-Length</tt> and 
1798       * <tt>Transfer-Encoding</tt> headers. If no response body is available
1799       * returns <tt>null</tt>.
1800       * <p>
1801       *
1802       * @see #readResponse
1803       * @see #processResponseBody
1804       *
1805       * @param conn the {@link HttpConnection connection} used to execute
1806       *        this HTTP method
1807       *
1808       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1809       *                     can be recovered from.
1810       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1811       *                    cannot be recovered from.
1812       */
1813      private InputStream readResponseBody(HttpConnection conn)
1814          throws HttpException, IOException {
1815  
1816          LOG.trace("enter HttpMethodBase.readResponseBody(HttpConnection)");
1817  
1818          responseBody = null;
1819          InputStream is = conn.getResponseInputStream();
1820          if (Wire.CONTENT_WIRE.enabled()) {
1821              is = new WireLogInputStream(is, Wire.CONTENT_WIRE);
1822          }
1823          boolean canHaveBody = canResponseHaveBody(statusLine.getStatusCode());
1824          InputStream result = null;
1825          Header transferEncodingHeader = responseHeaders.getFirstHeader("Transfer-Encoding");
1826          // We use Transfer-Encoding if present and ignore Content-Length.
1827          // RFC2616, 4.4 item number 3
1828          if (transferEncodingHeader != null) {
1829  
1830              String transferEncoding = transferEncodingHeader.getValue();
1831              if (!"chunked".equalsIgnoreCase(transferEncoding) 
1832                  && !"identity".equalsIgnoreCase(transferEncoding)) {
1833                  if (LOG.isWarnEnabled()) {
1834                      LOG.warn("Unsupported transfer encoding: " + transferEncoding);
1835                  }
1836              }
1837              HeaderElement[] encodings = transferEncodingHeader.getElements();
1838              // The chunked encoding must be the last one applied
1839              // RFC2616, 14.41
1840              int len = encodings.length;            
1841              if ((len > 0) && ("chunked".equalsIgnoreCase(encodings[len - 1].getName()))) { 
1842                  // if response body is empty
1843                  if (conn.isResponseAvailable(conn.getParams().getSoTimeout())) {
1844                      result = new ChunkedInputStream(is, this);
1845                  } else {
1846                      if (getParams().isParameterTrue(HttpMethodParams.STRICT_TRANSFER_ENCODING)) {
1847                          throw new ProtocolException("Chunk-encoded body declared but not sent");
1848                      } else {
1849                          LOG.warn("Chunk-encoded body missing");
1850                      }
1851                  }
1852              } else {
1853                  LOG.info("Response content is not chunk-encoded");
1854                  // The connection must be terminated by closing 
1855                  // the socket as per RFC 2616, 3.6
1856                  setConnectionCloseForced(true);
1857                  result = is;  
1858              }
1859          } else {
1860              long expectedLength = getResponseContentLength();
1861              if (expectedLength == -1) {
1862                  if (canHaveBody && this.effectiveVersion.greaterEquals(HttpVersion.HTTP_1_1)) {
1863                      Header connectionHeader = responseHeaders.getFirstHeader("Connection");
1864                      String connectionDirective = null;
1865                      if (connectionHeader != null) {
1866                          connectionDirective = connectionHeader.getValue();
1867                      }
1868                      if (!"close".equalsIgnoreCase(connectionDirective)) {
1869                          LOG.info("Response content length is not known");
1870                          setConnectionCloseForced(true);
1871                      }
1872                  }
1873                  result = is;            
1874              } else {
1875                  result = new ContentLengthInputStream(is, expectedLength);
1876              }
1877          } 
1878  
1879          // See if the response is supposed to have a response body
1880          if (!canHaveBody) {
1881              result = null;
1882          }
1883          // if there is a result - ALWAYS wrap it in an observer which will
1884          // close the underlying stream as soon as it is consumed, and notify
1885          // the watcher that the stream has been consumed.
1886          if (result != null) {
1887  
1888              result = new AutoCloseInputStream(
1889                  result,
1890                  new ResponseConsumedWatcher() {
1891                      public void responseConsumed() {
1892                          responseBodyConsumed();
1893                      }
1894                  }
1895              );
1896          }
1897  
1898          return result;
1899      }
1900  
1901      /**
1902       * Reads the response headers from the given {@link HttpConnection connection}.
1903       *
1904       * <p>
1905       * Subclasses may want to override this method to to customize the
1906       * processing.
1907       * </p>
1908       *
1909       * <p>
1910       * "It must be possible to combine the multiple header fields into one
1911       * "field-name: field-value" pair, without changing the semantics of the
1912       * message, by appending each subsequent field-value to the first, each
1913       * separated by a comma." - HTTP/1.0 (4.3)
1914       * </p>
1915       *
1916       * @param state the {@link HttpState state} information associated with this method
1917       * @param conn the {@link HttpConnection connection} used to execute
1918       *        this HTTP method
1919       *
1920       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1921       *                     can be recovered from.
1922       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1923       *                    cannot be recovered from.
1924       *
1925       * @see #readResponse
1926       * @see #processResponseHeaders
1927       */
1928      protected void readResponseHeaders(HttpState state, HttpConnection conn)
1929      throws IOException, HttpException {
1930          LOG.trace("enter HttpMethodBase.readResponseHeaders(HttpState,"
1931              + "HttpConnection)");
1932  
1933          getResponseHeaderGroup().clear();
1934          
1935          Header[] headers = HttpParser.parseHeaders(
1936              conn.getResponseInputStream(), getParams().getHttpElementCharset());
1937          // Wire logging moved to HttpParser
1938          getResponseHeaderGroup().setHeaders(headers);
1939      }
1940  
1941      /**
1942       * Read the status line from the given {@link HttpConnection}, setting my
1943       * {@link #getStatusCode status code} and {@link #getStatusText status
1944       * text}.
1945       *
1946       * <p>
1947       * Subclasses may want to override this method to to customize the
1948       * processing.
1949       * </p>
1950       *
1951       * @param state the {@link HttpState state} information associated with this method
1952       * @param conn the {@link HttpConnection connection} used to execute
1953       *        this HTTP method
1954       *
1955       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1956       *                     can be recovered from.
1957       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
1958       *                    cannot be recovered from.
1959       *
1960       * @see StatusLine
1961       */
1962      protected void readStatusLine(HttpState state, HttpConnection conn)
1963      throws IOException, HttpException {
1964          LOG.trace("enter HttpMethodBase.readStatusLine(HttpState, HttpConnection)");
1965  
1966          final int maxGarbageLines = getParams().
1967              getIntParameter(HttpMethodParams.STATUS_LINE_GARBAGE_LIMIT, Integer.MAX_VALUE);
1968  
1969          //read out the HTTP status string
1970          int count = 0;
1971          String s;
1972          do {
1973              s = conn.readLine(getParams().getHttpElementCharset());
1974              if (s == null && count == 0) {
1975                  // The server just dropped connection on us
1976                  throw new NoHttpResponseException("The server " + conn.getHost() + 
1977                      " failed to respond");
1978              }
1979              if (Wire.HEADER_WIRE.enabled()) {
1980                  Wire.HEADER_WIRE.input(s + "\r\n");
1981              }
1982              if (s != null && StatusLine.startsWithHTTP(s)) {
1983                  // Got one
1984                  break;
1985              } else if (s == null || count >= maxGarbageLines) {
1986                  // Giving up
1987                  throw new ProtocolException("The server " + conn.getHost() + 
1988                          " failed to respond with a valid HTTP response");
1989              }
1990              count++;
1991          } while(true);
1992  
1993          //create the status line from the status string
1994          statusLine = new StatusLine(s);
1995  
1996          //check for a valid HTTP-Version
1997          String versionStr = statusLine.getHttpVersion();
1998          if (getParams().isParameterFalse(HttpMethodParams.UNAMBIGUOUS_STATUS_LINE) 
1999             && versionStr.equals("HTTP")) {
2000              getParams().setVersion(HttpVersion.HTTP_1_0);
2001              if (LOG.isWarnEnabled()) {
2002                  LOG.warn("Ambiguous status line (HTTP protocol version missing):" +
2003                  statusLine.toString());
2004              }
2005          } else {
2006              this.effectiveVersion = HttpVersion.parse(versionStr);
2007          }
2008  
2009      }
2010  
2011      // ------------------------------------------------------ Protected Methods
2012  
2013      /**
2014       * <p>
2015       * Sends the request via the given {@link HttpConnection connection}.
2016       * </p>
2017       *
2018       * <p>
2019       * The request is written as the following sequence of actions:
2020       * </p>
2021       *
2022       * <ol>
2023       * <li>
2024       * {@link #writeRequestLine(HttpState, HttpConnection)} is invoked to 
2025       * write the request line.
2026       * </li>
2027       * <li>
2028       * {@link #writeRequestHeaders(HttpState, HttpConnection)} is invoked 
2029       * to write the associated headers.
2030       * </li>
2031       * <li>
2032       * <tt>\r\n</tt> is sent to close the head part of the request.
2033       * </li>
2034       * <li>
2035       * {@link #writeRequestBody(HttpState, HttpConnection)} is invoked to 
2036       * write the body part of the request.
2037       * </li>
2038       * </ol>
2039       *
2040       * <p>
2041       * Subclasses may want to override one or more of the above methods to to
2042       * customize the processing. (Or they may choose to override this method
2043       * if dramatically different processing is required.)
2044       * </p>
2045       *
2046       * @param state the {@link HttpState state} information associated with this method
2047       * @param conn the {@link HttpConnection connection} used to execute
2048       *        this HTTP method
2049       *
2050       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2051       *                     can be recovered from.
2052       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
2053       *                    cannot be recovered from.
2054       */
2055      protected void writeRequest(HttpState state, HttpConnection conn)
2056      throws IOException, HttpException {
2057          LOG.trace(
2058              "enter HttpMethodBase.writeRequest(HttpState, HttpConnection)");
2059          writeRequestLine(state, conn);
2060          writeRequestHeaders(state, conn);
2061          conn.writeLine(); // close head
2062          if (Wire.HEADER_WIRE.enabled()) {
2063              Wire.HEADER_WIRE.output("\r\n");
2064          }
2065  
2066          HttpVersion ver = getParams().getVersion();
2067          Header expectheader = getRequestHeader("Expect");
2068          String expectvalue = null;
2069          if (expectheader != null) {
2070              expectvalue = expectheader.getValue();
2071          }
2072          if ((expectvalue != null) 
2073           && (expectvalue.compareToIgnoreCase("100-continue") == 0)) {
2074              if (ver.greaterEquals(HttpVersion.HTTP_1_1)) {
2075  
2076                  // make sure the status line and headers have been sent
2077                  conn.flushRequestOutputStream();
2078                  
2079                  int readTimeout = conn.getParams().getSoTimeout();
2080                  try {
2081                      conn.setSocketTimeout(RESPONSE_WAIT_TIME_MS);
2082                      readStatusLine(state, conn);
2083                      processStatusLine(state, conn);
2084                      readResponseHeaders(state, conn);
2085                      processResponseHeaders(state, conn);
2086  
2087                      if (this.statusLine.getStatusCode() == HttpStatus.SC_CONTINUE) {
2088                          // Discard status line
2089                          this.statusLine = null;
2090                          LOG.debug("OK to continue received");
2091                      } else {
2092                          return;
2093                      }
2094                  } catch (InterruptedIOException e) {
2095                      if (!ExceptionUtil.isSocketTimeoutException(e)) {
2096                          throw e;
2097                      }
2098                      // Most probably Expect header is not recongnized
2099                      // Remove the header to signal the method 
2100                      // that it's okay to go ahead with sending data
2101                      removeRequestHeader("Expect");
2102                      LOG.info("100 (continue) read timeout. Resume sending the request");
2103                  } finally {
2104                      conn.setSocketTimeout(readTimeout);
2105                  }
2106                  
2107              } else {
2108                  removeRequestHeader("Expect");
2109                  LOG.info("'Expect: 100-continue' handshake is only supported by "
2110                      + "HTTP/1.1 or higher");
2111              }
2112          }
2113  
2114          writeRequestBody(state, conn);
2115          // make sure the entire request body has been sent
2116          conn.flushRequestOutputStream();
2117      }
2118  
2119      /**
2120       * Writes the request body to the given {@link HttpConnection connection}.
2121       *
2122       * <p>
2123       * This method should return <tt>true</tt> if the request body was actually
2124       * sent (or is empty), or <tt>false</tt> if it could not be sent for some
2125       * reason.
2126       * </p>
2127       *
2128       * <p>
2129       * This implementation writes nothing and returns <tt>true</tt>.
2130       * </p>
2131       *
2132       * @param state the {@link HttpState state} information associated with this method
2133       * @param conn the {@link HttpConnection connection} used to execute
2134       *        this HTTP method
2135       *
2136       * @return <tt>true</tt>
2137       *
2138       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2139       *                     can be recovered from.
2140       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
2141       *                    cannot be recovered from.
2142       */
2143      protected boolean writeRequestBody(HttpState state, HttpConnection conn)
2144      throws IOException, HttpException {
2145          return true;
2146      }
2147  
2148      /**
2149       * Writes the request headers to the given {@link HttpConnection connection}.
2150       *
2151       * <p>
2152       * This implementation invokes {@link #addRequestHeaders(HttpState,HttpConnection)},
2153       * and then writes each header to the request stream.
2154       * </p>
2155       *
2156       * <p>
2157       * Subclasses may want to override this method to to customize the
2158       * processing.
2159       * </p>
2160       *
2161       * @param state the {@link HttpState state} information associated with this method
2162       * @param conn the {@link HttpConnection connection} used to execute
2163       *        this HTTP method
2164       *
2165       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2166       *                     can be recovered from.
2167       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
2168       *                    cannot be recovered from.
2169       *
2170       * @see #addRequestHeaders
2171       * @see #getRequestHeaders
2172       */
2173      protected void writeRequestHeaders(HttpState state, HttpConnection conn)
2174      throws IOException, HttpException {
2175          LOG.trace("enter HttpMethodBase.writeRequestHeaders(HttpState,"
2176              + "HttpConnection)");
2177          addRequestHeaders(state, conn);
2178  
2179          String charset = getParams().getHttpElementCharset();
2180          
2181          Header[] headers = getRequestHeaders();
2182          for (int i = 0; i < headers.length; i++) {
2183              String s = headers[i].toExternalForm();
2184              if (Wire.HEADER_WIRE.enabled()) {
2185                  Wire.HEADER_WIRE.output(s);
2186              }
2187              conn.print(s, charset);
2188          }
2189      }
2190  
2191      /**
2192       * Writes the request line to the given {@link HttpConnection connection}.
2193       *
2194       * <p>
2195       * Subclasses may want to override this method to to customize the
2196       * processing.
2197       * </p>
2198       *
2199       * @param state the {@link HttpState state} information associated with this method
2200       * @param conn the {@link HttpConnection connection} used to execute
2201       *        this HTTP method
2202       *
2203       * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2204       *                     can be recovered from.
2205       * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
2206       *                    cannot be recovered from.
2207       *
2208       * @see #generateRequestLine
2209       */
2210      protected void writeRequestLine(HttpState state, HttpConnection conn)
2211      throws IOException, HttpException {
2212          LOG.trace(
2213              "enter HttpMethodBase.writeRequestLine(HttpState, HttpConnection)");
2214          String requestLine = getRequestLine(conn);
2215          if (Wire.HEADER_WIRE.enabled()) {
2216              Wire.HEADER_WIRE.output(requestLine);
2217          }
2218          conn.print(requestLine, getParams().getHttpElementCharset());
2219      }
2220  
2221      /**
2222       * Returns the request line.
2223       * 
2224       * @param conn the {@link HttpConnection connection} used to execute
2225       *        this HTTP method
2226       * 
2227       * @return The request line.
2228       */
2229      private String getRequestLine(HttpConnection conn) {
2230          return  HttpMethodBase.generateRequestLine(conn, getName(),
2231                  getPath(), getQueryString(), this.effectiveVersion.toString());
2232      }
2233  
2234      /**
2235       * Returns {@link HttpMethodParams HTTP protocol parameters} associated with this method.
2236       *
2237       * @return HTTP parameters.
2238       *
2239       * @since 3.0
2240       */
2241      public HttpMethodParams getParams() {
2242          return this.params;
2243      }
2244  
2245      /**
2246       * Assigns {@link HttpMethodParams HTTP protocol parameters} for this method.
2247       * 
2248       * @since 3.0
2249       * 
2250       * @see HttpMethodParams
2251       */
2252      public void setParams(final HttpMethodParams params) {
2253          if (params == null) {
2254              throw new IllegalArgumentException("Parameters may not be null");
2255          }
2256          this.params = params;
2257      }
2258  
2259      /**
2260       * Returns the HTTP version used with this method (may be <tt>null</tt>
2261       * if undefined, that is, the method has not been executed)
2262       *
2263       * @return HTTP version.
2264       *
2265       * @since 3.0
2266       */
2267      public HttpVersion getEffectiveVersion() {
2268          return this.effectiveVersion;
2269      }
2270  
2271      /**
2272       * Per RFC 2616 section 4.3, some response can never contain a message
2273       * body.
2274       *
2275       * @param status - the HTTP status code
2276       *
2277       * @return <tt>true</tt> if the message may contain a body, <tt>false</tt> if it can not
2278       *         contain a message body
2279       */
2280      private static boolean canResponseHaveBody(int status) {
2281          LOG.trace("enter HttpMethodBase.canResponseHaveBody(int)");
2282  
2283          boolean result = true;
2284  
2285          if ((status >= 100 && status <= 199) || (status == 204)
2286              || (status == 304)) { // NOT MODIFIED
2287              result = false;
2288          }
2289  
2290          return result;
2291      }
2292  
2293      /**
2294       * Returns proxy authentication realm, if it has been used during authentication process. 
2295       * Otherwise returns <tt>null</tt>.
2296       * 
2297       * @return proxy authentication realm
2298       * 
2299       * @deprecated use #getProxyAuthState()
2300       */
2301      public String getProxyAuthenticationRealm() {
2302          return this.proxyAuthState.getRealm();
2303      }
2304  
2305      /**
2306       * Returns authentication realm, if it has been used during authentication process. 
2307       * Otherwise returns <tt>null</tt>.
2308       * 
2309       * @return authentication realm
2310       * 
2311       * @deprecated use #getHostAuthState()
2312       */
2313      public String getAuthenticationRealm() {
2314          return this.hostAuthState.getRealm();
2315      }
2316  
2317      /**
2318       * Returns the character set from the <tt>Content-Type</tt> header.
2319       * 
2320       * @param contentheader The content header.
2321       * @return String The character set.
2322       */
2323      protected String getContentCharSet(Header contentheader) {
2324          LOG.trace("enter getContentCharSet( Header contentheader )");
2325          String charset = null;
2326          if (contentheader != null) {
2327              HeaderElement values[] = contentheader.getElements();
2328              // I expect only one header element to be there
2329              // No more. no less
2330              if (values.length == 1) {
2331                  NameValuePair param = values[0].getParameterByName("charset");
2332                  if (param != null) {
2333                      // If I get anything "funny" 
2334                      // UnsupportedEncondingException will result
2335                      charset = param.getValue();
2336                  }
2337              }
2338          }
2339          if (charset == null) {
2340              charset = getParams().getContentCharset();
2341              if (LOG.isDebugEnabled()) {
2342                  LOG.debug("Default charset used: " + charset);
2343              }
2344          }
2345          return charset;
2346      }
2347  
2348  
2349      /**
2350       * Returns the character encoding of the request from the <tt>Content-Type</tt> header.
2351       * 
2352       * @return String The character set.
2353       */
2354      public String getRequestCharSet() {
2355          return getContentCharSet(getRequestHeader("Content-Type"));
2356      }
2357  
2358  
2359      /**  
2360       * Returns the character encoding of the response from the <tt>Content-Type</tt> header.
2361       * 
2362       * @return String The character set.
2363       */
2364      public String getResponseCharSet() {
2365          return getContentCharSet(getResponseHeader("Content-Type"));
2366      }
2367  
2368      /**
2369       * @deprecated no longer used
2370       * 
2371       * Returns the number of "recoverable" exceptions thrown and handled, to
2372       * allow for monitoring the quality of the connection.
2373       *
2374       * @return The number of recoverable exceptions handled by the method.
2375       */
2376      public int getRecoverableExceptionCount() {
2377          return recoverableExceptionCount;
2378      }
2379  
2380      /**
2381       * A response has been consumed.
2382       *
2383       * <p>The default behavior for this class is to check to see if the connection
2384       * should be closed, and close if need be, and to ensure that the connection
2385       * is returned to the connection manager - if and only if we are not still
2386       * inside the execute call.</p>
2387       *
2388       */
2389      protected void responseBodyConsumed() {
2390  
2391          // make sure this is the initial invocation of the notification,
2392          // ignore subsequent ones.
2393          responseStream = null;
2394          if (responseConnection != null) {
2395              responseConnection.setLastResponseInputStream(null);
2396  
2397              // At this point, no response data should be available.
2398              // If there is data available, regard the connection as being
2399              // unreliable and close it.
2400              
2401              if (shouldCloseConnection(responseConnection)) {
2402                  responseConnection.close();
2403              } else {
2404                  try {
2405                      if(responseConnection.isResponseAvailable()) {
2406                          boolean logExtraInput =
2407                              getParams().isParameterTrue(HttpMethodParams.WARN_EXTRA_INPUT);
2408  
2409                          if(logExtraInput) {
2410                              LOG.warn("Extra response data detected - closing connection");
2411                          } 
2412                          responseConnection.close();
2413                      }
2414                  }
2415                  catch (IOException e) {
2416                      LOG.warn(e.getMessage());
2417                      responseConnection.close();
2418                  }
2419              }
2420          }
2421          this.connectionCloseForced = false;
2422          ensureConnectionRelease();
2423      }
2424  
2425      /**
2426       * Insure that the connection is released back to the pool.
2427       */
2428      private void ensureConnectionRelease() {
2429          if (responseConnection != null) {
2430              responseConnection.releaseConnection();
2431              responseConnection = null;
2432          }
2433      }
2434  
2435      /**
2436       * Returns the {@link HostConfiguration host configuration}.
2437       * 
2438       * @return the host configuration
2439       * 
2440       * @deprecated no longer applicable
2441       */
2442      public HostConfiguration getHostConfiguration() {
2443          HostConfiguration hostconfig = new HostConfiguration();
2444          hostconfig.setHost(this.httphost);
2445          return hostconfig;
2446      }
2447      /**
2448       * Sets the {@link HostConfiguration host configuration}.
2449       * 
2450       * @param hostconfig The hostConfiguration to set
2451       * 
2452       * @deprecated no longer applicable
2453       */
2454      public void setHostConfiguration(final HostConfiguration hostconfig) {
2455          if (hostconfig != null) {
2456              this.httphost = new HttpHost(
2457                      hostconfig.getHost(),
2458                      hostconfig.getPort(),
2459                      hostconfig.getProtocol());
2460          } else {
2461              this.httphost = null;
2462          }
2463      }
2464  
2465      /**
2466       * Returns the {@link MethodRetryHandler retry handler} for this HTTP method
2467       * 
2468       * @return the methodRetryHandler
2469       * 
2470       * @deprecated use {@link HttpMethodParams}
2471       */
2472      public MethodRetryHandler getMethodRetryHandler() {
2473          return methodRetryHandler;
2474      }
2475  
2476      /**
2477       * Sets the {@link MethodRetryHandler retry handler} for this HTTP method
2478       * 
2479       * @param handler the methodRetryHandler to use when this method executed
2480       * 
2481       * @deprecated use {@link HttpMethodParams}
2482       */
2483      public void setMethodRetryHandler(MethodRetryHandler handler) {
2484          methodRetryHandler = handler;
2485      }
2486  
2487      /**
2488       * This method is a dirty hack intended to work around 
2489       * current (2.0) design flaw that prevents the user from
2490       * obtaining correct status code, headers and response body from the 
2491       * preceding HTTP CONNECT method.
2492       * 
2493       * TODO: Remove this crap as soon as possible
2494       */
2495      void fakeResponse(
2496          StatusLine statusline, 
2497          HeaderGroup responseheaders,
2498          InputStream responseStream
2499      ) {
2500          // set used so that the response can be read
2501          this.used = true;
2502          this.statusLine = statusline;
2503          this.responseHeaders = responseheaders;
2504          this.responseBody = null;
2505          this.responseStream = responseStream;
2506      }
2507      
2508      /**
2509       * Returns the target host {@link AuthState authentication state}
2510       * 
2511       * @return host authentication state
2512       * 
2513       * @since 3.0
2514       */
2515      public AuthState getHostAuthState() {
2516          return this.hostAuthState;
2517      }
2518  
2519      /**
2520       * Returns the proxy {@link AuthState authentication state}
2521       * 
2522       * @return host authentication state
2523       * 
2524       * @since 3.0
2525       */
2526      public AuthState getProxyAuthState() {
2527          return this.proxyAuthState;
2528      }
2529      
2530      /**
2531       * Tests whether the execution of this method has been aborted
2532       * 
2533       * @return <tt>true</tt> if the execution of this method has been aborted,
2534       *  <tt>false</tt> otherwise
2535       * 
2536       * @since 3.0
2537       */
2538      public boolean isAborted() {
2539          return this.aborted;
2540      }
2541      
2542      /**
2543       * Returns <tt>true</tt> if the HTTP has been transmitted to the target
2544       * server in its entirety, <tt>false</tt> otherwise. This flag can be useful 
2545       * for recovery logic. If the request has not been transmitted in its entirety,
2546       * it is safe to retry the failed method.
2547       * 
2548       * @return <tt>true</tt> if the request has been sent, <tt>false</tt> otherwise
2549       */
2550      public boolean isRequestSent() {
2551          return this.requestSent;
2552      }
2553      
2554  }