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: 487802 $ $Date: 2006-12-16 14:14:16 +0000 (Sat, 16 Dec 2006) $
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 *
660 * Note: This will cause the entire response body to be buffered in memory. A
661 * malicious server may easily exhaust all the VM memory. It is strongly
662 * recommended, to use getResponseAsStream if the content length of the response
663 * is unknown or resonably large.
664 *
665 * @return The response body.
666 *
667 * @throws IOException If an I/O (transport) problem occurs while obtaining the
668 * response body.
669 */
670 public byte[] getResponseBody() throws IOException {
671 if (this.responseBody == null) {
672 InputStream instream = getResponseBodyAsStream();
673 if (instream != null) {
674 long contentLength = getResponseContentLength();
675 if (contentLength > Integer.MAX_VALUE) {
676 throw new IOException("Content too large to be buffered: "+ contentLength +" bytes");
677 }
678 int limit = getParams().getIntParameter(HttpMethodParams.BUFFER_WARN_TRIGGER_LIMIT, 1024*1024);
679 if ((contentLength == -1) || (contentLength > limit)) {
680 LOG.warn("Going to buffer response body of large or unknown size. "
681 +"Using getResponseBodyAsStream instead is recommended.");
682 }
683 LOG.debug("Buffering response body");
684 ByteArrayOutputStream outstream = new ByteArrayOutputStream(
685 contentLength > 0 ? (int) contentLength : DEFAULT_INITIAL_BUFFER_SIZE);
686 byte[] buffer = new byte[4096];
687 int len;
688 while ((len = instream.read(buffer)) > 0) {
689 outstream.write(buffer, 0, len);
690 }
691 outstream.close();
692 setResponseStream(null);
693 this.responseBody = outstream.toByteArray();
694 }
695 }
696 return this.responseBody;
697 }
698
699 /***
700 * Returns the response body of the HTTP method, if any, as an array of bytes.
701 * If response body is not available or cannot be read, returns <tt>null</tt>
702 *
703 * Note: This will cause the entire response body to be buffered in memory. This method is
704 * safe if the content length of the response is unknown, because the amount of memory used
705 * is limited.<p>
706 *
707 * If the response is large this method involves lots of array copying and many object
708 * allocations, which makes it unsuitable for high-performance / low-footprint applications.
709 * Those applications should use {@link #getResponseBodyAsStream()}.
710 *
711 * @param maxlen the maximum content length to accept (number of bytes).
712 * @return The response body.
713 *
714 * @throws IOException If an I/O (transport) problem occurs while obtaining the
715 * response body.
716 */
717 public byte[] getResponseBody(int maxlen) throws IOException {
718 if (maxlen < 0) throw new IllegalArgumentException("maxlen must be positive");
719 if (this.responseBody == null) {
720 InputStream instream = getResponseBodyAsStream();
721 if (instream != null) {
722
723 long contentLength = getResponseContentLength();
724 if ((contentLength != -1) && (contentLength > maxlen)) {
725 throw new HttpContentTooLargeException(
726 "Content-Length is " + contentLength, maxlen);
727 }
728
729 LOG.debug("Buffering response body");
730 ByteArrayOutputStream rawdata = new ByteArrayOutputStream(
731 contentLength > 0 ? (int) contentLength : DEFAULT_INITIAL_BUFFER_SIZE);
732 byte[] buffer = new byte[2048];
733 int pos = 0;
734 int len;
735 do {
736 len = instream.read(buffer, 0, Math.min(buffer.length, maxlen-pos));
737 if (len == -1) break;
738 rawdata.write(buffer, 0, len);
739 pos += len;
740 } while (pos < maxlen);
741
742 setResponseStream(null);
743
744 if (pos == maxlen) {
745 if (instream.read() != -1)
746 throw new HttpContentTooLargeException(
747 "Content-Length not known but larger than "
748 + maxlen, maxlen);
749 }
750 this.responseBody = rawdata.toByteArray();
751 }
752 }
753 return this.responseBody;
754 }
755
756 /***
757 * Returns the response body of the HTTP method, if any, as an {@link InputStream}.
758 * If response body is not available, returns <tt>null</tt>
759 *
760 * @return The response body or <code>null</code>.
761 *
762 * @throws IOException If an I/O (transport) problem occurs while obtaining the
763 * response body.
764 */
765 public InputStream getResponseBodyAsStream() throws IOException {
766 if (responseStream != null) {
767 return responseStream;
768 }
769 if (responseBody != null) {
770 InputStream byteResponseStream = new ByteArrayInputStream(responseBody);
771 LOG.debug("re-creating response stream from byte array");
772 return byteResponseStream;
773 }
774 return null;
775 }
776
777 /***
778 * Returns the response body of the HTTP method, if any, as a {@link String}.
779 * If response body is not available or cannot be read, returns <tt>null</tt>
780 * The string conversion on the data is done using the character encoding specified
781 * in <tt>Content-Type</tt> header.
782 *
783 * Note: This will cause the entire response body to be buffered in memory. A
784 * malicious server may easily exhaust all the VM memory. It is strongly
785 * recommended, to use getResponseAsStream if the content length of the response
786 * is unknown or resonably large.
787 *
788 * @return The response body or <code>null</code>.
789 *
790 * @throws IOException If an I/O (transport) problem occurs while obtaining the
791 * response body.
792 */
793 public String getResponseBodyAsString() throws IOException {
794 byte[] rawdata = null;
795 if (responseAvailable()) {
796 rawdata = getResponseBody();
797 }
798 if (rawdata != null) {
799 return EncodingUtil.getString(rawdata, getResponseCharSet());
800 } else {
801 return null;
802 }
803 }
804
805 /***
806 * Returns the response body of the HTTP method, if any, as a {@link String}.
807 * If response body is not available or cannot be read, returns <tt>null</tt>
808 * The string conversion on the data is done using the character encoding specified
809 * in <tt>Content-Type</tt> header.<p>
810 *
811 * Note: This will cause the entire response body to be buffered in memory. This method is
812 * safe if the content length of the response is unknown, because the amount of memory used
813 * is limited.<p>
814 *
815 * If the response is large this method involves lots of array copying and many object
816 * allocations, which makes it unsuitable for high-performance / low-footprint applications.
817 * Those applications should use {@link #getResponseBodyAsStream()}.
818 *
819 * @param maxlen the maximum content length to accept (number of bytes). Note that,
820 * depending on the encoding, this is not equal to the number of characters.
821 * @return The response body or <code>null</code>.
822 *
823 * @throws IOException If an I/O (transport) problem occurs while obtaining the
824 * response body.
825 */
826 public String getResponseBodyAsString(int maxlen) throws IOException {
827 if (maxlen < 0) throw new IllegalArgumentException("maxlen must be positive");
828 byte[] rawdata = null;
829 if (responseAvailable()) {
830 rawdata = getResponseBody(maxlen);
831 }
832 if (rawdata != null) {
833 return EncodingUtil.getString(rawdata, getResponseCharSet());
834 } else {
835 return null;
836 }
837 }
838
839 /***
840 * Returns an array of the response footers that the HTTP method currently has
841 * in the order in which they were read.
842 *
843 * @return an array of footers
844 */
845 public Header[] getResponseFooters() {
846 return getResponseTrailerHeaderGroup().getAllHeaders();
847 }
848
849 /***
850 * Gets the response footer associated with the given name.
851 * Footer name matching is case insensitive.
852 * <tt>null</tt> will be returned if either <i>footerName</i> is
853 * <tt>null</tt> or there is no matching footer for <i>footerName</i>
854 * or there are no footers available. If there are multiple footers
855 * with the same name, there values will be combined with the ',' separator
856 * as specified by RFC2616.
857 *
858 * @param footerName the footer name to match
859 * @return the matching footer
860 */
861 public Header getResponseFooter(String footerName) {
862 if (footerName == null) {
863 return null;
864 } else {
865 return getResponseTrailerHeaderGroup().getCondensedHeader(footerName);
866 }
867 }
868
869 /***
870 * Sets the response stream.
871 * @param responseStream The new response stream.
872 */
873 protected void setResponseStream(InputStream responseStream) {
874 this.responseStream = responseStream;
875 }
876
877 /***
878 * Returns a stream from which the body of the current response may be read.
879 * If the method has not yet been executed, if <code>responseBodyConsumed</code>
880 * has been called, or if the stream returned by a previous call has been closed,
881 * <code>null</code> will be returned.
882 *
883 * @return the current response stream
884 */
885 protected InputStream getResponseStream() {
886 return responseStream;
887 }
888
889 /***
890 * Returns the status text (or "reason phrase") associated with the latest
891 * response.
892 *
893 * @return The status text.
894 */
895 public String getStatusText() {
896 return statusLine.getReasonPhrase();
897 }
898
899 /***
900 * Defines how strictly HttpClient follows the HTTP protocol specification
901 * (RFC 2616 and other relevant RFCs). In the strict mode HttpClient precisely
902 * implements the requirements of the specification, whereas in non-strict mode
903 * it attempts to mimic the exact behaviour of commonly used HTTP agents,
904 * which many HTTP servers expect.
905 *
906 * @param strictMode <tt>true</tt> for strict mode, <tt>false</tt> otherwise
907 *
908 * @deprecated Use {@link org.apache.commons.httpclient.params.HttpParams#setParameter(String, Object)}
909 * to exercise a more granular control over HTTP protocol strictness.
910 */
911 public void setStrictMode(boolean strictMode) {
912 if (strictMode) {
913 this.params.makeStrict();
914 } else {
915 this.params.makeLenient();
916 }
917 }
918
919 /***
920 * @deprecated Use {@link org.apache.commons.httpclient.params.HttpParams#setParameter(String, Object)}
921 * to exercise a more granular control over HTTP protocol strictness.
922 *
923 * @return <tt>false</tt>
924 */
925 public boolean isStrictMode() {
926 return false;
927 }
928
929 /***
930 * Adds the specified request header, NOT overwriting any previous value.
931 * Note that header-name matching is case insensitive.
932 *
933 * @param headerName the header's name
934 * @param headerValue the header's value
935 */
936 public void addRequestHeader(String headerName, String headerValue) {
937 addRequestHeader(new Header(headerName, headerValue));
938 }
939
940 /***
941 * Tests if the connection should be force-closed when no longer needed.
942 *
943 * @return <code>true</code> if the connection must be closed
944 */
945 protected boolean isConnectionCloseForced() {
946 return this.connectionCloseForced;
947 }
948
949 /***
950 * Sets whether or not the connection should be force-closed when no longer
951 * needed. This value should only be set to <code>true</code> in abnormal
952 * circumstances, such as HTTP protocol violations.
953 *
954 * @param b <code>true</code> if the connection must be closed, <code>false</code>
955 * otherwise.
956 */
957 protected void setConnectionCloseForced(boolean b) {
958 if (LOG.isDebugEnabled()) {
959 LOG.debug("Force-close connection: " + b);
960 }
961 this.connectionCloseForced = b;
962 }
963
964 /***
965 * Tests if the connection should be closed after the method has been executed.
966 * The connection will be left open when using HTTP/1.1 or if <tt>Connection:
967 * keep-alive</tt> header was sent.
968 *
969 * @param conn the connection in question
970 *
971 * @return boolean true if we should close the connection.
972 */
973 protected boolean shouldCloseConnection(HttpConnection conn) {
974
975 if (isConnectionCloseForced()) {
976 LOG.debug("Should force-close connection.");
977 return true;
978 }
979
980 Header connectionHeader = null;
981
982 if (!conn.isTransparent()) {
983
984 connectionHeader = responseHeaders.getFirstHeader("proxy-connection");
985 }
986
987
988
989 if (connectionHeader == null) {
990 connectionHeader = responseHeaders.getFirstHeader("connection");
991 }
992
993
994 if (connectionHeader == null) {
995 connectionHeader = requestHeaders.getFirstHeader("connection");
996 }
997 if (connectionHeader != null) {
998 if (connectionHeader.getValue().equalsIgnoreCase("close")) {
999 if (LOG.isDebugEnabled()) {
1000 LOG.debug("Should close connection in response to directive: "
1001 + connectionHeader.getValue());
1002 }
1003 return true;
1004 } else if (connectionHeader.getValue().equalsIgnoreCase("keep-alive")) {
1005 if (LOG.isDebugEnabled()) {
1006 LOG.debug("Should NOT close connection in response to directive: "
1007 + connectionHeader.getValue());
1008 }
1009 return false;
1010 } else {
1011 if (LOG.isDebugEnabled()) {
1012 LOG.debug("Unknown directive: " + connectionHeader.toExternalForm());
1013 }
1014 }
1015 }
1016 LOG.debug("Resorting to protocol version default close connection policy");
1017
1018 if (this.effectiveVersion.greaterEquals(HttpVersion.HTTP_1_1)) {
1019 if (LOG.isDebugEnabled()) {
1020 LOG.debug("Should NOT close connection, using " + this.effectiveVersion.toString());
1021 }
1022 } else {
1023 if (LOG.isDebugEnabled()) {
1024 LOG.debug("Should close connection, using " + this.effectiveVersion.toString());
1025 }
1026 }
1027 return this.effectiveVersion.lessEquals(HttpVersion.HTTP_1_0);
1028 }
1029
1030 /***
1031 * Tests if the this method is ready to be executed.
1032 *
1033 * @param state the {@link HttpState state} information associated with this method
1034 * @param conn the {@link HttpConnection connection} to be used
1035 * @throws HttpException If the method is in invalid state.
1036 */
1037 private void checkExecuteConditions(HttpState state, HttpConnection conn)
1038 throws HttpException {
1039
1040 if (state == null) {
1041 throw new IllegalArgumentException("HttpState parameter may not be null");
1042 }
1043 if (conn == null) {
1044 throw new IllegalArgumentException("HttpConnection parameter may not be null");
1045 }
1046 if (this.aborted) {
1047 throw new IllegalStateException("Method has been aborted");
1048 }
1049 if (!validate()) {
1050 throw new ProtocolException("HttpMethodBase object not valid");
1051 }
1052 }
1053
1054 /***
1055 * Executes this method using the specified <code>HttpConnection</code> and
1056 * <code>HttpState</code>.
1057 *
1058 * @param state {@link HttpState state} information to associate with this
1059 * request. Must be non-null.
1060 * @param conn the {@link HttpConnection connection} to used to execute
1061 * this HTTP method. Must be non-null.
1062 *
1063 * @return the integer status code if one was obtained, or <tt>-1</tt>
1064 *
1065 * @throws IOException if an I/O (transport) error occurs
1066 * @throws HttpException if a protocol exception occurs.
1067 */
1068 public int execute(HttpState state, HttpConnection conn)
1069 throws HttpException, IOException {
1070
1071 LOG.trace("enter HttpMethodBase.execute(HttpState, HttpConnection)");
1072
1073
1074
1075 this.responseConnection = conn;
1076
1077 checkExecuteConditions(state, conn);
1078 this.statusLine = null;
1079 this.connectionCloseForced = false;
1080
1081 conn.setLastResponseInputStream(null);
1082
1083
1084 if (this.effectiveVersion == null) {
1085 this.effectiveVersion = this.params.getVersion();
1086 }
1087
1088 writeRequest(state, conn);
1089 this.requestSent = true;
1090 readResponse(state, conn);
1091
1092 used = true;
1093
1094 return statusLine.getStatusCode();
1095 }
1096
1097 /***
1098 * Aborts the execution of this method.
1099 *
1100 * @since 3.0
1101 */
1102 public void abort() {
1103 if (this.aborted) {
1104 return;
1105 }
1106 this.aborted = true;
1107 HttpConnection conn = this.responseConnection;
1108 if (conn != null) {
1109 conn.close();
1110 }
1111 }
1112
1113 /***
1114 * Returns <tt>true</tt> if the HTTP method has been already {@link #execute executed},
1115 * but not {@link #recycle recycled}.
1116 *
1117 * @return <tt>true</tt> if the method has been executed, <tt>false</tt> otherwise
1118 */
1119 public boolean hasBeenUsed() {
1120 return used;
1121 }
1122
1123 /***
1124 * Recycles the HTTP method so that it can be used again.
1125 * Note that all of the instance variables will be reset
1126 * once this method has been called. This method will also
1127 * release the connection being used by this HTTP method.
1128 *
1129 * @see #releaseConnection()
1130 *
1131 * @deprecated no longer supported and will be removed in the future
1132 * version of HttpClient
1133 */
1134 public void recycle() {
1135 LOG.trace("enter HttpMethodBase.recycle()");
1136
1137 releaseConnection();
1138
1139 path = null;
1140 followRedirects = false;
1141 doAuthentication = true;
1142 queryString = null;
1143 getRequestHeaderGroup().clear();
1144 getResponseHeaderGroup().clear();
1145 getResponseTrailerHeaderGroup().clear();
1146 statusLine = null;
1147 effectiveVersion = null;
1148 aborted = false;
1149 used = false;
1150 params = new HttpMethodParams();
1151 responseBody = null;
1152 recoverableExceptionCount = 0;
1153 connectionCloseForced = false;
1154 hostAuthState.invalidate();
1155 proxyAuthState.invalidate();
1156 cookiespec = null;
1157 requestSent = false;
1158 }
1159
1160 /***
1161 * Releases the connection being used by this HTTP method. In particular the
1162 * connection is used to read the response(if there is one) and will be held
1163 * until the response has been read. If the connection can be reused by other
1164 * HTTP methods it is NOT closed at this point.
1165 *
1166 * @since 2.0
1167 */
1168 public void releaseConnection() {
1169 try {
1170 if (this.responseStream != null) {
1171 try {
1172
1173 this.responseStream.close();
1174 } catch (IOException ignore) {
1175 }
1176 }
1177 } finally {
1178 ensureConnectionRelease();
1179 }
1180 }
1181
1182 /***
1183 * Remove the request header associated with the given name. Note that
1184 * header-name matching is case insensitive.
1185 *
1186 * @param headerName the header name
1187 */
1188 public void removeRequestHeader(String headerName) {
1189
1190 Header[] headers = getRequestHeaderGroup().getHeaders(headerName);
1191 for (int i = 0; i < headers.length; i++) {
1192 getRequestHeaderGroup().removeHeader(headers[i]);
1193 }
1194
1195 }
1196
1197 /***
1198 * Removes the given request header.
1199 *
1200 * @param header the header
1201 */
1202 public void removeRequestHeader(final Header header) {
1203 if (header == null) {
1204 return;
1205 }
1206 getRequestHeaderGroup().removeHeader(header);
1207 }
1208
1209
1210
1211 /***
1212 * Returns <tt>true</tt> the method is ready to execute, <tt>false</tt> otherwise.
1213 *
1214 * @return This implementation always returns <tt>true</tt>.
1215 */
1216 public boolean validate() {
1217 return true;
1218 }
1219
1220
1221 /***
1222 * Returns the actual cookie policy
1223 *
1224 * @param state HTTP state. TODO: to be removed in the future
1225 *
1226 * @return cookie spec
1227 */
1228 private CookieSpec getCookieSpec(final HttpState state) {
1229 if (this.cookiespec == null) {
1230 int i = state.getCookiePolicy();
1231 if (i == -1) {
1232 this.cookiespec = CookiePolicy.getCookieSpec(this.params.getCookiePolicy());
1233 } else {
1234 this.cookiespec = CookiePolicy.getSpecByPolicy(i);
1235 }
1236 this.cookiespec.setValidDateFormats(
1237 (Collection)this.params.getParameter(HttpMethodParams.DATE_PATTERNS));
1238 }
1239 return this.cookiespec;
1240 }
1241
1242 /***
1243 * Generates <tt>Cookie</tt> request headers for those {@link Cookie cookie}s
1244 * that match the given host, port and path.
1245 *
1246 * @param state the {@link HttpState state} information associated with this method
1247 * @param conn the {@link HttpConnection connection} used to execute
1248 * this HTTP method
1249 *
1250 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1251 * can be recovered from.
1252 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1253 * cannot be recovered from.
1254 */
1255 protected void addCookieRequestHeader(HttpState state, HttpConnection conn)
1256 throws IOException, HttpException {
1257
1258 LOG.trace("enter HttpMethodBase.addCookieRequestHeader(HttpState, "
1259 + "HttpConnection)");
1260
1261 Header[] cookieheaders = getRequestHeaderGroup().getHeaders("Cookie");
1262 for (int i = 0; i < cookieheaders.length; i++) {
1263 Header cookieheader = cookieheaders[i];
1264 if (cookieheader.isAutogenerated()) {
1265 getRequestHeaderGroup().removeHeader(cookieheader);
1266 }
1267 }
1268
1269 CookieSpec matcher = getCookieSpec(state);
1270 String host = this.params.getVirtualHost();
1271 if (host == null) {
1272 host = conn.getHost();
1273 }
1274 Cookie[] cookies = matcher.match(host, conn.getPort(),
1275 getPath(), conn.isSecure(), state.getCookies());
1276 if ((cookies != null) && (cookies.length > 0)) {
1277 if (getParams().isParameterTrue(HttpMethodParams.SINGLE_COOKIE_HEADER)) {
1278
1279 String s = matcher.formatCookies(cookies);
1280 getRequestHeaderGroup().addHeader(new Header("Cookie", s, true));
1281 } else {
1282
1283 for (int i = 0; i < cookies.length; i++) {
1284 String s = matcher.formatCookie(cookies[i]);
1285 getRequestHeaderGroup().addHeader(new Header("Cookie", s, true));
1286 }
1287 }
1288 if (matcher instanceof CookieVersionSupport) {
1289 CookieVersionSupport versupport = (CookieVersionSupport) matcher;
1290 int ver = versupport.getVersion();
1291 boolean needVersionHeader = false;
1292 for (int i = 0; i < cookies.length; i++) {
1293 if (ver != cookies[i].getVersion()) {
1294 needVersionHeader = true;
1295 }
1296 }
1297 if (needVersionHeader) {
1298
1299 getRequestHeaderGroup().addHeader(versupport.getVersionHeader());
1300 }
1301 }
1302 }
1303 }
1304
1305 /***
1306 * Generates <tt>Host</tt> request header, as long as no <tt>Host</tt> request
1307 * header already exists.
1308 *
1309 * @param state the {@link HttpState state} information associated with this method
1310 * @param conn the {@link HttpConnection connection} used to execute
1311 * this HTTP method
1312 *
1313 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1314 * can be recovered from.
1315 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1316 * cannot be recovered from.
1317 */
1318 protected void addHostRequestHeader(HttpState state, HttpConnection conn)
1319 throws IOException, HttpException {
1320 LOG.trace("enter HttpMethodBase.addHostRequestHeader(HttpState, "
1321 + "HttpConnection)");
1322
1323
1324
1325
1326
1327 String host = this.params.getVirtualHost();
1328 if (host != null) {
1329 LOG.debug("Using virtual host name: " + host);
1330 } else {
1331 host = conn.getHost();
1332 }
1333 int port = conn.getPort();
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343 if (LOG.isDebugEnabled()) {
1344 LOG.debug("Adding Host request header");
1345 }
1346
1347
1348 if (conn.getProtocol().getDefaultPort() != port) {
1349 host += (":" + port);
1350 }
1351
1352 setRequestHeader("Host", host);
1353 }
1354
1355 /***
1356 * Generates <tt>Proxy-Connection: Keep-Alive</tt> request header when
1357 * communicating via a proxy server.
1358 *
1359 * @param state the {@link HttpState state} information associated with this method
1360 * @param conn the {@link HttpConnection connection} used to execute
1361 * this HTTP method
1362 *
1363 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1364 * can be recovered from.
1365 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1366 * cannot be recovered from.
1367 */
1368 protected void addProxyConnectionHeader(HttpState state,
1369 HttpConnection conn)
1370 throws IOException, HttpException {
1371 LOG.trace("enter HttpMethodBase.addProxyConnectionHeader("
1372 + "HttpState, HttpConnection)");
1373 if (!conn.isTransparent()) {
1374 if (getRequestHeader("Proxy-Connection") == null) {
1375 addRequestHeader("Proxy-Connection", "Keep-Alive");
1376 }
1377 }
1378 }
1379
1380 /***
1381 * Generates all the required request {@link Header header}s
1382 * to be submitted via the given {@link HttpConnection connection}.
1383 *
1384 * <p>
1385 * This implementation adds <tt>User-Agent</tt>, <tt>Host</tt>,
1386 * <tt>Cookie</tt>, <tt>Authorization</tt>, <tt>Proxy-Authorization</tt>
1387 * and <tt>Proxy-Connection</tt> headers, when appropriate.
1388 * </p>
1389 *
1390 * <p>
1391 * Subclasses may want to override this method to to add additional
1392 * headers, and may choose to invoke this implementation (via
1393 * <tt>super</tt>) to add the "standard" headers.
1394 * </p>
1395 *
1396 * @param state the {@link HttpState state} information associated with this method
1397 * @param conn the {@link HttpConnection connection} used to execute
1398 * this HTTP method
1399 *
1400 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1401 * can be recovered from.
1402 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1403 * cannot be recovered from.
1404 *
1405 * @see #writeRequestHeaders
1406 */
1407 protected void addRequestHeaders(HttpState state, HttpConnection conn)
1408 throws IOException, HttpException {
1409 LOG.trace("enter HttpMethodBase.addRequestHeaders(HttpState, "
1410 + "HttpConnection)");
1411
1412 addUserAgentRequestHeader(state, conn);
1413 addHostRequestHeader(state, conn);
1414 addCookieRequestHeader(state, conn);
1415 addProxyConnectionHeader(state, conn);
1416 }
1417
1418 /***
1419 * Generates default <tt>User-Agent</tt> request header, as long as no
1420 * <tt>User-Agent</tt> request header already exists.
1421 *
1422 * @param state the {@link HttpState state} information associated with this method
1423 * @param conn the {@link HttpConnection connection} used to execute
1424 * this HTTP method
1425 *
1426 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1427 * can be recovered from.
1428 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1429 * cannot be recovered from.
1430 */
1431 protected void addUserAgentRequestHeader(HttpState state,
1432 HttpConnection conn)
1433 throws IOException, HttpException {
1434 LOG.trace("enter HttpMethodBase.addUserAgentRequestHeaders(HttpState, "
1435 + "HttpConnection)");
1436
1437 if (getRequestHeader("User-Agent") == null) {
1438 String agent = (String)getParams().getParameter(HttpMethodParams.USER_AGENT);
1439 if (agent == null) {
1440 agent = "Jakarta Commons-HttpClient";
1441 }
1442 setRequestHeader("User-Agent", agent);
1443 }
1444 }
1445
1446 /***
1447 * Throws an {@link IllegalStateException} if the HTTP method has been already
1448 * {@link #execute executed}, but not {@link #recycle recycled}.
1449 *
1450 * @throws IllegalStateException if the method has been used and not
1451 * recycled
1452 */
1453 protected void checkNotUsed() throws IllegalStateException {
1454 if (used) {
1455 throw new IllegalStateException("Already used.");
1456 }
1457 }
1458
1459 /***
1460 * Throws an {@link IllegalStateException} if the HTTP method has not been
1461 * {@link #execute executed} since last {@link #recycle recycle}.
1462 *
1463 *
1464 * @throws IllegalStateException if not used
1465 */
1466 protected void checkUsed() throws IllegalStateException {
1467 if (!used) {
1468 throw new IllegalStateException("Not Used.");
1469 }
1470 }
1471
1472
1473
1474 /***
1475 * Generates HTTP request line according to the specified attributes.
1476 *
1477 * @param connection the {@link HttpConnection connection} used to execute
1478 * this HTTP method
1479 * @param name the method name generate a request for
1480 * @param requestPath the path string for the request
1481 * @param query the query string for the request
1482 * @param version the protocol version to use (e.g. HTTP/1.0)
1483 *
1484 * @return HTTP request line
1485 */
1486 protected static String generateRequestLine(HttpConnection connection,
1487 String name, String requestPath, String query, String version) {
1488 LOG.trace("enter HttpMethodBase.generateRequestLine(HttpConnection, "
1489 + "String, String, String, String)");
1490
1491 StringBuffer buf = new StringBuffer();
1492
1493 buf.append(name);
1494 buf.append(" ");
1495
1496 if (!connection.isTransparent()) {
1497 Protocol protocol = connection.getProtocol();
1498 buf.append(protocol.getScheme().toLowerCase());
1499 buf.append("://");
1500 buf.append(connection.getHost());
1501 if ((connection.getPort() != -1)
1502 && (connection.getPort() != protocol.getDefaultPort())
1503 ) {
1504 buf.append(":");
1505 buf.append(connection.getPort());
1506 }
1507 }
1508
1509 if (requestPath == null) {
1510 buf.append("/");
1511 } else {
1512 if (!connection.isTransparent() && !requestPath.startsWith("/")) {
1513 buf.append("/");
1514 }
1515 buf.append(requestPath);
1516 }
1517
1518 if (query != null) {
1519 if (query.indexOf("?") != 0) {
1520 buf.append("?");
1521 }
1522 buf.append(query);
1523 }
1524
1525 buf.append(" ");
1526 buf.append(version);
1527 buf.append("\r\n");
1528
1529 return buf.toString();
1530 }
1531
1532 /***
1533 * This method is invoked immediately after
1534 * {@link #readResponseBody(HttpState,HttpConnection)} and can be overridden by
1535 * sub-classes in order to provide custom body processing.
1536 *
1537 * <p>
1538 * This implementation does nothing.
1539 * </p>
1540 *
1541 * @param state the {@link HttpState state} information associated with this method
1542 * @param conn the {@link HttpConnection connection} used to execute
1543 * this HTTP method
1544 *
1545 * @see #readResponse
1546 * @see #readResponseBody
1547 */
1548 protected void processResponseBody(HttpState state, HttpConnection conn) {
1549 }
1550
1551 /***
1552 * This method is invoked immediately after
1553 * {@link #readResponseHeaders(HttpState,HttpConnection)} and can be overridden by
1554 * sub-classes in order to provide custom response headers processing.
1555
1556 * <p>
1557 * This implementation will handle the <tt>Set-Cookie</tt> and
1558 * <tt>Set-Cookie2</tt> headers, if any, adding the relevant cookies to
1559 * the given {@link HttpState}.
1560 * </p>
1561 *
1562 * @param state the {@link HttpState state} information associated with this method
1563 * @param conn the {@link HttpConnection connection} used to execute
1564 * this HTTP method
1565 *
1566 * @see #readResponse
1567 * @see #readResponseHeaders
1568 */
1569 protected void processResponseHeaders(HttpState state,
1570 HttpConnection conn) {
1571 LOG.trace("enter HttpMethodBase.processResponseHeaders(HttpState, "
1572 + "HttpConnection)");
1573
1574 CookieSpec parser = getCookieSpec(state);
1575
1576
1577 Header[] headers = getResponseHeaderGroup().getHeaders("set-cookie");
1578 processCookieHeaders(parser, headers, state, conn);
1579
1580
1581 if (parser instanceof CookieVersionSupport) {
1582 CookieVersionSupport versupport = (CookieVersionSupport) parser;
1583 if (versupport.getVersion() > 0) {
1584
1585
1586 headers = getResponseHeaderGroup().getHeaders("set-cookie2");
1587 processCookieHeaders(parser, headers, state, conn);
1588 }
1589 }
1590 }
1591
1592 /***
1593 * This method processes the specified cookie headers. It is invoked from
1594 * within {@link #processResponseHeaders(HttpState,HttpConnection)}
1595 *
1596 * @param headers cookie {@link Header}s to be processed
1597 * @param state the {@link HttpState state} information associated with
1598 * this HTTP method
1599 * @param conn the {@link HttpConnection connection} used to execute
1600 * this HTTP method
1601 */
1602 protected void processCookieHeaders(
1603 final CookieSpec parser,
1604 final Header[] headers,
1605 final HttpState state,
1606 final HttpConnection conn) {
1607 LOG.trace("enter HttpMethodBase.processCookieHeaders(Header[], HttpState, "
1608 + "HttpConnection)");
1609
1610 String host = this.params.getVirtualHost();
1611 if (host == null) {
1612 host = conn.getHost();
1613 }
1614 for (int i = 0; i < headers.length; i++) {
1615 Header header = headers[i];
1616 Cookie[] cookies = null;
1617 try {
1618 cookies = parser.parse(
1619 host,
1620 conn.getPort(),
1621 getPath(),
1622 conn.isSecure(),
1623 header);
1624 } catch (MalformedCookieException e) {
1625 if (LOG.isWarnEnabled()) {
1626 LOG.warn("Invalid cookie header: \""
1627 + header.getValue()
1628 + "\". " + e.getMessage());
1629 }
1630 }
1631 if (cookies != null) {
1632 for (int j = 0; j < cookies.length; j++) {
1633 Cookie cookie = cookies[j];
1634 try {
1635 parser.validate(
1636 host,
1637 conn.getPort(),
1638 getPath(),
1639 conn.isSecure(),
1640 cookie);
1641 state.addCookie(cookie);
1642 if (LOG.isDebugEnabled()) {
1643 LOG.debug("Cookie accepted: \""
1644 + parser.formatCookie(cookie) + "\"");
1645 }
1646 } catch (MalformedCookieException e) {
1647 if (LOG.isWarnEnabled()) {
1648 LOG.warn("Cookie rejected: \"" + parser.formatCookie(cookie)
1649 + "\". " + e.getMessage());
1650 }
1651 }
1652 }
1653 }
1654 }
1655 }
1656
1657 /***
1658 * This method is invoked immediately after
1659 * {@link #readStatusLine(HttpState,HttpConnection)} and can be overridden by
1660 * sub-classes in order to provide custom response status line processing.
1661 *
1662 * @param state the {@link HttpState state} information associated with this method
1663 * @param conn the {@link HttpConnection connection} used to execute
1664 * this HTTP method
1665 *
1666 * @see #readResponse
1667 * @see #readStatusLine
1668 */
1669 protected void processStatusLine(HttpState state, HttpConnection conn) {
1670 }
1671
1672 /***
1673 * Reads the response from the given {@link HttpConnection connection}.
1674 *
1675 * <p>
1676 * The response is processed as the following sequence of actions:
1677 *
1678 * <ol>
1679 * <li>
1680 * {@link #readStatusLine(HttpState,HttpConnection)} is
1681 * invoked to read the request line.
1682 * </li>
1683 * <li>
1684 * {@link #processStatusLine(HttpState,HttpConnection)}
1685 * is invoked, allowing the method to process the status line if
1686 * desired.
1687 * </li>
1688 * <li>
1689 * {@link #readResponseHeaders(HttpState,HttpConnection)} is invoked to read
1690 * the associated headers.
1691 * </li>
1692 * <li>
1693 * {@link #processResponseHeaders(HttpState,HttpConnection)} is invoked, allowing
1694 * the method to process the headers if desired.
1695 * </li>
1696 * <li>
1697 * {@link #readResponseBody(HttpState,HttpConnection)} is
1698 * invoked to read the associated body (if any).
1699 * </li>
1700 * <li>
1701 * {@link #processResponseBody(HttpState,HttpConnection)} is invoked, allowing the
1702 * method to process the response body if desired.
1703 * </li>
1704 * </ol>
1705 *
1706 * Subclasses may want to override one or more of the above methods to to
1707 * customize the processing. (Or they may choose to override this method
1708 * if dramatically different processing is required.)
1709 * </p>
1710 *
1711 * @param state the {@link HttpState state} information associated with this method
1712 * @param conn the {@link HttpConnection connection} used to execute
1713 * this HTTP method
1714 *
1715 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1716 * can be recovered from.
1717 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1718 * cannot be recovered from.
1719 */
1720 protected void readResponse(HttpState state, HttpConnection conn)
1721 throws IOException, HttpException {
1722 LOG.trace(
1723 "enter HttpMethodBase.readResponse(HttpState, HttpConnection)");
1724
1725
1726 while (this.statusLine == null) {
1727 readStatusLine(state, conn);
1728 processStatusLine(state, conn);
1729 readResponseHeaders(state, conn);
1730 processResponseHeaders(state, conn);
1731
1732 int status = this.statusLine.getStatusCode();
1733 if ((status >= 100) && (status < 200)) {
1734 if (LOG.isInfoEnabled()) {
1735 LOG.info("Discarding unexpected response: " + this.statusLine.toString());
1736 }
1737 this.statusLine = null;
1738 }
1739 }
1740 readResponseBody(state, conn);
1741 processResponseBody(state, conn);
1742 }
1743
1744 /***
1745 * Read the response body from the given {@link HttpConnection}.
1746 *
1747 * <p>
1748 * The current implementation wraps the socket level stream with
1749 * an appropriate stream for the type of response (chunked, content-length,
1750 * or auto-close). If there is no response body, the connection associated
1751 * with the request will be returned to the connection manager.
1752 * </p>
1753 *
1754 * <p>
1755 * Subclasses may want to override this method to to customize the
1756 * processing.
1757 * </p>
1758 *
1759 * @param state the {@link HttpState state} information associated with this method
1760 * @param conn the {@link HttpConnection connection} used to execute
1761 * this HTTP method
1762 *
1763 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1764 * can be recovered from.
1765 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1766 * cannot be recovered from.
1767 *
1768 * @see #readResponse
1769 * @see #processResponseBody
1770 */
1771 protected void readResponseBody(HttpState state, HttpConnection conn)
1772 throws IOException, HttpException {
1773 LOG.trace(
1774 "enter HttpMethodBase.readResponseBody(HttpState, HttpConnection)");
1775
1776
1777 InputStream stream = readResponseBody(conn);
1778 if (stream == null) {
1779
1780 responseBodyConsumed();
1781 } else {
1782 conn.setLastResponseInputStream(stream);
1783 setResponseStream(stream);
1784 }
1785 }
1786
1787 /***
1788 * Returns the response body as an {@link InputStream input stream}
1789 * corresponding to the values of the <tt>Content-Length</tt> and
1790 * <tt>Transfer-Encoding</tt> headers. If no response body is available
1791 * returns <tt>null</tt>.
1792 * <p>
1793 *
1794 * @see #readResponse
1795 * @see #processResponseBody
1796 *
1797 * @param conn the {@link HttpConnection connection} used to execute
1798 * this HTTP method
1799 *
1800 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1801 * can be recovered from.
1802 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1803 * cannot be recovered from.
1804 */
1805 private InputStream readResponseBody(HttpConnection conn)
1806 throws HttpException, IOException {
1807
1808 LOG.trace("enter HttpMethodBase.readResponseBody(HttpConnection)");
1809
1810 responseBody = null;
1811 InputStream is = conn.getResponseInputStream();
1812 if (Wire.CONTENT_WIRE.enabled()) {
1813 is = new WireLogInputStream(is, Wire.CONTENT_WIRE);
1814 }
1815 boolean canHaveBody = canResponseHaveBody(statusLine.getStatusCode());
1816 InputStream result = null;
1817 Header transferEncodingHeader = responseHeaders.getFirstHeader("Transfer-Encoding");
1818
1819
1820 if (transferEncodingHeader != null) {
1821
1822 String transferEncoding = transferEncodingHeader.getValue();
1823 if (!"chunked".equalsIgnoreCase(transferEncoding)
1824 && !"identity".equalsIgnoreCase(transferEncoding)) {
1825 if (LOG.isWarnEnabled()) {
1826 LOG.warn("Unsupported transfer encoding: " + transferEncoding);
1827 }
1828 }
1829 HeaderElement[] encodings = transferEncodingHeader.getElements();
1830
1831
1832 int len = encodings.length;
1833 if ((len > 0) && ("chunked".equalsIgnoreCase(encodings[len - 1].getName()))) {
1834
1835 if (conn.isResponseAvailable(conn.getParams().getSoTimeout())) {
1836 result = new ChunkedInputStream(is, this);
1837 } else {
1838 if (getParams().isParameterTrue(HttpMethodParams.STRICT_TRANSFER_ENCODING)) {
1839 throw new ProtocolException("Chunk-encoded body declared but not sent");
1840 } else {
1841 LOG.warn("Chunk-encoded body missing");
1842 }
1843 }
1844 } else {
1845 LOG.info("Response content is not chunk-encoded");
1846
1847
1848 setConnectionCloseForced(true);
1849 result = is;
1850 }
1851 } else {
1852 long expectedLength = getResponseContentLength();
1853 if (expectedLength == -1) {
1854 if (canHaveBody && this.effectiveVersion.greaterEquals(HttpVersion.HTTP_1_1)) {
1855 Header connectionHeader = responseHeaders.getFirstHeader("Connection");
1856 String connectionDirective = null;
1857 if (connectionHeader != null) {
1858 connectionDirective = connectionHeader.getValue();
1859 }
1860 if (!"close".equalsIgnoreCase(connectionDirective)) {
1861 LOG.info("Response content length is not known");
1862 setConnectionCloseForced(true);
1863 }
1864 }
1865 result = is;
1866 } else {
1867 result = new ContentLengthInputStream(is, expectedLength);
1868 }
1869 }
1870
1871
1872 if (!canHaveBody) {
1873 result = null;
1874 }
1875
1876
1877
1878 if (result != null) {
1879
1880 result = new AutoCloseInputStream(
1881 result,
1882 new ResponseConsumedWatcher() {
1883 public void responseConsumed() {
1884 responseBodyConsumed();
1885 }
1886 }
1887 );
1888 }
1889
1890 return result;
1891 }
1892
1893 /***
1894 * Reads the response headers from the given {@link HttpConnection connection}.
1895 *
1896 * <p>
1897 * Subclasses may want to override this method to to customize the
1898 * processing.
1899 * </p>
1900 *
1901 * <p>
1902 * "It must be possible to combine the multiple header fields into one
1903 * "field-name: field-value" pair, without changing the semantics of the
1904 * message, by appending each subsequent field-value to the first, each
1905 * separated by a comma." - HTTP/1.0 (4.3)
1906 * </p>
1907 *
1908 * @param state the {@link HttpState state} information associated with this method
1909 * @param conn the {@link HttpConnection connection} used to execute
1910 * this HTTP method
1911 *
1912 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1913 * can be recovered from.
1914 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1915 * cannot be recovered from.
1916 *
1917 * @see #readResponse
1918 * @see #processResponseHeaders
1919 */
1920 protected void readResponseHeaders(HttpState state, HttpConnection conn)
1921 throws IOException, HttpException {
1922 LOG.trace("enter HttpMethodBase.readResponseHeaders(HttpState,"
1923 + "HttpConnection)");
1924
1925 getResponseHeaderGroup().clear();
1926
1927 Header[] headers = HttpParser.parseHeaders(
1928 conn.getResponseInputStream(), getParams().getHttpElementCharset());
1929 if (Wire.HEADER_WIRE.enabled()) {
1930 for (int i = 0; i < headers.length; i++) {
1931 Wire.HEADER_WIRE.input(headers[i].toExternalForm());
1932 }
1933 }
1934 getResponseHeaderGroup().setHeaders(headers);
1935 }
1936
1937 /***
1938 * Read the status line from the given {@link HttpConnection}, setting my
1939 * {@link #getStatusCode status code} and {@link #getStatusText status
1940 * text}.
1941 *
1942 * <p>
1943 * Subclasses may want to override this method to to customize the
1944 * processing.
1945 * </p>
1946 *
1947 * @param state the {@link HttpState state} information associated with this method
1948 * @param conn the {@link HttpConnection connection} used to execute
1949 * this HTTP method
1950 *
1951 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
1952 * can be recovered from.
1953 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
1954 * cannot be recovered from.
1955 *
1956 * @see StatusLine
1957 */
1958 protected void readStatusLine(HttpState state, HttpConnection conn)
1959 throws IOException, HttpException {
1960 LOG.trace("enter HttpMethodBase.readStatusLine(HttpState, HttpConnection)");
1961
1962 final int maxGarbageLines = getParams().
1963 getIntParameter(HttpMethodParams.STATUS_LINE_GARBAGE_LIMIT, Integer.MAX_VALUE);
1964
1965
1966 int count = 0;
1967 String s;
1968 do {
1969 s = conn.readLine(getParams().getHttpElementCharset());
1970 if (s == null && count == 0) {
1971
1972 throw new NoHttpResponseException("The server " + conn.getHost() +
1973 " failed to respond");
1974 }
1975 if (Wire.HEADER_WIRE.enabled()) {
1976 Wire.HEADER_WIRE.input(s + "\r\n");
1977 }
1978 if (s != null && StatusLine.startsWithHTTP(s)) {
1979
1980 break;
1981 } else if (s == null || count >= maxGarbageLines) {
1982
1983 throw new ProtocolException("The server " + conn.getHost() +
1984 " failed to respond with a valid HTTP response");
1985 }
1986 count++;
1987 } while(true);
1988
1989
1990 statusLine = new StatusLine(s);
1991
1992
1993 String versionStr = statusLine.getHttpVersion();
1994 if (getParams().isParameterFalse(HttpMethodParams.UNAMBIGUOUS_STATUS_LINE)
1995 && versionStr.equals("HTTP")) {
1996 getParams().setVersion(HttpVersion.HTTP_1_0);
1997 if (LOG.isWarnEnabled()) {
1998 LOG.warn("Ambiguous status line (HTTP protocol version missing):" +
1999 statusLine.toString());
2000 }
2001 } else {
2002 this.effectiveVersion = HttpVersion.parse(versionStr);
2003 }
2004
2005 }
2006
2007
2008
2009 /***
2010 * <p>
2011 * Sends the request via the given {@link HttpConnection connection}.
2012 * </p>
2013 *
2014 * <p>
2015 * The request is written as the following sequence of actions:
2016 * </p>
2017 *
2018 * <ol>
2019 * <li>
2020 * {@link #writeRequestLine(HttpState, HttpConnection)} is invoked to
2021 * write the request line.
2022 * </li>
2023 * <li>
2024 * {@link #writeRequestHeaders(HttpState, HttpConnection)} is invoked
2025 * to write the associated headers.
2026 * </li>
2027 * <li>
2028 * <tt>\r\n</tt> is sent to close the head part of the request.
2029 * </li>
2030 * <li>
2031 * {@link #writeRequestBody(HttpState, HttpConnection)} is invoked to
2032 * write the body part of the request.
2033 * </li>
2034 * </ol>
2035 *
2036 * <p>
2037 * Subclasses may want to override one or more of the above methods to to
2038 * customize the processing. (Or they may choose to override this method
2039 * if dramatically different processing is required.)
2040 * </p>
2041 *
2042 * @param state the {@link HttpState state} information associated with this method
2043 * @param conn the {@link HttpConnection connection} used to execute
2044 * this HTTP method
2045 *
2046 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2047 * can be recovered from.
2048 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
2049 * cannot be recovered from.
2050 */
2051 protected void writeRequest(HttpState state, HttpConnection conn)
2052 throws IOException, HttpException {
2053 LOG.trace(
2054 "enter HttpMethodBase.writeRequest(HttpState, HttpConnection)");
2055 writeRequestLine(state, conn);
2056 writeRequestHeaders(state, conn);
2057 conn.writeLine();
2058 if (Wire.HEADER_WIRE.enabled()) {
2059 Wire.HEADER_WIRE.output("\r\n");
2060 }
2061
2062 HttpVersion ver = getParams().getVersion();
2063 Header expectheader = getRequestHeader("Expect");
2064 String expectvalue = null;
2065 if (expectheader != null) {
2066 expectvalue = expectheader.getValue();
2067 }
2068 if ((expectvalue != null)
2069 && (expectvalue.compareToIgnoreCase("100-continue") == 0)) {
2070 if (ver.greaterEquals(HttpVersion.HTTP_1_1)) {
2071
2072
2073 conn.flushRequestOutputStream();
2074
2075 int readTimeout = conn.getParams().getSoTimeout();
2076 try {
2077 conn.setSocketTimeout(RESPONSE_WAIT_TIME_MS);
2078 readStatusLine(state, conn);
2079 processStatusLine(state, conn);
2080 readResponseHeaders(state, conn);
2081 processResponseHeaders(state, conn);
2082
2083 if (this.statusLine.getStatusCode() == HttpStatus.SC_CONTINUE) {
2084
2085 this.statusLine = null;
2086 LOG.debug("OK to continue received");
2087 } else {
2088 return;
2089 }
2090 } catch (InterruptedIOException e) {
2091 if (!ExceptionUtil.isSocketTimeoutException(e)) {
2092 throw e;
2093 }
2094
2095
2096
2097 removeRequestHeader("Expect");
2098 LOG.info("100 (continue) read timeout. Resume sending the request");
2099 } finally {
2100 conn.setSocketTimeout(readTimeout);
2101 }
2102
2103 } else {
2104 removeRequestHeader("Expect");
2105 LOG.info("'Expect: 100-continue' handshake is only supported by "
2106 + "HTTP/1.1 or higher");
2107 }
2108 }
2109
2110 writeRequestBody(state, conn);
2111
2112 conn.flushRequestOutputStream();
2113 }
2114
2115 /***
2116 * Writes the request body to the given {@link HttpConnection connection}.
2117 *
2118 * <p>
2119 * This method should return <tt>true</tt> if the request body was actually
2120 * sent (or is empty), or <tt>false</tt> if it could not be sent for some
2121 * reason.
2122 * </p>
2123 *
2124 * <p>
2125 * This implementation writes nothing and returns <tt>true</tt>.
2126 * </p>
2127 *
2128 * @param state the {@link HttpState state} information associated with this method
2129 * @param conn the {@link HttpConnection connection} used to execute
2130 * this HTTP method
2131 *
2132 * @return <tt>true</tt>
2133 *
2134 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2135 * can be recovered from.
2136 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
2137 * cannot be recovered from.
2138 */
2139 protected boolean writeRequestBody(HttpState state, HttpConnection conn)
2140 throws IOException, HttpException {
2141 return true;
2142 }
2143
2144 /***
2145 * Writes the request headers to the given {@link HttpConnection connection}.
2146 *
2147 * <p>
2148 * This implementation invokes {@link #addRequestHeaders(HttpState,HttpConnection)},
2149 * and then writes each header to the request stream.
2150 * </p>
2151 *
2152 * <p>
2153 * Subclasses may want to override this method to to customize the
2154 * processing.
2155 * </p>
2156 *
2157 * @param state the {@link HttpState state} information associated with this method
2158 * @param conn the {@link HttpConnection connection} used to execute
2159 * this HTTP method
2160 *
2161 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2162 * can be recovered from.
2163 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
2164 * cannot be recovered from.
2165 *
2166 * @see #addRequestHeaders
2167 * @see #getRequestHeaders
2168 */
2169 protected void writeRequestHeaders(HttpState state, HttpConnection conn)
2170 throws IOException, HttpException {
2171 LOG.trace("enter HttpMethodBase.writeRequestHeaders(HttpState,"
2172 + "HttpConnection)");
2173 addRequestHeaders(state, conn);
2174
2175 String charset = getParams().getHttpElementCharset();
2176
2177 Header[] headers = getRequestHeaders();
2178 for (int i = 0; i < headers.length; i++) {
2179 String s = headers[i].toExternalForm();
2180 if (Wire.HEADER_WIRE.enabled()) {
2181 Wire.HEADER_WIRE.output(s);
2182 }
2183 conn.print(s, charset);
2184 }
2185 }
2186
2187 /***
2188 * Writes the request line to the given {@link HttpConnection connection}.
2189 *
2190 * <p>
2191 * Subclasses may want to override this method to to customize the
2192 * processing.
2193 * </p>
2194 *
2195 * @param state the {@link HttpState state} information associated with this method
2196 * @param conn the {@link HttpConnection connection} used to execute
2197 * this HTTP method
2198 *
2199 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
2200 * can be recovered from.
2201 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
2202 * cannot be recovered from.
2203 *
2204 * @see #generateRequestLine
2205 */
2206 protected void writeRequestLine(HttpState state, HttpConnection conn)
2207 throws IOException, HttpException {
2208 LOG.trace(
2209 "enter HttpMethodBase.writeRequestLine(HttpState, HttpConnection)");
2210 String requestLine = getRequestLine(conn);
2211 if (Wire.HEADER_WIRE.enabled()) {
2212 Wire.HEADER_WIRE.output(requestLine);
2213 }
2214 conn.print(requestLine, getParams().getHttpElementCharset());
2215 }
2216
2217 /***
2218 * Returns the request line.
2219 *
2220 * @param conn the {@link HttpConnection connection} used to execute
2221 * this HTTP method
2222 *
2223 * @return The request line.
2224 */
2225 private String getRequestLine(HttpConnection conn) {
2226 return HttpMethodBase.generateRequestLine(conn, getName(),
2227 getPath(), getQueryString(), this.effectiveVersion.toString());
2228 }
2229
2230 /***
2231 * Returns {@link HttpMethodParams HTTP protocol parameters} associated with this method.
2232 *
2233 * @return HTTP parameters.
2234 *
2235 * @since 3.0
2236 */
2237 public HttpMethodParams getParams() {
2238 return this.params;
2239 }
2240
2241 /***
2242 * Assigns {@link HttpMethodParams HTTP protocol parameters} for this method.
2243 *
2244 * @since 3.0
2245 *
2246 * @see HttpMethodParams
2247 */
2248 public void setParams(final HttpMethodParams params) {
2249 if (params == null) {
2250 throw new IllegalArgumentException("Parameters may not be null");
2251 }
2252 this.params = params;
2253 }
2254
2255 /***
2256 * Returns the HTTP version used with this method (may be <tt>null</tt>
2257 * if undefined, that is, the method has not been executed)
2258 *
2259 * @return HTTP version.
2260 *
2261 * @since 3.0
2262 */
2263 public HttpVersion getEffectiveVersion() {
2264 return this.effectiveVersion;
2265 }
2266
2267 /***
2268 * Per RFC 2616 section 4.3, some response can never contain a message
2269 * body.
2270 *
2271 * @param status - the HTTP status code
2272 *
2273 * @return <tt>true</tt> if the message may contain a body, <tt>false</tt> if it can not
2274 * contain a message body
2275 */
2276 private static boolean canResponseHaveBody(int status) {
2277 LOG.trace("enter HttpMethodBase.canResponseHaveBody(int)");
2278
2279 boolean result = true;
2280
2281 if ((status >= 100 && status <= 199) || (status == 204)
2282 || (status == 304)) {
2283 result = false;
2284 }
2285
2286 return result;
2287 }
2288
2289 /***
2290 * Returns proxy authentication realm, if it has been used during authentication process.
2291 * Otherwise returns <tt>null</tt>.
2292 *
2293 * @return proxy authentication realm
2294 *
2295 * @deprecated use #getProxyAuthState()
2296 */
2297 public String getProxyAuthenticationRealm() {
2298 return this.proxyAuthState.getRealm();
2299 }
2300
2301 /***
2302 * Returns authentication realm, if it has been used during authentication process.
2303 * Otherwise returns <tt>null</tt>.
2304 *
2305 * @return authentication realm
2306 *
2307 * @deprecated use #getHostAuthState()
2308 */
2309 public String getAuthenticationRealm() {
2310 return this.hostAuthState.getRealm();
2311 }
2312
2313 /***
2314 * Returns the character set from the <tt>Content-Type</tt> header.
2315 *
2316 * @param contentheader The content header.
2317 * @return String The character set.
2318 */
2319 protected String getContentCharSet(Header contentheader) {
2320 LOG.trace("enter getContentCharSet( Header contentheader )");
2321 String charset = null;
2322 if (contentheader != null) {
2323 HeaderElement values[] = contentheader.getElements();
2324
2325
2326 if (values.length == 1) {
2327 NameValuePair param = values[0].getParameterByName("charset");
2328 if (param != null) {
2329
2330
2331 charset = param.getValue();
2332 }
2333 }
2334 }
2335 if (charset == null) {
2336 charset = getParams().getContentCharset();
2337 if (LOG.isDebugEnabled()) {
2338 LOG.debug("Default charset used: " + charset);
2339 }
2340 }
2341 return charset;
2342 }
2343
2344
2345 /***
2346 * Returns the character encoding of the request from the <tt>Content-Type</tt> header.
2347 *
2348 * @return String The character set.
2349 */
2350 public String getRequestCharSet() {
2351 return getContentCharSet(getRequestHeader("Content-Type"));
2352 }
2353
2354
2355 /***
2356 * Returns the character encoding of the response from the <tt>Content-Type</tt> header.
2357 *
2358 * @return String The character set.
2359 */
2360 public String getResponseCharSet() {
2361 return getContentCharSet(getResponseHeader("Content-Type"));
2362 }
2363
2364 /***
2365 * @deprecated no longer used
2366 *
2367 * Returns the number of "recoverable" exceptions thrown and handled, to
2368 * allow for monitoring the quality of the connection.
2369 *
2370 * @return The number of recoverable exceptions handled by the method.
2371 */
2372 public int getRecoverableExceptionCount() {
2373 return recoverableExceptionCount;
2374 }
2375
2376 /***
2377 * A response has been consumed.
2378 *
2379 * <p>The default behavior for this class is to check to see if the connection
2380 * should be closed, and close if need be, and to ensure that the connection
2381 * is returned to the connection manager - if and only if we are not still
2382 * inside the execute call.</p>
2383 *
2384 */
2385 protected void responseBodyConsumed() {
2386
2387
2388
2389 responseStream = null;
2390 if (responseConnection != null) {
2391 responseConnection.setLastResponseInputStream(null);
2392
2393
2394
2395
2396
2397 if (shouldCloseConnection(responseConnection)) {
2398 responseConnection.close();
2399 } else {
2400 try {
2401 if(responseConnection.isResponseAvailable()) {
2402 boolean logExtraInput =
2403 getParams().isParameterTrue(HttpMethodParams.WARN_EXTRA_INPUT);
2404
2405 if(logExtraInput) {
2406 LOG.warn("Extra response data detected - closing connection");
2407 }
2408 responseConnection.close();
2409 }
2410 }
2411 catch (IOException e) {
2412 LOG.warn(e.getMessage());
2413 responseConnection.close();
2414 }
2415 }
2416 }
2417 this.connectionCloseForced = false;
2418 ensureConnectionRelease();
2419 }
2420
2421 /***
2422 * Insure that the connection is released back to the pool.
2423 */
2424 private void ensureConnectionRelease() {
2425 if (responseConnection != null) {
2426 responseConnection.releaseConnection();
2427 responseConnection = null;
2428 }
2429 }
2430
2431 /***
2432 * Returns the {@link HostConfiguration host configuration}.
2433 *
2434 * @return the host configuration
2435 *
2436 * @deprecated no longer applicable
2437 */
2438 public HostConfiguration getHostConfiguration() {
2439 HostConfiguration hostconfig = new HostConfiguration();
2440 hostconfig.setHost(this.httphost);
2441 return hostconfig;
2442 }
2443 /***
2444 * Sets the {@link HostConfiguration host configuration}.
2445 *
2446 * @param hostconfig The hostConfiguration to set
2447 *
2448 * @deprecated no longer applicable
2449 */
2450 public void setHostConfiguration(final HostConfiguration hostconfig) {
2451 if (hostconfig != null) {
2452 this.httphost = new HttpHost(
2453 hostconfig.getHost(),
2454 hostconfig.getPort(),
2455 hostconfig.getProtocol());
2456 } else {
2457 this.httphost = null;
2458 }
2459 }
2460
2461 /***
2462 * Returns the {@link MethodRetryHandler retry handler} for this HTTP method
2463 *
2464 * @return the methodRetryHandler
2465 *
2466 * @deprecated use {@link HttpMethodParams}
2467 */
2468 public MethodRetryHandler getMethodRetryHandler() {
2469 return methodRetryHandler;
2470 }
2471
2472 /***
2473 * Sets the {@link MethodRetryHandler retry handler} for this HTTP method
2474 *
2475 * @param handler the methodRetryHandler to use when this method executed
2476 *
2477 * @deprecated use {@link HttpMethodParams}
2478 */
2479 public void setMethodRetryHandler(MethodRetryHandler handler) {
2480 methodRetryHandler = handler;
2481 }
2482
2483 /***
2484 * This method is a dirty hack intended to work around
2485 * current (2.0) design flaw that prevents the user from
2486 * obtaining correct status code, headers and response body from the
2487 * preceding HTTP CONNECT method.
2488 *
2489 * TODO: Remove this crap as soon as possible
2490 */
2491 void fakeResponse(
2492 StatusLine statusline,
2493 HeaderGroup responseheaders,
2494 InputStream responseStream
2495 ) {
2496
2497 this.used = true;
2498 this.statusLine = statusline;
2499 this.responseHeaders = responseheaders;
2500 this.responseBody = null;
2501 this.responseStream = responseStream;
2502 }
2503
2504 /***
2505 * Returns the target host {@link AuthState authentication state}
2506 *
2507 * @return host authentication state
2508 *
2509 * @since 3.0
2510 */
2511 public AuthState getHostAuthState() {
2512 return this.hostAuthState;
2513 }
2514
2515 /***
2516 * Returns the proxy {@link AuthState authentication state}
2517 *
2518 * @return host authentication state
2519 *
2520 * @since 3.0
2521 */
2522 public AuthState getProxyAuthState() {
2523 return this.proxyAuthState;
2524 }
2525
2526 /***
2527 * Tests whether the execution of this method has been aborted
2528 *
2529 * @return <tt>true</tt> if the execution of this method has been aborted,
2530 * <tt>false</tt> otherwise
2531 *
2532 * @since 3.0
2533 */
2534 public boolean isAborted() {
2535 return this.aborted;
2536 }
2537
2538 /***
2539 * Returns <tt>true</tt> if the HTTP has been transmitted to the target
2540 * server in its entirety, <tt>false</tt> otherwise. This flag can be useful
2541 * for recovery logic. If the request has not been transmitted in its entirety,
2542 * it is safe to retry the failed method.
2543 *
2544 * @return <tt>true</tt> if the request has been sent, <tt>false</tt> otherwise
2545 */
2546 public boolean isRequestSent() {
2547 return this.requestSent;
2548 }
2549
2550 }