1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.apache.commons.httpclient;
31
32 import java.io.BufferedInputStream;
33 import java.io.BufferedOutputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.InterruptedIOException;
37 import java.io.OutputStream;
38 import java.lang.reflect.Method;
39 import java.net.InetAddress;
40 import java.net.Socket;
41 import java.net.SocketException;
42
43 import org.apache.commons.httpclient.params.HttpConnectionParams;
44 import org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory;
45 import org.apache.commons.httpclient.protocol.Protocol;
46 import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
47 import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
48 import org.apache.commons.httpclient.util.EncodingUtil;
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51
52 /***
53 * An abstraction of an HTTP {@link InputStream} and {@link OutputStream}
54 * pair, together with the relevant attributes.
55 * <p>
56 * The following options are set on the socket before getting the input/output
57 * streams in the {@link #open()} method:
58 * <table border=1><tr>
59 * <th>Socket Method
60 * <th>Sockets Option
61 * <th>Configuration
62 * </tr><tr>
63 * <td>{@link java.net.Socket#setTcpNoDelay(boolean)}
64 * <td>SO_NODELAY
65 * <td>{@link HttpConnectionParams#setTcpNoDelay(boolean)}
66 * </tr><tr>
67 * <td>{@link java.net.Socket#setSoTimeout(int)}
68 * <td>SO_TIMEOUT
69 * <td>{@link HttpConnectionParams#setSoTimeout(int)}
70 * </tr><tr>
71 * <td>{@link java.net.Socket#setSendBufferSize(int)}
72 * <td>SO_SNDBUF
73 * <td>{@link HttpConnectionParams#setSendBufferSize(int)}
74 * </tr><tr>
75 * <td>{@link java.net.Socket#setReceiveBufferSize(int)}
76 * <td>SO_RCVBUF
77 * <td>{@link HttpConnectionParams#setReceiveBufferSize(int)}
78 * </tr></table>
79 *
80 * @author Rod Waldhoff
81 * @author Sean C. Sullivan
82 * @author Ortwin Gl??ck
83 * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
84 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
85 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
86 * @author Michael Becke
87 * @author Eric E Johnson
88 * @author Laura Werner
89 *
90 * @version $Revision: 1.100 $ $Date: 2004/09/15 20:32:21 $
91 */
92 public class HttpConnection {
93
94
95
96 /***
97 * Creates a new HTTP connection for the given host and port.
98 *
99 * @param host the host to connect to
100 * @param port the port to connect to
101 */
102 public HttpConnection(String host, int port) {
103 this(null, -1, host, null, port, Protocol.getProtocol("http"));
104 }
105
106 /***
107 * Creates a new HTTP connection for the given host and port
108 * using the given protocol.
109 *
110 * @param host the host to connect to
111 * @param port the port to connect to
112 * @param protocol the protocol to use
113 */
114 public HttpConnection(String host, int port, Protocol protocol) {
115 this(null, -1, host, null, port, protocol);
116 }
117
118 /***
119 * Creates a new HTTP connection for the given host with the virtual
120 * alias and port using given protocol.
121 *
122 * @param host the host to connect to
123 * @param virtualHost the virtual host requests will be sent to
124 * @param port the port to connect to
125 * @param protocol the protocol to use
126 */
127 public HttpConnection(String host, String virtualHost, int port, Protocol protocol) {
128 this(null, -1, host, virtualHost, port, protocol);
129 }
130
131 /***
132 * Creates a new HTTP connection for the given host and port via the
133 * given proxy host and port using the default protocol.
134 *
135 * @param proxyHost the host to proxy via
136 * @param proxyPort the port to proxy via
137 * @param host the host to connect to
138 * @param port the port to connect to
139 */
140 public HttpConnection(
141 String proxyHost,
142 int proxyPort,
143 String host,
144 int port) {
145 this(proxyHost, proxyPort, host, null, port, Protocol.getProtocol("http"));
146 }
147
148 /***
149 * Creates a new HTTP connection for the given host configuration.
150 *
151 * @param hostConfiguration the host/proxy/protocol to use
152 */
153 public HttpConnection(HostConfiguration hostConfiguration) {
154 this(hostConfiguration.getProxyHost(),
155 hostConfiguration.getProxyPort(),
156 hostConfiguration.getHost(),
157 hostConfiguration.getVirtualHost(),
158 hostConfiguration.getPort(),
159 hostConfiguration.getProtocol());
160 this.localAddress = hostConfiguration.getLocalAddress();
161 }
162
163 /***
164 * Creates a new HTTP connection for the given host with the virtual
165 * alias and port via the given proxy host and port using the given
166 * protocol.
167 *
168 * @param proxyHost the host to proxy via
169 * @param proxyPort the port to proxy via
170 * @param host the host to connect to. Parameter value must be non-null.
171 * @param virtualHost the virtual host requests will be sent to
172 * @param port the port to connect to
173 * @param protocol The protocol to use. Parameter value must be non-null.
174 */
175 public HttpConnection(
176 String proxyHost,
177 int proxyPort,
178 String host,
179 String virtualHost,
180 int port,
181 Protocol protocol) {
182
183 if (host == null) {
184 throw new IllegalArgumentException("host parameter is null");
185 }
186 if (protocol == null) {
187 throw new IllegalArgumentException("protocol is null");
188 }
189
190 proxyHostName = proxyHost;
191 proxyPortNumber = proxyPort;
192 hostName = host;
193 virtualName = virtualHost;
194 portNumber = protocol.resolvePort(port);
195 protocolInUse = protocol;
196 }
197
198
199
200 /***
201 * Returns the connection socket.
202 *
203 * @return the socket.
204 *
205 * @since 3.0
206 */
207 protected Socket getSocket() {
208 return this.socket;
209 }
210
211 /***
212 * Returns the host.
213 *
214 * @return the host.
215 */
216 public String getHost() {
217 return hostName;
218 }
219
220 /***
221 * Sets the host to connect to.
222 *
223 * @param host the host to connect to. Parameter value must be non-null.
224 * @throws IllegalStateException if the connection is already open
225 */
226 public void setHost(String host) throws IllegalStateException {
227 if (host == null) {
228 throw new IllegalArgumentException("host parameter is null");
229 }
230 assertNotOpen();
231 hostName = host;
232 }
233
234 /***
235 * Returns the target virtual host.
236 *
237 * @return the virtual host.
238 */
239 public String getVirtualHost() {
240 return virtualName;
241 }
242
243 /***
244 * Sets the virtual host to target.
245 *
246 * @param host the virtual host name that should be used instead of
247 * physical host name when sending HTTP requests. Virtual host
248 * name can be set to <tt> null</tt> if virtual host name is not
249 * to be used
250 *
251 * @throws IllegalStateException if the connection is already open
252 */
253 public void setVirtualHost(String host) throws IllegalStateException {
254 assertNotOpen();
255 virtualName = host;
256 }
257
258 /***
259 * Returns the port of the host.
260 *
261 * If the port is -1 (or less than 0) the default port for
262 * the current protocol is returned.
263 *
264 * @return the port.
265 */
266 public int getPort() {
267 if (portNumber < 0) {
268 return isSecure() ? 443 : 80;
269 } else {
270 return portNumber;
271 }
272 }
273
274 /***
275 * Sets the port to connect to.
276 *
277 * @param port the port to connect to
278 *
279 * @throws IllegalStateException if the connection is already open
280 */
281 public void setPort(int port) throws IllegalStateException {
282 assertNotOpen();
283 portNumber = port;
284 }
285
286 /***
287 * Returns the proxy host.
288 *
289 * @return the proxy host.
290 */
291 public String getProxyHost() {
292 return proxyHostName;
293 }
294
295 /***
296 * Sets the host to proxy through.
297 *
298 * @param host the host to proxy through.
299 *
300 * @throws IllegalStateException if the connection is already open
301 */
302 public void setProxyHost(String host) throws IllegalStateException {
303 assertNotOpen();
304 proxyHostName = host;
305 }
306
307 /***
308 * Returns the port of the proxy host.
309 *
310 * @return the proxy port.
311 */
312 public int getProxyPort() {
313 return proxyPortNumber;
314 }
315
316 /***
317 * Sets the port of the host to proxy through.
318 *
319 * @param port the port of the host to proxy through.
320 *
321 * @throws IllegalStateException if the connection is already open
322 */
323 public void setProxyPort(int port) throws IllegalStateException {
324 assertNotOpen();
325 proxyPortNumber = port;
326 }
327
328 /***
329 * Returns <tt>true</tt> if the connection is established over
330 * a secure protocol.
331 *
332 * @return <tt>true</tt> if connected over a secure protocol.
333 */
334 public boolean isSecure() {
335 return protocolInUse.isSecure();
336 }
337
338 /***
339 * Returns the protocol used to establish the connection.
340 * @return The protocol
341 */
342 public Protocol getProtocol() {
343 return protocolInUse;
344 }
345
346 /***
347 * Sets the protocol used to establish the connection
348 *
349 * @param protocol The protocol to use.
350 *
351 * @throws IllegalStateException if the connection is already open
352 */
353 public void setProtocol(Protocol protocol) {
354 assertNotOpen();
355
356 if (protocol == null) {
357 throw new IllegalArgumentException("protocol is null");
358 }
359
360 protocolInUse = protocol;
361
362 }
363
364 /***
365 * Return the local address used when creating the connection.
366 * If <tt>null</tt>, the default address is used.
367 *
368 * @return InetAddress the local address to be used when creating Sockets
369 */
370 public InetAddress getLocalAddress() {
371 return this.localAddress;
372 }
373
374 /***
375 * Set the local address used when creating the connection.
376 * If unset or <tt>null</tt>, the default address is used.
377 *
378 * @param localAddress the local address to use
379 */
380 public void setLocalAddress(InetAddress localAddress) {
381 assertNotOpen();
382 this.localAddress = localAddress;
383 }
384
385 /***
386 * Tests if the connection is open.
387 *
388 * @return <code>true</code> if the connection is open
389 */
390 public boolean isOpen() {
391 return isOpen;
392 }
393
394 /***
395 * Closes the connection if stale.
396 *
397 * @return <code>true</code> if the connection was stale and therefore closed,
398 * <code>false</code> otherwise.
399 *
400 * @see #isStale()
401 */
402 public boolean closeIfStale() {
403 if (used && isOpen && isStale()) {
404 LOG.debug("Connection is stale, closing...");
405 close();
406 return true;
407 }
408 return false;
409 }
410
411 /***
412 * Tests if stale checking is enabled.
413 *
414 * @return <code>true</code> if enabled
415 *
416 * @see #isStale()
417 *
418 * @deprecated Use {@link HttpConnectionParams#isStaleCheckingEnabled()},
419 * {@link HttpConnection#getParams()}.
420 */
421 public boolean isStaleCheckingEnabled() {
422 return this.params.isStaleCheckingEnabled();
423 }
424
425 /***
426 * Sets whether or not isStale() will be called when testing if this connection is open.
427 *
428 * <p>Setting this flag to <code>false</code> will increase performance when reusing
429 * connections, but it will also make them less reliable. Stale checking ensures that
430 * connections are viable before they are used. When set to <code>false</code> some
431 * method executions will result in IOExceptions and they will have to be retried.</p>
432 *
433 * @param staleCheckEnabled <code>true</code> to enable isStale()
434 *
435 * @see #isStale()
436 * @see #isOpen()
437 *
438 * @deprecated Use {@link HttpConnectionParams#setStaleCheckingEnabled(boolean)},
439 * {@link HttpConnection#getParams()}.
440 */
441 public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
442 this.params.setStaleCheckingEnabled(staleCheckEnabled);
443 }
444
445 /***
446 * Determines whether this connection is "stale", which is to say that either
447 * it is no longer open, or an attempt to read the connection would fail.
448 *
449 * <p>Unfortunately, due to the limitations of the JREs prior to 1.4, it is
450 * not possible to test a connection to see if both the read and write channels
451 * are open - except by reading and writing. This leads to a difficulty when
452 * some connections leave the "write" channel open, but close the read channel
453 * and ignore the request. This function attempts to ameliorate that
454 * problem by doing a test read, assuming that the caller will be doing a
455 * write followed by a read, rather than the other way around.
456 * </p>
457 *
458 * <p>To avoid side-effects, the underlying connection is wrapped by a
459 * {@link BufferedInputStream}, so although data might be read, what is visible
460 * to clients of the connection will not change with this call.</p.
461 *
462 * @return <tt>true</tt> if the connection is already closed, or a read would
463 * fail.
464 */
465 protected boolean isStale() {
466 boolean isStale = true;
467 if (isOpen) {
468
469
470 isStale = false;
471 try {
472 if (inputStream.available() == 0) {
473 try {
474 socket.setSoTimeout(1);
475 inputStream.mark(1);
476 int byteRead = inputStream.read();
477 if (byteRead == -1) {
478
479
480 isStale = true;
481 } else {
482 inputStream.reset();
483 }
484 } finally {
485 socket.setSoTimeout(this.params.getSoTimeout());
486 }
487 }
488 } catch (InterruptedIOException e) {
489
490 } catch (IOException e) {
491
492 LOG.debug(
493 "An error occurred while reading from the socket, is appears to be stale",
494 e
495 );
496 isStale = true;
497 }
498 }
499
500 return isStale;
501 }
502
503 /***
504 * Returns <tt>true</tt> if the connection is established via a proxy,
505 * <tt>false</tt> otherwise.
506 *
507 * @return <tt>true</tt> if a proxy is used to establish the connection,
508 * <tt>false</tt> otherwise.
509 */
510 public boolean isProxied() {
511 return (!(null == proxyHostName || 0 >= proxyPortNumber));
512 }
513
514 /***
515 * Set the state to keep track of the last response for the last request.
516 *
517 * <p>The connection managers use this to ensure that previous requests are
518 * properly closed before a new request is attempted. That way, a GET
519 * request need not be read in its entirety before a new request is issued.
520 * Instead, this stream can be closed as appropriate.</p>
521 *
522 * @param inStream The stream associated with an HttpMethod.
523 */
524 public void setLastResponseInputStream(InputStream inStream) {
525 lastResponseInputStream = inStream;
526 }
527
528 /***
529 * Returns the stream used to read the last response's body.
530 *
531 * <p>Clients will generally not need to call this function unless
532 * using HttpConnection directly, instead of calling {@link HttpClient#executeMethod}.
533 * For those clients, call this function, and if it returns a non-null stream,
534 * close the stream before attempting to execute a method. Note that
535 * calling "close" on the stream returned by this function <i>may</i> close
536 * the connection if the previous response contained a "Connection: close" header. </p>
537 *
538 * @return An {@link InputStream} corresponding to the body of the last
539 * response.
540 */
541 public InputStream getLastResponseInputStream() {
542 return lastResponseInputStream;
543 }
544
545
546
547 /***
548 * Returns {@link HttpConnectionParams HTTP protocol parameters} associated with this method.
549 *
550 * @return HTTP parameters.
551 *
552 * @since 3.0
553 */
554 public HttpConnectionParams getParams() {
555 return this.params;
556 }
557
558 /***
559 * Assigns {@link HttpConnectionParams HTTP protocol parameters} for this method.
560 *
561 * @since 3.0
562 *
563 * @see HttpConnectionParams
564 */
565 public void setParams(final HttpConnectionParams params) {
566 if (params == null) {
567 throw new IllegalArgumentException("Parameters may not be null");
568 }
569 this.params = params;
570 }
571
572 /***
573 * Set the {@link Socket}'s timeout, via {@link Socket#setSoTimeout}. If the
574 * connection is already open, the SO_TIMEOUT is changed. If no connection
575 * is open, then subsequent connections will use the timeout value.
576 * <p>
577 * Note: This is not a connection timeout but a timeout on network traffic!
578 *
579 * @param timeout the timeout value
580 * @throws SocketException - if there is an error in the underlying
581 * protocol, such as a TCP error.
582 *
583 * @deprecated Use {@link HttpConnectionParams#setSoTimeout(int)},
584 * {@link HttpConnection#getParams()}.
585 */
586 public void setSoTimeout(int timeout)
587 throws SocketException, IllegalStateException {
588 this.params.setSoTimeout(timeout);
589 if (this.socket != null) {
590 this.socket.setSoTimeout(timeout);
591 }
592 }
593
594 /***
595 * Sets <code>SO_TIMEOUT</code> value directly on the underlying {@link Socket socket}.
596 * This method does not change the default read timeout value set via
597 * {@link HttpConnectionParams}.
598 *
599 * @param timeout the timeout value
600 * @throws SocketException - if there is an error in the underlying
601 * protocol, such as a TCP error.
602 * @throws IllegalStateException if not connected
603 *
604 * @since 3.0
605 */
606 public void setSocketTimeout(int timeout)
607 throws SocketException, IllegalStateException {
608 assertOpen();
609 if (this.socket != null) {
610 this.socket.setSoTimeout(timeout);
611 }
612 }
613
614 /***
615 * Returns the {@link Socket}'s timeout, via {@link Socket#getSoTimeout}, if the
616 * connection is already open. If no connection is open, return the value subsequent
617 * connection will use.
618 * <p>
619 * Note: This is not a connection timeout but a timeout on network traffic!
620 *
621 * @return the timeout value
622 *
623 * @deprecated Use {@link HttpConnectionParams#getSoTimeout()},
624 * {@link HttpConnection#getParams()}.
625 */
626 public int getSoTimeout() throws SocketException {
627 return this.params.getSoTimeout();
628 }
629
630 /***
631 * Sets the connection timeout. This is the maximum time that may be spent
632 * until a connection is established. The connection will fail after this
633 * amount of time.
634 * @param timeout The timeout in milliseconds. 0 means timeout is not used.
635 *
636 * @deprecated Use {@link HttpConnectionParams#setConnectionTimeout(int)},
637 * {@link HttpConnection#getParams()}.
638 */
639 public void setConnectionTimeout(int timeout) {
640 this.params.setConnectionTimeout(timeout);
641 }
642
643 /***
644 * Establishes a connection to the specified host and port
645 * (via a proxy if specified).
646 * The underlying socket is created from the {@link ProtocolSocketFactory}.
647 *
648 * @throws IOException if an attempt to establish the connection results in an
649 * I/O error.
650 */
651 public void open() throws IOException {
652 LOG.trace("enter HttpConnection.open()");
653
654 final String host = (proxyHostName == null) ? hostName : proxyHostName;
655 final int port = (proxyHostName == null) ? portNumber : proxyPortNumber;
656 assertNotOpen();
657 try {
658 if (this.socket == null) {
659 usingSecureSocket = isSecure() && !isProxied();
660
661
662 final ProtocolSocketFactory socketFactory =
663 (isSecure() && isProxied()
664 ? new DefaultProtocolSocketFactory()
665 : protocolInUse.getSocketFactory());
666 this.socket = socketFactory.createSocket(
667 host, port,
668 localAddress, 0,
669 this.params);
670 }
671
672
673
674
675
676
677
678
679
680 socket.setTcpNoDelay(this.params.getTcpNoDelay());
681 socket.setSoTimeout(this.params.getSoTimeout());
682
683 int linger = this.params.getLinger();
684 if (linger >= 0) {
685 socket.setSoLinger(linger > 0, linger);
686 }
687
688 int sndBufSize = this.params.getSendBufferSize();
689 if (sndBufSize >= 0) {
690 socket.setSendBufferSize(sndBufSize);
691 }
692 int rcvBufSize = this.params.getReceiveBufferSize();
693 if (rcvBufSize >= 0) {
694 socket.setReceiveBufferSize(rcvBufSize);
695 }
696 int outbuffersize = socket.getSendBufferSize();
697 if (outbuffersize > 2048) {
698 outbuffersize = 2048;
699 }
700 int inbuffersize = socket.getReceiveBufferSize();
701 if (inbuffersize > 2048) {
702 inbuffersize = 2048;
703 }
704 inputStream = new BufferedInputStream(socket.getInputStream(), inbuffersize);
705 outputStream = new BufferedOutputStream(socket.getOutputStream(), outbuffersize);
706 isOpen = true;
707 used = false;
708 } catch (IOException e) {
709
710
711 closeSocketAndStreams();
712 throw e;
713 }
714 }
715
716 /***
717 * Instructs the proxy to establish a secure tunnel to the host. The socket will
718 * be switched to the secure socket. Subsequent communication is done via the secure
719 * socket. The method can only be called once on a proxied secure connection.
720 *
721 * @throws IllegalStateException if connection is not secure and proxied or
722 * if the socket is already secure.
723 * @throws IOException if an attempt to establish the secure tunnel results in an
724 * I/O error.
725 */
726 public void tunnelCreated() throws IllegalStateException, IOException {
727 LOG.trace("enter HttpConnection.tunnelCreated()");
728
729 if (!isSecure() || !isProxied()) {
730 throw new IllegalStateException(
731 "Connection must be secure "
732 + "and proxied to use this feature");
733 }
734
735 if (usingSecureSocket) {
736 throw new IllegalStateException("Already using a secure socket");
737 }
738
739 SecureProtocolSocketFactory socketFactory =
740 (SecureProtocolSocketFactory) protocolInUse.getSocketFactory();
741
742 socket = socketFactory.createSocket(socket, hostName, portNumber, true);
743 int sndBufSize = this.params.getSendBufferSize();
744 if (sndBufSize >= 0) {
745 socket.setSendBufferSize(sndBufSize);
746 }
747 int rcvBufSize = this.params.getReceiveBufferSize();
748 if (rcvBufSize >= 0) {
749 socket.setReceiveBufferSize(rcvBufSize);
750 }
751 int outbuffersize = socket.getSendBufferSize();
752 if (outbuffersize > 2048) {
753 outbuffersize = 2048;
754 }
755 int inbuffersize = socket.getReceiveBufferSize();
756 if (inbuffersize > 2048) {
757 inbuffersize = 2048;
758 }
759 inputStream = new BufferedInputStream(socket.getInputStream(), inbuffersize);
760 outputStream = new BufferedOutputStream(socket.getOutputStream(), outbuffersize);
761 usingSecureSocket = true;
762 tunnelEstablished = true;
763 LOG.debug("Secure tunnel created");
764 }
765
766 /***
767 * Indicates if the connection is completely transparent from end to end.
768 *
769 * @return true if conncetion is not proxied or tunneled through a transparent
770 * proxy; false otherwise.
771 */
772 public boolean isTransparent() {
773 return !isProxied() || tunnelEstablished;
774 }
775
776 /***
777 * Flushes the output request stream. This method should be called to
778 * ensure that data written to the request OutputStream is sent to the server.
779 *
780 * @throws IOException if an I/O problem occurs
781 */
782 public void flushRequestOutputStream() throws IOException {
783 LOG.trace("enter HttpConnection.flushRequestOutputStream()");
784 assertOpen();
785 outputStream.flush();
786 }
787
788 /***
789 * Returns an {@link OutputStream} suitable for writing the request.
790 *
791 * @throws IllegalStateException if the connection is not open
792 * @throws IOException if an I/O problem occurs
793 * @return a stream to write the request to
794 */
795 public OutputStream getRequestOutputStream()
796 throws IOException, IllegalStateException {
797 LOG.trace("enter HttpConnection.getRequestOutputStream()");
798 assertOpen();
799 OutputStream out = this.outputStream;
800 if (Wire.CONTENT_WIRE.enabled()) {
801 out = new WireLogOutputStream(out, Wire.CONTENT_WIRE);
802 }
803 return out;
804 }
805
806 /***
807 * Return a {@link InputStream} suitable for reading the response.
808 * @return InputStream The response input stream.
809 * @throws IOException If an IO problem occurs
810 * @throws IllegalStateException If the connection isn't open.
811 */
812 public InputStream getResponseInputStream()
813 throws IOException, IllegalStateException {
814 LOG.trace("enter HttpConnection.getResponseInputStream()");
815 assertOpen();
816 return inputStream;
817 }
818
819 /***
820 * Tests if input data avaialble. This method returns immediately
821 * and does not perform any read operations on the input socket
822 *
823 * @return boolean <tt>true</tt> if input data is available,
824 * <tt>false</tt> otherwise.
825 *
826 * @throws IOException If an IO problem occurs
827 * @throws IllegalStateException If the connection isn't open.
828 */
829 public boolean isResponseAvailable()
830 throws IOException {
831 LOG.trace("enter HttpConnection.isResponseAvailable()");
832 assertOpen();
833 return this.inputStream.available() > 0;
834 }
835
836 /***
837 * Tests if input data becomes available within the given period time in milliseconds.
838 *
839 * @param timeout The number milliseconds to wait for input data to become available
840 * @return boolean <tt>true</tt> if input data is availble,
841 * <tt>false</tt> otherwise.
842 *
843 * @throws IOException If an IO problem occurs
844 * @throws IllegalStateException If the connection isn't open.
845 */
846 public boolean isResponseAvailable(int timeout)
847 throws IOException {
848 LOG.trace("enter HttpConnection.isResponseAvailable(int)");
849 assertOpen();
850 boolean result = false;
851 if (this.inputStream.available() > 0) {
852 result = true;
853 } else {
854 try {
855 this.socket.setSoTimeout(timeout);
856 inputStream.mark(1);
857 int byteRead = inputStream.read();
858 if (byteRead != -1) {
859 inputStream.reset();
860 LOG.debug("Input data available");
861 result = true;
862 } else {
863 LOG.debug("Input data not available");
864 }
865 } catch (InterruptedIOException e) {
866 if (LOG.isDebugEnabled()) {
867 LOG.debug("Input data not available after " + timeout + " ms");
868 }
869 } finally {
870 try {
871 socket.setSoTimeout(this.params.getSoTimeout());
872 } catch (IOException ioe) {
873 LOG.debug("An error ocurred while resetting soTimeout, we will assume that"
874 + " no response is available.",
875 ioe);
876 result = false;
877 }
878 }
879 }
880 return result;
881 }
882
883 /***
884 * Writes the specified bytes to the output stream.
885 *
886 * @param data the data to be written
887 * @throws IllegalStateException if not connected
888 * @throws IOException if an I/O problem occurs
889 * @see #write(byte[],int,int)
890 */
891 public void write(byte[] data)
892 throws IOException, IllegalStateException {
893 LOG.trace("enter HttpConnection.write(byte[])");
894 this.write(data, 0, data.length);
895 }
896
897 /***
898 * Writes <i>length</i> bytes in <i>data</i> starting at
899 * <i>offset</i> to the output stream.
900 *
901 * The general contract for
902 * write(b, off, len) is that some of the bytes in the array b are written
903 * to the output stream in order; element b[off] is the first byte written
904 * and b[off+len-1] is the last byte written by this operation.
905 *
906 * @param data array containing the data to be written.
907 * @param offset the start offset in the data.
908 * @param length the number of bytes to write.
909 * @throws IllegalStateException if not connected
910 * @throws IOException if an I/O problem occurs
911 */
912 public void write(byte[] data, int offset, int length)
913 throws IOException, IllegalStateException {
914 LOG.trace("enter HttpConnection.write(byte[], int, int)");
915
916 if (offset < 0) {
917 throw new IllegalArgumentException("Array offset may not be negative");
918 }
919 if (length < 0) {
920 throw new IllegalArgumentException("Array length may not be negative");
921 }
922 if (offset + length > data.length) {
923 throw new IllegalArgumentException("Given offset and length exceed the array length");
924 }
925 assertOpen();
926 this.outputStream.write(data, offset, length);
927 }
928
929 /***
930 * Writes the specified bytes, followed by <tt>"\r\n".getBytes()</tt> to the
931 * output stream.
932 *
933 * @param data the bytes to be written
934 * @throws IllegalStateException if the connection is not open
935 * @throws IOException if an I/O problem occurs
936 */
937 public void writeLine(byte[] data)
938 throws IOException, IllegalStateException {
939 LOG.trace("enter HttpConnection.writeLine(byte[])");
940 write(data);
941 writeLine();
942 }
943
944 /***
945 * Writes <tt>"\r\n".getBytes()</tt> to the output stream.
946 *
947 * @throws IllegalStateException if the connection is not open
948 * @throws IOException if an I/O problem occurs
949 */
950 public void writeLine()
951 throws IOException, IllegalStateException {
952 LOG.trace("enter HttpConnection.writeLine()");
953 write(CRLF);
954 }
955
956 /***
957 * @deprecated Use {@link #print(String, String)}
958 *
959 * Writes the specified String (as bytes) to the output stream.
960 *
961 * @param data the string to be written
962 * @throws IllegalStateException if the connection is not open
963 * @throws IOException if an I/O problem occurs
964 */
965 public void print(String data)
966 throws IOException, IllegalStateException {
967 LOG.trace("enter HttpConnection.print(String)");
968 write(EncodingUtil.getBytes(data, "ISO-8859-1"));
969 }
970
971 /***
972 * Writes the specified String (as bytes) to the output stream.
973 *
974 * @param data the string to be written
975 * @param charset the charset to use for writing the data
976 * @throws IllegalStateException if the connection is not open
977 * @throws IOException if an I/O problem occurs
978 *
979 * @since 3.0
980 */
981 public void print(String data, String charset)
982 throws IOException, IllegalStateException {
983 LOG.trace("enter HttpConnection.print(String)");
984 write(EncodingUtil.getBytes(data, charset));
985 }
986
987 /***
988 * @deprecated Use {@link #printLine(String, String)}
989 *
990 * Writes the specified String (as bytes), followed by
991 * <tt>"\r\n".getBytes()</tt> to the output stream.
992 *
993 * @param data the data to be written
994 * @throws IllegalStateException if the connection is not open
995 * @throws IOException if an I/O problem occurs
996 */
997 public void printLine(String data)
998 throws IOException, IllegalStateException {
999 LOG.trace("enter HttpConnection.printLine(String)");
1000 writeLine(EncodingUtil.getBytes(data, "ISO-8859-1"));
1001 }
1002
1003 /***
1004 * Writes the specified String (as bytes), followed by
1005 * <tt>"\r\n".getBytes()</tt> to the output stream.
1006 *
1007 * @param data the data to be written
1008 * @param charset the charset to use for writing the data
1009 * @throws IllegalStateException if the connection is not open
1010 * @throws IOException if an I/O problem occurs
1011 *
1012 * @since 3.0
1013 */
1014 public void printLine(String data, String charset)
1015 throws IOException, IllegalStateException {
1016 LOG.trace("enter HttpConnection.printLine(String)");
1017 writeLine(EncodingUtil.getBytes(data, charset));
1018 }
1019
1020 /***
1021 * Writes <tt>"\r\n".getBytes()</tt> to the output stream.
1022 *
1023 * @throws IllegalStateException if the connection is not open
1024 * @throws IOException if an I/O problem occurs
1025 */
1026 public void printLine()
1027 throws IOException, IllegalStateException {
1028 LOG.trace("enter HttpConnection.printLine()");
1029 writeLine();
1030 }
1031
1032 /***
1033 * Reads up to <tt>"\n"</tt> from the (unchunked) input stream.
1034 * If the stream ends before the line terminator is found,
1035 * the last part of the string will still be returned.
1036 *
1037 * @throws IllegalStateException if the connection is not open
1038 * @throws IOException if an I/O problem occurs
1039 * @return a line from the response
1040 *
1041 * @deprecated use #readLine(String)
1042 */
1043 public String readLine() throws IOException, IllegalStateException {
1044 LOG.trace("enter HttpConnection.readLine()");
1045
1046 assertOpen();
1047 return HttpParser.readLine(inputStream);
1048 }
1049
1050 /***
1051 * Reads up to <tt>"\n"</tt> from the (unchunked) input stream.
1052 * If the stream ends before the line terminator is found,
1053 * the last part of the string will still be returned.
1054 *
1055 * @param charset the charset to use for reading the data
1056 *
1057 * @throws IllegalStateException if the connection is not open
1058 * @throws IOException if an I/O problem occurs
1059 * @return a line from the response
1060 *
1061 * @since 3.0
1062 */
1063 public String readLine(final String charset) throws IOException, IllegalStateException {
1064 LOG.trace("enter HttpConnection.readLine()");
1065
1066 assertOpen();
1067 return HttpParser.readLine(inputStream, charset);
1068 }
1069
1070 /***
1071 * Attempts to shutdown the {@link Socket}'s output, via Socket.shutdownOutput()
1072 * when running on JVM 1.3 or higher.
1073 *
1074 * @deprecated unused
1075 */
1076 public void shutdownOutput() {
1077 LOG.trace("enter HttpConnection.shutdownOutput()");
1078
1079 try {
1080
1081
1082
1083 Class[] paramsClasses = new Class[0];
1084 Method shutdownOutput =
1085 socket.getClass().getMethod("shutdownOutput", paramsClasses);
1086 Object[] params = new Object[0];
1087 shutdownOutput.invoke(socket, params);
1088 } catch (Exception ex) {
1089 LOG.debug("Unexpected Exception caught", ex);
1090
1091 }
1092
1093 }
1094
1095 /***
1096 * Closes the socket and streams.
1097 */
1098 public void close() {
1099 LOG.trace("enter HttpConnection.close()");
1100 closeSocketAndStreams();
1101 }
1102
1103 /***
1104 * Returns the httpConnectionManager.
1105 * @return HttpConnectionManager
1106 */
1107 public HttpConnectionManager getHttpConnectionManager() {
1108 return httpConnectionManager;
1109 }
1110
1111 /***
1112 * Sets the httpConnectionManager.
1113 * @param httpConnectionManager The httpConnectionManager to set
1114 */
1115 public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
1116 this.httpConnectionManager = httpConnectionManager;
1117 }
1118
1119 /***
1120 * Release the connection.
1121 */
1122 public void releaseConnection() {
1123 LOG.trace("enter HttpConnection.releaseConnection()");
1124 if (locked) {
1125 LOG.debug("Connection is locked. Call to releaseConnection() ignored.");
1126 } else if (httpConnectionManager != null) {
1127 LOG.debug("Releasing connection back to connection manager.");
1128 httpConnectionManager.releaseConnection(this);
1129 } else {
1130 LOG.warn("HttpConnectionManager is null. Connection cannot be released.");
1131 }
1132 }
1133
1134 /***
1135 * @return
1136 *
1137 * @since 3.0
1138 */
1139 boolean isLocked() {
1140 return locked;
1141 }
1142
1143 /***
1144 * @param locked
1145 *
1146 * @since 3.0
1147 */
1148 void setLocked(boolean locked) {
1149 this.locked = locked;
1150 }
1151
1152
1153 /***
1154 * Closes everything out.
1155 */
1156 protected void closeSocketAndStreams() {
1157 LOG.trace("enter HttpConnection.closeSockedAndStreams()");
1158
1159
1160 lastResponseInputStream = null;
1161
1162 if (null != outputStream) {
1163 OutputStream temp = outputStream;
1164 outputStream = null;
1165 try {
1166 temp.close();
1167 } catch (Exception ex) {
1168 LOG.debug("Exception caught when closing output", ex);
1169
1170 }
1171 }
1172
1173 if (null != inputStream) {
1174 InputStream temp = inputStream;
1175 inputStream = null;
1176 try {
1177 temp.close();
1178 } catch (Exception ex) {
1179 LOG.debug("Exception caught when closing input", ex);
1180
1181 }
1182 }
1183
1184 if (null != socket) {
1185 Socket temp = socket;
1186 socket = null;
1187 try {
1188 temp.close();
1189 } catch (Exception ex) {
1190 LOG.debug("Exception caught when closing socket", ex);
1191
1192 }
1193 }
1194 isOpen = false;
1195 used = false;
1196 tunnelEstablished = false;
1197 usingSecureSocket = false;
1198 }
1199
1200 /***
1201 * Throws an {@link IllegalStateException} if the connection is already open.
1202 *
1203 * @throws IllegalStateException if connected
1204 */
1205 protected void assertNotOpen() throws IllegalStateException {
1206 if (isOpen) {
1207 throw new IllegalStateException("Connection is open");
1208 }
1209 }
1210
1211 /***
1212 * Throws an {@link IllegalStateException} if the connection is not open.
1213 *
1214 * @throws IllegalStateException if not connected
1215 */
1216 protected void assertOpen() throws IllegalStateException {
1217 if (!isOpen) {
1218 throw new IllegalStateException("Connection is not open");
1219 }
1220 }
1221
1222 /***
1223 * Gets the socket's sendBufferSize.
1224 *
1225 * @return the size of the buffer for the socket OutputStream, -1 if the value
1226 * has not been set and the socket has not been opened
1227 *
1228 * @throws SocketException if an error occurs while getting the socket value
1229 *
1230 * @see Socket#getSendBufferSize()
1231 */
1232 public int getSendBufferSize() throws SocketException {
1233 if (socket == null) {
1234 return -1;
1235 } else {
1236 return socket.getSendBufferSize();
1237 }
1238 }
1239
1240 /***
1241 * Sets the socket's sendBufferSize.
1242 *
1243 * @param sendBufferSize the size to set for the socket OutputStream
1244 *
1245 * @throws SocketException if an error occurs while setting the socket value
1246 *
1247 * @see Socket#setSendBufferSize(int)
1248 *
1249 * @deprecated Use {@link HttpConnectionParams#setSendBufferSize(int)},
1250 * {@link HttpConnection#getParams()}.
1251 */
1252 public void setSendBufferSize(int sendBufferSize) throws SocketException {
1253 this.params.setSendBufferSize(sendBufferSize);
1254 }
1255
1256
1257
1258 /*** <tt>"\r\n"</tt>, as bytes. */
1259 private static final byte[] CRLF = new byte[] {(byte) 13, (byte) 10};
1260
1261 /*** Log object for this class. */
1262 private static final Log LOG = LogFactory.getLog(HttpConnection.class);
1263
1264
1265
1266 /*** A flag indicating if this connection has been used since being opened */
1267 private boolean used = false;
1268
1269 /*** My host. */
1270 private String hostName = null;
1271
1272 /*** My virtual host. */
1273 private String virtualName = null;
1274
1275 /*** My port. */
1276 private int portNumber = -1;
1277
1278 /*** My proxy host. */
1279 private String proxyHostName = null;
1280
1281 /*** My proxy port. */
1282 private int proxyPortNumber = -1;
1283
1284 /*** My client Socket. */
1285 private Socket socket = null;
1286
1287 /*** My InputStream. */
1288 private InputStream inputStream = null;
1289
1290 /*** My OutputStream. */
1291 private OutputStream outputStream = null;
1292
1293 /*** An {@link InputStream} for the response to an individual request. */
1294 private InputStream lastResponseInputStream = null;
1295
1296 /*** Whether or not the connection is connected. */
1297 protected boolean isOpen = false;
1298
1299 /*** the protocol being used */
1300 private Protocol protocolInUse;
1301
1302 /*** Collection of HTTP parameters associated with this HTTP connection*/
1303 private HttpConnectionParams params = new HttpConnectionParams();
1304
1305 /*** flag to indicate if this connection can be released, if locked the connection cannot be
1306 * released */
1307 private boolean locked = false;
1308
1309 /*** Whether or not the socket is a secure one. */
1310 private boolean usingSecureSocket = false;
1311
1312 /*** Whether the connection is open via a secure tunnel or not */
1313 private boolean tunnelEstablished = false;
1314
1315 /*** the connection manager that created this connection or null */
1316 private HttpConnectionManager httpConnectionManager;
1317
1318 /*** The local interface on which the connection is created, or null for the default */
1319 private InetAddress localAddress;
1320 }