View Javadoc

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