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