You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by js...@apache.org on 2002/12/03 06:46:16 UTC

cvs commit: jakarta-commons/httpclient/src/test/org/apache/commons/httpclient TestLocalHostBase.java TestHttpClientLocalHost.java TestHttpConnection.java TestHttpConnectionManager.java TestLocalHost.java

jsdever     2002/12/02 21:46:16

  Modified:    httpclient/src/java/org/apache/commons/httpclient
                        HttpClient.java HttpConnection.java
                        HttpConnectionManager.java HttpMethod.java
                        HttpMethodBase.java HttpState.java
                        HttpUrlMethod.java
               httpclient/src/test/org/apache/commons/httpclient
                        TestHttpClientLocalHost.java
                        TestHttpConnection.java
                        TestHttpConnectionManager.java TestLocalHost.java
  Added:       httpclient/src/java/org/apache/commons/httpclient
                        MultiThreadedHttpConnectionManager.java
                        SimpleHttpConnectionManager.java
               httpclient/src/test/org/apache/commons/httpclient
                        TestLocalHostBase.java
  Removed:     httpclient/src/java/org/apache/commons/httpclient
                        HttpMultiClient.java HttpSharedState.java
  Log:
  Massive merger of HttpClient and HttpMultiClient.
  
  Contributed by: Michael Becke
  
  Revision  Changes    Path
  1.61      +264 -138  jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpClient.java
  
  Index: HttpClient.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpClient.java,v
  retrieving revision 1.60
  retrieving revision 1.61
  diff -u -r1.60 -r1.61
  --- HttpClient.java	12 Nov 2002 09:58:22 -0000	1.60
  +++ HttpClient.java	3 Dec 2002 05:46:15 -0000	1.61
  @@ -62,13 +62,14 @@
   
   package org.apache.commons.httpclient;
   
  -import org.apache.commons.logging.Log;
  -import org.apache.commons.logging.LogFactory;
  -
   import java.io.IOException;
   import java.net.URL;
  +
   import javax.net.ssl.SSLSocketFactory;
   
  +import org.apache.commons.logging.Log;
  +import org.apache.commons.logging.LogFactory;
  +
   
   /**
    * <p>
  @@ -81,6 +82,8 @@
    * @author Sean C. Sullivan
    * @author <a href="mailto:dion@apache.org">dIon Gillard</a>
    * @author Ortwin Gl�ck
  + * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
  + * 
    * @version $Revision$ $Date$
    */
   
  @@ -95,17 +98,33 @@
       // ----------------------------------------------------------- Constructors
   
       /**
  -     * Constructor.
  +     * Creates an HttpClient using <code>SimpleHttpConnectionManager</code>.
  +     * 
  +     * @see SimpleHttpConnectionManager
        */
       public HttpClient() {
  +        this( new SimpleHttpConnectionManager() );
       }
   
  -    // ----------------------------------------------------- Instance Variables
  -
       /**
  -     * My {@link HttpConnection connection}.
  +     * Creates an HttpClient with a user specified connection manager.
  +     * 
  +     * @since 2.0
        */
  -    private HttpConnection connection = null;
  +    public HttpClient( HttpConnectionManager httpConnectionManager ) {
  +
  +        if ( httpConnectionManager == null ) {
  +            throw new IllegalArgumentException("httpConnectionManager cannot be null");  
  +        }
  +
  +        this.state = new HttpState();
  +        this.state.setHttpConnectionManager( httpConnectionManager );
  +
  +        this.hostConfiguration = new HostConfiguration();
  +
  +    }
  +    
  +    // ----------------------------------------------------- Instance Variables
   
       /**
        * My {@link HttpState state}.
  @@ -114,10 +133,17 @@
   
       private SSLSocketFactory sslSocketFactory = null;
   
  +    /** the timout when waiting for a connection from the connectionManager */
  +    private long httpConnectionTimeout = 0;
  +
       private int timeoutInMilliseconds = 0;
   
       private int connectionTimeout = 0;
   
  +    private HostConfiguration hostConfiguration;
  +    
  +    private boolean strictMode = false;
  +
       // ------------------------------------------------------------- Properties
   
       /**
  @@ -126,10 +152,7 @@
        * @see #setState(HttpState)
        * @return the shared client state
        */
  -    public HttpState getState() {
  -        if (null == state) {
  -            state = new HttpState();
  -        }
  +    public synchronized HttpState getState() {
           return state;
       }
   
  @@ -139,16 +162,38 @@
        * @see #getState()
        * @param state the new state for the client
        */
  -    public void setState(HttpState state) {
  +    public synchronized void setState(HttpState state) {
           this.state = state;
       }
   
       /**
  +     *
  +     * @param strictMode <code>true</code> if strict mode should be used
  +     *
  +     * @see #isStrictMode()
  +     *
  +     */
  +    public synchronized void setStrictMode(boolean strictMode) {
  +        this.strictMode = strictMode;
  +    }
  +
  +    /**
  +     *
  +     * @return <code>true</code> if strict mode being used
  +     *
  +     * @see #setStrictMode(boolean)
  +     *
  +     */
  +    public synchronized boolean isStrictMode() {
  +        return strictMode;
  +    }
  +    
  +    /**
        * Specifies an alternative factory for SSL sockets.
        * @see HttpConnection#setSSLSocketFactory(SSLSocketFactory) HttpConnection.setSSLSocketFactory
        * @param sslSocketFactory a living instance of the alternative SSLSocketFactory
        */
  -    public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
  +    public synchronized void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {
           this.sslSocketFactory = sslSocketFactory;
       }
   
  @@ -160,34 +205,44 @@
        * @param newTimeoutInMilliseconds Timeout in milliseconds
        *
        */
  -    public void setTimeout(int newTimeoutInMilliseconds) {
  +    public synchronized void setTimeout(int newTimeoutInMilliseconds) {
           this.timeoutInMilliseconds = newTimeoutInMilliseconds;
       }
   
       /**
  +     * Sets the timeout used when retrieving an HttpConnection from the
  +     * HttpConnectionManager.
  +     * 
  +     * @param timeout the timeout in milliseconds
  +     * 
  +     * @see HttpConnectionManager#getConnection(String, long)
  +     */
  +    public synchronized void setHttpConnectionFactoryTimeout( long timeout ) {
  +        this.httpConnectionTimeout = timeout;
  +    }
  +
  +    /**
        * Sets the timeout until a connection is etablished. A value of 0 means
        * the timeout is not used. The default value is 0.
        * @see HttpConnection#setConnectionTimeout(int)
        * @param newTimeoutInMilliseconds Timeout in milliseconds.
        */
  -    public void setConnectionTimeout(int newTimeoutInMilliseconds) {
  +    public synchronized void setConnectionTimeout(int newTimeoutInMilliseconds) {
          this.connectionTimeout = newTimeoutInMilliseconds;
       }
   
       // --------------------------------------------------------- Public Methods
   
       /**
  -     * Start an HTTP session with the server at the given
  -     * <i>host</i> and <i>port</i>.
  +     * @deprecated use hostConfiguration
  +     * 
  +     * Sets the host, port and protocol(http) to be used when executing a
  +     * method.
  +     * 
        * @param host the host to connect to
        * @param port the port to connect to
        *
  -     * @see #startSession(String, int, boolean)
  -     * @see #startSession(String, int, Credentials)
  -     * @see #startSession(String, int, Credentials, boolean)
  -     * @see #startSession(String, int, String, int)
  -     * @see #endSession()
  -     *
  +     * @see #getHostConfiguration()
        */
       public void startSession(String host, int port) {
           log.trace("enter HttpClient.startSession(String, int)");
  @@ -195,19 +250,16 @@
       }
   
       /**
  -     * Start an HTTP or HTTPS session with the server at the given
  -     * <i>host</i> and <i>port</i>.
  +     * @deprecated use hostConfiguration
  +     * 
  +     * Sets the host, port and protocol to be used when executing a method.
  +     * 
        * @param host the host to connect to
        * @param port the port to connect to
        * @param https when <code>true</code>, create an HTTPS session
        *
  -     * @see #startSession(String, int)
  -     * @see #startSession(String, int, Credentials)
  -     * @see #startSession(String, int, Credentials, boolean)
  -     * @see #startSession(String, int, String, int)
  -     * @see #endSession()
  -     *
  -     */
  +     * @see #getHostConfiguration()
  +     */    
       public void startSession(String host, int port, boolean https) {
           log.trace("enter HttpClient.startSession(String, int, boolean)");
   
  @@ -215,48 +267,47 @@
               log.debug("HttpClient.startSession(String,int,boolean): Host:"
                   + host + " Port:" + port + " HTTPS:" + https);
           }
  -        connection = new HttpConnection(host, port, https);
  +        
  +        this.hostConfiguration.setHost( 
  +            host, 
  +            port, 
  +            https ? "https" : "http"
  +        );
       }
   
       /**
  -     * Start an HTTP session with the server at the given
  -     * <i>host</i> and <i>port</i> using the given default
  -     * default credentials.
  +     * @deprecated use hostConfiguration and httpState
  +     * 
  +     * Sets the host, port, protocol(http) and credentials to be used when
  +     * executing a method.
        *
        * @param host the host to connect to
        * @param port the port to connect to
        * @param creds the default credentials to use
        *
  -     * @see #startSession(String, int, boolean)
  -     * @see #startSession(String, int)
  +     * @see #getHostConfiguration()
  +     * @see #getState()
        * @see #startSession(String, int, Credentials, boolean)
  -     * @see #startSession(String, int, String, int)
  -     * @see #endSession()
  -     *
        */
       public void startSession(String host, int port, Credentials creds) {
           log.trace("enter HttpClient.startSession(String, int, Credentials)");
           startSession(host, port, creds, false);
       }
   
  -
       /**
  -     * Start an HTTP or HTTPS session with the server at the given
  -     * <i>host</i> and <i>port</i> using the given default
  -     * default credentials.
  +     * @deprecated use hostConfiguration and httpState
  +     *
  +     * Sets the host, port, protocol and credentials to be used when executing a
  +     * method.
        *
        * @param host the host to connect to
        * @param port the port to connect to
        * @param creds the default credentials to use
        * @param https when <code>true</code>, create an HTTPS session
        *
  -     * @see #startSession(String, int, boolean)
  -     * @see #startSession(String, int)
  -     * @see #startSession(String, int, Credentials)
  -     * @see #startSession(String, int, String, int)
  -     * @see #endSession()
  -     *
  -     */
  +     * @see #getHostConfiguration()
  +     * @see #getState()
  +	 */
       public void startSession(String host, int port, Credentials creds, boolean https) {
           log.trace("enter HttpClient.startSession(String, int, Credentials, boolean)");
   
  @@ -268,22 +319,30 @@
                   + " HTTPS:" + https);
           }
           getState().setCredentials(null, creds);
  -        connection = new HttpConnection(host, port, https);
  +        this.hostConfiguration.setHost(
  +            host,
  +            port,
  +            https ? "https" : "http"
  +        );
       }
   
  -
       /**
  -     * Start an HTTP or HTTPS session with the server specified by the scheme,
  -     * userinfo, host and port of the given <i>uri</i>.
  +     * @deprecated use hostConfiguration and httpState
  +     *
  +     * Sets the host, port, protocol and credentials to be used when executing a
  +     * method using the server specified by the scheme, userinfo, host and port
  +     * of the given <i>uri</i>.
        * <p>
        * Note that the path component is not utilized.
        * <p>
        * @param uri an <code>HttpURL</code> or <code>HttpsURL</code> instance; the
        * {@link URI URI} from which the scheme, userinfo, host and port of the
        * session are determined
  +     *
        * @exception IllegalStateException not enough information to process
  -     * @see #startSession
  -     * @see #endSession
  +     * 
  +     * @see #getHostConfiguration()
  +     * @see #getState()
        */
       public void startSession(URI uri) throws URIException {
           log.trace("enter HttpClient.startSession(URI)");
  @@ -315,29 +374,27 @@
               throw new IllegalStateException
                   ("HttpURL or HttpsURL instance required");
           }
  -        connection = new HttpConnection(host, port, secure);
  +        this.hostConfiguration.setHost(
  +            host,
  +            port,
  +            secure ? "https" : "http"
  +        );
       }
   
  -
       /**
  -     * Start an HTTP or HTTPS session with the server specified
  -     * by the protocol, host and port of the given
  -     * <i>url</i>.
  +     * @deprecated use hostConfiguration
  +     *
  +     * Sets the host, port and protocol to be used when executing a method.
        * <p>
        * Note that everything but the protocol, host and port of the
        * given <i>url</i> is ignored.
        * </p>
  -     * @param url the {@link URL URL} from which the protocol, host,
  -     *            and port of the session are determined
  -     *
  -     * @see #startSession(String, int, boolean)
  -     * @see #startSession(String, int)
  -     * @see #startSession(String, int, Credentials)
  -     * @see #startSession(String, int, Credentials, boolean)
  -     * @see #startSession(java.net.URL, Credentials)
  -     * @see #startSession(String, int, String, int)
  -     * @see #endSession()
  +     * @param url the {@link URL URL} from which the protocol, host, and port of
  +     * the session are determined
  +     * 
  +     * @exception IllegalArgumentException if the protocol is not http or https
        *
  +     * @see #getHostConfiguration()
        */
       public void startSession(URL url) {
           log.trace("enter HttpClient.startSession(String, int, Credentials, boolean)");
  @@ -355,25 +412,22 @@
       }
   
       /**
  -     * Start an HTTP or HTTPS session with the server specified
  -     * by the protocol, host and port of the given
  -     * <i>url</i>, using the given credentials by default.
  +     * @deprecated use hostConfiguration and httpState
  +     *
  +     * Sets the host, port, protocol and credentials to be used when executing a
  +     * method.
        * <p>
        * Note that everything but the protocol, host and port of the
        * given <i>url</i> is ignored.
        * </p>
  +     * @param url the {@link URL URL} from which the protocol, host, and port of
  +     * the session are determined
        * @param creds the default credentials to use
  -     * @param url the {@link URL URL} from which the protocol, host,
  -     *            and port of the session are determined
  -     *
  -     * @see #startSession(java.net.URL)
  -     * @see #startSession(String, int, boolean)
  -     * @see #startSession(String, int)
  -     * @see #startSession(String, int, Credentials)
  -     * @see #startSession(String, int, Credentials, boolean)
  -     * @see #startSession(String, int, String, int)
  -     * @see #endSession()
  +     *  
  +     * @exception IllegalArgumentException if the protocol is not http or https
        *
  +     * @see #getHostConfiguration()   
  +     * @see #getState()
        */
       public void startSession(URL url, Credentials creds) {
           log.trace("enter HttpClient.startSession(URL, Credentials)");
  @@ -382,100 +436,151 @@
       }
   
       /**
  -     * Start an HTTP session with the server specified
  -     * by the given <i>host</i> and <i>port</i>
  -     * via the given <i>proxyhost</i> and <i>proxyport</i>.
  +     * @deprecated use hostConfiguration
  +     * 
  +     * Sets the host, port, protocol(http) and proxy to be used when executing a
  +     * method.
  +     * 
        * @param host the host to connect to
        * @param port the port to connect to
        * @param proxyhost the proxy host to connect via
        * @param proxyport the proxy port to connect via
        *
  -     * @see #endSession()
  -     *
  +     * @see #getHostConfiguration()
        */
       public void startSession(String host, int port, String proxyhost, int proxyport) {
           log.trace("enter HttpClient.startSession(String, int, String, int)");
           startSession(host, port, proxyhost, proxyport, false);
       }
   
  -
       /**
  -     * Start an HTTP session with the server specified
  -     * by the given <i>host</i> and <i>port</i>
  -     * via the given <i>proxyhost</i> and <i>proxyport</i>.
  -     *
  +     * @deprecated use hostConfiguration
  +     * 
  +     * Sets the host, port, protocol and proxy to be used when executing a
  +     * method.
  +     * 
        * @param host the host to connect to
        * @param port the port to connect to
        * @param proxyhost the proxy host to connect via
        * @param proxyport the proxy port to connect via
        * @param secure whether or not to connect using HTTPS
  -     *
  -     * @see #endSession()
  -     *
  +     * 
  +     * @see #getHostConfiguration()
        */
       public void startSession(String host, int port, String proxyhost, int proxyport, boolean secure) {
           log.trace("enter HttpClient.startSession(String, int, String, int, boolean)");
  -        connection = new HttpConnection(proxyhost, proxyport, host, port, secure);
  +        this.hostConfiguration.setHost(
  +            host,
  +            port,
  +            secure ? "https" : "http"
  +        );
  +        this.hostConfiguration.setProxy( proxyhost, proxyport );
       }
   
       /**
  -     * Execute the given {@link HttpMethod} using my current
  -     * {@link HttpConnection connection} and {@link HttpState}.
  +     * Executes the given method.
        *
  -     * @param method the {@link HttpMethod} to execute. This parameter must be non-null.
  +     * @param method the {@link HttpMethod} to execute.
        * @return the method's response code
        *
        * @throws java.io.IOException if an I/O error occurs
        * @throws HttpException if a protocol exception occurs
  -     * @throws IllegalStateException if the session has not been started
  -     *
        */
  -    public synchronized int executeMethod(HttpMethod method)
  -        throws IOException, HttpException, IllegalStateException  {
  +    public int executeMethod(HttpMethod method)
  +        throws IOException, HttpException  {
  +            
           log.trace("enter HttpClient.executeMethod(HttpMethod)");
  +        return executeMethod(getHostConfiguration(), method);
  +        
  +    }
  +    
  +    /**
  +     * Executes the given method.
  +     *
  +     * @param method the {@link HttpMethod} to execute.
  +     * @return the method's response code
  +     *
  +     * @throws java.io.IOException if an I/O error occurs
  +     * @throws HttpException if a protocol exception occurs
  +     * 
  +     * @since 2.0
  +     */
  +    public int executeMethod(HostConfiguration hostConfiguration, HttpMethod method)
  +        throws IOException, HttpException  {
  +        log.trace("enter HttpClient.executeMethod(HostConfiguration,HttpMethod)");
   
           if (null == method) {
               throw new NullPointerException("HttpMethod parameter");
           }
   
  -        if (null == connection) {
  -            throw new IllegalStateException(
  -                "The startSession method must be called before executeMethod");
  +        int soTimeout = 0;
  +        boolean strictMode = false;
  +        SSLSocketFactory socketFactory = null;
  +        int connectionTimeout = 0;
  +        long httpConnectionTimeout = 0;
  +        HttpState state = null;
  +        HostConfiguration defaultHostConfiguration = null;
  +
  +        /* access all synchronized data in a single block, this will keeps us
  +         * from accessing data asynchronously as well having to regain the lock
  +         * for each item.
  +         */
  +        synchronized ( this ) {
  +            soTimeout = this.timeoutInMilliseconds;
  +            strictMode = this.strictMode;
  +            socketFactory = this.sslSocketFactory;
  +            connectionTimeout = this.connectionTimeout;
  +            httpConnectionTimeout = this.httpConnectionTimeout;
  +            state = getState();
  +            defaultHostConfiguration = getHostConfiguration();
  +        }
  +
  +        HostConfiguration methodConfiguration = new HostConfiguration(hostConfiguration);
  +        
  +        if ( hostConfiguration != defaultHostConfiguration ) {
  +            // we may need to appy some defaults
  +            if ( !methodConfiguration.isHostSet() ) {
  +                methodConfiguration.setHost(
  +                    defaultHostConfiguration.getHost(),
  +                    defaultHostConfiguration.getPort(),
  +                    defaultHostConfiguration.getProtocol()
  +                );
  +            }
  +            if ( !methodConfiguration.isProxySet() && defaultHostConfiguration.isProxySet() ) {
  +                methodConfiguration.setProxy(
  +                    defaultHostConfiguration.getProxyHost(),
  +                    defaultHostConfiguration.getProxyPort() 
  +                );   
  +            }
           }
   
  +        HttpConnection connection = state.getHttpConnectionManager().getConnection(
  +            methodConfiguration,
  +            httpConnectionTimeout
  +        );
  +
  +        method.setStrictMode(strictMode);
  +        
           if (!connection.isOpen()) {
  -            connection.setSSLSocketFactory(sslSocketFactory);
  -            connection.setSoTimeout(timeoutInMilliseconds);
  +            connection.setSSLSocketFactory(socketFactory);
  +            connection.setSoTimeout(soTimeout);
               connection.setConnectionTimeout(connectionTimeout);
               connection.open();
               if (connection.isProxied() && connection.isSecure()) {
                   method = new ConnectMethod(method);
               }
           }
  -        return method.execute(getState(), connection);
  +        
  +        return method.execute(state, connection);
       }
   
       /**
  -     * End the current session, closing my the associated
  -     * {@link HttpConnection connection} if any.
  -     *
  -     * @see #startSession(String, int)
  -     * @see #startSession(String, int, boolean)
  -     * @see #startSession(String, int, Credentials)
  -     * @see #startSession(String, int, Credentials, boolean)
  -     * @see #startSession(java.net.URL)
  -     * @see #startSession(java.net.URL, Credentials)
  -     * @see #startSession(String, int, String, int)
  -     *
  -     * @throws java.io.IOException when i/o errors occur closing the connection
  -     *
  +     * @deprecated this method has no effect. HttpMethod.releaseConnection()
  +     * should be used to release resources after a HttpMethod has been executed.
  +     * 
  +     * @see HttpMethod#releaseConnection()
        */
       public void endSession() throws IOException {
  -        log.trace("enter HttpClient.endSession()");
  -        if (null != connection) {
  -            connection.close();
  -            connection = null;
  -        }
       }
   
       /**
  @@ -485,7 +590,7 @@
         * the session has not been started via startSession.
         */
        public String getHost() {
  -         return connection != null ? connection.getHost() : null;
  +         return hostConfiguration.getHost();
        }
   
        /**
  @@ -495,6 +600,27 @@
         * has not been started via startSession().
         */
        public int getPort() {
  -         return connection != null ? connection.getPort() : -1;
  +         return hostConfiguration.getPort();
        }
  +     
  +    /**
  +     * Returns the hostConfiguration.
  +     * @return HostConfiguration
  +     * 
  +     * @since 2.0
  +     */
  +    public synchronized HostConfiguration getHostConfiguration() {
  +        return hostConfiguration;
  +    }
  +
  +    /**
  +     * Sets the hostConfiguration.
  +     * @param hostConfiguration The hostConfiguration to set
  +     * 
  +     * @since 2.0
  +     */
  +    public synchronized void setHostConfiguration(HostConfiguration hostConfiguration) {
  +        this.hostConfiguration = hostConfiguration;
  +    }
  +
   }
  
  
  
  1.26      +31 -4     jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java
  
  Index: HttpConnection.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnection.java,v
  retrieving revision 1.25
  retrieving revision 1.26
  diff -u -r1.25 -r1.26
  --- HttpConnection.java	21 Nov 2002 09:54:52 -0000	1.25
  +++ HttpConnection.java	3 Dec 2002 05:46:15 -0000	1.26
  @@ -744,6 +744,29 @@
           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;
  +    }
  +    
  +    public void releaseConnection() {
  +        log.trace("enter HttpConnection.releaseConnection()");
  +        if ( httpConnectionManager != null ) {
  +            httpConnectionManager.releaseConnection( this );   
  +        }   
  +    }
  +
       // ------------------------------------------------------ Protected Methods
   
   
  @@ -847,6 +870,7 @@
               return sslSocketFactory.createSocket(host, port);
           }
       }
  +    
       // ------------------------------------------------------------- Attributes
   
       /** Log object for this class. */
  @@ -883,4 +907,7 @@
       private boolean _tunnelEstablished = false;
       /** Timeout until connection established (Socket created). 0 means no timeout. */
       private int connect_timeout = 0;
  +    /** the connection manager that created this connection or null */
  +    private HttpConnectionManager httpConnectionManager;
  +
   }
  
  
  
  1.12      +43 -346   jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnectionManager.java
  
  Index: HttpConnectionManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpConnectionManager.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- HttpConnectionManager.java	29 Sep 2002 21:59:47 -0000	1.11
  +++ HttpConnectionManager.java	3 Dec 2002 05:46:15 -0000	1.12
  @@ -28,7 +28,7 @@
    *    Alternately, this acknowlegement may appear in the software itself,
    *    if and wherever such third-party acknowlegements normally appear.
    *
  - * 4. The names "The Jakarta Project", "HttpClient", and "Apache Software
  + * 4. The names "The Jakarta Project", "Tomcat", 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.
  @@ -62,366 +62,63 @@
   
   package org.apache.commons.httpclient;
   
  -import org.apache.commons.logging.Log;
  -import org.apache.commons.logging.LogFactory;
  -
   import java.net.MalformedURLException;
  -import java.net.URL;
  -import java.util.HashMap;
  -import java.util.Map;
  -import java.util.LinkedList;
   
   /**
  - * Manages a set of HttpConnections for various host:ports.  This class is
  - * used by HttpMultiClient.  
  - *
  - * @author Marc A. Saegesser
  + * An interface for classes that manage HttpConnections.
  + * 
  + * @see org.apache.commons.httpclient.HttpConnection
  + * @see org.apache.commons.httpclient.HttpClient#HttpClient(HttpConnectionManager)
  + * 
  + * @since 2.0
    */
  -public class HttpConnectionManager
  -{
  -    // -------------------------------------------------------- Class Variables
  -    /** Log object for this class. */
  -    private static final Log log = LogFactory.getLog(HttpConnectionManager.class);
  -
  -    // ----------------------------------------------------- Instance Variables
  -    private Map mapHosts = new HashMap();
  -    private Map mapNumConnections = new HashMap();
  -    private int maxConnections = 2;   // Per RFC 2616 sec 8.1.4
  -    private String proxyHost = null;
  -    private int proxyPort = -1;
  -
  -    /**
  -     * No-args constructor
  -     */
  -    public HttpConnectionManager() {
  -    }
  -
  -    /**
  -     * Set the proxy host to use for all connections.
  -     * 
  -     * @param proxyHost - the proxy host name
  -     */
  -    public void setProxyHost(String proxyHost) {
  -        this.proxyHost = proxyHost;
  -    }
  -
  -    /**
  -     * Get the proxy host.
  -     *
  -     * @return the proxy host name
  -     */
  -    public String getProxyHost() {
  -        return proxyHost;
  -    }
  -
  -    /**
  -     * Set the proxy port to use for all connections.
  -     *
  -     * @param proxyPort - the proxy port number
  -     */
  -    public void setProxyPort(int proxyPort) {
  -        this.proxyPort = proxyPort;
  -    }
  -
  -    /**
  -     * Get the proxy port number.
  -     *
  -     * @return the proxy port number
  -     */
  -    public int getProxyPort() {
  -        return proxyPort;
  -    }
  -
  -    /**
  -     * Set the maximum number of connections allowed for a given host:port.
  -     * Per RFC 2616 section 8.1.4, this value defaults to 2.
  -     *
  -     * @param maxConnections - number of connections allowed for each host:port
  -     */
  -    public void setMaxConnectionsPerHost(int maxConnections) {
  -        this.maxConnections = maxConnections;
  -    }
  -
  -    /**
  -     * Get the maximum number of connections allowed for a given host:port.
  -     *
  -     * @return The maximum number of connections allowed for a given host:port.
  -     */
  -    public int getMaxConnectionsPerHost() {
  -        return maxConnections;
  -    }
  -
  -    /**
  -     * Get an HttpConnection for a given URL.  The URL must be fully
  -     * specified (i.e. contain a protocol and a host (and optional port number).
  -     * If the maximum number of connections for the host has been reached, this
  -     * method will block forever until a connection becomes available.
  -     *
  -     * @param sURL - a fully specified URL.
  -     * @return an HttpConnection for the given host:port
  -     * @exception java.net.MalformedURLException
  -     * @exception org.apache.commons.httpclient.HttpException -
  -     *            If no connection becomes available before the timeout expires
  -     */
  -    public HttpConnection getConnection(String sURL)
  -    throws HttpException, MalformedURLException {
  -        return getConnection(sURL, 0);
  -    }
  +public interface HttpConnectionManager {
   
       /**
  -     * Return the port provided if not -1 (default), return 443 if the
  -     * protocol is HTTPS, otherwise 80. 
  +     * Gets an HttpConnection for a given host configuration. If a connection is
  +     * not available this method will block until one is.
        *
  -     * This functionality is a URLUtil and may be better off in URIUtils
  +     * The connection manager should be registered with any HttpConnection that
  +     * is created.
        *
  -     * @param protocol the protocol to use to get the port for, e.g. http or
  -     *      https
  -     * @param port the port provided with the url (could be -1)
  -     * @return the port for the specified port and protocol.
  +     * @param hostConfiguration the host configuration to use to configure the
  +     * connection
  +     * 
  +     * @return an HttpConnection for the given configuration
  +     * 
  +     * @see HttpConnection#setHttpConnectionManager(HttpConnectionManager)
        */
  -    private static int getPort(String protocol, int port) {
  -        log.trace("HttpConnectionManager.getPort(String, port)");
  +    public HttpConnection getConnection(HostConfiguration hostConfiguration)
  +        throws MalformedURLException;
   
  -        // default to provided port
  -        int portForProtocol = port;
  -        if (portForProtocol == -1) {
  -            if (protocol.equalsIgnoreCase("HTTPS")) {
  -                portForProtocol = 443;
  -            } else {
  -                portForProtocol = 80;
  -            }
  -        }
  -        return portForProtocol;
  -    }
  -    
       /**
  -     * Get an HttpConnection for a given URL.  The URL must be fully
  -     * specified (i.e. contain a protocol and a host (and optional port number).
  -     * If the maximum number of connections for the host has been reached, this
  -     * method will block for <code>timeout</code> milliseconds or until a 
  -     * connection becomes available.  If no connection becomes available before
  -     * the timeout expires an HttpException exception will be thrown.
  +     * Gets an HttpConnection for a given host configuration. If a connection is
  +     * not available, this method will block for at most the specified number of
  +     * milliseconds or until a connection becomes available.
        *
  -     * @param sURL - a fully specified URL.
  -     * @param timeout - the time (in milliseconds) to wait for a connection 
  -     *      to become available
  -     * @return an HttpConnection for the given host:port
  -     * @exception java.net.MalformedURLException
  -     * @exception org.apache.commons.httpclient.HttpException -
  -     *            If no connection becomes available before the timeout expires
  -     */
  -    public HttpConnection getConnection(String sURL, long timeout) 
  -    throws HttpException, MalformedURLException {
  -        log.trace("enter HttpConnectionManager.getConnection(String, long)");
  -
  -        // FIXME: This method is too big
  -        if (sURL == null) {
  -            throw new MalformedURLException("URL is null");
  -        }
  -
  -        URL url = new URL(sURL);
  -        // Get the protocol and port (use default port if not specified)
  -        String protocol = url.getProtocol();
  -        String host = url.getHost();
  -        int port = HttpConnectionManager.getPort(protocol, url.getPort());
  -        final String hostAndPort = host + ":" + port;
  -
  -        if (log.isDebugEnabled()) {
  -            log.debug("HttpConnectionManager.getConnection:  key = "
  -                + hostAndPort);
  -        }
  -
  -        // Look for a list of connections for the given host:port
  -        LinkedList listConnections = getConnections(hostAndPort);
  -        
  -        HttpConnection conn = null;
  -        // get a connection from the 'pool', waiting for 'timeout' if no
  -        // connections are currently available
  -        synchronized(listConnections) {
  -            if (listConnections.size() > 0) {
  -                conn = (HttpConnection)listConnections.removeFirst();
  -            } else {
  -                // get number of connections to host:port
  -                Integer numConnections =  getConnectionsInUse(hostAndPort);
  -                if (numConnections.intValue() < maxConnections) {
  -                    // Create a new connection
  -                    boolean isSecure = protocol.equalsIgnoreCase("HTTPS");
  -                    conn = new HttpConnection(proxyHost, proxyPort, host, port,
  -                        isSecure);
  -                    numConnections = new Integer(numConnections.intValue() + 1);
  -                    mapNumConnections.put(hostAndPort, numConnections);
  -                } else {
  -                    conn = waitForConnection(listConnections, timeout);
  -                }
  -            }
  -        }
  -
  -        return conn;
  -    }
  -
  -    /**
  -     * Get the pool (list) of connections available for the given host and port
  +     * The connection manager should be registered with any HttpConnection that
  +     * is created.
        *
  -     * @param hostAndPort the key for the connection pool
  -     * @return a pool (list) of connections available for the given key
  +     * @param hostConfiguration the host configuration to use to configure the
  +     * connection
  +     * @param timeout - the time (in milliseconds) to wait for a connection to
  +     * become available, 0 to specify an infinite timeout
  +     * 
  +     * @return an HttpConnection for the given configuraiton
  +     * 
  +     * @exception org.apache.commons.httpclient.HttpException if no connection
  +     * becomes available before the timeout expires
  +     * 
  +     * @see HttpConnection#setHttpConnectionManager(HttpConnectionManager)
        */
  -    private LinkedList getConnections(String hostAndPort) {
  -        log.trace("enter HttpConnectionManager.getConnections(String)");
  +    public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout)
  +        throws HttpException, MalformedURLException;
   
  -        // Look for a list of connections for the given host:port
  -        LinkedList listConnections = null;
  -        synchronized (mapHosts) {
  -            listConnections = (LinkedList) mapHosts.get(hostAndPort);
  -            if (listConnections == null) {
  -                // First time for this host:port
  -                listConnections = new LinkedList();
  -                mapHosts.put(hostAndPort, listConnections);
  -                mapNumConnections.put(hostAndPort, new Integer(0));
  -            }
  -        }
  -        return listConnections;
  -    }
  -    
       /**
  -     * Get the number of connections in use for the key
  +     * Releases the given HttpConnection for use by other requests.
        *
  -     * @param hostAndPort the key that connections are tracked on
  -     * @return the number of connections in use for the given key
  -     */
  -    public Integer getConnectionsInUse(String hostAndPort) {
  -        log.trace("enter HttpConnectionManager.getConnectionsInUse(String)");
  -        // FIXME: Shouldn't this be synchronized on mapNumConnections? or
  -        //        mapHosts?
  -
  -        Integer numConnections = (Integer)mapNumConnections.get(hostAndPort);
  -        if (numConnections == null) {
  -            log.error("HttpConnectionManager.getConnection:  "
  -                + "No connection count for " + hostAndPort);
  -            // This should never happen, but just in case we'll try to recover.
  -            numConnections = new Integer(0);
  -            mapNumConnections.put(hostAndPort, numConnections);
  -        }
  -        return numConnections;
  -    }
  -    
  -    /**
  -     * wait for a connection from the pool
  -     */
  -    private HttpConnection waitForConnection(LinkedList pool, long timeout)
  -    throws HttpException {
  -        log.trace("enter HttpConnectionManager.waitForConnection(LinkedList, long)");
  -
  -        // No connections available, so wait
  -        // Start the timeout thread
  -        TimeoutThread threadTimeout = new TimeoutThread();
  -        threadTimeout.setTimeout(timeout);
  -        threadTimeout.setWakeupThread(Thread.currentThread());
  -        threadTimeout.start();
  -
  -        HttpConnection conn = null;
  -        // wait for the connection to be available
  -        while(conn == null){    // spin lock
  -            try {
  -                log.debug("HttpConnectionManager.getConnection:  waiting for "
  -                    + "connection from " + pool);
  -                pool.wait();
  -            } catch (InterruptedException e) {
  -                throw new HttpException("Timeout waiting for connection.");
  -            }
  -            if (pool.size() > 0) {
  -                conn = (HttpConnection)pool.removeFirst();
  -                threadTimeout.interrupt();
  -            }
  -        }
  -        return conn;
  -    }
  -    
  -    /**
  -     * Make the given HttpConnection available for use by other requests.
  -     * If another thread is blocked in getConnection() waiting for a connection
  -     * for this host:port, they will be woken up.
  -     * 
        * @param conn - The HttpConnection to make available.
        */
  -    public void releaseConnection(HttpConnection conn) {
  -        log.trace("enter HttpConnectionManager.releaseConnection(HttpConnection)");
  -
  -        String host = conn.getHost();
  -        int port = conn.getPort();
  -        String key = host + ":" + port;
  -
  -        if(log.isDebugEnabled()){
  -            log.debug("HttpConnectionManager.releaseConnection:  Release connection for " + host + ":" + port);
  -        }
  -
  -        LinkedList listConnections = null;
  -        synchronized(mapHosts){
  -            listConnections = (LinkedList)mapHosts.get(key);
  -            if(listConnections == null){
  -                // This is an error, but we'll try to recover
  -                log.error("HttpConnectionManager.releaseConnection:  No connect list for " + key);
  -                listConnections = new LinkedList();
  -                mapHosts.put(key, listConnections);
  -                mapNumConnections.put(key, new Integer(1));
  -            }
  -        }
  -
  -        synchronized(listConnections){
  -            // Put the connect back in the available list and notify a waiter
  -            listConnections.addFirst(conn);
  -            listConnections.notify();
  -        }
  -    }
  -
  -    /**
  -     * In getConnection, if the maximum number of connections has already
  -     * been reached the call will block.  This class is used to help provide
  -     * a timeout facility for this wait.  Because Java does not provide a way to
  -     * determine if wait() returned due to a notify() or a timeout, we need
  -     * an outside mechanism to interrupt the waiting thread after the specified
  -     * timeout interval.
  -     */
  -    private static class TimeoutThread extends Thread {
  -
  -        private long timeout = 0;
  -        private Thread thrdWakeup = null;
  -
  -        public void setTimeout(long timeout)
  -        {
  -            this.timeout = timeout;
  -        }
  -
  -        public long getTimeout()
  -        {
  -            return timeout;
  -        }
  -
  -        public void setWakeupThread(Thread thrdWakeup)
  -        {
  -            this.thrdWakeup = thrdWakeup;
  -        }
  -
  -        public Thread getWakeupThread()
  -        {
  -            return thrdWakeup;
  -        }
  -
  -        public void run() {
  -	    log.trace("TimeoutThread.run()");
  -            if(timeout == 0){
  -                return;
  -            }
  -            if(thrdWakeup == null){
  -                return;
  -            }
  +    public void releaseConnection(HttpConnection conn);
   
  -            try{
  -                sleep(timeout);
  -                thrdWakeup.interrupt();
  -            }catch(InterruptedException e){
  -	        log.debug("InterruptedException caught as expected");
  -                // This is expected
  -            }
  -        }
  -    }
   }
  
  
  
  1.20      +11 -4     jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethod.java
  
  Index: HttpMethod.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethod.java,v
  retrieving revision 1.19
  retrieving revision 1.20
  diff -u -r1.19 -r1.20
  --- HttpMethod.java	7 Sep 2002 01:07:47 -0000	1.19
  +++ HttpMethod.java	3 Dec 2002 05:46:15 -0000	1.20
  @@ -303,6 +303,13 @@
       public void recycle();
   
       /**
  +     * Releases the connection being used by this method.  In particular the
  +     * connection is used to read the response(if there is one) and will be held
  +     * until the response has been read.
  +     */
  +    public void releaseConnection();
  +
  +    /**
        * Use this method internally to add footers.
        * @since 2.0
        */
  
  
  
  1.81      +110 -12   jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java
  
  Index: HttpMethodBase.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpMethodBase.java,v
  retrieving revision 1.80
  retrieving revision 1.81
  diff -u -r1.80 -r1.81
  --- HttpMethodBase.java	2 Dec 2002 06:14:56 -0000	1.80
  +++ HttpMethodBase.java	3 Dec 2002 05:46:15 -0000	1.81
  @@ -64,23 +64,21 @@
   import java.io.ByteArrayInputStream;
   import java.io.ByteArrayOutputStream;
   import java.io.IOException;
  -import java.io.UnsupportedEncodingException;
   import java.io.InputStream;
  -import java.io.InputStreamReader;
  -import java.io.OutputStream;
  -import java.net.URL;
  +import java.io.UnsupportedEncodingException;
   import java.net.MalformedURLException;
  +import java.net.URL;
   import java.util.Date;
  -import java.util.Map;
   import java.util.HashMap;
   import java.util.HashSet;
   import java.util.Iterator;
  +import java.util.Map;
   import java.util.Set;
   import java.util.StringTokenizer;
   
  +import org.apache.commons.httpclient.util.URIUtil;
   import org.apache.commons.logging.Log;
   import org.apache.commons.logging.LogFactory;
  -import org.apache.commons.httpclient.util.URIUtil;
   
   /**
    * <p>
  @@ -210,6 +208,9 @@
   
       /** The response body, assuming it has not be intercepted by a sub-class. */
       private InputStream responseStream = null;
  +    
  +    /** The connection that the response stream was read from. */
  +    private HttpConnection responseConnection = null;
   
       /** Buffer for the response */
       private byte[] responseBody = null;
  @@ -758,6 +759,15 @@
           return false;
       }
   
  +    private void wrapResponseStream( HttpConnection connection ) {
  +        
  +        if ( responseStream != null ) {
  +            this.responseConnection = connection;
  +            this.responseStream = new ResponseAutoReleaseInputStream(responseStream);   
  +        }
  +           
  +    }
  +
       /**
        * Execute this method. Note that we cannot currently support redirects
        * that change  the connection parameters (host, port, protocol) because
  @@ -831,9 +841,11 @@
                           //if the authentication is successful, return the statusCode
                           //otherwise, drop through the switch and try again.
                           if (processAuthenticationResponse(state, conn)) {
  +                            wrapResponseStream(conn);
                               return statusCode;
                           }
                       } else { //let the client handle the authenticaiton
  +                        wrapResponseStream(conn);                      
                           return statusCode;
                       }
                       break;
  @@ -844,12 +856,14 @@
                       log.debug("Redirect required");
   
                       if (! processRedirectResponse(state, conn)) {
  +                        wrapResponseStream(conn);                
                           return statusCode;
                       }
                       break;
   
                   default:
                       // neither an unauthorized nor a redirect response
  +                    wrapResponseStream(conn);
                       return statusCode;
               } //end of switch
   
  @@ -872,6 +886,8 @@
               }
           } //end of loop
   
  +        wrapResponseStream(conn);
  +        
           log.error("Narrowly avoided an infinite loop in execute");
           throw new HttpRecoverableException("Maximum redirects ("+ maxForwards +") exceeded");
       }
  @@ -1027,6 +1043,8 @@
       public void recycle() {
           log.trace("enter HttpMethodBase.recycle()");
   
  +        releaseConnection();
  +        
           path = null;
           followRedirects = false;
           doAuthentication = true;
  @@ -1041,6 +1059,21 @@
       }
   
       /**
  +     * @see org.apache.commons.httpclient.HttpMethod#releaseConnection()
  +     * 
  +     * @since 2.0
  +     */
  +    public void releaseConnection() {
  +        
  +        if ( responseConnection != null ) {
  +            responseConnection.releaseConnection();
  +            this.responseConnection = null;
  +            this.responseStream = null;
  +        }
  +        
  +    }
  +    
  +    /**
        * Remove the request header associated with the given name. Note that
        * header-name matching is case insensitive.
        * 
  @@ -1586,7 +1619,7 @@
           responseBody = null; // is this desired?
           Header lengthHeader = getResponseHeader("Content-Length");
           Header transferEncodingHeader = getResponseHeader("Transfer-Encoding");
  -        InputStream is = conn.getResponseInputStream(this);
  +        InputStream is = conn.getResponseInputStream();
           if (wireLog.isDebugEnabled()) {
               is = new WireLogInputStream(is);
           }
  @@ -1616,6 +1649,8 @@
   
               try {
                   int expectedLength = Integer.parseInt(lengthValue);
  +                // FIXME: what if the content length is 0, perhaps we should 
  +                // just return an empty stream in that case                
                   result = new ContentLengthInputStream(is, expectedLength);
               } catch(NumberFormatException e) {
                   throw new HttpException( 
  @@ -1629,11 +1664,14 @@
                   && !getName().equals(ConnectMethod.NAME)){
               result = is;
           }
  -        if (result == null) return null;
  +        if (result == null) {
  +            return null;
  +        }
   
           if (shouldCloseConnection()) {
               result = new AutoCloseInputStream(result, conn);
           }
  +
           return result;
       }
   
  @@ -2263,5 +2301,65 @@
           return getContentCharSet(getResponseHeader("Content-Type"));
       }
   
  +    /**
  +     * Releases this connection from its connectionManager when the response has
  +     * been read.
  +     */
  +    private class ResponseAutoReleaseInputStream extends InputStream {
  +
  +        private InputStream is;
  +
  +        public ResponseAutoReleaseInputStream(InputStream is) {
  +            this.is = is;
  +        }
  +
  +        /**
  +         * @see java.io.InputStream#close()
  +         */
  +        public void close() throws IOException {
  +            is.close();
  +            releaseConnection();
  +        }
  +
  +        /**
  +         * @see java.io.InputStream#read()
  +         */
  +        public int read() throws IOException {
  +            int b = is.read();
  +
  +            if ( b == -1 ) {
  +                releaseConnection();
  +            }
  +
  +            return b;
  +        }
  +
  +        /**
  +         * @see java.io.InputStream#read(byte, int, int)
  +         */
  +        public int read(byte[] array, int off, int len) throws IOException {
  +            int b = is.read(array, off, len);
  +
  +            if ( b == -1 ) {
  +                releaseConnection();
  +            }
  +
  +            return b;
  +        }
  +
  +        /**
  +         * @see java.io.InputStream#read(byte)
  +         */
  +        public int read(byte[] array) throws IOException {
  +            int b = is.read(array);
  +
  +            if ( b == -1 ) {
  +                releaseConnection();
  +            }
  +
  +            return b;
  +        }
  +
  +    }
   
   }
  
  
  
  1.13      +58 -15    jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java
  
  Index: HttpState.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpState.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- HttpState.java	5 Sep 2002 04:57:00 -0000	1.12
  +++ HttpState.java	3 Dec 2002 05:46:15 -0000	1.13
  @@ -82,6 +82,7 @@
    * @author Rodney Waldhoff
    * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
    * @author Sean C. Sullivan
  + * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
    * 
    * @version $Revision$ $Date$
    * 
  @@ -105,11 +106,24 @@
        */
       private ArrayList cookies = new ArrayList();
   
  +    private HttpConnectionManager httpConnectionManager;
  +
       // -------------------------------------------------------- Class Variables
   
       /** Log object for this class. */
       private static final Log log = LogFactory.getLog(HttpState.class);
   
  +    /**
  +     * Constructor for HttpState.
  +     */
  +    public HttpState() {
  +        
  +        super();
  +        
  +        this.httpConnectionManager = new SimpleHttpConnectionManager();
  +        
  +    }
  +
       // ------------------------------------------------------------- Properties
   
       /**
  @@ -122,7 +136,7 @@
        * @see #addCookies(Cookie[])
        * 
        */
  -    public void addCookie(Cookie cookie) {
  +    public synchronized void addCookie(Cookie cookie) {
           log.trace("enter HttpState.addCookie(Cookie)");
   
           if (cookie != null) {
  @@ -151,7 +165,7 @@
        * 
        * 
        */
  -    public void addCookies(Cookie[] newcookies) {
  +    public synchronized void addCookies(Cookie[] newcookies) {
           log.trace("enter HttpState.addCookies(Cookie[])");
   
           if (newcookies != null) {
  @@ -169,7 +183,7 @@
        * @see #getCookies(String, int, String, boolean, java.util.Date)
        * 
        */
  -    public Cookie[] getCookies() {
  +    public synchronized Cookie[] getCookies() {
           log.trace("enter HttpState.getCookies()");
           return (Cookie[])(cookies.toArray(new Cookie[cookies.size()]));
       }
  @@ -189,7 +203,13 @@
        * @see #getCookies()
        * 
        */
  -    public Cookie[] getCookies(String domain, int port, String path, boolean secure, Date now) {
  +    public synchronized Cookie[] getCookies(
  +        String domain, 
  +        int port, 
  +        String path, 
  +        boolean secure, 
  +        Date now
  +    ) {
           log.trace("enter HttpState.getCookies(String, int, String, boolean, Date)");
   
           ArrayList list = new ArrayList(cookies.size());
  @@ -211,7 +231,7 @@
        * @see #purgeExpiredCookies(java.util.Date)
        * 
        */
  -    public boolean purgeExpiredCookies() {
  +    public synchronized boolean purgeExpiredCookies() {
           log.trace("enter HttpState.purgeExpiredCookies()");
           return purgeExpiredCookies(new Date());
       }
  @@ -224,7 +244,7 @@
        * @see #purgeExpiredCookies()
        * 
        */
  -    public boolean purgeExpiredCookies(Date date) {
  +    public synchronized boolean purgeExpiredCookies(Date date) {
           log.trace("enter HttpState.purgeExpiredCookies(Date)");
           boolean removed = false;
           Iterator it = cookies.iterator();
  @@ -256,7 +276,7 @@
        * @see #setProxyCredentials(String, Credentials)
        * 
        */
  -    public void setCredentials(String realm, Credentials credentials) {
  +    public synchronized void setCredentials(String realm, Credentials credentials) {
           log.trace("enter HttpState.setCredentials(String, Credentials)");
           credMap.put(realm,credentials);
       }
  @@ -275,7 +295,7 @@
        * @see #setCredentials(String, Credentials)
        * 
        */
  -    public Credentials getCredentials(String realm) {
  +    public synchronized Credentials getCredentials(String realm) {
           log.trace("enter HttpState.getCredentials(String)");
   
           Credentials creds = (Credentials) credMap.get(realm);
  @@ -305,7 +325,7 @@
        * @see #setCredentials(String, Credentials)
        * 
        */
  -    public void setProxyCredentials(String realm, Credentials credentials) {
  +    public synchronized void setProxyCredentials(String realm, Credentials credentials) {
           log.trace("enter HttpState.setProxyCredentials(String, credentials)");
           proxyCred.put(realm, credentials);
       }
  @@ -322,7 +342,7 @@
        * @return the credentials 
        * @see #setProxyCredentials
        */
  -    public Credentials getProxyCredentials(String realm) {
  +    public synchronized Credentials getProxyCredentials(String realm) {
           log.trace("enter HttpState.getProxyCredentials(String)");
           Credentials creds = (Credentials) proxyCred.get(realm);
           if (creds == null) {
  @@ -331,7 +351,7 @@
           return creds;
       }
       
  -    public String toString()
  +    public synchronized String toString()
       {
       	StringBuffer sbResult = new StringBuffer();
   
  @@ -401,4 +421,27 @@
       	}
       	return sbResult;
       }
  +    
  +    /**
  +     * Returns the httpConnectionManager.
  +     * @return HttpConnectionManager
  +     * 
  +     * @since 2.0
  +     */
  +    public synchronized HttpConnectionManager getHttpConnectionManager() {
  +        return httpConnectionManager;
  +    }
  +
  +    /**
  +     * Sets the httpConnectionManager.
  +     * @param httpConnectionManager The httpConnectionManager to set
  +     * 
  +     * @since 2.0
  +     */
  +    public synchronized void setHttpConnectionManager(
  +        HttpConnectionManager httpConnectionManager
  +    ) {
  +        this.httpConnectionManager = httpConnectionManager;
  +    }
  +
   }
  
  
  
  1.6       +4 -4      jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpUrlMethod.java
  
  Index: HttpUrlMethod.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/HttpUrlMethod.java,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- HttpUrlMethod.java	3 Sep 2002 01:36:26 -0000	1.5
  +++ HttpUrlMethod.java	3 Dec 2002 05:46:15 -0000	1.6
  @@ -68,7 +68,7 @@
    * of a URL and gets the host:port from the connection maintained in
    * HttpCleint.  HttpUrlMethod is initialized with a fully specified URL and is
    * used with HttpMultiClient.  HttpMultiClient  chooses the appropriate
  - * HttpConnectoin (via HttpConnectionManager) based on the host and port in
  + * HttpConnectoin (via MultiThreadedHttpConnectionManager) based on the host and port in
    * the URL.
    * 
    * @author Marc A. Saegesser
  
  
  
  1.1                  jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java
  
  Index: MultiThreadedHttpConnectionManager.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/MultiThreadedHttpConnectionManager.java,v 1.1 2002/12/03 05:46:15 jsdever Exp $
   * $Revision: 1.1 $
   * $Date: 2002/12/03 05:46:15 $
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 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", "Tomcat", 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
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  package org.apache.commons.httpclient;
  
  import java.lang.ref.Reference;
  import java.lang.ref.ReferenceQueue;
  import java.lang.ref.WeakReference;
  import java.net.MalformedURLException;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.LinkedList;
  import java.util.Map;
  
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  
  /**
   * Manages a set of HttpConnections for various host:ports.
   *
   * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
   * 
   * @since 2.0
   */
  public class MultiThreadedHttpConnectionManager implements HttpConnectionManager {
      
      // -------------------------------------------------------- Class Variables
      /** Log object for this class. */
      private static final Log log = LogFactory.getLog(MultiThreadedHttpConnectionManager.class);
  
      // ----------------------------------------------------- Instance Variables
      private Map mapHosts = new HashMap();
      private int maxConnections = 2;   // Per RFC 2616 sec 8.1.4
  
      // mapping from reference to hostPort
      private Map referenceToHostPort;
      // the reference queue used to track when HttpConnections are lost to the
      // garbage collector
      private ReferenceQueue referenceQueue;
  
      /**
       * No-args constructor
       */
      public MultiThreadedHttpConnectionManager() {
          
          this.referenceToHostPort = Collections.synchronizedMap( new HashMap() );
          this.referenceQueue = new ReferenceQueue();
          
          new ReferenceQueueThread().start();
          
      }
  
      /**
       * Set the maximum number of connections allowed for a given host:port.
       * Per RFC 2616 section 8.1.4, this value defaults to 2.
       *
       * @param maxConnections - number of connections allowed for each host:port
       */
      public void setMaxConnectionsPerHost(int maxConnections) {
          this.maxConnections = maxConnections;
      }
  
      /**
       * Get the maximum number of connections allowed for a given host:port.
       *
       * @return The maximum number of connections allowed for a given host:port.
       */
      public int getMaxConnectionsPerHost() {
          return maxConnections;
      }
  
      /**
       * @see HttpConnectionManager#getConnection(HostConfiguration)
       */
      public HttpConnection getConnection(HostConfiguration hostConfiguration)
      throws MalformedURLException {
          
          while( true ) {
              try {
                  return getConnection(hostConfiguration, 0);
              } catch ( HttpException e ) {
                  log.debug( 
                      "Unexpected exception while waiting for connection", 
                      e 
                  );
              };
          }
          
      }
  
      /**
       * Return the port provided if not -1 (default), return 443 if the
       * protocol is HTTPS, otherwise 80. 
       *
       * This functionality is a URLUtil and may be better off in URIUtils
       *
       * @param protocol the protocol to use to get the port for, e.g. http or
       *      https
       * @param port the port provided with the url (could be -1)
       * @return the port for the specified port and protocol.
       */
      private static int getPort(String protocol, int port) {
          log.trace("HttpConnectionManager.getPort(String, port)");
  
          // default to provided port
          int portForProtocol = port;
          if (portForProtocol == -1) {
              if (protocol.equalsIgnoreCase("HTTPS")) {
                  portForProtocol = 443;
              } else {
                  portForProtocol = 80;
              }
          }
          return portForProtocol;
      }
      
      /**
       * @see HttpConnectionManager#getConnection(HostConfiguration, long)
       */
      public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout) 
      throws HttpException, MalformedURLException {
          log.trace("enter HttpConnectionManager.getConnection(HostConfiguration, long)");
  
          if (hostConfiguration == null) {
              throw new MalformedURLException("hostConfiguration is null");
          }
  
          // Get the protocol and port (use default port if not specified)
          String protocol = hostConfiguration.getProtocol();
          String host = hostConfiguration.getHost();
          int port = MultiThreadedHttpConnectionManager.getPort(
              protocol, 
              hostConfiguration.getPort()
          );
          String hostAndPort = host + ":" + port;
  
          if (log.isDebugEnabled()) {
              log.debug("HttpConnectionManager.getConnection:  key = "
                  + hostAndPort);
          }
  
          // Look for a list of connections for the given host:port
          HostConnectionPool connectionPool = getConnectionPool(hostAndPort);
          
          HttpConnection conn = getConnection( 
              connectionPool,
              host,
              port,
              protocol.equalsIgnoreCase("HTTPS"),
              hostConfiguration.getProxyHost(),
              hostConfiguration.getProxyPort(),
              timeout
          );
  
          return conn;
      }
  
      /**
       * Gets a connection or waits if one is not available.  A connection is
       * available if one exists that is not being used or if fewer than
       * maxConnections have been created in the connectionPool.
       * 
       * @param connectionPool
       * @param host
       * @param port
       * @param useHttps
       * @param proxyHost
       * @param proxyPort
       * @param timeout the number of milliseconds to wait for a connection, 0 to
       * wait indefinitely
       * 
       * @return HttpConnection an available connection
       * 
       * @throws HttpException if a connection does not available in 'timeout'
       * milliseconds
       */
      private HttpConnection getConnection(
          HostConnectionPool connectionPool,
          String host,
          int port,
          boolean useHttps,
          String proxyHost,
          int proxyPort,
          long timeout
      ) throws HttpException {
          
          HttpConnection connection = null;
          
          synchronized( connectionPool ) {
              
              // keep trying until a connection is available, should happen at 
              // most twice
              while ( connection == null ) {
          
                  if (connectionPool.freeConnections.size() > 0) {
                      connection = (HttpConnection)connectionPool.freeConnections.removeFirst();
                  } else {
                      // get number of connections to host:port
                      if (connectionPool.numConnections < maxConnections) {
                          // Create a new connection
                          connection = new HttpConnection(
                              proxyHost, 
                              proxyPort, 
                              host, 
                              port,
                              useHttps
                          );
                          connection.setHttpConnectionManager( this );
                          connectionPool.numConnections++;
  
                          // add a weak reference to this connection
                          referenceToHostPort.put(
                              new WeakReference( connection, referenceQueue ),
                              host + ":" + port
                          );
                          
                      } else {
      
                          TimeoutThread threadTimeout = new TimeoutThread();
                          threadTimeout.setTimeout(timeout);
                          threadTimeout.setWakeupThread(Thread.currentThread());
                          threadTimeout.start();
      
                          try {
                              log.debug(
                                  "HttpConnectionManager.getConnection:  waiting for "
                                  + "connection from " + connectionPool
                              );
                              connectionPool.wait();
                              // we were woken up before the timeout occurred, so
                              // there should be a connection available
                              threadTimeout.interrupt();
                          } catch (InterruptedException e) {
                              throw new HttpException("Timeout waiting for connection.");
                          }
      
                      }
                  }
              }
              
          }
          
          return connection;        
      }
  
      /**
       * Get the pool (list) of connections available for the given host and port
       *
       * @param hostAndPort the key for the connection pool
       * @return a pool (list) of connections available for the given key
       */
      private HostConnectionPool getConnectionPool(String hostAndPort) {
          log.trace("enter HttpConnectionManager.getConnections(String)");
  
          // Look for a list of connections for the given host:port
          HostConnectionPool listConnections = null;
          synchronized (mapHosts) {
              listConnections = (HostConnectionPool) mapHosts.get(hostAndPort);
              if (listConnections == null) {
                  // First time for this host:port
                  listConnections = new HostConnectionPool();
                  mapHosts.put(hostAndPort, listConnections);
              }
          }
          return listConnections;
      }
      
      /**
       * Get the number of connections in use for the key
       *
       * @param hostAndPort the key that connections are tracked on
       * @return the number of connections in use for the given key
       */
      public int getConnectionsInUse(String hostAndPort) {
          log.trace("enter HttpConnectionManager.getConnectionsInUse(String)");
  
          HostConnectionPool connectionPool = getConnectionPool(hostAndPort);
          synchronized( connectionPool ) {
              return connectionPool.numConnections;   
          }
  
      }
      
      /**
       * Make the given HttpConnection available for use by other requests.
       * If another thread is blocked in getConnection() waiting for a connection
       * for this host:port, they will be woken up.
       * 
       * @param conn - The HttpConnection to make available.
       */
      public void releaseConnection(HttpConnection conn) {
          log.trace("enter HttpConnectionManager.releaseConnection(HttpConnection)");
  
          String host = conn.getHost();
          int port = conn.getPort();
          String key = host + ":" + port;
  
          if(log.isDebugEnabled()){
              log.debug("HttpConnectionManager.releaseConnection:  Release connection for " + host + ":" + port);
          }
  
          HostConnectionPool listConnections = getConnectionPool(key);
          synchronized(listConnections){
              // Put the connect back in the available list and notify a waiter
              listConnections.freeConnections.addFirst(conn);
              if ( listConnections.numConnections == 0 ) {
                  // for some reason this connection pool didn't already exist
                  log.error("connection pool not found for host: " + key);
                  listConnections.numConnections = 1;
              }
              listConnections.notify();
          }
      }
      
      /**
       * A simple struct-link class to combine the connection list and the count
       * of created connections.
       */
      private class HostConnectionPool {
          
          public LinkedList freeConnections = new LinkedList();
          public int numConnections = 0;
             
      }
  
      /**
       * A thread for listening for HttpConnections reclaimed by the garbage
       * collector.
  	 */
      private class ReferenceQueueThread extends Thread {
      
          public ReferenceQueueThread() {
              setDaemon(true);   
          }
      
          /**
           * @see java.lang.Runnable#run()
           */
          public void run() {
              
              while(true) {
                  
                  try {
                      Reference ref = referenceQueue.remove();
                      
                      if ( ref != null ) {
                          String hostPort = (String)referenceToHostPort.get(ref);
                          referenceToHostPort.remove(ref);
                          HostConnectionPool connectionPool = getConnectionPool(hostPort);
                          synchronized( connectionPool ) {
                              connectionPool.numConnections--;
                              connectionPool.notify();
                          }
                      }
                  } catch (InterruptedException e) {
                      log.debug("ReferenceQueueThread interrupted", e);
                  }
                  
              }
              
          }
  
      }
  
      /**
       * In getConnection, if the maximum number of connections has already
       * been reached the call will block.  This class is used to help provide
       * a timeout facility for this wait.  Because Java does not provide a way to
       * determine if wait() returned due to a notify() or a timeout, we need
       * an outside mechanism to interrupt the waiting thread after the specified
       * timeout interval.
       */
      private static class TimeoutThread extends Thread {
  
          private long timeout = 0;
          private Thread thrdWakeup = null;
  
          public void setTimeout(long timeout)
          {
              this.timeout = timeout;
          }
  
          public long getTimeout()
          {
              return timeout;
          }
  
          public void setWakeupThread(Thread thrdWakeup)
          {
              this.thrdWakeup = thrdWakeup;
          }
  
          public Thread getWakeupThread()
          {
              return thrdWakeup;
          }
  
          public void run() {
  	    log.trace("TimeoutThread.run()");
              if(timeout == 0){
                  return;
              }
              if(thrdWakeup == null){
                  return;
              }
  
              try{
                  sleep(timeout);
                  thrdWakeup.interrupt();
              }catch(InterruptedException e){
  	        log.debug("InterruptedException caught as expected");
                  // This is expected
              }
          }
      }
      
  }
  
  
  
  1.1                  jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/SimpleHttpConnectionManager.java
  
  Index: SimpleHttpConnectionManager.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/httpclient/src/java/org/apache/commons/httpclient/SimpleHttpConnectionManager.java,v 1.1 2002/12/03 05:46:15 jsdever Exp $
   * $Revision: 1.1 $
   * $Date: 2002/12/03 05:46:15 $
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 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", "Tomcat", 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
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  package org.apache.commons.httpclient;
  
  import java.net.MalformedURLException;
  
  
  /**
   * A connection manager that provides access to a single HttpConnection.  This
   * manager makes no attempt to provide exclusive access to the contained
   * HttpConnection.
   * 
   * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
   *
   * @since 2.0
   */
  public class SimpleHttpConnectionManager implements HttpConnectionManager {
  
      private HttpConnection httpConnection;
  
      /**
       * Constructor for SimpleHttpConnectionManager.
       */
      public SimpleHttpConnectionManager() {
          super();
      }
  
      /**
       * @see org.apache.commons.httpclient.HttpConnectionManager#getConnection(HostConfiguration)
       */
      public HttpConnection getConnection(HostConfiguration hostConfiguration)
          throws MalformedURLException {
  
          return getConnection(hostConfiguration, 0);
      }
  
      /**
       * @see org.apache.commons.httpclient.HttpConnectionManager#getConnection(HostConfiguration, long)
       */
      public HttpConnection getConnection(HostConfiguration hostConfiguration, long timeout)
          throws MalformedURLException {
  
          String protocol = hostConfiguration.getProtocol();
          String host = hostConfiguration.getHost();
          int port = hostConfiguration.getPort();
          boolean isSecure = protocol.equalsIgnoreCase("HTTPS");
          
          if ( httpConnection == null ) {
              
              if ( hostConfiguration.isProxySet() ) {
                  httpConnection = new HttpConnection(
                      hostConfiguration.getProxyHost(),
                      hostConfiguration.getProxyPort(),
                      host, 
                      port, 
                      isSecure
                  );
              } else {            
                  httpConnection = new HttpConnection(host, port, isSecure);
              }
              
          } else {
              
              // make sure the host and proxy are correct for this connection
              // close it and set the values if they are not
              if ( 
                  !hostConfiguration.hostEquals(httpConnection) 
                  || !hostConfiguration.proxyEquals(httpConnection)
              ) {
                  if ( httpConnection.isOpen() ) {
                      httpConnection.close();
                  }
  
                  httpConnection.setHost(host);
                  httpConnection.setPort(port);
                  httpConnection.setSecure(isSecure);
  
                  httpConnection.setProxyHost(hostConfiguration.getProxyHost());
                  httpConnection.setProxyPort(hostConfiguration.getProxyPort());
  
              } 
              
          }
  
          return httpConnection;
          
      }
  
      /**
       * @see org.apache.commons.httpclient.HttpConnectionManager#releaseConnection(org.apache.commons.httpclient.HttpConnection)
       */
      public void releaseConnection(HttpConnection conn) {
      }
  
  }
  
  
  
  1.4       +5 -9      jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpClientLocalHost.java
  
  Index: TestHttpClientLocalHost.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpClientLocalHost.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- TestHttpClientLocalHost.java	1 Nov 2002 09:51:05 -0000	1.3
  +++ TestHttpClientLocalHost.java	3 Dec 2002 05:46:16 -0000	1.4
  @@ -78,14 +78,10 @@
    * @author Rodney Waldhoff
    * @version $Id$
    */
  -public class TestHttpClientLocalHost extends TestCase {
  +public class TestHttpClientLocalHost extends TestLocalHostBase {
   
   
       // -------------------------------------------------------------- Constants
  -
  -
  -    private static final String host = "127.0.0.1";
  -    private static final int port = 8080;
   
   
       // ------------------------------------------------------------ Constructor
  
  
  
  1.3       +7 -8      jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnection.java
  
  Index: TestHttpConnection.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnection.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- TestHttpConnection.java	12 Nov 2002 09:58:23 -0000	1.2
  +++ TestHttpConnection.java	3 Dec 2002 05:46:16 -0000	1.3
  @@ -76,9 +76,9 @@
    * @version $Id$
    *
    */
  -public class TestHttpConnection extends TestCase {
  +public class TestHttpConnection extends TestLocalHostBase {
       // ----------------------------------------------------- Instance Variables
  -
  +    
       // ------------------------------------------------------------ Constructor
       public TestHttpConnection(String testName) {
           super(testName);
  @@ -100,13 +100,13 @@
       // ----------------------------------------------------------- Test Methods
   
       public void testConstructThenClose() {
  -        HttpConnection conn = new HttpConnection("localhost", 8080);
  +        HttpConnection conn = new HttpConnection(host,port);
           conn.close();
           assertTrue( ! conn.isOpen() );
       }
   
       public void testConnTimeout() {
  -        HttpConnection conn = new HttpConnection("localhost", 8080);
  +        HttpConnection conn = new HttpConnection(host,port);
           // 1 ms is short enough to make this fail
           conn.setConnectionTimeout(1);
           try {
  @@ -118,9 +118,8 @@
           }
       }
   
  -
       public void testForIllegalStateExceptions() {
  -        HttpConnection conn = new HttpConnection("localhost", 8080);
  +        HttpConnection conn = new HttpConnection(host,port);
   
           try {
               OutputStream out = conn.getRequestOutputStream();
  @@ -134,7 +133,7 @@
           }
   
           try {
  -            OutputStream out = conn.getRequestOutputStream(true);
  +            OutputStream out = new ChunkedOutputStream(conn.getRequestOutputStream());
               fail("getRequestOutputStream(true) did not throw the expected exception");
           }
           catch (IllegalStateException expected) {
  @@ -145,7 +144,7 @@
           }
   
           try {
  -            InputStream in = conn.getResponseInputStream(new PostMethod());
  +            InputStream in = conn.getResponseInputStream();
               fail("getResponseInputStream() did not throw the expected exception");
           }
           catch (IllegalStateException expected) {
  
  
  
  1.2       +194 -39   jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java
  
  Index: TestHttpConnectionManager.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestHttpConnectionManager.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TestHttpConnectionManager.java	12 Apr 2002 21:17:06 -0000	1.1
  +++ TestHttpConnectionManager.java	3 Dec 2002 05:46:16 -0000	1.2
  @@ -6,7 +6,7 @@
    *
    * The Apache Software License, Version 1.1
    *
  - * Copyright (c) 1999 The Apache Software Foundation.  All rights
  + * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights
    * reserved.
    *
    * Redistribution and use in source and binary forms, with or without
  @@ -62,11 +62,13 @@
   
   package org.apache.commons.httpclient;
   
  -import junit.framework.*;
  +import java.io.IOException;
   import java.net.MalformedURLException;
  -import java.lang.reflect.*;
   
  -import org.apache.commons.httpclient.methods.*;
  +import junit.framework.Test;
  +import junit.framework.TestSuite;
  +
  +import org.apache.commons.httpclient.methods.GetMethod;
   
   /**
    *
  @@ -76,7 +78,7 @@
    * @author Marc A. Saegesser
    * @version $Id$
    */
  -public class TestHttpConnectionManager extends TestCase {
  +public class TestHttpConnectionManager extends TestLocalHostBase {
       // ----------------------------------------------------- Instance Variables
   
       // ------------------------------------------------------------ Constructor
  @@ -100,22 +102,8 @@
       // ----------------------------------------------------------- Test Methods
   
       // Test the accessor methods
  -    public void testProxyHostAccessors() {
  -        HttpConnectionManager mgr = new HttpConnectionManager();
  -
  -        mgr.setProxyHost("proxyhost");
  -        assertEquals("Proxy Host", "proxyhost", mgr.getProxyHost());
  -    }
  -
  -    public void testProxyPortAccessors() {
  -        HttpConnectionManager mgr = new HttpConnectionManager();
  -
  -        mgr.setProxyPort(8888);
  -        assertEquals("Proxy Port", 8888, mgr.getProxyPort());
  -    }
  -
       public void testMaxConnectionsAccessors() {
  -        HttpConnectionManager mgr = new HttpConnectionManager();
  +        MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
   
           // First test the default value
           assertEquals("Default MaxConnections", 2, mgr.getMaxConnectionsPerHost());
  @@ -125,11 +113,14 @@
       }
   
       public void testGetConnection() {
  -        HttpConnectionManager mgr = new HttpConnectionManager();
  +        MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
  +
  +        HostConfiguration hostConfiguration = new HostConfiguration();
  +        hostConfiguration.setHost("www.nosuchserver.com", 80, "http");
   
           try{
               // Create a new connection
  -            HttpConnection conn = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string");
  +            HttpConnection conn = mgr.getConnection(hostConfiguration);
               // Validate the connection properties
               assertEquals("Host", "www.nosuchserver.com", conn.getHost());
               assertEquals("Port", 80, conn.getPort());
  @@ -137,7 +128,8 @@
               mgr.releaseConnection(conn);
   
               // Create a new connection
  -            conn = mgr.getConnection("https://www.nosuchserver.com/path/path?query=string");
  +            hostConfiguration.setHost("www.nosuchserver.com", -1, "https");
  +            conn = mgr.getConnection(hostConfiguration);
               // Validate the connection properties
               assertEquals("Host", "www.nosuchserver.com", conn.getHost());
               assertEquals("Port", 443, conn.getPort());
  @@ -145,7 +137,8 @@
               mgr.releaseConnection(conn);
   
               // Create a new connection
  -            conn = mgr.getConnection("http://www.nowhere.org:8080/path/path?query=string");
  +            hostConfiguration.setHost("www.nowhere.org", 8080, "http");
  +            conn = mgr.getConnection(hostConfiguration);
               // Validate the connection properties
               assertEquals("Host", "www.nowhere.org", conn.getHost());
               assertEquals("Port", 8080, conn.getPort());
  @@ -154,47 +147,209 @@
   
           }catch(MalformedURLException e){
               fail("Caught unexpected MalformedURLException (" + e.toString() + ")");
  -        }catch(HttpException e){
  -            fail("Caught unexpected HttpException (" + e.toString() + ")");
           }
   
  +    }
  +
  +    public void testReleaseConnection() {
  +
  +        MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
  +        connectionManager.setMaxConnectionsPerHost(1);
  +
  +        HttpClient client = new HttpClient(connectionManager);
  +        client.getHostConfiguration().setHost(host, port, "http");
  +        // we shouldn't have to wait if a connection is available
  +        client.setHttpConnectionFactoryTimeout( 1 );
  +
  +        GetMethod getMethod = new GetMethod("/");
  +        getMethod.setFollowRedirects(true);
  +
  +        try {
  +            client.executeMethod(getMethod);
  +        } catch (Exception e) {
  +            fail("error reading from server: " + e);
  +        }
  +
  +        try {
  +            // this should fail quickly since the connection has not been released
  +            client.executeMethod(getMethod);
  +            fail("a httpConnection should not be available");
  +        } catch (HttpException e) {            
  +        } catch (IOException e) {
  +            fail("error reading from server; " + e);
  +        }
  +
  +        // this should release the connection
  +        getMethod.releaseConnection();
  +
  +        getMethod = new GetMethod("/");
  +        getMethod.setFollowRedirects(true);
  +
  +        try {
  +            // this should fail quickly if the connection has not been released
  +            client.executeMethod(getMethod);
  +        } catch (HttpException e) {
  +            fail("httpConnection does not appear to have been released: " + e);
  +        } catch (IOException e) {
  +            fail("error reading from server; " + e);
  +        }
   
       }
   
  +    /**
  +     * Makes sure that a connection gets released after the content of the body
  +     * is read.
  +     */
  +    public void testResponseAutoRelease() {
  +
  +        MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
  +        connectionManager.setMaxConnectionsPerHost(1);
  +
  +        HttpClient client = new HttpClient(connectionManager);
  +        client.getHostConfiguration().setHost(host, port, "http");
  +        // we shouldn't have to wait if a connection is available
  +        client.setHttpConnectionFactoryTimeout( 1 );
  +
  +        GetMethod getMethod = new GetMethod("/");
  +        getMethod.setFollowRedirects(true);
  +
  +        try {
  +            client.executeMethod(getMethod);
  +        } catch (Exception e) {
  +            fail("error reading from server: " + e);
  +        }
  +        
  +        // this should release the connection
  +        getMethod.getResponseBody();
  +
  +        getMethod = new GetMethod("/");
  +        getMethod.setFollowRedirects(true);
  +
  +        try {
  +            // this should fail quickly if the connection has not been released
  +            client.executeMethod(getMethod);
  +        } catch (HttpException e) {
  +            fail("httpConnection does not appear to have been released: " + e);
  +        } catch (IOException e) {
  +            fail("error reading from server; " + e);
  +        }
  +
  +    }
  +    
  +    public void testMaxConnectionsPerServer() {
  +     
  +        MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
  +        connectionManager.setMaxConnectionsPerHost(1);
  +
  +        HttpClient client = new HttpClient(connectionManager);
  +        client.getHostConfiguration().setHost(host, port, "http");
  +        // we shouldn't have to wait if a connection is available
  +        client.setHttpConnectionFactoryTimeout( 1 );
  +
  +        GetMethod getMethod = new GetMethod("/");
  +        getMethod.setFollowRedirects(true);
  +
  +        try {
  +            client.executeMethod(getMethod);
  +        } catch (Exception e) {
  +            fail("error reading from server: " + e);
  +        }
  +
  +        GetMethod getMethod2 = new GetMethod("/");
  +        getMethod2.setFollowRedirects(true);
  +
  +        try {
  +            // this should fail quickly since the connection has not been released
  +            client.executeMethod(getMethod2);
  +            fail("a httpConnection should not be available");
  +        } catch (HttpException e) {
  +        } catch (IOException e) {
  +            fail("error reading from server; " + e);
  +        }
  +                
  +    }
  +    
  +    public void testReclaimUnusedConnection() {
  +
  +        MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
  +        connectionManager.setMaxConnectionsPerHost(1);
  +
  +        HttpClient client = new HttpClient(connectionManager);
  +        client.getHostConfiguration().setHost(host, port, "http");
  +        // we shouldn't have to wait if a connection is available
  +        client.setHttpConnectionFactoryTimeout( 30000 );
  +
  +        GetMethod getMethod = new GetMethod("/");
  +        getMethod.setFollowRedirects(true);
  +
  +        try {
  +            client.executeMethod(getMethod);
  +        } catch (Exception e) {
  +            fail("error reading from server: " + e);
  +        }
  +
  +        getMethod = new GetMethod("/");
  +        getMethod.setFollowRedirects(true);
  +        
  +        Runtime.getRuntime().gc();
  +
  +        try {
  +            // we didn't explicitly release the connection, but it should be 
  +            // reclaimed by the garbage collector, we hope:)
  +            client.executeMethod(getMethod);
  +        } catch (HttpException e) {
  +            fail("httpConnection does not appear to have been reclaimed by the GC: " + e);
  +        } catch (IOException e) {
  +            fail("error reading from server; " + e);
  +        }
  +
  +    }
  +    
       public void testGetMultipleConnections() {
  -        HttpConnectionManager mgr = new HttpConnectionManager();
  +        HttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
  +
  +        HostConfiguration hostConfig1 = new HostConfiguration();
  +        hostConfig1.setHost("www.nosuchserver.com", 80, "http");
  +        
  +        HostConfiguration hostConfig2 = new HostConfiguration();
  +        hostConfig2.setHost("www.nosuchserver.com", -1, "http");
  +
  +        HostConfiguration hostConfig3 = new HostConfiguration();
  +        hostConfig3.setHost("www.nosuchserver.com", -1, "http");
   
           try{
               // Create a new connection
  -            HttpConnection conn1 = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string");
  +            HttpConnection conn1 = mgr.getConnection(hostConfig1);
               // Release the connection
               mgr.releaseConnection(conn1);
   
               // Get the same connection again
  -            HttpConnection conn2 = mgr.getConnection("http://www.nosuchserver.com/");
  +            HttpConnection conn2 = mgr.getConnection(hostConfig2);
               assertEquals("Same connection", conn1, conn2);
               // don't release yet
   
               // Get another new connection
  -            HttpConnection conn3 = mgr.getConnection("http://www.nosuchserver.com/");
  +            HttpConnection conn3 = mgr.getConnection(hostConfig3);
               assertTrue(conn2 != conn3);
   
           }catch(MalformedURLException e){
               fail("Caught unexpected MalformedURLException (" + e.toString() + ")");
  -        }catch(HttpException e){
  -            fail("Caught unexpected HttpException (" + e.toString() + ")");
           }
       }
   
       public void testTimeout()
       {
  -        HttpConnectionManager mgr = new HttpConnectionManager();
  +        MultiThreadedHttpConnectionManager mgr = new MultiThreadedHttpConnectionManager();
           
           try{
  -            HttpConnection conn1 = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string");
  -            HttpConnection conn2 = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string");
  -            HttpConnection conn3 = mgr.getConnection("http://www.nosuchserver.com/path/path?query=string", 5000);
  +            HostConfiguration hostConfig = new HostConfiguration();
  +            hostConfig.setHost("www.nosuchserver.com", 80, "http");
  +            
  +            HttpConnection conn1 = mgr.getConnection(hostConfig);
  +            HttpConnection conn2 = mgr.getConnection(hostConfig);
  +            HttpConnection conn3 = mgr.getConnection(hostConfig, 5000);
               fail("Expected an HttpException.");
  +            
           }catch(MalformedURLException e){
               fail("Caught unexpected MalformedURLException (" + e.toString() + ")");
           }catch(HttpException e){
  
  
  
  1.5       +5 -4      jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestLocalHost.java
  
  Index: TestLocalHost.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestLocalHost.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- TestLocalHost.java	3 Sep 2002 01:24:52 -0000	1.4
  +++ TestLocalHost.java	3 Dec 2002 05:46:16 -0000	1.5
  @@ -81,6 +81,7 @@
       public static Test suite() {
           TestSuite suite = new TestSuite();
           suite.addTest(TestHttpClientLocalHost.suite());
  +        suite.addTest(TestHttpConnection.suite());
           suite.addTest(TestMethodsLocalHost.suite());
           suite.addTest(TestGetMethodLocal.suite());
           suite.addTest(TestTraceMethodLocal.suite());
  
  
  
  1.1                  jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestLocalHostBase.java
  
  Index: TestLocalHostBase.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/httpclient/src/test/org/apache/commons/httpclient/TestLocalHostBase.java,v 1.1 2002/12/03 05:46:16 jsdever Exp $
   * $Revision: 1.1 $
   * $Date: 2002/12/03 05:46:16 $
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002 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", "Tomcat", 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
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */
  package org.apache.commons.httpclient;
  
  import junit.framework.TestCase;
  
  /**
   *
   * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
   */
  public abstract class TestLocalHostBase extends TestCase {
  
      /**
       * Constructor for TestLocalHostBase.
       * @param testName
       */
      public TestLocalHostBase(String testName) {
          super(testName);
      }
  
      protected static final String host = System.getProperty("httpclient.test.localHost","localhost");
      protected static final int port;
      static {
          String portString = System.getProperty("httpclient.test.localPort","8080");
          int tempPort = 8080;
          try {
              tempPort = Integer.parseInt(portString);
          } catch(Exception e) {
              tempPort = 8080;
          }
          port = tempPort;
      }
  
  }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>