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