mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			898 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			898 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Java
		
	
	
	
/* HTTPConnection.java --
 | 
						|
   Copyright (C) 2004, 2005, 2006  Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
 | 
						|
package gnu.java.net.protocol.http;
 | 
						|
 | 
						|
import gnu.classpath.SystemProperties;
 | 
						|
 | 
						|
import gnu.java.lang.CPStringBuilder;
 | 
						|
import gnu.java.net.EmptyX509TrustManager;
 | 
						|
 | 
						|
import java.io.BufferedInputStream;
 | 
						|
import java.io.BufferedOutputStream;
 | 
						|
import java.io.IOException;
 | 
						|
import java.io.InputStream;
 | 
						|
import java.io.OutputStream;
 | 
						|
import java.net.InetSocketAddress;
 | 
						|
import java.net.Socket;
 | 
						|
import java.net.SocketException;
 | 
						|
import java.security.GeneralSecurityException;
 | 
						|
import java.util.ArrayList;
 | 
						|
import java.util.HashMap;
 | 
						|
import java.util.Iterator;
 | 
						|
import java.util.LinkedList;
 | 
						|
import java.util.List;
 | 
						|
import java.util.ListIterator;
 | 
						|
import java.util.Map;
 | 
						|
 | 
						|
import javax.net.ssl.HandshakeCompletedListener;
 | 
						|
import javax.net.ssl.SSLContext;
 | 
						|
import javax.net.ssl.SSLSocket;
 | 
						|
import javax.net.ssl.SSLSocketFactory;
 | 
						|
import javax.net.ssl.TrustManager;
 | 
						|
 | 
						|
/**
 | 
						|
 * A connection to an HTTP server.
 | 
						|
 *
 | 
						|
 * @author Chris Burdess (dog@gnu.org)
 | 
						|
 */
 | 
						|
