KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > httpclient > HttpConnection


1 /*
2  * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v 1.67.2.12 2004/07/19 20:06:26 olegk Exp $
3  * $Revision: 1.67.2.12 $
4  * $Date: 2004/07/19 20:06:26 $
5  *
6  * ====================================================================
7  *
8  * Copyright 1999-2004 The Apache Software Foundation
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  * ====================================================================
22  *
23  * This software consists of voluntary contributions made by many
24  * individuals on behalf of the Apache Software Foundation. For more
25  * information on the Apache Software Foundation, please see
26  * <http://www.apache.org/>.
27  *
28  * [Additional notices, if required by prior licensing conditions]
29  *
30  */

31
32 package org.apache.commons.httpclient;
33
34 import java.io.BufferedOutputStream JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.io.InputStream JavaDoc;
37 import java.io.InterruptedIOException JavaDoc;
38 import java.io.OutputStream JavaDoc;
39 import java.io.PushbackInputStream JavaDoc;
40 import java.lang.reflect.Method JavaDoc;
41 import java.net.InetAddress JavaDoc;
42 import java.net.Socket JavaDoc;
43 import java.net.SocketException JavaDoc;
44
45 import org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory;
46 import org.apache.commons.httpclient.protocol.Protocol;
47 import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
48 import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
49 import org.apache.commons.httpclient.util.TimeoutController;
50 import org.apache.commons.logging.Log;
51 import org.apache.commons.logging.LogFactory;
52
53 /**
54  * An abstraction of an HTTP {@link InputStream} and {@link OutputStream}
55  * pair, together with the relevant attributes.
56  * <p>
57  * The following options are set on the socket before getting the input/output
58  * streams in the {@link #open()} method:
59  * <table border=1><tr>
60  * <th>Socket Method
61  * <th>Sockets Option
62  * <th>Configuration
63  * </tr><tr>
64  * <td>{@link java.net.Socket#setTcpNoDelay(boolean)}
65  * <td>SO_NODELAY
66  * <td>None
67  * </tr><tr>
68  * <td>{@link java.net.Socket#setSoTimeout(int)}
69  * <td>SO_TIMEOUT
70  * <td>{@link #setConnectionTimeout(int)}
71  * </tr></table>
72  *
73  * @author Rod Waldhoff
74  * @author Sean C. Sullivan
75  * @author Ortwin Gl??ck
76  * @author <a HREF="mailto:jsdever@apache.org">Jeff Dever</a>
77  * @author <a HREF="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
78  * @author <a HREF="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
79  * @author Michael Becke
80  * @author Eric E Johnson
81  * @author Laura Werner
82  *
83  * @version $Revision: 1.67.2.12 $ $Date: 2004/07/19 20:06:26 $
84  */

