View Javadoc

1   /*
2    * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v 1.100 2004/09/15 20:32:21 olegk Exp $
3    * $Revision: 1.100 $
4    * $Date: 2004/09/15 20:32:21 $
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.BufferedInputStream;
33  import java.io.BufferedOutputStream;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.io.InterruptedIOException;
37  import java.io.OutputStream;
38  import java.lang.reflect.Method;
39  import java.net.InetAddress;
40  import java.net.Socket;
41  import java.net.SocketException;
42  
43  import org.apache.commons.httpclient.params.HttpConnectionParams;
44  import org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory;
45  import org.apache.commons.httpclient.protocol.Protocol;
46  import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
47  import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
48  import org.apache.commons.httpclient.util.EncodingUtil;
49  import org.apache.commons.logging.Log;
50  import org.apache.commons.logging.LogFactory;
51  
52  /***
53   * An abstraction of an HTTP {@link InputStream} and {@link OutputStream}
54   * pair, together with the relevant attributes.
55   * <p>
56   * The following options are set on the socket before getting the input/output 
57   * streams in the {@link #open()} method:
58   * <table border=1><tr>
59   *    <th>Socket Method
60   *    <th>Sockets Option
61   *    <th>Configuration
62   * </tr><tr>
63   *    <td>{@link java.net.Socket#setTcpNoDelay(boolean)}
64   *    <td>SO_NODELAY
65   *    <td>{@link HttpConnectionParams#setTcpNoDelay(boolean)}
66   * </tr><tr>
67   *    <td>{@link java.net.Socket#setSoTimeout(int)}
68   *    <td>SO_TIMEOUT
69   *    <td>{@link HttpConnectionParams#setSoTimeout(int)}
70   * </tr><tr>
71   *    <td>{@link java.net.Socket#setSendBufferSize(int)}
72   *    <td>SO_SNDBUF
73   *    <td>{@link HttpConnectionParams#setSendBufferSize(int)}
74   * </tr><tr>
75   *    <td>{@link java.net.Socket#setReceiveBufferSize(int)}
76   *    <td>SO_RCVBUF
77   *    <td>{@link HttpConnectionParams#setReceiveBufferSize(int)}
78   * </tr></table>
79   *
80   * @author Rod Waldhoff
81   * @author Sean C. Sullivan
82   * @author Ortwin Gl??ck
83   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
84   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
85   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
86   * @author Michael Becke
87   * @author Eric E Johnson
88   * @author Laura Werner
89   * 
90   * @version   $Revision: 1.100 $ $Date: 2004/09/15 20:32:21 $
91   */
92  public class HttpConnection {
93  
94      // ----------------------------------------------------------- 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      * Tests if the connection is open. 
387      *
388      * @return <code>true</code> if the connection is open
389      */
390     public boolean isOpen() {
391         return isOpen;
392     }
393 
394     /***
395      * Closes the connection if stale.
396      * 
397      * @return <code>true</code> if the connection was stale and therefore closed, 
398      * <code>false</code> otherwise.
399      * 
400      * @see #isStale()
401      */
402     public boolean closeIfStale() {
403         if (used && isOpen && isStale()) {
404             LOG.debug("Connection is stale, closing...");
405             close();
406             return true;
407         }
408         return false;
409     }
410     
411     /***
412      * Tests if stale checking is enabled.
413      * 
414      * @return <code>true</code> if enabled
415      * 
416      * @see #isStale()
417      * 
418      * @deprecated Use {@link HttpConnectionParams#isStaleCheckingEnabled()},
419      * {@link HttpConnection#getParams()}.
420      */
421     public boolean isStaleCheckingEnabled() {
422         return this.params.isStaleCheckingEnabled();
423     }
424 
425     /***
426      * Sets whether or not isStale() will be called when testing if this connection is open.
427      * 
428      * <p>Setting this flag to <code>false</code> will increase performance when reusing
429      * connections, but it will also make them less reliable.  Stale checking ensures that
430      * connections are viable before they are used.  When set to <code>false</code> some
431      * method executions will result in IOExceptions and they will have to be retried.</p>
432      * 
433      * @param staleCheckEnabled <code>true</code> to enable isStale()
434      * 
435      * @see #isStale()
436      * @see #isOpen()
437      * 
438      * @deprecated Use {@link HttpConnectionParams#setStaleCheckingEnabled(boolean)},
439      * {@link HttpConnection#getParams()}.
440      */
441     public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
442         this.params.setStaleCheckingEnabled(staleCheckEnabled);
443     }
444 
445     /***
446      * Determines whether this connection is "stale", which is to say that either
447      * it is no longer open, or an attempt to read the connection would fail.
448      *
449      * <p>Unfortunately, due to the limitations of the JREs prior to 1.4, it is
450      * not possible to test a connection to see if both the read and write channels
451      * are open - except by reading and writing.  This leads to a difficulty when
452      * some connections leave the "write" channel open, but close the read channel
453      * and ignore the request.  This function attempts to ameliorate that
454      * problem by doing a test read, assuming that the caller will be doing a
455      * write followed by a read, rather than the other way around.
456      * </p>
457      *
458      * <p>To avoid side-effects, the underlying connection is wrapped by a
459      * {@link BufferedInputStream}, so although data might be read, what is visible
460      * to clients of the connection will not change with this call.</p.
461      *
462      * @return <tt>true</tt> if the connection is already closed, or a read would
463      * fail.
464      */
465     protected boolean isStale() {
466         boolean isStale = true;
467         if (isOpen) {
468             // the connection is open, but now we have to see if we can read it
469             // assume the connection is not stale.
470             isStale = false;
471             try {
472                 if (inputStream.available() == 0) {
473                     try {
474                         socket.setSoTimeout(1);
475                         inputStream.mark(1);
476                         int byteRead = inputStream.read();
477                         if (byteRead == -1) {
478                             // again - if the socket is reporting all data read,
479                             // probably stale
480                             isStale = true;
481                         } else {
482                             inputStream.reset();
483                         }
484                     } finally {
485                         socket.setSoTimeout(this.params.getSoTimeout());
486                     }
487                 }
488             } catch (InterruptedIOException e) {
489                 // aha - the connection is NOT stale - continue on!
490             } catch (IOException e) {
491                 // oops - the connection is stale, the read or soTimeout failed.
492                 LOG.debug(
493                     "An error occurred while reading from the socket, is appears to be stale",
494                     e
495                 );
496                 isStale = true;
497             }
498         }
499 
500         return isStale;
501     }
502 
503     /***
504      * Returns <tt>true</tt> if the connection is established via a proxy,
505      * <tt>false</tt> otherwise.
506      *
507      * @return <tt>true</tt> if a proxy is used to establish the connection, 
508      * <tt>false</tt> otherwise.
509      */
510     public boolean isProxied() {
511         return (!(null == proxyHostName || 0 >= proxyPortNumber));
512     }
513 
514     /***
515      * Set the state to keep track of the last response for the last request.
516      *
517      * <p>The connection managers use this to ensure that previous requests are
518      * properly closed before a new request is attempted.  That way, a GET
519      * request need not be read in its entirety before a new request is issued.
520      * Instead, this stream can be closed as appropriate.</p>
521      *
522      * @param inStream  The stream associated with an HttpMethod.
523      */
524     public void setLastResponseInputStream(InputStream inStream) {
525         lastResponseInputStream = inStream;
526     }
527 
528     /***
529      * Returns the stream used to read the last response's body.
530      *
531      * <p>Clients will generally not need to call this function unless
532      * using HttpConnection directly, instead of calling {@link HttpClient#executeMethod}.
533      * For those clients, call this function, and if it returns a non-null stream,
534      * close the stream before attempting to execute a method.  Note that
535      * calling "close" on the stream returned by this function <i>may</i> close
536      * the connection if the previous response contained a "Connection: close" header. </p>
537      *
538      * @return An {@link InputStream} corresponding to the body of the last
539      *  response.
540      */
541     public InputStream getLastResponseInputStream() {
542         return lastResponseInputStream;
543     }
544 
545     // --------------------------------------------------- Other Public Methods
546 
547     /***
548      * Returns {@link HttpConnectionParams HTTP protocol parameters} associated with this method.
549      *
550      * @return HTTP parameters.
551      *
552      * @since 3.0
553      */
554     public HttpConnectionParams getParams() {
555         return this.params;
556     }
557 
558     /***
559      * Assigns {@link HttpConnectionParams HTTP protocol parameters} for this method.
560      * 
561      * @since 3.0
562      * 
563      * @see HttpConnectionParams
564      */
565     public void setParams(final HttpConnectionParams params) {
566         if (params == null) {
567             throw new IllegalArgumentException("Parameters may not be null");
568         }
569         this.params = params;
570     }
571 
572     /***
573      * Set the {@link Socket}'s timeout, via {@link Socket#setSoTimeout}.  If the
574      * connection is already open, the SO_TIMEOUT is changed.  If no connection
575      * is open, then subsequent connections will use the timeout value.
576      * <p>
577      * Note: This is not a connection timeout but a timeout on network traffic!
578      *
579      * @param timeout the timeout value
580      * @throws SocketException - if there is an error in the underlying
581      * protocol, such as a TCP error.
582      * 
583      * @deprecated Use {@link HttpConnectionParams#setSoTimeout(int)},
584      * {@link HttpConnection#getParams()}.
585      */
586     public void setSoTimeout(int timeout)
587         throws SocketException, IllegalStateException {
588         this.params.setSoTimeout(timeout);
589         if (this.socket != null) {
590             this.socket.setSoTimeout(timeout);
591         }
592     }
593 
594     /***
595      * Sets <code>SO_TIMEOUT</code> value directly on the underlying {@link Socket socket}. 
596      * This method does not change the default read timeout value set via 
597      * {@link HttpConnectionParams}.
598      *
599      * @param timeout the timeout value
600      * @throws SocketException - if there is an error in the underlying
601      * protocol, such as a TCP error.
602      * @throws IllegalStateException if not connected
603      * 
604      * @since 3.0
605      */
606     public void setSocketTimeout(int timeout)
607         throws SocketException, IllegalStateException {
608         assertOpen();
609         if (this.socket != null) {
610             this.socket.setSoTimeout(timeout);
611         }
612     }
613 
614     /***
615      * Returns the {@link Socket}'s timeout, via {@link Socket#getSoTimeout}, if the
616      * connection is already open. If no connection is open, return the value subsequent 
617      * connection will use.
618      * <p>
619      * Note: This is not a connection timeout but a timeout on network traffic!
620      *
621      * @return the timeout value
622      * 
623      * @deprecated Use {@link HttpConnectionParams#getSoTimeout()},
624      * {@link HttpConnection#getParams()}.
625      */
626     public int getSoTimeout() throws SocketException {
627         return this.params.getSoTimeout();
628     }
629 
630     /***
631      * Sets the connection timeout. This is the maximum time that may be spent
632      * until a connection is established. The connection will fail after this
633      * amount of time.
634      * @param timeout The timeout in milliseconds. 0 means timeout is not used.
635      * 
636      * @deprecated Use {@link HttpConnectionParams#setConnectionTimeout(int)},
637      * {@link HttpConnection#getParams()}.
638      */
639     public void setConnectionTimeout(int timeout) {
640         this.params.setConnectionTimeout(timeout);
641     }
642 
643     /***
644      * Establishes a connection to the specified host and port
645      * (via a proxy if specified).
646      * The underlying socket is created from the {@link ProtocolSocketFactory}.
647      *
648      * @throws IOException if an attempt to establish the connection results in an
649      *   I/O error.
650      */
651     public void open() throws IOException {
652         LOG.trace("enter HttpConnection.open()");
653 
654         final String host = (proxyHostName == null) ? hostName : proxyHostName;
655         final int port = (proxyHostName == null) ? portNumber : proxyPortNumber;
656         assertNotOpen();
657         try {
658             if (this.socket == null) {
659                 usingSecureSocket = isSecure() && !isProxied();
660                 // use the protocol's socket factory unless this is a secure
661                 // proxied connection
662                 final ProtocolSocketFactory socketFactory =
663                     (isSecure() && isProxied()
664                             ? new DefaultProtocolSocketFactory()
665                             : protocolInUse.getSocketFactory());
666                 this.socket = socketFactory.createSocket(
667                             host, port, 
668                             localAddress, 0,
669                             this.params);
670             }
671 
672             /*
673             "Nagling has been broadly implemented across networks, 
674             including the Internet, and is generally performed by default 
675             - although it is sometimes considered to be undesirable in 
676             highly interactive environments, such as some client/server 
677             situations. In such cases, nagling may be turned off through 
678             use of the TCP_NODELAY sockets option." */
679 
680             socket.setTcpNoDelay(this.params.getTcpNoDelay());
681             socket.setSoTimeout(this.params.getSoTimeout());
682             
683             int linger = this.params.getLinger();
684             if (linger >= 0) {
685                 socket.setSoLinger(linger > 0, linger);
686             }
687             
688             int sndBufSize = this.params.getSendBufferSize();
689             if (sndBufSize >= 0) {
690                 socket.setSendBufferSize(sndBufSize);
691             }        
692             int rcvBufSize = this.params.getReceiveBufferSize();
693             if (rcvBufSize >= 0) {
694                 socket.setReceiveBufferSize(rcvBufSize);
695             }        
696             int outbuffersize = socket.getSendBufferSize();
697             if (outbuffersize > 2048) {
698                 outbuffersize = 2048;
699             }
700             int inbuffersize = socket.getReceiveBufferSize();
701             if (inbuffersize > 2048) {
702                 inbuffersize = 2048;
703             }
704             inputStream = new BufferedInputStream(socket.getInputStream(), inbuffersize);
705             outputStream = new BufferedOutputStream(socket.getOutputStream(), outbuffersize);
706             isOpen = true;
707             used = false;
708         } catch (IOException e) {
709             // Connection wasn't opened properly
710             // so close everything out
711             closeSocketAndStreams();
712             throw e;
713         }
714     }
715 
716     /***
717      * Instructs the proxy to establish a secure tunnel to the host. The socket will 
718      * be switched to the secure socket. Subsequent communication is done via the secure 
719      * socket. The method can only be called once on a proxied secure connection.
720      *
721      * @throws IllegalStateException if connection is not secure and proxied or
722      * if the socket is already secure.
723      * @throws IOException if an attempt to establish the secure tunnel results in an
724      *   I/O error.
725      */
726     public void tunnelCreated() throws IllegalStateException, IOException {
727         LOG.trace("enter HttpConnection.tunnelCreated()");
728 
729         if (!isSecure() || !isProxied()) {
730             throw new IllegalStateException(
731                 "Connection must be secure "
732                     + "and proxied to use this feature");
733         }
734 
735         if (usingSecureSocket) {
736             throw new IllegalStateException("Already using a secure socket");
737         }
738 
739         SecureProtocolSocketFactory socketFactory =
740             (SecureProtocolSocketFactory) protocolInUse.getSocketFactory();
741 
742         socket = socketFactory.createSocket(socket, hostName, portNumber, true);
743         int sndBufSize = this.params.getSendBufferSize();
744         if (sndBufSize >= 0) {
745             socket.setSendBufferSize(sndBufSize);
746         }        
747         int rcvBufSize = this.params.getReceiveBufferSize();
748         if (rcvBufSize >= 0) {
749             socket.setReceiveBufferSize(rcvBufSize);
750         }        
751         int outbuffersize = socket.getSendBufferSize();
752         if (outbuffersize > 2048) {
753             outbuffersize = 2048;
754         }
755         int inbuffersize = socket.getReceiveBufferSize();
756         if (inbuffersize > 2048) {
757             inbuffersize = 2048;
758         }
759         inputStream = new BufferedInputStream(socket.getInputStream(), inbuffersize);
760         outputStream = new BufferedOutputStream(socket.getOutputStream(), outbuffersize);
761         usingSecureSocket = true;
762         tunnelEstablished = true;
763         LOG.debug("Secure tunnel created");
764     }
765 
766     /***
767      * Indicates if the connection is completely transparent from end to end.
768      *
769      * @return true if conncetion is not proxied or tunneled through a transparent
770      * proxy; false otherwise.
771      */
772     public boolean isTransparent() {
773         return !isProxied() || tunnelEstablished;
774     }
775 
776     /***
777      * Flushes the output request stream.  This method should be called to 
778      * ensure that data written to the request OutputStream is sent to the server.
779      * 
780      * @throws IOException if an I/O problem occurs
781      */
782     public void flushRequestOutputStream() throws IOException {
783         LOG.trace("enter HttpConnection.flushRequestOutputStream()");
784         assertOpen();
785         outputStream.flush();
786     }
787 
788     /***
789      * Returns an {@link OutputStream} suitable for writing the request.
790      *
791      * @throws IllegalStateException if the connection is not open
792      * @throws IOException if an I/O problem occurs
793      * @return a stream to write the request to
794      */
795     public OutputStream getRequestOutputStream()
796         throws IOException, IllegalStateException {
797         LOG.trace("enter HttpConnection.getRequestOutputStream()");
798         assertOpen();
799         OutputStream out = this.outputStream;
800         if (Wire.CONTENT_WIRE.enabled()) {
801             out = new WireLogOutputStream(out, Wire.CONTENT_WIRE);
802         }
803         return out;
804     }
805 
806     /***
807      * Return a {@link InputStream} suitable for reading the response.
808      * @return InputStream The response input stream.
809      * @throws IOException If an IO problem occurs
810      * @throws IllegalStateException If the connection isn't open.
811      */
812     public InputStream getResponseInputStream()
813         throws IOException, IllegalStateException {
814         LOG.trace("enter HttpConnection.getResponseInputStream()");
815         assertOpen();
816         return inputStream;
817     }
818 
819     /***
820      * Tests if input data avaialble. This method returns immediately
821      * and does not perform any read operations on the input socket
822      * 
823      * @return boolean <tt>true</tt> if input data is available, 
824      *                 <tt>false</tt> otherwise.
825      * 
826      * @throws IOException If an IO problem occurs
827      * @throws IllegalStateException If the connection isn't open.
828      */
829     public boolean isResponseAvailable() 
830         throws IOException {
831         LOG.trace("enter HttpConnection.isResponseAvailable()");
832         assertOpen();
833         return this.inputStream.available() > 0;
834     }
835 
836     /***
837      * Tests if input data becomes available within the given period time in milliseconds.
838      * 
839      * @param timeout The number milliseconds to wait for input data to become available 
840      * @return boolean <tt>true</tt> if input data is availble, 
841      *                 <tt>false</tt> otherwise.
842      * 
843      * @throws IOException If an IO problem occurs
844      * @throws IllegalStateException If the connection isn't open.
845      */
846     public boolean isResponseAvailable(int timeout) 
847         throws IOException {
848         LOG.trace("enter HttpConnection.isResponseAvailable(int)");
849         assertOpen();
850         boolean result = false;
851         if (this.inputStream.available() > 0) {
852             result = true;
853         } else {
854             try {
855                 this.socket.setSoTimeout(timeout);
856                 inputStream.mark(1);
857                 int byteRead = inputStream.read();
858                 if (byteRead != -1) {
859                     inputStream.reset();
860                     LOG.debug("Input data available");
861                     result = true;
862                 } else {
863                     LOG.debug("Input data not available");
864                 }
865             } catch (InterruptedIOException e) {
866                 if (LOG.isDebugEnabled()) {
867                     LOG.debug("Input data not available after " + timeout + " ms");
868                 }
869             } finally {
870                 try {
871                     socket.setSoTimeout(this.params.getSoTimeout());
872                 } catch (IOException ioe) {
873                     LOG.debug("An error ocurred while resetting soTimeout, we will assume that"
874                         + " no response is available.",
875                         ioe);
876                     result = false;
877                 }
878             }
879         }
880         return result;
881     }
882 
883     /***
884      * Writes the specified bytes to the output stream.
885      *
886      * @param data the data to be written
887      * @throws IllegalStateException if not connected
888      * @throws IOException if an I/O problem occurs
889      * @see #write(byte[],int,int)
890      */
891     public void write(byte[] data)
892         throws IOException, IllegalStateException {
893         LOG.trace("enter HttpConnection.write(byte[])");
894         this.write(data, 0, data.length);
895     }
896 
897     /***
898      * Writes <i>length</i> bytes in <i>data</i> starting at
899      * <i>offset</i> to the output stream.
900      *
901      * The general contract for
902      * write(b, off, len) is that some of the bytes in the array b are written
903      * to the output stream in order; element b[off] is the first byte written
904      * and b[off+len-1] is the last byte written by this operation.
905      *
906      * @param data array containing the data to be written.
907      * @param offset the start offset in the data.
908      * @param length the number of bytes to write.
909      * @throws IllegalStateException if not connected
910      * @throws IOException if an I/O problem occurs
911      */
912     public void write(byte[] data, int offset, int length)
913         throws IOException, IllegalStateException {
914         LOG.trace("enter HttpConnection.write(byte[], int, int)");
915 
916         if (offset < 0) {
917             throw new IllegalArgumentException("Array offset may not be negative");
918         }
919         if (length < 0) {
920             throw new IllegalArgumentException("Array length may not be negative");
921         }
922         if (offset + length > data.length) {
923             throw new IllegalArgumentException("Given offset and length exceed the array length");
924         }
925         assertOpen();
926         this.outputStream.write(data, offset, length);
927     }
928 
929     /***
930      * Writes the specified bytes, followed by <tt>"\r\n".getBytes()</tt> to the
931      * output stream.
932      *
933      * @param data the bytes to be written
934      * @throws IllegalStateException if the connection is not open
935      * @throws IOException if an I/O problem occurs
936      */
937     public void writeLine(byte[] data)
938         throws IOException, IllegalStateException {
939         LOG.trace("enter HttpConnection.writeLine(byte[])");
940         write(data);
941         writeLine();
942     }
943 
944     /***
945      * Writes <tt>"\r\n".getBytes()</tt> to the output stream.
946      *
947      * @throws IllegalStateException if the connection is not open
948      * @throws IOException if an I/O problem occurs
949      */
950     public void writeLine()
951         throws IOException, IllegalStateException {
952         LOG.trace("enter HttpConnection.writeLine()");
953         write(CRLF);
954     }
955 
956     /***
957      * @deprecated Use {@link #print(String, String)}
958      * 
959      * Writes the specified String (as bytes) to the output stream.
960      *
961      * @param data the string to be written
962      * @throws IllegalStateException if the connection is not open
963      * @throws IOException if an I/O problem occurs
964      */
965     public void print(String data)
966         throws IOException, IllegalStateException {
967         LOG.trace("enter HttpConnection.print(String)");
968         write(EncodingUtil.getBytes(data, "ISO-8859-1"));
969     }
970 
971     /***
972      * Writes the specified String (as bytes) to the output stream.
973      *
974      * @param data the string to be written
975      * @param charset the charset to use for writing the data
976      * @throws IllegalStateException if the connection is not open
977      * @throws IOException if an I/O problem occurs
978      * 
979      * @since 3.0
980      */
981     public void print(String data, String charset)
982     	throws IOException, IllegalStateException {
983         LOG.trace("enter HttpConnection.print(String)");
984         write(EncodingUtil.getBytes(data, charset));
985     }
986     
987     /***
988      * @deprecated Use {@link #printLine(String, String)}
989      * 
990      * Writes the specified String (as bytes), followed by
991      * <tt>"\r\n".getBytes()</tt> to the output stream.
992      *
993      * @param data the data to be written
994      * @throws IllegalStateException if the connection is not open
995      * @throws IOException if an I/O problem occurs
996      */
997     public void printLine(String data)
998         throws IOException, IllegalStateException {
999         LOG.trace("enter HttpConnection.printLine(String)");
1000         writeLine(EncodingUtil.getBytes(data, "ISO-8859-1"));
1001     }
1002 
1003     /***
1004      * Writes the specified String (as bytes), followed by
1005      * <tt>"\r\n".getBytes()</tt> to the output stream.
1006      *
1007      * @param data the data to be written
1008      * @param charset the charset to use for writing the data
1009      * @throws IllegalStateException if the connection is not open
1010      * @throws IOException if an I/O problem occurs
1011      * 
1012      * @since 3.0
1013      */
1014     public void printLine(String data, String charset)
1015     	throws IOException, IllegalStateException {
1016         LOG.trace("enter HttpConnection.printLine(String)");
1017         writeLine(EncodingUtil.getBytes(data, charset));
1018     }    
1019     
1020     /***
1021      * Writes <tt>"\r\n".getBytes()</tt> to the output stream.
1022      *
1023      * @throws IllegalStateException if the connection is not open
1024      * @throws IOException if an I/O problem occurs
1025      */
1026     public void printLine()
1027         throws IOException, IllegalStateException {
1028         LOG.trace("enter HttpConnection.printLine()");
1029         writeLine();
1030     }
1031 
1032     /***
1033      * Reads up to <tt>"\n"</tt> from the (unchunked) input stream.
1034      * If the stream ends before the line terminator is found,
1035      * the last part of the string will still be returned.
1036      *
1037      * @throws IllegalStateException if the connection is not open
1038      * @throws IOException if an I/O problem occurs
1039      * @return a line from the response
1040      * 
1041      * @deprecated use #readLine(String)
1042      */
1043     public String readLine() throws IOException, IllegalStateException {
1044         LOG.trace("enter HttpConnection.readLine()");
1045 
1046         assertOpen();
1047         return HttpParser.readLine(inputStream);
1048     }
1049 
1050     /***
1051      * Reads up to <tt>"\n"</tt> from the (unchunked) input stream.
1052      * If the stream ends before the line terminator is found,
1053      * the last part of the string will still be returned.
1054      * 
1055      * @param charset the charset to use for reading the data
1056      *
1057      * @throws IllegalStateException if the connection is not open
1058      * @throws IOException if an I/O problem occurs
1059      * @return a line from the response
1060      * 
1061      * @since 3.0
1062      */
1063     public String readLine(final String charset) throws IOException, IllegalStateException {
1064         LOG.trace("enter HttpConnection.readLine()");
1065 
1066         assertOpen();
1067         return HttpParser.readLine(inputStream, charset);
1068     }
1069 
1070     /***
1071      * Attempts to shutdown the {@link Socket}'s output, via Socket.shutdownOutput()
1072      * when running on JVM 1.3 or higher.
1073      * 
1074      * @deprecated unused
1075      */
1076     public void shutdownOutput() {
1077         LOG.trace("enter HttpConnection.shutdownOutput()");
1078 
1079         try {
1080             // Socket.shutdownOutput is a JDK 1.3
1081             // method. We'll use reflection in case
1082             // we're running in an older VM
1083             Class[] paramsClasses = new Class[0];
1084             Method shutdownOutput =
1085                 socket.getClass().getMethod("shutdownOutput", paramsClasses);
1086             Object[] params = new Object[0];
1087             shutdownOutput.invoke(socket, params);
1088         } catch (Exception ex) {
1089             LOG.debug("Unexpected Exception caught", ex);
1090             // Ignore, and hope everything goes right
1091         }
1092         // close output stream?
1093     }
1094 
1095     /***
1096      * Closes the socket and streams.
1097      */
1098     public void close() {
1099         LOG.trace("enter HttpConnection.close()");
1100         closeSocketAndStreams();
1101     }
1102 
1103     /***
1104      * Returns the httpConnectionManager.
1105      * @return HttpConnectionManager
1106      */
1107     public HttpConnectionManager getHttpConnectionManager() {
1108         return httpConnectionManager;
1109     }
1110 
1111     /***
1112      * Sets the httpConnectionManager.
1113      * @param httpConnectionManager The httpConnectionManager to set
1114      */
1115     public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
1116         this.httpConnectionManager = httpConnectionManager;
1117     }
1118 
1119     /***
1120      * Release the connection.
1121      */
1122     public void releaseConnection() {
1123         LOG.trace("enter HttpConnection.releaseConnection()");
1124         if (locked) {
1125             LOG.debug("Connection is locked.  Call to releaseConnection() ignored.");
1126         } else if (httpConnectionManager != null) {
1127             LOG.debug("Releasing connection back to connection manager.");
1128             httpConnectionManager.releaseConnection(this);
1129         } else {
1130             LOG.warn("HttpConnectionManager is null.  Connection cannot be released.");
1131         }
1132     }
1133 
1134     /***
1135      * @return
1136      * 
1137      * @since 3.0
1138      */
1139     boolean isLocked() {
1140         return locked;
1141     }
1142 
1143     /***
1144      * @param locked
1145      * 
1146      * @since 3.0
1147      */
1148     void setLocked(boolean locked) {
1149         this.locked = locked;
1150     }
1151     // ------------------------------------------------------ Protected Methods
1152 
1153     /***
1154      * Closes everything out.
1155      */
1156     protected void closeSocketAndStreams() {
1157         LOG.trace("enter HttpConnection.closeSockedAndStreams()");
1158 
1159         // no longer care about previous responses...
1160         lastResponseInputStream = null;
1161 
1162         if (null != outputStream) {
1163             OutputStream temp = outputStream;
1164             outputStream = null;
1165             try {
1166                 temp.close();
1167             } catch (Exception ex) {
1168                 LOG.debug("Exception caught when closing output", ex);
1169                 // ignored
1170             }
1171         }
1172 
1173         if (null != inputStream) {
1174             InputStream temp = inputStream;
1175             inputStream = null;
1176             try {
1177                 temp.close();
1178             } catch (Exception ex) {
1179                 LOG.debug("Exception caught when closing input", ex);
1180                 // ignored
1181             }
1182         }
1183 
1184         if (null != socket) {
1185             Socket temp = socket;
1186             socket = null;
1187             try {
1188                 temp.close();
1189             } catch (Exception ex) {
1190                 LOG.debug("Exception caught when closing socket", ex);
1191                 // ignored
1192             }
1193         }
1194         isOpen = false;
1195         used = false;
1196         tunnelEstablished = false;
1197         usingSecureSocket = false;
1198     }
1199 
1200     /***
1201      * Throws an {@link IllegalStateException} if the connection is already open.
1202      *
1203      * @throws IllegalStateException if connected
1204      */
1205     protected void assertNotOpen() throws IllegalStateException {
1206         if (isOpen) {
1207             throw new IllegalStateException("Connection is open");
1208         }
1209     }
1210 
1211     /***
1212      * Throws an {@link IllegalStateException} if the connection is not open.
1213      *
1214      * @throws IllegalStateException if not connected
1215      */
1216     protected void assertOpen() throws IllegalStateException {
1217         if (!isOpen) {
1218             throw new IllegalStateException("Connection is not open");
1219         }
1220     }
1221 
1222     /***
1223      * Gets the socket's sendBufferSize.
1224      * 
1225      * @return the size of the buffer for the socket OutputStream, -1 if the value
1226      * has not been set and the socket has not been opened
1227      * 
1228      * @throws SocketException if an error occurs while getting the socket value
1229      * 
1230      * @see Socket#getSendBufferSize()
1231      */
1232     public int getSendBufferSize() throws SocketException {
1233         if (socket == null) {
1234             return -1;
1235         } else {
1236             return socket.getSendBufferSize();
1237         }
1238     }
1239 
1240     /***
1241      * Sets the socket's sendBufferSize.
1242      * 
1243      * @param sendBufferSize the size to set for the socket OutputStream
1244      * 
1245      * @throws SocketException if an error occurs while setting the socket value
1246      * 
1247      * @see Socket#setSendBufferSize(int)
1248      * 
1249      * @deprecated Use {@link HttpConnectionParams#setSendBufferSize(int)},
1250      * {@link HttpConnection#getParams()}.
1251      */
1252     public void setSendBufferSize(int sendBufferSize) throws SocketException {
1253         this.params.setSendBufferSize(sendBufferSize);
1254     }
1255 
1256     // ------------------------------------------------------- Static Variable
1257 
1258     /*** <tt>"\r\n"</tt>, as bytes. */
1259     private static final byte[] CRLF = new byte[] {(byte) 13, (byte) 10};
1260 
1261     /*** Log object for this class. */
1262     private static final Log LOG = LogFactory.getLog(HttpConnection.class);
1263     
1264     // ----------------------------------------------------- Instance Variables
1265     
1266     /*** A flag indicating if this connection has been used since being opened */
1267     private boolean used = false;
1268     
1269     /*** My host. */
1270     private String hostName = null;
1271     
1272     /*** My virtual host. */
1273     private String virtualName = null;
1274     
1275     /*** My port. */
1276     private int portNumber = -1;
1277     
1278     /*** My proxy host. */
1279     private String proxyHostName = null;
1280     
1281     /*** My proxy port. */
1282     private int proxyPortNumber = -1;
1283     
1284     /*** My client Socket. */
1285     private Socket socket = null;
1286     
1287     /*** My InputStream. */
1288     private InputStream inputStream = null;
1289 
1290     /*** My OutputStream. */
1291     private OutputStream outputStream = null;
1292     
1293     /*** An {@link InputStream} for the response to an individual request. */
1294     private InputStream lastResponseInputStream = null;
1295     
1296     /*** Whether or not the connection is connected. */
1297     protected boolean isOpen = false;
1298     
1299     /*** the protocol being used */
1300     private Protocol protocolInUse;
1301     
1302     /*** Collection of HTTP parameters associated with this HTTP connection*/
1303     private HttpConnectionParams params = new HttpConnectionParams();
1304     
1305     /*** flag to indicate if this connection can be released, if locked the connection cannot be 
1306      * released */
1307     private boolean locked = false;
1308     
1309     /*** Whether or not the socket is a secure one. */
1310     private boolean usingSecureSocket = false;
1311     
1312     /*** Whether the connection is open via a secure tunnel or not */
1313     private boolean tunnelEstablished = false;
1314     
1315     /*** the connection manager that created this connection or null */
1316     private HttpConnectionManager httpConnectionManager;
1317     
1318     /*** The local interface on which the connection is created, or null for the default */
1319     private InetAddress localAddress;
1320 }