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