85 public class HttpConnection {
86
87     // ----------------------------------------------------------- Constructors
88

89     /**
90      * Creates a new HTTP connection for the given host and port.
91      *
92      * @param host the host to connect to
93      * @param port the port to connect to
94      */

95     public HttpConnection(String JavaDoc host, int port) {
96         this(null, -1, host, port, false);
97     }
98
99     /**
100      * Creates a new HTTP connection for the given host and port.
101      * If secure attribute is set, use SSL to establish the connection.
102      *
103      * @param host the host to connect to
104      * @param port the port to connect to
105      * @param secure when <tt>true</tt>, connect via HTTPS (SSL)
106      *
107      * @deprecated use HttpConnection(String, int, Protocol)
108      *
109      * @see #HttpConnection(String,int,Protocol)
110      *
111      */

112     public HttpConnection(String JavaDoc host, int port, boolean secure) {
113         this(null, -1, host, port, secure);
114     }
115
116     /**
117      * Creates a new HTTP connection for the given host and port
118      * using the given protocol.
119      *
120      * @param host the host to connect to
121      * @param port the port to connect to
122      * @param protocol the protocol to use
123      */

124     public HttpConnection(String JavaDoc host, int port, Protocol protocol) {
125         this(null, -1, host, null, port, protocol);
126     }
127
128     /**
129      * Creates a new HTTP connection for the given host with the virtual
130      * alias and port using given protocol.
131      *
132      * @param host the host to connect to
133      * @param virtualHost the virtual host requests will be sent to
134      * @param port the port to connect to
135      * @param protocol the protocol to use
136      */

137     public HttpConnection(String JavaDoc host, String JavaDoc virtualHost, int port, Protocol protocol) {
138         this(null, -1, host, virtualHost, port, protocol);
139     }
140
141     /**
142      * Creates a new HTTP connection for the given host and port via the
143      * given proxy host and port using the default protocol.
144      *
145      * @param proxyHost the host to proxy via
146      * @param proxyPort the port to proxy via
147      * @param host the host to connect to
148      * @param port the port to connect to
149      */

150     public HttpConnection(
151         String JavaDoc proxyHost,
152         int proxyPort,
153         String JavaDoc host,
154         int port) {
155         this(proxyHost, proxyPort, host, port, false);
156     }
157
158     /**
159      * Creates a new HTTP connection for the given host and port via
160      * the given proxy host and port. If secure attribute is set,
161      * use SSL to establish the connection.
162      *
163      * @param proxyHost the host I should proxy via
164      * @param proxyPort the port I should proxy via
165      * @param host the host to connect to. Parameter value must be non-null.
166      * @param port the port to connect to
167      * @param secure when <tt>true</tt>, connect via HTTPS (SSL)
168      *
169      * @deprecated use HttpConnection(String, int, String, int, Protocol)
170      *
171      * @see #HttpConnection(String, int, String, String, int, Protocol)
172      *
173      */

174     public HttpConnection(
175         String JavaDoc proxyHost,
176         int proxyPort,
177         String JavaDoc host,
178         int port,
179         boolean secure) {
180         this(proxyHost, proxyPort, host, null, port,
181             Protocol.getProtocol(secure ? "https" : "http"));
182     }
183
184     /**
185      * Creates a new HTTP connection for the given host configuration.
186      *
187      * @param hostConfiguration the host/proxy/protocol to use
188      */

189     public HttpConnection(HostConfiguration hostConfiguration) {
190         this(hostConfiguration.getProxyHost(),
191              hostConfiguration.getProxyPort(),
192              hostConfiguration.getHost(),
193              hostConfiguration.getVirtualHost(),
194              hostConfiguration.getPort(),
195              hostConfiguration.getProtocol());
196         this.localAddress = hostConfiguration.getLocalAddress();
197     }
198
199     /**
200      * Creates a new HTTP connection for the given host with the virtual
201      * alias and port via the given proxy host and port using the given
202      * protocol.
203      *
204      * @param proxyHost the host to proxy via
205      * @param proxyPort the port to proxy via
206      * @param host the host to connect to. Parameter value must be non-null.
207      * @param virtualHost the virtual host requests will be sent to
208      * @param port the port to connect to
209      * @param protocol The protocol to use. Parameter value must be non-null.
210      */

211     public HttpConnection(
212         String JavaDoc proxyHost,
213         int proxyPort,
214         String JavaDoc host,
215         String JavaDoc virtualHost,
216         int port,
217         Protocol protocol) {
218
219         if (host == null) {
220             throw new IllegalArgumentException JavaDoc("host parameter is null");
221         }
222         if (protocol == null) {
223             throw new IllegalArgumentException JavaDoc("protocol is null");
224         }
225
226         proxyHostName = proxyHost;
227         proxyPortNumber = proxyPort;
228         hostName = host;
229         virtualName = virtualHost;
230         portNumber = protocol.resolvePort(port);
231         protocolInUse = protocol;
232     }
233
234     // ------------------------------------------ Attribute Setters and Getters
235

236     /**
237      * Returns the host.
238      *
239      * @return the host.
240      */

241     public String JavaDoc getHost() {
242         return hostName;
243     }
244
245     /**
246      * Sets the host to connect to.
247      *
248      * @param host the host to connect to. Parameter value must be non-null.
249      * @throws IllegalStateException if the connection is already open
250      */

251     public void setHost(String JavaDoc host) throws IllegalStateException JavaDoc {
252         if (host == null) {
253             throw new IllegalArgumentException JavaDoc("host parameter is null");
254         }
255         assertNotOpen();
256         hostName = host;
257     }
258
259     /**
260      * Returns the target virtual host.
261      *
262      * @return the virtual host.
263      */

264     public String JavaDoc getVirtualHost() {
265         return virtualName;
266     }
267
268     /**
269      * Sets the virtual host to target.
270      *
271      * @param host the virtual host name that should be used instead of
272      * physical host name when sending HTTP requests. Virtual host
273      * name can be set to <tt> null</tt> if virtual host name is not
274      * to be used
275      *
276      * @throws IllegalStateException if the connection is already open
277      */

278     public void setVirtualHost(String JavaDoc host) throws IllegalStateException JavaDoc {
279         assertNotOpen();
280         virtualName = host;
281     }
282
283     /**
284      * Returns the port of the host.
285      *
286      * If the port is -1 (or less than 0) the default port for
287      * the current protocol is returned.
288      *
289      * @return the port.
290      */

291     public int getPort() {
292         if (portNumber < 0) {
293             return isSecure() ? 443 : 80;
294         } else {
295             return portNumber;
296         }
297     }
298
299     /**
300      * Sets the port to connect to.
301      *
302      * @param port the port to connect to
303      *
304      * @throws IllegalStateException if the connection is already open
305      */

306     public void setPort(int port) throws IllegalStateException JavaDoc {
307         assertNotOpen();
308         portNumber = port;
309     }
310
311     /**
312      * Returns the proxy host.
313      *
314      * @return the proxy host.
315      */

316     public String JavaDoc getProxyHost() {
317         return proxyHostName;
318     }
319
320     /**
321      * Sets the host to proxy through.
322      *
323      * @param host the host to proxy through.
324      *
325      * @throws IllegalStateException if the connection is already open
326      */

327     public void setProxyHost(String JavaDoc host) throws IllegalStateException JavaDoc {
328         assertNotOpen();
329         proxyHostName = host;
330     }
331
332     /**
333      * Returns the port of the proxy host.
334      *
335      * @return the proxy port.
336      */

337     public int getProxyPort() {
338         return proxyPortNumber;
339     }
340
341     /**
342      * Sets the port of the host to proxy through.
343      *
344      * @param port the port of the host to proxy through.
345      *
346      * @throws IllegalStateException if the connection is already open
347      */

348     public void setProxyPort(int port) throws IllegalStateException JavaDoc {
349         assertNotOpen();
350         proxyPortNumber = port;
351     }
352
353     /**
354      * Returns <tt>true</tt> if the connection is established over
355      * a secure protocol.
356      *
357      * @return <tt>true</tt> if connected over a secure protocol.
358      */

359     public boolean isSecure() {
360         return protocolInUse.isSecure();
361     }
362
363     /**
364      * Returns the protocol used to establish the connection.
365      * @return The protocol
366      */

367     public Protocol getProtocol() {
368         return protocolInUse;
369     }
370
371     /**
372      * Defines whether the connection should be established over a
373      * secure protocol.
374      *
375      * @param secure whether or not to connect over a secure protocol.
376      *
377      * @throws IllegalStateException if the connection is already open
378      *
379      * @deprecated use setProtocol(Protocol)
380      *
381      * @see #setProtocol(Protocol)
382      */

383     public void setSecure(boolean secure) throws IllegalStateException JavaDoc {
384         assertNotOpen();
385         protocolInUse = secure
386             ? Protocol.getProtocol("https")
387             : Protocol.getProtocol("http");
388     }
389
390     /**
391      * Sets the protocol used to establish the connection
392      *
393      * @param protocol The protocol to use.
394      *
395      * @throws IllegalStateException if the connection is already open
396      */

397     public void setProtocol(Protocol protocol) {
398         assertNotOpen();
399
400         if (protocol == null) {
401             throw new IllegalArgumentException JavaDoc("protocol is null");
402         }
403
404         protocolInUse = protocol;
405
406     }
407
408     /**
409      * Return the local address used when creating the connection.
410      * If <tt>null</tt>, the default address is used.
411      *
412      * @return InetAddress the local address to be used when creating Sockets
413      */

414     public InetAddress JavaDoc getLocalAddress() {
415         return this.localAddress;
416     }
417     
418     /**
419      * Set the local address used when creating the connection.
420      * If unset or <tt>null</tt>, the default address is used.
421      *
422      * @param localAddress the local address to use
423      */

424     public void setLocalAddress(InetAddress JavaDoc localAddress) {
425         assertNotOpen();
426         this.localAddress = localAddress;
427     }
428
429     /**
430      * Returns <tt>true</tt> if the connection is open,
431      * <tt>false</tt> otherwise.
432      *
433      * @return <tt>true</tt> if the connection is open
434      */

435     public boolean isOpen() {
436         if (used && isOpen && isStaleCheckingEnabled() && isStale()) {
437             LOG.debug("Connection is stale, closing...");
438             close();
439         }
440         return isOpen;
441     }
442
443     /**
444      * Tests if stale checking is enabled.
445      *
446      * @return <code>true</code> if enabled
447      *
448      * @see #isStale()
449      */

450     public boolean isStaleCheckingEnabled() {
451         return staleCheckingEnabled;
452     }
453
454     /**
455      * Sets whether or not isStale() will be called when testing if this connection is open.
456      *
457      * <p>Setting this flag to <code>false</code> will increase performance when reusing
458      * connections, but it will also make them less reliable. Stale checking ensures that
459      * connections are viable before they are used. When set to <code>false</code> some
460      * method executions will result in IOExceptions and they will have to be retried.</p>
461      *
462      * @param staleCheckEnabled <code>true</code> to enable isStale()
463      *
464      * @see #isStale()
465      * @see #isOpen()
466      */

467     public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
468         this.staleCheckingEnabled = staleCheckEnabled;
469     }
470
471     /**
472      * Determines whether this connection is "stale", which is to say that either
473      * it is no longer open, or an attempt to read the connection would fail.
474      *
475      * <p>Unfortunately, due to the limitations of the JREs prior to 1.4, it is
476      * not possible to test a connection to see if both the read and write channels
477      * are open - except by reading and writing. This leads to a difficulty when
478      * some connections leave the "write" channel open, but close the read channel
479      * and ignore the request. This function attempts to ameliorate that
480      * problem by doing a test read, assuming that the caller will be doing a
481      * write followed by a read, rather than the other way around.
482      * </p>
483      *
484      * <p>To avoid side-effects, the underlying connection is wrapped by a
485      * {@link PushbackInputStream}, so although data might be read, what is visible
486      * to clients of the connection will not change with this call.</p.
487      *
488      * @return <tt>true</tt> if the connection is already closed, or a read would
489      * fail.
490      */

