alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  
/*
 * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v 1.67.2.6 2003/11/01 21:13:56 olegk Exp $
 * $Revision: 1.67.2.6 $
 * $Date: 2003/11/01 21:13:56 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * .
 *
 * [Additional notices, if required by prior licensing conditions]
 *
 */

package org.apache.commons.httpclient;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;

import org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.apache.commons.httpclient.util.TimeoutController;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * An abstraction of an HTTP {@link InputStream} and {@link OutputStream}
 * pair, together with the relevant attributes.
 * 

* The following options are set on the socket before getting the input/output * streams in the {@link #open()} method: * * * *
Socket Method * Sockets Option * Configuration *
{@link java.net.Socket#setTcpNoDelay(boolean)} * SO_NODELAY * None *
{@link java.net.Socket#setSoTimeout(int)} * SO_TIMEOUT * {@link #setConnectionTimeout(int)} *
* * @author Rod Waldhoff * @author Sean C. Sullivan * @author Ortwin Glück * @author Jeff Dever * @author Mike Bowler * @author Oleg Kalnichevski * @author Michael Becke * @author Eric E Johnson * @author Laura Werner * * @version $Revision: 1.67.2.6 $ $Date: 2003/11/01 21:13:56 $ */ public class HttpConnection { // ----------------------------------------------------------- Constructors /** * Creates a new HTTP connection for the given host and port. * * @param host the host to connect to * @param port the port to connect to */ public HttpConnection(String host, int port) { this(null, -1, host, port, false); } /** * Creates a new HTTP connection for the given host and port. * If secure attribute is set, use SSL to establish the connection. * * @param host the host to connect to * @param port the port to connect to * @param secure when true, connect via HTTPS (SSL) * * @deprecated use HttpConnection(String, int, Protocol) * * @see #HttpConnection(String,int,Protocol) * */ public HttpConnection(String host, int port, boolean secure) { this(null, -1, host, port, secure); } /** * Creates a new HTTP connection for the given host and port * using the given protocol. * * @param host the host to connect to * @param port the port to connect to * @param protocol the protocol to use */ public HttpConnection(String host, int port, Protocol protocol) { this(null, -1, host, null, port, protocol); } /** * Creates a new HTTP connection for the given host with the virtual * alias and port using given protocol. * * @param host the host to connect to * @param virtualHost the virtual host requests will be sent to * @param port the port to connect to * @param protocol the protocol to use */ public HttpConnection(String host, String virtualHost, int port, Protocol protocol) { this(null, -1, host, virtualHost, port, protocol); } /** * Creates a new HTTP connection for the given host and port via the * given proxy host and port using the default protocol. * * @param proxyHost the host to proxy via * @param proxyPort the port to proxy via * @param host the host to connect to * @param port the port to connect to */ public HttpConnection( String proxyHost, int proxyPort, String host, int port) { this(proxyHost, proxyPort, host, port, false); } /** * Creates a new HTTP connection for the given host and port via * the given proxy host and port. If secure attribute is set, * use SSL to establish the connection. * * @param proxyHost the host I should proxy via * @param proxyPort the port I should proxy via * @param host the host to connect to. Parameter value must be non-null. * @param port the port to connect to * @param secure when true, connect via HTTPS (SSL) * * @deprecated use HttpConnection(String, int, String, int, Protocol) * * @see #HttpConnection(String, int, String, String, int, Protocol) * */ public HttpConnection( String proxyHost, int proxyPort, String host, int port, boolean secure) { this(proxyHost, proxyPort, host, null, port, Protocol.getProtocol(secure ? "https" : "http")); } /** * Creates a new HTTP connection for the given host configuration. * * @param hostConfiguration the host/proxy/protocol to use */ public HttpConnection(HostConfiguration hostConfiguration) { this(hostConfiguration.getProxyHost(), hostConfiguration.getProxyPort(), hostConfiguration.getHost(), hostConfiguration.getVirtualHost(), hostConfiguration.getPort(), hostConfiguration.getProtocol()); this.localAddress = hostConfiguration.getLocalAddress(); } /** * Creates a new HTTP connection for the given host with the virtual * alias and port via the given proxy host and port using the given * protocol. * * @param proxyHost the host to proxy via * @param proxyPort the port to proxy via * @param host the host to connect to. Parameter value must be non-null. * @param virtualHost the virtual host requests will be sent to * @param port the port to connect to * @param protocol The protocol to use. Parameter value must be non-null. */ public HttpConnection( String proxyHost, int proxyPort, String host, String virtualHost, int port, Protocol protocol) { if (host == null) { throw new IllegalArgumentException("host parameter is null"); } if (protocol == null) { throw new IllegalArgumentException("protocol is null"); } proxyHostName = proxyHost; proxyPortNumber = proxyPort; hostName = host; virtualName = virtualHost; portNumber = protocol.resolvePort(port); protocolInUse = protocol; } // ------------------------------------------ Attribute Setters and Getters /** * Returns the host. * * @return the host. */ public String getHost() { return hostName; } /** * Sets the host to connect to. * * @param host the host to connect to. Parameter value must be non-null. * @throws IllegalStateException if the connection is already open */ public void setHost(String host) throws IllegalStateException { if (host == null) { throw new IllegalArgumentException("host parameter is null"); } assertNotOpen(); hostName = host; } /** * Returns the target virtual host. * * @return the virtual host. */ public String getVirtualHost() { return virtualName; } /** * Sets the virtual host to target. * * @param host the virtual host name that should be used instead of * physical host name when sending HTTP requests. Virtual host * name can be set to null if virtual host name is not * to be used * * @throws IllegalStateException if the connection is already open */ public void setVirtualHost(String host) throws IllegalStateException { assertNotOpen(); virtualName = host; } /** * Returns the port of the host. * * If the port is -1 (or less than 0) the default port for * the current protocol is returned. * * @return the port. */ public int getPort() { if (portNumber < 0) { return isSecure() ? 443 : 80; } else { return portNumber; } } /** * Sets the port to connect to. * * @param port the port to connect to * * @throws IllegalStateException if the connection is already open */ public void setPort(int port) throws IllegalStateException { assertNotOpen(); portNumber = port; } /** * Returns the proxy host. * * @return the proxy host. */ public String getProxyHost() { return proxyHostName; } /** * Sets the host to proxy through. * * @param host the host to proxy through. * * @throws IllegalStateException if the connection is already open */ public void setProxyHost(String host) throws IllegalStateException { assertNotOpen(); proxyHostName = host; } /** * Returns the port of the proxy host. * * @return the proxy port. */ public int getProxyPort() { return proxyPortNumber; } /** * Sets the port of the host to proxy through. * * @param port the port of the host to proxy through. * * @throws IllegalStateException if the connection is already open */ public void setProxyPort(int port) throws IllegalStateException { assertNotOpen(); proxyPortNumber = port; } /** * Returns true if the connection is established over * a secure protocol. * * @return true if connected over a secure protocol. */ public boolean isSecure() { return protocolInUse.isSecure(); } /** * Returns the protocol used to establish the connection. * @return The protocol */ public Protocol getProtocol() { return protocolInUse; } /** * Defines whether the connection should be established over a * secure protocol. * * @param secure whether or not to connect over a secure protocol. * * @throws IllegalStateException if the connection is already open * * @deprecated use setProtocol(Protocol) * * @see #setProtocol(Protocol) */ public void setSecure(boolean secure) throws IllegalStateException { assertNotOpen(); protocolInUse = secure ? Protocol.getProtocol("https") : Protocol.getProtocol("http"); } /** * Sets the protocol used to establish the connection * * @param protocol The protocol to use. * * @throws IllegalStateException if the connection is already open */ public void setProtocol(Protocol protocol) { assertNotOpen(); if (protocol == null) { throw new IllegalArgumentException("protocol is null"); } protocolInUse = protocol; } /** * Return the local address used when creating the connection. * If null, the default address is used. * * @return InetAddress the local address to be used when creating Sockets */ public InetAddress getLocalAddress() { return this.localAddress; } /** * Set the local address used when creating the connection. * If unset or null, the default address is used. * * @param localAddress the local address to use */ public void setLocalAddress(InetAddress localAddress) { assertNotOpen(); this.localAddress = localAddress; } /** * Returns true if the connection is open, * false otherwise. * * @return true if the connection is open */ public boolean isOpen() { if (used && isStaleCheckingEnabled() && isStale()) { LOG.debug("Connection is stale, closing..."); close(); } return isOpen; } /** * Tests if stale checking is enabled. * * @return true if enabled * * @see #isStale() */ public boolean isStaleCheckingEnabled() { return staleCheckingEnabled; } /** * Sets whether or not isStale() will be called when testing if this connection is open. * *

Setting this flag to false will increase performance when reusing * connections, but it will also make them less reliable. Stale checking ensures that * connections are viable before they are used. When set to false some * method executions will result in IOExceptions and they will have to be retried.

* * @param staleCheckEnabled true to enable isStale() * * @see #isStale() * @see #isOpen() */ public void setStaleCheckingEnabled(boolean staleCheckEnabled) { this.staleCheckingEnabled = staleCheckEnabled; } /** * Determines whether this connection is "stale", which is to say that either * it is no longer open, or an attempt to read the connection would fail. * *

Unfortunately, due to the limitations of the JREs prior to 1.4, it is * not possible to test a connection to see if both the read and write channels * are open - except by reading and writing. This leads to a difficulty when * some connections leave the "write" channel open, but close the read channel * and ignore the request. This function attempts to ameliorate that * problem by doing a test read, assuming that the caller will be doing a * write followed by a read, rather than the other way around. *

* *

To avoid side-effects, the underlying connection is wrapped by a * {@link PushbackInputStream}, so although data might be read, what is visible * to clients of the connection will not change with this call.true if the connection is already closed, or a read would * fail. */ protected boolean isStale() { boolean isStale = true; if (isOpen) { // the connection is open, but now we have to see if we can read it // assume the connection is not stale. isStale = false; try { if (inputStream.available() == 0) { try { socket.setSoTimeout(1); int byteRead = inputStream.read(); if (byteRead == -1) { // again - if the socket is reporting all data read, // probably stale isStale = true; } else { inputStream.unread(byteRead); } } finally { socket.setSoTimeout(soTimeout); } } } catch (InterruptedIOException e) { // aha - the connection is NOT stale - continue on! } catch (IOException e) { // oops - the connection is stale, the read or soTimeout failed. LOG.debug( "An error occurred while reading from the socket, is appears to be stale", e ); isStale = true; } } return isStale; } /** * Returns true if the connection is established via a proxy, * false otherwise. * * @return true if a proxy is used to establish the connection, * false otherwise. */ public boolean isProxied() { return (!(null == proxyHostName || 0 >= proxyPortNumber)); } /** * Set the state to keep track of the last response for the last request. * *

The connection managers use this to ensure that previous requests are * properly closed before a new request is attempted. That way, a GET * request need not be read in its entirety before a new request is issued. * Instead, this stream can be closed as appropriate.

* * @param inStream The stream associated with an HttpMethod. */ public void setLastResponseInputStream(InputStream inStream) { lastResponseInputStream = inStream; } /** * Returns the stream used to read the last response's body. * *

Clients will generally not need to call this function unless * using HttpConnection directly, instead of calling {@link HttpClient#executeMethod}. * For those clients, call this function, and if it returns a non-null stream, * close the stream before attempting to execute a method. Note that * calling "close" on the stream returned by this function may close * the connection if the previous response contained a "Connection: close" header.

* * @return An {@link InputStream} corresponding to the body of the last * response. */ public InputStream getLastResponseInputStream() { return lastResponseInputStream; } // --------------------------------------------------- Other Public Methods /** * Sets the {@link Socket}'s timeout, via {@link Socket#setSoTimeout}. If the * connection is already open, the SO_TIMEOUT is changed. If no connection * is open, then subsequent connections will use the timeout value. *

* Note: This is not a connection timeout but a timeout on network traffic! * * @param timeout the timeout value * @throws SocketException - if there is an error in the underlying * protocol, such as a TCP error. */ public void setSoTimeout(int timeout) throws SocketException, IllegalStateException { LOG.debug("HttpConnection.setSoTimeout(" + timeout + ")"); soTimeout = timeout; if (socket != null) { socket.setSoTimeout(timeout); } } /** * Returns the {@link Socket}'s timeout, via {@link Socket#getSoTimeout}, if the * connection is already open. If no connection is open, return the value subsequent * connection will use. *

* Note: This is not a connection timeout but a timeout on network traffic! * * @return the timeout value */ public int getSoTimeout() throws SocketException { LOG.debug("HttpConnection.getSoTimeout()"); if (this.socket != null) { return this.socket.getSoTimeout(); } else { return this.soTimeout; } } /** * Sets the connection timeout. This is the maximum time that may be spent * until a connection is established. The connection will fail after this * amount of time. * @param timeout The timeout in milliseconds. 0 means timeout is not used. */ public void setConnectionTimeout(int timeout) { this.connectTimeout = timeout; } /** * Establishes a connection to the specified host and port * (via a proxy if specified). * The underlying socket is created from the {@link ProtocolSocketFactory}. * * @throws IOException if an attempt to establish the connection results in an * I/O error. */ public void open() throws IOException { LOG.trace("enter HttpConnection.open()"); assertNotOpen(); try { if (null == socket) { final String host = (null == proxyHostName) ? hostName : proxyHostName; final int port = (null == proxyHostName) ? portNumber : proxyPortNumber; usingSecureSocket = isSecure() && !isProxied(); // use the protocol's socket factory unless this is a secure // proxied connection final ProtocolSocketFactory socketFactory = (isSecure() && isProxied() ? new DefaultProtocolSocketFactory() : protocolInUse.getSocketFactory()); if (connectTimeout == 0) { if (localAddress != null) { socket = socketFactory.createSocket(host, port, localAddress, 0); } else { socket = socketFactory.createSocket(host, port); } } else { SocketTask task = new SocketTask() { public void doit() throws IOException { if (localAddress != null) { setSocket(socketFactory.createSocket(host, port, localAddress, 0)); } else { setSocket(socketFactory.createSocket(host, port)); } } }; TimeoutController.execute(task, connectTimeout); socket = task.getSocket(); if (task.exception != null) { throw task.exception; } } } /* "Nagling has been broadly implemented across networks, including the Internet, and is generally performed by default - although it is sometimes considered to be undesirable in highly interactive environments, such as some client/server situations. In such cases, nagling may be turned off through use of the TCP_NODELAY sockets option." */ socket.setTcpNoDelay(soNodelay); socket.setSoTimeout(soTimeout); if (sendBufferSize != -1) { socket.setSendBufferSize(sendBufferSize); } inputStream = new PushbackInputStream(socket.getInputStream()); outputStream = new BufferedOutputStream( new WrappedOutputStream(socket.getOutputStream()), socket.getSendBufferSize() ); isOpen = true; used = false; } catch (IOException e) { // Connection wasn't opened properly // so close everything out closeSocketAndStreams(); throw e; } catch (TimeoutController.TimeoutException e) { if (LOG.isWarnEnabled()) { LOG.warn("The host " + hostName + ":" + portNumber + " (or proxy " + proxyHostName + ":" + proxyPortNumber + ") did not accept the connection within timeout of " + connectTimeout + " milliseconds"); } throw new ConnectionTimeoutException(); } } /** * Instructs the proxy to establish a secure tunnel to the host. The socket will * be switched to the secure socket. Subsequent communication is done via the secure * socket. The method can only be called once on a proxied secure connection. * * @throws IllegalStateException if connection is not secure and proxied or * if the socket is already secure. * @throws IOException if an attempt to establish the secure tunnel results in an * I/O error. */ public void tunnelCreated() throws IllegalStateException, IOException { LOG.trace("enter HttpConnection.tunnelCreated()"); if (!isSecure() || !isProxied()) { throw new IllegalStateException( "Connection must be secure " + "and proxied to use this feature"); } if (usingSecureSocket) { throw new IllegalStateException("Already using a secure socket"); } SecureProtocolSocketFactory socketFactory = (SecureProtocolSocketFactory) protocolInUse.getSocketFactory(); socket = socketFactory.createSocket(socket, hostName, portNumber, true); if (sendBufferSize != -1) { socket.setSendBufferSize(sendBufferSize); } inputStream = new PushbackInputStream(socket.getInputStream()); outputStream = new BufferedOutputStream( new WrappedOutputStream(socket.getOutputStream()), socket.getSendBufferSize() ); usingSecureSocket = true; tunnelEstablished = true; LOG.debug("Secure tunnel created"); } /** * Indicates if the connection is completely transparent from end to end. * * @return true if conncetion is not proxied or tunneled through a transparent * proxy; false otherwise. */ public boolean isTransparent() { return !isProxied() || tunnelEstablished; } /** * Flushes the output request stream. This method should be called to * ensure that data written to the request OutputStream is sent to the server. * * @throws IOException if an I/O problem occurs */ public void flushRequestOutputStream() throws IOException { LOG.trace("enter HttpConnection.flushRequestOutputStream()"); assertOpen(); outputStream.flush(); } /** * Returns an {@link OutputStream} suitable for writing the request. * * @throws IllegalStateException if the connection is not open * @throws IOException if an I/O problem occurs * @return a stream to write the request to */ public OutputStream getRequestOutputStream() throws IOException, IllegalStateException { LOG.trace("enter HttpConnection.getRequestOutputStream()"); assertOpen(); OutputStream out = this.outputStream; if (Wire.enabled()) { out = new WireLogOutputStream(out); } return out; } /** * Returns an {@link OutputStream} suitable for writing the request. * * @param useChunking when true the chunked transfer-encoding will * be used * @throws IllegalStateException if the connection is not open * @throws IOException if an I/O problem occurs * @return a stream to write the request to * @deprecated Use new ChunkedOutputStream(httpConnecion.getRequestOutputStream()); */ public OutputStream getRequestOutputStream(boolean useChunking) throws IOException, IllegalStateException { LOG.trace("enter HttpConnection.getRequestOutputStream(boolean)"); OutputStream out = getRequestOutputStream(); if (useChunking) { out = new ChunkedOutputStream(out); } return out; } /** * Return a {@link InputStream} suitable for reading the response. *

* If the given {@link HttpMethod} contains * a Transfer-Encoding: chunked header, * the returned stream will be configured * to read chunked bytes. * * @param method This argument is ignored. * @throws IllegalStateException if the connection is not open * @throws IOException if an I/O problem occurs * @return a stream to read the response from * @deprecated Use getResponseInputStream() instead. */ public InputStream getResponseInputStream(HttpMethod method) throws IOException, IllegalStateException { LOG.trace("enter HttpConnection.getResponseInputStream(HttpMethod)"); return getResponseInputStream(); } /** * Return a {@link InputStream} suitable for reading the response. * @return InputStream The response input stream. * @throws IOException If an IO problem occurs * @throws IllegalStateException If the connection isn't open. */ public InputStream getResponseInputStream() throws IOException, IllegalStateException { LOG.trace("enter HttpConnection.getResponseInputStream()"); assertOpen(); return inputStream; } /** * Tests if input data avaialble. This method returns immediately * and does not perform any read operations on the input socket * * @return boolean true if input data is available, * false otherwise. * * @throws IOException If an IO problem occurs * @throws IllegalStateException If the connection isn't open. */ public boolean isResponseAvailable() throws IOException { LOG.trace("enter HttpConnection.isResponseAvailable()"); assertOpen(); return this.inputStream.available() > 0; } /** * Tests if input data becomes available within the given period time in milliseconds. * * @param timeout The number milliseconds to wait for input data to become available * @return boolean true if input data is availble, * false otherwise. * * @throws IOException If an IO problem occurs * @throws IllegalStateException If the connection isn't open. */ public boolean isResponseAvailable(int timeout) throws IOException { LOG.trace("enter HttpConnection.isResponseAvailable(int)"); assertOpen(); boolean result = false; if (this.inputStream.available() > 0) { result = true; } else { try { this.socket.setSoTimeout(timeout); int byteRead = inputStream.read(); if (byteRead != -1) { inputStream.unread(byteRead); LOG.debug("Input data available"); result = true; } else { LOG.debug("Input data not available"); } } catch (InterruptedIOException e) { if (LOG.isDebugEnabled()) { LOG.debug("Input data not available after " + timeout + " ms"); } } finally { try { socket.setSoTimeout(soTimeout); } catch (IOException ioe) { LOG.debug("An error ocurred while resetting soTimeout, we will assume that" + " no response is available.", ioe); result = false; } } } return result; } /** * Writes the specified bytes to the output stream. * * @param data the data to be written * @throws HttpRecoverableException if a SocketException occurs * @throws IllegalStateException if not connected * @throws IOException if an I/O problem occurs * @see #write(byte[],int,int) */ public void write(byte[] data) throws IOException, IllegalStateException, HttpRecoverableException { LOG.trace("enter HttpConnection.write(byte[])"); this.write(data, 0, data.length); } /** * Writes length bytes in data starting at * offset to the output stream. * * The general contract for * write(b, off, len) is that some of the bytes in the array b are written * to the output stream in order; element b[off] is the first byte written * and b[off+len-1] is the last byte written by this operation. * * @param data array containing the data to be written. * @param offset the start offset in the data. * @param length the number of bytes to write. * @throws HttpRecoverableException if a SocketException occurs * @throws IllegalStateException if not connected * @throws IOException if an I/O problem occurs */ public void write(byte[] data, int offset, int length) throws IOException, IllegalStateException, HttpRecoverableException { LOG.trace("enter HttpConnection.write(byte[], int, int)"); if (offset + length > data.length) { throw new HttpRecoverableException("Unable to write:" + " offset=" + offset + " length=" + length + " data.length=" + data.length); } else if (data.length <= 0) { throw new HttpRecoverableException( "Unable to write:" + " data.length=" + data.length); } assertOpen(); try { outputStream.write(data, offset, length); } catch (HttpRecoverableException hre) { throw hre; } catch (SocketException se) { LOG.debug( "HttpConnection: Socket exception while writing data", se); throw new HttpRecoverableException(se.toString()); } catch (IOException ioe) { LOG.debug("HttpConnection: Exception while writing data", ioe); throw ioe; } } /** * Writes the specified bytes, followed by "\r\n".getBytes() to the * output stream. * * @param data the bytes to be written * @throws HttpRecoverableException when socket exceptions occur writing data * @throws IllegalStateException if the connection is not open * @throws IOException if an I/O problem occurs */ public void writeLine(byte[] data) throws IOException, IllegalStateException, HttpRecoverableException { LOG.trace("enter HttpConnection.writeLine(byte[])"); write(data); writeLine(); } /** * Writes "\r\n".getBytes() to the output stream. * * @throws HttpRecoverableException when socket exceptions occur writing * data * @throws IllegalStateException if the connection is not open * @throws IOException if an I/O problem occurs */ public void writeLine() throws IOException, IllegalStateException, HttpRecoverableException { LOG.trace("enter HttpConnection.writeLine()"); write(CRLF); } /** * Writes the specified String (as bytes) to the output stream. * * @param data the string to be written * @throws HttpRecoverableException when socket exceptions occur writing * data * @throws IllegalStateException if the connection is not open * @throws IOException if an I/O problem occurs */ public void print(String data) throws IOException, IllegalStateException, HttpRecoverableException { LOG.trace("enter HttpConnection.print(String)"); write(HttpConstants.getBytes(data)); } /** * Writes the specified String (as bytes), followed by * "\r\n".getBytes() to the output stream. * * @param data the data to be written * @throws HttpRecoverableException when socket exceptions occur writing * data * @throws IllegalStateException if the connection is not open * @throws IOException if an I/O problem occurs */ public void printLine(String data) throws IOException, IllegalStateException, HttpRecoverableException { LOG.trace("enter HttpConnection.printLine(String)"); writeLine(HttpConstants.getBytes(data)); } /** * Writes "\r\n".getBytes() to the output stream. * * @throws HttpRecoverableException when socket exceptions occur writing * data * @throws IllegalStateException if the connection is not open * @throws IOException if an I/O problem occurs */ public void printLine() throws IOException, IllegalStateException, HttpRecoverableException { LOG.trace("enter HttpConnection.printLine()"); writeLine(); } /** * Reads up to "\n" from the (unchunked) input stream. * If the stream ends before the line terminator is found, * the last part of the string will still be returned. * * @throws IllegalStateException if the connection is not open * @throws IOException if an I/O problem occurs * @return a line from the response */ public String readLine() throws IOException, IllegalStateException { LOG.trace("enter HttpConnection.readLine()"); assertOpen(); return HttpParser.readLine(inputStream); } /** * Attempts to shutdown the {@link Socket}'s output, via Socket.shutdownOutput() * when running on JVM 1.3 or higher. */ public void shutdownOutput() { LOG.trace("enter HttpConnection.shutdownOutput()"); try { // Socket.shutdownOutput is a JDK 1.3 // method. We'll use reflection in case // we're running in an older VM Class[] paramsClasses = new Class[0]; Method shutdownOutput = socket.getClass().getMethod("shutdownOutput", paramsClasses); Object[] params = new Object[0]; shutdownOutput.invoke(socket, params); } catch (Exception ex) { LOG.debug("Unexpected Exception caught", ex); // Ignore, and hope everything goes right } // close output stream? } /** * Closes the socket and streams. */ public void close() { LOG.trace("enter HttpConnection.close()"); closeSocketAndStreams(); } /** * Returns the httpConnectionManager. * @return HttpConnectionManager */ public HttpConnectionManager getHttpConnectionManager() { return httpConnectionManager; } /** * Sets the httpConnectionManager. * @param httpConnectionManager The httpConnectionManager to set */ public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) { this.httpConnectionManager = httpConnectionManager; } /** * Release the connection. */ public void releaseConnection() { LOG.trace("enter HttpConnection.releaseConnection()"); // we are assuming that the connection will only be released once used used = true; if (httpConnectionManager != null) { httpConnectionManager.releaseConnection(this); } } // ------------------------------------------------------ Protected Methods /** * Closes everything out. */ protected void closeSocketAndStreams() { LOG.trace("enter HttpConnection.closeSockedAndStreams()"); // no longer care about previous responses... lastResponseInputStream = null; if (null != outputStream) { OutputStream temp = outputStream; outputStream = null; try { temp.close(); } catch (Exception ex) { LOG.debug("Exception caught when closing output", ex); // ignored } } if (null != inputStream) { InputStream temp = inputStream; inputStream = null; try { temp.close(); } catch (Exception ex) { LOG.debug("Exception caught when closing input", ex); // ignored } } if (null != socket) { Socket temp = socket; socket = null; try { temp.close(); } catch (Exception ex) { LOG.debug("Exception caught when closing socket", ex); // ignored } } isOpen = false; used = false; tunnelEstablished = false; usingSecureSocket = false; } /** * Throws an {@link IllegalStateException} if the connection is already open. * * @throws IllegalStateException if connected */ protected void assertNotOpen() throws IllegalStateException { if (isOpen) { throw new IllegalStateException("Connection is open"); } } /** * Throws an {@link IllegalStateException} if the connection is not open. * * @throws IllegalStateException if not connected */ protected void assertOpen() throws IllegalStateException { if (!isOpen) { throw new IllegalStateException("Connection is not open"); } } /** * Gets the socket's sendBufferSize. * * @return the size of the buffer for the socket OutputStream, -1 if the value * has not been set and the socket has not been opened * * @throws SocketException if an error occurs while getting the socket value * * @see Socket#getSendBufferSize() */ public int getSendBufferSize() throws SocketException { if (socket == null) { return -1; } else { return socket.getSendBufferSize(); } } /** * Sets the socket's sendBufferSize. * * @param sendBufferSize the size to set for the socket OutputStream * * @throws SocketException if an error occurs while setting the socket value * * @see Socket#setSendBufferSize(int) */ public void setSendBufferSize(int sendBufferSize) throws SocketException { this.sendBufferSize = sendBufferSize; if (socket != null) { socket.setSendBufferSize(sendBufferSize); } } // -- Timeout Exception /** * Signals that a timeout occured while opening the socket. */ public class ConnectionTimeoutException extends IOException { /** Create an instance */ public ConnectionTimeoutException() { } } /** * Helper class for wrapping socket based tasks. */ private abstract class SocketTask implements Runnable { /** The socket */ private Socket socket; /** The exception */ private IOException exception; /** * Set the socket. * @param newSocket The new socket. */ protected void setSocket(final Socket newSocket) { socket = newSocket; } /** * Return the socket. * @return Socket The socket. */ protected Socket getSocket() { return socket; } /** * Perform the logic. * @throws IOException If an IO problem occurs */ public abstract void doit() throws IOException; /** Execute the logic in this object and keep track of any exceptions. */ public void run() { try { doit(); } catch (IOException e) { exception = e; } } } /** * A wrapper for output streams that closes the connection and converts to recoverable * exceptions as appropriable when an IOException occurs. */ private class WrappedOutputStream extends OutputStream { /** the actual output stream */ private OutputStream out; /** * @param out the output stream to wrap */ public WrappedOutputStream(OutputStream out) { this.out = out; } /** * Closes the connection and conditionally converts exception to recoverable. * @param ioe the exception that occurred * @return the exception to be thrown */ private IOException handleException(IOException ioe) { // keep the original value of used, as it will be set to false by close(). boolean tempUsed = used; HttpConnection.this.close(); if (tempUsed) { LOG.debug( "Output exception occurred on a used connection. Will treat as recoverable.", ioe ); return new HttpRecoverableException(ioe.toString()); } else { return ioe; } } public void write(int b) throws IOException { try { out.write(b); } catch (IOException ioe) { throw handleException(ioe); } } public void flush() throws IOException { try { out.flush(); } catch (IOException ioe) { throw handleException(ioe); } } public void close() throws IOException { try { out.close(); } catch (IOException ioe) { throw handleException(ioe); } } public void write(byte[] b, int off, int len) throws IOException { try { out.write(b, off, len); } catch (IOException ioe) { throw handleException(ioe); } } public void write(byte[] b) throws IOException { try { out.write(b); } catch (IOException ioe) { throw handleException(ioe); } } } // ------------------------------------------------------- Static Variable /** "\r\n", as bytes. */ private static final byte[] CRLF = new byte[] {(byte) 13, (byte) 10}; /** Log object for this class. */ private static final Log LOG = LogFactory.getLog(HttpConnection.class); // ----------------------------------------------------- Instance Variables /** A flag indicating if this connection has been used since being opened */ private boolean used = false; /** My host. */ private String hostName = null; /** My virtual host. */ private String virtualName = null; /** My port. */ private int portNumber = -1; /** My proxy host. */ private String proxyHostName = null; /** My proxy port. */ private int proxyPortNumber = -1; /** My client Socket. */ private Socket socket = null; /** My InputStream. */ private PushbackInputStream inputStream = null; /** My OutputStream. */ private OutputStream outputStream = null; /** the size of the buffer to use for the socket OutputStream */ private int sendBufferSize = -1; /** An {@link InputStream} for the response to an individual request. */ private InputStream lastResponseInputStream = null; /** Whether or not the connection is connected. */ protected boolean isOpen = false; /** the protocol being used */ private Protocol protocolInUse; /** SO_TIMEOUT socket value */ private int soTimeout = 0; /** TCP_NODELAY socket value */ private boolean soNodelay = true; /** Whether or not the socket is a secure one. */ private boolean usingSecureSocket = false; /** Whether the connection is open via a secure tunnel or not */ private boolean tunnelEstablished = false; /** Whether or not isStale() is used by isOpen() */ private boolean staleCheckingEnabled = true; /** Timeout until connection established (Socket created). 0 means no timeout. */ private int connectTimeout = 0; /** the connection manager that created this connection or null */ private HttpConnectionManager httpConnectionManager; /** The local interface on which the connection is created, or null for the default */ private InetAddress localAddress; }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.