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