491     protected boolean isStale() {
492         boolean isStale = true;
493         if (isOpen) {
494             // the connection is open, but now we have to see if we can read it
495
// assume the connection is not stale.
496
isStale = false;
497             try {
498                 if (inputStream.available() == 0) {
499                     try {
500                         socket.setSoTimeout(1);
501                         int byteRead = inputStream.read();
502                         if (byteRead == -1) {
503                             // again - if the socket is reporting all data read,
504
// probably stale
505
isStale = true;
506                         } else {
507                             inputStream.unread(byteRead);
508                         }
509                     } finally {
510                         socket.setSoTimeout(soTimeout);
511                     }
512                 }
513             } catch (InterruptedIOException JavaDoc e) {
514                 // aha - the connection is NOT stale - continue on!
515
} catch (IOException JavaDoc e) {
516                 // oops - the connection is stale, the read or soTimeout failed.
517
LOG.debug(
518                     "An error occurred while reading from the socket, is appears to be stale",
519                     e
520                 );
521                 isStale = true;
522             }
523         }
524
525         return isStale;
526     }
527
528     /**
529      * Returns <tt>true</tt> if the connection is established via a proxy,
530      * <tt>false</tt> otherwise.
531      *
532      * @return <tt>true</tt> if a proxy is used to establish the connection,
533      * <tt>false</tt> otherwise.
534      */

535     public boolean isProxied() {
536         return (!(null == proxyHostName || 0 >= proxyPortNumber));
537     }
538
539     /**
540      * Set the state to keep track of the last response for the last request.
541      *
542      * <p>The connection managers use this to ensure that previous requests are
543      * properly closed before a new request is attempted. That way, a GET
544      * request need not be read in its entirety before a new request is issued.
545      * Instead, this stream can be closed as appropriate.</p>
546      *
547      * @param inStream The stream associated with an HttpMethod.
548      */

549     public void setLastResponseInputStream(InputStream JavaDoc inStream) {
550         lastResponseInputStream = inStream;
551     }
552
553     /**
554      * Returns the stream used to read the last response's body.
555      *
556      * <p>Clients will generally not need to call this function unless
557      * using HttpConnection directly, instead of calling {@link HttpClient#executeMethod}.
558      * For those clients, call this function, and if it returns a non-null stream,
559      * close the stream before attempting to execute a method. Note that
560      * calling "close" on the stream returned by this function <i>may</i> close
561      * the connection if the previous response contained a "Connection: close" header. </p>
562      *
563      * @return An {@link InputStream} corresponding to the body of the last
564      * response.
565      */

566     public InputStream JavaDoc getLastResponseInputStream() {
567         return lastResponseInputStream;
568     }
569
570     // --------------------------------------------------- Other Public Methods
571

