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