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