572     /**
573      * Sets 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     public void setSoTimeout(int timeout)
584         throws SocketException JavaDoc, IllegalStateException JavaDoc {
585         LOG.debug("HttpConnection.setSoTimeout(" + timeout + ")");
586         soTimeout = timeout;
587         if (socket != null) {
588             socket.setSoTimeout(timeout);
589         }
590     }
591
592     /**
593      * Returns the {@link Socket}'s timeout, via {@link Socket#getSoTimeout}, if the
594      * connection is already open. If no connection is open, return the value subsequent
595      * connection will use.
596      * <p>
597      * Note: This is not a connection timeout but a timeout on network traffic!
598      *
599      * @return the timeout value
600      */

601     public int getSoTimeout() throws SocketException JavaDoc {
602         LOG.debug("HttpConnection.getSoTimeout()");
603         if (this.socket != null) {
604             return this.socket.getSoTimeout();
605         } else {
606             return this.soTimeout;
607         }
608     }
609
610     /**
611      * Sets the connection timeout. This is the maximum time that may be spent
612      * until a connection is established. The connection will fail after this
613      * amount of time.
614      * @param timeout The timeout in milliseconds. 0 means timeout is not used.
615      */

616     public void setConnectionTimeout(int timeout) {
617         this.connectTimeout = timeout;
618     }
619
620     /**
621      * Establishes a connection to the specified host and port
622      * (via a proxy if specified).
623      * The underlying socket is created from the {@link ProtocolSocketFactory}.
624      *
625      * @throws IOException if an attempt to establish the connection results in an
626      * I/O error.
627      */

