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