public class HTTPConnection
 | 
						|
{
 | 
						|
 | 
						|
  /**
 | 
						|
   * The default HTTP port.
 | 
						|
   */
 | 
						|
  public static final int HTTP_PORT = 80;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The default HTTPS port.
 | 
						|
   */
 | 
						|
  public static final int HTTPS_PORT = 443;
 | 
						|
 | 
						|
  private static final String userAgent = SystemProperties.getProperty("http.agent");
 | 
						|
 | 
						|
  /**
 | 
						|
   * The host name of the server to connect to.
 | 
						|
   */
 | 
						|
  protected final String hostname;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The port to connect to.
 | 
						|
   */
 | 
						|
  protected final int port;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Whether the connection should use transport level security (HTTPS).
 | 
						|
   */
 | 
						|
  protected final boolean secure;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The connection timeout for connecting the underlying socket.
 | 
						|
   */
 | 
						|
  protected final int connectionTimeout;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The read timeout for reads on the underlying socket.
 | 
						|
   */
 | 
						|
  protected final int timeout;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The host name of the proxy to connect to.
 | 
						|
   */
 | 
						|
  protected String proxyHostname;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The port on the proxy to connect to.
 | 
						|
   */
 | 
						|
  protected int proxyPort;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The major version of HTTP supported by this client.
 | 
						|
   */
 | 
						|
  protected int majorVersion;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The minor version of HTTP supported by this client.
 | 
						|
   */
 | 
						|
  protected int minorVersion;
 | 
						|
 | 
						|
  private final List<HandshakeCompletedListener> handshakeCompletedListeners;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The socket this connection communicates on.
 | 
						|
   */
 | 
						|
  protected Socket socket;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The SSL socket factory to use.
 | 
						|
   */
 | 
						|
  private SSLSocketFactory sslSocketFactory;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The socket input stream.
 | 
						|
   */
 | 
						|
  protected InputStream in;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The socket output stream.
 | 
						|
   */
 | 
						|
  protected OutputStream out;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Nonce values seen by this connection.
 | 
						|
   */
 | 
						|
  private Map<String, Integer> nonceCounts;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The cookie manager for this connection.
 | 
						|
   */
 | 
						|
  protected CookieManager cookieManager;
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * The pool that this connection is a member of (if any).
 | 
						|
   */
 | 
						|
  private Pool pool;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new HTTP connection.
 | 
						|
   * @param hostname the name of the host to connect to
 | 
						|
   */
 | 
						|
  public HTTPConnection(String hostname)
 | 
						|
  {
 | 
						|
    this(hostname, HTTP_PORT, false, 0, 0);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new HTTP or HTTPS connection.
 | 
						|
   * @param hostname the name of the host to connect to
 | 
						|
   * @param secure whether to use a secure connection
 | 
						|
   */
 | 
						|
  public HTTPConnection(String hostname, boolean secure)
 | 
						|
  {
 | 
						|
    this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure, 0, 0);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new HTTP or HTTPS connection on the specified port.
 | 
						|
   * @param hostname the name of the host to connect to
 | 
						|
   * @param secure whether to use a secure connection
 | 
						|
   * @param connectionTimeout the connection timeout
 | 
						|
   * @param timeout the socket read timeout
 | 
						|
   */
 | 
						|
  public HTTPConnection(String hostname, boolean secure,
 | 
						|
                        int connectionTimeout, int timeout)
 | 
						|
  {
 | 
						|
    this(hostname, secure ? HTTPS_PORT : HTTP_PORT, secure,
 | 
						|
         connectionTimeout, timeout);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new HTTP connection on the specified port.
 | 
						|
   * @param hostname the name of the host to connect to
 | 
						|
   * @param port the port on the host to connect to
 | 
						|
   */
 | 
						|
  public HTTPConnection(String hostname, int port)
 | 
						|
  {
 | 
						|
    this(hostname, port, false, 0, 0);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new HTTP or HTTPS connection on the specified port.
 | 
						|
   * @param hostname the name of the host to connect to
 | 
						|
   * @param port the port on the host to connect to
 | 
						|
   * @param secure whether to use a secure connection
 | 
						|
   */
 | 
						|
  public HTTPConnection(String hostname, int port, boolean secure)
 | 
						|
  {
 | 
						|
    this(hostname, port, secure, 0, 0);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new HTTP or HTTPS connection on the specified port.
 | 
						|
   * @param hostname the name of the host to connect to
 | 
						|
   * @param port the port on the host to connect to
 | 
						|
   * @param secure whether to use a secure connection
 | 
						|
   * @param connectionTimeout the connection timeout
 | 
						|
   * @param timeout the socket read timeout
 | 
						|
   *
 | 
						|
   * @throws IllegalArgumentException if either connectionTimeout or
 | 
						|
   * timeout less than zero.
 | 
						|
   */
 | 
						|
  public HTTPConnection(String hostname, int port, boolean secure,
 | 
						|
                        int connectionTimeout, int timeout)
 | 
						|
  {
 | 
						|
    if (connectionTimeout < 0 || timeout < 0)
 | 
						|
      throw new IllegalArgumentException();
 | 
						|
 | 
						|
    this.hostname = hostname;
 | 
						|
    this.port = port;
 | 
						|
    this.secure = secure;
 | 
						|
    this.connectionTimeout = connectionTimeout;
 | 
						|
    this.timeout = timeout;
 | 
						|
    majorVersion = minorVersion = 1;
 | 
						|
    handshakeCompletedListeners
 | 
						|
      = new ArrayList<HandshakeCompletedListener>(2);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the name of the host to connect to.
 | 
						|
   */
 | 
						|
  public String getHostName()
 | 
						|
  {
 | 
						|
    return hostname;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the port on the host to connect to.
 | 
						|
   */
 | 
						|
  public int getPort()
 | 
						|
  {
 | 
						|
    return port;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates whether to use a secure connection or not.
 | 
						|
   */
 | 
						|
  public boolean isSecure()
 | 
						|
  {
 | 
						|
    return secure;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the HTTP version string supported by this connection.
 | 
						|
   * @see #majorVersion
 | 
						|
   * @see #minorVersion
 | 
						|
   */
 | 
						|
  public String getVersion()
 | 
						|
  {
 | 
						|
    return "HTTP/" + majorVersion + '.' + minorVersion;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the HTTP version supported by this connection.
 | 
						|
   * @param majorVersion the major version
 | 
						|
   * @param minorVersion the minor version
 | 
						|
   */
 | 
						|
  public void setVersion(int majorVersion, int minorVersion)
 | 
						|
  {
 | 
						|
    if (majorVersion != 1)
 | 
						|
      {
 | 
						|
        throw new IllegalArgumentException("major version not supported: " +
 | 
						|
                                           majorVersion);
 | 
						|
      }
 | 
						|
    if (minorVersion < 0 || minorVersion > 1)
 | 
						|
      {
 | 
						|
        throw new IllegalArgumentException("minor version not supported: " +
 | 
						|
                                           minorVersion);
 | 
						|
      }
 | 
						|
    this.majorVersion = majorVersion;
 | 
						|
    this.minorVersion = minorVersion;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Directs this connection to use the specified proxy.
 | 
						|
   * @param hostname the proxy host name
 | 
						|
   * @param port the port on the proxy to connect to
 | 
						|
   */
 | 
						|
  public void setProxy(String hostname, int port)
 | 
						|
  {
 | 
						|
    proxyHostname = hostname;
 | 
						|
    proxyPort = port;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates whether this connection is using an HTTP proxy.
 | 
						|
   */
 | 
						|
  public boolean isUsingProxy()
 | 
						|
  {
 | 
						|
    return (proxyHostname != null && proxyPort > 0);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the cookie manager to use for this connection.
 | 
						|
   * @param cookieManager the cookie manager
 | 
						|
   */
 | 
						|
  public void setCookieManager(CookieManager cookieManager)
 | 
						|
  {
 | 
						|
    this.cookieManager = cookieManager;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the cookie manager in use for this connection.
 | 
						|
   */
 | 
						|
  public CookieManager getCookieManager()
 | 
						|
  {
 | 
						|
    return cookieManager;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Manages a pool of HTTPConections.  The pool will have a maximum
 | 
						|
   * size determined by the value of the maxConn parameter passed to
 | 
						|
   * the {@link #get} method.  This value inevitably comes from the
 | 
						|
   * http.maxConnections system property.  If the
 | 
						|
   * classpath.net.http.keepAliveTTL system property is set, that will
 | 
						|
   * be the maximum time (in seconds) that an idle connection will be
 | 
						|
   * maintained.
 | 
						|
   */
 | 
						|
  static class Pool
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Singleton instance of the pool.
 | 
						|
     */
 | 
						|
    static Pool instance = new Pool();
 | 
						|
 | 
						|
    /**
 | 
						|
     * The pool
 | 
						|
     */
 | 
						|
    final LinkedList<HTTPConnection> connectionPool
 | 
						|
      = new LinkedList<HTTPConnection>();
 | 
						|
 | 
						|
    /**
 | 
						|
     * Maximum size of the pool.
 | 
						|
     */
 | 
						|
    int maxConnections;
 | 
						|
 | 
						|
    /**
 | 
						|
     * If greater than zero, the maximum time a connection will remain
 | 
						|
     * int the pool.
 | 
						|
     */
 | 
						|
    int connectionTTL;
 | 
						|
 | 
						|
    /**
 | 
						|
     * A thread that removes connections older than connectionTTL.
 | 
						|
     */
 | 
						|
    class Reaper
 | 
						|
      implements Runnable
 | 
						|
    {
 | 
						|
      public void run()
 | 
						|
      {
 | 
						|
        synchronized (Pool.this)
 | 
						|
          {
 | 
						|
            try
 | 
						|
              {
 | 
						|
                do
 | 
						|
                  {
 | 
						|
                    while (connectionPool.size() > 0)
 | 
						|
                      {
 | 
						|
                        long currentTime = System.currentTimeMillis();
 | 
						|
 | 
						|
                        HTTPConnection c =
 | 
						|
                          (HTTPConnection)connectionPool.getFirst();
 | 
						|
 | 
						|
                        long waitTime = c.timeLastUsed
 | 
						|
                          + connectionTTL - currentTime;
 | 
						|
 | 
						|
                        if (waitTime <= 0)
 | 
						|
                          removeOldest();
 | 
						|
                        else
 | 
						|
                          try
 | 
						|
                            {
 | 
						|
                              Pool.this.wait(waitTime);
 | 
						|
                            }
 | 
						|
                          catch (InterruptedException _)
 | 
						|
                            {
 | 
						|
                              // Ignore the interrupt.
 | 
						|
                            }
 | 
						|
                      }
 | 
						|
                    // After the pool is empty, wait TTL to see if it
 | 
						|
                    // is used again.  This is because in the
 | 
						|
                    // situation where a single thread is making HTTP
 | 
						|
                    // requests to the same server it can remove the
 | 
						|
                    // connection from the pool before the Reaper has
 | 
						|
                    // a chance to start.  This would cause the Reaper
 | 
						|
                    // to exit if it were not for this extra delay.
 | 
						|
                    // The result would be starting a Reaper thread
 | 
						|
                    // for each HTTP request.  With the delay we get
 | 
						|
                    // at most one Reaper created each TTL.
 | 
						|
                    try
 | 
						|
                      {
 | 
						|
                        Pool.this.wait(connectionTTL);
 | 
						|
                      }
 | 
						|
                    catch (InterruptedException _)
 | 
						|
                      {
 | 
						|
                        // Ignore the interrupt.
 | 
						|
                      }
 | 
						|
                  }
 | 
						|
                while (connectionPool.size() > 0);
 | 
						|
              }
 | 
						|
            finally
 | 
						|
              {
 | 
						|
                reaper = null;
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    Reaper reaper;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Private constructor to ensure singleton.
 | 
						|
     */
 | 
						|
    private Pool()
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Tests for a matching connection.
 | 
						|
     *
 | 
						|
     * @param c connection to match.
 | 
						|
     * @param h the host name.
 | 
						|
     * @param p the port.
 | 
						|
     * @param sec true if using https.
 | 
						|
     *
 | 
						|
     * @return true if c matches h, p, and sec.
 | 
						|
     */
 | 
						|
    private static boolean matches(HTTPConnection c,
 | 
						|
                                   String h, int p, boolean sec)
 | 
						|
    {
 | 
						|
      return h.equals(c.hostname) && (p == c.port) && (sec == c.secure);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get a pooled HTTPConnection.  If there is an existing idle
 | 
						|
     * connection to the requested server it is returned.  Otherwise a
 | 
						|
     * new connection is created.
 | 
						|
     *
 | 
						|
     * @param host the name of the host to connect to
 | 
						|
     * @param port the port on the host to connect to
 | 
						|
     * @param secure whether to use a secure connection
 | 
						|
     *
 | 
						|
     * @return the HTTPConnection.
 | 
						|
     */
 | 
						|
    synchronized HTTPConnection get(String host,
 | 
						|
                                    int port,
 | 
						|
                                    boolean secure,
 | 
						|
                                    int connectionTimeout, int timeout)
 | 
						|
    {
 | 
						|
      String ttl =
 | 
						|
        SystemProperties.getProperty("classpath.net.http.keepAliveTTL");
 | 
						|
      connectionTTL = 10000;
 | 
						|
      if (ttl != null && ttl.length() > 0)
 | 
						|
        try
 | 
						|
          {
 | 
						|
            int v = 1000 * Integer.parseInt(ttl);
 | 
						|
            if (v >= 0)
 | 
						|
              connectionTTL = v;
 | 
						|
          }
 | 
						|
        catch (NumberFormatException _)
 | 
						|
          {
 | 
						|
            // Ignore.
 | 
						|
          }
 | 
						|
 | 
						|
      String mc = SystemProperties.getProperty("http.maxConnections");
 | 
						|
      maxConnections = 5;
 | 
						|
      if (mc != null && mc.length() > 0)
 | 
						|
        try
 | 
						|
          {
 | 
						|
            int v = Integer.parseInt(mc);
 | 
						|
            if (v > 0)
 | 
						|
              maxConnections = v;
 | 
						|
          }
 | 
						|
        catch (NumberFormatException _)
 | 
						|
          {
 | 
						|
            // Ignore.
 | 
						|
          }
 | 
						|
 | 
						|
      HTTPConnection c = null;
 | 
						|
 | 
						|
      ListIterator it = connectionPool.listIterator(0);
 | 
						|
      while (it.hasNext())
 | 
						|
        {
 | 
						|
          HTTPConnection cc = (HTTPConnection)it.next();
 | 
						|
          if (matches(cc, host, port, secure))
 | 
						|
            {
 | 
						|
              c = cc;
 | 
						|
              it.remove();
 | 
						|
              // Update the timeout.
 | 
						|
              if (c.socket != null)
 | 
						|
                try
 | 
						|
                  {
 | 
						|
                    c.socket.setSoTimeout(timeout);
 | 
						|
                  }
 | 
						|
                catch (SocketException _)
 | 
						|
                  {
 | 
						|
                    // Ignore.
 | 
						|
                  }
 | 
						|
              break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
      if (c == null)
 | 
						|
        {
 | 
						|
          c = new HTTPConnection(host, port, secure,
 | 
						|
                                 connectionTimeout, timeout);
 | 
						|
          c.setPool(this);
 | 
						|
        }
 | 
						|
      return c;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Put an idle HTTPConnection back into the pool.  If this causes
 | 
						|
     * the pool to be come too large, the oldest connection is removed
 | 
						|
     * and closed.
 | 
						|
     *
 | 
						|
     */
 | 
						|
    synchronized void put(HTTPConnection c)
 | 
						|
    {
 | 
						|
      c.timeLastUsed = System.currentTimeMillis();
 | 
						|
      connectionPool.addLast(c);
 | 
						|
 | 
						|
      // maxConnections must always be >= 1
 | 
						|
      while (connectionPool.size() >= maxConnections)
 | 
						|
        removeOldest();
 | 
						|
 | 
						|
      if (connectionTTL > 0 && null == reaper) {
 | 
						|
        // If there is a connectionTTL, then the reaper has removed
 | 
						|
        // any stale connections, so we don't have to check for stale
 | 
						|
        // now.  We do have to start a reaper though, as there is not
 | 
						|
        // one running now.
 | 
						|
        reaper = new Reaper();
 | 
						|
        Thread t = new Thread(reaper, "HTTPConnection.Reaper");
 | 
						|
        t.setDaemon(true);
 | 
						|
        t.start();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Remove the oldest connection from the pool and close it.
 | 
						|
     */
 | 
						|
    void removeOldest()
 | 
						|
    {
 | 
						|
      HTTPConnection cx = (HTTPConnection)connectionPool.removeFirst();
 | 
						|
      try
 | 
						|
        {
 | 
						|
          cx.closeConnection();
 | 
						|
        }
 | 
						|
      catch (IOException ioe)
 | 
						|
        {
 | 
						|
          // Ignore it.  We are just cleaning up.
 | 
						|
        }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * The number of times this HTTPConnection has be used via keep-alive.
 | 
						|
   */
 | 
						|
  int useCount;
 | 
						|
 | 
						|
  /**
 | 
						|
   * If this HTTPConnection is in the pool, the time it was put there.
 | 
						|
   */
 | 
						|
  long timeLastUsed;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Set the connection pool that this HTTPConnection is a member of.
 | 
						|
   * If left unset or set to null, it will not be a member of any pool
 | 
						|
   * and will not be a candidate for reuse.
 | 
						|
   *
 | 
						|
   * @param p the pool.
 | 
						|
   */
 | 
						|
  void setPool(Pool p)
 | 
						|
  {
 | 
						|
    pool = p;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Signal that this HTTPConnection is no longer needed and can be
 | 
						|
   * returned to the connection pool.
 | 
						|
   *
 | 
						|
   */
 | 
						|
  void release()
 | 
						|
  {
 | 
						|
    if (pool != null)
 | 
						|
      {
 | 
						|
        useCount++;
 | 
						|
        pool.put(this);
 | 
						|
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        // If there is no pool, just close.
 | 
						|
        try
 | 
						|
          {
 | 
						|
            closeConnection();
 | 
						|
          }
 | 
						|
        catch (IOException ioe)
 | 
						|
          {
 | 
						|
            // Ignore it.  We are just cleaning up.
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new request using this connection.
 | 
						|
   * @param method the HTTP method to invoke
 | 
						|
   * @param path the URI-escaped RFC2396 <code>abs_path</code> with
 | 
						|
   * optional query part
 | 
						|
   */
 | 
						|
  public Request newRequest(String method, String path)
 | 
						|
  {
 | 
						|
    if (method == null || method.length() == 0)
 | 
						|
      {
 | 
						|
        throw new IllegalArgumentException("method must have non-zero length");
 | 
						|
      }
 | 
						|
    if (path == null || path.length() == 0)
 | 
						|
      {
 | 
						|
        path = "/";
 | 
						|
      }
 | 
						|
    Request ret = new Request(this, method, path);
 | 
						|
    if ((secure && port != HTTPS_PORT) ||
 | 
						|
        (!secure && port != HTTP_PORT))
 | 
						|
      {
 | 
						|
        ret.setHeader("Host", hostname + ":" + port);
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        ret.setHeader("Host", hostname);
 | 
						|
      }
 | 
						|
    ret.setHeader("User-Agent", userAgent);
 | 
						|
    ret.setHeader("Connection", "keep-alive");
 | 
						|
    ret.setHeader("Accept-Encoding",
 | 
						|
                  "chunked;q=1.0, gzip;q=0.9, deflate;q=0.8, " +
 | 
						|
                  "identity;q=0.6, *;q=0");
 | 
						|
    if (cookieManager != null)
 | 
						|
      {
 | 
						|
        Cookie[] cookies = cookieManager.getCookies(hostname, secure, path);
 | 
						|
        if (cookies != null && cookies.length > 0)
 | 
						|
          {
 | 
						|
            CPStringBuilder buf = new CPStringBuilder();
 | 
						|
            buf.append("$Version=1");
 | 
						|
            for (int i = 0; i < cookies.length; i++)
 | 
						|
              {
 | 
						|
                buf.append(',');
 | 
						|
                buf.append(' ');
 | 
						|
                buf.append(cookies[i].toString());
 | 
						|
              }
 | 
						|
            ret.setHeader("Cookie", buf.toString());
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Closes this connection.
 | 
						|
   */
 | 
						|
  public void close()
 | 
						|
    throws IOException
 | 
						|
  {
 | 
						|
    closeConnection();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Retrieves the socket associated with this connection.
 | 
						|
   * This creates the socket if necessary.
 | 
						|
   */
 | 
						|
  protected synchronized Socket getSocket()
 | 
						|
    throws IOException
 | 
						|
  {
 | 
						|
    if (socket == null)
 | 
						|
      {
 | 
						|
        String connectHostname = hostname;
 | 
						|
        int connectPort = port;
 | 
						|
        if (isUsingProxy())
 | 
						|
          {
 | 
						|
            connectHostname = proxyHostname;
 | 
						|
            connectPort = proxyPort;
 | 
						|
          }
 | 
						|
        socket = new Socket();
 | 
						|
        InetSocketAddress address =
 | 
						|
          new InetSocketAddress(connectHostname, connectPort);
 | 
						|
        if (connectionTimeout > 0)
 | 
						|
          {
 | 
						|
            socket.connect(address, connectionTimeout);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            socket.connect(address);
 | 
						|
          }
 | 
						|
        if (timeout > 0)
 | 
						|
          {
 | 
						|
            socket.setSoTimeout(timeout);
 | 
						|
          }
 | 
						|
        if (secure)
 | 
						|
          {
 | 
						|
            try
 | 
						|
              {
 | 
						|
                SSLSocketFactory factory = getSSLSocketFactory();
 | 
						|
                SSLSocket ss =
 | 
						|
                  (SSLSocket) factory.createSocket(socket, connectHostname,
 | 
						|
                                                   connectPort, true);
 | 
						|
                String[] protocols = { "TLSv1", "SSLv3" };
 | 
						|
                ss.setEnabledProtocols(protocols);
 | 
						|
                ss.setUseClientMode(true);
 | 
						|
                synchronized (handshakeCompletedListeners)
 | 
						|
                  {
 | 
						|
                    if (!handshakeCompletedListeners.isEmpty())
 | 
						|
                      {
 | 
						|
                        for (Iterator i =
 | 
						|
                             handshakeCompletedListeners.iterator();
 | 
						|
                             i.hasNext(); )
 | 
						|
                          {
 | 
						|
                            HandshakeCompletedListener l =
 | 
						|
                              (HandshakeCompletedListener) i.next();
 | 
						|
                            ss.addHandshakeCompletedListener(l);
 | 
						|
                          }
 | 
						|
                      }
 | 
						|
                  }
 | 
						|
                ss.startHandshake();
 | 
						|
                socket = ss;
 | 
						|
              }
 | 
						|
            catch (GeneralSecurityException e)
 | 
						|
              {
 | 
						|
                throw new IOException(e.getMessage());
 | 
						|
              }
 | 
						|
          }
 | 
						|
        in = socket.getInputStream();
 | 
						|
        in = new BufferedInputStream(in);
 | 
						|
        out = socket.getOutputStream();
 | 
						|
        out = new BufferedOutputStream(out);
 | 
						|
      }
 | 
						|
    return socket;
 | 
						|
  }
 | 
						|
 | 
						|
  SSLSocketFactory getSSLSocketFactory()
 | 
						|
    throws GeneralSecurityException
 | 
						|
  {
 | 
						|
    if (sslSocketFactory == null)
 | 
						|
      {
 | 
						|
        TrustManager tm = new EmptyX509TrustManager();
 | 
						|
        SSLContext context = SSLContext.getInstance("SSL");
 | 
						|
        TrustManager[] trust = new TrustManager[] { tm };
 | 
						|
        context.init(null, trust, null);
 | 
						|
        sslSocketFactory = context.getSocketFactory();
 | 
						|
      }
 | 
						|
    return sslSocketFactory;
 | 
						|
  }
 | 
						|
 | 
						|
  void setSSLSocketFactory(SSLSocketFactory factory)
 | 
						|
  {
 | 
						|
    sslSocketFactory = factory;
 | 
						|
  }
 | 
						|
 | 
						|
  protected synchronized InputStream getInputStream()
 | 
						|
    throws IOException
 | 
						|
  {
 | 
						|
    if (socket == null)
 | 
						|
      {
 | 
						|
        getSocket();
 | 
						|
      }
 | 
						|
    return in;
 | 
						|
  }
 | 
						|
 | 
						|
  protected synchronized OutputStream getOutputStream()
 | 
						|
    throws IOException
 | 
						|
  {
 | 
						|
    if (socket == null)
 | 
						|
      {
 | 
						|
        getSocket();
 | 
						|
      }
 | 
						|
    return out;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Closes the underlying socket, if any.
 | 
						|
   */
 | 
						|
  protected synchronized void closeConnection()
 | 
						|
    throws IOException
 | 
						|
  {
 | 
						|
    if (socket != null)
 | 
						|
      {
 | 
						|
        try
 | 
						|
          {
 | 
						|
            socket.close();
 | 
						|
          }
 | 
						|
        finally
 | 
						|
          {
 | 
						|
            socket = null;
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns a URI representing the connection.
 | 
						|
   * This does not include any request path component.
 | 
						|
   */
 | 
						|
  protected String getURI()
 | 
						|
  {
 | 
						|
    CPStringBuilder buf = new CPStringBuilder();
 | 
						|
    buf.append(secure ? "https://" : "http://");
 | 
						|
    buf.append(hostname);
 | 
						|
    if (secure)
 | 
						|
      {
 | 
						|
        if (port != HTTPConnection.HTTPS_PORT)
 | 
						|
          {
 | 
						|
            buf.append(':');
 | 
						|
            buf.append(port);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        if (port != HTTPConnection.HTTP_PORT)
 | 
						|
          {
 | 
						|
            buf.append(':');
 | 
						|
            buf.append(port);
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return buf.toString();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Get the number of times the specified nonce has been seen by this
 | 
						|
   * connection.
 | 
						|
   */
 | 
						|
  int getNonceCount(String nonce)
 | 
						|
  {
 | 
						|
    if (nonceCounts == null)
 | 
						|
      {
 | 
						|
        return 0;
 | 
						|
      }
 | 
						|
    return nonceCounts.get(nonce).intValue();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Increment the number of times the specified nonce has been seen.
 | 
						|
   */
 | 
						|
  void incrementNonce(String nonce)
 | 
						|
  {
 | 
						|
    int current = getNonceCount(nonce);
 | 
						|
    if (nonceCounts == null)
 | 
						|
      {
 | 
						|
        nonceCounts = new HashMap<String, Integer>();
 | 
						|
      }
 | 
						|
    nonceCounts.put(nonce, new Integer(current + 1));
 | 
						|
  }
 | 
						|
 | 
						|
  // -- Events --
 | 
						|
 | 
						|
  void addHandshakeCompletedListener(HandshakeCompletedListener l)
 | 
						|
  {
 | 
						|
    synchronized (handshakeCompletedListeners)
 | 
						|
      {
 | 
						|
        handshakeCompletedListeners.add(l);
 | 
						|
      }
 | 
						|
  }
 | 
						|
  void removeHandshakeCompletedListener(HandshakeCompletedListener l)
 | 
						|
  {
 | 
						|
    synchronized (handshakeCompletedListeners)
 | 
						|
      {
 | 
						|
        handshakeCompletedListeners.remove(l);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
}
 |