628     public void open() throws IOException JavaDoc {
629         LOG.trace("enter HttpConnection.open()");
630
631         assertNotOpen();
632         try {
633             if (null == socket) {
634
635                 final String JavaDoc host = (null == proxyHostName) ? hostName : proxyHostName;
636                 final int port = (null == proxyHostName) ? portNumber : proxyPortNumber;
637
638                 usingSecureSocket = isSecure() && !isProxied();
639
640                 // use the protocol's socket factory unless this is a secure
641
// proxied connection
642
final ProtocolSocketFactory socketFactory =
643                     (isSecure() && isProxied()
644                             ? new DefaultProtocolSocketFactory()
645                             : protocolInUse.getSocketFactory());
646
647                 if (connectTimeout == 0) {
648                     if (localAddress != null) {
649                         socket = socketFactory.createSocket(host, port, localAddress, 0);
650                     } else {
651                         socket = socketFactory.createSocket(host, port);
652                     }
653                 } else {
654                     SocketTask task = new SocketTask() {
655                         public void doit() throws IOException JavaDoc {
656                             if (localAddress != null) {
657                                 setSocket(socketFactory.createSocket(host, port, localAddress, 0));
658                             } else {
659                                 setSocket(socketFactory.createSocket(host, port));
660                             }
661                         }
662                     };
663                     TimeoutController.execute(task, connectTimeout);
664                     socket = task.getSocket();
665                     if (task.exception != null) {
666                         throw task.exception;
667                     }
668                 }
669
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(soNodelay);
681             socket.setSoTimeout(soTimeout);
682             if (sendBufferSize != -1) {
683                 socket.setSendBufferSize(sendBufferSize);
684             }
685             int outbuffersize = socket.getSendBufferSize();
686             if (outbuffersize > 2048) {
687                 outbuffersize = 2048;
688             }
689             inputStream = new PushbackInputStream JavaDoc(socket.getInputStream());
690             outputStream = new BufferedOutputStream JavaDoc(
691                 new WrappedOutputStream(socket.getOutputStream()),
692                 outbuffersize
693             );
694             isOpen = true;
695             used = false;
696         } catch (IOException JavaDoc e) {
697             // Connection wasn't opened properly
698
// so close everything out
699
closeSocketAndStreams();
700             throw e;
701         } catch (TimeoutController.TimeoutException e) {
702             if (LOG.isWarnEnabled()) {
703                 LOG.warn("The host " + hostName + ":" + portNumber
704                         + " (or proxy " + proxyHostName + ":" + proxyPortNumber
705                         + ") did not accept the connection within timeout of "
706                         + connectTimeout + " milliseconds");
707             }
708             throw new ConnectionTimeoutException();
709         }
710     }
711
712     /**
713      * Instructs the proxy to establish a secure tunnel to the host. The socket will
714      * be switched to the secure socket. Subsequent communication is done via the secure
715      * socket. The method can only be called once on a proxied secure connection.
716      *
717      * @throws IllegalStateException if connection is not secure and proxied or
718      * if the socket is already secure.
719      * @throws IOException if an attempt to establish the secure tunnel results in an
720      * I/O error.
721      */

722     public void tunnelCreated() throws IllegalStateException JavaDoc, IOException JavaDoc {
723         LOG.trace("enter HttpConnection.tunnelCreated()");
724
725         if (!isSecure() || !isProxied()) {
726             throw new IllegalStateException JavaDoc(
727                 "Connection must be secure "
728                     + "and proxied to use this feature");
729         }
730
731         if (usingSecureSocket) {
732             throw new IllegalStateException JavaDoc("Already using a secure socket");
733         }
734
735         SecureProtocolSocketFactory socketFactory =
736             (SecureProtocolSocketFactory) protocolInUse.getSocketFactory();
737
738         socket = socketFactory.createSocket(socket, hostName, portNumber, true);
739         if (sendBufferSize != -1) {
740             socket.setSendBufferSize(sendBufferSize);
741         }
742         int outbuffersize = socket.getSendBufferSize();
743         if (outbuffersize > 2048) {
744             outbuffersize = 2048;
745         }
746         inputStream = new PushbackInputStream JavaDoc(socket.getInputStream());
747         outputStream = new BufferedOutputStream JavaDoc(
748             new WrappedOutputStream(socket.getOutputStream()),
749             outbuffersize
750         );
751         usingSecureSocket = true;
752         tunnelEstablished = true;
753         LOG.debug("Secure tunnel created");
754     }
755
756     /**
757      * Indicates if the connection is completely transparent from end to end.
758      *
759      * @return true if conncetion is not proxied or tunneled through a transparent
760      * proxy; false otherwise.
761      */

762     public boolean isTransparent() {
763         return !isProxied() || tunnelEstablished;
764     }
765
766     /**
767      * Flushes the output request stream. This method should be called to
768      * ensure that data written to the request OutputStream is sent to the server.
769      *
770      * @throws IOException if an I/O problem occurs
771      */

772     public void flushRequestOutputStream() throws IOException JavaDoc {
773         LOG.trace("enter HttpConnection.flushRequestOutputStream()");
774         assertOpen();
775         outputStream.flush();
776     }
777
778     /**
779      * Returns an {@link OutputStream} suitable for writing the request.
780      *
781      * @throws IllegalStateException if the connection is not open
782      * @throws IOException if an I/O problem occurs
783      * @return a stream to write the request to
784      */

785     public OutputStream JavaDoc getRequestOutputStream()
786         throws IOException JavaDoc, IllegalStateException JavaDoc {
787         LOG.trace("enter HttpConnection.getRequestOutputStream()");
788         assertOpen();
789         OutputStream JavaDoc out = this.outputStream;
790         if (Wire.CONTENT_WIRE.enabled()) {
791             out = new WireLogOutputStream(out, Wire.CONTENT_WIRE);
792         }
793         return out;
794     }
795
796     /**
797      * Returns an {@link OutputStream} suitable for writing the request.
798      *
799      * @param useChunking when <tt>true</tt> the chunked transfer-encoding will
800      * be used
801      * @throws IllegalStateException if the connection is not open
802      * @throws IOException if an I/O problem occurs
803      * @return a stream to write the request to
804      * @deprecated Use new ChunkedOutputStream(httpConnecion.getRequestOutputStream());
805      */

806     public OutputStream JavaDoc getRequestOutputStream(boolean useChunking)
807         throws IOException JavaDoc, IllegalStateException JavaDoc {
808         LOG.trace("enter HttpConnection.getRequestOutputStream(boolean)");
809
810         OutputStream JavaDoc out = getRequestOutputStream();
811         if (useChunking) {
812             out = new ChunkedOutputStream(out);
813         }
814         return out;
815     }
816
817     /**
818      * Return a {@link InputStream} suitable for reading the response.
819      * <p>
820      * If the given {@link HttpMethod} contains
821      * a <tt>Transfer-Encoding: chunked</tt> header,
822      * the returned stream will be configured
823      * to read chunked bytes.
824      *
825      * @param method This argument is ignored.
826      * @throws IllegalStateException if the connection is not open
827      * @throws IOException if an I/O problem occurs
828      * @return a stream to read the response from
829      * @deprecated Use getResponseInputStream() instead.
830      */

831     public InputStream JavaDoc getResponseInputStream(HttpMethod method)
832         throws IOException JavaDoc, IllegalStateException JavaDoc {
833         LOG.trace("enter HttpConnection.getResponseInputStream(HttpMethod)");
834         return getResponseInputStream();
835     }
836
837     /**
838      * Return a {@link InputStream} suitable for reading the response.
839      * @return InputStream The response input stream.
840      * @throws IOException If an IO problem occurs
841      * @throws IllegalStateException If the connection isn't open.
842      */

843     public InputStream JavaDoc getResponseInputStream()
844         throws IOException JavaDoc, IllegalStateException JavaDoc {
845         LOG.trace("enter HttpConnection.getResponseInputStream()");
846         assertOpen();
847         return inputStream;
848     }
849
850     /**
851      * Tests if input data avaialble. This method returns immediately
852      * and does not perform any read operations on the input socket
853      *
854      * @return boolean <tt>true</tt> if input data is available,
855      * <tt>false</tt> otherwise.
856      *
857      * @throws IOException If an IO problem occurs
858      * @throws IllegalStateException If the connection isn't open.
859      */

860     public boolean isResponseAvailable()
861         throws IOException JavaDoc {
862         LOG.trace("enter HttpConnection.isResponseAvailable()");
863         assertOpen();
864         return this.inputStream.available() > 0;
865     }
866
867     /**
868      * Tests if input data becomes available within the given period time in milliseconds.
869      *
870      * @param timeout The number milliseconds to wait for input data to become available
871      * @return boolean <tt>true</tt> if input data is availble,
872      * <tt>false</tt> otherwise.
873      *
874      * @throws IOException If an IO problem occurs
875      * @throws IllegalStateException If the connection isn't open.
876      */

877     public boolean isResponseAvailable(int timeout)
878         throws IOException JavaDoc {
879         LOG.trace("enter HttpConnection.isResponseAvailable(int)");
880         assertOpen();
881         boolean result = false;
882         if (this.inputStream.available() > 0) {
883             result = true;
884         } else {
885             try {
886                 this.socket.setSoTimeout(timeout);
887                 int byteRead = inputStream.read();
888                 if (byteRead != -1) {
889                     inputStream.unread(byteRead);
890                     LOG.debug("Input data available");
891                     result = true;
892                 } else {
893                     LOG.debug("Input data not available");
894                 }
895             } catch (InterruptedIOException JavaDoc e) {
896                 if (LOG.isDebugEnabled()) {
897                     LOG.debug("Input data not available after " + timeout + " ms");
898                 }
899             } finally {
900                 try {
901                     socket.setSoTimeout(soTimeout);
902                 } catch (IOException JavaDoc ioe) {
903                     LOG.debug("An error ocurred while resetting soTimeout, we will assume that"
904                         + " no response is available.",
905                         ioe);
906                     result = false;
907                 }
908             }
909         }
910         return result;
911     }
912
913     /**
914      * Writes the specified bytes to the output stream.
915      *
916      * @param data the data to be written
917      * @throws HttpRecoverableException if a SocketException occurs
918      * @throws IllegalStateException if not connected
919      * @throws IOException if an I/O problem occurs
920      * @see #write(byte[],int,int)
921      */

922     public void write(byte[] data)
923         throws IOException JavaDoc, IllegalStateException JavaDoc, HttpRecoverableException {
924         LOG.trace("enter HttpConnection.write(byte[])");
925         this.write(data, 0, data.length);
926     }
927
928     /**
929      * Writes <i>length</i> bytes in <i>data</i> starting at
930      * <i>offset</i> to the output stream.
931      *
932      * The general contract for
933      * write(b, off, len) is that some of the bytes in the array b are written
934      * to the output stream in order; element b[off] is the first byte written
935      * and b[off+len-1] is the last byte written by this operation.
936      *
937      * @param data array containing the data to be written.
938      * @param offset the start offset in the data.
939      * @param length the number of bytes to write.
940      * @throws HttpRecoverableException if a SocketException occurs
941      * @throws IllegalStateException if not connected
942      * @throws IOException if an I/O problem occurs
943      */

944     public void write(byte[] data, int offset, int length)
945         throws IOException JavaDoc, IllegalStateException JavaDoc, HttpRecoverableException {
946         LOG.trace("enter HttpConnection.write(byte[], int, int)");
947
948         if (offset + length > data.length) {
949             throw new HttpRecoverableException("Unable to write:"
950                     + " offset=" + offset + " length=" + length
951                     + " data.length=" + data.length);
952         } else if (data.length <= 0) {
953             throw new HttpRecoverableException(
954                 "Unable to write:" + " data.length=" + data.length);
955         }
956
957         assertOpen();
958
959         try {
960             outputStream.write(data, offset, length);
961         } catch (HttpRecoverableException hre) {
962             throw hre;
963         } catch (SocketException JavaDoc se) {
964             LOG.debug(
965                 "HttpConnection: Socket exception while writing data",
966                 se);
967             throw new HttpRecoverableException(se.toString());
968         } catch (IOException JavaDoc ioe) {
969             LOG.debug("HttpConnection: Exception while writing data", ioe);
970             throw ioe;
971         }
972     }
973
974     /**
975      * Writes the specified bytes, followed by <tt>"\r\n".getBytes()</tt> to the
976      * output stream.
977      *
978      * @param data the bytes to be written
979      * @throws HttpRecoverableException when socket exceptions occur writing data
980      * @throws IllegalStateException if the connection is not open
981      * @throws IOException if an I/O problem occurs
982      */

983     public void writeLine(byte[] data)
984         throws IOException JavaDoc, IllegalStateException JavaDoc, HttpRecoverableException {
985         LOG.trace("enter HttpConnection.writeLine(byte[])");
986         write(data);
987         writeLine();
988     }
989
990     /**
991      * Writes <tt>"\r\n".getBytes()</tt> to the output stream.
992      *
993      * @throws HttpRecoverableException when socket exceptions occur writing
994      * data
995      * @throws IllegalStateException if the connection is not open
996      * @throws IOException if an I/O problem occurs
997      */

998     public void writeLine()
999         throws IOException JavaDoc, IllegalStateException JavaDoc, HttpRecoverableException {
1000        LOG.trace("enter HttpConnection.writeLine()");
1001        write(CRLF);
1002    }
1003
1004    /**
1005     * Writes the specified String (as bytes) to the output stream.
1006     *
1007     * @param data the string to be written
1008     * @throws HttpRecoverableException when socket exceptions occur writing
1009     * data
1010     * @throws IllegalStateException if the connection is not open
1011     * @throws IOException if an I/O problem occurs
1012     */

1013    public void print(String JavaDoc data)
1014        throws IOException JavaDoc, IllegalStateException JavaDoc, HttpRecoverableException {
1015        LOG.trace("enter HttpConnection.print(String)");
1016        write(HttpConstants.getBytes(data));
1017    }
1018
1019    /**
1020     * Writes the specified String (as bytes), followed by
1021     * <tt>"\r\n".getBytes()</tt> to the output stream.
1022     *
1023     * @param data the data to be written
1024     * @throws HttpRecoverableException when socket exceptions occur writing
1025     * data
1026     * @throws IllegalStateException if the connection is not open
1027     * @throws IOException if an I/O problem occurs
1028     */

1029    public void printLine(String JavaDoc data)
1030        throws IOException JavaDoc, IllegalStateException JavaDoc, HttpRecoverableException {
1031        LOG.trace("enter HttpConnection.printLine(String)");
1032        writeLine(HttpConstants.getBytes(data));
1033    }
1034
1035    /**
1036     * Writes <tt>"\r\n".getBytes()</tt> to the output stream.
1037     *
1038     * @throws HttpRecoverableException when socket exceptions occur writing
1039     * data
1040     * @throws IllegalStateException if the connection is not open
1041     * @throws IOException if an I/O problem occurs
1042     */

1043    public void printLine()
1044        throws IOException JavaDoc, IllegalStateException JavaDoc, HttpRecoverableException {
1045        LOG.trace("enter HttpConnection.printLine()");
1046        writeLine();
1047    }
1048
1049    /**
1050     * Reads up to <tt>"\n"</tt> from the (unchunked) input stream.
1051     * If the stream ends before the line terminator is found,
1052     * the last part of the string will still be returned.
1053     *
1054     * @throws IllegalStateException if the connection is not open
1055     * @throws IOException if an I/O problem occurs
1056     * @return a line from the response
1057     */

1058    public String JavaDoc readLine() throws IOException JavaDoc, IllegalStateException JavaDoc {
1059        LOG.trace("enter HttpConnection.readLine()");
1060
1061        assertOpen();
1062        return HttpParser.readLine(inputStream);
1063    }
1064
1065    /**
1066     * Attempts to shutdown the {@link Socket}'s output, via Socket.shutdownOutput()
1067     * when running on JVM 1.3 or higher.
1068     */

1069    public void shutdownOutput() {
1070        LOG.trace("enter HttpConnection.shutdownOutput()");
1071
1072        try {
1073            // Socket.shutdownOutput is a JDK 1.3
1074
// method. We'll use reflection in case
1075
// we're running in an older VM
1076
Class JavaDoc[] paramsClasses = new Class JavaDoc[0];
1077            Method JavaDoc shutdownOutput =
1078                socket.getClass().getMethod("shutdownOutput", paramsClasses);
1079            Object JavaDoc[] params = new Object JavaDoc[0];
1080            shutdownOutput.invoke(socket, params);
1081        } catch (Exception JavaDoc ex) {
1082            LOG.debug("Unexpected Exception caught", ex);
1083            // Ignore, and hope everything goes right
1084
}
1085        // close output stream?
1086
}
1087
1088    /**
1089     * Closes the socket and streams.
1090     */

1091    public void close() {
1092        LOG.trace("enter HttpConnection.close()");
1093        closeSocketAndStreams();
1094    }
1095
1096    /**
1097     * Returns the httpConnectionManager.
1098     * @return HttpConnectionManager
1099     */

1100    public HttpConnectionManager getHttpConnectionManager() {
1101        return httpConnectionManager;
1102    }
1103
1104    /**
1105     * Sets the httpConnectionManager.
1106     * @param httpConnectionManager The httpConnectionManager to set
1107     */

1108    public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
1109        this.httpConnectionManager = httpConnectionManager;
1110    }
1111
1112    /**
1113     * Release the connection.
1114     */

