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