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