1115    public void releaseConnection() {
1116        LOG.trace("enter HttpConnection.releaseConnection()");
1117
1118        // we are assuming that the connection will only be released once used
1119
used = true;
1120        if (httpConnectionManager != null) {
1121            httpConnectionManager.releaseConnection(this);
1122        }
1123    }
1124
1125    // ------------------------------------------------------ Protected Methods
1126

1127    /**
1128     * Closes everything out.
1129     */

1130    protected void closeSocketAndStreams() {
1131        LOG.trace("enter HttpConnection.closeSockedAndStreams()");
1132
1133        // no longer care about previous responses...
1134
lastResponseInputStream = null;
1135
1136        if (null != outputStream) {
1137            OutputStream JavaDoc temp = outputStream;
1138            outputStream = null;
1139            try {
1140                temp.close();
1141            } catch (Exception JavaDoc ex) {
1142                LOG.debug("Exception caught when closing output", ex);
1143                // ignored
1144
}
1145        }
1146
1147        if (null != inputStream) {
1148            InputStream JavaDoc temp = inputStream;
1149            inputStream = null;
1150            try {
1151                temp.close();
1152            } catch (Exception JavaDoc ex) {
1153                LOG.debug("Exception caught when closing input", ex);
1154                // ignored
1155
}
1156        }
1157
1158        if (null != socket) {
1159            Socket JavaDoc temp = socket;
1160            socket = null;
1161            try {
1162                temp.close();
1163            } catch (Exception JavaDoc ex) {
1164                LOG.debug("Exception caught when closing socket", ex);
1165                // ignored
1166
}
1167        }
1168        isOpen = false;
1169        used = false;
1170        tunnelEstablished = false;
1171        usingSecureSocket = false;
1172    }
1173
1174    /**
1175     * Throws an {@link IllegalStateException} if the connection is already open.
1176     *
1177     * @throws IllegalStateException if connected
1178     */

