View Javadoc

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