1179    protected void assertNotOpen() throws IllegalStateException JavaDoc {
1180        if (isOpen) {
1181            throw new IllegalStateException JavaDoc("Connection is open");
1182        }
1183    }
1184
1185    /**
1186     * Throws an {@link IllegalStateException} if the connection is not open.
1187     *
1188     * @throws IllegalStateException if not connected
1189     */

1190    protected void assertOpen() throws IllegalStateException JavaDoc {
1191        if (!isOpen) {
1192            throw new IllegalStateException JavaDoc("Connection is not open");
1193        }
1194    }
1195
1196    /**
1197     * Gets the socket's sendBufferSize.
1198     *
1199     * @return the size of the buffer for the socket OutputStream, -1 if the value
1200     * has not been set and the socket has not been opened
1201     *
1202     * @throws SocketException if an error occurs while getting the socket value
1203     *
1204     * @see Socket#getSendBufferSize()
1205     */

1206    public int getSendBufferSize() throws SocketException JavaDoc {
1207        if (socket == null) {
1208            return -1;
1209        } else {
1210            return socket.getSendBufferSize();
1211        }
1212    }
1213
1214    /**
1215     * Sets the socket's sendBufferSize.
1216     *
1217     * @param sendBufferSize the size to set for the socket OutputStream
1218     *
1219     * @throws SocketException if an error occurs while setting the socket value
1220     *
1221     * @see Socket#setSendBufferSize(int)
1222     */

1223    public void setSendBufferSize(int sendBufferSize) throws SocketException JavaDoc {
1224        this.sendBufferSize = sendBufferSize;
1225        if (socket != null) {
1226            socket.setSendBufferSize(sendBufferSize);
1227        }
1228    }
1229
1230    // -- Timeout Exception
1231
/**
1232     * Signals that a timeout occured while opening the socket.
1233     */

1234    public static class ConnectionTimeoutException extends IOException JavaDoc {
1235        /** Create an instance */
1236        public ConnectionTimeoutException() {
1237        }
1238    }
1239
1240
1241    /**
1242     * Helper class for wrapping socket based tasks.
1243     */

1244    private abstract class SocketTask implements Runnable JavaDoc {
1245
1246        /** The socket */
1247        private Socket JavaDoc socket;
1248        /** The exception */
1249        private IOException JavaDoc exception;
1250
1251        /**
1252         * Set the socket.
1253         * @param newSocket The new socket.
1254         */

1255        protected void setSocket(final Socket JavaDoc newSocket) {
1256            socket = newSocket;
1257        }
1258
1259        /**
1260         * Return the socket.
1261         * @return Socket The socket.
1262         */

1263        protected Socket JavaDoc getSocket() {
1264            return socket;
1265        }
1266        /**
1267         * Perform the logic.
1268         * @throws IOException If an IO problem occurs
1269         */

1270        public abstract void doit() throws IOException JavaDoc;
1271
1272        /** Execute the logic in this object and keep track of any exceptions. */
1273        public void run() {
1274            try {
1275                doit();
1276            } catch (IOException JavaDoc e) {
1277                exception = e;
1278            }
1279        }
1280    }
1281
1282    /**
1283     * A wrapper for output streams that closes the connection and converts to recoverable
1284     * exceptions as appropriable when an IOException occurs.
1285     */

1286    private class WrappedOutputStream extends OutputStream JavaDoc {
1287
1288        /** the actual output stream */
1289        private OutputStream JavaDoc out;
1290
1291        /**
1292         * @param out the output stream to wrap
1293         */

1294        public WrappedOutputStream(OutputStream JavaDoc out) {
1295            this.out = out;
1296        }
1297
1298        /**
1299         * Closes the connection and conditionally converts exception to recoverable.
1300         * @param ioe the exception that occurred
1301         * @return the exception to be thrown
1302         */

1303        private IOException JavaDoc handleException(IOException JavaDoc ioe) {
1304            // keep the original value of used, as it will be set to false by close().
1305
boolean tempUsed = used;
1306            HttpConnection.this.close();
1307            if (tempUsed) {
1308                LOG.debug(
1309                    "Output exception occurred on a used connection. Will treat as recoverable.",
1310                    ioe
1311                );
1312                return new HttpRecoverableException(ioe.toString());
1313            } else {
1314                return ioe;
1315            }
1316        }
1317
1318        public void write(int b) throws IOException JavaDoc {
1319            try {
1320                out.write(b);
1321            } catch (IOException JavaDoc ioe) {
1322                throw handleException(ioe);
1323            }
1324        }
1325        
1326        public void flush() throws IOException JavaDoc {
1327            try {
1328                out.flush();
1329            } catch (IOException JavaDoc ioe) {
1330                throw handleException(ioe);
1331            }
1332        }
1333
1334        public void close() throws IOException JavaDoc {
1335            try {
1336                out.close();
1337            } catch (IOException JavaDoc ioe) {
1338                throw handleException(ioe);
1339            }
1340        }
1341
1342        public void write(byte[] b, int off, int len) throws IOException JavaDoc {
1343            try {
1344                out.write(b, off, len);
1345            } catch (IOException JavaDoc ioe) {
1346                throw handleException(ioe);
1347            }
1348        }
1349
1350        public void write(byte[] b) throws IOException JavaDoc {
1351            try {
1352                out.write(b);
1353            } catch (IOException JavaDoc ioe) {
1354                throw handleException(ioe);
1355            }
1356        }
1357
1358    }
1359
1360    // ------------------------------------------------------- Static Variable
1361

1362    /** <tt>"\r\n"</tt>, as bytes. */
1363    private static final byte[] CRLF = new byte[] {(byte) 13, (byte) 10};
1364
1365    /** Log object for this class. */
1366    private static final Log LOG = LogFactory.getLog(HttpConnection.class);
1367    
1368    // ----------------------------------------------------- Instance Variables
1369

1370    /** A flag indicating if this connection has been used since being opened */
1371    private boolean used = false;
1372    
1373    /** My host. */
1374    private String JavaDoc hostName = null;
1375    
1376    /** My virtual host. */
1377    private String JavaDoc virtualName = null;
1378    
1379    /** My port. */
1380    private int portNumber = -1;
1381    
1382    /** My proxy host. */
1383    private String JavaDoc proxyHostName = null;
1384    
1385    /** My proxy port. */
1386    private int proxyPortNumber = -1;
1387    
1388    /** My client Socket. */
1389    private Socket JavaDoc socket = null;
1390    
1391    /** My InputStream. */
1392    private PushbackInputStream JavaDoc inputStream = null;
1393
1394    /** My OutputStream. */
1395    private OutputStream JavaDoc outputStream = null;
1396    
1397    /** the size of the buffer to use for the socket OutputStream */
1398    private int sendBufferSize = -1;
1399    
1400    /** An {@link InputStream} for the response to an individual request. */
1401    private InputStream JavaDoc lastResponseInputStream = null;
1402    
1403    /** Whether or not the connection is connected. */
1404    protected boolean isOpen = false;
1405    
1406    /** the protocol being used */
1407    private Protocol protocolInUse;
1408    
1409    /** SO_TIMEOUT socket value */
1410    private int soTimeout = 0;
1411    
1412    /** TCP_NODELAY socket value */
1413    private boolean soNodelay = true;
1414    
1415    /** Whether or not the socket is a secure one. */
1416    private boolean usingSecureSocket = false;
1417    
1418    /** Whether the connection is open via a secure tunnel or not */
1419    private boolean tunnelEstablished = false;
1420    
1421    /** Whether or not isStale() is used by isOpen() */
1422    private boolean staleCheckingEnabled = true;
1423    
1424    /** Timeout until connection established (Socket created). 0 means no timeout. */
1425    private int connectTimeout = 0;
1426    
1427    /** the connection manager that created this connection or null */
1428    private HttpConnectionManager httpConnectionManager;
1429    
1430    /** The local interface on which the connection is created, or null for the default */
1431    private InetAddress JavaDoc localAddress;
1432}
1433
Popular Tags